0 · 复现目标与口径
1 · 环境与版本锁定
# 建议:Python 3.10+;首次缺包现场安装一次即可 try: import tensorflow as tf except ModuleNotFoundError: # %pip install tensorflow import tensorflow as tf tf.random.set_seed(42) # 锁定初始化与 shuffle,保证可比 print('TF:', tf.__version__) print('GPU:', tf.config.list_physical_devices('GPU') or '无(CPU 可跑)')
预期:TF 2.x 正常输出版本号;无 GPU 不阻塞,CPU 约 1–2 分钟跑完 5 epoch。若安装后仍报 GPU 相关警告,可忽略(自动回退 CPU)。
2 · 数据管线:形状与值域先行
fashion_mnist = tf.keras.datasets.fashion_mnist (train_images, train_labels), (valid_images, valid_labels) = fashion_mnist.load_data() # 关键修复:原始 uint8 0–255,必须重标度,否则损失初值过大、梯度爆炸 train_images = train_images / 255.0 valid_images = valid_images / 255.0 # 形状断言:跑通前先 assert,比事后查 log 快 assert train_images.shape == (60000, 28, 28) assert valid_images.shape == (10000, 28, 28) assert train_images.min() >= 0 and train_images.max() <= 1.0
| 检查点 | 正确值 | 错值含义 |
|---|---|---|
shape | (60000,28,28) / (10000,28,28) | 下载截断或版本错 |
max() | ≤1.0 | 忘记 /255,损失初值会到十几 |
train_labels.max() | 9(类别数 10) | 用此推导 Dense 输出维度,勿硬编码 |
红线:验证集不得参与任何拟合(包括归一化统计、调参窥视后回灌)。本课归一化系数为常数 255,无泄漏风险;若改用均值方差,必须只在训练集上估计。
3 · 模型定义:三层与 7850 的来源
number_of_classes = int(train_labels.max() + 1) # 10,由数据推导 model = tf.keras.Sequential([ tf.keras.layers.Input(shape=(28, 28)), # 新版推荐:Input 独立声明 tf.keras.layers.Flatten(), # (b,28,28)→(b,784),0 参数 tf.keras.layers.Dense(number_of_classes) # W:(784,10)+b:(10,)=7850 ]) model.summary() # 验收:Dense 7,850 / Total 7,850 (30.66 KB)
计算:784×10+10=7850。Flatten 只重塑;旧写法 Flatten(input_shape=...) 在新版 Keras 会告警,统一用 Input。
验收:
flatten: (None,784) 0,dense: (None,10) 7850。任一数字不对即结构错,无需继续训练。4 · 训练:编译与 fit 日志
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
history = model.fit(train_images, train_labels, epochs=5, verbose=2,
validation_data=(valid_images, valid_labels))
| 参数 | 本课取值 | 改动后果 |
|---|---|---|
from_logits | True(输出为 logits) | False 会对概率再 softmax,损失错误 |
optimizer | adam(默认 lr≈1e-3) | SGD 亦可收敛但更慢,需调 lr |
epochs | 5 | 1 则欠拟合约 75%;20 则验证损失大概率回升 |
validation_data | 验证集整体 | 仅评估,不产生梯度 |
正常日志形如:Epoch 1/5 - accuracy: 0.79 - loss: 0.59 - val_accuracy: 0.82 - val_loss: 0.50 → Epoch 5/5 - accuracy: 0.85 - loss: 0.41 - val_accuracy: 0.84 - val_loss: 0.45。若首 epoch loss > 5,优先检查是否忘记 /255。
5 · 评估与排错:曲线、预测形状、错例
# 曲线:两线对照,单看一条无意义 plt.plot(history.history['loss'], label='train'); plt.plot(history.history['val_loss'], label='val') plt.plot(history.history['accuracy'], label='train acc'); plt.plot(history.history['val_accuracy'], label='val acc') # 预测:必须带 batch 维;输出 (n,10),argmax 即类别 logits = model.predict(train_images[0:10]) # (10,10) preds = logits.argmax(axis=1) # 错例挖掘:前 500 验证样本找 4 个错的 import numpy as np p_all = model.predict(valid_images[:500], verbose=0).argmax(axis=1) wrong = np.where(p_all != valid_labels[:500])[0][:4]
| 现象 | 定位 | 修复 |
|---|---|---|
predict 报形状错 | 喂了 (28,28) | 改 [i:i+1] 或 expand_dims → (1,28,28) |
| 训练 loss 降、验证 loss 升 | 过拟合 | 本课 5 epoch 极少见;若出现检查是否重复 fit 累加 |
| loss 降、acc 不动 | 校准改善、边界未动 | 检查标签、阈值,而非一味加 epoch |
| 6↔0、4↔3 集中错 | 线性容量不足(预期内) | 非 bug;见下一节加层验证 |
6 · 扩展实验:64 单元隐藏层
# 独立模型,不污染原 model;对比 7850 vs 50890 model2 = tf.keras.Sequential([tf.keras.layers.Input(shape=(28,28)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dense(number_of_classes)]) model2.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=['accuracy']) model2.fit(train_images, train_labels, epochs=5, verbose=2, validation_data=(valid_images, valid_labels)) # 参数 (784*64+64)+(64*10+10)=50890;val_accuracy 约 0.86–0.87
变量:epochs=1 观察欠拟合幅度;Dense(64) 改 128 观察收益递减。每次只改一处并记录种子,否则不可比。
7 · 上线前清单
summary 为 7850;max()≤1.0;number_of_classes==10 由数据推导而非硬编码。Input 独立声明;from_logits=True 与无激活输出配对;验证集未参与拟合。5 epoch 日志落在 0.79→0.85 / 0.82→0.84 区间;首 epoch loss 若 >5 立即停查归一化。
predict 均带 batch 维;错例集中于易混类属预期;扩展实验单独建模。