首页 / 资讯中心 / 文章详情

Fashion-MNIST图像分类实战:神经网络代码全流程解析

Fashion-MNIST图像分类实战:神经网络代码全流程解析 ★ FEATURED ARTICLE
简介一套针对时装数据集的神经网络分类实现代码与配套说明文档面向深度学习初学者及需要完成图像分类课程作业的学生覆盖数据预处理、模型构建、训练评估到调参优化的完整流程。压缩包共2个文件包含一个可直接运行的Python脚本和一份Word说明文档脚本以卷积神经网络Conv2D、池化层结合全连接层的方式实现分类文档则对归一化、one-hot编码、交叉熵损失、Adam优化器等关键环节进行了讲解整体大小仅559KB。已有297人学习很适合对照代码逐步理解时装数据集中10类衣物图像的识别过程。借助压缩包内的代码和文档读者能够快速掌握加载并处理图像数据、搭建并编译模型、训练与评估模型的具体写法也可参考代码结构完成自己的作业或进一步尝试调整学习率、批量大小等超参数进行优化实验。1. 时装分类代码包到底解决了什么从一份作业代码看懂神经网络全流程拿到「神经网络实现fashion数据分类代码.zip」的时候多数人以为它就是一份交差的作业解压出来一个 homework6 文件夹、一个 main.py、一份 Word 文档。实际上这份资源把神经网络做图像分类的完整流程——加载数据、预处理、建模、训练、评估、调参——压缩进了一个能直接跑的最小闭环里。Fashion-MNIST 是 Zalando 做的服装图片数据集10 个类别、6 万张训练图、1 万张测试图每张 28×28 灰度比手写数字 MNIST 更难也更接近真实图像分类的难度。对刚接触深度学习的人来说把它跑通、改参数、看准确率变化比看十篇教程都管用。这份代码包适合三类人正在做图像分类作业的学生、想用真实数据集练手 TensorFlow 的初学者、以及需要一份基线代码快速验证想法的工程师。对已经跑过几个模型的人这份代码的参考价值在于结构规范数据预处理、模型定义、训练评估被分得很清楚你能直接替换成自己的数据集。核心算法是前馈神经网络和卷积神经网络的对比实现源码基于 TensorFlow/Keras 写成训练入口在 main.pyWord 文档则把每一行的意图解释了一遍。2. Fashion-MNIST 数据预处理reshape、归一化与 one-hot 编码的落地写法2.1 先看数据长什么样shape 与类别分布决定后面所有代码解压后先别急着跑 main.py第一步应该打开 Python 交互环境把这个数据集的实际结构打印出来。Fashion-MNIST 在 Keras 里自带加载函数不需要手动下载第一次运行会自动从网络获取之后缓存在本地。from tensorflow.keras.datasets import fashion_mnist (x_train, y_train), (x_test, y_test) fashion_mnist.load_data() print(x_train shape:, x_train.shape) # (60000, 28, 28) print(y_train shape:, y_train.shape) # (60000,) print(x_test shape:, x_test.shape) # (10000, 28, 28) print(类别取值范围:, y_train.min(), -, y_train.max())这里的输出值得逐行确认x_train是四维结构第一维 60000 是样本数后两维 28×28 是像素矩阵没有通道维。y_train是整数索引0 到 9 共 10 类分别对应 T 恤、裤子、套头衫、裙子、外套、凉鞋、衬衫、运动鞋、包、短靴。类别顺序有时候会记混建议打印出前几张图的标签和像素范围确认数据没加载错。import matplotlib.pyplot as plt print(像素范围:, x_train[0].min(), -, x_train[0].max()) plt.imshow(x_train[0], cmapgray) plt.title(flabel: {y_train[0]}) plt.show()如果打印出的像素范围是 0 到 255就说明还没归一化坦率说这一步是最容易被跳过的。另外注意x_train是三维数组直接塞进全连接层会报维度错误这也是后面要先 reshape 的原因。我一般会在加载完数据后立刻把 shape 写进注释里防止隔几天回来忘了维度。2.2 reshape 与归一化除以 255 不是玄学是收敛速度的分水岭神经网络对输入特征的尺度非常敏感。28×28 灰度图的像素值范围是 0 到 255如果原样输入大数值特征会在前向传播中主导梯度更新导致 loss 震荡甚至不收敛。归一化就是把每个像素除以 255把范围压到 0 到 1 之间。这一步看起来简单但跳过的后果在训练曲线上非常明显loss 下降慢、准确率长期在 80% 附近卡住。全连接网络的输入需要把 28×28 拉平成 784 维向量卷积网络则保留 28×28 并在末尾加一个通道维度# 全连接网络MLP版拉平成向量 x_train_flat x_train.reshape(-1, 784) / 255.0 x_test_flat x_test.reshape(-1, 784) / 255.0 # 卷积网络CNN版保留空间结构加灰度通道 x_train_cnn x_train.reshape(-1, 28, 28, 1) / 255.0 x_test_cnn x_test.reshape(-1, 28, 28, 1) / 255.0reshape(-1, 784)里的-1表示自动推断样本数784 是 28×28 的乘积。卷积版本最后加的 1 是灰度通道数如果是三通道彩色图这里就是 3。两个版本的数据要分开存因为后面模型接的输入维度完全不同。归一化统一用浮点除法注意 Python 3 里整数除法要用/而不是//否则 255 整除后全部变成 0。提示 像素值缩放到 0-1 区间是最常见的做法但也可以尝试标准化(x - mean) / std。对 Fashion-MNIST 这种图像数据集简单除以 255 已经足够数据量小、特征明显。 /提示2.3 标签编码为什么不能直接把 0-9 整数喂给模型分类任务的输出层用 softmax 输出 10 个概率值标签就需要变成 10 维的 one-hot 向量。比如类别 3 变成[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]。如果直接拿整数标签训练模型会认为类别之间有大小关系——5 比 3 大、9 比 7 大这完全误导了分类学习。from tensorflow.keras.utils import to_categorical y_train_oh to_categorical(y_train, num_classes10) y_test_oh to_categorical(y_test, num_classes10) print(原始标签:, y_train[0]) # 例如 9 print(one-hot标签:, y_train_oh[0]) # [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]to_categorical的num_classes参数必须和模型输出层神经元数量一致否则训练时会报维度不匹配。另外模型评估阶段计算准确率时model.predict返回的是 10 个概率值需要用np.argmax取最大概率的索引再和原始整数标签比较。这个 argmax 环节是初学者最容易漏的漏掉之后准确率算出来会非常难看原因是拿概率向量和整数标签做了逐元素比较。3. 模型构建从 784 维输入到 10 类输出前馈网络与卷积网络的搭配3.1 全连接基线一个隐藏层的前馈网络能跑到多少Fashion-MNIST 的难度比手写数字高不少MNIST 用全连接网络轻松到 98%Fashion-MNIST 同样的结构大概只在 88% 左右。但这不妨碍先用全连接网络搭一个基线它能验证数据预处理和训练流程是否正确。一个典型的前馈网络结构是 784 → 128 → 10隐藏层用 ReLU 激活输出层用 softmax。在 Keras 里用 Sequential 写这个网络每一层的神经元数量和激活函数是核心参数。隐藏层 128 是经验值太少学不到特征太多在小数据集上容易过拟合。不要一上来就堆 512、1024先把基线跑通再逐步加宽。from tensorflow.keras import Sequential from tensorflow.keras.layers import Dense, Dropout from tensorflow.keras.optimizers import Adam model_mlp Sequential([ Dense(128, activationrelu, input_shape(784,)), Dropout(0.3), Dense(10, activationsoftmax) ]) model_mlp.compile( optimizerAdam(learning_rate0.001), losscategorical_crossentropy, metrics[accuracy] ) model_mlp.summary()input_shape(784,)这里的逗号不能漏它表示输入是一个 784 维的一维向量。Dropout 层的作用是随机丢弃 30% 的神经元防止网络死记硬背训练集。learning_rate0.001是我常用的默认值如果 loss 不降或者降得太猛再调整到 0.0001 或 0.01。summary()会打印每一层的参数量全连接层的参数是输入维度 × 神经元数量 偏置第一层就是 784×128128约十万个参数量级在可控范围内。3.2 卷积特征提取Conv2D 与 MaxPooling2D 的每一维都是为什么CNN 比全连接网络强在参数共享和局部感受野。一张 28×28 的图像全连接第一层每个神经元要连 784 个输入而卷积核只用 3×39 个权重在图像上滑动参数少得多而且能捕捉到边缘、纹理这类局部特征。对服装这种有明显纹理和轮廓的图片卷积层的特征提取能力远超前馈网络。一个常用的 Fashion-MNIST CNN 结构是两层卷积加池化再接全连接层。我给出的是这份作业代码里最常见的搭建方式也是能稳定跑到 92% 以上的配置from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten model_cnn Sequential([ Conv2D(32, (3, 3), activationrelu, input_shape(28, 28, 1)), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activationrelu), MaxPooling2D((2, 2)), Flatten(), Dense(128, activationrelu), Dropout(0.5), Dense(10, activationsoftmax) ])Conv2D(32, (3, 3))的第一个参数 32 是卷积核数量也叫输出通道数可以理解为提取 32 种不同特征第二个参数是卷积核尺寸3×3 是最常用的5×5 感受野更大但参数更多。MaxPooling2D((2, 2))把 2×2 区域取最大值图像尺寸缩小一半同时保留最显著的特征。第一层池化后是 14×14第二层后是 7×7Flatten 把它拉平成 7×7×643136 维向量再接全连接层。这里有一个输入数据对齐的问题CNN 的input_shape(28, 28, 1)对应之前预处理的x_train_cnn如果误把x_train_flat喂进来第一层就会报维度错误。作业代码里通常会写清楚哪个变量喂给哪个模型但自己改的时候经常会把这两组数据搞混。3.3 输出层与损失函数softmax 和交叉熵是配对使用的输出层的Dense(10, activationsoftmax)是全篇代码的关键落点。softmax 把 10 个神经元的输出变成和为 1 的概率分布哪个类别的概率最大模型就预测哪个类。配合categorical_crossentropy损失函数梯度更新会更稳定。如果标签做成了 one-hot就用categorical_crossentropy如果标签还是整数用sparse_categorical_crossentropy也能训练但作业代码里大多是 one-hot 方案。这是一个容易踩的坑标签编码方式与损失函数不匹配时训练不报错但准确率异常因为 Keras 内部做了不同的比较方式。建议代码里写死 one-hot 编码损失函数固定用categorical_crossentropy排查问题也更方便。隐藏层的激活函数用 ReLU 而不是 sigmoid 或 tanh原因是 ReLU 的计算简单、梯度不会饱和深层网络也能稳定传播。卷积层里的 ReLU 还会把负值特征直接砍掉相当于做了一个隐式的特征筛选。提示 关于 CNN 的参数量可以对比全连接网络约 10 万参数CNN 只有约 5 万参数但准确率反而高 4 到 5 个点。这就是「参数共享 局部连接」带来的效果。 /提示4. 训练闭环损失函数、优化器与准确率评估的配合要点4.1 编译配置交叉熵搭配 Adam 的选型理由模型定义完compile是训练前最后一个关键步骤。损失函数选categorical_crossentropy优化器选 Adam评估指标选accuracy。Adam 的优点是自适应学习率它会在训练过程中根据梯度大小动态调整每个参数的学习步长相比 SGD 需要手动调整学习率和动量Adam 对新手更友好。from tensorflow.keras.optimizers import Adam model_cnn.compile( optimizerAdam(learning_rate0.001), losscategorical_crossentropy, metrics[accuracy] )learning_rate这个参数值得多说一句0.001 是 Adam 的默认值对大多数图像分类任务都够用。如果训练时 loss 出现剧烈震荡说明学习率偏大降到 0.0003 或 0.0001如果 loss 下降得极其缓慢可以试着提到 0.003。不要用太小的学习率硬撑着跑几十个 epoch浪费时间且收益很低。metrics[accuracy]会在训练过程中同步计算准确率Keras 会自动把预测结果和标签做argmax再比较。这里需要注意训练时的准确率是 one-hot 标签的匹配结果和我们手动用整数标签算出来的准确率在数值上是一致的但代码路径不同别混着用。4.2 fit 训练参数batch_size、epochs 和过拟合信号训练过程的参数直接影响模型的收敛速度和最终效果。batch_size每次喂给网络多少张图epochs跑几轮完整数据集validation_split从训练集里切出多少比例做验证。这三者的关系是batch 越小梯度更新越频繁训练越不稳定但可能跳出局部最优batch 越大训练越平滑但显存占用高。64 是经验平衡点。history model_cnn.fit( x_train_cnn, y_train_oh, batch_size64, epochs20, validation_split0.2, verbose1 )validation_split0.2表示把 60000 张训练图里的 12000 张拿出来当验证集不参与梯度更新。每次 epoch 结束后Keras 会在验证集上计算 loss 和准确率这个结果比训练集的准确率更能反映泛化能力。verbose1会打印进度条verbose0安静模式作业里推荐保持 1 方便观察。训练中最常见的信号是训练准确率持续上升但验证准确率开始下降这就是过拟合的典型曲线。遇到这种情况优先改 Dropout 的比例0.3 调到 0.5或者减小 epochs。不要只盯训练集准确率那个数字高得再漂亮也没有实际意义。Fashion-MNIST 的 CNN 模型在 8 到 12 个 epoch 之后验证准确率就趋于平稳20 个 epoch 足够观察到完整趋势。4.3 评估与持久化evaluate 的返回值对应什么训练完成后需要拿测试集做最终评估。测试集是模型没见过的 1 万张图它的准确率才是作业报告里真正要写的数字。evaluate返回两个值第一个是 loss第二个是 accuracytest_loss, test_acc model_cnn.evaluate(x_test_cnn, y_test_oh, verbose0) print(f测试集 loss: {test_loss:.4f}) print(f测试集准确率: {test_acc:.4f})如果这里打印出的准确率和训练时差不多说明模型泛化正常如果测试准确率比验证准确率低很多可能存在数据分布差异或者是训练集 shuffle 不够。verbose0不打印进度条适合批量评估时使用。训练完的模型也要保存否则下次跑还得重新训练。作业里常见的是把整个模型存成 h5 文件model_cnn.save(fashion_mnist_cnn.h5) # 重新加载 from tensorflow.keras.models import load_model loaded_model load_model(fashion_mnist_cnn.h5)保存格式上.h5是 Keras 默认格式把模型结构和权重打包在一起。加载后可以直接predict和evaluate不需要重新定义结构。这里建议给文件加上模型名字和准确率比如fashion_cnn_9215.h5避免后面实验多个模型时分辨不清。加载模型之后顺手用loaded_model.summary()确认结构没变这是一个值得养成的习惯。5. 避坑指南跑通 main.py 之前必看的五个排查点5.1 启动报错找不到 msvcp140.dll 或 TensorFlow 导入失败现象安装好 TensorFlow 后运行 main.py直接弹出找不到 msvcp140.dll或者提示ImportError: DLL load failed。原因TensorFlow 是编译型库依赖 Microsoft Visual C Redistributable 提供的运行时组件。系统缺失这个运行库时Python 无法加载 TensorFlow 的 C 扩展。操作系统重新安装或者纯净环境里最常见。解决去微软官网下载 VC Redistributable 最新版安装x64 版本必装。装完重启终端再跑一次。如果问题变成找不到cudart64_*.dll那是 GPU 版 TensorFlow 缺 CUDA 运行库把 CUDA Toolkit 装上即可。CPU 版 TensorFlow 一般不依赖 CUDA。5.2 维度错误input_shape 与数据 shape 对不上现象fit阶段报错ValueError: Input 0 of layer sequential is incompatible with the layer提示期望形状和实际形状不一致。原因预处理时生成了两个版本的数据x_train_flat是 (60000, 784)x_train_cnn是 (60000, 28, 28, 1)。可能是用 CNN 模型训练时误喂了x_train_flat或者全连接网络喂了带通道维的数据。解决检查模型定义里的input_shape和喂进去的数据。训练 CNN 用x_train_cnn训练 MLP 用x_train_flat。在fit前加一行打印print(x_train_cnn.shape)确认维度是四维再继续。还有一个容易忽略的点数据是 (60000, 28, 28) 但忘记加通道维直接 reshape 成 (60000, 28, 28) 而不是 (60000, 28, 28, 1)一样会报错。5.3 准确率一直在 10% 左右徘徊现象训练了很多轮准确率始终在 0.1 附近打转和随机猜测无异。原因最常见的是归一化没做像素值 0-255 直接进网络梯度更新异常其次是标签 one-hot 编码写错了比如num_classes设成了其他数值还有一种情况是模型最后没有softmax激活输出值不是概率分布。解决回到预处理代码确认像素值已经除以 255打印y_train_oh.shape确认是 (60000, 10)检查输出层是Dense(10, activationsoftmax)而不是linear。这三个检查点五分钟能查完如果都正常把学习率调到 0.0001 再试一轮。5.4 训练集 99% 测试集 85%过拟合的典型信号现象训练准确率接近 100%验证和测试准确率只有 85% 左右差值超过 10 个点。原因模型容量过大把训练集里的噪声和细节都背下来了碰到新数据就露馅。Fashion-MNIST 本身类别间有相似性比如衬衫和 T 恤模型在训练集上做出了过强拟合。解决增大 Dropout 比例从 0.3 调到 0.5 甚至 0.6减小隐藏层神经元数量比如 128 降到 64加入早停机制——在验证准确率连续 3 个 epoch 不提升时自动停止训练Keras 原生支持EarlyStopping回调。这套组合拳能把测试准确率从 85% 拉回到 90% 以上。from tensorflow.keras.callbacks import EarlyStopping early_stop EarlyStopping( monitorval_accuracy, patience3, restore_best_weightsTrue ) history model_cnn.fit( x_train_cnn, y_train_oh, batch_size64, epochs50, validation_split0.2, callbacks[early_stop], verbose1 )patience3表示连续 3 个 epoch 验证准确率不提升就停止restore_best_weightsTrue会把权重回滚到验证准确率最高的那一步避免保存过拟合后的权重。5.5 测试集上的分类结果和直觉不符现象模型整体准确率还行但查看具体样本时明明很像运动鞋的图被分成了短靴或者把包识别成了裙子。原因Fashion-MNIST 的类别之间存在真实的重叠比如短靴和运动鞋轮廓接近衬衫和 T 恤差别仅在袖子长度人眼都可能看错。模型没有额外的语义信息只能根据像素分布判断。解决看混淆矩阵找到哪些类别对经常被搞混再决定是否需要增加对应类别的训练数据、或者调整类别权重。这一步不是 bug是这个数据集本身的难点。后面的第六节会具体演示怎么定位这些问题。注意 排查问题的一个通用原则先确认数据正确再确认模型结构最后才调训练参数。顺序反过来的话经常会在模型写错的情况下白调半天学习率。 /注意6. 用混淆矩阵定位分类边界找出模型真正没学会的类别准确率只是一个数字模型到底在哪两类之间犹豫要靠混淆矩阵才能看清楚。Keras 的evaluate只给一个总分但作业和实际项目里分析具体错分才是调优的前提。import numpy as np from sklearn.metrics import confusion_matrix import matplotlib.pyplot as plt import seaborn as sns y_pred np.argmax(model_cnn.predict(x_test_cnn), axis1) class_names [T恤, 裤子, 套头衫, 裙子, 外套, 凉鞋, 衬衫, 运动鞋, 包, 短靴] cm confusion_matrix(y_test, y_pred) plt.figure(figsize(10, 8)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsclass_names, yticklabelsclass_names) plt.xlabel(预测类别) plt.ylabel(真实类别) plt.show()predict返回的是概率矩阵np.argmax(..., axis1)按行取最大值索引得到的就是每个样本的预测类别。混淆矩阵的行是真实标签列是预测标签对角线越深越好。观察非对角线的高亮位置如果「衬衫」这一行大量落在「T 恤」列上说明模型分不清这两类的袖长差异「短靴」和「运动鞋」互错说明鞋底轮廓特征学得不够。一个具体做法是让模型按置信度排序输出预测结果找到那些置信度高但预测错的样本——这些是模型的盲区比随机抽样更有分析价值。从那以后我每次跑完训练都强制走一遍混淆矩阵加错误样本可视化哪怕是时间紧也至少看一眼对角线这份代码包里的 main.py 结构完整跑通后把第五节和第六节的片段加进去就是一个比原作业更有参考价值的调优脚本希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站