第六週學習任務
一.Selective Kernel Networks
1.介紹:
SKNet是SENet的加強版,讓每一個神經元能夠動態的調整感受野的大小。
2.大體流程:
1.Split
首先對原本C*H*W的輸入經過多個(自己定義,圖中為3個)大小不同的卷積核得到U1,U2,U3三個特徵圖。
注意,這裡涉及了空洞卷積的內容。
2.Fuse
為了融合多個感受野的資訊,這裡得出U=U1+U2+U3的特徵圖。
之後在H,W維度求平局值,得到一個關於channel資訊的C*1*1的一維向量,該向量代表通道的重要程度。
最後用一個線性變換將原本的C維對映成為d維的資訊,進行資訊的抽取。(???)
其中L預設設定為32。
3.Select
首先用三個線性變換,將Z維恢復為C維,讓後使用softmax對c維進行歸一化,這時每個channel對應一個分數,代表channal的重要程度。
之後將得到的三個值分別乘以對應的U1,U2,U3,得到A1,A2,A3,然後將其相加,進行特徵融合,的到最後的融合多個感受野資訊的模組A。
import torch import torch.nn as nn import torch.nn.functional as F class SKConv(nn.Module): def __init__(self,in_channels,r=16,L=32): nn.Conv2d super(SKConv,self).__init__() self.in_channels = in_channels d = max(round(in_channels/r),L) self.conv_A = nn.Conv2d(in_channels,in_channels,3,stride=1,padding=1,groups=32,bias=False) self.bn_A = nn.BatchNorm2d(in_channels) self.conv_B = nn.Conv2d(in_channels,in_channels,3,stride=1,padding=2,dilation=1,groups=32,bias=False) #dilation=2表示使用空洞卷積 self.bn_B= nn.BatchNorm2d(in_channels) self.globalAvgPool = nn.AdaptiveAvgPool2d((1,1)) self.conv_fc1 = nn.Conv2d(in_channels,d,1,bias=False) self.bn_fc1 = nn.BatchNorm2d(d) self.conv_fc2 = nn.Conv2d(d,2*in_channels,1,bias=False) #前一半結果是第一個分支的,後一半結果是第二個分支的 def forward(self,x): dA = F.relu(self.bn_A(self.conv_A(x))) dB = F.relu(self.bn_B(self.conv_B(x))) print(dA.shape) print(dB.shape) out = self.globalAvgPool(dA+dB) out = F.relu(self.bn_fc1(self.conv_fc1(out))) out = self.conv_fc2(out) out = out.reshape(-1,2,self.in_channels,1,1) out = F.softmax(out,1) dA = dA * out[:,0] dB = dB * out[:,1] out = dA + dB return out
二:Strip Pooling:Rethinking Spatial Pooling for Scene Parsing
1.目的:傳統的N*N的池化層對於長而窄的目標,往往會丟失部分資訊;在處理不規則形狀的目標時,它會包含許多不相關的區域。
2.Strip Pooling
對於一個H*W的輸入,分別使用一個H*1的strip pool和1*W的strip pool進行平均值池化,得到兩個輸出。這兩個輸出專注於捕獲區域性的細節。
之後用1*1卷積對兩個輸出進行擴張(到H*W),然後經過1*1卷積和Sigmoid函式,最後將該值與原輸入進行element-wise multiplication,得到輸出(注意力機制)。
3.Mixed Pooling Module
目的是通過池化操作來收集不同型別的上下文資訊,使特徵更具有鑑別性。
(1)首先了解金字塔池化結構(PPM):參考原文連結:https://blog.csdn.net/rocking_struggling/article/details/108550637
(2)作者改進了PPM,設計了MPM:
如上圖(a),為原始PPM,可以捕獲較短距離的特徵間的依賴關係
如上圖(b),MPM可以捕獲更長距離特徵之間的依賴關係。
作後將這兩個分支拼接起來,進行卷積升維。
這裡參考大佬程式碼:
import torch import torch.nn as nn import torch.nn.functional as F class MPMBlock(nn.Module): def __init__(self,in_channels,pool_size): ''' Parameters ---------- in_channels : TYPE 輸入通道. pool_size : TYPE 第一個分支中前兩個通路進行池化後的尺寸. Returns ------- None. ''' super(MPMBlock,self).__init__() hide_channels = round(in_channels/4) #資料進入兩個分支前先用1x1卷積降維 self.redu_conv1 = nn.Sequential(nn.Conv2d(in_channels,hide_channels,1,bias=False), nn.BatchNorm2d(hide_channels), nn.ReLU(inplace=True)) self.redu_conv2 = nn.Sequential(nn.Conv2d(in_channels,hide_channels,1,bias=False), nn.BatchNorm2d(hide_channels), nn.ReLU(inplace=True)) #定義第一個分支的操作 #可以注意到有三個分路,其中最下面的並沒有進行池化,所以我們定義兩個池化層 self.pool1_1 = nn.AdaptiveAvgPool2d(pool_size[0]) self.pool1_2 = nn.AdaptiveAvgPool2d(pool_size[1]) #接著定義這三個分路的卷積層 self.conv1_1 = nn.Sequential(nn.Conv2d(hide_channels,hide_channels,3,1,1,bias=False), nn.BatchNorm2d(hide_channels)) self.conv1_2 = nn.Sequential(nn.Conv2d(hide_channels,hide_channels,3,1,1,bias=False), nn.BatchNorm2d(hide_channels)) self.conv1_3 = nn.Sequential(nn.Conv2d(hide_channels,hide_channels,3,1,1,bias=False), nn.BatchNorm2d(hide_channels)) #接著定義將上面三個卷積層輸出相加後,再進行的卷積操作 self.conv1_4 = nn.Sequential(nn.Conv2d(hide_channels,hide_channels,3,1,1, bias=False), nn.BatchNorm2d(hide_channels), nn.ReLU(True)) #定義第二個分支的操作 #首先是兩個條形池化,這裡用的是1x3和3x1 self.pool2_1 = nn.AdaptiveAvgPool2d([1,None])#例如32x3x64x64的輸入經過這一步操作,得到輸出32x3x1x64 self.pool2_2 = nn.AdaptiveAvgPool2d([None,1]) #接著是兩個卷積層擴充條形池化的結果 #注意此處使用的卷積核也是條形的 self.conv2_1 = nn.Sequential(nn.Conv2d(hide_channels,hide_channels,(1,3),1,(0,1),bias=False), nn.BatchNorm2d(hide_channels)) self.conv2_2 = nn.Sequential(nn.Conv2d(hide_channels,hide_channels,(3,1),1,(1,0),bias=False), nn.BatchNorm2d(hide_channels)) #接著定義將上面兩個卷積層輸出相加後,再進行的卷積操作 self.conv2_3 = nn.Sequential(nn.Conv2d(hide_channels,hide_channels,(3,1),1,(1,0),bias=False), nn.BatchNorm2d(hide_channels), nn.ReLU(inplace=True)) #最後將兩個分支的結果拼接起來,進行卷積升維,使維度同原始輸入一致 self.conv3 = nn.Sequential(nn.Conv2d(hide_channels*2,in_channels,1,bias=False), nn.BatchNorm2d(in_channels)) def forward(self,x): #F.interpolate是將輸入資料上取樣或者下采樣到你輸入的維度,可以指定取樣方法等,有些複雜,並未深入研究 #通過F.interpolate就可以使多個分支的輸出維度保持一致,方便後面操作 _,_,h,w = x.shape x1 = self.redu_conv1(x) x2 = self.redu_conv2(x) x1_1 = F.interpolate(self.conv1_1(self.pool1_1(x1)),(h,w)) x1_2 = F.interpolate(self.conv1_2(self.pool1_2(x1)),(h,w)) x1_3 = self.conv1_3(x1) x1 = self.conv1_4(F.relu(x1_1 + x1_2 + x1_3)) print(x1.shape) x2_1 = F.interpolate(self.conv2_1(self.pool2_1(x2)),(h,w)) x2_2 = F.interpolate(self.conv2_2(self.pool2_2(x2)),(h,w)) x2 = self.conv2_3(F.relu(x2_1+x2_2)) out = F.relu(self.conv3(torch.cat([x1,x2],dim=1))) #在channel維度拼接 return out + x
三.HRNet:Deep High-Resolution Representation Learning for Human Pose Estimation
與常規的多尺度特徵提取網路(高解析度-->低解析度-->高解析度)不同,HRNet在整個特徵提取的過程中,始終保持著高解析度特徵圖,在實現多尺度特徵提取的過程中,在高解析度特徵圖主網路逐漸並行加入低解析度特徵圖子網路,不同網路實現多尺度融合和特徵提取。
1.人體姿態估計:人體姿態估計,又稱關鍵點檢測,是指從大小為W×H×3的影象I中檢測出K個關鍵點或身體部位(如肘部、腕部等)的位置。
2.並行多解析度卷積
本文的核心思想,從一個高解析度卷積作為第一階段,逐步新增高解析度到低解析度的流,形成新的階段,並行連線多解析度流。即後一階段的並行流的解析度由前一階段的並行流和更低的解析度組成。
如圖所示,其中Nsr表示第s階段的r解析度指數(前一階段的r為後一階段的)
3.重複多尺度融合
下面以第三階段為例:
下采樣:使用步長為2的3*3卷積進行下采樣,如上圖(3)所示,如果要進行4倍降取樣,則採用兩個序列的卷積。
上取樣:最近鄰插值
4.網路具體化:
網路由四個階段組成,由四個並行的子網路組成,其解析度逐漸下降到一半,網路寬度(通道數量)增加到原來的兩倍。