?
要將注意力機制模塊添加到YoloV5工程項目中的yolo.py中,可參考以下四種情況。
以下4個elif代碼來自https://yolov5.blog.csdn.net/article/details/129108082
elif m in [SimAM, ECA, SpatialGroupEnhance,TripletAttention]:args = [*args[:]]elif m in [CoordAtt, GAMAttention]:c1, c2 = ch[f], args[0]if c2 != no:c2 = make_divisible(c2 * gw, 8)args = [c1, c2, *args[1:]]elif m in [SE, ShuffleAttention, CBAM, SKAttention, DoubleAttention, CoTAttention, EffectiveSEModule,GlobalContext, GatherExcite, MHSA]:c1 = ch[f]args = [c1, *args[0:]]elif m in [S2Attention, NAMAttention, CrissCrossAttention, SequentialPolarizedSelfAttention,ParallelPolarizedSelfAttention, ParNetAttention]:c1 = ch[f]args = [c1]
?
根據這4種情況,我們在yaml文件中,填寫args時(比如下圖中RefConv的[1024,3,1]以及SE中的[1024]),需要填入的參數個數是不同的
# YOLOv5 v6.0 backbone
backbone:# [from, number, module, args][[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2[-1, 1, Conv, [128, 3, 2]], # 1-P2/4[-1, 3, C3, [128]], #2[-1, 1, Conv, [256, 3, 2]], # 3-P3/8[-1, 6, C3, [256]], #4[-1, 1, Conv, [512, 3, 2]], # 5-P4/16[-1, 9, C3, [512]], #6[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32[-1, 3, C3, [1024]], # 8[-1,1,SE,[1024]], #9[[-1, 1, SimAM, [1e-4]], # 10[-1, 1, SPPF, [1024, 5]], # 11[-1, 1, RefConv, [1024, 3, 1]], # 12]
具體來說,是要將elif模塊添加到yolo.py文件中的parse_model函數里。在編寫elif模塊代碼時,我們需要關注的是,你的注意力機制模塊代碼(在common.py)中的__init__里面的參數,是否設置了“輸入通道數”和“輸出通道數”,這兩個參數。
?
一、先上結論
情況1:_init_中不包含“輸入通道數”和“輸出通道數”,但含有其它參數
以下這些模塊:SimAM,ECA,SpatialGroupEnhance,TripletAttention 全都滿足情況1
elif m in [SimAM, ECA, SpatialGroupEnhance,TripletAttention]:args = [*args[:]]
以下是SimAM,ECA,SpatialGroupEnhance,TripletAttention 的__init__
class SimAM(torch.nn.Module):def __init__(self, e_lambda=1e-4):class ECA(nn.Module):def __init__(self, k_size=3):class SpatialAttention(nn.Module):def __init__(self, kernel_size=7):class SpatialGroupEnhance(nn.Module):def __init__(self, groups=8):class TripletAttention(nn.Module):def __init__(self, no_spatial=False):
這里解釋一下args = [*args[:]]是什么意思
在Python中,*args 是一個特殊的語法,用于在函數定義中處理不確定數量的位置參數。args 是一個元組,包含了所有傳遞給函數的位置參數。
args[:] 是一個切片操作,它會創建一個 args 的淺拷貝。這意味著如果你修改了 args[:] 的內容,原始的 args 不會被改變。
[*args[:]] 則是將 args[:] 中的元素解包(unpack)成一個列表。這樣做的目的通常是為了創建一個新的列表,而不是修改原始的 args。
例如:
args = [1, 2, 3, 4]
new_args = [*args[:]]
print(new_args) #輸出為[1,2,3,4]
情況2:_init_中同時包含“輸入通道數”和“輸出通道數”,且含有其它參數
以下這些模塊:CoordAtt, GAMAttention 全都滿足情況2
elif m in [CoordAtt, GAMAttention]:c1, c2 = ch[f], args[0]if c2 != no:c2 = make_divisible(c2 * gw, 8)args = [c1, c2, *args[1:]]
以下是CoordAtt, GAMAttention 的__init__
class CoordAtt(nn.Module):def __init__(self, inp, oup, reduction=32):class GAMAttention(nn.Module):def __init__(self, c1, c2, group=True, rate=4):
情況3:_init_中只包含“輸入通道數”,不包含“輸出通道數”,且含有其它參數
以下這些模塊:SE, ShuffleAttention, CBAM, SKAttention, DoubleAttention, CoTAttention, EffectiveSEModule,GlobalContext, GatherExcite, MHSA全都滿足情況3
elif m in [SE, ShuffleAttention, CBAM, SKAttention, DoubleAttention, CoTAttention, EffectiveSEModule,GlobalContext, GatherExcite, MHSA]:c1 = ch[f]args = [c1, *args[0:]]
以下是SE, ShuffleAttention, CBAM, SKAttention, DoubleAttention, CoTAttention, EffectiveSEModule,GlobalContext, GatherExcite, MHSA的__init__
#SE機制它的輸入通道和輸出通道是一樣的,所以在實現上可以只傳入輸入通道c1,但如果也給出輸出通道c2的參數也是可以的。下面這兩種都是在迪菲赫爾曼博客中實現過的SE模塊
class SE(nn.Module):def __init__(self, c1, ratio=16):
class SE(nn.Module):def __init__(self, c1, c2, ratio=16):class ShuffleAttention(nn.Module):def __init__(self, channel=512, G=8):class CBAM(nn.Module):def __init__(self, c1, ratio=16, kernel_size=7):class SKAttention(nn.Module):def __init__(self, channel=512, kernels=[1, 3, 5, 7], reduction=16, group=1, L=32):class DoubleAttention(nn.Module):def __init__(self, in_channels, reconstruct=True):class CoTAttention(nn.Module):def __init__(self, dim=512, kernel_size=3):class EffectiveSEModule(nn.Module):def __init__(self, channels, add_maxpool=False, gate_layer='hard_sigmoid'):class GlobalContext(nn.Module):def __init__(self, channels, use_attn=True, fuse_add=False, fuse_scale=True, init_last_zero=False,rd_ratio=1. / 8, rd_channels=None, rd_divisor=1, act_layer=nn.ReLU, gate_layer='sigmoid'):
class GatherExcite(nn.Module):def __init__(self, channels, feat_size=None, extra_params=False, extent=0, use_mlp=True,rd_ratio=1. / 16, rd_channels=None, rd_divisor=1, add_maxpool=False,act_layer=nn.ReLU, norm_layer=nn.BatchNorm2d, gate_layer='sigmoid'):class MHSA(nn.Module):def __init__(self, n_dims, width=14, height=14, heads=4, pos_emb=False):
情況4:_init_中只包含“輸入通道數”,不包含“輸出通道數”,且不含有其他參數(注意對比情況3)
以下這些模塊:S2Attention, NAMAttention, CrissCrossAttention, SequentialPolarizedSelfAttention,ParallelPolarizedSelfAttention, ParNetAttention全都滿足情況4
elif m in [S2Attention, NAMAttention, CrissCrossAttention, SequentialPolarizedSelfAttention,ParallelPolarizedSelfAttention, ParNetAttention]:c1 = ch[f]args = [c1]
以下是S2Attention, NAMAttention, CrissCrossAttention, SequentialPolarizedSelfAttention,ParallelPolarizedSelfAttention, ParNetAttention的__init__
class S2Attention(nn.Module):def __init__(self, channels=512):class NAMAttention(nn.Module):def __init__(self, channels):class CrissCrossAttention(nn.Module):def __init__(self, in_dim):class SequentialPolarizedSelfAttention(nn.Module):def __init__(self, channel=512):class ParallelPolarizedSelfAttention(nn.Module):def __init__(self, channel=512):class ParNetAttention(nn.Module):def __init__(self, channel=512):
?
二、解釋代碼
在理解代碼前,我們需要知道,在parse_model函數中,args列表的前兩個位置被設計為存放輸入通道數(c1)和輸出通道數(c2)。這是因為在創建這些模塊時,我們通常會按照這個順序傳遞參數。例如,對于nn.Conv2d,其構造函數的簽名為Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True),其中in_channels和out_channels就對應于我們的c1和c2。因此,當我們在parse_model函數中創建這些模塊時,我們需要先獲取c1和c2,然后將它們放在args的前兩個位置,以確保它們能被正確地傳遞給模塊的構造函數。
情況1
'''
這段代碼的目的是為了處理SimAM模塊的參數。elif m in [SimAM]:
這行代碼檢查當前的模塊m是否是SimAM模塊。如果是,那么就執行下一行代碼。args = [*args[:]]:
這行代碼創建了args的一個淺拷貝。在Python中,args[:]會創建一個新的列表,這個新列表包含了args中的所有元素。*操作符會將這個新列表解包,然后我們再用[]將解包后的元素重新組裝成一個新的列表。所以,[*args[:]]就等于args[:],它們都會創建args的一個淺拷貝。綜上,這段代碼使args列表保持不變,因為SimAM模塊不需要修改輸入和輸出通道數。
'''elif m in [SimAM, ECA, SpatialGroupEnhance,TripletAttention]:args = [*args[:]]
?
情況2
'''
這段代碼的目的是為了處理CoordAtt和GAMAttention模塊的參數。elif m in [CoordAtt, GAMAttention]:
這行代碼檢查當前的模塊m是否是CoordAtt模塊或GAMAttention模塊。如果是,那么就執行下面的代碼。c1, c2 = ch[f], args[0]:
這行代碼從ch和args兩個列表中獲取了兩個值c1和c2。ch是一個列表,它保存了模型中每一層的輸出通道數。f是一個索引,它指向ch中的某個元素,這個元素表示當前層的輸入通道數。所以ch[f]就是當前層的輸入通道數。args也是一個列表,它保存了當前層的參數。在模型配置文件中,每一層的參數都被保存在一個列表中,例如[64, 6, 2, 2]。這個列表的第一個元素通常是當前層的輸出通道數。所以args[0]就是當前層的輸出通道數。if c2 != no:
這行代碼檢查當前層的輸出通道數c2是否不等于no。no是模型的總輸出通道數。c2 = make_divisible(c2 * gw, 8):
如果c2不等于no,那么就重新計算c2的值。gw是模型的寬度倍數,make_divisible(c2 * gw, 8)會將c2 * gw調整為最接近的8的倍數。這是因為某些硬件(如GPU)在處理通道數為8的倍數的數據時,可以獲得更好的性能。args = [c1, c2, *args[1:]]:
這行代碼創建了一個新的參數列表args。新的args列表的第一個元素是c1,第二個元素是c2,剩下的元素是原始args列表的第二個元素及其后面的所有元素。*args[1:]是Python的解包(unpack)操作,它可以將列表args[1:]中的所有元素解包出來。所以,[c1, c2, *args[1:]]就等于[c1, c2]和args[1:]兩個列表的連接。
'''elif m in [CoordAtt, GAMAttention]:c1, c2 = ch[f], args[0]if c2 != no:c2 = make_divisible(c2 * gw, 8)args = [c1, c2, *args[1:]]
情況3
'''
這段代碼的目的是為了處理SE, ShuffleAttention等模塊的參數。elif m in [SE, ShuffleAttention, CBAM, SKAttention, DoubleAttention, CoTAttention, EffectiveSEModule,GlobalContext, GatherExcite, MHSA]:
這行代碼檢查當前的模塊m是否是SE, ShuffleAttention等模塊中的一個。如果是,那么就執行下面的代碼。c1 = ch[f]:
這行代碼從ch列表中獲取了一個值c1。ch是一個列表,它保存了模型中每一層的輸出通道數。f是一個索引,它指向ch中的某個元素,這個元素表示當前層的輸入通道數。所以ch[f]就是當前層的輸入通道數。args = [c1, *args[0:]]:
這行代碼創建了一個新的參數列表args。新的args列表的第一個元素是c1,剩下的元素是原始args列表的第一個元素及其后面的所有元素。*args[0:]是Python的解包(unpack)操作,它可以將列表args[0:]中的所有元素解包出來。所以,[c1, *args[0:]]就等于[c1]和args[0:]兩個列表的連接。綜上,這段代碼將當前層的輸入通道數c1添加到參數列表args的開始位置,因為這些模塊的初始化函數通常需要輸入通道數作為第一個參數。'''elif m in [SE, ShuffleAttention, CBAM, SKAttention, DoubleAttention, CoTAttention, EffectiveSEModule,GlobalContext, GatherExcite, MHSA]:c1 = ch[f]args = [c1, *args[0:]]
?
情況4
'''
這段代碼的目的是為了處理S2Attention, NAMAttention等模塊的參數。elif m in [S2Attention, NAMAttention, CrissCrossAttention, SequentialPolarizedSelfAttention,ParallelPolarizedSelfAttention, ParNetAttention]:
這行代碼檢查當前的模塊m是否是S2Attention, NAMAttention等模塊中的一個。如果是,那么就執行下面的代碼。c1 = ch[f]:
這行代碼從ch列表中獲取了一個值c1。ch是一個列表,它保存了模型中每一層的輸出通道數。f是一個索引,它指向ch中的某個元素,這個元素表示當前層的輸入通道數。所以ch[f]就是當前層的輸入通道數。args = [c1]:這行代碼創建了一個新的參數列表args。新的args列表只有一個元素,就是c1。綜上,這段代碼將當前層的輸入通道數c1作為唯一的參數傳遞給這些模塊,因為這些模塊的初始化函數通常只需要輸入通道數作為參數。
'''elif m in [S2Attention, NAMAttention, CrissCrossAttention, SequentialPolarizedSelfAttention,ParallelPolarizedSelfAttention, ParNetAttention]:c1 = ch[f]args = [c1]
?
?
?