基于Pytorch的動(dòng)態(tài)卷積復(fù)現(xiàn)

極市導(dǎo)讀
?本文重點(diǎn)介紹了Pytorch中卷積的實(shí)現(xiàn),并為實(shí)現(xiàn)過(guò)程中可能出現(xiàn)的問(wèn)題給出解決方案。文末列出了相關(guān)代碼,讀者可對(duì)照代碼進(jìn)行實(shí)踐。
Dynamic Convolution: Attention over Convolution Kernels
其中包含一維,二維,三維的動(dòng)態(tài)卷積;分別可以用于實(shí)現(xiàn)eeg的處理,正常圖像的處理,醫(yī)療圖像中三維腦部的處理等等。
?的大小視為分組卷積里面的組的大小進(jìn)行動(dòng)態(tài)卷積。如?
?,那么就轉(zhuǎn)化成?
?,?
?的分組卷積。簡(jiǎn)單回顧

?,先對(duì)特征圖做幾次運(yùn)算,生成?
?個(gè)和為?
?的參數(shù)?
?,然后對(duì)?
?個(gè)卷積核參數(shù)進(jìn)行線性求和,這樣推理的時(shí)候卷積核是隨著輸入的變化而變化的。(可以看看其他的講解文章,本文主要理解怎么寫(xiě)代碼)
?,?
?]大小的加權(quán)參數(shù)。?
?對(duì)應(yīng)著要被求和的卷積核數(shù)量。class attention2d(nn.Module):
def __init__(self, in_planes, K,):
super(attention2d, self).__init__()
self.avgpool = nn.AdaptiveAvgPool2d(1)
self.fc1 = nn.Conv2d(in_planes, K, 1,)
self.fc2 = nn.Conv2d(K, K, 1,)
def forward(self, x):
x = self.avgpool(x)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x).view(x.size(0), -1)
return F.softmax(x, 1)
?個(gè)卷積核求和的公式。
?是輸入,?
?是輸出;可以看到?
?進(jìn)行了兩次運(yùn)算,一次用于求注意力的參數(shù)(用于生成動(dòng)態(tài)的卷積核),一次用于被卷積。
?個(gè)卷積核求和,會(huì)出現(xiàn)問(wèn)題。接下來(lái)我們先回顧一下Pytorch里面的卷積參數(shù),然后描述一下可能會(huì)出現(xiàn)的問(wèn)題,再講解如何通過(guò)分組卷積去解決問(wèn)題。Pytorch卷積的實(shí)現(xiàn)
?,?
?,?
?,?
?]。
?,?
?,?
?,?
?]。
?,?
?,?
?,?
?]。(在Pytorch中,2d卷積核參數(shù)應(yīng)該是固定這種維度的)
?的。因?yàn)閷?duì)于正常的卷積來(lái)說(shuō),不同的輸入數(shù)據(jù),使用的是相同的卷積核,卷積核的數(shù)量與一次前向運(yùn)算所輸入的?
?大小無(wú)關(guān)(相同層的卷積核參數(shù)只需要一份)。可能會(huì)出現(xiàn)的問(wèn)題
?大于1而出現(xiàn)的問(wèn)題。
?個(gè)數(shù),他們的維度為[?
?,?
?,?
?,?
?],可以直接.view成[?
?,?
?],緊接著?
?作用于?
?卷積核參數(shù)上(形成動(dòng)態(tài)卷積)。
?份網(wǎng)絡(luò)參數(shù),不符合Pytorch里面的卷積參數(shù)格式,會(huì)出錯(cuò)。
?,?
?]*[?
?,?
?,?
?,?
?,?
?],生成的動(dòng)態(tài)卷積核是[?
?,?
?,?
?,?
?,?
?],不符合Pytorch里面的規(guī)定,不能直接參與運(yùn)算(大家可以按照這個(gè)思路寫(xiě)個(gè)代碼看看,體會(huì)一下,光看可能感覺(jué)不出來(lái)問(wèn)題),最簡(jiǎn)單的解決辦法就是?
?等于1,不會(huì)出現(xiàn)錯(cuò)誤,但是慢啊!
?大于1會(huì)導(dǎo)致中間卷積核參數(shù)不符合規(guī)定。分組卷積以及如何通過(guò)分組卷積實(shí)現(xiàn)?
?大于1的動(dòng)態(tài)卷積
?,?
?,?
?,?
?],假設(shè)?
?為?
?,那么分組卷積就是將他分為兩個(gè)?
?為?
?的數(shù)據(jù)(也可以用其他方法分),那么維度就是[?
?, 5x2?,?
?,?
?],換個(gè)維度換下視角,[?
?,?
?,?
?,?
?],那么?
?為2的組卷積可以看成?
?的正常卷積。(如果還是有點(diǎn)不了解分組卷積,可以閱讀其他文章仔細(xì)了解一下。)
?翻倍即可將分組卷積轉(zhuǎn)化成正常卷積,那么反向思考一下,將?
?變?yōu)?,是不是可以將正常卷積變成分組卷積?
?大小看成分組卷積中?
?的數(shù)量,令?
?所在維度直接變?yōu)?
??。?!直接將輸入數(shù)據(jù)從[?
?,?
?,?
?,?
?]變成[1,?
?,?
?,?
?],就可以用分組卷積解決問(wèn)題了?。?!
?,?
?,?
?](分組卷積的節(jié)奏);卷積權(quán)重參數(shù)初始化為[?
?,?
?,?
?,?
?,?
?],attention模塊生成的維度為[?
?,?
?],直接進(jìn)行正常的矩陣乘法[?
?,?
?]*[?
?,?
?*
?*?
?*?
?]生成動(dòng)態(tài)卷積的參數(shù),生成的動(dòng)態(tài)卷積權(quán)重維度為[?
?,?
?,?
?,?
?,?
?],將其看成分組卷積的權(quán)重[?
?,?
?,?
?,?
?](過(guò)程中包含reshape)。這樣的處理就完成了,輸入數(shù)據(jù)[?
?,?
?,?
?,?
?],動(dòng)態(tài)卷積核[?
?,?
?,?
?,?
?],直接是?
?的分組卷積,問(wèn)題解決。class Dynamic_conv2d(nn.Module):
def __init__(self, in_planes, out_planes, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, K=4,):
super(Dynamic_conv2d, self).__init__()
assert in_planes%groups==0
self.in_planes = in_planes
self.out_planes = out_planes
self.kernel_size = kernel_size
self.stride = stride
self.padding = padding
self.dilation = dilation
self.groups = groups
self.bias = bias
self.K = K
self.attention = attention2d(in_planes, K, )
self.weight = nn.Parameter(torch.Tensor(K, out_planes, in_planes//groups, kernel_size, kernel_size), requires_grad=True)
if bias:
self.bias = nn.Parameter(torch.Tensor(K, out_planes))
else:
self.bias = None
def forward(self, x):#將batch視作維度變量,進(jìn)行組卷積,因?yàn)榻M卷積的權(quán)重是不同的,動(dòng)態(tài)卷積的權(quán)重也是不同的
softmax_attention = self.attention(x)
batch_size, in_planes, height, width = x.size()
x = x.view(1, -1, height, width)# 變化成一個(gè)維度進(jìn)行組卷積
weight = self.weight.view(self.K, -1)
# 動(dòng)態(tài)卷積的權(quán)重的生成, 生成的是batch_size個(gè)卷積參數(shù)(每個(gè)參數(shù)不同)
aggregate_weight = torch.mm(softmax_attention, weight).view(-1, self.in_planes, self.kernel_size, self.kernel_size)
if self.bias is not None:
aggregate_bias = torch.mm(softmax_attention, self.bias).view(-1)
output = F.conv2d(x, weight=aggregate_weight, bias=aggregate_bias, stride=self.stride, padding=self.padding,
dilation=self.dilation, groups=self.groups*batch_size)
else:
output = F.conv2d(x, weight=aggregate_weight, bias=None, stride=self.stride, padding=self.padding,
dilation=self.dilation, groups=self.groups * batch_size)
output = output.view(batch_size, self.out_planes, output.size(-2), output.size(-1))
return output
推薦閱讀
動(dòng)態(tài)卷積:自適應(yīng)調(diào)整卷積參數(shù),顯著提升模型表達(dá)能力|CVPR2020
高性能漲點(diǎn)的動(dòng)態(tài)卷積 DyNet 與 CondConv、DynamicConv 有什么區(qū)別聯(lián)系?
競(jìng)賽方案解讀:8組天池鋁型材表面瑕疵總決賽方案,含數(shù)據(jù)集、模型改進(jìn)、創(chuàng)新點(diǎn)等

評(píng)論
圖片
表情
