一文教你搞懂2D卷积和3D卷积
前言
本人最近在搞毕设时发现自己一直会搞混2D卷积和3D卷积,于是在网上查阅了大量资料,终于明白了其中的原理。希望刷到这篇博客的小伙伴能够停下来静心阅读10分钟,相信你读完之后一定会有所收获。
2D卷积
单通道卷积
在深度学习中,卷积是最基本的乘法和加法,对于一幅只有一个信道的图像,其卷积如图所示(本例中stride = 1, padding = 0)。
这里的过滤器是一个3×3的矩阵,它的元素是[[0,1,2],[2,2,0],[0,1,2]]。过滤器在输入端滑动。在每个位置,它都在做元素的乘法和加法。每个滑动位置都以一个数字结束。最终的输出是一个3x3矩阵。
多通道卷积
多通道卷积过程如下:每个kernel都应用于前一层的输入通道,以生成一个输出通道。我们对所有kernel重复这个过程以生成多个通道。然后将这些通道汇总在一起,形成一个单独的输出通道。
这里的输入层是一个5×5×3的矩阵,有3个通道。Convolution Filter是一个3×3×3矩阵。首先,Filter中的每个kernel分别应用于输入层中的三个通道,执行三个卷积,得到3个通道,大小为3 x 3。
在5×5矩阵上执行遍历的就是每一个kernel。然后这三个输出的通道相加(元素相加)形成一个单独的通道(3 x 3 x 1)。这个最终的单通道就是使用Filter(3 x 3 x 3矩阵)对输入层(5 x 5 x 3矩阵)进行卷积的结果。
我们可以认为此过程是将3D Filter矩阵滑过输入层。请注意,输入层的通道数和Filter的Kernel数相同。3D Filter 只能在图像的2个方向(高度和宽度)上移动(这就是为什么这种操作被称为2D卷积的原因,尽管3D Filter 用于处理3D体积(高度×宽度×通道数)数据)。在每个滑动位置,我们执行逐元素的乘法和加法运算,结果为单个数字。
在以下示例中,滑动在水平5个位置和垂直5个位置(5=7-3+1)进行。在深度方向上再进行元素相加后,我们得到一个输出通道。
现在我们可以知道如何在不同深度的图层之间进行变换。假设输入层具有Din通道,而我们希望输出层具有Dout通道。我们需要做的只是将Dout 个Filter应用于输入层,每个Filter都有Din个kernel,每个Filter提供一个输出通道。应用Dout个Filter后,我们将拥有Dout个通道,然后可以将它们堆叠在一起以形成输出层。(输出层的Dout个通道也可称为Dout个Feature map,因此输出的Feature map数实际与Convolution Filter的数量相同。)
3D卷积
在上面已经解释过,虽然我们是在3D数据(高度×宽度×通道数)上进行卷积,但由于Convolution Filter只能在高度和宽度方向上移动,因此仍被称为2D卷积,一个Filter和一张图像卷积只能生成一个通道的输出数据。
3D卷积使用的数据和2D卷积最大的不同就在于数据的时序性。3D卷积中的数据通常是视频的多个帧或者是一张医学图像的多个分割图像堆叠在一起,这样每帧图像之间就有时间或者空间上的联系。
下面这张图可以说明3D卷积的大致过程。左边是输入层,此时我们在空间上看到的3维(即深度方向)不再表示通道数,而是图片的帧数,**也就是说这其实是图像中的单个通道的一帧帧图片堆叠之后的效果。**因此对于三通道(RGB)图像组成的一段视频,实际应该包含三个这样的3D数据,这里只是其中一个通道。Convolution Filter的深度不再与通道数相同(很显然),只需要满足<=帧数即输入层的深度即可。总结一下,输入层现在的维度是(Channel×Depth1×Height1×Width1),Filter的维度是(Depth2×Height2×Width2)。
自然地,卷积核就可以在深度、高度、宽度三个方向上自由移动,两个立方体之间的每一层进行卷积然后再在深度上逐元素相加,得到一个数据,形成一个平面后,卷积核向深度方向移动,继续卷积,于是就输出了一个3D的Feature map。
有人可能会问,一个通道的多个帧和一个Filter生成了一个3D Feature map,那三通道的图像卷积最后就一定生成三个3D Feature map吗,它们的转化关系究竟是怎么样的??关于这里的疑惑,我们放到代码部分来讲解。先再次强调,输出层的通道数就等价于输出层的Feature map数!!
代码部分
相信通过上面理论部分知识的讲解,小伙伴已经能看懂个大概了,接下来我们就用代码来证明一下。这里使用的是深度学习框架Pytorch。
2D Convolution
首先创建一个模型:
import torch as t
import torch.nn as nnclass A(nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(2, 2, 3)self.conv2 = nn.Conv2d(2, 2, 3)self.conv3 = nn.Conv2d(2, 2, 3)
这个模型非常的简单,就是三个2D卷积层的结构,每个卷积层的输入通道数和输出通道数均为2,卷积核的size是(3,3)。具体可以参照以下参数定义:
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
然后我们输出模型每一层的参数:
a=A()
print(list(a.parameters()))
结果如下:
Parameter containing:
tensor([[[[-0.0299, 0.0891, 0.0303],[ 0.0869, -0.0230, -0.1760],[ 0.1408, 0.0348, 0.1795]],[[ 0.2001, 0.0023, -0.1775],[ 0.0947, -0.0231, -0.1756],[ 0.1201, -0.0997, -0.0303]]],[[[-0.0425, 0.0748, -0.1754],[-0.1191, -0.1203, -0.1219],[-0.0794, 0.0895, -0.1719]],[[ 0.1968, -0.0463, 0.0550],[-0.0386, 0.1594, 0.1282],[-0.0009, 0.2167, -0.1783]]]], requires_grad=True)
Parameter containing:
tensor([ 0.0147, -0.0406], requires_grad=True)
Parameter containing:
tensor([[[[-0.0578, -0.1114, -0.1194],[-0.1469, -0.1175, -0.1616],[-0.2289, -0.0975, -0.1700]],[[-0.0894, 0.0074, 0.1222],[-0.0176, -0.0509, 0.1622],[-0.0405, -0.1349, 0.1782]]],[[[-0.0739, 0.2167, 0.1864],[ 0.0956, -0.1761, 0.0464],[ 0.0062, -0.0685, 0.0748]],[[ 0.1085, 0.1481, 0.1334],[ 0.2236, -0.0706, -0.0224],[ 0.0079, -0.1835, -0.0407]]]], requires_grad=True)
Parameter containing:
tensor([-8.0720e-05, 1.6026e-01], requires_grad=True)
Parameter containing:
tensor([[[[-0.0702, 0.1846, 0.0419],[-0.1891, -0.0893, -0.0024],[-0.0349, -0.0213, 0.0936]],[[-0.1062, 0.1242, 0.0391],[-0.1924, 0.0535, -0.1480],[ 0.0400, -0.0487, -0.2317]]],[[[ 0.1202, 0.0961, 0.2336],[ 0.2225, -0.2294, -0.2283],[-0.0963, -0.0311, -0.2354]],[[ 0.0676, -0.0439, -0.0962],[-0.2316, -0.0639, -0.0671],[ 0.1737, -0.1169, -0.1751]]]], requires_grad=True)
Parameter containing:
tensor([-0.1939, -0.0959], requires_grad=True)
接下来我们需要对这个输出结果进行详细分析了。可以看出输出的一共有6个Tensor,即每一个卷积层输出了2个Tensor。首先观察一维Tensor,这个是self.bias,加到输出层的每个通道上,因此只有两个元素。那么为什么卷积层的参数Tensor的维度是(2,2,3,3)呢?因为在nn.Conv2d(2, 2, 3)中可以看中输出channel数是2,因此有2个filter,每个filter的深度又和输入层的channel数相同,所以每个filter的kernel数为2,每个kernel的size又是(3,3),所以最终维度是(2,2,3,3)。 代码输出结果证明了理论的正确性。
3D Convlution
同理,定义一个模型并输出每层参数:
import torch as t
import torch.nn as nnclass A(nn.Module):def __init__(self):super().__init__()# Conv3d的输入是5维的Tensor(N,C_in,D_in,H_in,W_in),输出为(N,C_out,D_out,H_out,W_out)self.conv1 = nn.Conv3d(1, # 输入图像的channel数,C_in3, # 卷积产生的channel数,C_outkernel_size=2, # 卷积核的尺寸,这里实际是(2,2,2),第一维表示卷积核处理的帧数stride=(1,1,1), # 卷积步长,(D,H,W)padding=(0,0,0), # 输入的每一条边补充0的层数,(D,H,W)bias=False)self.conv2 = nn.Conv3d(1, # 输入图像的channel数,C_in3, # 卷积产生的channel数,C_outkernel_size=2, # 卷积核的尺寸,这里实际是(2,2,2),第一维表示卷积核处理的帧数stride=(1, 1, 1), # 卷积步长,(D,H,W)padding=(0, 0, 0), # 输入的每一条边补充0的层数,(D,H,W)bias=False)if __name__ == "__main__":a=A()print(list(a.parameters()))
结果如下:
[Parameter containing:
tensor([[[[[-0.0935, 0.0865],[ 0.2209, -0.2845]],[[-0.0739, -0.1571],[-0.0937, 0.0381]]]],[[[[ 0.3118, 0.0228],[ 0.0528, 0.3362]],[[ 0.2571, -0.2860],[-0.0248, 0.0413]]]],[[[[ 0.2757, -0.2870],[-0.0762, -0.0426]],[[-0.1114, -0.2718],[-0.2009, 0.2822]]]]], requires_grad=True), Parameter containing:
tensor([[[[[ 0.2868, 0.2352],[-0.0007, 0.0850]],[[-0.3403, -0.1515],[ 0.1643, 0.0257]]]],[[[[ 0.0032, 0.1932],[-0.0097, -0.2940]],[[-0.0324, -0.1837],[ 0.1531, 0.0724]]]],[[[[-0.0221, -0.0570],[-0.2833, -0.0661]],[[ 0.2186, -0.3194],[ 0.2589, 0.3500]]]]], requires_grad=True)]
我们继续来分析这个结果,顺便解答上文中提到的疑惑。输出的每一层weight的维数是(3,1,2,2,2),这是为什么呢?因为输出的channel数是3,输入的channel数是1,因此每组filter个数只有一个,对应有3组;而每个filter的size是(2,2,2),因此最终构成了这样的维度。如果我们将输入channel数改为2,结果如下:
[Parameter containing:
tensor([[[[[ 0.0010, 0.2182],[ 0.1555, -0.2320]],[[-0.0709, 0.0921],[-0.1355, 0.0346]]],[[[-0.0892, -0.0357],[ 0.2133, 0.0405]],[[ 0.2069, 0.0941],[-0.0764, 0.1953]]]],[[[[ 0.2018, -0.2457],[-0.1158, -0.1205]],[[-0.0726, 0.0943],[ 0.0482, -0.0663]]],[[[-0.0650, -0.1981],[ 0.0315, 0.2254]],[[ 0.0718, 0.0973],[ 0.1592, -0.1737]]]],[[[[ 0.2142, 0.1077],[ 0.1751, 0.0160]],[[-0.1013, 0.0274],[ 0.1019, 0.1532]]],[[[-0.0256, 0.2313],[ 0.0342, -0.1884]],[[ 0.2467, 0.2350],[-0.0755, -0.0327]]]]], requires_grad=True), Parameter containing:
tensor([[[[[ 0.0423, 0.1196],[-0.0128, -0.2416]],[[-0.0489, -0.0292],[-0.1986, 0.0883]]],[[[-0.0821, -0.0545],[-0.0520, 0.0355]],[[-0.2379, 0.1677],[ 0.1035, 0.0444]]]],[[[[-0.1435, -0.1594],[-0.2482, 0.2464]],[[ 0.0265, 0.2154],[ 0.1938, -0.0941]]],[[[-0.2427, 0.1314],[ 0.2092, 0.0563]],[[-0.2206, -0.2361],[ 0.0972, 0.0308]]]],[[[[-0.1759, 0.2255],[-0.0830, 0.0939]],[[ 0.0004, -0.1010],[-0.2034, -0.0256]]],[[[-0.1844, 0.2256],[-0.1646, -0.0182]],[[-0.0159, -0.1947],[ 0.0426, -0.2360]]]]], requires_grad=True)]
可以看到输出的每一层weight的维度是(3,2,2,2,2),即每组filter有两个,一共有三组。所以现在的卷积过程等于是:一组filter中的一个filter和一个channel的多个帧分别进行3维的卷积,相乘相加后得到一个数据,然后两个数据相加得到最终的一个数据。 等于是下面这幅图进行两次,一个channel对应进行一次,然后最后再加上两个数据相加的过程。
所以最终的输出是三个绿色立方体堆叠在一起,形成了三个channel的feature map,即一个feature map不再是一个平面,而是一个三维的立方体了。在这里我们就可以看出,在3D卷积中,输出的feature map数(即输出通道数)仍然与卷积核的个数有关,不过在这里准确来说应该称为组数而不是个数,因为每一组可能会有多个filter,每组的filter数量就等于输入的通道数。
后言
看到这里,小伙伴有没有清楚2D卷积和3D卷积的原理了呢?如果觉得博主写的还不错,麻烦小伙伴点赞收藏加关注,也欢迎在评论区发言,谢谢!
一文教你搞懂2D卷积和3D卷积相关推荐
- 常规卷积,DW卷积和PW卷积的区别
常规卷积,DW卷积和PW卷积的区别 转载于卷积神经网络中的Separable Convolution 卷积神经网络在图像处理中的地位已然毋庸置疑.卷积运算具备强大的特征提取能力.相比全连接又消耗更少的 ...
- mysql 未找到命令_MySQL主从复制配置说明,一文教你搞懂数据库主从复制
一,MySQL主从配置原理 1. mysql支持的复制格式 基于语句复制(STATEMENT) (优点)基于statement复制的优点很明显,简单的记录执行语句同步到从库执行同样的语句,占用磁盘空间 ...
- 一文教你搞懂C语言的Q格式使用
用过DSP的应该都知道Q格式吧: 1 前言 Q格式是二进制的定点数格式,相对于浮点数,Q格式指定了相应的小数位数和整数位数,在没有浮点运算的平台上,可以更快地对浮点数据进行处理,以及应用在需要恒定分辨 ...
- js和jsp文件后缀还在傻傻分不清?一文教你搞懂来龙去脉
以js为后缀的文件是什么? 这个文件里面是JavaScript 脚本语言. JavaScript 是一种解释型语言. 因此,它不需要编译. JavaScript 以交互式和动态的方式呈现网页. Jav ...
- 深度学习:从2D卷积到3D卷积的简单理解
很多人容易混淆2D卷积和3D卷积的概念,把多通道的2D卷积当成3D卷积,本文展示了一种直观理解2D卷积和3D卷积的方式. 2D卷积 单通道 首先了解什么是卷积核,卷积核(filter)是由一组参数构成 ...
- 深度学习(6)之卷积的几种方式:1D、2D和3D卷积的不同卷积原理(全网最全!)
深度学习(6)之卷积的几种方式:1D.2D和3D卷积的不同卷积原理(全网最全!) 英文原文 :A Comprehensive Introduction to Different Types of Co ...
- 3d卷积和2d卷积1d卷积运算-CNN卷积核与通道讲解
全网最全的卷积运算过程:https://blog.csdn.net/Lucinda6/article/details/115575534?spm=1001.2101.3001.6661.1&u ...
- 不用NAS,无需attention,只用3x3卷积和ReLU 的SOTA算法RepVGG
本文转载自知乎,已获作者授权转载. 链接:https://zhuanlan.zhihu.com/p/344324470 2020年B站年度弹幕是"爷青回".一定有很多瞬间,让你感觉 ...
- java写一个外网访问的接口_【JAVA基础】一个案例搞懂类、对象、重载、封装、继承、多态、覆盖、抽象和接口概念及区别(中篇)...
0 前言 初学JAVA时,总会对一些概念一知半解,相互混淆,不明其设计的用意,如类.对象.重载.封装.继承.多态.覆盖.抽象类.接口概念.为便于理解和巩固,本文将基于一个案例及其变形,展现各个概念的定 ...
最新文章
- css伪类元素及选择器
- VS code 的变量设定
- 在python IDLE里执行py文件
- 电子商务之网购魅力何在?(网购用户行为分析)
- java对jar包的复制_Java安全之jar包调试技巧
- Controller的返回值
- 傲游浏览器语言怎么切换 傲游浏览器语言切换方法简述
- Stream操作Collection集合
- 微课|玩转Python轻松过二级:第2章课后习题解答(3课,79题)
- android数码管字体,matplotlib绘图时显示额外的“figure”浮窗
- matlab 对直方图均衡化,基于直方图均衡化的图像增强技术分析与Matlab实现_直方图均衡化matlab...
- android ui设计 面试问题,2019新版UI设计面试题汇总附答案
- java 切割冒号_java split 冒号(java中split是什么意思啊)
- 旋转框目标检测————关于旋转框定义和解决方案
- r语言html乱码,R语言:读入txt文件中文文本出现乱码解决方案
- C语言实验——逆置正整数
- 解决Page index must not be less than zero问题
- RabbitMQ实现即时通讯
- python应用seo_SEO快排技术和应用技术编程大全
- 自学数据结构_五月十日_综述