机器学习 / A LEAF IN THE GARDEN
转置卷积
机器学习 · 动手深度学习。来自 Daily Learning 的学习笔记。
步骤
- 输入图像 每个像素 与卷积核 中所有元素相乘
- 得到和卷积核大小相同的矩阵
- 每个 按原始像素位置放置
- 重叠处求和得到输出图像
步幅
移动一格时, 移动的格数就是卷积的步幅
转置卷积步幅 时,可以增大高宽
也相当于把 的像素拉开,空隙插入 个零,再做普通转置卷积
填充
和卷积填充相反,剥离 外缘的 圈像素
转置
对于卷积
- 可以对 构造一个带状矩阵
- 展开为向量
- 使卷积相当于矩阵乘法
此时转置卷积等价于
- 卷积:
- 转置卷积:
形状换算
输入高(宽)为 ,卷积核 ,步幅 ,填充 ,输出高(宽)为
- 卷积:
- 转置卷积:
如果让高宽成倍增加,则
实现
代码 4 · In [1]
import torch
from torch import nn
import util
已保存输出
/root/autodl-tmp/envs/daily_learning/lib/python3.11/site-packages/requests/__init__.py:92: RequestsDependencyWarning: Unable to find acceptable character detection dependency (chardet or charset_normalizer). warnings.warn(
基本转置卷积运算
代码 6 · In [2]
def trans_conv(X, K):
h, w = K.shape
# 创建一个全 0 矩阵承接输出
Y = torch.zeros((X.shape[0] + h - 1, X.shape[1] + w - 1))
for i in range(X.shape[0]):
for j in range(X.shape[1]):
Y[i:i+h, j:j+w] += X[i, j] * K # 逐位置叠加 K'
return Y
验证
代码 8 · In [3]
K = torch.FloatTensor([[1, 2], [3, 4]])
X = torch.FloatTensor([[0, 1], [2, 3]])
trans_conv(X, K)
已保存输出
tensor([[ 0., 1., 2.],
[ 2., 10., 10.],
[ 6., 17., 12.]])直接调用 api 也是一样的
代码 10 · In [4]
X = X.reshape((1, 1, 2, 2)) # (N, C, H, W)
K = K.reshape((1, 1, 2, 2)) # (C_out, C_in, H, W)
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, bias=False)
tconv.weight.data = K
tconv(X).squeeze() # (N, C, H, W) -> (H, W)
已保存输出
tensor([[ 0., 1., 2.],
[ 2., 10., 10.],
[ 6., 17., 12.]], grad_fn=<SqueezeBackward0>)填充、步幅和通道数
代码 12 · In [5]
tconv = nn.ConvTranspose2d(1, 1, kernel_size=3, padding=1, bias=False) # 剥离一圈
tconv.weight.data = K
tconv(X).squeeze((0, 1)) # (N, C, H, W) -> (H, W)
已保存输出
tensor([[10.]], grad_fn=<SqueezeBackward2>)
代码 13 · In [6]
tconv = nn.ConvTranspose2d(1, 1, kernel_size=3, stride=2, bias=False)
tconv.weight.data = K
tconv(X).squeeze()
已保存输出
tensor([[ 0., 0., 1., 2.],
[ 0., 0., 3., 4.],
[ 2., 4., 3., 6.],
[ 6., 8., 9., 12.]], grad_fn=<SqueezeBackward0>)代码 14 · In [7]
conv = nn.Conv2d(1, 8, kernel_size=2, stride=2, padding=2)
tconv = nn.ConvTranspose2d(8, 1, kernel_size=2, stride=2, padding=2)
Y = conv(X)
X.shape, Y.shape, tconv(Y).shape
已保存输出
(torch.Size([1, 1, 2, 2]), torch.Size([1, 8, 3, 3]), torch.Size([1, 1, 2, 2]))
与矩阵乘法的联系
代码 16 · In [12]
def conv_to_mm(X, K):
"""卷积转矩阵乘法"""
h, w = K.shape
V = torch.zeros((X.shape[0] + h - 1, X.shape[1] + w - 1, X.shape[0], X.shape[1]))
for i in range(X.shape[0]):
for j in range(X.shape[1]):
V[i:i+h, j:j+w, i, j] = K
return V.reshape((X.shape[0] + h - 1) * (X.shape[1] + w - 1), X.numel())
V = conv_to_mm(X.squeeze(), K.squeeze())
V
已保存输出
tensor([[1., 0., 0., 0.],
[2., 1., 0., 0.],
[0., 2., 0., 0.],
[3., 0., 1., 0.],
[4., 3., 2., 1.],
[0., 4., 0., 2.],
[0., 0., 3., 0.],
[0., 0., 4., 3.],
[0., 0., 0., 4.]])代码 17 · In [13]
Y = torch.matmul(V, X.squeeze().reshape(-1))
Y.reshape(3, 3)
已保存输出
tensor([[ 0., 1., 2.],
[ 2., 10., 10.],
[ 6., 17., 12.]])代码 18 · In [14]
Z = torch.matmul(V.T, Y.reshape(-1))
Z.reshape(2, 2)
已保存输出
tensor([[ 48., 75.],
[108., 129.]])