ICNet
一. ICNet网络结构编写
- ICNet先将图片分别下采样得到x_sub1, x_sub2, x_sub4三个不同大小的特征图, 再将这三个特征图输入ICHead(包含CFF) 得到四个不同大小的预测结果图。
- Cityscapes数据集的dataset和采样器的制作移步:Cityscapes数据集采样器制作
x_sub1 (1/8,channels:64)
三个ConvBNRLU模块,将图片下采样至1/8
self.conv_sub1 = nn.Sequential(
_ConvBNRLU(3, 32, 3, 2, **kwargs),
_ConvBNRLU(32, 32, 3,2, **kwargs),
_ConvBNRLU(32, 64, 3, 2, **kwargs)
)
x_sub1 = self.conv_sub1(x)
x_sub2 ( 1/16,channels:512)
- 将原图线性插值至1/2
x_sub2 = F.interpolate(x, scale_factor=0.5, mode='bilinear', align_corners=True)
- layer0 + layer1 + layer2 三个模块累计下采样至1/16
x_sub2 = self.resnet.layer0(x_sub2) #1/4
x_sub2 = self.resnet.layer1(x_sub2)
x_sub2 = self.resnet.layer2(x_sub2) #1/2
x_sub4 ( 1/32,channels:2048)
- 将原图线性插值至1/4
x_sub4 = F.interpolate(x, scale_factor=0.25, mode='bilinear', align_corners=True)
- layer0 + layer1 + layer2 + layer3 + layer4 五个模块累计下采样至1/32
x_sub4 = self.resnet.layer0(x_sub4)
x_sub4 = self.resnet.layer1(x_sub4)
x_sub4 = self.resnet.layer2(x_sub4) #1/2
x_sub4 = self.resnet.layer3(x_sub4) #1/2
x_sub4 = self.resnet.layer4(x_sub4) #1/2
- PPM金字塔池化模块
x_sub4 = self.ppm(x_sub4)
CFF模块(size, channels)
- x_sub4(1/32, 2048)插值至(1/16, 2048),再卷积至 A:(1/16, 128)
- x_sub2(1/16, 512)卷积至 B:(1/16, 128)
- 将上两步的结果A和B叠加得到 x_cff_24:(1/16, 128)
- x_cff_24(1/16, 128)插值至(1/8, 128),再卷积至 C:(1/8, 128)
- x_sub1(1/8, 64)卷积至 D:(1/8, 128)
- 将上两步的C和D叠加得到 x_cff_12
ICHead
1/16 结果图
将A(1/16, 128) 卷积至:(1/16, nclass)
1/8结果图
将C(1/8, 128) 卷积至:(1/8, nclass)
1/4结果图
将x_cff_12插值至:(1/4, 128), 再卷积至 (1/4, nclass)
1结果图
将1/4结果图再插值至:(1, nclass)
返回结果
上诉的四张结果图就是ICNet网络返回的预测结果
二. 网络结构细节
layers
- 每个layer都由不同数量的block堆叠而成
- 每个layer的stride只在第一个block里实现,也就是说:第一个以外的其他block的stride都为1
- 每个layer的通道数的转换只在第一个block里实现,也就是说:第一个以外的其他block的in_channels和out_channels是相同的
block_num=[3,4,6,3]
self.layer1 = self._make_layer(64, 256, block_num[0])
self.layer2 = self._make_layer(256, 512, block_num[1], stride=2)
self.layer3 = self._make_layer(512, 1024, block_num[2], stride=2)
self.layer4 = self._make_layer(1024, 2048, block_num[3], stride=2)
block(Bottleneck)
- 每个block都由三个卷积层 + 一个残差边 构成
- 由layers部分的讲解你会发现,只有每个layers的第一个block的残差边(skip_connection)不是恒等的,既需要改变size(如果该layer的stride !=1),也需要改变channels
- 只在第二个卷积层才实现size大小的变化
self.conv1: (in_channels, out_channels/4, 1)
self.conv2: (out_channels/4, out_channels/4, 3, stride)
self.conv3: (out_channels/4, out_channels, 1)
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
identity = self.skip_connection(x)
out += identity
out = self.relu(out)
return out
PPM金字塔池化
- 这是一个简陋的金字塔池化
- 只有x_sub4进行了金字塔池化,池化前池化后size和channels都没变
- 先将x_sub4 分别平均池化成:1x1, 2x2, 3x3, 6x6 的大小,再线性插值回x_sub4的大小
- 将原来的x_sub4 和4份池化再插值后的特征图进行叠加,得到新的x_sub4
三. 实验细节
数据集 | Cityscapes |
---|---|
batchsize | 16 |
base_lr | 0.01 |
pl_lr_power | 0.9 |
iter | 30k |
momentum | 0.9 |
weight_decay | 0.0001 |
\(\lambda_{1,2,3}\) | 0.4,0.4,1 |