介紹
本文作者提出了一種名為SCConv(Spatial and Channel reconstruction Convolution, 空間和通道重建卷積)的卷積模塊,目的是減少卷積神經網絡中特征之間的空間和通道冗余,從而壓縮CNN模型并提高其性能。
作者設計的 SCConv 模塊,包含兩個單元。一個名為SRU (Spatial Reconstruction Unit, 空間重構單元),一個名為CRU (Channel Reconstruction Unit, 通道重構單元)。其中 SRU 通過分離-重構方法來減少空間冗余,CRU 則使用分割-轉換-融合方法來減少通道冗余。這兩個單元協(xié)同工作,以減少CNN中特征的冗余信息。
作者指出,SCConv 是一種可以直接替代標準卷積操作的插件式卷積模塊 ,可以應用于各種卷積神經網絡中,從而降低冗余特征并減少計算復雜性。
在后續(xù)的實驗中,文章作者認為相對于其他流行的 SOTA 方法,他們提出的 SCConv 可以以更低的計算成本獲得更高的準確率。下圖是 ResNet50 在 ImageNet 上的 Top1 準確性測試結果。
模塊設計SCConv
如下圖,SCConv 由兩個單元組成,即空間重構單元 (SRU) 和信道重構單元 (CRU) ,兩個單元按順序排列。輸入的特征 X 先經過空間重構單元,得到空間細化的特征Xw。再經過通道重構單元,得到通道提煉的特征 Y 作為輸出。
SCConv 模塊利用了特征之間的空間冗余和信道冗余,模塊可以無縫集成到任何 CNN 框架中,減少特征之間的冗余,提高 CNN 特征的代表性。
作者對SRU和CRU進行不同的組合,包括:
-
不使用 SRU 和 CRU
-
單獨使用 SRU
-
單獨使用 CRU
-
并行使用 SRU 和 CRU
-
先使用 CRU 再使用 SRU
-
先使用 SRU 在使用 CRU
最終發(fā)現先使用 SRU 再使用 CRU 的效果最好。
下面詳細介紹 SRU 和 CRU 這兩個單元。
SRU 空間重建單元
在作者的設計中,該單元采用分離-重構的方法。
分離操作的目的是將信息量大的特征圖從信息量小的特征圖中分離出來,與空間內容相對應。作者使用組歸一化 (Group Normalization) 里的縮放因子來評估不同特征圖中的信息含量。
經過 SRU 處理后,信息量大的特征從信息量小的特征中分離出來,減少了空間維度上的冗余特征。
CRU 通道重建單元
在作者的設計中,該單元采用分割-轉換-融合的方法。
實驗
消融實驗
下圖的消融實驗確定了 SRU 和 CRU 的排列方式
下圖的消融實驗確定了 CRU 中的拆分系數α
圖片分類實驗
下圖是與其他 SOTA 方法的比較,作者認為在所有的情況下,SCConv-embedded 模型的準確性都優(yōu)于先前所有的網絡。在某些模型中,對比同類模型在減少參數和 FLOPs 的同時還實現了更高的準確率
在CVer微信公眾號后臺回復:SCConv,可以下載本論文pdf和代碼相關代碼中文注釋
import torch # 導入 PyTorch 庫
import torch.nn.functional as F # 導入 PyTorch 的函數庫
import torch.nn as nn # 導入 PyTorch 的神經網絡模塊
# 自定義 GroupBatchnorm2d 類,實現分組批量歸一化
class GroupBatchnorm2d(nn.Module):
def __init__(self, c_num:int, group_num:int = 16, eps:float = 1e-10):
super(GroupBatchnorm2d,self).__init__() # 調用父類構造函數
assert c_num >= group_num # 斷言 c_num 大于等于 group_num
self.group_num = group_num # 設置分組數量
self.gamma = nn.Parameter(torch.randn(c_num, 1, 1)) # 創(chuàng)建可訓練參數 gamma
self.beta = nn.Parameter(torch.zeros(c_num, 1, 1)) # 創(chuàng)建可訓練參數 beta
self.eps = eps # 設置小的常數 eps 用于穩(wěn)定計算
def forward(self, x):
N, C, H, W = x.size() # 獲取輸入張量的尺寸
x = x.view(N, self.group_num, -1) # 將輸入張量重新排列為指定的形狀
mean = x.mean(dim=2, keepdim=True) # 計算每個組的均值
std = x.std(dim=2, keepdim=True) # 計算每個組的標準差
x = (x - mean) / (std + self.eps) # 應用批量歸一化
x = x.view(N, C, H, W) # 恢復原始形狀
return x * self.gamma + self.beta # 返回歸一化后的張量
# 自定義 SRU(Spatial and Reconstruct Unit)類
class SRU(nn.Module):
def __init__(self,
oup_channels:int, # 輸出通道數
group_num:int = 16, # 分組數,默認為16
gate_treshold:float = 0.5, # 門控閾值,默認為0.5
torch_gn:bool = False # 是否使用PyTorch內置的GroupNorm,默認為False
):
super().__init__() # 調用父類構造函數
# 初始化 GroupNorm 層或自定義 GroupBatchnorm2d 層
self.gn = nn.GroupNorm(num_channels=oup_channels, num_groups=group_num) if torch_gn else GroupBatchnorm2d(c_num=oup_channels, group_num=group_num)
self.gate_treshold = gate_treshold # 設置門控閾值
self.sigomid = nn.Sigmoid() # 創(chuàng)建 sigmoid 激活函數
def forward(self, x):
gn_x = self.gn(x) # 應用分組批量歸一化
w_gamma = self.gn.gamma / sum(self.gn.gamma) # 計算 gamma 權重
reweights = self.sigomid(gn_x * w_gamma) # 計算重要性權重
# 門控機制
info_mask = reweights >= self.gate_treshold # 計算信息門控掩碼
noninfo_mask = reweights < self.gate_treshold # 計算非信息門控掩碼
x_1 = info_mask * x # 使用信息門控掩碼
x_2 = noninfo_mask * x # 使用非信息門控掩碼
x = self.reconstruct(x_1, x_2) # 重構特征
return x
def reconstruct(self, x_1, x_2):
x_11, x_12 = torch.split(x_1, x_1.size(1) // 2, dim=1) # 拆分特征為兩部分
x_21, x_22 = torch.split(x_2, x_2.size(1) // 2, dim=1) # 拆分特征為兩部分
return torch.cat([x_11 + x_22, x_12 + x_21], dim=1) # 重構特征并連接
# 自定義 CRU(Channel Reduction Unit)類
class CRU(nn.Module):
def __init__(self, op_channel:int, alpha:float = 1/2, squeeze_radio:int = 2, group_size:int = 2, group_kernel_size:int = 3):
super().__init__() # 調用父類構造函數
self.up_channel = up_channel = int(alpha * op_channel) # 計算上層通道數
self.low_channel = low_channel = op_channel - up_channel # 計算下層通道數
self.squeeze1 = nn.Conv2d(up_channel, up_channel // squeeze_radio, kernel_size=1, bias=False) # 創(chuàng)建卷積層
self.squeeze2 = nn.Conv2d(low_channel, low_channel // squeeze_radio, kernel_size=1, bias=False) # 創(chuàng)建卷積層
# 上層特征轉換
self.GWC = nn.Conv2d(up_channel // squeeze_radio, op_channel, kernel_size=group_kernel_size, stride=1, padding=group_kernel_size // 2, groups=group_size) # 創(chuàng)建卷積層
self.PWC1 = nn.Conv2d(up_channel // squeeze_radio, op_channel, kernel_size=1, bias=False) # 創(chuàng)建卷積層
# 下層特征轉換
self.PWC2 = nn.Conv2d(low_channel // squeeze_radio, op_channel - low_channel // squeeze_radio, kernel_size=1, bias=False) # 創(chuàng)建卷積層
self.advavg = nn.AdaptiveAvgPool2d(1) # 創(chuàng)建自適應平均池化層
def forward(self, x):
# 分割輸入特征
up, low = torch.split(x, [self.up_channel, self.low_channel], dim=1)
up, low = self.squeeze1(up), self.squeeze2(low)
# 上層特征轉換
Y1 = self.GWC(up) + self.PWC1(up)
# 下層特征轉換
Y2 = torch.cat([self.PWC2(low), low], dim=1)
# 特征融合
out = torch.cat([Y1, Y2], dim=1)
out = F.softmax(self.advavg(out), dim=1) * out
out1, out2 = torch.split(out, out.size(1) // 2, dim=1)
return out1 + out2
# 自定義 ScConv(Squeeze and Channel Reduction Convolution)模型
class ScConv(nn.Module):
def __init__(self, op_channel:int, group_num:int = 16, gate_treshold:float = 0.5, alpha:float = 1/2, squeeze_radio:int = 2, group_size:int = 2, group_kernel_size:int = 3):
super().__init__() # 調用父類構造函數
self.SRU = SRU(op_channel, group_num=group_num, gate_treshold=gate_treshold) # 創(chuàng)建 SRU 層
self.CRU = CRU(op_channel, alpha=alpha, squeeze_radio=squeeze_radio, group_size=group_size, group_kernel_size=group_kernel_size) # 創(chuàng)建 CRU 層
def forward(self, x):
x = self.SRU(x) # 應用 SRU 層
x = self.CRU(x) # 應用 CRU 層
return x
if __name__ == '__main__':
x = torch.randn(1, 32, 16, 16) # 創(chuàng)建隨機輸入張量
model = ScConv(32) # 創(chuàng)建 ScConv 模型
print(model(x).shape) # 打印模型輸出的形狀
-
模塊
+關注
關注
7文章
2658瀏覽量
47294 -
神經網絡
+關注
關注
42文章
4734瀏覽量
100423 -
cnn
+關注
關注
3文章
350瀏覽量
22139
原文標題:CVPR 2023 | 漲點神器!SCConv:即插即用的空間和通道重建卷積
文章出處:【微信號:CVer,微信公眾號:CVer】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論