SSD與vgg目標檢測網絡原理


目錄:

一、SSD

二、基於SSD的極速人臉檢測

三、VGG

 

一、SSD

SSD主干網絡結構(SSD是一個多級分類網絡)

圖1  ssd主干網絡結構圖

ssd中的vgg-19網絡:

  SSD采用的主干網絡是VGG網絡,關於VGG的介紹大家可以看我的另外一篇博客https://blog.csdn.net/weixin_44791964/article/details/102779878,這里的VGG網絡相比普通的VGG網絡有一定的修改,主要修改的地方就是:

  • 1、將VGG16的FC6和FC7層轉化為卷積層。
  • 2、去掉所有的Dropout層和FC8層;
  • 3、新增了Conv6、Conv7、Conv8、Conv9。

如圖所示,輸入的圖片經過了改進的VGG網絡(Conv1->fc7)和幾個另加的卷積層(Conv6->Conv9),進行特征提取:
a、輸入一張圖片后,被resize到300x300的shape

b、conv1,經過兩次[3,3]卷積網絡,輸出的特征層為64,輸出為(300,300,64),再2X2最大池化,該最大池化步長為2,輸出net為(150,150,64)。

c、conv2,經過兩次[3,3]卷積網絡,輸出的特征層為128,輸出net為(150,150,128),再2X2最大池化,該最大池化步長為2,輸出net為(75,75,128)。

d、conv3,經過三次[3,3]卷積網絡,輸出的特征層為256,輸出net為(75,75,256),再2X2最大池化,該最大池化步長為2,輸出net為(38,38,256)。

e、conv4,經過三次[3,3]卷積網絡,輸出的特征層為512,輸出net為(38,38,512),再2X2最大池化,該最大池化步長為2,輸出net為(19,19,512)。

f、conv5,經過三次[3,3]卷積網絡,輸出的特征層為512,輸出net為(19,19,512),再3X3最大池化,該最大池化步長為1,輸出net為(19,19,512)。

g、利用卷積代替全連接層,進行了一次[3,3]卷積網絡和一次[1,1]卷積網絡,分別為fc6和fc7,輸出的通道數為1024,因此輸出的net為(19,19,1024)。

(從這里往前都是VGG的結構)

h、conv6,經過一次[1,1]卷積網絡,調整通道數,一次步長為2的[3,3]卷積網絡,輸出的通道數為512,因此輸出的net為(10,10,512)。

i、conv7,經過一次[1,1]卷積網絡,調整通道數,一次步長為2的[3,3]卷積網絡,輸出的通道數為256,因此輸出的net為(5,5,256)。

j、conv8,經過一次[1,1]卷積網絡,調整通道數,一次padding為valid的[3,3]卷積網絡,輸出的通道數為256,因此輸出的net為(3,3,256)。

k、conv9,經過一次[1,1]卷積網絡,調整通道數,一次padding為valid的[3,3]卷積網絡,輸出的特征層為256,因此輸出的net為(1,1,256)。

 

下面直接上代碼(項目地址:https://github.com/bubbliiiing/ssd-pytorch):

文件vgg.py中(ssd算法對vgg-16的修改):

  代碼中,如下,有個參數ceil_mode,設置為false、true的區別見下圖:

layers += [nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)]

 

 1 import torch.nn as nn
 2 from torchvision.models.utils import load_state_dict_from_url
 3 
 4 
 5 '''
 6 該代碼用於獲得VGG主干特征提取網絡的輸出。
 7 輸入變量i代表的是輸入圖片的通道數,通常為3。
 8 
 9 300, 300, 3 -> 300, 300, 64 -> 300, 300, 64 -> 150, 150, 64 -> 150, 150, 128 -> 150, 150, 128 -> 75, 75, 128 ->
10 75, 75, 256 -> 75, 75, 256 -> 75, 75, 256 -> 38, 38, 256 -> 38, 38, 512 -> 38, 38, 512 -> 38, 38, 512 -> 19, 19, 512 ->
11 19, 19, 512 -> 19, 19, 512 -> 19, 19, 512 -> 19, 19, 512 -> 19, 19, 1024 -> 19, 19, 1024
12 
13 # SSD 中需要從VGG提取兩個特征圖(見SSD主干網絡結構)
14 38, 38, 512的序號是22
15 19, 19, 1024的序號是34
16 '''
17 '''
18 卷積通道數量
19 '''
20 base = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'C', 512, 512, 512, 'M',
21             512, 512, 512]
22 
23 def vgg(pretrained = False):
24     layers = []
25     in_channels = 3
26     for v in base:
27         if v == 'M':
28             layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
29         elif v == 'C':
30             layers += [nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)]
31         else:
32             conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
33             layers += [conv2d, nn.ReLU(inplace=True)]
34             in_channels = v
35     # 19, 19, 512 -> 19, 19, 512 
36     pool5 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
37     # 19, 19, 512 -> 19, 19, 1024
38     conv6 = nn.Conv2d(512, 1024, kernel_size=3, padding=6, dilation=6)
39     # 19, 19, 1024 -> 19, 19, 1024
40     conv7 = nn.Conv2d(1024, 1024, kernel_size=1)
41     layers += [pool5, conv6,
42                nn.ReLU(inplace=True), conv7, nn.ReLU(inplace=True)]
43 
44     model = nn.ModuleList(layers)
45     if pretrained:
46         state_dict = load_state_dict_from_url("https://download.pytorch.org/models/vgg16-397923af.pth", model_dir="./model_data")
47         state_dict = {k.replace('features.', '') : v for k, v in state_dict.items()}
48         model.load_state_dict(state_dict, strict = False)
49     return model
50 
51 if __name__ == "__main__":
52     net = vgg()
53     for i, layer in enumerate(net):
54         print(i, layer)

