論文《GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond》
1、作用
GCNet通過聚合每個查詢位置的全局上下文信息來捕獲長距離依賴關系,從而改善了圖像/視頻分類、對象檢測和分割等一系列識別任務的性能。非局部網絡(NLNet)首次提出了通過聚合查詢特定的全局上下文到每個查詢位置來捕獲長距離依賴的方法。GCNet在此基礎上進行了改進和簡化,旨在以更少的計算量保持NLNet的準確性。
2、機制
GCNet通過以下三個步驟來建模全局上下文:
1、上下文建模:
通過加權平均所有位置的特征來形成全局上下文特征
2、特征轉換:
捕捉通道間的依賴關系。
3、融合:
將全局上下文特征合并到每個位置的特征中。GCNet發現NLNet中的全局上下文對于圖像內的不同查詢位置幾乎是相同的,基于這一發現,GCNet采用了查詢獨立的注意力圖來簡化計算過程。
3、獨特優勢
1、計算效率:GCNet通過使用查詢獨立的注意力圖顯著減少了計算量,與NLNet相比,保持了準確性的同時大幅減少了計算需求。
2、輕量級:GCNet的設計允許它被應用于背骨網絡的多個層次,與SENet相似,它通過特征重標定和全局上下文建模來提高性能,但引入的計算和參數增量非常小。
3、通用性和魯棒性:在多個基準數據集和不同的視覺識別任務(如對象檢測/分割、圖像分類和動作識別)上,GCNet普遍優于簡化的NLNet和SENet,展示了其優越的性能和廣泛的適用性。
4、代碼
import torch
import torch.nn as nn# 定義全局上下文塊類
class GlobalContextBlock(nn.Module):def __init__(self, inplanes, ratio, pooling_type="att", fusion_types=('channel_mul')) -> None:super().__init__()# 定義有效的融合類型valid_fusion_types = ['channel_add', 'channel_mul']# 斷言池化類型為'avg'或'att'assert pooling_type in ['avg', 'att']# 斷言至少使用一種融合方式assert len(fusion_types) > 0, 'at least one fusion should be used'# 初始化基本參數self.inplanes = inplanesself.ratio = ratioself.planes = int(inplanes * ratio)self.pooling_type = pooling_typeself.fusion_type = fusion_typesif pooling_type == 'att':self.conv_mask = nn.Conv2d(inplanes, 1, kernel_size=1)self.softmax = nn.Softmax(dim=2)else:# 否則,使用自適應平均池化self.avg_pool = nn.AdaptiveAvgPool2d(1)# 如果池化類型為'att',使用1x1卷積作為掩碼,并使用Softmax進行歸一化if 'channel_add' in fusion_types:self.channel_add_conv = nn.Sequential(nn.Conv2d(self.inplanes, self.planes, kernel_size=1),nn.LayerNorm([self.planes, 1, 1]),nn.ReLU(inplace=True),nn.Conv2d(self.planes, self.inplanes, kernel_size=1))else:self.channel_add_conv = None# 如果融合類型包含'channel_mul',定義通道相乘卷積if 'channel_mul' in fusion_types:self.channel_mul_conv = nn.Sequential(nn.Conv2d(self.inplanes, self.planes, kernel_size=1),nn.LayerNorm([self.planes, 1, 1]),nn.ReLU(inplace=True),nn.Conv2d(self.planes, self.inplanes, kernel_size=1))else:self.channel_mul_conv = None# 定義空間池化函數def spatial_pool(self, x):batch, channel, height, width = x.size()if self.pooling_type == 'att':input_x = xinput_x = input_x.view(batch, channel, height * width) # 使用1x1卷積生成掩碼input_x = input_x.unsqueeze(1)context_mask = self.conv_mask(x) # 使用1x1卷積生成掩碼context_mask = context_mask.view(batch, 1, height * width)context_mask = self.softmax(context_mask)# 應用Softmax進行歸一化context_mask = context_mask.unsqueeze(-1)context = torch.matmul(input_x, context_mask) # 計算上下文context = context.view(batch, channel, 1, 1)else:context = self.avg_pool(x) # 執行自適應平均池化return context# 定義前向傳播函數def forward(self, x):context = self.spatial_pool(x)out = xif self.channel_mul_conv is not None:channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) # 將權重進行放大縮小out = out * channel_mul_term # 與x進行相乘if self.channel_add_conv is not None:channel_add_term = self.channel_add_conv(context)out = out + channel_add_termreturn outif __name__ == "__main__":input = torch.randn(16, 64, 32, 32) #生成隨機數net = GlobalContextBlock(64, ratio=1 / 16) #還是實例化哈out = net(input)print(out.shape)