AI-深度学习

从零搭建视觉模型 10:损失函数与优化器:怎样把错误变成更新

展开合集目录
  1. 从零搭建视觉模型 01:图片进入模型后发生了什么
  2. 从零搭建视觉模型 02:Python 起步,写给 C/C++ 开发者
  3. 从零搭建视觉模型 03:从像素到张量的基础概念
  4. 从零搭建视觉模型 04:图片如何变成张量
  5. 从零搭建视觉模型 05:MLP 入门,从手算到图片输入
  6. 从零搭建视觉模型 06:亲手搭建一个小型 CNN
  7. 从零搭建视觉模型 07:模型怎样预测类别:分数、概率与决策
  8. 从零搭建视觉模型 08:让模型开始学习,损失、梯度与训练循环
  9. 从零搭建视觉模型 09:数据集、标签与数据划分
  10. 从零搭建视觉模型 10:损失函数与优化器:怎样把错误变成更新
  11. 从零搭建视觉模型 11:批次、训练轮次与学习率
  12. 从零搭建视觉模型 12:训练第一个真实图片分类器
  13. 从零搭建视觉模型 13:过拟合、数据增强与正则化
  14. 从零搭建视觉模型 14:验证、测试与错误分析
  15. 从零搭建视觉模型 15:保存模型、恢复训练与独立推理
  16. 从零搭建视觉模型 · 综合实践:实现 MLP,从损失计算、训练到 ONNX 推理
  17. 从零搭建视觉模型 16:ResNet 与残差连接
  18. 从零搭建视觉模型 17:预训练、迁移学习与微调
  19. 从零搭建视觉模型 18:轻量模型与计算成本
  20. 从零搭建视觉模型 19:目标检测与 YOLO:从类别到目标框
  21. 从零搭建视觉模型 20:图像分割与 U-Net:给每个像素一个判断
  22. 从零搭建视觉模型 21:注意力与视觉 Transformer
  23. 从零搭建视觉模型 22:从训练模型到 ONNX
  24. 从零搭建视觉模型 23:量化与精度对比
  25. 从零搭建视觉模型 24:从摄像头输入到部署验收
  26. 从零搭建视觉模型 25:发展历史、主流分类与当前格局
从零搭建视觉模型 10:损失函数与优化器:怎样把错误变成更新的封面

学习初稿 · 第 10 课。 本篇已开放网页阅读,用于先建立完整认识。文中手算值和示意记录不代表真实训练成绩;真实数据训练、预训练权重和板端测试以本篇具体说明为准,本篇已附代码练习,实际验证范围见文末与包内记录。

一个预测错了,应该罚多少

准确率只记录对或错。真实类别是汽车时,模型给汽车 0.49 和 0.01 都可能被判错,但后者明显更偏离目标。训练需要一个能随输出连续变化的量来表达误差,这就是损失。

本课延续单标签分类,标签为整数类别编号,网络输出为原始 logits。损失用于指导优化;最终任务还要看准确率、误报漏报和实际错误样本,不能把损失数字直接当百分比。

交叉熵先从一个样本理解

对于一个正确类别,交叉熵可以理解为对其预测概率取负对数。正确类别概率越高,损失越低。例如概率 0.8 对应约 0.223,概率 0.2 对应约 1.609。这里的对数采用自然对数。

Softmax 与交叉熵在框架中通常一起稳定计算,因此单标签 CrossEntropyLoss 直接接 logits,不先手工做 Softmax。整数目标 [N] 表示每张图的类别索引,输出 [N,C] 则包含每张图对所有类别的分数。官方接口约定

以下小实验只需 PyTorch,不加载图片,也不会更新参数:

import torch
import torch.nn.functional as F

logits = torch.tensor([[2.0, 1.0]], requires_grad=True)
target = torch.tensor([1])
loss = F.cross_entropy(logits, target)
loss.backward()
print(round(loss.item(), 4))
print(logits.grad)
assert logits.grad[0, 0] > 0
assert logits.grad[0, 1] < 0

手算预期损失约为 1.3133。沿负梯度方向改变 logits,会降低错误类别的分数、提高正确类别的分数。实际训练时 logits 是网络计算结果,梯度还要通过计算图传回各层参数。

梯度不是新的参数值

梯度描述当前点附近,参数小幅变化时损失如何变化。backward() 计算并累积梯度,优化器的 step() 才根据梯度和更新规则改变参数。zero_grad() 清理旧梯度,避免无意把多次计算累积在一起。

