Fashion-MNIST 图像分类实战:TensorFlow 五模型对比与 Flask 可视化
**本文源码+数据集:**通过网盘分享的文件:服装图像分类模型对比与可视化系统研究-相关资料.zip
链接: https://pan.baidu.com/s/1r_xZ41SJf2DYwOucrgnvpw 提取码: cp52
**本文实验结果:**在相同的 10 000 张 Fashion-MNIST 测试图像上,CNN+BN+Dropout 获得 90.60% 准确率和 0.9065 宏平均 F1;基础 CNN 以 41 866 个参数达到 90.48%。当前数据增强模型训练 60 轮后准确率为 86.70%,没有超过未增强模型。本文给出数据划分、模型结构、运行步骤与错误分析,说明这些数字如何得到,以及哪些结论仍需重复实验验证。
实验结果核对日期:2026 年 10 月 20 日。本文面向已掌握 Python 基础、希望完成图像分类课程设计或毕业设计的读者。
本次结果更新补齐了数据增强 CNN 的详细评估,并重新生成全部五模型评估与 17 张静态图表。数据来源为项目保存的模型、训练历史和评估 JSON,属于本项目单次训练记录,不是公开排行榜成绩。
1. Fashion-MNIST 五种模型的实际效果如何?
本项目中,CNN+BN+Dropout 的测试准确率最高,但基础 CNN 的参数规模更小。两者准确率仅相差 0.12 个百分点,相当于 10 000 张测试图像中正确样本数相差 12。没有多随机种子重复实验时,这个差值不足以证明前者具有稳定优势。
| 模型 | 测试准确率 | 测试损失 | 宏平均 F1 | 错误数 | 参数量 | 累计训练时间 |
|---|---|---|---|---|---|---|
| MLP 基线 | 88.37% | 0.4639 | 0.8829 | 1 163 | 235 146 | 122.2 秒 |
| 基础 CNN | 90.48% | 0.4192 | 0.9043 | 952 | 41 866 | 251.8 秒 |
| 三层 CNN | 90.23% | 0.3976 | 0.9010 | 977 | 26 826 | 202.5 秒 |
| CNN+BN+Dropout | 90.60% | 0.2746 | 0.9065 | 940 | 98 474 | 606.2 秒 |
| CNN+数据增强 | 86.70% | 0.3881 | 0.8665 | 1 330 | 98 474 | 2 075.6 秒 |
前四个模型训练 20 轮,增强模型训练 60 轮。训练时间取每轮训练与验证过程的累计耗时,不包含完整进程初始化、数据下载和图表生成。由于没有记录完整 CPU 型号、线程设置与后台负载,表中耗时只描述本次运行条件。

