CoordConv實(shí)現(xiàn)卷積加上坐標(biāo)實(shí)例詳解
CoordConv:給你的卷積加上坐標(biāo)

一、理論介紹
1.1 CoordConv理論詳解
這是一篇考古的論文復(fù)現(xiàn)項(xiàng)目,在2018年Uber團(tuán)隊(duì)提出這個(gè)CoordConv模塊的時(shí)候有很多文章對(duì)其進(jìn)行批評(píng),認(rèn)為這個(gè)不值得發(fā)布一篇論文,但是現(xiàn)在重新看一下這個(gè)idea,同時(shí)再對(duì)比一下目前Transformer中提出的位置編碼(Position Encoding),你就會(huì)感概歷史是個(gè)圈,在角點(diǎn)卷積中,為卷積添加兩個(gè)坐標(biāo)編碼實(shí)際上與Transformer中提出的位置編碼是同樣的道理。 眾所周知,深度學(xué)習(xí)里的卷積運(yùn)算是具有平移等變性的,這樣可以在圖像的不同位置共享統(tǒng)一的卷積核參數(shù),但是這樣卷積學(xué)習(xí)過(guò)程中是不能感知當(dāng)前特征在圖像中的坐標(biāo)的,論文中的實(shí)驗(yàn)證明如下圖所示。通過(guò)該實(shí)驗(yàn),作者證明了傳統(tǒng)卷積在卷積核進(jìn)行局部運(yùn)算時(shí),僅僅能感受到局部信息,并且是無(wú)法感受到位置信息的。CoordConv就是通過(guò)在卷積的輸入特征圖中新增對(duì)應(yīng)的通道來(lái)表征特征圖像素點(diǎn)的坐標(biāo),讓卷積學(xué)習(xí)過(guò)程中能夠一定程度感知坐標(biāo)來(lái)提升檢測(cè)精度。

傳統(tǒng)卷積無(wú)法將空間表示轉(zhuǎn)換成笛卡爾空間中的坐標(biāo)和one-hot像素空間中的坐標(biāo)。卷積是等變的,也就是說(shuō)當(dāng)每個(gè)過(guò)濾器應(yīng)用到輸入上時(shí),它不知道每個(gè)過(guò)濾器在哪。我們可以幫助卷積,讓它知道過(guò)濾器的位置。這一過(guò)程需要在輸入上添加兩個(gè)通道實(shí)現(xiàn),一個(gè)在i坐標(biāo),另一個(gè)在j坐標(biāo)。通過(guò)上面的添加坐標(biāo)的操作,我們可以的出一種新的卷積結(jié)構(gòu)--CoordConv,其結(jié)構(gòu)如下圖所示:

二、代碼實(shí)戰(zhàn)
本部分根據(jù)CoordConv論文并參考飛槳的官方實(shí)現(xiàn)完成CoordConv的復(fù)現(xiàn)。
import paddle import paddle.nn as nn import paddle.nn.functional as F from paddle import ParamAttr from paddle.regularizer import L2Decay from paddle.nn import AvgPool2D, Conv2D
2.2 CoordConv類代碼實(shí)現(xiàn)
首先繼承nn.Layer基類,其次使用paddle.arange定義gx``gy兩個(gè)坐標(biāo),并且停止它們的梯度反傳gx.stop_gradient = True,最后將它們concat到一起送入卷積即可。
class CoordConv(nn.Layer):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding):
super(CoordConv, self).__init__()
self.conv = Conv2D(
in_channels + 2, out_channels , kernel_size , stride , padding)
def forward(self, x):
b = x.shape[0]
h = x.shape[2]
w = x.shape[3]
gx = paddle.arange(w, dtype='float32') / (w - 1.) * 2.0 - 1.
gx = gx.reshape([1, 1, 1, w]).expand([b, 1, h, w])
gx.stop_gradient = True
gy = paddle.arange(h, dtype='float32') / (h - 1.) * 2.0 - 1.
gy = gy.reshape([1, 1, h, 1]).expand([b, 1, h, w])
gy.stop_gradient = True
y = paddle.concat([x, gx, gy], axis=1)
y = self.conv(y)
return y
class dcn2(paddle.nn.Layer):
def __init__(self, num_classes=1):
super(dcn2, self).__init__()
self.conv1 = paddle.nn.Conv2D(in_channels=3, out_channels=32, kernel_size=(3, 3), stride=1, padding = 1)
self.conv2 = paddle.nn.Conv2D(in_channels=32, out_channels=64, kernel_size=(3,3), stride=2, padding = 0)
self.conv3 = paddle.nn.Conv2D(in_channels=64, out_channels=64, kernel_size=(3,3), stride=2, padding = 0)
self.offsets = paddle.nn.Conv2D(64, 18, kernel_size=3, stride=2, padding=1)
self.mask = paddle.nn.Conv2D(64, 9, kernel_size=3, stride=2, padding=1)
self.conv4 = CoordConv(64, 64, (3,3), 2, 1)
self.flatten = paddle.nn.Flatten()
self.linear1 = paddle.nn.Linear(in_features=1024, out_features=64)
self.linear2 = paddle.nn.Linear(in_features=64, out_features=num_classes)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = self.conv3(x)
x = F.relu(x)
x = self.conv4(x)
x = F.relu(x)
x = self.flatten(x)
x = self.linear1(x)
x = F.relu(x)
x = self.linear2(x)
return x
cnn3 = dcn2() model3 = paddle.Model(cnn3) model3.summary((64, 3, 32, 32))
---------------------------------------------------------------------------
Layer (type) Input Shape Output Shape Param #
===========================================================================
Conv2D-26 [[64, 3, 32, 32]] [64, 32, 32, 32] 896
Conv2D-27 [[64, 32, 32, 32]] [64, 64, 15, 15] 18,496
Conv2D-28 [[64, 64, 15, 15]] [64, 64, 7, 7] 36,928
Conv2D-31 [[64, 66, 7, 7]] [64, 64, 4, 4] 38,080
CoordConv-4 [[64, 64, 7, 7]] [64, 64, 4, 4] 0
Flatten-1 [[64, 64, 4, 4]] [64, 1024] 0
Linear-1 [[64, 1024]] [64, 64] 65,600
Linear-2 [[64, 64]] [64, 1] 65
===========================================================================
Total params: 160,065
Trainable params: 160,065
Non-trainable params: 0
---------------------------------------------------------------------------
Input size (MB): 0.75
Forward/backward pass size (MB): 26.09
Params size (MB): 0.61
Estimated Total Size (MB): 27.45
---------------------------------------------------------------------------
{'total_params': 160065, 'trainable_params': 160065}
class MyNet(paddle.nn.Layer):
def __init__(self, num_classes=1):
super(MyNet, self).__init__()
self.conv1 = paddle.nn.Conv2D(in_channels=3, out_channels=32, kernel_size=(3, 3), stride=1, padding = 1)
self.conv2 = paddle.nn.Conv2D(in_channels=32, out_channels=64, kernel_size=(3,3), stride=2, padding = 0)
self.conv3 = paddle.nn.Conv2D(in_channels=64, out_channels=64, kernel_size=(3,3), stride=2, padding = 0)
self.conv4 = paddle.nn.Conv2D(in_channels=64, out_channels=64, kernel_size=(3,3), stride=2, padding = 1)
self.flatten = paddle.nn.Flatten()
self.linear1 = paddle.nn.Linear(in_features=1024, out_features=64)
self.linear2 = paddle.nn.Linear(in_features=64, out_features=num_classes)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = self.conv3(x)
x = F.relu(x)
x = self.conv4(x)
x = F.relu(x)
x = self.flatten(x)
x = self.linear1(x)
x = F.relu(x)
x = self.linear2(x)
return x
# 可視化模型 cnn1 = MyNet() model1 = paddle.Model(cnn1) model1.summary((64, 3, 32, 32))
---------------------------------------------------------------------------
Layer (type) Input Shape Output Shape Param #
===========================================================================
Conv2D-1 [[64, 3, 32, 32]] [64, 32, 32, 32] 896
Conv2D-2 [[64, 32, 32, 32]] [64, 64, 15, 15] 18,496
Conv2D-3 [[64, 64, 15, 15]] [64, 64, 7, 7] 36,928
Conv2D-4 [[64, 64, 7, 7]] [64, 64, 4, 4] 36,928
Flatten-1 [[64, 64, 4, 4]] [64, 1024] 0
Linear-1 [[64, 1024]] [64, 64] 65,600
Linear-2 [[64, 64]] [64, 1] 65
===========================================================================
Total params: 158,913
Trainable params: 158,913
Non-trainable params: 0
---------------------------------------------------------------------------
Input size (MB): 0.75
Forward/backward pass size (MB): 25.59
Params size (MB): 0.61
Estimated Total Size (MB): 26.95
---------------------------------------------------------------------------
{'total_params': 158913, 'trainable_params': 158913}
總結(jié)
相信通過(guò)之前的教程,相信大家已經(jīng)能夠熟練掌握了迅速開(kāi)啟訓(xùn)練的方法。所以,之后的教程我都會(huì)關(guān)注于具體的代碼實(shí)現(xiàn)以及相關(guān)的理論介紹。如無(wú)必要,不再進(jìn)行對(duì)比實(shí)驗(yàn)。本次教程主要對(duì)CoordConv的理論進(jìn)行了介紹,對(duì)其進(jìn)行了復(fù)現(xiàn),并展示了其在網(wǎng)絡(luò)結(jié)構(gòu)中的用法。大家可以根據(jù)的實(shí)際需要,將其移植到自己的網(wǎng)絡(luò)中。
一些需要注意的點(diǎn)
CoordConv的位置在網(wǎng)絡(luò)中應(yīng)該盡量靠前
最好的應(yīng)用方向是姿態(tài)估計(jì)等對(duì)位置高度敏感的CV任務(wù)
以上就是CoordConv實(shí)現(xiàn)卷積加上坐標(biāo)實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于CoordConv卷積加坐標(biāo)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- python深度學(xué)習(xí)tensorflow卷積層示例教程
- Caffe卷積神經(jīng)網(wǎng)絡(luò)solver及其配置詳解
- Caffe卷積神經(jīng)網(wǎng)絡(luò)視覺(jué)層Vision?Layers及參數(shù)詳解
- Caffe卷積神經(jīng)網(wǎng)絡(luò)數(shù)據(jù)層及參數(shù)
- Pytorch卷積神經(jīng)網(wǎng)絡(luò)遷移學(xué)習(xí)的目標(biāo)及好處
- Pytorch深度學(xué)習(xí)經(jīng)典卷積神經(jīng)網(wǎng)絡(luò)resnet模塊訓(xùn)練
相關(guān)文章
在GitHub Pages上使用Pelican搭建博客的教程
這篇文章主要介紹了在GitHub Pages上使用Pelican搭建博客的教程,Pelican是一個(gè)使用Python實(shí)現(xiàn)的開(kāi)源博客系統(tǒng),需要的朋友可以參考下2015-04-04
Python可視化Matplotlib介紹和簡(jiǎn)單圖形的繪制
這篇文章主要介紹了Python可視化Matplotlib介紹和簡(jiǎn)單圖形的繪制,文中附含詳細(xì)示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09
Python TCPServer 多線程多客戶端通信的實(shí)現(xiàn)
這篇文章主要介紹了Python TCPServer 多線程多客戶端通信的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
用python實(shí)現(xiàn)日志文件,并且按時(shí)間命名文件名方式
這篇文章主要介紹了用python實(shí)現(xiàn)日志文件,并且按時(shí)間命名文件名方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Python簡(jiǎn)單檢測(cè)文本類型的2種方法【基于文件頭及cchardet庫(kù)】
這篇文章主要介紹了Python簡(jiǎn)單檢測(cè)文本類型的方法,結(jié)合實(shí)例形式分析了基于基于文件頭及cchardet庫(kù)兩種文本類型檢測(cè)的方法,需要的朋友可以參考下2016-09-09

