【深度学习】国科大:CIFAR-100 图像分类项目 一、这个项目在做什么一句话概括教会计算机看图识物。具体来说我们拿到了一个叫 CIFAR-100 的图片数据集——6 万张 32×32 像素的小图片涵盖苹果、汽车、老虎、向日葵等 100 种类别。我们的任务是训练神经网络让它看到一张图后能判断这是什么。听起来简单但 32×32 像素非常小大约指甲盖大小的缩略图而且 100 个类别里有很多很相近的东西比如不同品种的花、不同类型的车所以分类难度其实不低。为了搞清楚什么样的网络 什么样的训练方式效果最好我们设计了一组系统的对比实验两种网络结构MLP 和 CNN两种优化器SGD 和 Adam三个学习率0.1、0.01、0.001交叉组合一共 12 组实验一键运行自动对比。项目见https://github.com/david-gao1/883AI/tree/main/%E7%A0%94%E7%A9%B6%E7%94%9F%E4%BD%9C%E4%B8%9A/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0二、两种网络MLP 和 CNNMLP——最朴素的做法MLP多层感知机是最基础的神经网络。它处理图片的方式非常粗暴把一张 32×32×3 的彩色图片直接拍平成一个长度为 3072 的数字串然后送进两层全连接网络最后输出 100 个分数分数最高的那个类别就是答案。MLP 的问题在于它把图片拍平的一瞬间所有空间位置信息就丢了——左上角的像素和右下角的像素在向量里变成了地位平等的数字网络不知道它们谁和谁是邻居。一只猫的眼睛和耳朵明明挨着拍平后网络完全感知不到这个关系。但 MLP 并不是没有价值。它是一个重要的基线——先用最朴素的方式跑一遍再用更高级的方式跑一遍两相对比才能清楚地看出改进有多大。CNN——像人眼一样看图CNN卷积神经网络的设计哲学直接模仿了人眼的工作方式。你看一张猫的照片大脑不会一个像素一个像素地扫描而是先看局部——这里有尖尖的轮廓耳朵、这里有圆形高光眼睛、这里有柔和纹理毛发然后把这些局部特征组合起来得出这是一只猫的结论。CNN 做的事情一模一样用一个 3×3 的小窗口卷积核在图片上滑动每滑一步就计算出窗口覆盖区域的一个特征值。不同的卷积核能检测不同的模式——有的专门找水平边缘有的专门找垂直边缘有的对颜色变化敏感。这些卷积核不是人工设计的而是网络在训练过程中自己学出来的。卷积扫描完一遍后用池化操作把特征图缩小——每 2×2 的区域取最大值尺寸减半。这样做既减少了计算量又让网络的视野变大了同样的 3×3 卷积核在缩小后的特征图上实际覆盖的原图区域更大。重复几轮卷积 池化特征从低级边缘、色块逐渐变成高级纹理、部件、物体轮廓。一张 32×32 的图片经过三轮处理后变成了 4×4空间尺寸缩了 8 倍但通道数从 3 涨到了 256——图变小了每个位置蕴含的语义信息却更丰富了。最后把这些高级特征拍平送进全连接层做最终分类。本项目的 CNN 采用了经典的 VGG 风格设计三个阶段每个阶段包含两层 3×3 卷积通道数依次为 64、128、256中间穿插批归一化稳定训练、ReLU 激活引入非线性、最大池化缩小尺寸和 Dropout防过拟合。麻雀虽小五脏俱全——理解了这个结构再去看 ResNet、EfficientNet 这些更复杂的网络就只是在这些基础组件上做加法。三、训练一个神经网络意味着什么神经网络刚创建时所有参数卷积核里的数字、全连接层的权重都是随机的它对图片的分类完全靠蒙。训练的过程就是不断地看题 → 答题 → 对答案 → 改参数循环往复直到它学会为止。每一轮epoch的训练流程是这样的第一步前向传播。把一批 128 张图片送入网络网络层层计算最后输出每张图对 100 个类别的得分。第二步算损失。用交叉熵损失函数衡量预测和真实答案差多远。如果网络给正确类别打了最高分损失就小如果正确类别的分数被其他类别压过去了损失就大。第三步反向传播。从损失出发沿着网络的计算链路从后往前追溯算出每个参数对这次错误贡献了多少——这就是梯度。梯度告诉你如果你把这个参数往某个方向调一点点损失会变小还是变大。第四步更新参数。优化器根据梯度调整参数往损失变小的方向走一步。步子多大由学习率决定。把训练集里的 45000 张图全部过一遍就算完成了一个 epoch。本项目默认训练 10 个 epoch。训练过程中还有一个重要的细节每个 epoch 结束后我们会在验证集5000 张图上评估模型的表现记录验证准确率最高的那一轮模型参数。为什么不直接用最后一轮的参数因为最后一轮不一定最好——可能第 7 轮时验证准确率最高之后模型反而开始死记硬背训练数据了过拟合。保留验证集上最好的那一轮就是一种简单有效的防过拟合策略。四、优化器和学习率——训练效果的决定性因素SGD 和 Adam 的区别优化器决定了怎么根据梯度调参数。可以用一个下山的比喻来理解你站在山上想尽快走到最低点损失最小的地方。SGD随机梯度下降 动量的做法是每一步沿着当前最陡的下坡方向走一步步长就是学习率。加上动量后它还会记住之前的行进方向——就像一个滚下山的球有惯性不容易被小坑小洼拦住。SGD 的所有参数共用一个学习率所以对学习率的选择很敏感太大会震荡飞出去太小会爬得很慢。但如果调好了SGD 训出来的模型泛化性能往往不错。Adam自适应矩估计则更聪明它给每个参数单独维护一份档案记录这个参数最近的梯度大小和波动程度。梯度一直很稳定的参数它就大胆地迈大步梯度忽大忽小的参数它就谨慎地迈小步。这种自适应学习率的特性让 Adam 几乎不需要精心调参开箱即用收敛通常也更快。代价是每个参数多了两倍的存储开销。学习率的影响学习率控制的是每步走多远。本项目试了三个值0.1太大相当于下山时跳着走很可能一脚踩过头跳到对面山坡上。实验中 Adam 0.1 的组合直接完全发散准确率降到了 1%——等于在 100 个类别里瞎猜。0.001太小相当于一毫米一毫米地挪10 个 epoch 还没走到好的位置。SGD 0.001 的收敛速度明显慢于 SGD 0.01。0.01适中正常步伐走路稳定下山。实验中 SGD 0.01 是 CNN 的最佳组合Adam 的最佳学习率则更小一些0.001这恰好印证了 Adam 的自适应步长本身就偏大需要更小的初始学习率来配合。实验结果充分说明了一个道理学习率是最敏感的超参数。一个不合适的学习率可以直接让训练崩掉而优化器的选择反倒是次要的。五、防止过拟合的三道防线神经网络有很强的记忆力如果不加约束它很容易把训练数据的细枝末节都背下来包括噪声和异常到了新数据上就抓瞎——这就是过拟合。本项目用了三种手段来对抗它第一道数据增强。训练时每次读取一张图片都随机做两件事四周补 4 像素后随机裁剪回 32×32相当于随机平移以及 50% 概率水平翻转。这让模型每次看到同一张图的不同变体等价于人为增加了数据量减轻了死记硬背的可能。第二道Dropout。训练时随机关掉 30% 的神经元CNN 里是关掉整个通道迫使网络不能依赖任何一个特定的神经元来做决策。每一步训练的其实是一个残缺版的网络最终效果相当于集成了指数级多个小网络泛化能力天然更强。测试时所有神经元都打开PyTorch 会自动处理这个切换。第三道权重衰减。两个优化器都加了weight_decay1e-4每次更新参数时都把权重往零的方向拉一点点。这防止参数长得过大让网络保持简洁。此外批归一化BatchNorm本身也有轻微的正则化效果——因为每个 batch 的统计量有随机性等于给训练加了一点噪声。六、实验结果CNN 对 MLP 的碾压12 组实验全部跑完后结果非常清晰CNN 全面优于 MLP。在最佳配置下SGD lr0.01CNN 测试准确率 38.69%MLP 只有 20.45%——将近一倍的差距。在所有对应配置下CNN 都比 MLP 好没有例外。这个结果并不意外CNN 保留了图片的空间结构能看到哪些像素是邻居而 MLP 一拍平就把这些信息全丢了。SGD 比 Adam 更稳。在较宽的学习率范围内0.001~0.01SGD 都能有效训练而 Adam 只在 lr0.001 时可用lr 稍大就崩掉。SGD 是图像分类里的经典选择配合动量和合理的学习率就能出好结果。10 个 epoch 远远不够。最好的模型CNN SGD lr0.01到第 10 轮时验证准确率还在持续上升loss 还在持续下降完全没有收敛。如果训练 50~100 个 epoch配合学习率衰减策略准确率预计还能大幅提升。学习率 0.1 对 Adam 是灾难。Adam 自适应步长本身就偏大再给一个大学习率参数更新就完全失控了。CNN Adam lr0.1 和 MLP Adam lr0.1 的测试准确率都掉到了 1%和瞎猜一样。从训练曲线上也能清楚地看到这些规律好的配置下 loss 稳定下降、accuracy 持续上升差的配置下 loss 高位震荡、accuracy 趴在底部不动。七、工程层面怎么把实验跑起来好的实验不只是算法好还需要工程设计能让实验跑得顺、管得住、扩展得了。文件组织整个项目分成几个清晰的模块src/models.py只管网络长什么样——定义 MLP 和 CNN 两个类以及一个根据名字创建模型的工厂函数。将来要加新模型比如 ResNet只在这个文件里加一个类和一行注册代码就行。src/trainer.py管怎么训练——数据加载、优化器创建、训练循环、画图、保存结果全在这里。train.py是总指挥——它自己不实现任何逻辑只负责解析命令行参数、生成实验组合、依次调用上述函数。summarize_results.py是报告员——训练完成后读取所有实验的 JSON 结果生成一份 Markdown 汇总报告。这种调度层 / 逻辑层 / 模型层分开的设计让任何一处改动都只影响一个文件不会牵连其他部分。一键跑完 12 组实验所有超参数都通过命令行控制。默认配置就是 2 模型 × 2 优化器 × 3 学习率 12 组一行命令跑完uv run train.py--epochs10如果只想快速验证代码能不能跑通可以用 5% 的数据跑 1 轮uv run train.py--epochs1--subset-ratio0.05每组实验的结果独立存放在各自的目录下模型参数、训练曲线、预测可视化、指标 JSON最后汇总成一张 CSV 排名表。可复现性固定随机种子42保证了同样的代码 同样的参数永远得到同样的结果。设备自动选择Mac GPU → NVIDIA GPU → CPU让代码在任何机器上都能跑。依赖用pyproject.tomluv.lock管理uv sync一行命令安装好所有库。八、核心概念速查概念一句话解释卷积用小窗口在图片上滑动提取局部特征池化每个小区域取最大值缩小图片保留关键信息ReLU负数变 0给网络引入非线性——没有它再多层也等于一层BatchNorm校准每层输出的分布让训练更稳定更快Dropout训练时随机关掉一部分神经元防止网络死记硬背全连接层每个输入都和每个输出相连用于最终分类交叉熵损失衡量你给正确类别打了多高的分分数越高损失越小前向传播数据从输入到输出依次经过各层的计算反向传播从损失出发逆序算出每个参数的梯度梯度告诉你参数往哪个方向调能让损失变小学习率每次调参走多大一步epoch完整遍历训练集一次batch一次送入网络的一小批图片128 张过拟合训练集表现好、新数据表现差——背答案了数据增强对训练图片做随机变换增加多样性九、总结与展望这个项目虽然只用了最基础的 MLP 和 CNN但完整覆盖了深度学习图像分类的核心链路数据加载与预处理 → 模型设计 → 损失函数选择 → 优化器配置 → 训练循环 → 结果评估与可视化。12 组对比实验清晰地展示了三个关键结论CNN 比 MLP 强在哪里空间特征提取、学习率为什么是最敏感的超参数太大发散太小龟速、SGD 和 Adam 各自的脾气SGD 稳但需调参Adam 方便但对大学习率敏感。从工程角度看项目采用了模块化设计、命令行参数控制、笛卡尔积实验遍历、独立目录隔离结果、自动生成报告等实践使得整个实验流程可复现、可扩展、易维护。如果继续改进有几个明确的方向增加训练轮数配合学习率衰减余弦退火或阶梯衰减、引入更深的网络结构ResNet 的残差连接可以让训练几百层成为可能、加强数据增强策略Mixup、CutMix、使用学习率预热Warmup缓解初始阶段的不稳定。但即使是当前这个版本它已经把深度学习图像分类的核心知识和工程实践都串起来了——理解了这些再往更深更广的方向走都会顺畅很多。