资讯动态

Fashion-MNIST图像分类实战:识别、图片展示与90%准确率

发布时间:2026/10/27 4:53:20 来源:尧图企业网站定制
1. 项目缘起与整体设计思路做图像分类练手很多人第一个真正跑通的项目就是 Fashion-MNIST。它不像手写数字那样“干净得不像话”10 个类别全是衣服鞋包类间差异小、类内差异大一件毛衣和一件衬衫在 28×28 的灰度图里经常糊成一团。我当初选它做自用项目目标很明确不追求刷榜而是搭一条从数据加载、模型训练、准确率评估到图片展示的完整闭环而且准确率要能稳稳站在 90% 以上同时训练时间控制在普通笔记本能接受的范围。这个标题里的三个要素——识别、图片展示、准确率 90%——其实对应了三个真实需求模型能分类、结果能看见、效果能验证。先说清楚这个项目适合谁。如果你是刚学完卷积神经网络、想找个不那么玩具的数据集练手的人它很合适如果你是想给家里小孩做个“拍照识衣服”的小工具原型它也能跑如果你只是想要一份把训练、评估、可视化串起来的可复现代码骨架那更合适。不适合谁呢想直接拿去做工业级服饰识别的人不适合28×28 灰度图的信息量摆在那里它只能证明你的流程通顺、模型结构合理换成真实商品图还得重新做数据。设计思路上我做了几个取舍后面会逐个展开。第一模型不用太大两层卷积加池化的结构就足够把准确率推过 90%再往上堆层数收益递减反而容易在小数据集上过拟合。第二必须留出独立的验证集和测试集很多人把测试集反复用来调参最后报的 90% 是“看过答案的 90%”没有意义。第三图片展示这一环不能省训练日志里的数字是抽象的把预测结果和原图并排画出来你才能直观感受到模型到底在哪些类别上犯糊涂。还有一个容易被忽略的点数据预处理的一致性。训练时做了什么归一化推理时就必须做完全一样的处理否则同一个模型在展示环节会表现得很差你会误以为是模型不行其实是流程有 bug。我在这个项目里把预处理封装成一个函数训练、评估、单图推理三处共用从根上避免这类问题。2. 数据集拆解与预处理的关键细节2.1 Fashion-MNIST 到底长什么样这个数据集包含 70000 张 28×28 的灰度图片其中 60000 张是训练集、10000 张是测试集共 10 个类别T 恤/上衣、裤子、套头衫、连衣裙、外套、凉鞋、衬衫、运动鞋、包、短靴。类别标签是 0 到 9 的整数这个顺序一定要记牢展示图片时要用它把数字翻译成中文名称否则你看到一堆“类别 6”根本不知道是衬衫还是包。它最大的特点是“规整”每张图都是居中裁剪过的人物、背景、光照这些干扰因素基本被去掉了。好处是入门友好坏处是模型学到的特征偏“剪影”换到真实场景会不灵。我个人的看法是把它当流程验证集别当能力上限。你在这个数据集上跑到 92%、93%说明你的训练流程是对的但别拿这个数字去对外宣称模型有多强。还有一个实际使用中的坑类别不均衡的问题虽然不严重但衬衫shirt这一类特别难分它和 T 恤、套头衫、外套在轮廓上高度重叠。我做混淆矩阵的时候发现模型错得最多的就是衬衫被认成 T 恤、套头衫被认成外套。这不是你的代码有问题是数据本身的相似度决定的所以后面我在展示环节特意把错分样本单独画出来看。2.2 归一化参数的来历与实操注意事项常见的做法是除以 255 把像素压到 0 到 1 之间或者用全数据集算出的均值 0.2860、标准差 0.3530 做标准化。这两种都行我实测下来标准化收敛稍快一点但对最终准确率影响不到半个百分点。真正会影响结果的是训练集和测试集必须用同一套参数。有些教程图省事直接在测试集上重新算均值和方差这是典型的数据泄漏报出来的准确率虚高一旦换成真实新图就原形毕露。我给自己的规则是统计量只在训练集上算一次写成常量训练、评估、推理全部引用它。代码里就这么几行但能省掉后面大量的困惑。import numpy as np MEAN 0.2860 STD 0.3530 def preprocess(images): x images.astype(np.float32) / 255.0 x (x - MEAN) / STD return x[:, :, :, np.newaxis] # 补上通道维注意如果你用的是现成的数据加载库它可能默认已经做过某种变换别叠加两次。动手前先打印一张图的最大值、最小值和均值确认数据处在什么范围这一步花 10 秒能避免后面半小时的排查。2.3 数据增强小数据集上的双刃剑Fashion-MNIST 的图都是居中且朝向固定的直接做水平翻转其实有点争议因为左右翻了以后凉鞋、短靴的形态会变得不太自然但实测影响很小我保留了这个操作。旋转和缩放我控制得很保守旋转角度不超过 10 度缩放比例在 0.9 到 1.1 之间目的是让模型对轻微的位移鲁棒一点而不是把图变成另一件衣服。这里有个经验增强强度不是越大越好。我早期试过 20 度旋转加 0.8 到 1.2 的缩放训练集准确率掉得厉害验证集却没涨说明增强把有效信息破坏掉了。判断增强是否合适的标准很简单——你自己看图还能认出这是裤子吗认不出就把参数调回来。3. 模型结构与训练配置的落地实现3.1 为什么选这个结构我用的是一个很朴素的卷积网络结构如下表。这个结构在 CPU 上训练十几轮就能过 90%GPU 上更快适合自用和反复调试。层类型参数配置输出尺寸说明卷积层 132 个 3×3 卷积核padding128×28×32提取边缘、纹理池化层 12×2 最大池化14×14×32降采样卷积层 264 个 3×3 卷积核padding114×14×64组合低级特征池化层 22×2 最大池化7×7×64降采样展平-3136转成一维全连接层 1128 个神经元ReLU128特征整合Dropout丢弃率 0.3128抑制过拟合输出层10 个神经元Softmax10输出类别概率这套结构的逻辑是两次池化把 28×28 压到 7×7感受野足够覆盖整件衣服的轮廓通道数从 32 翻到 64让模型有空间去表达更复杂的纹理组合。全连接层只留一层 128是为了防止参数过多。Dropout 放在全连接层前面而不是卷积层里因为卷积层的参数共享本身就有一定正则效果在卷积后加 Dropout 收益不大还会拖慢收敛。参数量的粗略计算也值得说一句方便你判断显存和训练时间。第一层卷积约 32×3×3×1 加偏置不到 320 个参数第二层约 64×3×3×32接近 1.8 万个大头在全连接层3136×128 约 40 万个。总参数量在 45 万左右非常轻量一张入门级显卡甚至纯 CPU 都能跑。这个规模的模型有个好处你可以在一小时内做完几十次实验快速感受学习率、批大小、增强强度对结果的影响这比盯着一个大模型跑一整天更有学习价值。3.2 训练配置与参数选择过程优化器用 Adam学习率初值 1e-3批大小 64训练轮数 20 轮。这几个数字不是我随口定的说一下我当时的试错过程。学习率我试过 1e-2前几轮损失下降很快但到第 5 轮左右开始震荡验证准确率卡在 87% 上不去换成 1e-3 后曲线明显平滑第 12 轮左右越过了 90%1e-4 太慢20 轮还没收敛。批大小方面64 是比较稳的折中128 训练更快但梯度估计噪声小、泛化略差32 太慢。这些都是经验值但方向是对的学习率负责“走得对不对”批大小负责“走得稳不稳”。学习率调度我加了一个简单的衰减策略验证集准确率连续 3 轮不提升学习率乘以 0.5。这个策略解决了我遇到的一个具体问题——模型在第 10 轮前后准确率停滞加了衰减后通常还能再挤出 1 到 2 个百分点。除了调度我还用了早停耐心值设为 6 轮防止过拟合白跑。model.compile( optimizerkeras.optimizers.Adam(learning_rate1e-3), losssparse_categorical_crossentropy, metrics[accuracy] ) callbacks [ keras.callbacks.ReduceLROnPlateau( monitorval_accuracy, factor0.5, patience3, min_lr1e-5), keras.callbacks.EarlyStopping( monitorval_accuracy, patience6, restore_best_weightsTrue) ]注意sparse_categorical_crossentropy对应标签是整数0 到 9如果你的标签做成了独热编码就要换成categorical_crossentropy两者用错不会报错但损失会算错准确率永远上不去这是很隐蔽的坑。3.3 训练结果与准确率构成分析最终我在测试集上拿到 91.6% 左右验证集曲线和测试集基本贴合没有出现验证集远高于测试集的情况说明划分合理、没有明显过拟合。为了确认这个数字可信我做了三件事一是固定随机种子重跑三次结果浮动在 0.4 个百分点以内二是看混淆矩阵确认错误的分布符合直觉衬衫、套头衫、外套三者互相错分占了大头三是把错分样本的图片调出来肉眼检查发现有几张确实模糊到人眼也难判断。这里特别提醒一句达到 90% 不难难的是知道这 90% 是怎么来的。如果你的训练准确率 98%、测试准确率 88%说明过拟合该加正则或减容量如果两个都只有 85%说明欠拟合该加轮数或加容量如果测试集和验证集差距大先怀疑数据划分和预处理不一致。这几个判断路径比盲目调参有用得多。4. 图片展示环节的实现与调优4.1 展示什么比怎么展示更重要很多人理解的“图片展示”就是把预测结果画出来但真正有用的是分层展示。我把它分成三个视图第一个是随机抽样展示看图、真实标签、预测标签、置信度四元组第二个是错分样本专页只展示预测错误的图按置信度从高到低排列这些“错得很自信”的样本最值得研究第三个是混淆矩阵热力图看清哪两类纠缠最严重。随机抽样展示的实现不难用 matplotlib 画子图把图片反归一化后显示标题写“真衬衫 / 预T 恤 0.72”。这里有个细节反归一化要用同一套均值和标准差先乘标准差再加均值最后裁到 0 到 1 区间否则图会偏暗或偏亮看起来像数据坏了。错分样本视图是我认为最有价值的一环。我印象很深的是一张被认成 T 恤的衬衫领口纹理在 28×28 下几乎消失只剩一个宽肩轮廓人眼乍看也会犹豫。这种样本提醒你模型的错误不全是 bug有一部分是分辨率决定的天花板。这也解释了为什么准确率在 91% 到 93% 之间就开始变得难挤——剩下的错误很多是数据本身的信息不足。4.2 置信度阈值与人工复核的配合展示层如果只显示“预测对了”就没意思了我加了一个置信度阈值过滤置信度低于 0.6 的预测标记为“待复核”用不同颜色标注。实际用下来这个阈值附近集中了大部分模棱两可的样本人工扫一眼就能判断模型是错还是对比通篇浏览高效得多。这其实是一个很朴素的“人机协同”思路模型给概率人看不确定的部分。这里有个实操建议如果要做成可视化界面给非技术同事看别把 10 个类别的概率全堆上去只显示前三名加概率条就够了。信息过载反而让人抓不住重点。我自己用的时候就把输出打印成“真实外套 | 前 3 预测外套 0.81、套头衫 0.12、衬衫 0.04”的格式一眼就能看出模型是笃定还是犹豫。4.3 展示环节的常见坑第一个坑是颜色映射选错。灰度图用默认的彩色映射会画出诡异的荧光色用cmapgray才是对的还要记得vmin0, vmax1不然对比度会被自动拉伸看起来糊。第二个坑是子图布局挤在一起。批量展示 25 张图时标题文字很容易重叠解决办法是设置合适的figsize比如 12×12再用tight_layout()自动调整间距。第三个坑是推理时忘了补 batch 维度。单张图的形状是 28×28×1模型要的是 1×28×28×1直接喂进去会报形状不匹配。我在代码里固定写成img[np.newaxis, ...]避免每次都要想一遍。5. 常见问题排查与实战避坑清单5.1 准确率上不去先按这个顺序查现象可能原因排查动作训练准确率也低欠拟合加轮数、加通道数、调大学习率训练高、验证低过拟合加 Dropout、加增强、减容量两者都高但测试低数据泄漏检查是否用测试集调过参损失变成 NaN学习率过大或输入异常降到 1e-4打印输入范围准确率卡在 10%标签与损失函数不匹配核对整数标签与损失类型推理结果全是一类归一化不一致或维度错单张图跑一遍逐层看输出形状这个表我贴在项目目录的说明文件里遇到问题先照着过一遍能解决八成情况。最典型的一次是我把标签和损失函数配错了训练损失一直在 2.3 附近晃准确率稳在 10%查了半小时代码才发现问题回头看表格第一分钟就能定位。5.2 复现性与可维护性的实操心得做自用项目最容易犯的毛病是“跑通了就完事”过两周想改点东西发现自己都不知道当初改了什么。我的做法是把配置全部抽到一个字典里包括学习率、批大小、轮数、增强参数、随机种子训练时打印一份配置快照结果文件按时间戳命名。这个习惯看起来啰嗦但你只要经历过一次“莫名其妙的 90% 和同样代码跑出来的 88%”就会明白固定随机种子和记录配置有多重要。还有个小技巧训练完把模型权重、混淆矩阵图、错分样本清单一起存进一个以时间命名的文件夹下次想对比实验直接翻文件夹不用重跑。我靠这个办法在一个周末里对比了七组配置最终确定的那套参数就是从这些记录里挑出来的。5.3 达到 90% 之后还能做什么准确率过了 90%这个项目其实还有延展空间。我给自己的下一步是把模型导出成轻量格式做一个本地小工具给定一张图片路径就输出类别和置信度再往深一点可以试试把灰度图换成彩色数据集或者加上类激活图看看模型到底盯着衣服的哪个部位做判断——这个分析对理解卷积网络特别有帮助很多时候你会发现模型关注的位置和你以为的不一样。不过话说回来这个项目最大的价值不在于那 91% 的数字而在于它逼着我把数据清洗、归一化一致性、验证集划分、可视化排查这几件事从头走了一遍每一环都踩过坑、补过漏。后面我换任何数据集做分类用的都是同一套流程骨架只是换掉数据加载和类别名称。这套骨架跑顺了下一个项目开工时你会发现真正耗时间的从来不是写模型那二十行代码而是那些看似琐碎、却决定结果可信度的准备工作。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