文章目录

  • 反卷积的作用
  • 卷积中padding的几个概念
    • No Padding
    • Half(Same) Padding
    • Full Padding
  • 反卷积
    • 反卷积中的Padding参数
    • 反卷积的stride参数
    • 反卷积的output_padding参数
  • 反卷积总结
  • 参考资料

反卷积的作用

传统的卷积通常是将大图片卷积成一张小图片,而反卷积就是反过来,将一张小图片变成大图片

但这有什么用呢?其实有用,例如,在生成网络(GAN)中,我们是给网络一个向量,然后生成一张图片

所以我们需要想办法把这个向量一直扩,最终扩到图片的的大小。

卷积中padding的几个概念

在了解反卷积前,先来学习传统卷积的几个padding概念,因为后面反卷积也有相同的概念

No Padding


No Padding就是padding为0,这样卷积之后图片尺寸就会缩小,这个大家应该都知道

下面的图片都是 蓝色为输入图片,绿色为输出图片。

Half(Same) Padding


Half Padding也称为Same Padding,先说SameSame指的就是输出的图片和输入图片的大小一致,而在stride为1的情况下,若想让输入输出尺寸一致,需要指定 p=⌊k/2⌋p=\lfloor k/2 \rfloorp=⌊k/2⌋,这就是 Half 的由来,即padding数为kerner_size的一半。

在 pytorch 中支持same padding,例如:

inputs = torch.rand(1, 3, 32, 32)
outputs = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=5, padding='same')(inputs)
outputs.size()
torch.Size([1, 3, 32, 32])

Full Padding

当 p=k−1p=k-1p=k−1 时就达到了 Full Padding。为什么这么说呢?可以观察上图,k=3k=3k=3,p=2p=2p=2,此时在第一格卷积的时候,只有一个输入单位参与了卷积。假设 p=3p=3p=3 了,那么就会存在一些卷积操作根本没有输入单位参与,最终导致值为0,那跟没做一个样。

我们可以用pytorch做个验证,首先我们来一个Full Padding:

inputs = torch.rand(1, 1, 2, 2)
outputs = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=2, bias=False)(inputs)
outputs
tensor([[[[-0.0302, -0.0356, -0.0145, -0.0203],[-0.0515, -0.2749, -0.0265, -0.1281],[ 0.0076, -0.1857, -0.1314, -0.0838],[ 0.0187,  0.2207,  0.1328, -0.2150]]]],grad_fn=<SlowConv2DBackward0>)

可以看到此时的输出都是正常的,我们将padding再增大,变为3:

inputs = torch.rand(1, 1, 2, 2)
outputs = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=3, bias=False)(inputs)
outputs
tensor([[[[ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000],[ 0.0000,  0.1262,  0.2506,  0.1761,  0.3091,  0.0000],[ 0.0000,  0.3192,  0.6019,  0.5570,  0.3143,  0.0000],[ 0.0000,  0.1465,  0.0853, -0.1829, -0.1264,  0.0000],[ 0.0000, -0.0703, -0.2774, -0.3261, -0.1201,  0.0000],[ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000]]]],grad_fn=<SlowConv2DBackward0>)

可以看到最终输出图像周围多了一圈 0,这就是部分卷积没有输入图片参与,导致无效了计算。

反卷积

反卷积其实和卷积是一样的,只不是参数对应关系有点变化。例如:


这是一个padding=0的反卷积,这时候你肯定要问了,这padding分明是2嘛,你怎么说是0呢?请看下面

反卷积中的Padding参数

在传统卷积中,我们的 padding 范围为 [0,k−1][0, k-1][0,k−1],p=0p=0p=0 被称为 No padding,p=k−1p=k-1p=k−1 被称为 Full Padding。

而在反卷积中的 p′p'p′ 刚好相反,也就是 p′=k−1−pp' = k-1 - pp′=k−1−p 。也就是当我们传 p′=0p'=0p′=0 时,相当于在传统卷积中传了 p=k−1p=k-1p=k−1,而传 p′=k−1p'=k-1p′=k−1 时,相当于在传统卷积中传了 p=0p=0p=0。

我们可以用如下实验进行验证:

