深度學習模型元件 ------ 深度可分離卷積、瓶頸層Bottleneck、CSP瓶頸層BottleneckCSP、ResNet模組、SPP空間金字塔池化模組
YOLOv5 元件
目錄
- 1、標準卷積: Conv + BN + activate
- 2、DWConv深度可分離卷積
- 3、Bottleneck瓶頸層
- 4、BottleneckCSP-CSP瓶頸層
- 5、ResNet模組
- 6、SPP空間金字塔池化模組
1、標準卷積: Conv + BN + activate
class Conv(nn.Module): # Standard convolution # ch_in, ch_out, kernel, stride, padding, groups def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): super(Conv, self).__init__() self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) self.bn = nn.BatchNorm2d(c2) self.act = nn.Hardswish() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) def forward(self, x): return self.act(self.bn(self.conv(x))) def fuseforward(self, x): return self.act(self.conv(x))
引數說明:
-
g:groups,通道分組的引數,輸入通道數、輸出通道數必須同時滿足被groups整除;
groups: 如果輸出通道為6,輸入通道也為6,假設groups為3,卷積核為 1x1 ; 則卷積核的shape為2x1x1,即把輸入通道分成了3份;那麼卷積核的個數呢?之前是由輸出通道決定的,這裡也一樣,輸出通道為6,那麼就有6個卷積核!這裡實際上是將卷積核也平分為groups份,在groups份特徵圖上計算,以輸入、輸出都為6為例,每個2xhxw的特徵圖子層就有且僅有2個卷積核,最後相加恰好是6。這裡可以起到的作用是不同通道分別計算特徵!
2、DWConv深度可分離卷積
def DWConv(c1, c2, k=1, s=1, act=True):
# Depthwise convolution
return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
這裡的深度可分離卷積,主要是將通道按輸入輸出的最大公約數進行切分,在不同的通道圖層上進行特徵學習!
關於深度可分離卷積的更早資料參考:我的github
3、Bottleneck瓶頸層
class Bottleneck(nn.Module): # Standard bottleneck def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion super(Bottleneck, self).__init__() c_ = int(c2 * e) # hidden channels self.cv1 = Conv(c1, c_, 1, 1) self.cv2 = Conv(c_, c2, 3, 1, g=g) self.add = shortcut and c1 == c2 def forward(self, x): return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
引數說明:
- c1:bottleneck 結構的輸入通道維度;
- c2:bottleneck 結構的輸出通道維度;
- shortcut:是否給bottleneck 結構新增shortcut連線,新增後即為ResNet模組;
- g:groups,通道分組的引數,輸入通道數、輸出通道數必須同時滿足被groups整除;
- e:expansion: bottleneck 結構中的瓶頸部分的通道膨脹率,使用0.5即為變為輸入的\(\frac{1}{2}\);
模型結構:
這裡的瓶頸層,瓶頸主要體現在通道數channel上面!一般1x1
卷積具有很強的靈活性,這裡用於降低通道數,如上面的膨脹率為0.5,若輸入通道為640,那麼經過1x1
的卷積層之後變為320;經過3x3
之後變為輸出的通道數,這樣引數量會大量減少!
這裡的shortcut即為圖中的紅色虛線,在實際中,shortcut(捷徑)不一定是上面都不操作,也有可能有卷積處理,但此時,另一支一般是多個ResNet模組串聯而成!這裡使用的shortcut也成為identity分支,可以理解為恆等對映,另一個分支被稱為殘差分支(Residual分支)。
我們常使用的殘差分支實際上是1x1
+3x3
+1x1
的結構!
4、BottleneckCSP-CSP瓶頸層
class BottleneckCSP(nn.Module):
# CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
# ch_in, ch_out, number, shortcut, groups, expansion
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
super(BottleneckCSP, self).__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
self.cv4 = Conv(2 * c_, c2, 1, 1)
self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3)
self.act = nn.LeakyReLU(0.1, inplace=True)
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
def forward(self, x):
y1 = self.cv3(self.m(self.cv1(x)))
y2 = self.cv2(x)
return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))
引數說明:
- c1:BottleneckCSP 結構的輸入通道維度;
- c2:BottleneckCSP 結構的輸出通道維度;
- n:bottleneck 結構 結構的個數;
- shortcut:是否給bottleneck 結構新增shortcut連線,新增後即為ResNet模組;
- g:groups,通道分組的引數,輸入通道數、輸出通道數必須同時滿足被groups整除;
- e:expansion: bottleneck 結構中的瓶頸部分的通道膨脹率,使用0.5即為變為輸入的\(\frac{1}{2}\);
- torch.cat((y1, y2), dim=1):這裡是指定在第\(1\)個維度上進行合併,即在channel維度上合併;
- c_:BottleneckCSP 結構的中間層的通道數,由膨脹率e決定。
模型結構:
CSP瓶頸層結構在Bottleneck部分存在一個可修改的引數n,標識使用的Bottleneck結構個數!這一條也是我們的主分支,是對殘差進行學習的主要結構,右側分支nn.Conv2d
實際上是shortcut分支實現不同stage的連線。
5、ResNet模組
殘差模組是深度神經網路中非常重要的模組,在建立模型的過程中經常被使用。
殘差模組結構如其名,實際上就是shortcut的直接應用,最出名的殘差模組應用這樣的:
左邊這個結構即Bottleneck結構,也叫瓶頸殘差模組!右邊的圖片展示的是基本的殘差模組!
6、SPP空間金字塔池化模組
class SPP(nn.Module):
# Spatial pyramid pooling layer used in YOLOv3-SPP
def __init__(self, c1, c2, k=(5, 9, 13)):
super(SPP, self).__init__()
c_ = c1 // 2 # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
def forward(self, x):
x = self.cv1(x)
return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
SPP即為空間金字塔池化模組!上面的程式碼是yolov5的模型程式碼,視覺化為:
三種池化核,padding都是根據核的大小自適應,保證池化後的特徵圖[H, W]保持一致!
完!