文件ssd.py中:

  見圖下ssd主干網絡結構圖,vgg中輸出兩個38*38*512和19*19*1024的特征圖(圖中的block6、7、8、9和代碼中的注釋的block一一對應):

 1 def add_extras(in_channels, backbone_name):
 2     layers = []
 3     if backbone_name == 'vgg':
 4         # Block 6
 5         # 19,19,1024 -> 19,19,256 -> 10,10,512
 6         layers += [nn.Conv2d(in_channels, 256, kernel_size=1, stride=1)]
 7         layers += [nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1)]
 8 
 9         # Block 7
10         # 10,10,512 -> 10,10,128 -> 5,5,256
11         layers += [nn.Conv2d(512, 128, kernel_size=1, stride=1)]
12         layers += [nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1)]
13 
14         # Block 8
15         # 5,5,256 -> 5,5,128 -> 3,3,256
16         layers += [nn.Conv2d(256, 128, kernel_size=1, stride=1)]
17         layers += [nn.Conv2d(128, 256, kernel_size=3, stride=1)]
18         
19         # Block 9
20         # 3,3,256 -> 3,3,128 -> 1,1,256
21         layers += [nn.Conv2d(256, 128, kernel_size=1, stride=1)]
22         layers += [nn.Conv2d(128, 256, kernel_size=3, stride=1)]
23     else:
24         layers += [InvertedResidual(in_channels, 512, stride=2, expand_ratio=0.2)]
25         layers += [InvertedResidual(512, 256, stride=2, expand_ratio=0.25)]
26         layers += [InvertedResidual(256, 256, stride=2, expand_ratio=0.5)]
27         layers += [InvertedResidual(256, 64, stride=2, expand_ratio=0.25)]
28         
29     return nn.ModuleList(layers)

由上圖可知,我們分別取:

  • conv4_3的第三次卷積的特征;
  • fc7卷積的特征;
  • conv6的第二次卷積的特征(block6);
  • conv7的第二次卷積的特征(block7)
  • conv8的第二次卷積的特征(block8)
  • conv9的第二次卷積的特征(block9)

共六個特征層進行下一步的處理。為了和普通特征層區分,我們稱之為有效特征層,來獲取預測結果。

對獲取到的每一個有效特征層,我們都需要對其做兩個操作,分別是:

  • 一次卷積得到channel維度為:num_anchors x 4的featureMap(用於回歸預測候選框的 x y w h)
  • 一次卷積得到channel維度為:num_anchors x num_classes的featureMap(用於分類預測候選框的置信度)

  而num_anchors指的是該特征層每一個特征點所擁有的先驗框數量。上述提到的六個特征層,每個特征層的每個特征點對應的先驗框數量分別為4、6、6、6、4、4。上述操作分別對應的對象為:

num_anchors x 4的卷積 用於預測 該特征層上 每一個網格點上 每一個先驗框的變化情況。所以有:(4、6、6、6、4、4)*4 = (16、24、24、24、16、16)
num_anchors x num_classes的卷積 用於預測 該特征層上 每一個網格點上 每一個預測對應的種類。(4、6、6、6、4、4)*21 = (84、126、126、126、84、84)
所有的特征層對應的預測結果的shape如下:

  1 class SSD300(nn.Module):
  2     def __init__(self, num_classes, backbone_name, pretrained = False):
  3         super(SSD300, self).__init__()
  4         self.num_classes    = num_classes
  5         if backbone_name    == "vgg":
  6             self.vgg        = add_vgg(pretrained)
  7             self.extras     = add_extras(1024, backbone_name)
  8             self.L2Norm     = L2Norm(512, 20)
  9             mbox            = [4, 6, 6, 6, 4, 4]
 10             
 11             loc_layers      = []
 12             conf_layers     = []
 13             backbone_source = [21, -2]
 14             #---------------------------------------------------#
 15             #   在add_vgg獲得的特征層里
 16             #   第21層和-2層可以用來進行回歸預測和分類預測。
 17             #   分別是conv4-3(38,38,512)和conv7(19,19,1024)的輸出
 18             #---------------------------------------------------#
 19             for k, v in enumerate(backbone_source):
 20                 loc_layers  += [nn.Conv2d(self.vgg[v].out_channels, mbox[k] * 4, kernel_size = 3, padding = 1)]
 21                 conf_layers += [nn.Conv2d(self.vgg[v].out_channels, mbox[k] * num_classes, kernel_size = 3, padding = 1)]
 22             #-------------------------------------------------------------#
 23             #   在add_extras獲得的特征層里
 24             #   第1層、第3層、第5層、第7層可以用來進行回歸預測和分類預測。
 25             #   shape分別為(10,10,512), (5,5,256), (3,3,256), (1,1,256)
 26             #-------------------------------------------------------------#  
 27             for k, v in enumerate(self.extras[1::2], 2):
 28                 loc_layers  += [nn.Conv2d(v.out_channels, mbox[k] * 4, kernel_size = 3, padding = 1)]
 29                 conf_layers += [nn.Conv2d(v.out_channels, mbox[k] * num_classes, kernel_size = 3, padding = 1)]
 30         else:
 31             self.mobilenet  = mobilenet_v2(pretrained).features
 32             self.extras     = add_extras(1280, backbone_name)
 33             self.L2Norm     = L2Norm(96, 20)
 34             mbox            = [6, 6, 6, 6, 6, 6]
 35 
 36             loc_layers      = []
 37             conf_layers     = []
 38             backbone_source = [13, -1]
 39             for k, v in enumerate(backbone_source):
 40                 loc_layers  += [nn.Conv2d(self.mobilenet[v].out_channels, mbox[k] * 4, kernel_size = 3, padding = 1)]
 41                 conf_layers += [nn.Conv2d(self.mobilenet[v].out_channels, mbox[k] * num_classes, kernel_size = 3, padding = 1)]
 42             for k, v in enumerate(self.extras, 2):
 43                 loc_layers  += [nn.Conv2d(v.out_channels, mbox[k] * 4, kernel_size = 3, padding = 1)]
 44                 conf_layers += [nn.Conv2d(v.out_channels, mbox[k] * num_classes, kernel_size = 3, padding = 1)]
 45 
 46         self.loc            = nn.ModuleList(loc_layers)
 47         self.conf           = nn.ModuleList(conf_layers)
 48         self.backbone_name  = backbone_name
 49         
 50     def forward(self, x):
 51         #---------------------------#
 52         #   x是300,300,3
 53         #---------------------------#
 54         sources = list()
 55         loc     = list()
 56         conf    = list()
 57 
 58         #---------------------------#
 59         #   獲得conv4_3的內容
 60         #   shape為38,38,512
 61         #---------------------------#
 62         if self.backbone_name == "vgg":
 63             for k in range(23):
 64                 x = self.vgg[k](x)
 65         else:
 66             for k in range(14):
 67                 x = self.mobilenet[k](x)
 68         #---------------------------#
 69         #   conv4_3的內容
 70         #   需要進行L2標准化
 71         #---------------------------#
 72         s = self.L2Norm(x)
 73         sources.append(s)
 74 
 75         #---------------------------#
 76         #   獲得conv7的內容
 77         #   shape為19,19,1024
 78         #---------------------------#
 79         if self.backbone_name == "vgg":
 80             for k in range(23, len(self.vgg)):
 81                 x = self.vgg[k](x)
 82         else:
 83             for k in range(14, len(self.mobilenet)):
 84                 x = self.mobilenet[k](x)
 85 
 86         sources.append(x)
 87         #-------------------------------------------------------------#
 88         #   在add_extras獲得的特征層里
 89         #   第1層、第3層、第5層、第7層可以用來進行回歸預測和分類預測。
 90         #   shape分別為(10,10,512), (5,5,256), (3,3,256), (1,1,256)
 91         #-------------------------------------------------------------#      
 92         for k, v in enumerate(self.extras):
 93             x = F.relu(v(x), inplace=True)
 94             if self.backbone_name == "vgg":
 95                 if k % 2 == 1:
 96                     sources.append(x)
 97             else:
 98                 sources.append(x)
 99 
