简介:一套针对时装数据集的神经网络分类实现代码与配套说明文档,面向深度学习初学者及需要完成图像分类课程作业的学生,覆盖数据预处理、模型构建、训练评估到调参优化的完整流程。压缩包共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.py,Word 文档则把每一行的意图解释了一遍。
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], cmap="gray") plt.title(f"label: {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_classes=10) y_test_oh = to_categorical(y_test, num_classes=10) 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, activation='relu', input_shape=(784,)), Dropout(0.3), Dense(10, activation='softmax') ]) model_mlp.compile( optimizer=Adam(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'] ) model_mlp.summary()input_shape=(784,)这里的逗号不能漏,它表示输入是一个 784 维的一维向量。Dropout 层的作用是随机丢弃 30% 的神经元,防止网络死记硬背训练集。learning_rate=0.001是我常用的默认值,如果 loss 不降或者降得太猛,再调整到 0.0001 或 0.01。summary()会打印每一层的参数量,全连接层的参数是输入维度 × 神经元数量 + 偏置,第一层就是 784×128+128,约十万个参数,量级在可控范围内。
3.2 卷积特征提取:Conv2D 与 MaxPooling2D 的每一维都是为什么
CNN 比全连接网络强在参数共享和局部感受野。一张 28×28 的图像,全连接第一层每个神经元要连 784 个输入,而卷积核只用 3×3=9 个权重在图像上滑动,参数少得多,而且能捕捉到边缘、纹理这类局部特征。对服装这种有明显纹理和轮廓的图片,卷积层的特征提取能力远超前馈网络。
一个常用的 Fashion-MNIST CNN 结构是两层卷积加池化,再接全连接层。我给出的是这份作业代码里最常见的搭建方式,也是能稳定跑到 92% 以上的配置:
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten model_cnn = 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.5), Dense(10, activation='softmax') ])Conv2D(32, (3, 3))的第一个参数 32 是卷积核数量,也叫输出通道数,可以理解为提取 32 种不同特征;第二个参数是卷积核尺寸,3×3 是最常用的,5×5 感受野更大但参数更多。MaxPooling2D((2, 2))把 2×2 区域取最大值,图像尺寸缩小一半,同时保留最显著的特征。第一层池化后是 14×14,第二层后是 7×7,Flatten 把它拉平成 7×7×64=3136 维向量,再接全连接层。
这里有一个输入数据对齐的问题:CNN 的input_shape=(28, 28, 1)对应之前预处理的x_train_cnn,如果误把x_train_flat喂进来,第一层就会报维度错误。作业代码里通常会写清楚哪个变量喂给哪个模型,但自己改的时候经常会把这两组数据搞混。
3.3 输出层与损失函数:softmax 和交叉熵是配对使用的
输出层的Dense(10, activation='softmax')是全篇代码的关键落点。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( optimizer=Adam(learning_rate=0.001), loss='categorical_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_size=64, epochs=20, validation_split=0.2, verbose=1 )validation_split=0.2表示把 60000 张训练图里的 12000 张拿出来当验证集,不参与梯度更新。每次 epoch 结束后,Keras 会在验证集上计算 loss 和准确率,这个结果比训练集的准确率更能反映泛化能力。verbose=1会打印进度条,verbose=0安静模式,作业里推荐保持 1 方便观察。
训练中最常见的信号是:训练准确率持续上升但验证准确率开始下降,这就是过拟合的典型曲线。遇到这种情况,优先改 Dropout 的比例(0.3 调到 0.5),或者减小 epochs。不要只盯训练集准确率,那个数字高得再漂亮也没有实际意义。Fashion-MNIST 的 CNN 模型在 8 到 12 个 epoch 之后验证准确率就趋于平稳,20 个 epoch 足够观察到完整趋势。
4.3 评估与持久化:evaluate 的返回值对应什么
训练完成后需要拿测试集做最终评估。测试集是模型没见过的 1 万张图,它的准确率才是作业报告里真正要写的数字。evaluate返回两个值,第一个是 loss,第二个是 accuracy:
test_loss, test_acc = model_cnn.evaluate(x_test_cnn, y_test_oh, verbose=0) print(f"测试集 loss: {test_loss:.4f}") print(f"测试集准确率: {test_acc:.4f}")如果这里打印出的准确率和训练时差不多,说明模型泛化正常;如果测试准确率比验证准确率低很多,可能存在数据分布差异,或者是训练集 shuffle 不够。verbose=0不打印进度条,适合批量评估时使用。
训练完的模型也要保存,否则下次跑还得重新训练。作业里常见的是把整个模型存成 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, activation='softmax')而不是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( monitor='val_accuracy', patience=3, restore_best_weights=True ) history = model_cnn.fit( x_train_cnn, y_train_oh, batch_size=64, epochs=50, validation_split=0.2, callbacks=[early_stop], verbose=1 )patience=3表示连续 3 个 epoch 验证准确率不提升就停止;restore_best_weights=True会把权重回滚到验证准确率最高的那一步,避免保存过拟合后的权重。
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), axis=1) class_names = ['T恤', '裤子', '套头衫', '裙子', '外套', '凉鞋', '衬衫', '运动鞋', '包', '短靴'] cm = confusion_matrix(y_test, y_pred) plt.figure(figsize=(10, 8)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=class_names, yticklabels=class_names) plt.xlabel('预测类别') plt.ylabel('真实类别') plt.show()predict返回的是概率矩阵,np.argmax(..., axis=1)按行取最大值索引,得到的就是每个样本的预测类别。混淆矩阵的行是真实标签,列是预测标签,对角线越深越好。观察非对角线的高亮位置:如果「衬衫」这一行大量落在「T 恤」列上,说明模型分不清这两类的袖长差异;「短靴」和「运动鞋」互错说明鞋底轮廓特征学得不够。
一个具体做法是让模型按置信度排序输出预测结果,找到那些置信度高但预测错的样本——这些是模型的盲区,比随机抽样更有分析价值。从那以后,我每次跑完训练都强制走一遍混淆矩阵加错误样本可视化,哪怕是时间紧也至少看一眼对角线;这份代码包里的 main.py 结构完整,跑通后把第五节和第六节的片段加进去,就是一个比原作业更有参考价值的调优脚本,希望帮到你。
本文还有配套的精品资源,点击获取