赞
踩
首先是一个block块
- class InvertedResidual(nn.Module): # 继承nn.Module的类,用于定义Inverted Residual模块。
- def __init__(self, inp, oup, stride, expand_ratio): # expand_ratio 扩展比例(扩展通道数)
- super(InvertedResidual, self).__init__() # 父类初始化
- self.stride = stride # 创建类属性-步幅
- assert stride in [1, 2] # 断言,若stride不等一1或2,则报错,避免参数不合法
-
- hidden_dim = round(inp * expand_ratio) # 计算隐藏层的通道数 round()四舍五入
- self.use_res_connect = self.stride == 1 and inp == oup # 如果当前层的步幅为1,并且输入通道数等于输出通道数,则将 self.use_res_connect 设置为 True,表示使用残差连接。
-
- if expand_ratio == 1: # 若扩展比例为1
- self.conv = nn.Sequential( # 定义一个层
- #--------------------------------------------#
- # 进行3x3的逐层卷积,进行跨特征点的特征提取
- #--------------------------------------------#
- nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False), # 分组卷积(在这里分组数等于输入通道数,相当于逐层卷积)
- BatchNorm2d(hidden_dim), # 归一化
- nn.ReLU6(inplace=True), # 激活函数
- #-----------------------------------#
- # 利用1x1卷积进行通道数的调整
- #-----------------------------------#
- nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
- BatchNorm2d(oup),
- )
- else: # 若扩展比例不为1
- self.conv = nn.Sequential(
- #-----------------------------------#
- # 利用1x1卷积进行通道数的上升
- #-----------------------------------#
- nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False), # 输出通道数为hidden_dim
- BatchNorm2d(hidden_dim),
- nn.ReLU6(inplace=True),
- #--------------------------------------------#
- # 进行3x3的逐层卷积,进行跨特征点的特征提取
- #--------------------------------------------#
- nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
- BatchNorm2d(hidden_dim),
- nn.ReLU6(inplace=True),
- #-----------------------------------#
- # 利用1x1卷积进行通道数的下降
- #-----------------------------------#
- nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
- BatchNorm2d(oup),
- )
-
- def forward(self, x):
- if self.use_res_connect: # 如果使用残差网络
- return x + self.conv(x)
- else:
- return self.conv(x)
一个block块大致就是对输入图像进行通道升维,卷积,再降维的过程,一个基本的模块,MobilenetV2就是由许多这样的模块构成。
- class MobileNetV2(nn.Module): # 定义MobileNetV2类模型
- def __init__(self, n_class=1000, input_size=224, width_mult=1.): # n_class:模型输出的类别数;width_mult:宽度倍率,用于调整通道数
- super(MobileNetV2, self).__init__()
- block = InvertedResidual # block等于上面的类本身
- input_channel = 32 # 模型初始输入通道数为32
- last_channel = 1280 # 模型的最终输出通道数为 1280
定义了block块,设置了一些参数。
- # 定义了模型中不同阶段的网络结构,包括扩展比例(t)、输出通道数(c)、重复次数(n)和步幅(s)(是否进行尺寸压缩)
- interverted_residual_setting = [
- # t, c, n, s
- [1, 16, 1, 1],
- [6, 24, 2, 2],
- [6, 32, 3, 2],
- [6, 64, 4, 2],
- [6, 96, 3, 1],
- [6, 160, 3, 2],
- [6, 320, 1, 1],
- ]
定义了一个列表,代表网络结构。
t:(通道的扩展比例) c:(输出的通道数) n:(模块被重复的次数)
s:(卷积步幅,控制尺寸,步幅大于 1 时,特征图的尺寸会缩小。)
- assert input_size % 32 == 0 # 确保输入尺寸能被32整除,否则报错
- input_channel = int(input_channel * width_mult) # 根据指定的宽度倍率 width_mult 调整输入通道数 input_channel
- # 如果 width_mult 大于 1.0,则将最终输出通道数 last_channel 乘以 width_mult,并将结果转换为整数类型,以更新最终输出通道数。
- # 如果 width_mult 小于等于 1.0,则不对输出通道数进行调整,保持其不变。
- self.last_channel = int(last_channel * width_mult) if width_mult > 1.0 else last_channel
通道倍率说明
- self.features = [conv_bn(3, input_channel, 2)] # features,其中包含了一个使用了 conv_bn 函数的卷积层序列(小网络1)
-
- for t, c, n, s in interverted_residual_setting:
- output_channel = int(c * width_mult)
- for i in range(n):
- if i == 0:
- self.features.append(block(input_channel, output_channel, s, expand_ratio=t))
- else:
- self.features.append(block(input_channel, output_channel, 1, expand_ratio=t))
- input_channel = output_channel
创建features列表来存储网络。第一个网络是conv_bn,如下:
- def conv_bn(inp, oup, stride): # 小网路1(输入通道数,输出通道数,卷积步幅)
- return nn.Sequential(
- nn.Conv2d(inp, oup, 3, stride, 1, bias=False), # 3*3卷积 ,填充1圈
- BatchNorm2d(oup), # 归一化
- nn.ReLU6(inplace=True) # 激活函数
- )
后面利用for循环,以block块为基础构建主体网络。外循环次数为7,每轮的内循环次数为每一个子列表的n的值。内循环每轮第一次都进行设置步长的block块内的卷积,如果步长为2,尺寸会缩小一倍,后续步长都为1,尺寸不变。
输入尺寸为224*224*32,经过上述操作得到输出尺寸为14*14*320
- self.features.append(conv_1x1_bn(input_channel, self.last_channel))# 加一个1*1卷积,输出通道数为1280*width_mult
- self.features = nn.Sequential(*self.features) # 将特征提取部分的各个操作序列整合为一个神经网络模块 nn.Sequential
-
- self.classifier = nn.Sequential( # 模型分类部分,包含一个丢弃层和全连接层
- nn.Dropout(0.2),
- nn.Linear(self.last_channel, n_class),
- )
- self._initialize_weights() # 调用权重初始化函数
- def forward(self, x):
- x = self.features(x)
- x = x.mean(3).mean(2) # 全局平均池化
- x = self.classifier(x)
- return x
- def _initialize_weights(self):
- for m in self.modules(): # 遍历模型所有子模块
- if isinstance(m, nn.Conv2d): # m模块是否为nn.Conv2d
- n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels # 卷积核的高度*宽度*卷积核个数(输出通道),计算了卷积核的参数量
- m.weight.data.normal_(0, math.sqrt(2. / n)) # 对卷积层的权重参数进行初始化
- if m.bias is not None:
- m.bias.data.zero_() # 卷积层偏置项设置为0
- elif isinstance(m, BatchNorm2d): # 批量归一化初始化
- m.weight.data.fill_(1)
- m.bias.data.zero_()
- elif isinstance(m, nn.Linear): # 全连接层初始化设置
- n = m.weight.size(1)
- m.weight.data.normal_(0, 0.01)
- m.bias.data.zero_()
参数初始化函数。
下面打印一下整体的网络结构
- if __name__ == "__main__": # 本模块测试
- model = mobilenetv2()
- for i, layer in enumerate(model.features): # enumerate 函数返回一个迭代器
- print(i, layer)
0 Sequential(
(0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
)
1 InvertedResidual(
(conv): Sequential(
(0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)
(4): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
2 InvertedResidual(
(conv): Sequential(
(0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)
(4): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
3 InvertedResidual(
(conv): Sequential(
(0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144, bias=False)
(4): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
4 InvertedResidual(
(conv): Sequential(
(0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144, bias=False)
(4): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
5 InvertedResidual(
(conv): Sequential(
(0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)
(4): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
6 InvertedResidual(
(conv): Sequential(
(0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)
(4): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
7 InvertedResidual(
(conv): Sequential(
(0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192, bias=False)
(4): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
8 InvertedResidual(
(conv): Sequential(
(0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)
(4): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
9 InvertedResidual(
(conv): Sequential(
(0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)
(4): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
10 InvertedResidual(
(conv): Sequential(
(0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)
(4): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
11 InvertedResidual(
(conv): Sequential(
(0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)
(4): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
12 InvertedResidual(
(conv): Sequential(
(0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)
(4): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
13 InvertedResidual(
(conv): Sequential(
(0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)
(4): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
14 InvertedResidual(
(conv): Sequential(
(0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576, bias=False)
(4): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
15 InvertedResidual(
(conv): Sequential(
(0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)
(4): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
16 InvertedResidual(
(conv): Sequential(
(0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)
(4): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
17 InvertedResidual(
(conv): Sequential(
(0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
(3): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)
(4): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU6(inplace=True)
(6): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1), bias=False)
(7): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
18 Sequential(
(0): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(1280, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU6(inplace=True)
)
可见,一共有19个网络,其中block块有17个,是第2个到第18个。
在另一个deeplabv3_plus.py文件中,定义了一个同名的类。用于对基础的网络进行一些修改,再输出最终结果。
- class MobileNetV2(nn.Module):
- def __init__(self, downsample_factor=8, pretrained=True):
- super(MobileNetV2, self).__init__()
- from functools import partial # functools.partial 函数的作用是创建一个新的函数,通过固定一部分参数来生成一个新的可调用对象。
-
- model = mobilenetv2.mobilenetv2(pretrained) # 导入mobilenetv2文件中的mobilenetv2函数,并进行权重初始化
- self.features = model.features[:-1] # 取出mobilenetv2网络中除去最后块的网络)(取出了18块网络)
-
- self.total_idx = len(self.features) # 网络的长度 ,这里是18
- self.down_idx = [2, 4, 7, 14] # 定义了一个列表,表示特定网络块的索引,在写网络块中进行了下采样
这里降采样倍数设置为8, downsample_factor=8
self.features 包含了上面第0到第17个网络块,第1个到第17个为block块,我将每个block块中3*3卷积部分的步长提取出来如下图:
self.down_idx = [2, 4, 7, 14] 就是步长为2(也就是能使宽高变小(下采样))的网络块下标。
- def _nostride_dilate(self, m, dilate): # 修改卷积层参数的函数,m是模型层对象
- classname = m.__class__.__name__ # m.__class__.__name__ 返回了对象 m 所属类的类名
- if classname.find('Conv') != -1: # 如果能找到类名中包含字符串‘Conv’
- if m.stride == (2, 2): #如果步幅为2,则改为1
- 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): # 如果卷积核大小为 (3, 3),则将膨胀率和填充都设置为 (dilate, dilate)。这一步如果步幅是1,则输出结果尺寸不变
- m.dilation = (dilate, dilate)
- m.padding = (dilate, dilate)
这是修改卷积层参数的函数,如果stride=2,则把stride改为1,同时膨胀率和填充都变为设定值整除2。
如果stride = 1,膨胀率和填充都更改为设定值。
- # 根据给定的膨胀率,修改卷积层的参数
- if downsample_factor == 8: # 如果下采样倍数为8
- for i in range(self.down_idx[-2], self.down_idx[-1]): # i从7-13
- self.features[i].apply(
- partial(self._nostride_dilate, dilate=2) # 调用_nostride_dilate函数,参数固定为2
- )
- for i in range(self.down_idx[-1], self.total_idx): # i从14-17
- 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): # 从14到17
- self.features[i].apply(
- partial(self._nostride_dilate, dilate=2)
- )
代码对卷积层进行修改:如果降采样倍数设置为8,那么第一步i从7遍历到13,对这些块内的3*3卷积进行修改,设定值dilate=2。
假设输入特征图的大小是 W × H,卷积核大小是 K × K,padding 是 P,stride 是 S,dilate 是 D,则输出特征图的大小可以计算为:
输出宽度 = [(W + 2 * P - D * (K - 1) - 1) / S] + 1
输出高度 = [(H + 2 * P - D * (K - 1) - 1) / S] + 1
那么带入函数可知,修改后的7-13网络块,不会对图片尺寸进行改变。(原来7号块会使图像缩小1倍)。
第二步,i从14遍历到17,对这些块内的3*3卷积进行修改,设定值dilate=4。计算可知14-17网络仍不会对图像尺寸改变。(ps:不懂为什么要分开弄)
所以只有2,4对图像进行下采样,再加上第0号网络也进行了一次下采样,所以一共进行了3次下采样,每次下采样倍数为2,三次就是8。
同样downsample_factor=16,会有4次下采样,代码中消除了14号网络的下采样,保留了0、2、4、7号网络进行下采样。
- def forward(self, x):
- low_level_features = self.features[:4](x) # 低层特征(前四层)
- x = self.features[4:](low_level_features) # 深层特征(继续传播)
- return low_level_features, x
最后输出前4块层网络为低级特征,输出所有网络为深层特征,继续进行下一步处理(ASPP)。(但是这里并没有包含去除掉的1*1卷积层,有点疑惑)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。