前面三篇文章我们学完了全连接神经网络ANN的完整知识体系。但 ANN 处理图像有一个致命弱点——它要把图像展平成一维向量丢失了空间结构信息。比如一张 28×28 的手写数字图片ANN 直接把它拉成 784 维向量像素之间的空间关系全没了。卷积神经网络CNN就是为了解决这个问题而生的。它保留图像的二维结构通过卷积核在图像上滑动扫描自动学习局部特征——从边缘、纹理到复杂图案层层递进。CNN 是计算机视觉领域的基石模型也是深度学习中最经典的网络结构之一。今天这篇文章我们从图像的基本知识出发理解卷积神经网络的三大核心组件——卷积层、池化层、全连接层掌握特征图大小计算、多通道卷积、填充与步长等关键概念最后走通一个图像分类的完整流程。一、图像基本知识在学 CNN 之前得先搞清楚图像在计算机里是怎么表示的。像素与通道一张图片在计算机里就是一个数字矩阵每个元素就是一个像素值。像素范围0 到 255。0 表示纯黑色255 表示纯白色中间的值表示不同深浅的灰色或颜色RGB 三通道彩色图片由三个通道组成——红R、绿G、蓝B。每个通道是一个二维矩阵三个矩阵叠加在一起就形成了我们看到的彩色图像。所以一张彩色图片的形状是 H×W×3其中 H 是高度W 是宽度3 是通道数。举个例子一张 224×224 的彩色图片在计算机里就是一个 224×224×3 的三维数组总共有 224×224×3 150528 个数值。每个数值都在 0 到 255 之间表示该位置该通道的颜色强度。而灰度图片只有一个通道形状是 H×W×1或者直接就是 H×W。图片形状HWC vs BCHW不同的深度学习框架对图片形状的约定不同matplotlibHWC高度在前通道在后。读取和展示图片时用的格式import matplotlib.pyplot as plt img plt.imread(photo.jpg) # 读取图片形状 HWC[H, W, C] plt.imshow(img) # 展示图片CNNBCHW批量在前通道在中高度宽度在后。PyTorch 处理图像时需要的格式。多了个 BBatch是因为训练时一次处理一批图片。形状转换从 HWC 转成 BCHW需要两步操作# 假设 img 形状为 [H, W, C]HWC img img.permute(2, 0, 1) # 维度交换 → [C, H, W]CHW img img.unsqueeze(0) # 添加 batch 维度 → [1, C, H, W]BCHWpermute()用于多个维度位置的交换unsqueeze()在指定位置添加一个大小为 1 的维度。这两步是处理图像数据时的标准操作务必牢记。为什么要做这个维度转换因为 PyTorch 的卷积层nn.Conv2d要求输入格式必须是[B, C, H, W]——批量在第一个维度通道在第二个维度高度和宽度在后两个维度。如果直接把 matplotlib 读出来的 HWC 格式扔进卷积层会直接报维度不匹配的错误。二、卷积神经网络概述什么是 CNN定义CNN 是包含卷积层的神经网络适合处理图像任务。和 ANN 的全连接层相比卷积层有两个核心优势局部感知卷积核只关注图像的一小块区域不是全部像素能捕捉局部特征边缘、角点、纹理等。参数共享同一个卷积核在整张图上滑动每个位置用的是同一组权重。参数量大大减少而且特征具有平移不变性——物体出现在图片哪个位置都能被检测到。组成部分一个完整的 CNN 由三种层组成组件作用关键操作卷积层提取特征卷积核滑动做点积池化层压缩特征取最大值或平均值全连接层输出结果展平后做分类/回归CNN 的搭建流程输入图片 → 数据预处理归一化、HWC→BCHW → 卷积层提取特征 → 激活函数引入非线性 → 池化层压缩特征 → 卷积激活池化 可重复多组 → 全连接层展平后输出分类结果注意池化层之后传入全连接层时需要先reshape把多维特征图展平成一维向量——因为全连接层只接受一维输入。这个展平操作很容易被忽略。比如经过卷积和池化后特征图形状是[B, 32, 7, 7]直接传给全连接层会报错必须先变成[B, 1568]32×7×71568才行。PyTorch 中用x.view(x.size(0), -1)来做其中-1表示自动计算这个维度的大小。三、卷积层CNN 的核心卷积层是 CNN 中最核心的部分作用是自动学习并提取图像的局部特征。卷积核Filter / Kernel / 滤波矩阵卷积核是一个小的权重矩阵核心任务是和输入图像的局部区域做点积运算。运算过程卷积核放在图像的某个位置把卷积核覆盖区域的像素值与卷积核中的权重逐个相乘把所有乘积加起来得到一个输出值卷积核滑动到下一个位置重复以上步骤每个卷积核运算完毕后都会得到一张特征图。不同的卷积核学习不同的特征——有的学习边缘有的学习纹理有的学习形状。输入图像 卷积核 特征图 ┌─────────┐ ┌───┐ ┌───────┐ │ 1 2 0 1 │ │1 0│ │ 5 3 │ │ 0 1 3 2 │ × │0 1│ → │ 4 6 │ │ 2 0 1 1 │ └───┘ └───────┘ │ 1 0 2 0 │ └─────────┘填充Padding作用在图像四周补 0有两个目的学习边缘特征如果不填充图像边缘的像素被卷积核覆盖的次数比中间少边缘特征学不充分。保持图片形状不变填充合适的圈数可以让输出特征图和输入图像大小相同。特征图大小计算公式卷积操作后输出特征图的大小由以下公式决定N ⌊(W - F 2P) / S⌋ 1其中W 输入尺寸宽或高F 卷积核大小P 填充大小S 步长N 输出尺寸举个例子输入图片 28×28卷积核 3×3步长 1填充 1 N ⌊(28 - 3 2×1) / 1⌋ 1 ⌊27⌋ 1 28输出还是 28×28。如果步长改为 2N ⌊(28 - 3 2) / 2⌋ 1 ⌊13.5⌋ 1 14输出变成 14×14缩小了一半。这个公式是 CNN 网络设计的基础工具。每搭一层卷积或池化都要用这个公式算一下输出尺寸否则后面的全连接层维度就会算错。实际设计中通常用表格把每一层的输入输出尺寸列出来确保最后展平的维度和全连接层的输入维度匹配。多通道卷积输入图像通常是多通道的比如 RGB 三通道卷积核也必须是多通道的——卷积核的通道数必须等于输入的通道数。多通道卷积的过程针对每个通道的数据分别和卷积核对应通道做计算把各通道的结果相加再加一个偏置值最终得到一张特征图注意不管输入有几个通道一个卷积核只产生一张特征图。要产生多张特征图就要用多个卷积核——输出通道数 卷积核个数。API 调用PyTorch 中卷积层的 APIimport torch.nn as nn conv nn.Conv2d( in_channels3, # 输入通道数如 RGB 图片为 3 out_channels16, # 卷积核数量 输出特征图数量 kernel_size3, # 卷积核大小3×3 stride1, # 步长 padding1 # 填充 )参数含义非常直观in_channels告诉模型输入有几个通道out_channels告诉模型你要输出几张特征图用几个卷积核kernel_size决定每个卷积核多大stride和padding控制输出大小。这里有个细节值得注意Conv2d的权重是可学习的——卷积核里的那些数值不是人为设定的而是网络在训练过程中通过反向传播自动学习出来的。你只需要指定卷积核的数量和大小具体学什么特征由训练数据和损失函数来决定。这就是深度学习自动特征提取的核心——卷积核自己学会了对什么特征敏感。四、池化层压缩特征池化层通常放在卷积层之后作用是压缩特征图。四大作用作用说明减少计算量特征图变小了后面的层参数减少降低维度从 28×28 压缩到 14×14维度减半减少内存消耗存储的数据量减少了提高模型鲁棒性对位置微小的变化不敏感两种池化方式最大池化Max Pooling在窗口内取最大值。保留最显著的特征是最常用的池化方式。特征图 池化窗口2×2 输出 ┌───────┐ ┌───────┐ │ 1 3 2 4│ stride2 │ 4 4 │ │ 5 2 1 0│ → 取最大值 → │ 6 3 │ │ 6 1 0 3│ │ 2 0 1 2│ └───────┘ └───────┘平均池化Average Pooling在窗口内取平均值。保留整体信息但不如最大池化常用。多通道池化池化层的操作是每个特征图分别池化不改变通道数。输入 16 个通道输出还是 16 个通道只是每个通道的空间尺寸变小了。这一点和卷积层不同——卷积会改变通道数池化不会。池化层没有可学习参数重要特性池化层没有可学习的参数没有权重和偏置它只根据固定的数学统计规则设置窗口大小在对应位置做最大值或平均值运算。这也意味着池化层不需要训练它只是一个固定的数学操作。API 调用# 最大池化 maxpool nn.MaxPool2d(kernel_size2, stride2, padding0) # 平均池化 avgpool nn.AvgPool2d(kernel_size2, stride2, padding0)最常用的配置是kernel_size2, stride2效果是特征图长宽各缩小一半——28×28 变成 14×14。注意池化层和卷积层的一个重要区别卷积层的步长默认是 1而池化层的步长默认等于窗口大小kernel_size。也就是说池化窗口默认不会重叠——每个位置只被计算一次干净利落。当然你也可以手动设置stride让窗口重叠但实际中很少这么做。五、图像分类案例把所有知识串起来走通一个完整的 CNN 图像分类流程。完整流程加载数据 → 构建CNN网络 → 训练模型 → 进行测试构建 CNN 网络import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transforms # 1. 加载数据 transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset datasets.MNIST(root./data, trainTrue, downloadTrue, transformtransform) test_dataset datasets.MNIST(root./data, trainFalse, downloadTrue, transformtransform) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size1000, shuffleFalse) # 2. 构建CNN网络 class CNN(nn.Module): def __init__(self): super().__init__() # 第一组卷积 激活 池化 self.conv1 nn.Conv2d(1, 16, kernel_size3, stride1, padding1) self.relu1 nn.ReLU() self.pool1 nn.MaxPool2d(kernel_size2, stride2) # 第二组卷积 激活 池化 self.conv2 nn.Conv2d(16, 32, kernel_size3, stride1, padding1) self.relu2 nn.ReLU() self.pool2 nn.MaxPool2d(kernel_size2, stride2) # 全连接层 self.fc nn.Linear(32 * 7 * 7, 10) # 28→14→7通道32 def forward(self, x): # x 形状[B, 1, 28, 28] x self.pool1(self.relu1(self.conv1(x))) # → [B, 16, 14, 14] x self.pool2(self.relu2(self.conv2(x))) # → [B, 32, 7, 7] x x.view(x.size(0), -1) # 展平 → [B, 1568] x self.fc(x) # → [B, 10] return x model CNN() criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) # 3. 训练模型 for epoch in range(10): model.train() for data, target in train_loader: optimizer.zero_grad() output model(data) loss criterion(output, target) loss.backward() optimizer.step() # 4. 进行测试 model.eval() correct 0 with torch.no_grad(): for data, target in test_loader: output model(data) pred output.argmax(dim1) correct pred.eq(target).sum().item() accuracy 100. * correct / len(test_loader.dataset) print(f测试准确率{accuracy:.2f}%)代码解读网络结构两组卷积ReLU最大池化提取特征最后接一个全连接层做分类。第一组卷积输入 1 通道灰度图输出 16 个特征图卷积核 3×3padding1 保持尺寸不变。经过 ReLU 激活后2×2 最大池化把 28×28 压缩成 14×14。第二组卷积输入 16 通道输出 32 个特征图同样 padding1。池化后 14×14 变成 7×7。全连接层展平后 32×7×71568 维输入输出 10 维10 个数字类别。x.view(x.size(0), -1)这个展平操作是卷积层到全连接层的桥梁不可少。为什么 CNN 比 ANN 强同样的 MNIST 任务ANN 准确率 97% 左右CNN 能达到 99%。因为 CNN 保留了图像的空间结构卷积核能学习到左上角有个弧线这种空间特征而 ANN 把图像展平后这些信息全丢了。六、学习心得与建议第一理解局部感知和参数共享是理解 CNN 的钥匙。这两个概念解释了为什么 CNN 比 ANN 更适合处理图像——局部感知让网络关注局部结构参数共享让网络用少量参数覆盖整张图而且具备平移不变性。抓住这两个核心CNN 的设计思想就通了。第二特征图大小公式是基本功。(W - F 2P) / S 1这个公式贯穿整个 CNN 的设计——每加一层卷积或池化都要算输出尺寸变了多少。不弄清这个全连接层的输入维度就会算错代码直接报错。第三池化层是免费的降维。没有可学习参数没有反向传播计算只是一个固定操作。但它把特征图压缩了后面的计算量和参数量都大幅减少。简单但不可或缺。第四卷积核的通道数和个数是两个容易搞混的概念。卷积核的通道数必须等于输入的通道数才能做逐通道计算再相加而卷积核的个数等于输出的特征图数。一句话记住输入通道数决定卷积核有多厚输出通道数决定有多少个卷积核。写在最后从 ANN 到 CNN是深度学习处理图像的关键升级。ANN 把图像展平成一维向量丢失了空间信息CNN 用卷积核在图像上滑动扫描保留了空间结构能学习从边缘到纹理到复杂图案的层次化特征。今天学完了 CNN 的三大核心组件——卷积层提取特征、池化层压缩特征、全连接层输出结果。但今天的案例还比较简单真正的 CNN 经典模型LeNet、AlexNet、VGG、ResNet还有更多精妙的设计。下一步我们会深入这些经典网络结构学习更强大的 CNN 架构。图像的世界很精彩CNN 就是打开这扇门的钥匙。一步一步来你也能成为那个让机器看懂世界的人。
阅读完成 · 觉得有帮助?