拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CNN入门实战:从LeNet-5到AlexNet,用PyTorch实现图像分类

卷积神经网络CNN是深度学习入门时绕不开的第一座山。图像分类、目标检测、人脸识别、OCR、视频理解底层几乎都有卷积、池化、激活函数和全连接层这四个组件的影子。很多新手在学到这里时容易陷入两个极端一是盯着公式推导不放越推越懵二是直接复制一段网络代码跑起来却不知道每一层到底在做什么后面调参、换任务时完全无从下手。这篇文章就按最直接的方式把 CNN 讲清楚。你会看到卷积层、池化层、激活函数、全连接层各自的输入输出和代码实现然后用 LeNet-5 和 AlexNet 两个经典网络把整套知识串起来。最后我给出一个可以完整运行的 PyTorch 手写数字识别工程涵盖环境安装、模型定义、训练验证、批量推理、ONNX 导出和 HTTP 接口封装。整个流程对 CPU 友好入门显卡速度更快适合刚接触深度学习的人照着做。本文适合三类读者正在学机器学习基础、需要快速理解 CNN 工作原理的学生准备面试、需要把网络结构讲清楚的求职者以及想在本地做一个图像识别小工具、走通“训练—导出—部署”全流程的开发者。文章比较长建议先收藏再照着跑一遍。1. CNN 核心概念速览先建立整体印象后续章节再逐个展开。表里列出的都是本次会讲到的重点。概念核心作用一句话理解卷积层提取局部特征用一个小窗口在图片上滑动把局部模式提取出来池化层降低空间尺寸把相邻区域压缩成一个值减少计算量激活函数引入非线性让网络能拟合复杂映射常用 ReLU全连接层特征组合与分类输出把前面提取的特征打平成向量映射到类别概率LeNet-5经典手写数字识别网络1998 年提出结构简洁适合入门AlexNet深度学习爆发标志2012 年 ImageNet 夺冠奠定现代 CNN 基本范式本文用到的技术路线如下项目说明任务类型图像分类手写数字识别网络模型LeNet-5、AlexNet开发框架PyTorch torchvision数据集MNIST运行设备CPU 可跑NVIDIA GPU 加速批量预测支持按目录批量处理接口封装FastAPI HTTP 服务导出格式ONNX / TorchScript需要说明的是显存占用和训练速度与 batch size、图片分辨率、网络层数直接相关没有一个统一数字。后面我会给出观察方法而不是拍脑袋报一个固定值。机器不同结果只能以本机为准。2. CNN 要解决什么问题适用场景与使用边界CNN 提出的核心背景是传统的全连接网络在处理图像时会“参数爆炸”。一张 32x32x3 的 CIFAR 图片直接拉平后是 3072 维。如果第一层全连接设置 1024 个神经元这一层就有约 315 万个参数如果换成 224x224x3 的 ImageNet 图片第一层全连接的参数量会轻松过亿。更大的问题是图像里的目标并不会因为你把它整体拉平就改变局部结构猫的眼睛、耳朵、胡须这些特征应该是“在哪里都认识”的全连接网络做不到这一点。CNN 用三个设计解决了这个问题局部连接每个神经元只看图片的一个小区域权值共享同一个卷积核在整个图上滑动参数不随位置变化空间降维通过池化逐步压缩特征图。这三板斧让图像分类的参数量和过拟合风险大幅下降也让模型对目标位置有了一定的容忍度。这也是卷积神经网络CNN在图像任务中长期占据主导地位的根本原因。使用边界也要说清楚。CNN 适合图像、视频帧、一维信号等具有明显局部相关性的数据。对于长文本、长序列、需要全局依赖建模的任务CNN 并不占优势这也是后来 Transformer 在很多场景里取代 CNN 的原因之一。实际工程中两者经常配合使用比如先用 CNN 提取局部特征再用注意力机制做全局建模。CNN 没有“过时”而是成为基础组件。3. 卷积层CNN 的灵魂卷积层的输入一般是形状为 (N, C, H, W) 的四维张量N 是 batch sizeC 是输入通道数H 和 W 是高度和宽度。对 MNIST 来说单张图是 1 通道、28x28所以形状是 (1, 1, 28, 28)CIFAR-10 则是 3 通道、32x32。卷积核是一个很小的窗口常见 3x3、5x5在图上从左上到右下按步长滑动每次把窗口内的像素和卷积核参数做加权求和再加上偏置得到一个输出值。三个关键参数决定输出尺寸卷积核大小 K、步长 stride、填充 padding。输出尺寸可以按下面的公式计算out (W - K 2 * padding) / stride 1例如输入 28x28卷积核 5x5padding2stride1输出仍是 28x28如果不做 padding输出会变成 24x24。这个公式是调试代码和面试时最高频的问题建议直接记住。PyTorch 里一行代码就能定义卷积层import torch.nn as nn conv nn.Conv2d( in_channels1, # 输入通道数MNIST 为 1 out_channels6, # 输出通道数也就是滤波器数量 kernel_size5, # 卷积核大小 stride1, padding2 )卷积层的参数量是 (K * K * C_in 1) * C_out。以这个例子计算(5x5x11)*6156 个参数非常小。真正的大头一般在全连接层这也是为什么后来出现了全局平均池化和 1x1 卷积来压缩参数。理解了参数量计算方式你就能解释为什么同一个网络在 28x28 小图上很轻量换成 224x224 大图后显存会迅速上涨。4. 池化层降维不是玄学池化层不改变通道数只改变空间尺寸。最常用的是最大池化和平均池化。最大池化取窗口内的最大值平均池化取平均值。默认做法是 2x2 窗口、stride2这样宽高各减半整体特征图缩小为原来的四分之一。池化的价值主要有三个。一是降低计算量后面每一层的数据量都变小二是扩大感受野让网络在更靠后的层看到更大的范围三是提供一定的平移不变性目标稍微移动几个像素最大池化的输出通常不会剧烈变化。PyTorch 里是这样写的pool nn.MaxPool2d(kernel_size2, stride2)也有不少人会问既然卷积层可以用 stride2 来降采样为什么还要池化答案是可以互相替代现代网络很多用 stride2 的卷积代替池化比如 ResNet 系列。原版 LeNet-5 和 AlexNet 用的是池化所以学习经典结构时还是要把池化理解透。池化本身没有可学习的参数这也是它和卷积层最明显的区别之一。5. 激活函数把非线性加回来如果网络里只有卷积和全连接那么无论堆叠多少层整体都等价于一个线性变换。激活函数的作用就是给网络引入非线性让模型能拟合复杂的映射关系。常见候选有 Sigmoid、Tanh、ReLU 和 LeakyReLU。早期网络常用 Sigmoid它的输出在 0 到 1 之间有一个明显问题输入绝对值较大时梯度接近 0深度网络中误差反向传播时会逐层衰减导致前面层几乎学不动。ReLU 的出现缓解了这一点正区间梯度恒为 1负区间输出为 0计算简单让深层网络可以更好地被训练。AlexNet 能顺利训练起来ReLU 是重要原因之一。PyTorch 里的定义很简单activation nn.ReLU(inplaceTrue)使用时需要留意 ReLU 的缺点负数部分被直接置零可能造成神经元死亡学习率过大时尤其明显。遇到这种情况可以换 LeakyReLU、PReLU 或 GELU。激活函数不是一个可以忽略的超参数不同任务、不同网络深度下它对收敛速度和最终精度都有直接影响。6. 全连接层与分类头全连接层的作用是把前面卷积和池化得到的特征图压平成一维向量然后接若干线性层。以 LeNet-5 在 MNIST 上的一个简单变体为例最后一个池化层输出 16 个 5x5 的特征图压平后是 16x5x5400 维。这 400 维向量经过两层线性变换最后映射到 10 个类别。分类头最后通常会接 softmax或者直接配合 CrossEntropyLoss 使用。PyTorch 的 CrossEntropyLoss 内部已经做了 softmax所以模型最后一层直接输出 logits 即可不需要手动加 softmax。训练时用 CrossEntropyLoss推理时想要概率可以再套一层 softmax。全连接层的问题在于参数多。400 维转到 120 维参数量约 4.8 万如果输入是 224x224 的彩色大图全连接层参数量会暴涨。因此现代分类网络往往用全局平均池化替代部分全连接层。这一变化在从 AlexNet 到 ResNet 的发展中可以看得很清楚。对初学者来说先掌握全连接层的压平和输出逻辑再去看全局平均池化会顺畅很多。7. LeNet-5 实战从零实现手写数字识别7.1 环境准备与数据下载建议使用 Python 3.8 以上版本。先创建虚拟环境避免依赖冲突python -m venv venv # Windows venv\Scripts\activate # Linux / macOS source venv/bin/activate然后安装 PyTorch。CPU 用户可以直接装 CPU 版体积小很多pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu有 NVIDIA 显卡的用户先去 PyTorch 官网根据 CUDA 版本选择对应的安装命令不要照抄上面的 CPU 命令。安装完成后执行下面的脚本确认设备import torch print(torch.__version__) print(torch.cuda.is_available()) if torch.cuda.is_available(): print(torch.cuda.get_device_name(0))如果输出 True说明 GPU 可用如果输出 False也没关系本教程全部步骤都可以在 CPU 上完成只是训练速度会慢一些。MNIST 是 60k 训练图、10k 测试图的 28x28 灰度手写数字数据集。用 torchvision 可以自动下载import torchvision import torchvision.transforms as T transform T.Compose([ T.ToTensor(), T.Normalize((0.1307,), (0.3081,)), ]) train_ds torchvision.datasets.MNIST(root./data, trainTrue, transformtransform, downloadTrue) test_ds torchvision.datasets.MNIST(root./data, trainFalse, transformtransform, downloadTrue) print(len(train_ds
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门