其实你去看pytorch中的nn.Conv2d的源码的时候,你会发现它还是调用的nn.functional.conv2d,如下图所示:
显然这在前向传播中我们已经用的非常多了,有时候在跟踪里面会计算template和search的互相关,也是利用F.conv2d函数实现的,本篇文章用生动易懂的方式讲解一下F.conv2d前向传播和反向传播的过程。
前向传播(forward propagation)
官方的参数列表是这样的:
torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1)
- input – input tensor of shape (minibatch,in_channels,iH,iW)
- weight – filters of shape(out_channels, i n _ c h a n n e l s g r o u p s \frac {in\_channels}{groups} groupsin_channels,kH,kW)
- output – output tensor of shape (minibatch,out_channels,oH,oW)
看一下一个例子,假设
这里按照卷积的公式和上面的文档就可以容易得出。也可以看下面的图:
其实上面还涉及了分组卷积,把1024的通道数改成了2个512,此时也就有2个512通道的卷积核
f
1
f_{1}
f1,
f
2
f_{2}
f2,分别与
g
1
g_{1}
g1,
b
1
b_{1}
b1,
y
1
y_{1}
y1和
g
2
g_{2}
g2,
b
2
b_{2}
b2,
y
2
y_{2}
y2卷积得到
g
11
g_{11}
g11,
b
11
b_{11}
b11,
y
11
y_{11}
y11和
g
22
g_{22}
g22,
b
22
b_{22}
b22,
y
22
y_{22}
y22,这样输出就是3个2通道的19×19的tensor了。
反向传播(backward propagation)
前面的前向传播较好理解,不过也是理解下面用F.conv2d实现反向传播的基础,让我们先看一下简单的反向传播例子:
假设有一张1×1的feature map,然后周围padding=2,用4×4的卷积核去卷积,得到一张2×2的输出map
左图中四种不同的颜色的4×4的框表示卷积核滑动窗口在不同的位置,右边是其对应的输出位置,由相同颜色对应。
假设现在得到了一张2×2的上游传过来的梯度map,这时要计算相对于卷积核的梯度,这时我们知道,卷积核和特征feature map之间就是+和×的操作,所以对于卷积核的梯度可以由下图四张4×4的特征值和对应上游梯度位置相乘后相加得到(即图中的土色tensor)
那有没有更高效的方法实现上面的四张4×4的特征值和对应上游梯度位置相乘后相加
这个操作呢?不难发现,这种相乘相加的操作不就是卷积嘛?再仔细看一眼,发现F.conv2d(feature_map, upstream_gradient)就是最后的图中土色的tensor
那对于一般的情况该怎么操作呢?还是拿上面前向传播的例子举例:
此时我们就需要
g
11
g_{11}
g11的上游梯度
G
11
G_{11}
G11和
g
1
g_{1}
g1做卷积得到
f
g
1
f_{g1}
fg1,
g
22
g_{22}
g22的上游梯度
G
22
G_{22}
G22和
g
2
g_{2}
g2做卷积得到
f
g
2
f_{g2}
fg2,以此类推。最后
f
g
1
+
f
b
1
+
f
y
1
=
∂
f
1
f_{g1}+f_{b1}+f_{y1}=\partial f_{1}
fg1+fb1+fy1=∂f1
f
g
2
+
f
b
2
+
f
y
2
=
∂
f
2
f_{g2}+f_{b2}+f_{y2}=\partial f_{2}
fg2+fb2+fy2=∂f2
如下图所示的:
只不过需要在输入的时候转化一下维度,具体过程可见下面:
更一般的总结就是
F.conv2d(input, weight, padding=p, groups=g)
- forward: when given
input tensor shape: [B, Cin, iH, iW]
weight tensor shape: [g, C i n g \frac {Cin}{g} gCin, kH, kW]
we can get output tensor shape: [B, g, oH, oW]- backward: when given
input tensor shape: [B, Cin, iH, iW]
upstream gradient shape: [B, g, oH, oW]
we can get gradient relative to weight by following:
2.1 F.conv2d([ C i n g \frac {Cin}{g} gCin, Bg, iH, iW], [Bg,1,oH,oW], groups=Bg, padding=p) ,and get [ C i n g \frac {Cin}{g} gCin,Bg,kH,kW]
2.2 [ C i n g \frac {Cin}{g} gCin,Bg,kH,kW] view as [ C i n g \frac {Cin}{g} gCin,B, g,kH,kW] and sum along with dim=1, get [ C i n g \frac {Cin}{g} gCin,g,kH,kW]
2.3 [ C i n g \frac {Cin}{g} gCin,g,kH,kW] re-view as [g, C i n g \frac {Cin}{g} gCin,kH,kW] same shape with weight