上次跑完手写数字识别之后,我一度觉得图像分类也就那样:数据加载、归一化、搭个网络、看看准确率,流程熟得不能再熟。直到我把同样的套路原封不动搬到 Fashion-MNIST 上,才发现自己之前的很多“经验”其实只是数据集比较友好带来的错觉。这个数据集同样是 28×28 灰度图,同样是 10 分类,训练曲线的性格却完全不一样。
Fashion-MNIST 是服饰图片版的经典手写数字数据,包含 T 恤、裤子、套头衫、连衣裙、外套、凉鞋、衬衫、运动鞋、包和短靴这十类。对正在学机器学习或者深度学习的人而言,它是很难得的过渡数据集:既不像手写数字那样容易刷到虚高,又不会像真实场景图片那样难以收敛。这篇文章是实战系列第二篇,我不打算做从零普及,而是把数据准备、Baseline 搭建、CNN 设计、训练监控、数据增强和推理部署这几个环节里真正值得注意的坑,按我实际跑下来的顺序记录下来。如果你也准备用这个数据集做项目练手,照着这条链路走一遍,比单纯调高几个点的准确率有用得多。
1. 为什么第二篇实战选它:一个看似简单但没那么简单的替代数据集
1.1 灰度单通道的“简单”是假象
第一次接触 Fashion-MNIST 的人,第一反应大概率是“这不就是把手写数字换成了衣服图片吗”。确实,单通道灰度图让数据加载变得极其轻量,不需要处理复杂的颜色空间和背景干扰,一份标准的像素矩阵丢进模型就能开始训练。但真正跑起来之后你会发现,这个数据集在“简单”的外壳下面藏了不少麻烦。
手写数字分类,模型主要依赖笔画结构:一个圆圈加一条竖线是 9,上半圆加一竖是 6,形状本身的区分度非常高。而服饰图片不一样,很多类别的决定性信息是轮廓、纹理和局部部件组合。比如短靴和运动鞋,在 28×28 的分辨率下,鞋帮高度、鞋底厚度、鞋带区域这些细节对分类非常关键,但人眼往往都需要仔细分辨,模型自然更容易混淆。单纯从准确率指标看,Fashion-MNIST 上的全连接网络通常只能做到 90% 上下,比手写数字差了快 8 个百分点,刚开始我还以为是代码写错了,后来才意识到是任务本身的难度差别。
所以如果你想找一个“入门级但又不无脑”的数据集来练习真实建模流程,Fashion-MNIST 非常合适。它逼你去关注数据分布、模型结构、正则化手段和错误分析,而不是靠数据集本身的红利拿一个好看的数字。
1.2 类别语义从数字变成了“款式”,模型要学的东西变了
手写数字的类别语义是非常刚性的:2 就是 2,7 就是 7,人为主观因素很少。服饰类别则带有一层明显的“款式语义”。T 恤和衬衫之间,短袖和长袖可能只差一个袖口;连衣裙和外套之间,长度和开襟方式又存在交集。这种类别间的模糊边界,会导致模型在训练集上强行记住很多模式,却很难迁移到测试集。
我在训练过程中观察到一个很典型的现象:验证集损失已经降到很低,但验证集准确率卡在某个值附近波动,每次上下浮动零点几个百分点。最开始我怀疑是模型容量不够,后来把卷积核从 32 加到 128,准确率只提升了一点点,说明问题不是表达力不足,而是类别边界本身的重叠程度很高。换句话说,模型已经学到了能学到的内容,剩下的错误是大密度分布重叠导致的必然成本。
这个认知对后续所有调参决策都很重要。不要把 Fashion-MNIST 当作一个只要堆参数就能刷到 99% 的数据集,它的合理天花板是 95% 左右,再往上需要数据增强、模型集成甚至额外标注信息。理解这一点,你就能把更多精力放在正确的事情上,而不是跟准确率死磕。
2. 数据准备阶段最容易翻车的三个细节
2.1 归一化要按训练集的统计量,还是直接除以 255?
很多初学者会在数据加载这里直接写一行x_train / 255,然后觉得万事大吉。这个做法在 Fashion-MNIST 上完全够用,因为像素值范围是 0 到 255,除以 255 可以让数值落在 0 到 1 之间,网络收敛会更稳定。但如果你倾向于使用更通用的 z-score 归一化,也就是减去均值再除以标准差,那一定要记住一个原则:均值、标准差只能从训练集上计算,验证集和测试集必须使用同一组统计量。
我在给另一个模拟项目做图像预处理时见过这样的错误:有人对训练集和测试集分别计算各自的 mean 和 std,然后分别归一化。表面上看两组数据都变成了均值为 0、方差为 1 的分布,但实际上测试集的信息已经被偷偷用到了参数计算里,相当于数据泄漏。虽然 Fashion-MNIST 的像素分布比较稳定,这种操作对最终准确率影响可能不大,但它会形成非常坏的工程习惯。等以后遇到分布差异明显的真实业务数据,这个坑会直接放大成线上事故。最简单的做法是始终用训练集统计量归一化所有数据,或者干脆用x / 255这种无状态变换,从源头上杜绝泄漏风险。
2.2 训练集和验证集怎么切,随机种子要不要固定
Fashion-MNIST 官方会把训练集和测试集分开,但实际调参时你通常需要从训练集中再切出一部分做验证集。常见做法是随机选 5000 到 10000 张作为验证集,剩下的大部分用于训练。这里有两个细节需要注意。
第一,切分前最好先按类别做分层抽样。由于 Fashion-MNIST 的各个类别本身是均衡的,随机切分也不会产生严重的类别偏斜,但分层抽样是个更好习惯,尤其是以后遇到类别不均衡数据时。第二,一定要固定随机种子。我用一个随机种子切完数据之后,前后跑了四个不同模型,如果每次种子都不一样,你就没法判断准确率差异到底是模型结构变化造成的,还是数据划分变化造成的。固定种子的代价几乎为零,收益却非常大。
另外,你可能会看到有些人直接用官方测试集做验证,反复调整模型后得到一个高分。这种做法严格来说属于以测试集为基准进行调参,相当于把测试集当成了训练的一部分,最终报告的指标会偏乐观。更稳妥的方案是把官方训练集内部切出验证集,把官方测试集留到最后做一次性评估。实验次数越多,这种隔离就越重要。
2.3 标签和类别的对应关系:落地部署时最容易错的一环
Fashion-MNIST 的标签是 0 到 9 的整数,对应关系是:0 表示 T 恤或上衣,1 表示裤子,2 表示套头衫,3 表示连衣裙,4 表示外套,5 表示凉鞋,6 表示衬衫,7 表示运动鞋,8 表示包,9 表示短靴。很多人训练阶段不关心这个映射,反正模型输出一个整数,算准确率时直接和标签比对就行。但一旦要部署或做可视化,这个顺序就非常关键。
我见过有人训练完了,想把输出数字转成中文类别名,结果因为列表顺序写错,把运动鞋和短靴全部对调了。模型本身没任何问题,但业务方的反馈是“识别完全不准”。在工程代码里,建议把类别列表固化成常量,顺序严格与训练时一致,并且在可视化、日志打印和接口返回处复用同一份映射,不要在多处各自维护一份字符串数组。如果团队里有多个成员,最好把类别映射独立成一个文件,让所有人引用同一份配置。
3. 第一个 Baseline:从全连接网络开始,但不建议在这儿耗时间
3.1 全连接网络的最低表现水平
开始复杂模型之前,先跑通一个简单的全连接网络作为 Baseline 是个好习惯。它的作用不是追求高分,而是验证整条数据处理链路是否正确:数据加载有没有问题、标签对齐有没有搞错、损失函数和评估指标有没有配错。一旦这个最小系统能正常运行,后面换成卷积网络时就可以把注意力集中在模型结构上。
对于 Fashion-MNIST,一个很基础的三层全连接网络就能达到大约 88% 到 90% 的测试准确率。代码看起来不复杂,就是把 28×28 的像素展平成 784 维向量,接两个全连接层,最后输出 10 分类。如果你发现自己的全连接网络连 85% 都到不了,那大概率不是模型的问题,而是数据流水线某一步出了错,比如归一化忘记做了,或者验证集和测试集切反了。
model = Sequential([ Flatten(input_shape=(28, 28)), Dense(128, activation="relu"), Dropout(0.2), Dense(10, activation="softmax") ])这个结构相当轻,但已经具备了一个完整分类网络的所有要素。跑通它,等于把“数据端到端流动”这条链路验证了一次,接下来再折腾卷积都为时不晚。
3.2 激活函数、初始化和 BN 的叠加效果
全连接网络虽然简单,但内部的细节选择依然会影响结果。无脑堆三个 ReLU 层可能不如“两层 ReLU 加一个 Dropout”效果好。实践里我一般这样配置:隐藏层用 ReLU,不做额外的权重初始化花活,靠框架默认初始化就能训练得很好。真正明显的提升来自 BatchNorm 和 Dropout。
给全连接网络加上 BatchNorm 之后,Fashion-MNIST 的准确率可以从 88% 左右拉到 91% 上下。原因很简单,批量归一化让每一层输入的分布稳定下来,缓解了梯度在深度网络中传递时的震荡。不过全连接网络的提升空间依然有限,因为它缺乏对图像局部结构的利用。这就像你把一页书剪成几百个碎片再随机排列,人类也几乎不可能拼回原样;全连接网络面对的就是这种被打乱的像素关系。
3.3 为什么全连接网络调参天花板很低
在全连接网络上花太多时间调参,是我见过的最常见的资源浪费。你可以试不同的隐藏层宽度、激活函数、学习率、Dropout 比例,最后发现模型准确率基本在 90% 左右游走,很难突破 93%。这不是调参者水平不够,而是模型结构本身的拟合偏好不适合图像问题。
全连接层把每个像素当作独立特征,没有“相邻像素更可能属于同一个物体部件”的先验。物体稍微平移几个像素,所有像素组合都变了,模型只能重新学习。卷积网络则通过滑动窗口共享权重,天然具备平移不变性和局部感知能力,更适合捕捉边缘、纹理和部件结构。所以在跑通全连接 Baseline 后,我的建议是很直接地说:别在这里刷分了,换个 CNN 再继续。
4. 换成卷积网络:结构设计是重点,不是堆层
4.1 一份可以直接跑的 CNN 结构
把全连接模型换成卷积网络后,Fashion-MNIST 的表现会有明显跃升。一个经典的微型 CNN 就能把准确率带到 93% 左右,结构是两层卷积加池化,再接一个全连接分类器:
model = Sequential([ Conv2D(32, (3, 3), activation="relu", input_shape=(28, 28, 1)), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activation="relu"), MaxPooling2D((2, 2)), Flatten(), Dense(128, activation="relu"), Dropout(0.3), Dense(10, activation="softmax") ])训练时用 Adam 优化器,默认学习率 1e-3,批次大小 128,跑 20 个 epoch,通常验证集准确率能到 93% 附近。如果再加上数据增强,可以继续推到 94% 到 95%。这个结构没有什么神秘之处,但它在小尺寸灰度图上表现稳定,参数数量也不大,单卡 CPU 都能在合理时间内训练完。
有一点需要注意:输入形状是(28, 28, 1),多出的最后一维表示通道数。如果你使用现成数据集加载函数拿到的是(28, 28),训练时第一步很可能报维度不匹配的错误。这不是模型的问题,而是数据格式需要调整。
4.2 池化和步长的取舍
很多人在设计 CNN 时会纠结到底用 MaxPooling 还是卷积层步长为 2 来下采样。两种方式都能降低特征图尺寸,但我个人在 Fashion-MNIST 这种小尺寸图像上更推荐 MaxPooling。原因很简单:3×3 卷积加 2×2 最大池化是一种久经考验的组合,每经过一次池化,尺寸从 28 降到 14,再降到 7,特征图虽然变小,但空间信息仍保留得很充分。池化本身没有参数,不容易过拟合,也省去调整步长带来的一些副作用。
如果非要踩坑比较一下,你可以试试在第一层用步长为 2 的 3×3 卷积来替代池化,参数量会多一点,准确率不会有明显变化。对于入门项目,我不建议在这里做过于复杂的实验,先选一个稳妥方案跑出一个基准结果,再考虑结构变体。
4.3 加几层卷积才算“够用”
另一个常见问题是:CNN 是不是层数越多越好?对 Fashion-MNIST 这种 28×28 的小图,答案明显是否定的。单层卷积已经能做基础边缘检测,两层卷积可以组合出纹理和局部部件,三层卷积基本就是在更大视野里做语义组合了。如果再继续加深,特征图经过三轮 2×2 池化会从 28 缩到 3,几乎丢失所有空间信息,后面的卷积层只是在处理一个 3×3 的小块,意义不大。
因此我的建议是两到三个卷积块足够。与其盲目堆层,不如在每一层的卷积核数量、Dropout 比例和训练策略上做文章。很多公开的高分方案会使用预先在更大数据集上预训练的骨干网络,但对于 28×28 的灰度服饰图,这样做收益有限,反而会把简单问题复杂化。先把基础 CNN 练熟,对“什么样的任务匹配什么样的网络深度”建立感觉,比抄一个复杂结构更有价值。
5. 训练过程监控:准确率没涨,先别急着加数据
5.1 loss 曲线和 accuracy 曲线要分开看
我在这个项目上得到的第一个教训是:不要只看准确率曲线。准确率是一个比较粗糙的指标,它对类别均衡的数据有效,但无法反映模型在每个样本上的置信度变化。训练早期准确率可能还徘徊在 70%,但 loss 已经稳步下降,这是正常的,因为模型正在逐渐把高置信度分给正确类别。如果 loss 下降而准确率几乎不动,可能是类别之间存在系统性的混淆,比如模型始终把某些类别当成另一类的更大概率。
反过来,如果训练后期 loss 还在降但验证准确率停滞,甚至出现准确率小幅下降,那通常意味着模型进入了过拟合区。此时加数据确实可能有效果,但前提是你能先判断它真的过拟合了。否则盲目加数据只会增加训练时间,解决不了优化器或学习率的问题。
5.2 学习率热启动和衰减的作用
Fashion-MNIST 对优化器的要求不高,Adam 配默认学习率就够。但如果你想稳定地把模型训到收敛,建议加一个验证集损失监控的学习率衰减。一个典型的做法是在验证 loss 连续几个 epoch 不再改善时,把学习率乘以 0.5,然后继续训练。这套机制在很多框架里都有现成回调,不需要自己写。
callbacks = [ ReduceLROnPlateau(monitor="val_loss", factor=0.5, patience=3, min_lr=1e-5) ]加入这个回调之后,模型通常会在接近收敛时变得更加稳定,验证集准确率会少一些抖动。不过要提醒一句:不要在模型完全没训练好时就依赖调度器,学习率的初始值还是要设对。Adam 默认的 1e-3 在大多数情况下没问题,如果你发现训练 loss 一开始就在震荡,可以把学习率降到 3e-4 或 1e-4 再试。
5.3 关于过拟合的误判:验证集稳定但测试集掉点
实验做到后期,极容易遇到一种尴尬情况:验证集准确率很好看,与训练集差距也不大,但你最后在测试集上一跑,分数忽然掉了 1 到 2 个百分点。很多人第一反应是测试集有问题,或者模型泛化能力差。但我的排查经验是,先确认预处理流程在测试阶段没有变化。
比如训练时你对像素除以 255,测试时写代码的人可能忘了一行;或者训练时输入形状是(28, 28, 1),测试时却传入了(28, 28)。这些错误会让模型输出结果整体偏移,准确率自然下降。另一个潜在原因是数据划分时所用的随机种子没有固定,导致验证集和测试集之间出现了微小分布偏差。Fashion-MNIST 的类别是均衡的,这种偏差影响不大,但在更复杂的数据集里会被放大。每次实验记录随机种子,看起来是个小事,关键时刻能帮你排除很多假问题。
6. 数据增强的边界:哪些操作真的适合服饰图片
6.1 上下翻转、旋转、平移的适用性分析
数据增强几乎是图像分类的标配,但很多教程会在 Fashion-MNIST 上直接照搬 CIFAR-10 里的一整套增强流程,这其实是有问题的。CIFAR-10 里面的飞机、汽车、猫狗大多没有严格的“上方”和“下方”语义,水平翻转和上下翻转基本无害。服饰图片则不一样,T 恤、衬衫、连衣裙一旦上下翻转,领口到了下边,衣服看起来非常违反直觉,模型学到的东西在真实场景里完全没有意义。
我的建议是把增强操作分成三类:可用、慎用、不用。水平翻转对大多数对称服装类别可用,但如果衣服上有明显文字或 logo,翻转会制造不真实样本。平移和小角度旋转可用,范围控制在几个像素或 ±10 度以内。上下翻转则直接不用。做增强之前,先人为看一眼增强后的图片,如果连你自己都判断不了这是什么类别,那就别指望模型能学出好的表征。
6.2 增强强度过大反而伤害特征的案例
我在实验里曾经把旋转范围放到 ±30 度,配合比较大的平移,目的很简单:想用增强提高鲁棒性。结果训练出来的模型在验证集上准确率反而掉了约一个百分点。原因是 Fashion-MNIST 中的服饰图片大多是正面展示的,大角度旋转会扭曲鞋底、领口、袖口等关键判别区域,让模型被迫去拟合一些不真实的形状变体。
这不是说增强不好,而是增强强度必须与数据的真实分布匹配。对于产品图风格的数据集,小的形变能模拟拍摄角度差异,大的形变则超出了实际可能出现的变化范围。增强是一种正则化手段,而正则化本身是有代价的——它会限制模型对训练样本的拟合程度。如果代价大于收益,效果就变差。
6.3 一个相对可靠的数据增强组合
基于多轮实验,我目前比较推荐在 Fashion-MNIST 上使用如下组合:随机水平翻转(概率 0.3 到 0.5)、小角度旋转(±8 度)、少量平移(宽度和高度各 ±2 像素)、轻微缩放(0.9 到 1.1 倍)。这个组合能在不明显破坏服饰结构的前提下增加样本多样性,训练出来的模型通常比无增强版本高 1 到 2 个百分点。
需要强调的是,增强只应该应用在训练集上,验证集和测试集必须保持原始的预处理流程。如果验证集也做了随机增强,你会得到一个波动很大且不可复现的评估结果。把所有增强步骤封装在训练数据流里,让验证和推理走另一条干净的数据管线,这是避免混乱的工程做法。
7. 错误分析:从混淆矩阵看到模型真正的问题
7.1 混淆矩阵里永远都会出现的“邻居”类别
准确率只能告诉你模型做得好不好,混淆矩阵才能告诉你哪些地方做得不好。Fashion-MNIST 的混淆矩阵里几乎必然会出现两个明显的错误簇:一个是 T 恤、衬衫、套头衫、连衣裙之间的互相混淆,一个是运动鞋、凉鞋、短靴之间的互相混淆。
这并不意味着模型失败,而更像是类别定义本身的模糊性。T 恤和衬衫在 28×28 分辨率下,很多时候就是袖口长度几个像素的差别;短靴和运动鞋的鞋底轮廓也很接近。在项目汇报里,如果能用混淆矩阵说明这些错误全都集中在语义相近的类别之间,结论会可信得多。相反,如果出现 包 被识别成 凉鞋 这种跨组别错误,那大概率是模型结构或数据有问题,值得深入排查。
7.2 抽样看错图:人眼和模型的判断依据差异
分析完混淆矩阵,我建议再抽样打印一批预测错误的样本,按真实标签、预测标签和各类别置信度排列成表格。很多错误样本在你眼里可能也是模糊的:有的短靴高度很低,看起来接近凉鞋;有的衬衫领口特征不明显,更像 T 恤。人眼和模型在这些样本上都会犯错,说明错误主要来自标注歧义,而不是模型能力不足。
但如果你发现模型把一个带有明显鞋带的运动鞋预测成短靴,就需要去看看预处理是否丢失了关键细节。一个常见的例子是数据归一化不当导致图像对比度下降,鞋带区域变成一片灰。此时调整预处理流程往往比调网络结构更有效。
7.3 类别优化建议:增加特定类别的权重还是增加样本
Fashion-MNIST 类别总体均衡,不需要做重采样,但如果你自己构造了一个不平衡的服饰数据集,问题就会复杂一些。最简单的做法是给样本较少的类别提高损失权重,让模型在梯度更新时更重视这些类。另一个更有效的思路是从错误分析中提取规则,比如把明显属于同一语义组的类别合并成一个粗粒度标签,先训练一个粗分类器,再在组内做细分类。这种层级式思路在很多真实项目里比单纯堆模型要好用得多。
在 Fashion-MNIST 上,9 类短靴和 5 类凉鞋容易混淆,你不可能通过修改权重彻底解决,因为信息量不够。这时就该接受当前分辨率的边界,把目标从“提高准确率”调整为“提高 top-k 准确率”。如果真实标签总是出现在预测概率前三名里,说明模型的知识只是不够锐利,而不是完全错误。
8. 从训练到推理:识别“看起来一样”的部署问题
8.1 推理时的预处理必须和训练严格一致
训练结束之后,很多人会直接在测试集上 evaluate 一下,看到准确率不错就直接收工。但真正把模型接到应用里时,问题往往出现在“测试集数据没问题、用户传来的图片有问题”这一步。用于训练和评估的测试集图片,已经经过了固定尺寸裁剪、归一化和通道顺序整理;而实际应用里的一张手机照片可能是任意尺寸、任意光照、任意背景。
我在一个小 demo 里试过直接把一张 300×300 的图片缩放到 28×28,然后忘了除以 255,模型输出的置信度乱七八糟。后来我把预处理封装成一个独立函数,所有入口都走同一个函数,问题就消失了。这个函数里的操作包括:统一图片尺寸、转灰度图、数值归一化、reshape 成网络期望的输入形状。
def preprocess_image(img): img = img.resize((28, 28)) arr = np.array(img, dtype="float32") / 255.0 return arr.reshape(1, 28, 28, 1)训练和推理共用这一个函数,能省掉大量“本地能跑、部署就不行”的麻烦。
8.2 模型导出后的输入形状和数值范围
很多框架导出模型时会把输入形状固定下来,比如(None, 28, 28, 1)。这不只是给人看的说明,实际推理引擎会严格按照这个形状来校验输入。如果你在部署端传入了没有通道维度的数组,引擎要么报错,要么在你没注意的地方做隐式广播,最后得到毫无意义的结果。
还需要留意数值范围。训练时像素被归一化到 0 到 1 之间,模型学到的权重就是针对这个范围设计的。部署端如果直接喂入 0 到 255 的原始像素,即使模型不报错,输出也会整体偏移。排查这类问题最快的办法是找一张训练样本,分别用训练时的预处理和部署端的预处理跑一次,对比输出的 logits 是否一致。
8.3 二值化、JS 端推理等迁移场景的小提醒
如果要把模型迁移到浏览器或其他设备上,额外的坑会更多。比如浏览器里拿到的图片往往是 RGB 三通道彩色图,你需要先把它转成灰度图。很多人图省事直接调用现成的灰度转换接口,但不同库对 RGB 三通道的加权公式可能不同,有的是 0.299R + 0.587G + 0.114B,有的则简单取平均。细微的数值差异虽然不会让结果完全崩溃,但会对置信度产生扰动。
另一个需要注意的点是输出层。训练时用 softmax 做概率输出去计算交叉熵,部署时你可能希望直接拿 logits 做 top-k 排序,这样数值稳定性更好。但如果业务上要展示“置信度”,就必须使用 softmax。无论哪种方式,都要保证推理端的输出解析和训练时的评测逻辑对齐,否则你测出来的准确率和线上实际表现会不一致。
9. 最后补几个容易让人困惑的细节
9.1 随机种子和复现性
固定随机种子是负责任实验的基础,但它并不等于绝对复现。GPU 上很多算子是非确定性的,即使你固定了所有种子,重复运行两次也可能有微小差异。更有效的做法是固定数据集划分、模型初始化随机种子,同时在日志里记录环境版本、超参数和数据增强开关。这样即使结果不能逐位复现,也能在几次实验之间做公平对比。
9.2 指标打印里的隐藏问题
使用现成深度学习接口训练时,每个 epoch 打印的 loss 和准确率是训练集上的结果,不要把它当成模型最终表现。训练集准确率接近 100% 不代表验证集也能达标。另一个隐藏问题是,如果你在训练时传入了验证集,又在训练结束后用同一块验证集做多次调参,那么验证集的信息已经被间接用进模型选择里,最后再用测试集评估时分数才会相对可信。
9.3 后续可以做的方向
Fashion-MNIST 练完之后,下一步可以试试在更大尺度、更少预处理的数据集上做迁移学习,也可以尝试给现有 CNN 加上注意力机制、标签平滑、混合样本增强等技巧。但比较重要的不是这些技法本身,而是你通过这个项目形成的问题排查习惯:数据加工是否可靠、验证策略是否干净、错误分析是否深入。把这些内化成肌肉记忆,比在这个数据集上多刷一个百分点更有长期价值。
最后分享一个我在实际使用中发现的小技巧:每次实验结束后,不要只记录准确率,顺手把 confusion matrix 和几张错误样本的预测概率都存档。后续调整模型时,这些历史记录能帮你快速判断改动到底解决了哪些错误、又引入了哪些新错误。有了这套记录习惯,下一次遇到新的图像分类项目,你就能少走很多弯路。