如果希望先建立可运行、开销较低的卷积基线,基础 CNN 值得优先复现;如果重点分析正则化配置的分类表现,可以进一步研究 CNN+BN+Dropout。部署选型还需要另外测试推理延迟和内存占用,训练耗时不能代替推理速度。
2. Fashion-MNIST 数据集怎么划分,如何避免测试集泄漏?
Fashion-MNIST 包含 70 000 张 28×28 灰度图像,分为 10 个服装类别,官方训练集和测试集分别为 60 000 张与 10 000 张。数据集定义见 Fashion-MNIST 原始论文。
本项目保留官方测试集,从原训练集末尾取 6 000 张作为验证集,其余 54 000 张参与梯度更新。验证集采用固定末尾划分,没有额外做类别分层抽样。
| 数据部分 | 图像数量 | 用途 |
|---|---|---|
| 训练集 | 54 000 | 更新模型参数 |
| 验证集 | 6 000 | 观察验证曲线与停止条件 |
| 测试集 | 10 000 | 评估已保存模型 |
数据预处理包括转为 float32、像素除以 255、增加单通道维度和标签独热编码。下面是与项目处理方式一致的核心代码,可独立检查输入形状:
python
import numpy as np
import tensorflow as tf
(x_train, y_train), (x_test, y_test) = (
tf.keras.datasets.fashion_mnist.load_data()
)
x_train = np.expand_dims(x_train.astype("float32") / 255.0, -1)
x_test = np.expand_dims(x_test.astype("float32") / 255.0, -1)
x_val, y_val = x_train[-6000:], y_train[-6000:]
x_train, y_train = x_train[:-6000], y_train[:-6000]
y_train_1h = tf.keras.utils.to_categorical(y_train, 10)
y_val_1h = tf.keras.utils.to_categorical(y_val, 10)
y_test_1h = tf.keras.utils.to_categorical(y_test, 10)
print(x_train.shape) # (54000, 28, 28, 1)
print(x_val.shape) # (6000, 28, 28, 1)
print(x_test.shape) # (10000, 28, 28, 1)
测试图像不参与模型训练。继续调参时,应依据验证集选择配置;如果反复根据测试结果修改模型,测试集就逐渐承担了模型选择的角色,最终分数可能出现选择偏差。
3. 五种神经网络分别采用什么结构?
五模型比较覆盖全连接基线、卷积深度变化、正则化配置与几何增强。模型名称中的"三层"指卷积层数量;原代码将该模型命名为 improved_cnn,名称不意味着结果必然更优。
| 模型标识 | 主体结构 | 分类头 |
|---|---|---|
mlp |
Flatten → Dense 256 → Dense 128 | Dense 10,Softmax |
basic_cnn |
Conv 32 → Pool → Conv 64 → Pool | Flatten → Dense 10 |
improved_cnn |
Conv 32 → Pool → Conv 32 → Pool → Conv 32 | Flatten → Dense 10 |
cnn_bn_dropout |
三个 Conv+BN+ReLU+Pool 块,通道 32/64/128 | Flatten → Dropout 0.5 → Dense 10 |
cnn_augment |
输入增强 → 相同的 32/64/128 卷积主体 | Flatten → Dropout 0.5 → Dense 10 |
3.1 基础 CNN 的 TensorFlow 实现
基础 CNN 先保留 28×28 空间尺寸,再通过池化降为 14×14。第二层使用 valid 卷积得到 12×12,再池化为 6×6。分类头接收 2 304 维特征,模型总参数量为 41 866。
python
from tensorflow.keras import layers, models
def build_basic_cnn():
return models.Sequential([
layers.Input(shape=(28, 28, 1)),
layers.Conv2D(32, (3, 3), padding="same"),
layers.ReLU(),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), padding="valid"),
layers.ReLU(),
layers.MaxPooling2D((2, 2)),
layers.Flatten(),
layers.Dense(10, activation="softmax"),
], name="basic_cnn")
model = build_basic_cnn()
model.compile(
optimizer="adam",
loss="categorical_crossentropy",
metrics=["accuracy"],
)
print(model.count_params()) # 41866
本文基础 CNN 的准确率比 MLP 高 2.11 个百分点,参数量则更少。这个结果支持当前卷积配置的有效性,但不能代表所有卷积模型都优于所有全连接模型。
3.2 Batch Normalization 与 Dropout 怎么组合?
CNN+BN+Dropout 使用三个无偏置卷积块,每个块依次执行卷积、批归一化、ReLU 和最大池化,末端使用 0.5 Dropout。
Batch Normalization 与 Dropout 的方法来源分别见 BN 原始论文和 Dropout 原始论文。本文采用这两种方法构建组合模型,不预设组合后必然提高分数。
python
from tensorflow.keras import layers, models
def build_cnn_bn_dropout():
net = models.Sequential([layers.Input(shape=(28, 28, 1))])
for filters, padding in [(32, "same"), (64, "same"), (128, "valid")]:
net.add(layers.Conv2D(filters, 3, padding=padding, use_bias=False))
net.add(layers.BatchNormalization())
net.add(layers.ReLU())
net.add(layers.MaxPooling2D(2))
net.add(layers.Flatten())
net.add(layers.Dropout(0.5))
net.add(layers.Dense(10, activation="softmax"))
return net
CNN+BN+Dropout 的 count_params() 为 98 474,包含批归一化的可训练参数与非训练状态参数。与基础 CNN 比较时,通道宽度、网络结构及正则化同时变化,因此 90.60% 的结果不能全部归因于 BN 或 Dropout 中的某一个模块。
4. 如何运行训练、评估和可视化流程?
项目将训练、评估、绘图和 Web 展示分成独立入口。重新生成实验报告时,只需运行评估和绘图,不需要重复训练已有模型。
text
Fashion-MNIST/
├── src/
│ ├── common.py
│ ├── models_def.py
│ ├── train.py
│ ├── evaluate.py
│ └── visualize.py
├── models/
├── outputs/
│ ├── results/
│ └── figures/
└── web/
├── app.py
├── templates/
└── static/
当前项目记录的环境为 Python 3.10.18、TensorFlow 2.21.0,使用 CPU 训练。核心依赖包括 NumPy、Matplotlib、Flask 和 TensorFlow。下面的命令面向已有项目源码的 Windows PowerShell 环境,依赖安装列表是运行示例,并非完整锁定环境:
powershell
python -m venv .venv
& .venv/Scripts/python.exe -m pip install tensorflow==2.21.0 numpy matplotlib pandas flask
# 训练五种模型
& .venv/Scripts/python.exe src/train.py
# 根据已保存模型生成详细评估
& .venv/Scripts/python.exe src/evaluate.py
# 根据评估结果和训练历史重新生成图表
& .venv/Scripts/python.exe src/visualize.py
# 启动本地仪表盘
& .venv/Scripts/python.exe web/app.py
浏览器打开 http://127.0.0.1:5006。独立复现本文分数还需要相同代码与依赖、数据划分和随机性设置;仅安装相同 TensorFlow 版本不能保证得到完全相同的数值。
4.1 训练结果会保存到哪里?
| 工件 | 文件位置 | 主要内容 |
|---|---|---|
| 模型 | models/<key>.h5 |
已训练网络 |
| 训练历史 | outputs/results/history_<key>.json |
每轮准确率、损失、耗时 |
| 详细评估 | outputs/results/eval_<key>.json |
混淆矩阵、分类指标、错误样本 |
| 汇总 | outputs/results/summary.json |
五模型总体指标 |
| 图表 | outputs/figures/ |
17 张 PNG |
训练入口发现同一模型的模型文件与历史文件均存在时,会跳过该模型。这属于"复用已有训练结果",不是自动恢复最近 epoch 的断点续训。
4.2 95% 是训练结果还是停止目标?
95% 是项目设置的验证准确率停止目标,本次五种模型均未达到。程序要求训练至少完成 5 轮,且验证准确率达到 95% 后才提前停止;实际运行中前四个模型完成 20 轮,增强模型完成 60 轮。
项目保存最终轮模型,不会自动恢复验证集最优轮的权重。CNN+BN+Dropout 最佳验证准确率为 92.18%,出现在第17轮;最终测试准确率 90.60% 来自训练结束保存的模型。两者来自不同数据集,也不应被写成同一轮模型的一组验证与测试结果。
5. 为什么 CNN 加数据增强后准确率反而下降?
本项目的数据增强 CNN 最终准确率为 86.70%,比未增强的相同卷积主体低 3.90 个百分点。这个结果只说明当前增强组合在本次设置下没有改善最终测试表现,不能证明数据增强对 Fashion-MNIST 普遍无效。
增强配置如下:
python
augmentation = models.Sequential([
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.08),
layers.RandomZoom(0.10),
layers.RandomTranslation(0.10, 0.10),
])
上述代码展示增强配置;实际项目将相同的增强层放在卷积主体之前。增强层在训练时执行随机变换,预测和评估时按默认推理行为关闭随机增强。
RandomRotation(0.08) 的 factor 按整周比例定义,对应最大约 ±28.8°,不是 ±0.08°。参数含义见 Keras RandomRotation 官方文档。在只有 28×28 像素的输入上,旋转插值、缩放和位移可能改变衣领、边缘等细节。
当前结果可提出三个待验证的解释:几何变换组合可能过强;增强与 0.5 Dropout 共同增加训练难度;末端 2×2 空间表示可能限制细节保留。项目尚未进行单项增强和 Dropout 消融,因此不能确认主要原因。
验证这些解释时,先固定网络与训练预算,再分别测试仅翻转、仅弱旋转、仅缩放、仅平移;之后逐步组合,并用验证集选择强度。增强模型与未增强模型当前训练轮数不同,也应另做等轮数或等耗时对照。
6. 模型主要把哪些服装类别识别错了?
CNN+BN+Dropout 的主要错误集中在T恤、套头衫、外套和衬衫之间。裤子、凉鞋、运动鞋、包和短靴的召回率均不低于 97.00%,而套头衫与衬衫的召回率分别为 72.50% 和 78.90%。
| 真实类别 | 预测类别 | 错误数量 |
|---|---|---|
| T恤 | 衬衫 | 145 |
| 套头衫 | 外套 | 139 |
| 套头衫 | 衬衫 | 112 |
| 衬衫 | 外套 | 86 |
| 衬衫 | T恤 | 81 |
前两个误判方向合计 284 个,占该模型全部 940 个错误的约 30.21%。因此,下一轮优化可以优先检查上装细粒度区分,而不是只盯着总体准确率。

