深度学习-卷积层(代码+理论)python opencv源码(史上最全)
目录
1 什么是卷积
1.1 卷积能够干什么
图像识别网络的四大核心特性
1.2 卷积的原理
1.3 卷积的计算
一个卷积核
多个卷积核
卷积相关概念
2 卷积计算类型
通过前面一系列文章的介绍,我们算是对神经网络有了一个基本的概念,但是设前向传播难道就只有全连接层那种吗?也就是每个神经元都连接的那种网络。这里我们将从逻辑回归和线性回归为基础延伸到了深度学习中的分类和回归模型,并且得出的一个结论就是,对于输出层之前的所有层,我们都可以将其看成是一个特征提取的过程,而且越靠后的隐含层也就意味着提取得到的特征越抽象。在得到这些抽象特征后,我们再通过最后一层来完成特定场景下的任务,这也就是深度学习的核心思想。
1 什么是卷积
卷积(Convolution)是深度学习中最重要的技术之一,其历史可以追溯到上世纪八十年代。尽管这项技术已有近四十年的历史,但时至今日,它依然是各类神经网络模型的核心组成部分。
那么,究竟什么是卷积呢?许多初学者第一次接触“卷积”这个概念时,往往会联想到数学中的卷积定义——即“卷积是通过两个函数f和g生成第三个函数的数学算子,表征函数f与g经过翻转和平移后的重叠部分函数值乘积对重叠长度的积分”。然而,这样的数学解释虽然严谨,却容易让人望而生畏。
在深度学习中,卷积的本质其实更加直观:它是一种局部加权计算的方式,能够高效地从数据(如图像、信号)中提取特征。通过滑动一个卷积核(Kernel)在输入数据上进行逐点相乘并求和,卷积操作能够捕捉数据中的关键模式,例如图像中的边缘、纹理等。
因此,与其纠结于数学公式的抽象定义,不如从实际应用的角度理解卷积——它是让计算机“看懂”世界的一种强大工具。
1.1 卷积能够干什么
首先需要明确的是,卷积操作本质上也是一种特征提取技术,主要应用于图像处理领域。细心的读者可能还记得,我们之前介绍过如何使用深层全连接网络(Dense Network)进行图像分类:通过多层全连接层提取特征,最后用Softmax层完成分类。既然全连接网络也能提取特征,为什么我们还需要卷积呢?二者究竟有什么区别?
图像识别网络的四大核心特性
从人类认知的角度来看,一个优秀的图像识别网络应该具备以下四个关键特性:
-
平移不变性(Translation Invariance) 无论图像中的物体如何移动位置,网络都能准确识别。
-
旋转不变性(Rotation Invariance) 无论物体的角度如何变化,网络都能保持识别能力。
-
缩放不变性(Size Invariance) 无论物体被放大还是缩小,网络都能正确识别。
-
明暗不变性(Illumination Invariance) 无论光照条件如何变化,网络都能稳定识别目标。
这些特性不仅符合人类的视觉认知规律,也是数据增强技术(如平移、旋转、缩放、亮度调整)的理论基础。毕竟,一个实用的图像识别系统,总不能因为物体位置或角度的变化就失效,对吧?
那什么样的特征提取方式能够同时满足这四项特性呢?
首先我们用个程序体验一下卷积的魅力:
import torch
import torch.nn as nn
import torch.optim as optim
import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图像并转为灰度
input_image = cv2.imread("卷积/input/content.jpg", cv2.IMREAD_GRAYSCALE)# 转为 float32,归一化到 0~1
input_image = input_image.astype(np.float32) / 255.0# 创建边缘检测 kernel 并应用
kernel = np.array([[-1., -1., -1.],[-1., 1., -1.],[-1., -1., -1.]], dtype=np.float32)
target_edge_image = cv2.filter2D(input_image, -1, kernel)# 转为 torch tensor,调整维度为 [1, 1, H, W]
input_tensor = torch.tensor(input_image).unsqueeze(0).unsqueeze(0)
target_tensor = torch.tensor(target_edge_image).unsqueeze(0).unsqueeze(0)
# 进行数据类型转换,并归一化
target_tensor = target_tensor.float()
print("训练中。。。")
# 模型
class EdgeLearner(nn.Module):def __init__(self):super(EdgeLearner, self).__init__()self.conv = nn.Conv2d(1, 1, kernel_size=3, bias=False, padding=1)def forward(self, x):return self.conv(x)model = EdgeLearner()
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)# 训练
for epoch in range(100):optimizer.zero_grad()output = model(input_tensor)loss = criterion(output, target_tensor)loss.backward()optimizer.step()# 打印卷积核
print("训练后的卷积核:")
print(model.conv.weight.data[0, 0] )# 获取训练后的卷积算子
trained_kernel = model.conv.weight.data[0, 0].cpu().detach().numpy()import torch.nn.functional as F# 手动用训练后的卷积核进行卷积
trained_kernel_tensor = torch.tensor(trained_kernel).unsqueeze(0).unsqueeze(0) # 转为适合卷积的形状
output_manual = F.conv2d(input_tensor, trained_kernel_tensor, padding=1)
# 取出结果
output_manual_image = output_manual.squeeze().cpu().detach().numpy()demo2 = cv2.imread(r"卷积\input\test.jpg",cv2.IMREAD_GRAYSCALE)#新的图像,测试效果
# 转为 float32,归一化到 0~1
demo2 = demo2.astype(np.float32) / 255.0
# 手动用训练后的卷积核进行卷积
trained_kernel_tensor = torch.tensor(trained_kernel).unsqueeze(0).unsqueeze(0) # 转为适合卷积的形状
# 转为 torch tensor,调整维度为 [1, 1, H, W]
input_tensor2 = torch.tensor(demo2).unsqueeze(0).unsqueeze(0)output_manual2 = F.conv2d(input_tensor2, trained_kernel_tensor, padding=1)
# 取出结果
output_manual_image2 = output_manual2.squeeze().cpu().detach().numpy()
# 可视化结果
plt.figure(figsize=(12, 7))# 原图
plt.subplot(1, 5, 1)
plt.title("Input Image")
plt.imshow(input_image, cmap='gray')# 边缘检测处理图像
plt.subplot(1, 5, 2)
plt.title("Target Edge")
plt.imshow(target_edge_image, cmap='gray')# 训练后的卷积核
plt.subplot(1, 5, 3)
plt.title("Trained Kernel")
plt.imshow(trained_kernel, cmap='seismic')
plt.colorbar()# 训练后的卷积结果
plt.subplot(1, 5, 4)
plt.title("Trained Output")
plt.imshow(output_manual_image, cmap='gray')
# 新图片的测试效果
plt.subplot(1, 5, 5)
plt.title("test Output")
plt.imshow(output_manual_image2, cmap='gray')
plt.tight_layout()
plt.show()
我们读入一张图片,然后我用边缘卷积核:
kernel = np.array([[-1., -1., -1.],
[-1., 1., -1.],
[-1., -1., -1.]], dtype=np.float32)
处理了input_image原图,获得一张边缘图像
下面这句话可能有点绕,我拿这个边缘处理后的图像和原图进行训练,然后我设计一个神经网络让原图经过训练后让随便输入一张图片获得的效果都跟边缘处理的效果一样。
也就是数据是原图,答案是图像的边缘,我们通过机器学习后获得一个规则。以后我们就可以用这个规则代替边缘处理了,我们把这个规则套在任何图片上都能进行边缘处理。也就是我们知道规则以后,有数据就能知道答案了。
说这么多,你可能觉得有点绕,简单点说就是我想要训练获得一个卷积,这个卷积跟边缘卷积一样,对图像都能进行边缘处理。
如图,target edge是边缘卷积核处理后的效果。trained output是我们训练的卷积核处理后的效果。两个卷积核处理的效果是不是很像,也就是说我们训练的卷积核和边缘卷积核处理的效果是一样的,我们解决了这个任务。
那么以后要是想识别车,我们只要投入数据和答案,我们经过机器学习后,就学会这个规则,我们就能做一个识别汽车的系统了。说回我们的任务,我们卷积核和边缘卷积核的作用效果是一样的。
但是我发现训练出来的卷积核是
tensor([[-0.5322, -0.9783, -0.8349],
[-0.8978, -0.7059, -0.7655],
[-0.7865, -0.8618, -0.6314]])
但是边缘卷积核是
kernel = np.array([[-1., -1., -1.],
[-1., 1., -1.],
[-1., -1., -1.]], dtype=np.float32)
也就是两个卷积核是不一致的,但是他们两都能提取图像的边缘,也就是他们的方法不同,但是结果一样的。也就是神经网络能过自动去学习怎么解决这个任务,他跟正确的解决方式可能不太一样,但是他就是能解决任务,所以神经网络是一个黑匣子,经过这个黑匣子他就是能把任务给解决掉。
也就是以后我们不管遇到什么任务,准备好数据和答案,机器学习就能找到解决问题的方法。
然后我们用test图像测试效果,也得到了很好的解决。
1.2 卷积的原理
为了回答上面这个问题,下面我们就来对全连接和卷积操作进行一个比较,看看它们俩在工作原理上有何不同。下面我们以在大小为4x4的图片中识别是否有下图所示三角形形状目标。
如果是用全连接网络来识别这个三角形状目标,其中输入层输入的是由图片拉伸后的向量。
为了方便叙述,我们将输入层的神经元进行一个编号
我们通过卷积和全连接两种神经网络来识别这个三角形,我们先准备训练集,这里我们准备4种目标和标签,如图只有第一种目标才是true
如果我们通过卷积网络训练这个数据集,我们是可以设计一个2*2的卷积核在原图中移动,每次移动获取一个新的特征点。
卷积的计算过程就是卷积核先和原图先相乘,再把输出值加起来,获得一个输出点。
比如这个图,我们通过一个3*3的卷积核在原图中移动,然后获取一个新的特征点,拼凑成右边的特征图。特征图中每个像素点是原图的3*3个像素,也就是我们对原图进行信息浓缩,也就是对图像进行深层次的抽象。
感受野就是和卷积核相乘的那个区域,也就是感受野是随着卷积核的移动而变化的,相当于卷积核“看”到的区域。
这里涉及到CNN的一个特点——权值共享。注意到,对于上图的一轮卷积操作,不同感受野内右下角的权值矩阵是一样的,也就是说9次卷积的卷积核权值是一样的。权值共享有两个好处,一是特征位置无关,二是参数量大大下降。
也就是说权值共享的意思是图中3*3的卷积核数值一直为
{1 0 1}
{0 1 0}
{1 0 1}
他在处理一张图的时候卷积核的权重是不变的,也就是这张图共用一个卷积核参数。那么这一层卷积他的权重数就是:3*3+1=10,这里1是偏置。
这一层的计算量(FLOPs)是多少?计算量(FLOPs)通常指完成一次前向传播所需的浮点运算次数(包括乘法和加法)。
输入:5×5 的单通道图像
卷积核:3×3
输出特征图:3×3
-
滑动次数:
-
卷积核在输入上每次滑动1步(stride=1),横向滑动
5 - 3 + 1 = 3
次,纵向同理,共滑动3 × 3 = 9
次。
-
-
每次滑动的计算:
-
每个位置需要计算卷积核与输入局部区域的点积(即9次乘法和8次加法)。
-
乘法次数:
3×3 = 9
次(每个权重乘对应输入像素)。 -
加法次数:
9 - 1 = 8
次(累加9个乘积结果)。
-
-
总计算量(FLOPs):
-
每次滑动:
9(乘法) + 8(加法) = 17
FLOPs -
总计算量:
9次滑动 × 17 FLOPs = 153 FLOPs
-
对于特征位置无关 。这个3*3的卷积核相当于一个特征提取器或者说滤波器,比如这个特征提取器能够提取“猫”这个特征,则无论猫在输入图片的左上角还是右下角,“猫”这个特征都能被提取出来,因为卷积核在小范围移动,无论“猫”位于图片的哪个区域,当卷积核移动到这个区域时,卷积得到的输出比较大,被激活,得到“猫”这个特征。所以CNN对位置不敏感,这对图像处理尤其有利。正因为这个特点,经过卷积核卷积操作之后的小图片(上图右边的红色图片)被称为特征图(feature map),因为它就是用卷积核提取出来的符合这个卷积核描述的一个特征。
这里我们学会了特征图、感受野、卷积核的操作、计算量、卷积核权重数目、权值共享。试着问自己是否对这些概念有一定认知,并且能复述出来。
说回我们前面的三角目标识别,如果我们用卷积来完成的话
将具有识别某种特征能力的权重共享到其它位置上就是卷积操作的核心思想,它就像一个“扫描器”一样,能够逐个扫描所有位置上是否包含有“扫描器”能够识别的对应元素。
我们在对输入的图片进行特征提取时,不管潜在的三角形位于什么位置,我只需要用同一组权重对其每个位置进行扫描即可,只要某个位置存在三角形这么一个元素,那么模型都能将其识别出来。由此我们便可以得出卷积操作的核心原理,那就是在空间上共享权重。
而如果我们用全连接层的话就是将整张图拆成一个维度,并不很好的确定三角形的位置他只能学到全局的特征,并且数据量也太多了,如果是下图这种就是4096*4096个权重,比如这个4*4的图就有4*4个权重,不像卷积层一样。
上图中这种全连接层一般是中间结构过程中全连接层,就是在隐藏层中的全连接层,这种计算量是非常大的!如果是输出层的全连接层,输出层一般就只有一个输出。为什么是一般一个,这个需要根据任务来设计。
比如你是识别三角形,那么输出就只有是或者不是两种可能,所以这种是一个输出,也就是我们这里全连接层输出就只有一个神经元,那么就是4*4*1个权重,
如果是16个类别,那我们输出就不是一个神经元了,比如你要识别这张图是三角形还是四角形还是五角星等,一共16个类别的话,那这个全连接层就是16*16个权重。
咱们这里只识别一个是不是三角形,那么就是16个权重,比我们的卷积层3*3=9个权重多,而且这还是比较小的图片,这看不出差距。如果你的图片是640*640的图片呢?那你的全连接权重就非常夸张了而且全连接层对于我们这种识别三角形位置可不是很好,无法确定具体位置。
1.3 卷积的计算
一个卷积核
从本质上讲,卷积的计算过程其实同全连接一样,也是各个神经元之间的线性组合。只是卷积操作在进行线性组合时选择的是特定位置上的神经元。下面我们首先通过一张动图来直观感受一下卷积的过程。
如图所示,卷积运算的本质是通过一个可学习的滤波器矩阵F(图中蓝色矩阵的阴影部分)在输入特征图X(蓝色矩阵)上进行滑动扫描的计算过程。具体来说,滤波器F以一定的步长在输入数据上逐次移动,每次覆盖的局部区域与滤波器进行矩阵内积运算,从而生成输出特征图Y(绿色矩阵)中的一个对应值(绿色阴影部分)。当滤波器完成对整个输入区域的扫描后,最终就会得到完整的卷积输出结果Y(绿色矩阵)。
这种滑动窗口式的计算方式具有两个显著特点:
-
局部连接性:每次计算只关注输入数据的局部区域
-
参数共享:同一个滤波器在整个扫描过程中重复使用
这种计算范式不仅大幅减少了参数量,还能有效捕捉图像中的局部特征模式,如边缘、纹理等基础视觉特征。通过堆叠多个这样的卷积层,网络就能逐步构建出从低级到高级的层次化特征表示。
同时,我们将这个特定大小的矩阵F称为卷积核,即convolutional kernel或kernel或filter或detector,它可以是一个也可以是多个;将卷积后的结果Y称为特征图,即feature map,并且每一个卷积核卷积后都会得到一个对应的特征图;最后,对于输入X的形状,都会用三个维度来进行表示,即宽(width),高(high)和通道(channel)。例如上图中输入X的形状为[7,7,1]
。
多个卷积核
注意,在上面笔者提到了卷积核的个数还可以是多个,那我们为什么需要多个卷积核进行卷积呢?在上一篇文章中我们介绍到:对于一个卷积核,可以认为其具有识别某一类元素(特征)的能力;而对于一些复杂的数据来说,仅仅只是通过一类特征来进行辨识往往是不够的。因此,通常来说我们都会通过多个不同的卷积核来对输入进行特征提取得到多个特征图,然再输入到后续的网络中。
对于同一个输入,通过两个不同的卷积核对其进行卷积特征提取,最后便能得到两个不同的特征图。从图2右边的特征图可以发现,上面的特征图在锐利度方面明显会强于下面的特征图。当然,这也是使用多卷积核进行卷积的意义,探测到多种特征属性以有利于后续的下游任务。
卷积相关概念
-
感受野(Receptive Field)
感受野指的是卷积神经网络每一层输出的特征图(feature map)上每个像素点映射回输入图像上的区域大小,神经元感受野的范围越大表示其能接触到的原始图像范围就越大,也意味着它能学习更为全局,语义层次更高的特征信息,相反,范围越小则表示其所包含的特征越趋向局部和细节。因此感受野的范围可以用来大致判断每一层的抽象层次,并且我们可以很明显地知道网络越深,神经元的感受野越大。
-
分辨率(Resolution)
分辨率指的是输入模型的图像尺寸,即长宽大小。通常情况会根据模型下采样次数n和最后一次下采样后feature map的分辨率k×k来决定输入分辨率的大小,即:
从输入r×r到最后一个卷积特征feature map的k×k,整个过程是一个信息逐渐抽象化的过程,即网络学习到的信息逐渐由低级的几何信息转变为高级的语义信息,这个feature map的大小可以是3×3,5×5,7×7,9×9等等,k太大会增加后续的计算量且信息抽象层次不够高,影响网络性能,k太小会造成非常严重的信息丢失,如原始分辨率映射到最后一层的feature map有效区域可能不到一个像素点,使得训练无法收敛。
在ImageNet分类任务中,通常设置的5次下采样,并且考虑到其原始图像大多数在300分辨率左右,所以把最后一个卷积特征大小设定为7×7,将输入尺寸固定为224×224×3。在目标检测任务中,很多采用的是416×416×3的输入尺寸,当然由于很多目标检测模型是全卷积的结构,通常可以使用多尺寸训练的方式,即每次输入只需要保证是32×的图像尺寸大小就行,不固定具体数值。但这种多尺度训练的方式在图像分类当中是不通用的,因为分类模型最后一层是全连接结构,即矩阵乘法,需要固定输入数据的维度。
-
深度(Depth)
神经网络的深度决定了网络的表达能力,它有两种计算方法,早期的backbone设计都是直接使用卷积层堆叠的方式,它的深度即神经网络的层数,后来的backbone设计采用了更高效的module(或block)堆叠的方式,每个module是由多个卷积层组成,它的深度也可以指module的个数,这种说法在神经架构搜索(NAS)中出现的更为频繁。通常而言网络越深表达能力越强,但深度大于某个值可能会带来相反的效果,所以它的具体设定需要不断调参得到。
深度学习是机器学习的一个分支领域:它是从数据中学习表示的一种新方法,强调从连续的层( layer)中进行学习,这些层对应于越来越有意义的表示。“深度学习”中的“深度”指的并不是利用这种方法所获取的更深层次的理解,而是指一系列连续的表示层。数据模型中包含多少层,这被称为模型的深度( depth)。这一领域的其他名称包括分层表示学习( layeredrepresentations learning)和层级表示学习( hierarchical representations learning)。现代深度学习通常包含数十个甚至上百个连续的表示层,这些表示层全都是从训练数据中自动学习的。与此相反,其他机器学习方法的重点往往是仅仅学习一两层的数据表示,因此有时也被称为浅层学习( shallow learning)。
-
宽度(Width)
宽度决定了网络在某一层学到的信息量,但网络的宽度时指的是卷积神经网络中最大的通道数,由卷积核数量最多的层决定。通常的结构设计中卷积核的数量随着层数越来越多的,直到最后一层feature map达到最大,这是因为越到深层,feature map的分辨率越小,所包含的信息越高级,所以需要更多的卷积核来进行学习。通道越多效果越好,但带来的计算量也会大大增加,所以具体设定也是一个调参的过程,并且各层通道数会按照8×的倍数来确定,这样有利于GPU的并行计算。
-
下采样(Down-Sample)
下采样层有两个作用,一是减少计算量,防止过拟合,二是增大感受野,使得后面的卷积核能够学到更加全局的信息。下采样的设计有两种:
1.采用stride为2的池化层,如Max-pooling或Average-pooling,目前通常使用Max-pooling,因为它计算简单且最大响应能更好保留纹理特征;
2.采用stride为2的卷积层,下采样的过程是一个信息损失的过程,而池化层是不可学习的,用stride为2的可学习卷积层来代替pooling可以得到更好的效果,当然同时也增加了一定的计算量。
(突然想到为啥不使用双线性插值,向下插值来代替Pooling,这个虽然比MaxPooling计算量更大,但是保留的信息应该更丰富才是)
-
上采样(Up-Sampling)
在卷积神经网络中,由于输入图像通过卷积神经网络(CNN)提取特征后,输出的尺寸往往会变小,而有时我们需要将图像恢复到原来的尺寸以便进行进一步的计算(如图像的语义分割),这个使图像由小分辨率映射到大分辨率的操作,叫做上采样,它的实现一般有三种方式:
-
插值,一般使用的是双线性插值,因为效果最好,虽然计算上比其他插值方式复杂,但是相对于卷积计算可以说不值一提;
-
转置卷积又或是说反卷积,通过对输入feature map间隔填充0,再进行标准的卷积计算,可以使得输出feature map的尺寸比输入更大;
-
Max Unpooling,在对称的max pooling位置记录最大值的索引位置,然后在unpooling阶段时将对应的值放置到原先最大值位置,其余位置补0;
-
参数量(Params)
参数量指的网络中可学习变量的数量,包括卷积核的权重weight,批归一化(BN)的缩放系数γ,偏移系数β,有些没有BN的层可能有偏置bias,这些都是可学习的参数 ,即在模型训练开始前被赋予初值,在训练过程根据链式法则中不断迭代更新,整个模型的参数量主要由卷积核的权重weight的数量决定,参数量越大,则该结构对运行平台的内存要求越高,参数量的大小是轻量化网络设计的一个重要评价指标。
-
计算量(FLOPs)
神经网络的前向推理过程基本上都是乘累加计算,所以它的计算量也是指的前向推理过程中乘加运算的次数,通常用FLOPs来表示,即floating point operations(浮点运算数)。计算量越大,在同一平台上模型运行延时越长,尤其是在移动端/嵌入式这种资源受限的平台上想要达到实时性的要求就必须要求模型的计算量尽可能地低,但这个不是严格成正比关系,也跟具体算子的计算密集程度(即计算时间与IO时间占比)和该算子底层优化的程度有关。
2 卷积计算类型
-
标准卷积 (Convolution)
在神经网络架构设计中,标准卷积是最常见的结构,假设其输入feature map的维度是(1, iC, iH, iW),每个卷积核的维度是(1, iC, k, k),一次卷积滤波得到一层feature map的维度为(1,1, oH, oW),一共有oC个卷积核,则输出feature map的维度是(1, oC, oH, oW),计算量为iC×k×k×oC×oH×oW,计算过程如下图所示,由于太过基础,故不赘述。
-
深度卷积 (Depthwise Convolution)
深度卷积与标准卷积相比,顾名思义是在深度上做了文章,而这里的深度跟网络的深度无关,它指的通道,标准卷积中每个卷积核都需要与feature map的所有层进行计算,所以每个卷积核的通道数等于输入feature map的通道数,通过设定卷积核的数量可以控制输出feature map的通道数。而深度卷积每个卷积核都是单通道的,维度为(1,1,k,k) ,卷积核的个数为iC,即第i个卷积核与feature map第i个通道进行二维的卷积计算,最后输出维度为(1,iC,oH,oW),它不能改变输出feature map的通道数,所以通常会在深度卷积后面接上一个(oC,iC,1,1)的标准卷积来代替3×3或更大尺寸的标准卷积,总的计算量为k×k×iC×oH×oW+iC×1×1×oH×oW×oC,是普通卷积的1/oC+1/(k×k),大大减少了计算量和参数量,又可以达到相同的效果,这种结构被称为深度可分离卷积(Depthwise Separable Convolution),在MobileNet V1被提出,后来渐渐成为轻量化结构设计的标配。
深度可分离卷积
深度卷积之前一直被吐槽在GPU上运行速度还不如一般的标准卷积,因为depthwise 的卷积核复用率比普通卷积要小很多,计算和内存访问的比值比普通卷积更小,因此会花更多时间在内存开销上,而且per-channel的矩阵计算很小不容易并行导致的更慢,但理论上计算量和参数量都是大大减少的,只是底层优化的问题。
-
分组卷积 (Group Convolution)
分组卷积最早在AlexNet中出现,当时作者在训练模型时为了减少显存占用而将feature map分组然后给多个GPU进行处理,最后把多个输出进行融合。具体计算过程是,分组卷积首先将输入feature map分成g个组,每个组的大小为(1, iC/g, iH, iW),对应每组中一个卷积核的大小是(1,iC/g,k,k),每组有oC/g个卷积核,所以每组输出feature map的尺寸为(1,oC/g,oH,oW),最终g组输出拼接得到一个(1,oC,oH,oW)的大feature map,总的计算量为iC/g×k×k×oC×oH×oW,是标准卷积的1/g,参数量也是标准卷积的1/g。
分组卷积
但由于feature map组与组之间相互独立,存在信息的阻隔,所以ShuffleNet提出对输出的feature map做一次channel shuffle的操作,即通道混洗,打乱原先的顺序,使得各个组之间的信息能够交互起来。
-
空洞卷积 (Dilated Convolution)
空洞卷积是针对图像语义分割问题中下采样会降低图像分辨率、丢失信息而提出的一种卷积思路。通过间隔取值扩大感受野,让原本3x3的卷积核,在相同参数量和计算量下拥有更大的感受野。这里面有个扩张率(dilation rate)的系数,这个系数定义了这个间隔的大小,标准卷积相当于dilation rate为1的空洞卷积,下图展示的是dilation rate为2的空洞卷积计算过程,可以看出3×3的卷积核可以感知标准的5×5卷积核的范围,还有一种理解思路就是先对3×3的卷积核间隔补0,使它变成5×5的卷积,然后再执行标准卷积的操作。
https://picx.zhimg.com/v2-4959201e816888c6648f2e78cccfd253_b.webp
-
转置卷积 (Transposed Convolutions)
转置卷积又称反卷积(Deconvolution),它和空洞卷积的思路正好相反,是为上采样而生,也应用于语义分割当中,而且他的计算也和空洞卷积正好相反,先对输入的feature map间隔补0,卷积核不变,然后使用标准的卷积进行计算,得到更大尺寸的feature map。
-
可变形卷积 (deformable convolution)
以上的卷积计算都是固定的,每次输入不同的图像数据,卷积计算的位置都是完全固定不变,即使是空洞卷积/转置卷积,0填充的位置也都是事先确定的。而可变性卷积是指卷积核上对每一个元素额外增加了一个h和w方向上偏移的参数,然后根据这个偏移在feature map上动态取点来进行卷积计算,这样卷积核就能在训练过程中扩展到很大的范围。而显而易见的是可变性卷积虽然比其他卷积方式更加灵活,可以根据每张输入图片感知不同位置的信息,类似于注意力,从而达到更好的效果,但是它比可行变卷积在增加了很多计算量和实现难度,目前感觉只在GPU上优化的很好,在其他平台上还没有见到部署。