100         #-------------------------------------------------------------#
101         #   為獲得的6個有效特征層添加回歸預測和分類預測
102         #-------------------------------------------------------------#      
103         for (x, l, c) in zip(sources, self.loc, self.conf):
104             loc.append(l(x).permute(0, 2, 3, 1).contiguous())
105             conf.append(c(x).permute(0, 2, 3, 1).contiguous())
106 
107         #-------------------------------------------------------------#
108         #   進行reshape方便堆疊
109         #-------------------------------------------------------------#  
110         loc     = torch.cat([o.view(o.size(0), -1) for o in loc], 1)
111         conf    = torch.cat([o.view(o.size(0), -1) for o in conf], 1)
112         #-------------------------------------------------------------#
113         #   loc會reshape到batch_size, num_anchors, 4
114         #   conf會reshap到batch_size, num_anchors, self.num_classes
115         #-------------------------------------------------------------#     
116         output = (
117             loc.view(loc.size(0), -1, 4),
118             conf.view(conf.size(0), -1, self.num_classes),
119         )
120         return output

  得到不同尺度每個網格中先驗框的位置、置信度之后,后續訓練和前向傳播部署(YOLO的目標函數是將框的位置和置信度寫在一個目標函數中,SSD也是),我就不多BB了,和yolo系列差不多。

   然后就是超參數有哪些????

VGG :

  如下圖:以VGG-16為例子,特點是全是3*3卷積,參數相對少。然后maxpool后面必然會對featureMap的channel維度進行升維。因為maxpool會導致特征圖信息丟失,所以后面接一個升維。

   如下左圖,VGG-34比VGG-18訓練、測試效果都差(沒有給全),不是過擬合導致,而是層數加深,網絡學偏了。ResNet的加入后,有明顯的改觀。

  • conv:特提特征,3*3提取得比5*5細粒度更細
  • relu:增加網絡非線性
  • maxpool:降采樣

反向傳播:

      就一個鏈式求導+梯度下降。

參考:https://blog.csdn.net/weixin_38347387/article/details/82936585

      對於反向傳播,真的不難。例如,對於某一次迭代,得到最終的loss,此時,已知中間網絡任何一層輸入、輸出,包括每一層具體參數。對於第i層的權重參數wi,我們是可以求得loss對它的偏導f’,於是參數更新公式為:w = w – n*f’,其中n為學習率。

(思考:對於Focal loss,分類的錯誤的樣本,loss大,而分類正確的loss值,loss小。Loss大,則一階導數大,導致網絡權重增量大,網絡收斂更快。)

 

參考:https://blog.csdn.net/weixin_44791964/article/details/104981486

余思琪人臉檢測:https://mp.weixin.qq.com/s?__biz=MzIyMDY2MTUyNg==&mid=2247483818&idx=1&sn=12e14dfd5154b35f14575d876785da76&chksm=97c9d3d3a0be5ac5a4e5cb05b23dd90bb73d57c2b3be6a70a139d8c28391a6185ec6297b6da0&scene=21#wechat_redirect

 

https://mp.weixin.qq.com/s?__biz=MzIzNTU5MzA1OQ==&mid=2247483937&idx=1&sn=df27185114ca8c4d5db55a65b26aed69&chksm=e8e58e2ddf92073b162e75c1525b85dbad2551893889d8a5d9dde29baf58a16e91fd5c263e64&mpshare=1&scene=21&srcid=&sharer_sharetime=1583410558996&sharer_shareid=8457d4b9d590baced734dd1ede727998&exportkey=AYPFSxpVXLxn1WLk54RH1VQ=&pass_ticket=FoKnbqhyZbDrumsm%20Z7QDd8kcPLIpaufOB8LggwGl71QJQygeG0J45UJB52tqXSg#wechat_redirect

 

https://mp.weixin.qq.com/s?__biz=MzIyMDY2MTUyNg%3D%3D&chksm=97c9d060a0be5976d73484081239c3b5acd346fdd32acdfe47ab9a5407e05470e8c050f628c8&idx=1&mid=2247483929&scene=21&sn=d453f0ebfa06289cade4d37a824519b6#wechat_redirect

 


免責聲明!

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



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