用 L=(w-3)² 手算:当 w=1,梯度为 2(w-3)=-4。学习率 0.1 时,更新为 w=1-0.1×(-4)=1.4,损失从 4 降到 2.56。学习率如果过大,则可能越过低点并离得更远。

清理梯度 → 前向计算 → 计算损失 → 反向计算 → 优化器更新

需要梯度累积时可以有意不每次清理,但还要正确处理损失缩放与更新频率。初学阶段先保持每批一次清理与更新,确认基本闭环。

SGD、动量和 AdamW 的不同角色

最简单的 SGD 按当前梯度与学习率更新。带动量的版本还利用历史更新方向。Adam 类方法根据梯度的一阶、二阶统计调整更新,AdamW 将权重衰减与其自适应梯度更新解耦。

这些方法都不是“保证找到最好参数”的按钮。同一个学习率在不同优化器中含义和有效范围不能简单等价,改变优化器后应重新检查训练曲线,而不是只比较最后一次打印。

权重衰减控制参数的收缩倾向,学习率控制更新尺度,两者不能互相替代。把所有参数都用相同衰减方式是一个实现选择,较复杂模型可能会单独处理偏置与归一化参数。本课先固定简单配置,后面再做对照。

三类经常被混淆的问题

损失不下降,可能是没有调用更新、标签错位、输入尺度错误、学习率不合适,也可能只是任务更难。损失下降但验证恶化,可能是过拟合或划分存在问题。出现 NaN,则需要先检查输入、数值范围和梯度,而不是继续增加训练轮数。

调试时可先对少量固定样本反复训练,检查模型能否降低其训练误差。这是检查优化管线的工具,不能把这个结果报告成泛化性能。

练习

  1. 为什么训练前不需要把 logits 限制在 0~1?
  2. L=(w-3)²,从 w=1 用学习率 1.2 更新,结果如何?
  3. 有梯度但参数没有改变,首先检查哪一步?
参考答案

交叉熵接口接原始分数,并在内部稳定处理。第二题更新到 5.8,损失为 7.84,比原来的 4 更大。第三题先检查是否执行了 optimizer.step()、优化器是否包含目标参数、学习率是否为零,不能仅凭存在梯度就认定更新完成。

下一课把这些步骤放进 batch 与 epoch,计算一次实验到底进行了多少次更新。

综合实践

想把这些知识接成一套可运行的代码,可以进入实现 MLP:从损失计算、训练到 ONNX 推理。实践包含自生成数据、训练与验证、模型保存、独立推理、ONNX 导出和数值对照;遇到问题可按实践中的索引返回对应理论章节。

配套代码练习

实验:交叉熵梯度与一次 SGD 更新

查看完整代码:main.py
"""交叉熵梯度与一次 SGD 更新。运行方式与练习见 README.md。"""
import torch
import torch.nn.functional as F
torch.set_num_threads(2)
logits = torch.tensor([[2.,1.]], requires_grad=True)
target = torch.tensor([1])
loss = F.cross_entropy(logits, target)
manual = -torch.log_softmax(logits, dim=1)[0,1]
torch.testing.assert_close(loss, manual)
loss.backward()
expected_grad = logits.detach().softmax(1) - F.one_hot(target,2)
torch.testing.assert_close(logits.grad, expected_grad)
print('loss:', loss.item(), 'gradient:', logits.grad.tolist())
with torch.no_grad(): updated = logits - 0.1 * logits.grad
new_loss = F.cross_entropy(updated, target)
assert new_loss < loss
print('更新后的 loss:', new_loss.item())
下载代码

在项目根目录执行;下载包解压后,在解压目录执行 python3 main.py。

cd 'content/posts/AI-深度学习/从零搭建视觉模型/10-损失函数与优化器/code' && python3 main.py

本篇已附独立实验包,在代码卡片中查看源码、下载或复制运行命令。解压后执行 python3 main.py;依赖安装见包内 README.md。

观察目标: 标签为 1 时损失约 1.3133,梯度约 [0.7311,-0.7311],更新后损失下降。

动手修改: 把标签改为 0,先预测梯度符号再运行;把学习率改成 0,检查参数和损失。

所有输入均在本篇代码中构造;运行范围和验证记录见包内 verification.md。


完整阅读目录 · 上一篇 · 下一篇