您现在的位置是:首页 >其他 >转置卷积(一) 搞懂转置卷积的计算网站首页其他
转置卷积(一) 搞懂转置卷积的计算
搞懂转置卷积的计算
文章首发于https://zhaodongyu-ak47.github.io/Transposed_Convolution/
最近做了一些转置卷积的部署工作,最开始搞的时候其实有点晕头转向的,总是在用卷积的计算方式反过来理解转置卷积,尤其是padding
部分和stride
部分,搞得我头更大了。
现在也算是了解了具体工作机制以及加速方式,在这里整理总结一下。欢迎留言、指正 ?
0、参考文档
先敬上各位大佬的文档,这对我非常非常有帮助!
tf.keras.layers.Conv2DTranspose
What is Transposed Convolutional Layer?
Up-sampling with Transposed Convolution
图解转置卷积,我分别在conv_arithmetic和What is Transposed Convolutional Layer里看到,感觉后者更容易理解。
1、转置卷积是什么?
1.1 定义
转置卷积
有时候也被称为反卷积
,我个人认为反卷积
有很强的误导性,因为这并不是卷积的逆运算,还是叫转置卷积
比较好。
转置卷积
在深度学习中表示为卷积
的一个逆向过程,可以根据卷积核大小和输出的大小,恢复卷积前的feature map
尺寸,而不是恢复原始值。
如果将卷积表示为y=Cx,转置卷积则是将的输入输出互换:x = CTy
其中, CT表示矩阵转置。
详细定义这里就不仔细介绍了,上文里的各个参考文档里说的都很明白。
1.2 需要注意
总结一下我认为的最重要的(最开始纠结了很久的)几个点:
-
转置卷积不是恢复原始值,而是恢复原始尺寸(所以不要试图从卷积的逆运算角度考虑)
-
padding
方式和卷积的padding是不一样的,转置卷积的实际padding是k-p-1
-
stride
在这里用途不是跳几个数,而是用于判断填充几个0 -
用公式法直接计算的话,首先对卷积核做中心对称操作(矩阵旋转180°)
-
不考虑性能的话,直接按照转置卷积定义写。反之,一定要优化,不然慢得很。
The table below summarizes the two convolutions, standard and transposed.
Conv Type | Operation | Zero Insertions | Padding | Stride | Output Size |
---|---|---|---|---|---|
Standard | Downsampling | 0 | p | s | (i+2p-k)/s+1 |
Transposed | Upsampling | (s-1) | (k-p-1) | 1 | (i-1)*s+k-2p |
2、转置卷积的计算
2.1 从最简单的开始
conv_transpose有一种最直接的计算方式:首先对卷积核做中心对称操作(矩阵旋转180°),并对输入feature map进行插0,然后把旋转后的卷积核和插0后的feature map进行卷积操作
现在假设输入的feature map是3x3大小,kernel size是3x3大小,stride为1, padding为0,即:
input_sz: 3
kernel_sz = 3
stride = 1
padding_sz = 0
写一段torch代码计算一下:
import torch
X = torch.Tensor([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]])
K = torch.Tensor([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]])
Y = torch.nn.functional.conv_transpose2d(X, K, stride=1, padding=0)
print(Y)
得到输出结果:
tensor([[[[ 1., 4., 10., 12., 9.],
[ 8., 26., 56., 54., 36.],
[ 30., 84., 165., 144., 90.],
[ 56., 134., 236., 186., 108.],
[ 49., 112., 190., 144., 81.]]]])
计算过程:
(1) 对输入X进行处理,插入(s-1)的0,做(k-p-1)的padding
在这个例子中,s=1,则无需插入0,只进行(k-p-1)=(3-0-1)=2的padding。输入X则转化为
[ 1 2 3 4 5 6 7 8 9 ] − > [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 0 0 0 0 4 5 6 0 0 0 0 7 8 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] egin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \ 7 & 8 & 9 end{bmatrix} -> egin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0 & 0 \ 0 & 0 & 0 & 0 & 0 & 0 & 0 \ 0 & 0 & 1 & 2 & 3 & 0 & 0 \ 0 & 0 & 4 & 5 & 6 & 0 & 0 \ 0 & 0 & 7 & 8 & 9 & 0 & 0 \ 0 & 0 & 0 & 0 & 0 & 0 & 0 \0 & 0 & 0 & 0 & 0 & 0 & 0 end{bmatrix} 147258369 −> 0000000000000000147000025800003690000000000000000
(2) 对卷积核K进行中心对称操作
卷积核K则转化为为
[
1
2
3
4
5
6
7
8
9
]
−
>
[
9
8
7
6
5
4
3
2
1
]
egin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \ 7 & 8 & 9 end{bmatrix} -> egin{bmatrix} 9 & 8 & 7 \ 6 & 5 & 4 \ 3 & 2 & 1 end{bmatrix}
147258369
−>
963852741
(3) 进行卷积计算
[
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
2
3
0
0
0
0
4
5
6
0
0
0
0
7
8
9
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
]
∗
[
9
8
7
6
5
4
3
2
1
]
=
[
1
4
10
12
9
8
26
56
54
36
30
84
165
144
90
56
134
236
186
108
49
112
190
144
81
]
egin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0 & 0 \ 0 & 0 & 0 & 0 & 0 & 0 & 0 \ 0 & 0 & 1 & 2 & 3 & 0 & 0 \ 0 & 0 & 4 & 5 & 6 & 0 & 0 \ 0 & 0 & 7 & 8 & 9 & 0 & 0 \ 0 & 0 & 0 & 0 & 0 & 0 & 0 \0 & 0 & 0 & 0 & 0 & 0 & 0 end{bmatrix} * egin{bmatrix} 9 & 8 & 7 \ 6 & 5 & 4 \ 3 & 2 & 1 end{bmatrix} = egin{bmatrix} 1 & 4 & 10 & 12 & 9 \ 8 & 26 & 56 & 54 & 36 \ 30 & 84 & 165 & 144 & 90 \ 56 & 134 & 236 & 186 & 108 \ 49 & 112 & 190 & 144 & 81 end{bmatrix}
0000000000000000147000025800003690000000000000000
∗
963852741
=
1830564942684134112105616523619012541441861449369010881
(4) gif图解
2.2 考虑stride
我个人建议不要用卷积的stride来理解转置卷积的stride,stride
在这里用途不是跳几个数,而是用于判断填充几个0。
现在假设输入的feature map是3x3大小,kernel size是3x3大小,stride为2, padding为0,即:
input_sz: 3
kernel_sz = 3
stride = 2
padding_sz = 0
同样,写一段torch代码计算一下:
import torch
X = torch.Tensor([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]])
K = torch.Tensor([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]])
Y = torch.nn.functional.conv_transpose2d(X, K, stride=2, padding=0)
print(Y)
得到输出结果:
tensor([[[[ 1., 2., 5., 4., 9., 6., 9.],
[ 4., 5., 14., 10., 24., 15., 18.],
[ 11., 16., 40., 26., 60., 36., 45.],
[ 16., 20., 44., 25., 54., 30., 36.],
[ 35., 46., 100., 56., 120., 66., 81.],
[ 28., 35., 74., 40., 84., 45., 54.],
[ 49., 56., 119., 64., 135., 72., 81.]]]])
计算过程:
(1) 对输入X进行处理,插入(s-1)的0,做(k-p-1)的padding
在这个例子中,s=2,需插入1个0,进行(k-p-1)=(3-0-1)=2的padding。输入X则转化为
[ 1 2 3 4 5 6 7 8 9 ] − > [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 2 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 5 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 8 0 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] egin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \ 7 & 8 & 9 end{bmatrix} -> egin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \ 0 & 0 & 1 & 0 & 2 & 0 & 3 & 0 & 0 \0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\ 0 & 0 & 4 & 0 & 5 & 0 & 6 & 0 & 0 \0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\ 0 & 0 & 7 & 0 & 8 & 0 & 9 & 0 & 0 \ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 end{bmatrix} 147258369 −> 000000000000000000001040700000000000002050800000000000003060900000000000000000000
(2) 对卷积核K进行中心对称操作
卷积核K则转化为为
[
1
2
3
4
5
6
7
8
9
]
−
>
[
9
8
7
6
5
4
3
2
1
]
egin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \ 7 & 8 & 9 end{bmatrix} -> egin{bmatrix} 9 & 8 & 7 \ 6 & 5 & 4 \ 3 & 2 & 1 end{bmatrix}
147258369
−>
963852741
(3) 进行卷积计算
[
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
2
0
3
0
0
0
0
0
0
0
0
0
0
0
0
0
4
0
5
0
6
0
0
0
0
0
0
0
0
0
0
0
0
0
7
0
8
0
9
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
]
∗
[
9
8
7
6
5
4
3
2
1
]
=
[
1
2
5
4
9
6
9
4
5
14
10
24
15
18
11
16
40
26
60
36
45
16
20
44
25
54
30
36
35
46
100
56
120
66
81
28
35
74
40
84
45
54
49
56
119
64
135
72
81
]
egin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \ 0 & 0 & 1 & 0 & 2 & 0 & 3 & 0 & 0 \0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\ 0 & 0 & 4 & 0 & 5 & 0 & 6 & 0 & 0 \0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\ 0 & 0 & 7 & 0 & 8 & 0 & 9 & 0 & 0 \ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 end{bmatrix}* egin{bmatrix} 9 & 8 & 7 \ 6 & 5 & 4 \ 3 & 2 & 1 end{bmatrix} = egin{bmatrix} 1 & 2 & 5 & 4 & 9 & 6 & 9\ 4 & 5 & 14 & 10 & 24 & 15 & 18\ 11 & 16 & 40 & 26 & 60 & 36 & 45\ 16 & 20 & 44 & 25 & 54 & 30 & 36\ 35 & 46 & 100 & 56 & 120 & 66 & 81\ 28 & 35 & 74 & 40 & 84 & 45 & 54\ 49 & 56 & 119 & 64 & 135 & 72 & 81 end{bmatrix}
000000000000000000001040700000000000002050800000000000003060900000000000000000000
∗
963852741
=
141116352849251620463556514404410074119410262556406492460541208413561536306645729184536815481
(4) gif图解
2.3 考虑padding
我最开始在padding
这里疑惑了好一会儿,老是在从卷积的角度想转置卷积的padding
。就很疑惑,怎么padding
越大,计算结果的feature map
越小呢?
后来也不想具体物理含义了,直接认为转置卷积的实际padding
是k-p-1
,万事大吉。
实际上,tensorflow的padding计算还是有点差异的,除了上面所说的计算,在计算padding的时候还有一个专门针对转置卷积的offset
,这可能会导致 左右/上下 的padding数不一致。
为什么这么做呢?个人认为要从转置卷积的目的来看————还原原始feature map
的尺寸。
本文暂不考虑这种情况,感兴趣的可以查看tensorflow源码。
现在假设输入的feature map是3x3大小,kernel size是3x3大小,stride为1, padding为1,即:
input_sz: 3
kernel_sz = 3
stride = 1
padding_sz = 1
写一段torch代码计算一下:
import torch
X = torch.Tensor([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]])
K = torch.Tensor([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]])
Y = torch.nn.functional.conv_transpose2d(X, K, stride=1, padding=1)
print(Y)
得到输出结果:
tensor([[[[ 26., 56., 54.],
[ 84., 165., 144.],
[134., 236., 186.]]]])
计算过程:
(1) 对输入X进行处理,插入(s-1)的0,做(k-p-1)的padding
在这个例子中,s=1,则无需插入0,只进行(k-p-1)=(3-1-1)=1的padding。输入X则转化为
[ 1 2 3 4 5 6 7 8 9 ] − > [ 0 0 0 0 0 0 1 2 3 0 0 4 5 6 0 0 7 8 9 0 0 0 0 0 0 ] egin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \ 7 & 8 & 9 end{bmatrix} -> egin{bmatrix} 0 & 0 & 0 & 0 & 0 \ 0 & 1 & 2 & 3 & 0 \ 0 & 4 & 5 & 6 & 0 \ 0 & 7 & 8 & 9 & 0 \ 0 & 0 & 0 & 0 & 0 end{bmatrix} 147258369 −> 0000001470025800369000000
(2) 对卷积核K进行中心对称操作
卷积核K则转化为为
[ 1 2 3 4 5 6 7 8 9 ] − > [ 9 8 7 6 5 4 3 2 1 ] egin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \ 7 & 8 & 9 end{bmatrix} -> egin{bmatrix} 9 & 8 & 7 \ 6 & 5 & 4 \ 3 & 2 & 1 end{bmatrix} 147258369 −> 963852741
(3) 进行卷积计算
[
0
0
0
0
0
0
1
2
3
0
0
4
5
6
0
0
7
8
9
0
0
0
0
0
0
]
∗
[
9
8
7
6
5
4
3
2
1
]
=
[
26
56
54
84
165
144
134
236
186
]
egin{bmatrix} 0 & 0 & 0 & 0 & 0 \ 0 & 1 & 2 & 3 & 0 \ 0 & 4 & 5 & 6 & 0 \ 0 & 7 & 8 & 9 & 0 \ 0 & 0 & 0 & 0 & 0 end{bmatrix}* egin{bmatrix} 9 & 8 & 7 \ 6 & 5 & 4 \ 3 & 2 & 1 end{bmatrix} = egin{bmatrix} 26 & 56 & 54\ 84 & 165 & 144 \ 134 & 236 & 186end{bmatrix}
0000001470025800369000000
∗
963852741
=
26841345616523654144186
(4) gif图解
2.4 考虑dilation
这里就不考虑了,和卷积一样的,很容易理解。
3 转置卷积的加速
篇幅有限,转置卷积的加速工作就放到下一篇 转置卷积(二) 对转置卷积进行加速 吧
本文用图参考了aqeelanwar的代码,非常感谢。
图像压缩用了iloveimg,非常好用~