混淆矩阵按"行是真实类别,列是预测类别"读取。外套召回率为 92.10%,精确率为 77.53%,说明真实外套较容易被找出,但部分其他上装也被错分为外套。精确率和召回率一起看,才能区分漏识与误报。

项目保存按测试索引顺序出现的前 50 个错误样本,图中显示前 16 个。这些图像用于具体诊断,没有经过随机抽样,不能代表全部错误的比例分布。
7. 为什么准确率之外还要看 F1 和置信度?
Fashion-MNIST 总体准确率反映全部测试样本中预测正确的比例;宏平均 F1 则让每个类别的精确率与召回率共同参与评价。CNN+BN+Dropout 的准确率为 90.60%,宏平均 F1 为 0.9065,但衬衫 F1 只有 0.7408,总分会掩盖具体类别的困难。
python
import numpy as np
def confusion_matrix_10(y_true, y_pred):
cm = np.zeros((10, 10), dtype=int)
for true, pred in zip(y_true, y_pred):
cm[int(true), int(pred)] += 1
return cm
def precision_recall_f1(cm, class_id):
tp = cm[class_id, class_id]
fp = cm[:, class_id].sum() - tp
fn = cm[class_id, :].sum() - tp
precision = tp / (tp + fp) if tp + fp else 0.0
recall = tp / (tp + fn) if tp + fn else 0.0
f1 = 2 * precision * recall / (precision + recall) if precision + recall else 0.0
return precision, recall, f1
CNN+BN+Dropout 对正确样本的平均最大预测概率为 0.9537,对错误样本为 0.7265。错误样本也可能得到较高评分,最大 Softmax 概率不能直接当作客观正确概率。
当前项目的置信度直方图只统计 0.5,1.0,更低置信度的样本不显示在柱状图中;平均置信度则对全部相应样本计算。若需要概率校准,应补充完整区间的可靠性曲线与校准误差,本文没有报告这些尚未执行的实验。
8. Flask 与 ECharts 仪表盘如何读取实验结果?
Flask 仪表盘读取评估程序写出的 JSON,通过接口向 ECharts 提供数据。训练与展示分离后,更新模型评估不需要修改前端中的实验数值。
| 接口 | 用途 |
|---|---|
/api/meta |
类别名称、模型信息与样本索引 |
/api/summary |
五模型总体指标 |
/api/history/<key> |
单模型训练曲线数据 |
/api/eval/<key> |
分类报告与混淆矩阵 |
/api/image/<index> |
测试图像 PNG |
以下代码是后端读取汇总文件的最小示例,不是完整仪表盘实现:
python
import json
from pathlib import Path
from flask import Flask, jsonify
app = Flask(__name__)
# 将本示例保存为 web/app_minimal.py 后,在项目根目录执行
RESULTS = Path(__file__).resolve().parents[1] / "outputs" / "results"
@app.get("/api/summary")
def summary():
with (RESULTS / "summary.json").open(encoding="utf-8") as file:
return jsonify(json.load(file))
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5006, debug=False)
当前系统支持浏览已有测试样本,不包含用户上传图片的在线预测接口。本地绑定 127.0.0.1 适合实验展示;公网部署还需要独立考虑输入校验、访问控制与生产服务配置。
9. 常见问题
Fashion-MNIST 本项目达到 95% 准确率了吗?
没有。本项目最高测试准确率为 90.60%,最高记录的验证准确率为 92.18%。95% 是验证集提前停止目标,不是已经实现的结果。
三层 CNN 一定比两层 CNN 好吗?
不一定。本项目三层 CNN 为 90.23%,基础两层 CNN 为 90.48%。两个配置的通道数和分类头维度也不同,现有实验不能将差异全部归因于卷积层数。
重新生成图表会重新训练吗?
不会。src/visualize.py 读取现有历史、评估和汇总文件,生成静态图表。模型发生变化后,应先执行 src/evaluate.py,再执行 src/visualize.py。
训练脚本打印完成就代表五个模型都成功了吗?
不一定。当前入口会捕获单模型异常,因此还应检查各模型文件和评估工件。本文更新时核验了五个混淆矩阵的样本总数均为 10 000,错误数与汇总一致,17 张 PNG 均可解码。
可以直接复制本文代码得到同样结果吗?
本文给出了数据处理、代表性模型与接口的核心片段,完整五模型实验还依赖项目的其余模块。随机种子、依赖版本及硬件执行差异也会影响结果。本文没有提供尚不存在的公开源码仓库链接。
10. 后续怎样改进这组实验?
下一步先完善实验协议,再比较新结构。为每个配置运行多个随机种子,记录均值和标准差;保存最佳验证检查点;统一增强对照的训练预算;补充推理耗时与完整环境记录。对上装类别分别统计混淆方向,能进一步判断总体分数变化是否来自真正困难类别的改善。
现有证据支持的结论是:本次运行中 CNN+BN+Dropout 的最终测试准确率最高,基础 CNN 具有较低参数与训练开销,当前增强组合未带来提升。模型稳定排名、增强退化的具体原因以及真实商品图像上的表现,都需要新的实验支持。
参考资料与实验数据说明
- Fashion-MNIST 原始论文:数据集定义与官方划分。
- Batch Normalization 原始论文:批归一化方法。
- Dropout 原始论文:随机屏蔽正则化方法。
- Keras RandomRotation 官方文档:旋转 factor 与训练阶段行为。
- 本文测试结果来自本项目
summary.json、五个eval_<key>.json和五个history_<key>.json,不是上述参考论文中的实验分数。