深度卷积神经网络进化史与实战:从AlexNet到ResNet,手把手实现服装分类

张开发
2026/6/11 1:21:30 15 分钟阅读
深度卷积神经网络进化史与实战:从AlexNet到ResNet,手把手实现服装分类
前言如果你关注过人工智能的发展历程一定听说过2012年那个划时代的时刻——AlexNet在ImageNet图像分类挑战赛中以碾压性的优势夺冠将Top-5错误率从26.2%直接拉到15.3%。从此深度学习像一颗核弹引爆了整个计算机视觉领域并迅速蔓延到自然语言处理、语音识别等几乎所有AI分支。而引爆这一切的核心正是深度卷积神经网络Deep CNN。本文将带你系统回顾CNN的经典进化史AlexNet、VGG、GoogLeNet、ResNet并从一个完整的服装分类实战项目出发手把手教你用PyTorch搭建、训练和评估一个CNN模型。全文配有详细代码注释和原理解释即使你是初学者也能轻松跟上。一、深度CNN为何能“深度”传统的浅层神经网络1~3层在图像任务上表现平平因为图像中的特征具有层次性浅层网络只能提取边缘、颜色等低级特征深层网络则可以组合低级特征逐步形成纹理、部件乃至物体的高级语义表示。同时CNN通过权值共享和局部连接大大减少了参数数量使得训练深层网络成为可能。二、四大经典CNN架构解析1. AlexNet2012——深度学习“开山之作”2012年由Alex Krizhevsky、Ilya Sutskever与 Geoffrey Hinton合作提出是一个基于CNN构建的神经网络模型主要架构包含8层5个卷积层3个全连接层激活函数使用 ReLU最后经全连接层输出结果并且使用了Dropout。创新点使用ReLU激活函数加速收敛并缓解梯度消失。采用Dropout随机失活防止过拟合。利用GPU并行训练大幅提升计算速度。2. VGG2014——简单即美VGG由牛津大学视觉几何组提出主要有VGG-16和VGG-19两种。它证明了堆叠多个小卷积核3×3比使用大卷积核7×7效果更好同时参数更少。所有卷积层均使用3×3滤波器步长1padding1保持尺寸不变。池化层统一使用2×2最大池化步长2将尺寸减半。最后跟3个全连接层与AlexNet类似。VGG结构非常规整易于理解和复现因此被广泛用于迁移学习。3. GoogLeNet2014——更宽而非更深GoogLeNet也称Inception v1引入了Inception模块在同一层中并行使用1×1、3×3、5×5的卷积和3×3池化然后将结果在通道维度拼接起来。这种设计使得网络在“横向”上也具有了深度。Inception模块的优势1×1卷积可降维大幅减少参数。多尺度特征提取提高对不同大小物体的适应能力。缓解梯度消失问题。4. ResNet2015——残差学习训练超深网络不再难何恺明等人提出的ResNet残差网络是CNN史上的里程碑。它通过跳跃连接skip connection实现了恒等映射使网络学习的目标从原始的 h(x) 变为残差 f(x)h(x)-x。残差块公式h(x) F(x) x其中 F(x) 是需要学习的残差部分。当网络很深时残差学习比直接学习原始映射更容易优化有效解决了梯度消失和梯度爆炸。ResNet-152可以达到152层远超VGG的19层而性能却更好。三、实战基于Fashion MNIST的服装分类理论部分结束下面进入硬核实战环节。我们将使用Fashion MNIST数据集搭建一个与LeNet-5类似的CNN模型完成10类服装图像的分类任务。3.1 数据集介绍Fashion MNIST由Zalando Research发布包含70,000张28×28的灰度图像其中训练集60,000张测试集10,000张。共10个类别标签类别标签类别0T恤/上衣5凉鞋1裤子6衬衫2套头衫7运动鞋3连衣裙8包4外套9靴子3.2 环境准备与数据加载首先导入必要的库import torch import torch.nn as nn import torch.optim as optim import pandas as pd import numpy as np import matplotlib.pyplot as plt from torch.utils.data import TensorDataset, DataLoader plt.rcParams[font.sans-serif] [SimHei] # 黑体 # 方案B使用微软雅黑 # plt.rcParams[font.sans-serif] [Microsoft YaHei] # 解决负号显示为方块的问题 plt.rcParams[axes.unicode_minus] False # 设置随机种子保证结果可复现 torch.manual_seed(42)下载数据集CSV格式并读取# 从本地路径读取CSV文件 train_df pd.read_csv(data/fashion-mnist_train.csv) test_df pd.read_csv(data/fashion-mnist_test.csv) # 查看数据形状 print(f训练集形状: {train_df.shape}) # (60000, 785) print(f测试集形状: {test_df.shape}) # (10000, 785)数据集下载https://pan.baidu.com/s/1k-kQvWID5p_q9MAUXc61sQ?pwdi8jx数据预处理第一列是标签0~9后面784列是像素值28×28展开。需要将像素值reshape为 (batch, 1, 28, 28)并转换为 torch.float32。标签转换为 torch.int64CrossEntropyLoss要求。# 提取特征和标签 X_train torch.tensor(train_df.iloc[:, 1:].values, dtypetorch.float32).reshape(-1, 1, 28, 28) y_train torch.tensor(train_df.iloc[:, 0].values, dtypetorch.int64) X_test torch.tensor(test_df.iloc[:, 1:].values, dtypetorch.float32).reshape(-1, 1, 28, 28) y_test torch.tensor(test_df.iloc[:, 0].values, dtypetorch.int64) # 归一化将像素值从 [0, 255] 缩放到 [0, 1]有利于模型收敛 X_train / 255.0 X_test / 255.0 # 查看一个样本图像 plt.imshow(X_train[12345, 0, :, :], cmapgray) plt.title(fLabel: {y_train[12345].item()} - {[T恤,裤子,套头衫,连衣裙,外套,凉鞋,衬衫,运动鞋,包,靴子][y_train[12345].item()]}) plt.axis(off) plt.show()创建PyTorch的TensorDataset和DataLoadertrain_dataset TensorDataset(X_train, y_train) test_dataset TensorDataset(X_test, y_test) # DataLoader会自动打乱数据shuffleTrue并分批 batch_size 256 train_loader DataLoader(train_dataset, batch_sizebatch_size, shuffleTrue) test_loader DataLoader(test_dataset, batch_sizebatch_size, shuffleFalse)3.3 模型搭建我们构建一个类似LeNet-5的CNN结构如下使用nn.Sequential实现model nn.Sequential( # 第一个卷积块 nn.Conv2d(in_channels1, out_channels6, kernel_size5, stride1, padding2), nn.Sigmoid(), nn.AvgPool2d(kernel_size2, stride2), # 第二个卷积块 nn.Conv2d(6, 16, kernel_size5, stride1, padding0), # 输出尺寸: 28-5124? 不对前一层池化后是14再卷积14-5110 nn.Sigmoid(), nn.AvgPool2d(kernel_size2, stride2), # 10/25 # 全连接部分 nn.Flatten(), nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(), nn.Linear(120, 84), nn.Sigmoid(), nn.Linear(84, 10) ) # 测试各层输出形状 X_test_shape torch.rand(1, 1, 28, 28) print(逐层输出尺寸验证) for name, layer in model.named_children(): X_test_shape layer(X_test_shape) print(f{name:20} - {X_test_shape.shape})输出应类似3.4 权重初始化合适的初始化能加速收敛。我们采用Xavier均匀分布初始化线性层和卷积层def init_weights(m): if isinstance(m, nn.Linear) or isinstance(m, nn.Conv2d): nn.init.xavier_uniform_(m.weight) # 如果存在偏置项将其初始化为0 if m.bias is not None: nn.init.constant_(m.bias, 0) model.apply(init_weights)3.5 训练函数定义我们定义train函数包含模型移动至GPU如果可用损失函数交叉熵损失CrossEntropyLoss内部已包含Softmax优化器随机梯度下降SGD训练循环 验证循环记录每个epoch的损失和准确率def train_model(model, train_dataset, test_dataset, lr0.9, epochs20, batch_size256, devicecpu): # 移动到设备 model model.to(device) # 损失函数与优化器 criterion nn.CrossEntropyLoss() optimizer optim.SGD(model.parameters(), lrlr) # 创建DataLoader每个epoch重新打乱 train_loader DataLoader(train_dataset, batch_sizebatch_size, shuffleTrue) test_loader DataLoader(test_dataset, batch_sizebatch_size, shuffleFalse) train_losses [] train_accs [] test_accs [] for epoch in range(epochs): # ---------- 训练阶段 ---------- model.train() running_loss 0.0 correct_train 0 total_train 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) # 前向传播 outputs model(images) loss criterion(outputs, labels) # 反向传播与优化 optimizer.zero_grad() loss.backward() optimizer.step() running_loss loss.item() # 计算训练准确率 _, predicted torch.max(outputs, 1) total_train labels.size(0) correct_train (predicted labels).sum().item() epoch_loss running_loss / len(train_loader) epoch_train_acc correct_train / total_train train_losses.append(epoch_loss) train_accs.append(epoch_train_acc) # ---------- 验证阶段 ---------- model.eval() correct_test 0 total_test 0 with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) total_test labels.size(0) correct_test (predicted labels).sum().item() epoch_test_acc correct_test / total_test test_accs.append(epoch_test_acc) # 打印进度 print(fEpoch [{epoch1:2d}/{epochs}] Loss: {epoch_loss:.4f} | Train Acc: {epoch_train_acc:.4f} | Test Acc: {epoch_test_acc:.4f}) return train_losses, train_accs, test_accs3.6 启动训练device torch.device(cuda if torch.cuda.is_available() else cpu) print(fUsing device: {device}) # 重新初始化模型确保参数随机 model nn.Sequential( nn.Conv2d(1, 6, kernel_size5, padding2), nn.Sigmoid(), nn.AvgPool2d(2, 2), nn.Conv2d(6, 16, kernel_size5), nn.Sigmoid(), nn.AvgPool2d(2, 2), nn.Flatten(), nn.Linear(400, 120), nn.Sigmoid(), nn.Linear(120, 84), nn.Sigmoid(), nn.Linear(84, 10) ) model.apply(init_weights) losses, train_accs, test_accs train_model( model, train_dataset, test_dataset, lr0.9, epochs20, batch_size256, devicedevice )3.7 训练结果可视化plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(range(1, 21), losses, markero) plt.xlabel(Epoch) plt.ylabel(Loss) plt.title(Training Loss) plt.subplot(1, 2, 2) plt.plot(range(1, 21), train_accs, labelTrain Acc, markers) plt.plot(range(1, 21), test_accs, labelTest Acc, marker^) plt.xlabel(Epoch) plt.ylabel(Accuracy) plt.title(Accuracy Curve) plt.legend() plt.grid(True) plt.tight_layout() plt.show()在合理超参数下最终测试准确率通常能达到90%~92%左右。如果调整学习率、增加Batch Normalization或使用Adam优化器还可以进一步提升。3.8 使用模型进行预测# 随机取5张测试集图片进行预测 model.eval() test_iter iter(test_loader) images, labels next(test_iter) # 取前5张 images_sample images[:5].to(device) labels_sample labels[:5] with torch.no_grad(): outputs model(images_sample) _, preds torch.max(outputs, 1) # 显示结果 class_names [T恤/上衣, 裤子, 套头衫, 连衣裙, 外套, 凉鞋, 衬衫, 运动鞋, 包, 靴子] plt.figure(figsize(12, 4)) for i in range(5): plt.subplot(1, 5, i1) plt.imshow(images_sample[i].cpu().squeeze(), cmapgray) plt.title(fTrue: {class_names[labels_sample[i]]}\nPred: {class_names[preds[i].item()]}) plt.axis(off) plt.tight_layout() plt.show()四、总结与展望通过本文我们回顾了深度卷积神经网络的发展历程从AlexNet的革命性突破到ResNet的残差学习每一次架构创新都极大推动了计算机视觉的发展。随后我们使用PyTorch完整实现了一个服装分类任务涵盖了数据加载、模型构建、训练、评估和预测的全流程。要点回顾深度CNN通过层次化特征提取极大提升了图像分类精度。经典架构各有侧重AlexNet引入ReLU和DropoutVGG强调小卷积核堆叠GoogLeNet用Inception模块增加宽度ResNet用跳跃连接训练超深网络。实战代码展示了从CSV数据到模型部署的每一步注释详尽可直接复用。下一步建议尝试将本项目的简单CNN替换为ResNet-18或VGG-16观察精度提升。加入数据增强随机旋转、翻转等进一步提升泛化能力。使用学习率调度器如torch.optim.lr_scheduler.StepLR动态调整学习率。深度学习的魅力在于你不需要从零发明一切但必须深刻理解已有工具的原理。希望本文能为你打下坚实的基础在AI之路上走得更远。如果觉得本文对你有帮助欢迎点赞、收藏、转发。有任何疑问欢迎评论区交流讨论

更多文章