本节摘要:损失函数把「猜得有多离谱」变成一个标量,优化器按该标量对参数的梯度改权重。多分类且标签为整数时,Keras 原文用
sparse_categorical_crossentropy,PyTorch 原文用nn.CrossEntropyLoss。优化器两侧都示范 Adam:Kerascompile(optimizer='adam')常走默认学习率;PyTorchoptim.Adam(model.parameters(), lr=0.001)把学习率写在明处。对照时先对齐损失是否吃 logits、标签是否 one-hot,再谈谁收敛快。
阅读完本节,你应当能够:
sparse_categorical_crossentropy 与 CrossEntropyLoss 的输入约定准确率好懂:一百张里对了多少。但它不可微,对「差一点就分对」和「错得离谱」的惩罚不平滑,不适合直接当反向传播的目标。交叉熵在类别概率(或 logits)上提供平滑的负对数似然:正确类的分数越高,损失越低。原文把损失与优化器并列为「学习」的两块,评估指标另算——compile 里的 metrics=['accuracy'] 只监控,不替代 loss。
标签编码决定损失名字。Fashion MNIST 的标签是 0 到 9 的整数,不是十维 one-hot。Keras 因此选 sparse_categorical_crossentropy:内部会把整数当成类别索引。若你先把标签做成 one-hot,就要改用 categorical_crossentropy。PyTorch 的 CrossEntropyLoss 期望类别索引(Long 型),同样不是 one-hot。把整数标签误当成回归目标去配 MSELoss,网络会尝试把输出拟合成 0 到 9 的数值,十类问题会训歪。
回归用均方误差一类,输出层线性激活。二分类可用二元交叉熵,输出一个 Sigmoid 概率。这些在原文 3.5 预测解释里出现过:Sigmoid 输出用 0.5 阈值;Softmax 用 argmax;回归直接取数值。对照表要按任务锁死,不要从上一份笔记本复制损失名字。
💡 关键直觉:损失必须和「输出层激活 + 标签编码」成套购买。拆开重组是对照事故的主要来源。
Keras 把损失、优化器、指标绑在 compile 上,之后 fit / evaluate 都认这一套。字符串 'adam'、'sparse_categorical_crossentropy'、'accuracy' 是入门写法;也可以传入优化器对象以改学习率。PyTorch 没有 compile:损失是一个可调用模块 criterion = nn.CrossEntropyLoss(),优化器是另一个对象 optim.Adam(model.parameters(), lr=0.001),你在循环里自己 loss = criterion(outputs, labels)。参数必须显式交给优化器——漏掉某个子模块的 parameters(),那一部分权重永远不更新,准确率会莫名其妙地封顶。
原文 PyTorch 学习率写 0.001,并注释可以尝试不同值。Keras 字符串 'adam' 使用该优化器实现里的默认学习率,通常也是 1e-3 量级,但不把数字写在你的代码里。对照实验若要公平,应两侧都显式构造 Adam 并设同一学习率。否则你比较的是「默认超参 + 框架」,不是框架本身。
| 项目 | Keras 入门 | PyTorch 入门 | 对齐时注意 |
|---|---|---|---|
| 多分类整数标签 | sparse_categorical_crossentropy |
nn.CrossEntropyLoss |
都不要先 Softmax 两次 |
| 输出无激活 | from_logits=True |
默认吃 logits | SOURCE 3.5 写明 |
| 优化器 | compile(optimizer='adam') |
Adam(..., lr=0.001) |
显式 lr 才能公平 |
| 指标 | metrics=['accuracy'] |
手写正确个数 / 总数 | PT 评估循环自己除 |
| 参数来源 | 模型已注册的可训练变量 | model.parameters() |
漏注册等于不学习 |
SGD 带动量、RMSprop、Adam 原文都列为常用。Adam 自适应各参数的学习率,入门少调也能动,所以两侧示例都选它。它不是永远最好:有的视觉论文仍爱 SGD 动量,认为泛化更好。Fashion MNIST 小实验上,换优化器的差异往往小于换预处理或换卷积。先把损失约定对齐,再谈优化器竞赛。
学习率太大,损失出现 NaN 或震荡;太小,十条 epoch 曲线几乎平。Keras 可用回调做衰减;PyTorch 用 lr_scheduler。原文入门没有调度器,10 个 epoch 固定 lr。对照课尊重这个范围:第 5.3 节才允许你碰调度与正则,避免第 2 章变成调参手册。
loss.item()(PT)与 history.history['loss'](Keras)是记录曲线的入口。打印频率原文 PT 示例为每 100 个批次。Keras fit 默认每个 epoch 打一行,也可调 verbose。不要用训练损失单独下结论,验证损失才是过拟合探针。
类别不平衡时,准确率会骗人:全猜多数类也能很高。原文任务十类大致均衡,准确率可用。换成长尾数据要上混淆矩阵或各类 F1,那是 5.3 的话题。损失侧可用类别权重,Keras 与 PT 都能传入权重,入门先别加,以免把「框架差异」写成「权重字典写法差异」。
标签 dtype 在 PyTorch 上更挑剔:CrossEntropyLoss 要 Long,Float 会报错。Keras 对 NumPy 整数更宽容。从 TF 数据集误把标签转成 float 再喂 PT,会在损失那一行爆,而不是在 DataLoader。报错信息要读到「expected Long」为止,不要回头改模型宽度。
⚠️ 常见坑:Keras 使用
categorical_crossentropy却喂整数标签,或反过来。另一坑:把accuracy写进损失位置。监控指标不会推动Dense的权重。
优化器状态与权重是两套东西。Adam 内部有动量类缓冲。只保存权重、不保存优化器,再继续训练,等于让 Adam 失忆,前几个 epoch 行为会变。Keras model.save 默认整模型常含优化器状态;PT 推荐的 state_dict 默认只有层参数,要续训需另存 optimizer.state_dict()。这是第 4.4 节的伏笔,损失节先建立「优化器不是无状态的除法」。
from_logits 再强调一次。Keras 交叉熵默认假定输出已是概率(经过 Softmax)。输出层若是纯线性,必须 from_logits=True,否则数值不稳定且语义错误。PyTorch CrossEntropyLoss 默认假定 logits。两边各按各的默认写,能对上;混抄一行就会 silently 学坏。对照检查清单第三行就应该是:打印未训练模型的输出,看是「和为 1 的概率」还是「任意实数 logits」。
批次内损失归约默认多为对样本取平均。换成求和后,梯度规模随 batch_size 变,原文 PT 用 64、Keras 示例有时不写 batch 则另有默认。公平对照要同一 batch_size、同一归约。64 是原文 DataLoader 的数字,建议作为本教程默认批次,除非内存不够再降。
最后,损失为 NaN 的排查顺序:学习率、归一化是否把像素放大到 255 还当 0–1 用、是否出现 log(0)、标签是否越界到 10。越界在十类问题里会出现「索引 10 超出 10」这类报错,比 NaN 好查。两侧都会查,先查数据再查框架。
同一份损失若对 batch 求和而不是求平均,梯度会大约按 batch_size 放大。这时 0.001 的学习率不再和另一侧「平均归约 + 0.001」同义。Keras 交叉熵默认对样本平均;PT CrossEntropyLoss 默认也是 mean。不要把某一侧改成 sum 还声称公平对照。若你为了数值稳定改了归约,学习率要一起改,并写进实验卡片。
类别数与输出维必须相等。十类却写 Dense(9),PT 会在损失处因索引越界或 silently 忽略第 9 类而炸掉或学歪。Keras 稀疏交叉熵同样要求输出维覆盖所有标签值。检查 train_labels.max() 应为 9。若自己合并类别,输出维跟着改,不要只改数据集。
不一定。十类均衡时 accuracy 有意义;若你以后做「几乎全是负类的检测」,准确率会骗人,应换指标,损失仍可能是交叉熵。损失负责提供可反向的标量,指标负责向人报告。两者解耦是 2.4 的核心。Keras 把它们一起放进 compile 容易让人以为是一类东西。PT 手写循环则天然分开:criterion 一块,correct/total 一块。感到混淆时,用 PT 的分开写法去理解 Keras 的打包写法。
Adam 的 epsilon、beta 在入门不要动。原文只给 lr=0.001。动这些等于开始写第三套对照轴。SGD 动量若你想试,单独一次实验,学习率通常要调大一档,因为 Adam 的自适应与 SGD 的 0.001 不可比。对照框架时用同一优化器种类。对照优化器时用同一框架。两次实验目的不同,不要混在一张表里说「所以 PyTorch 更适合 Adam」。
sparse_categorical_crossentropy 这个长名字里的 sparse 就是「标签是整数」。把它念成「稀疏模型」是常见望文生义。模型可以很密,标签仍 sparse。categorical_crossentropy 配 one-hot。PT 没有这两个名字,统一 CrossEntropyLoss 吃整数。one-hot 在 PT 要用别的损失或自己把 one-hot 变回索引。入门保持整数标签,少一次转换。Keras 从整数改 one-hot 却忘了改损失名字,是 2.4 的经典事故,fit 可能仍运行,数字无意义。
权重衰减(L2)在 Keras 可写在层的 kernel_regularizer 或优化器;PT 写在 Adam 的 weight_decay(注意与真正的 AdamW 解耦衰减在更新版本里的区别,本课不展开版本故事)。第一轮过拟合优先 Dropout 与早停,衰减当第二轮。又是「一次一个旋钮」。
from_logits 才能吃线性输出;PT 交叉熵默认 logitsmodel.parameters() 漏子模块等于那部分冻结损失对照的验收是:同一批假 logits 和假整数标签,两侧算出的交叉熵应接近,前提是 Keras 侧不要双 Softmax、PyTorch 侧不要先 Softmax 再 CrossEntropy。数值差一个数量级,先查 from_logits 与 Softmax 次数,再查归约是 mean 还是 sum。Adam 两侧都显式 0.001 才能比收敛。accuracy 只监控。标签 max 必须是 9。优化器状态与权重分开记,续训要不要缓冲在 4.4 做选择。这里只要求你承认 Adam 不是无状态除法,不要在损失节开始存检查点。
假 logits 对照实验建议固定同一 NumPy 随机数组,分别转成两侧张量,标签同一组整数,比较损失标量。差在 1e-5 内可视为实现一致;差十倍去查 Softmax 次数。这比在 Fashion 上猜更干净。Adam 的其余超参保持默认。SGD 对照优化器时另开实验,不要和框架对照混表。sparse 一词指标签编码不是模型稀疏。权重衰减第二轮再用。指标不能反向传播。标签 dtype 在 PyTorch 上要 Long。批次归约两侧都 mean。把假损失实验留在笔记本,和二次函数求导实验并列,作为 2.2 与 2.4 的两道体检。
把假 logits 实验当成 2.4 出门证。同一数组两侧损失接近。差十倍查 Softmax 次数与 from_logits。Adam 显式 0.001。归约 mean。标签 Long 与整数。sparse 指标签。accuracy 不反向。SGD 另开表。出门证与二次函数求导并列。两证都过,才允许 compile 或 new CrossEntropyLoss 接到真模型。一证不过,不要用十个 epoch 去平均掉约定错误,平均不掉,只会得到一条自信的错曲线。
把假损失接近当成出门。出门后才 compile。Adam 显式同一学习率。mean 归约。整数标签。监控与优化拆开。SGD 另表。两证并列:梯度 6 与假损失接近。两证是 2.2 与 2.4 的双门。单门通过不能进第 5 章。双门通过仍可能在 Softmax 次数上翻车,所以 5.2 还要再查一次约定。查的是同一件事,不同阶段。
审查假损失实验的标签是否与输出维匹配。十类标签出现 10 会越界。越界比 NaN 好查。查完再比两侧标量。标量接近才能 compile。compile 不是体检的替代,是体检通过后的登记。登记了未体检的损失,fit 会自信地优化错误目标。自信是最贵的。贵在它不报错。
下一节把损失与梯度放进一次完整的前向—反向—更新,作为第 4 章训练循环对照的原理版。