深度学习 2 — 分类
:::tip Kaggle 笔记本 本章的完整可执行代码在 Kaggle 上:打开 →
法语和英语版本可在首页查看。 :::
在上一章中,我们做了回归——预测一个数字。这里我们攻克分类——预测一个类别。本章的最大惊喜,也是它的美妙之处:从一个到另一个几乎不需要任何改变。梯度保持相同的形式。
为什么要学这一章?
您将看到:
- 逻辑神经元(线性回归 + sigmoid);
- 交叉熵的直观解释及从 Bernoulli 分布的推导;
- 梯度保持相同形式 的事实;
- PyTorch 陷阱:
BCELossvsBCEWithLogitsLossvsCrossEntropyLoss; - 多类分类:softmax +
CrossEntropyLoss。
从线性到逻辑
对于二元分类,目标是 。我们想要一个概率 ,所以输出在 中。
解决方案:对线性输出应用 sigmoid 函数。
将任意实数压缩到 。这就是逻辑神经元。
交叉熵作为"惊讶"
分类的损失是交叉熵:
直观理解:交叉熵衡量模型面对真相的**"惊讶程度"**。
- 如果 且 :没有惊讶,。
- 如果 且 :巨大惊讶,。
- 对 对称。
模型对正确答案越自信,损失越小。它对错误答案越自信,损失爆炸越厉害。正是这种不对称性推动模型学习经过校准的概率。
从 Bernoulli 推导
为什么是这个精确公式?它来自最大似然。如果将 建模为参数为 的 Bernoulli 变量:
观测的似然是乘积。取负对数(得到要最小化的函数),我们刚好落在交叉熵上。
梯度保持相同形式
这是本章的神奇时刻。对于线性神经元:
对于逻辑神经元,经过计算:
严格相同的公式。唯一的区别在于 的定义:
- 线性:
- 逻辑:
实际后果:将 LinearNeuron 转换为 LogisticNeuron,只需改变 forward() 来应用 sigmoid。其余一切保持不变。
PyTorch 版本
model = nn.Linear(m, 1) # 仅 logits,没有 Sigmoid
criterion = nn.BCEWithLogitsLoss() # 应用 sigmoid + 交叉熵
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
经典陷阱
PyTorch 提供了几种看起来相似但不等价的模型 + 损失组合:
| 如果损失是... | 模型必须输出... |
|---|---|
nn.BCELoss | 概率(末尾带 Sigmoid) |
nn.BCEWithLogitsLoss | logit(不带 Sigmoid) |
nn.CrossEntropyLoss(多类) | logits 向量(不带 Softmax) |
:::warning 陷阱 #1
绝对不要在模型中放 Sigmoid 同时使用 BCEWithLogitsLoss。Sigmoid 会被应用两次,模型不会学习。
:::
BCEWithLogitsLoss 是推荐的:在数值上比 Sigmoid + BCELoss 更稳定。
预测类别
训练后,从 logit 到类别:
model.eval()
with torch.no_grad():
logits = model(X_test_t)
proba = torch.sigmoid(logits) # logit → 概率
y_hat = (proba >= 0.5).float() # 概率 → 类别
多类:softmax + CrossEntropyLoss
对于 类,最后一层有 个神经元,产生 logits 向量:
sigmoid 的推广是 softmax:
所有概率都为正且和为 1。
多类交叉熵就是:
负号加上为真实类别预测的概率的对数。
model = nn.Sequential(
nn.Linear(m, 64),
nn.ReLU(),
nn.Linear(64, C), # 多类 logits
)
criterion = nn.CrossEntropyLoss() # 应用 LogSoftmax + 交叉熵
y 的预期形状
| 任务 | 模型输出 | y 形状 | 类型 | Loss |
|---|---|---|---|---|
| 回归 | (n, 1) | (n, 1) | float32 | MSELoss |
| 二元分类 | (n, 1) | (n, 1) | float32 | BCEWithLogitsLoss |
| 多类分类 | (n, C) | (n,) | long | CrossEntropyLoss |
:::warning 多类的 y
对于 CrossEntropyLoss, 是整数的 1D 向量(不是 one-hot)。类型 long,不是 float。
:::
预测类别
with torch.no_grad():
logits = model(X_test_t)
y_hat = torch.argmax(logits, dim=1) # 最可能的类别
在类别维度上 argmax——不需要显式应用 softmax,顺序保持不变。