class MobileNetV2(nn.Module): def __init__(self, downsample_factor=8, pretrained=True): super(MobileNetV2, self).__init__() from functools import partial model = mobilenetv2(pretrained) self.features = model.features[:-1] self.total_idx = len(self.features) self.down_idx = [2, 4, 7, 14] if downsample_factor == 8: for i in range(self.down_idx[-2], self.down_idx[-1]): self.features[i].apply( partial(self._nostride_dilate, dilate=2) ) for i in range(self.down_idx[-1], self.total_idx): self.features[i].apply( partial(self._nostride_dilate, dilate=4) ) elif downsample_factor == 16: for i in range(self.down_idx[-1], self.total_idx): self.features[i].apply( partial(self._nostride_dilate, dilate=2) ) def _nostride_dilate(self, m, dilate): classname = m.__class__.__name__ if classname.find('Conv') != -1: if m.stride == (2, 2): m.stride = (1, 1) if m.kernel_size == (3, 3): m.dilation = (dilate // 2, dilate // 2) m.padding = (dilate // 2, dilate // 2) else: if m.kernel_size == (3, 3): m.dilation = (dilate, dilate) m.padding = (dilate, dilate)
时间: 2024-02-14 14:32:47 浏览: 97
这是一个使用 PyTorch 实现的 MobileNetV2 模型。MobileNetV2 是一种轻量级的卷积神经网络,适用于移动设备和嵌入式设备等资源受限的环境。
该模型的构造函数中有两个参数:downsample_factor 和 pretrained。downsample_factor 控制最后一层卷积的下采样倍数,可以选择 8 或 16。pretrained 控制是否使用预训练的权重。
该模型使用了 MobileNetV2 的预训练模型作为基础模型,然后通过修改其中的一些层来适应不同的下采样倍数。具体来说,它通过将一些卷积层的步幅从 2 改为 1,并在这些层上应用不同的膨胀率来实现下采样倍数的调整。
_nostride_dilate 是一个辅助函数,用于在不改变卷积层大小的情况下增大感受野。它通过在卷积核周围填充一些像素来实现膨胀卷积。
总体来说,这个 MobileNetV2 模型是一个轻量级的卷积神经网络,适用于在资源受限的环境中进行图像分类等任务。
相关问题
# New module: utils.pyimport torchfrom torch import nnclass ConvBlock(nn.Module): """A convolutional block consisting of a convolution layer, batch normalization layer, and ReLU activation.""" def __init__(self, in_chans, out_chans, drop_prob): super().__init__() self.conv = nn.Conv2d(in_chans, out_chans, kernel_size=3, padding=1) self.bn = nn.BatchNorm2d(out_chans) self.relu = nn.ReLU(inplace=True) self.dropout = nn.Dropout2d(p=drop_prob) def forward(self, x): x = self.conv(x) x = self.bn(x) x = self.relu(x) x = self.dropout(x) return x# Refactored U-Net modelfrom torch import nnfrom utils import ConvBlockclass UnetModel(nn.Module): """PyTorch implementation of a U-Net model.""" def __init__(self, in_chans, out_chans, chans, num_pool_layers, drop_prob, pu_args=None): super().__init__() PUPS.__init__(self, *pu_args) self.in_chans = in_chans self.out_chans = out_chans self.chans = chans self.num_pool_layers = num_pool_layers self.drop_prob = drop_prob # Calculate input and output channels for each ConvBlock ch_list = [chans] + [chans * 2 ** i for i in range(num_pool_layers - 1)] in_chans_list = [in_chans] + [ch_list[i] for i in range(num_pool_layers - 1)] out_chans_list = ch_list[::-1] # Create down-sampling layers self.down_sample_layers = nn.ModuleList() for i in range(num_pool_layers): self.down_sample_layers.append(ConvBlock(in_chans_list[i], out_chans_list[i], drop_prob)) # Create up-sampling layers self.up_sample_layers = nn.ModuleList() for i in range(num_pool_layers - 1): self.up_sample_layers.append(ConvBlock(out_chans_list[i], out_chans_list[i + 1] // 2, drop_prob)) self.up_sample_layers.append(ConvBlock(out_chans_list[-1], out_chans_list[-1], drop_prob)) # Create final convolution layer self.conv2 = nn.Sequential( nn.Conv2d(out_chans_list[-1], out_chans_list[-1] // 2, kernel_size=1), nn.Conv2d(out_chans_list[-1] // 2, out_chans, kernel_size=1), nn.Conv2d(out_chans, out_chans, kernel_size=1), ) def forward(self, x): # Down-sampling path encoder_outs = [] for layer in self.down_sample_layers: x = layer(x) encoder_outs.append(x) x = nn.MaxPool2d(kernel_size=2)(x) # Bottom layer x = self.conv(x) # Up-sampling path for i, layer in enumerate(self.up_sample_layers): x = nn.functional.interpolate(x, scale_factor=2, mode='bilinear', align_corners=True) x = torch.cat([x, encoder_outs[-(i + 1)]], dim=1) x = layer(x) # Final convolution layer x = self.conv2(x) return x
这里给出了一个 PyTorch 实现的 U-Net 模型,并且对代码进行了重构:
1. 将 ConvBlock 提取到了 utils.py 中,以便在 U-Net 中复用。
2. 在 U-Net 模型的 forward 方法中,使用了 ModuleList 对 down-sampling 和 up-sampling 层进行了封装,使得代码更加简洁和易于扩展。
3. 使用了 nn.functional.interpolate 对 feature map 进行了上采样,避免了使用 nn.ConvTranspose2d 带来的一些问题。
4. 在最后的卷积层中,使用了 nn.Sequential 对多个卷积层进行了封装,使得代码更加简洁。
这些重构都是为了提高代码的可读性、可维护性和可扩展性,使得代码更加清晰,易于理解和修改。
mmsegmentation框架下给mobilenetv2主干加入SKNet
在`mmsegmentation`框架中,可以通过修改`mmseg/models/backbones/mobilenet_v2.py`文件来给`MobileNetV2`主干加入`SKNet`。下面是实现的步骤:
1. 首先需要导入`SKNet`的相关模块,可以在文件最上面添加以下代码:
```python
from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer
from ..builder import BACKBONES
```
2. 接着在`MobileNetV2`类中定义`SKNet`的结构,可以在`_make_stem_layer`函数中加入以下代码:
```python
def _make_stem_layer(self, in_channels, stem_channels):
layers = []
layers.append(ConvModule(
in_channels,
stem_channels,
3,
stride=2,
padding=1,
bias=False,
norm_cfg=dict(type='BN', momentum=0.1, eps=1e-5),
activation='relu',
inplace=True))
in_channels = stem_channels
layers.append(ConvModule(
in_channels,
in_channels,
3,
stride=1,
padding=1,
bias=False,
norm_cfg=dict(type='BN', momentum=0.1, eps=1e-5),
activation='relu',
inplace=True))
# add SKNet module
channels = in_channels
mid_channels = channels // 2
squeeze_channels = max(1, mid_channels // 8)
layers.append(
build_plugin_layer(dict(
type='SKConv',
channels=channels,
squeeze_channels=squeeze_channels,
kernel_size=3,
stride=1,
padding=1,
dilation=1,
groups=32,
sk_mode='two',
norm_cfg=dict(type='BN', momentum=0.1, eps=1e-5),
act_cfg=dict(type='ReLU', inplace=True),
),
[build_conv_layer(
dict(type='Conv2d'),
channels,
channels,
kernel_size=3,
stride=1,
padding=1,
bias=False),
build_norm_layer(dict(type='BN', momentum=0.1, eps=1e-5), channels)[1]]))
return nn.Sequential(*layers)
```
3. 最后在`BACKBONES`中注册`MobileNetV2`主干即可。完整代码如下:
```python
import torch.nn as nn
from mmcv.cnn import ConvModule
from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer
from ..builder import BACKBONES
@BACKBONES.register_module()
class MobileNetV2(nn.Module):
def __init__(self,
widen_factor=1.0,
output_stride=32,
norm_cfg=dict(type='BN', momentum=0.1, eps=1e-5),
with_cp=False,
):
super(MobileNetV2, self).__init__()
assert output_stride in [8, 16, 32]
self.output_stride = output_stride
self.with_cp = with_cp
self.norm_cfg = norm_cfg
input_channel = int(32 * widen_factor)
self.stem = self._make_stem_layer(3, input_channel)
self.layer1 = self._make_layer(
input_channel, int(16 * widen_factor), 1, 1, 16, 2)
self.layer2 = self._make_layer(
int(16 * widen_factor), int(24 * widen_factor), 2, 6, 16, 2)
self.layer3 = self._make_layer(
int(24 * widen_factor), int(32 * widen_factor), 3, 6, 24, 2)
self.layer4 = self._make_layer(
int(32 * widen_factor), int(64 * widen_factor), 4, 6, 32, 2)
self.layer5 = self._make_layer(
int(64 * widen_factor), int(96 * widen_factor), 3, 6, 64, 1)
self.layer6 = self._make_layer(
int(96 * widen_factor), int(160 * widen_factor), 3, 6, 96, 1)
self.layer7 = self._make_layer(
int(160 * widen_factor), int(320 * widen_factor), 1, 6, 160, 1)
if self.output_stride == 8:
self.layer2[0].conv2.stride = (1, 1)
self.layer2[0].downsample[0].stride = (1, 1)
self.layer3[0].conv2.stride = (1, 1)
self.layer3[0].downsample[0].stride = (1, 1)
elif self.output_stride == 16:
self.layer3[0].conv2.stride = (1, 1)
self.layer3[0].downsample[0].stride = (1, 1)
self._freeze_stages()
def _make_stem_layer(self, in_channels, stem_channels):
layers = []
layers.append(ConvModule(
in_channels,
stem_channels,
3,
stride=2,
padding=1,
bias=False,
norm_cfg=dict(type='BN', momentum=0.1, eps=1e-5),
activation='relu',
inplace=True))
in_channels = stem_channels
layers.append(ConvModule(
in_channels,
in_channels,
3,
stride=1,
padding=1,
bias=False,
norm_cfg=dict(type='BN', momentum=0.1, eps=1e-5),
activation='relu',
inplace=True))
# add SKNet module
channels = in_channels
mid_channels = channels // 2
squeeze_channels = max(1, mid_channels // 8)
layers.append(
build_plugin_layer(dict(
type='SKConv',
channels=channels,
squeeze_channels=squeeze_channels,
kernel_size=3,
stride=1,
padding=1,
dilation=1,
groups=32,
sk_mode='two',
norm_cfg=dict(type='BN', momentum=0.1, eps=1e-5),
act_cfg=dict(type='ReLU', inplace=True),
),
[build_conv_layer(
dict(type='Conv2d'),
channels,
channels,
kernel_size=3,
stride=1,
padding=1,
bias=False),
build_norm_layer(dict(type='BN', momentum=0.1, eps=1e-5), channels)[1]]))
return nn.Sequential(*layers)
def _make_layer(self,
in_channels,
out_channels,
num_blocks,
stride,
t,
dilation=1):
layers = []
layers.append(InvertedResidual(
in_channels,
out_channels,
stride,
expand_ratio=t,
dilation=dilation,
norm_cfg=self.norm_cfg,
with_cp=self.with_cp))
for i in range(1, num_blocks):
layers.append(InvertedResidual(
out_channels,
out_channels,
1,
expand_ratio=t,
dilation=dilation,
norm_cfg=self.norm_cfg,
with_cp=self.with_cp))
return nn.Sequential(*layers)
def _freeze_stages(self):
if self.output_stride == 8:
self.stem.eval()
for param in self.stem.parameters():
param.requires_grad = False
elif self.output_stride == 16:
self.stem[0].eval()
self.stem[1].eval()
for param in self.stem[0].parameters():
param.requires_grad = False
for param in self.stem[1].parameters():
param.requires_grad = False
```
这样就给`MobileNetV2`主干加入了`SKNet`结构。
阅读全文