inputs = torch.rand(1, 1, 32, 32)
# 定义反卷积,这里 p'=2, 为反卷积中的Full Padding
transposed_conv = nn.ConvTranspose2d(in_channels=1, out_channels=1, kernel_size=3, padding=2, bias=False)
# 定义卷积,这里p=0,为卷积中的No Padding
conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=0, bias=False)
# 让反卷积与卷积kernel参数保持一致,这里其实是将卷积核参数的转置赋给了反卷积
transposed_conv.load_state_dict(OrderedDict([('weight', torch.Tensor(np.array(conv.state_dict().get('weight'))[:, :, ::-1, ::-1].copy()))]))
# 进行前向传递
transposed_conv_outputs = transposed_conv(inputs)
conv_outputs = conv(inputs)# 打印卷积输出和反卷积输出的size
print("transposed_conv_outputs.size", transposed_conv_outputs.size())
print("conv_outputs.size", conv_outputs.size())# 查看它们输出的值是否一致。
#(因为上面将参数转为numpy,又转了回来,所以其实卷积和反卷积的参数是有误差的,
# 所以不能直接使用==,采用了这种方式,其实等价于==)
(transposed_conv_outputs - conv_outputs) < 0.01
transposed_conv_outputs.size:  torch.Size([1, 1, 30, 30])
conv_outputs.size:  torch.Size([1, 1, 30, 30])tensor([[[[True, True, True, True, True, True, True, True, True, True, True,.... //略

从上面例子可以看出来,反卷积和卷积其实是一样的,区别就几点:

  1. 反卷积进行卷积时,使用的参数是kernel的转置,但这项其实我们不需要关心
  2. 反卷积的padding参数 p′p'p′ 和 传统卷积的参数 ppp 的对应关系为 p′=k−1−pp'=k-1-pp′=k−1−p。换句话说,卷积中的no padding对应反卷积的full padding;卷积中的full padding对应反卷积中的no padding。
  3. 从2中还可以看到一个事情,在反卷积中 p′p'p′ 不能无限大,最大值为 k−1−pk-1-pk−1−p。(其实也不是哦)

题外话,不感兴趣去可以跳过,在上面第三点我们说了 p′p'p′ 的最大值为 k−1−pk-1-pk−1−p,但实际你用pytorch实验会发现,p′p'p′是可以大于这个值的。而这背后,相当于是对原始图像做了裁剪

在pytorch的nn.Conv2d中,padding是不能为负数的,会报错,但有时可能你需要让padding为负数(应该没这种需求吧),此时就可以用反卷积来实现,例如:

inputs = torch.ones(1, 1, 3, 3)
transposed_conv = nn.ConvTranspose2d(in_channels=1, out_channels=1, kernel_size=1, padding=1, bias=False)
print(transposed_conv.state_dict())
outputs = transposed_conv(inputs)
print(outputs)
OrderedDict([('weight', tensor([[[[0.7700]]]]))])
tensor([[[[0.7700]]]], grad_fn=<SlowConvTranspose2DBackward0>)

上述例子中,我们传给网络的是图片:

[111111111]\begin{bmatrix} 1 & 1 &1 \\ 1 & 1 &1 \\ 1 & 1 &1 \end{bmatrix} ⎣⎡​111​111​111​⎦⎤​

但是我们传的p′=1,k=1p'=1, k=1p′=1,k=1,这样在传统卷积中相当于 p=k−1−p′=−1p=k-1-p'=-1p=k−1−p′=−1,相当于 Conv2d(padding=-1),这样在做卷积时,其实是对图片 [1][1][1] 在做卷积(因为把周围裁掉了一圈),所以最后输出的尺寸为 (1,1,1,1)(1,1,1,1)(1,1,1,1)

这个题外话好像没啥实际用途,就当是更加理解反卷积中的padding参数吧。


反卷积的stride参数

反卷积的stride这个名字有些歧义,感觉起的不怎么好,具体什么意思可以看下图:

左边是stride=1(称为No Stride)的反卷积,右边是stride=2 的反卷积。可以看到,他们的区别就是在原始图片的像素点中间填充了0。没错,在反卷积中,stride参数就是表示往输入图片每两个像素点中间填充0,而填充的数量就是 stride - 1

例如,我们对32x32的图片进行反卷积,stride=3,那么它就会在每两个像素点中间填充两个0,原始图片的大小就会变成32+31×2=9432+31\times 2=9432+31×2=94。用代码实验一下:

inputs = torch.ones(1, 1, 32, 32)
transposed_conv = nn.ConvTranspose2d(in_channels=1, out_channels=1, kernel_size=3, padding=2, stride=3, bias=False)
outputs = transposed_conv(inputs)
print(outputs.size())
torch.Size([1, 1, 92, 92])

我们来算一下,这里我使用了反卷积的Full Padding(相当于没有对原始图像的边缘进行padding),然后stride传了3,相当于在每两个像素点之间填充两个0,那么原始图像就会变成 94x94 的,然后kernal是3,所以最终的输出图片大小为 94−3+1=9294-3+1=9294−3+1=92.

反卷积的output_padding参数

不知道你有没有发现,如果卷积和反卷积的参数一致,卷积会让 AAA 尺寸变为 BBB 尺寸,那么反卷积就会将 BBB 尺寸变为 AAA 尺寸

举个例子:

inputs = torch.rand(1, 1, 32, 32)
outputs = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=18, padding=3, stride=1)(inputs)
outputs.size()
torch.Size([1, 1, 21, 21])

我们这里将32x32的图片通过卷积变为了 21x21。此时我们将卷积变为反卷积(参数不变),输入图片大小变为 21x21:

inputs = torch.rand(1, 1, 21, 21)
outputs = nn.ConvTranspose2d(in_channels=1, out_channels=1, kernel_size=18, padding=3, stride=1)(inputs)
outputs.size()
torch.Size([1, 1, 32, 32])

看,反卷积将 21x21 的图片又变回了 32x32,这也就是为什么要叫反卷积。

但。。,真的是这样嘛,我们再看一个例子:

inputs = torch.rand(1, 1, 7, 7)
outputs = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=0, stride=2)(inputs)
outputs.size()
torch.Size([1, 1, 3, 3])
inputs = torch.rand(1, 1, 8, 8)
outputs = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=0, stride=2)(inputs)
outputs.size()
torch.Size([1, 1, 3, 3])
inputs = torch.rand(1, 1, 3, 3)
outputs = nn.ConvTranspose2d(in_channels=1, out_channels=1, kernel_size=3, padding=0, stride=2)(inputs)
outputs.size()
torch.Size([1, 1, 7, 7])

上面我们对7x7和8x8的图片都使用卷积操作,他们最后结果都是3x3,这样反卷积就会存在歧义,而反卷积默认选择了转换为7x7。原因可以见下图:

从这张图可以看到,8x8的图片其实最右边和最下边的一行是没有参与卷积运算的,这是因为stride为2,再走2步就超出图片范围了。所以7x7和8x8最终的结果都为3x3。

那么如果我们想让3x3的反卷积得8x8而不是7x7,那么我们就需要在输出图片边缘补充数据,具体补几行就是output_padding指定的。所以output_padding的作用就是:在输出图像右侧和下侧补值,用于弥补stride大于1带来的缺失。其中output_stadding必须小于stride

例如:

inputs = torch.rand(1, 1, 3, 3)
outputs = nn.ConvTranspose2d(in_channels=1, out_channels=1, kernel_size=3, padding=0, stride=2, output_padding=1)(inputs)
outputs

具体这个 0.2199 是什么我也不太清楚,我测试了发现并不是平均值

反卷积总结

  1. 反卷积的作用是将原始图像进行扩大

  2. 反卷积与传统卷积的区别不大,主要区别有:

    2.1 padding的对应关系变了,反卷积的padding参数 p′=k−1−pp' = k-1-pp′=k−1−p。其中 kkk 是kernel_size, p为传统卷积的padding值
    2.2 stride参数的含义不一样,在反卷积中stride表示在输入图像中间填充0,每两个像素点之间填充的数量为 stride-1
    2.3 除了上述的俩参数外,其他参数没啥区别

  3. 如果卷积和反卷积的参数一致,卷积会让 AA 尺寸变为 BB 尺寸,那么反卷积就会将 BB 尺寸变为 AA 尺寸

  4. output_padding的作用就是:在输出图像右侧和下侧补值,用于弥补stride大于1带来的缺失。其中output_stadding必须小于stride


参考资料

Convolution arithmetic: https://github.com/vdumoulin/conv_arithmetic

A guide to convolution arithmetic for deep
learning: https://arxiv.org/pdf/1603.07285.pdf

nn.ConvTranspose2d官方文档: https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose2d.html

What output_padding does in nn.ConvTranspose2d?:https://stackoverflow.com/questions/67096544/what-output-padding-does-in-nn-convtranspose2d

反卷积通俗详细解析与nn.ConvTranspose2d重要参数解释相关推荐

  1. Pytorch的nn.DataParallel详细解析

    前言 pytorch中的GPU操作默认是异步的,当调用一个使用GPU的函数时,这些操作会在特定设备上排队但不一定在稍后执行.这就使得pytorch可以进行并行计算.但是pytorch异步计算的效果对调 ...

  2. cnn stride and padding_彻底搞懂CNN中的卷积和反卷积

    前言 卷积和反卷积在CNN中经常被用到,想要彻底搞懂并不是那么容易.本文主要分三个部分来讲解卷积和反卷积,分别包括概念.工作过程.代码示例,其中代码实践部分主结合TensorFlow框架来进行实践.给 ...

  3. 生成式对抗网络GAN中的反卷积

    卷积层.反卷积层的理解(生成式对抗网络) 文章来源:感谢~Transposed Convolution, Fractionally Strided Convolution or Deconvoluti ...

  4. 机器学习19:反卷积算法

    机器学习19:反卷积算法(转载和整理) 在整理全卷积网络的过程中,被反卷积的概念困扰很久,于是将反卷积算法单独整理为一篇博客,本文主要转载和整理自知乎问题如何通俗易懂地解释反卷积?中的高票答案. 1. ...

  5. 【pytorch系列】卷积操作原理解析与nn.Conv2d用法详解

    参考: https://pytorch.org/docs/master/generated/torch.nn.Conv2d.html#torch.nn.Conv2d https://zhuanlan. ...

  6. DL之CNN:卷积神经网络算法简介之卷积矩阵、转置卷积(反卷积Transpose)、膨胀卷积(扩张卷积Dilated/带孔卷积atrous)之详细攻略

    DL之CNN:卷积神经网络算法简介之卷积矩阵.转置卷积(反卷积Transpose).膨胀卷积(扩张卷积Dilated/带孔卷积atrous)之详细攻略 目录 卷积矩阵的简介 卷积.转置卷积--Tran ...

  7. 详细解析反爬手段以及处理方案

    详细解析反爬手段以及处理方案 前言 ​ 互联网时代,无论在工作上,还是生活上都离不开网络,而网络能给我们带来什么? ​ 新闻,小说,资料,各行业的数据或者报表等等: ​ 比如:快毕业了为了论文,在各种 ...

  8. 反卷积原理和实际代码详细讲解!

    做的项目里涉及到了反卷积,上网查了很多资料发现有的只讲了原理,没有直观的代码实践,有些讲了代码却又完全忽视原理,所以想要以这篇博文做一个小小的整合,方便以后查阅. 文章目录 反卷积原理 首先来看卷积操 ...

  9. Tensorflow卷积与反卷积(目前看到的最详细的解释)

    卷积操作 tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None) 除去name参数用以指定该操作 ...

最新文章

  1. R语言tidyr包Unite()函数实战详解:多个数据列合并为一列
  2. 索尼AI CEO:我们要让AI在30年内拿到诺贝尔奖
  3. 关于ESP8266 GPIO中断使用的总结
  4. MYSQL5.7版本sql_mode=only_full_group_by问题
  5. 关于解决Win32控制台程序编译后自动退出
  6. Linux 中yum的配置
  7. Servlet---注解开发
  8. 安装oracle配置监听出错,安装失败,无法建立监听?
  9. 新时代培育新动能:2021年“专精特新”发展蓝皮书
  10. Spark-submit执行流程,了解一下
  11. python百分号字符串_python--003--百分号字符串拼接、format
  12. 20.Yii 工作流
  13. 20145307《信息安全系统设计基础》第十四周学习总结
  14. 得力助手 消防员的 消防机器人_机器人化身消防员“得力助手”,进入危险火场执行工作|机器人日报...
  15. C语言统计1到100素数的个数,统计1到100素数的个数
  16. hive表 合并字段_hive sql常用技巧
  17. 每天被信息轰炸的你,如何辨别新闻真假?
  18. 自动化测试和测试自动化你分的清楚吗?
  19. Qt的QBuffer
  20. 如何运行jnlp文件

热门文章

  1. js 关于 toFixed 问题的总结
  2. c语言 引用定义变量,如何在c语言中定义及引用全局变量?
  3. Mac M1 Java开发环境搭建
  4. take me to your heart(中英对照版)
  5. 号外号外!宠粉抽奖福利来啦!赶紧看过来!
  6. qt调用android键盘,QT 软键盘输入
  7. 基于android在线点单系统APP餐饮餐厅订餐点餐【可微信小程序与android studio运行】
  8. 豆瓣评分小程序Part-1
  9. 《孙子兵法》——读书笔记
  10. 食品安全问题使贸易冲突硝烟四起