源碼鏈接:
鏈接:https://pan.baidu.com/s/1GkUM9WiGpzUHuFgBe1t2rA
提取碼:57zr
or
https://github.com/VainF/DeepLabV3Plus-Pytorch
以上兩個連接是一樣的,只不過百度盤中的包含voc數據。
環境安裝:
先裝pytorch&torchvision,再安裝requirments.txt其他依賴
報錯處理:
# error:raise ValueError("cannot allocate more than 256 colors") from e
# solution:將batch_size由16改為4
源碼中,main.py既可以用於訓練,也可以用於測試,命令行參數如下:
1 """ 2 訓練: 3 --model deeplabv3plus_mobilenet 4 --gpu_id 0 5 --year 2012_aug 6 --crop_val 7 --lr 0.01 8 --crop_size 513 9 --batch_size 4 10 --output_stride 16 11 測試: 12 --model deeplabv3plus_mobilenet 13 --gpu_id 0 --year 2012_aug 14 --crop_val 15 --lr 0.01 16 --crop_size 513 17 --batch_size 16 18 --output_stride 16 19 --ckpt checkpoints/best_deeplabv3plus_mobilenet_voc_os16.pth 20 --test_only 21 --save_val_results 22 """
- 一、網絡概述
- 二、BackBone
- 三、Neck:ASPP
- 3.1 空洞卷積
- 3.2 感受野
- 3.3 SPP
- 3.4 ASPP
- 四、Head:DeepLabHead
- 五、性能評估
- 六、模型部署
一、網絡概述
輸入圖像為:N*3*513*513,輸出特征圖為:N*C*513*513(N表示batch_size,C表示分類別數)。網絡主要包含兩部分:
EnCoder:一個BackBone和ASPP
Decoder:特征融合,進一步提取。
對於BackBone可選的有:resnet50、restnet101,mobilenet(v2版本,顯然最快),代碼會自動下載與訓練模型,默認存儲路徑:C:\Users\shiruiyu\.cache\torch\hub\checkpoints 。如下圖,BackBone即:DCNN(以restnet50為例),輸出5組特征:
layer1、layer2、layer3、layer4、layer5;layer1表示low-Level Features,記為B,layer5表示高層特征,記為C。
將C輸入到ASPP中得到A,然后上采樣得到A‘,將B進一步處理得到B’,然后將A‘、B’疊加......
圖1 網絡框架圖
二、BackBone
打開文件modeling.py文件,如下圖:
如上圖,最下面6個函數表述讀取6各種backbone各自的預訓練模型,最上面的_segm_resnet、_segm_mobilenet表示對這6種backbone輸出的多尺度特征圖作處理,具體處理就是:拿出最高層、最低層特征圖。在_deeplab.py文件中,我們以_segm_resnet()函數為例子,將resnet輸出特征圖中的其中兩組重命名為: 'out'(如圖1中B), 'low_level'(如圖1中C),便於后續拿出。
1 def _segm_resnet(name, backbone_name, num_classes, output_stride, pretrained_backbone): 2 3 if output_stride==8: 4 replace_stride_with_dilation=[False, True, True] 5 aspp_dilate = [12, 24, 36] # 空洞卷積倍率 6 else: 7 replace_stride_with_dilation=[False, False, True] 8 aspp_dilate = [6, 12, 18] # 如圖1,空洞卷積倍率 9 10 backbone = resnet.__dict__[backbone_name]( 11 pretrained=pretrained_backbone, 12 replace_stride_with_dilation=replace_stride_with_dilation) 13 14 inplanes = 2048 15 low_level_planes = 256 16 17 if name=='deeplabv3plus': 18 # eg:resnet輸出5個尺度的特征圖:layer1 layer2 layer3 layer4 layer5 19 # low_level:對應框架圖中的B 20 # out:對應框架圖中的C 21 return_layers = {'layer4': 'out', 'layer1': 'low_level'} 22 classifier = DeepLabHeadV3Plus(inplanes, low_level_planes, num_classes, aspp_dilate) 23 elif name=='deeplabv3': 24 return_layers = {'layer4': 'out'} 25 classifier = DeepLabHead(inplanes , num_classes, aspp_dilate) 26 # 提取網絡的第幾層輸出結果並給一個別名 27 backbone = IntermediateLayerGetter(backbone, return_layers=return_layers) 28 29 model = DeepLabV3(backbone, classifier) 30 return model
上面已經解釋了如果拿到特征C、B,如下圖,C由ASPP得到A,A經過4倍上采樣得到A‘。B經過處理后得到B’,然后和A一起concat,然后作后面的處理。
三、Neck:ASPP
3.1 空洞卷積
越難預測的樣本,往往需要更加全局的信息,空洞卷積提取大視野特征,可解決這個問題。
略
2014年 FCN
Xxxx年 DeepLabV3+(增加空洞卷積,增加感受野)
空洞卷積的優勢:
- 圖像分割任務中(其他場景也適用)需要較大感受野來更好完成任務
- 通過設置dilation rate參數來完成空洞卷積,並沒有額外計算
- 可以按照參數擴大任意倍數的感受野,而且沒有引入額外的參數
- 應用簡單,就是卷積層中多設置一個參數就可以了
3.2 感受野
3.3 SPP(Spital pyramid pooling)
我在博客:yolov5中講解過:https://www.cnblogs.com/winslam/p/14452136.html 但是當時理解深度不夠,這里補充下:當網絡中有FC層,此時輸入圖像分辨率必須是固定的;而當網絡FC前接一個SPP層后,則輸入圖像分辨率將不在有任何限制。如下圖,任意分辨率的圖像經過卷積層后,分三條路走,分別是經過4*4、2*2、1*1的pooling,將得到16*256、4*256、1*256的特征圖,然后concat一起,得到(16+4+1)*256的特征圖,后續連接FC層。
3.4 ASPP(atrous conv SPP)
ASPP差不多就是將SPP中的Pooling換成了空洞卷積,在文件_deeplab.py中,類ASPP代碼如下:紅色注釋對應下圖中ASPP的5個步驟:
1 # 如上圖:輸入特征C,輸出特征A 2 class ASPP(nn.Module): 3 def __init__(self, in_channels, atrous_rates): 4 super(ASPP, self).__init__() 5 out_channels = 256 6 modules = [] 7 # 1×1 Conv 8 modules.append(nn.Sequential( 9 nn.Conv2d(in_channels, out_channels, 1, bias=False), 10 nn.BatchNorm2d(out_channels), 11 nn.ReLU(inplace=True))) 12 13 rate1, rate2, rate3 = tuple(atrous_rates) 14 # 3×3 Conv rate6 15 modules.append(ASPPConv(in_channels, out_channels, rate1)) 16 # 3×3 Conv rate12 17 modules.append(ASPPConv(in_channels, out_channels, rate2)) 18 # 3×3 Conv rate18 19 modules.append(ASPPConv(in_channels, out_channels, rate3)) 20 # Image Pooling 21 modules.append(ASPPPooling(in_channels, out_channels)) 22 23 self.convs = nn.ModuleList(modules) 24 25 self.project = nn.Sequential( 26 nn.Conv2d(5 * out_channels, out_channels, 1, bias=False), 27 nn.BatchNorm2d(out_channels), 28 nn.ReLU(inplace=True), 29 nn.Dropout(0.1),) 30 31 def forward(self, x): 32 res = [] 33 for conv in self.convs: 34 #print(conv(x).shape) 35 res.append(conv(x)) 36 res = torch.cat(res, dim=1) 37 return self.project(res)
四、Head:DeepLabHead
網絡的head部分寫在_deeplab.py文件中的類DeepLabHeadV3Plus,從代碼看,Head部分包括如下圖藍圈部分,即:由B、C得到A‘、B’,之后concat+conv得到D,網絡最后的“UpSample by 4”在文件utils.py中的類_SimpleSegmentationModel。
1 class DeepLabHeadV3Plus(nn.Module): 2 def __init__(self, in_channels, low_level_channels, num_classes, aspp_dilate=[12, 24, 36]): 3 super(DeepLabHeadV3Plus, self).__init__() 4 self.project = nn.Sequential( 5 nn.Conv2d(low_level_channels, 48, 1, bias=False), # 實驗證明48比64好 6 nn.BatchNorm2d(48), 7 nn.ReLU(inplace=True), 8 ) 9 10 self.aspp = ASPP(in_channels, aspp_dilate) 11 12 self.classifier = nn.Sequential( 13 nn.Conv2d(304, 256, 3, padding=1, bias=False), 14 nn.BatchNorm2d(256), 15 nn.ReLU(inplace=True), 16 nn.Conv2d(256, num_classes, 1) 17 ) 18 self._init_weight() 19 20 def forward(self, feature): 21 # feature:見modeling.py文件中第28行 22 # low_level:對應上圖中的B 23 # out:對應上圖中的C 24 # 25 # B -> B‘ 26 low_level_feature = self.project( feature['low_level'] )#return_layers = {'layer4': 'out', 'layer1': 'low_level'} 27 #print(low_level_feature.shape) 28 # ASSP:C -> A 29 output_feature = self.aspp(feature['out']) 30 #print(output_feature.shape) 31 # (UpSample by 4):A -> A' 32 output_feature = F.interpolate(output_feature, size=low_level_feature.shape[2:], mode='bilinear', align_corners=False) 33 #print(output_feature.shape) 34 # concat(A',B') & 3*3 Conv 35 return self.classifier( torch.cat( [ low_level_feature, output_feature ], dim=1 ) ) 36 37 def _init_weight(self): 38 for m in self.modules(): 39 if isinstance(m, nn.Conv2d): 40 nn.init.kaiming_normal_(m.weight) 41 elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): 42 nn.init.constant_(m.weight, 1) 43 nn.init.constant_(m.bias, 0)
五、性能評估
學習下得了
六、性能評估
由於代碼使用了預訓練模型,例如mobilenet resnet Xception等作為BackBone提取特征,這部分網絡沒有在代碼中定義,代碼中定義的只有ASSP和EnCoder部分,所以torch_script導出的模型之后后半部分,這個有點蛋疼,暫時就懶得折騰了。