DeepLabV3+網絡模型與源碼解讀


源碼鏈接

鏈接: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導出的模型之后后半部分,這個有點蛋疼,暫時就懶得折騰了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2024 CODEPRJ.COM