共计 1393 个字符,预计需要花费 4 分钟才能阅读完成。
在二分类任务中,二元交叉熵(Binary Cross-Entropy, BCE)损失函数就像一位严格的考官,通过衡量预测概率与真实标签的差异,指导模型快速收敛到最优解。下面我们从原理到代码层层拆解它的工作方式。

数学原理拆解
二元交叉熵的公式看似简单却暗藏玄机:
$$L = -[y \cdot log(p) + (1-y) \cdot log(1-p)]$$
- 公式逐项解读
- $y$:真实标签(0 或 1),是标准答案
- $p$:sigmoid 输出的预测概率(0 到 1 之间)
- $log(p)$:当 y = 1 时,预测概率 p 越接近 1 损失越小(log 函数单调递增特性)
-
$log(1-p)$:当 y = 0 时,p 越接近 0 损失越小
-
与 MSE 的致命差异
- MSE 的梯度包含 $p(1-p)$ 项(sigmoid 导数),当预测完全错误时梯度反而趋近 0,导致梯度消失
-
BCE 的梯度只有 $\frac{p-y}{p(1-p)}$,错误越大梯度越强(参见下图对比)
-
概率解释
本质上是在最大化观测数据的似然函数,等价于最小化 KL 散度(概率分布距离)
PyTorch 实战演示
基础版本实现
import torch
import torch.nn as nn
def manual_bce_loss(y_pred, y_true):
# 必须经过 sigmoid 转换!常见错误点 1
p = torch.sigmoid(y_pred)
# 添加微小值避免 log(0)
loss = - (y_true*torch.log(p+1e-7) + (1-y_true)*torch.log(1-p+1e-7))
return loss.mean()
生产级实现(带数值稳定处理)
# 使用内置 BCELoss(已优化数值稳定性)criterion = nn.BCEWithLogitsLoss() # 内置 sigmoid
# 样本示例
y_pred = torch.randn(3, requires_grad=True) # logits
y_true = torch.tensor([1., 0., 1.])
# 关键技巧:logits 裁剪(防止梯度爆炸)y_pred_clipped = torch.clamp(y_pred, -10, 10)
loss = criterion(y_pred_clipped, y_true)
loss.backward()
避坑指南
-
激活函数遗忘
直接对线性输出计算 BCE 会导致损失函数不收敛,必须经过 sigmoid(或使用 BCEWithLogitsLoss) -
标签格式错误
标签必须是 0 或 1 的浮点数,常见错误: - 使用 -1/ 1 作为标签(某些旧教程的遗留问题)
-
忘记将整数标签转换为 float 类型
-
数值不稳定
- 手动实现时未添加 epsilon 导致 log(0)=NaN
- logits 值过大引发梯度爆炸(解决方案:clipping)
扩展思考
- 多标签分类场景
当每个样本可能属于多个类别时: - 对每个类别独立计算 BCE 损失
-
使用 sigmoid 而非 softmax(因为类别非互斥)
-
样本不均衡处理
对正负样本施加不同权重:pos_weight = torch.tensor([10.]) # 正样本权重提升 10 倍 criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)
通过这次学习,我们发现二元交叉熵就像一把精准的尺子:当预测完全错误时它会给出严厉惩罚,而接近正确时又变得宽容。这种特性使它成为处理概率输出任务的黄金标准。试着在你的下一个二分类项目中应用这些技巧吧!
正文完
