卷积层是卷积神经网络的核心组成部分,主要用于提取图像、语音、文本等数据的局部特征。
计算机视觉的典型任务:图像分类、目标检测(定义对象在哪里)、风格迁移(将两张图片的内容和风格结合)。
卷积网络解决传统全连接层在图像上的参数爆炸问题,使得大图像能够高效处理;能够支持图像分类,目标检测、风格迁移等多种任务。
一、卷积操作
CNN最重要的是构建卷积层:直接连接图片输入的卷积层不会连接到图像中的每个像素,而只是只与某个框定范围内的像素相连接;同样,第二卷积层的每个神经元也仅连接第一层中某个框定范围内的神经元;这种允许神经网络关注第一个隐藏层的低阶特征,然后在下一个隐藏层中将它们组装成高阶特征,以此类推。
也就是使用一个滤波器(Filter) 或内核(Kernel),像一个小窗口一样,扫过整个输入数据。同一个滤波器在不同位置使用的权重是相同的。这意味着,无论这个滤波器在图像的左上角还是右下角,它都在检测同一种特征(比如一个垂直边缘)。
CNN在图像识别方便效果好的原因之一是现实中的图像也有层次结构。
注意:之前研究的所有多层神经网络都具有由一长串神经元组成的层,必须将输入图像展平为一维,然后再将其输入神经网络。在CNN中,每一层都以二维形式表示,这使得将神经元与其相应的输入进行匹配变得更加容易。
1、卷积层的主要组件和参数定义
(1)滤波器Filter与内核Kernel
滤波器与内核是本质上是同一个内容,都是一个小的权重矩阵(通常是3x3, 5x5等),是卷积层要学习的参数。
每个滤波器专门用于提取输入中的某种特定特征(如边缘、角点、颜色、纹理等)。
深度(Depth):一个卷积层通常有多个滤波器。每个滤波器都会产生一个特征图(Feature Map)。所有特征图堆叠起来就构成了该层的输出。滤波器的数量决定了输出数据的深度。
(2)特征图(占用大量内存)
滤波器滑过整个输入后计算出的输出矩阵为特征图。特征图中的每个值代表了输入中对应位置是否存在该滤波器所检测的特征。特征值越高,表示该位置与滤波器的模式匹配度越高。
在卷积层训练时需要存储所有中间结果(也就是所有的特征图)以便反向传播使用,这些中间值会占用大量内存。
举例:
- 输入为150 ×100的像素的RGB图像,卷积层为200个5×5的滤波器,步幅为1,填充为same。
- 参数数量计算:每个滤波器参数为 5×5×3 = 75个权重 + 1个偏置 = 76个参数,200个滤波器对应的就有76 × 200 = 15,200个参数
- 计算量:因为输出的特征图都是150×100的,每次窗口(神经元)计算75次得到一个特征图上的数字,因此总计算量为150×100×75×200 = 225,000,000次运算
- 内存占用计算:每个特征图的大小为150×100,有200个滤波器,代表着有200个特征图,因此特征图的总大小为200×150×100 = 3,000,000个值,而一个32位浮点数占用4字节,因此该层占用内存为3,000,000 × 4字节 = 约12MB
- 批量训练时的内存需求:如果批量时100,那么需要占用12MB×100=1.2GB
推理期间与训练期间的区别:
- 推理期间(即对新实例进行预测时)只需要计算完一层后,就可以释放前一层占用的 RAM,因此只需要两个连续层所需的 RAM。
- 训练期间: 需要保留前向传播过程中计算出的所有中间值以便进行反向传播,因此训练期间的 RAM 需求 = 所有层所需 RAM 的总量。
内存优化方法:如果训练因内存不足而崩溃,可以尝试: 减小批量大小(batch size);使用步幅参数来降维,删除一些层;使用 16 位浮点数而不是 32 位浮点数或将 CNN 分布在多个设备上。
(3)padding填充
在卷积神经网络中,卷积操作通常会使输入图像的尺寸缩小。例如,一个 6×6 的图像与一个 3×3 卷积核做卷积,得到的输出是 4×4 。一般公式为:,其中 n 是输入大小,f 是卷积核大小,n_output是输出大小。
如上述公式可以看出:每次卷积都会减少图像的边长,深层网络中图像可能很快缩小到无法使用。而且,在实际计算的过程中图像边缘或角落的像素在卷积中被利用的次数远少于中心区域,导致边缘特征信息被"弱化"。
解决这些问题,可以在输入的四周补上一圈像素(通常为 0),即 padding参数。
例如,将 6×6 的输入补一圈(p=1),变为 8×8,再用 3×3 卷积,就能得到与原输入相同大小的 6×6 输出。 一般公式为:
两种常见的卷积填充方式:
valid
: 不填充。滤波器只在"有效"的输入位置做卷积,输出尺寸会小于输入尺寸。same
: 在输入的边缘填充0(零填充),使得输出特征图的高度和宽度与输入完全相同(在步长为1时)。这是最常用的选项,可以保留更多的边缘信息。
为什么卷积核常取奇数大小:
- 奇数核有自然的中心像素点,方便定义卷积的"中心"。
- 避免了左右/上下不对称填充的麻烦。
- 因此常见的卷积核有 3 \\times 3、5 \\times 5、7 \\times 7 等。
(4)stride步幅
在卷积操作中,stride(步幅)表示卷积核每次移动的距离:
- stride = 1:卷积核逐像素滑动,输出尺寸较大。
- stride > 1:卷积核"跳着走",每次跨过多个像素,输出尺寸明显缩小。
输出大小的公式:,n为输入大小,f为卷积核大小,p为padding(填充大小),s为stride(步幅大小),
表示向下取整(floor)
其作用主要是:
- 控制输出特征图的大小:步幅越大,输出越小。
- 减少计算量:更少的位置参与卷积运算。
- 调节特征提取粒度:小步幅 → 细致特征;大步幅 → 粗略特征。
2、边缘检测
边缘检测的目的是识别数字图像中亮度或颜色发生显著变化的点,这些点通常对应图像中所显示物体的重要事件和属性。
边缘检测的主要作用包括:简化数据、目标识别与检测、图像分割、三维重建。
python
from sklearn.datasets import load_sample_images
image = load_sample_images()["images"][-1]
gray_image = (0.299*image[...,0] + 0.587*image[...,1] + 0.114*image[...,2]).astype(np.float32) # 彩色图片并转为灰度
plt.imshow(gray_image,cmap="gray")
plt.axis("off")
plt.show()
# 构建 7*7的卷积窗口
d = np.array([-3, -2, -1, 0, 1, 2, 3], dtype=np.float32)
s = np.array([ 1, 6, 15,20,15, 6, 1], dtype=np.float32)
K_vertical_fancy = np.outer(s, d) # 7x7 垂直边缘检测核
K_horizontal_fancy = K_vertical_fancy.T # 7x7 水平平边缘检测的核
from scipy.signal import convolve2d
edge_v = convolve2d(gray_image[::2,::2], K_vertical_fancy, mode='valid')
edge_h = convolve2d(gray_image, K_horizontal_fancy, mode='valid')
import matplotlib.pyplot as plt
plt.figure(figsize=(10,10))
plt.subplot(121)
plt.imshow(edge_v, cmap="gray")
plt.axis("off")
plt.title("vertical edge")
plt.subplot(122)
plt.imshow(edge_h, cmap="gray")
plt.axis("off")
plt.title("horizontal edge")
plt.show()
上面例子可以看出,可以自己构造滤波器(filter)去对图片做卷积操作,实现对图片的特征提取(垂直边缘检测/水平边缘检测), 在神经网络中,滤波器的参数是通过反向传播去更新优化的,除了初始化,不需要去自己指定;所以它在优化过程中会根据任务学会怎么提取图片的特征。
此外,边缘检测的主要算法,有以下几种。在实际应用的过程中可以引用OpenCV/MATLAB主流的图像处理库进行实现。
算子名称 | 原理 | 优点 | 缺点 |
---|---|---|---|
Sobel | 一阶导数,近似计算梯度 | 计算简单、快速 | 对噪声敏感,边缘粗且模糊 |
Prewitt | 一阶导数,近似计算梯度 | 比Roberts好 | 对噪声敏感 |
Laplacian | 二阶导数,零交叉 | 各向同性,对细线和孤立点敏感 | 对噪声非常敏感,易产生双边缘 |
LoG | 先高斯平滑,再拉普拉斯 | 抗噪声能力优于普通Laplacian | 计算复杂,可能丢失边缘 |
Canny | 高斯平滑、梯度、NMS、双阈值 | 最优边缘检测,抗噪好,边缘连续单薄 | 计算复杂,参数(双阈值)需要调节 |
python
import cv2
import numpy as np
# 读取图像为灰度图
image = cv2.imread(image, cv2.IMREAD_GRAYSCALE)
# 1. Sobel 算子
sobelx = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3) # 检测x方向边缘
sobely = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3) # 检测y方向边缘
sobel_combined = cv2.magnitude(sobelx, sobely) # 合并两个方向
# 2. Laplacian 算子
laplacian = cv2.Laplacian(image, cv2.CV_64F, ksize=3)
# 3. Canny 算子 (最常用,一步到位)
# 参数:图像,低阈值,高阈值
canny_edges = cv2.Canny(image, 100, 200)
# 显示结果
cv2.imshow('Original', image)
cv2.imshow('Sobel', sobel_combined.astype(np.uint8))
cv2.imshow('Laplacian', laplacian.astype(np.uint8))
cv2.imshow('Canny', canny_edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
3、在三维数组上的卷积
三维卷积是二维卷积在空间维度上的自然延申,旨在捕获数据在三个维度上的特征,也就是体积卷积。
在彩色图像中,数据包含三个维度:前两个维度是高度和宽度(类似灰度图像),第三个维度是通道数。为了处理这种图像,卷积核本身也必须是三维的,且卷积核的通道数必须与输入一致。
卷积的过程:
- 把卷积核放在输入的一个位置上(覆盖 3×3×3 的小立方块)。
- 对应元素逐一相乘(27 次乘法),再相加,得到一个标量。
- 把卷积核滑动到下一个位置,重复以上步骤。
- 最终得到一个二维矩阵(例如 4×4 )。
卷积核的参数决定了它能检测的特征,不同的卷积核可以提取不同特征:垂直边缘、水平边缘、对角线纹理、颜色变化等:
- 只检测红色通道的边缘:卷积核在红色通道放置边缘检测模板,其他通道全为零。
- 检测任意颜色的边缘:卷积核在 RGB 三个通道都放置相同的边缘检测模板。
在实际的卷积层中,我们不会只用一个卷积核,而是会用 多个卷积核 来同时检测不同特征。每个卷积核产生一个二维输出(feature map)。所有输出 按通道堆叠,形成一个新的体积。由此可知:++体积卷积让我们能在多通道图像上提取特征,每个卷积核生成一个特征图,多个卷积核堆叠后形成新的输出体积。++
公式:
其中:n表示输入宽/高,n_C表示输入通道数(必须与卷积核通道一致),f 表示卷积核宽/高,n_C' 表示卷积核个数(决定输出通道数),(这里假设 stride=1,padding=0)
由此可以看出:
- 卷积不仅能在二维图像上操作,还能作用于多通道输入(体积)。
- 卷积核必须与输入的通道数一致。
- 每个卷积核会输出一个二维特征图,多个卷积核输出的结果会堆叠成新的体积。
- 输出体积的通道数 = 使用的卷积核数。
4、单个卷积层
单个卷积层可以理解成特征提取器,核心任务是扫描输入数据,并从中提取出各种有意义的特征,然后生成一组特征图作为输出。
(1)输入和输出
输入和输出都是三维体积(Volume),并引入了上标 [l]
来表示神经网络的层数,高度与宽度由公式决定, 深度(通道数)等于 卷积核个数。
输入体积: ( 高度 × 宽度 × 通道数)
输出体积(Output):
(2) 卷积层的关键参数
:filter size(卷积核大小)
:padding(填充)
:stride(步幅)
:number of filters(卷积核个数 = 输出通道数)
(3)卷积运算
每个卷积核的大小:
卷积核在输入上滑动,计算:,其中
表示卷积核权重,
表示前一层的激活,
表示偏置
(4) 输出尺寸公式
输出的高度/宽度:
输出通道数: = 卷积核个数
(5) 参数数量
每个卷积核参数量: 最后的1表示偏置
总参数量:
(6)单个卷积层完成的流程
- 输入体积与卷积核滑动相乘并加偏置
- 应用非线性激活函数(如 ReLU)
- 输出新的体积,通道数 = 卷积核个数
- 参数数量与 卷积核大小、数量、输入通道数 有关,与输入图像尺寸无关
5、内存需求
- 训练期间卷积层需要大量内存:CNN 的一个挑战是卷积层需要大量的 RAM,在训练期间尤其如此,因为反向传播需要在前向传播过程中计算出的所有中间值。
- 例如,考虑一个具有 200 个 5×5 滤波器、步幅为 1 且采用 "same" 填充的卷积层。
- 如果输入是150×100 的 RGB 图像(3 个通道),则参数数量为:(5×5×3+1)×200=15200(+1 表示偏置项)。与全连接层相比,它很小。
- 然而,200 个特征图中的每个特征图都包含 150×100个神经元,并且每个神经元都需要计算 5×5×3=75个输入的加权和:总共 = 2.25 亿次浮点运算
- 虽然不如全连接层那么糟糕,但仍然需要进行大量的计算。此外,如果使用 32 位浮点数来表示神经元,则单层占用的 RAM 为:200×150 × 100 ×32位 = 9600万位 ≈ 12MB。这还只是一个实例,如果训练 100 个实例,则会占用约 1.2 GB RAM
- 推理期间与训练期间的区别
- 推理期间(即对新实例进行预测时): 只需要计算完一层后,就可以释放前一层占用的 RAM,因此只需要两个连续层所需的 RAM。
- 训练期间: 需要保留前向传播过程中计算出的所有中间值以便进行反向传播,因此训练期间的 RAM 需求 = 所有层所需 RAM 的总量。
- 内存优化方法:如果训练因内存不足而崩溃,可以尝试:
- 减小批量大小(batch size)
- 使用步幅参数来降维,删除一些层
- 使用 16 位浮点数而不是 32 位浮点数
- 或将 CNN 分布在多个设备上
二、池化和典型CNN架构
1、池化层
池化,也叫下采样,是卷积神经网络中紧跟在卷积层之后常用的一种操作。它的核心思想为:在一个小的局部区域(池化窗口)内,用一个概括性的统计量代表这个区域的所有特征,这里池化窗口的大小和步长大小相同。
池化的主要功能是降维和保持特征不变性。
- 降维:减小特征图的尺寸(高度和宽度),从而减少计算量和参数数量,能够让网络可以更深,同时加快训练速度也减少了过拟合风险()。
- 特征不变性:不变性是指,当输入图像发生微小变化的时候,输出结果基本保持不变。也就是说,当图像中的主要内容发生平移、旋转、缩放的时候也能很好的关注特征是否存在,而不是内容的精确位置。
最常见的两中池化方式为最大池化和平均池化。
- 最大池化:最常用。在一个局部区域(如2x2窗口)内取最大值作为输出。它能更好地保留纹理特征。
- 平均池化:取局部区域的平均值作为输出。它更侧重于保留背景信息。
- 全局平均池化:不是对局部窗口的操作,而是对特征图的每个通道计算平均值。例如:一个 7x7x512 的特征图,经过GAP后,会变成一个 1x1x512 的向量。
在一些模型中会用步长卷积替代池化,以达到降维的效果。因此将两者做个对比:
特性 | 池化层 | 卷积层(带步长) |
---|---|---|
目的 | 降维、增强不变性、防止过拟合 | 特征提取,同时可兼做降维 |
参数 | 没有可学习的参数(规则固定) | 有可学习的参数(权重和偏置) |
作用 | 被动地压缩信息 | 主动地提取信息 |
2、典型CNN架构
典型的CNN架构是先堆叠一些卷积层(通常每个卷积层都跟随一个ReLU层),接着放一个池化层,然后再堆叠另外几个卷积层(+ReLU),再放另一个池化层,以此类推。
随着图像不断经过卷积网络的各层,图像变得越来越小,但由于卷积层的存在,图像通常也越来越深(即具有更多的通道)。在顶部,添加一个常规前馈神经网络,该网络由几个全连接层(+ReLU)组成,最后一层(例如输出估计类别概率的softmax层)输出预测结果。
模型 | 出现时间 | 特点 | 深度 | 特点 |
---|---|---|---|---|
LeNet-5 | 1998 | CNN奠基之作 使用平均池化和sigmoid/tanh激活函数 | ~5层 | 卷积+池化+全连接的基本范式 卷积层 -> 池化层 -> 卷积层 -> 池化层 -> 全连接层 -> 输出层 |
AlexNet | 2012 | 深度网络实践 使用了relu函数和Dropout以及数据增强技术 | 8层 | ReLU, Dropout, 数据增强 |
VGGNet | 2014 | 深度至关重要 全部使用3×3的卷积核与2×2的最大池化层 | 16/19层 | 小卷积核,结构规整 |
GoogLeNet | 2014 | 宽度与效率 Inception思想减少了计算成本,使用了辅助分类 | 22层 | Inception模块,1x1卷积降维 |
ResNet | 2015 | 缓解梯度消失/网络退化 使用残差块和跳跃连接使得网络能够更深层的训练 | 极深 50-152 +层) | 残差学习,跳跃连接 |