共计 1981 个字符,预计需要花费 5 分钟才能阅读完成。
为什么损失函数这么重要?
刚开始学机器学习时,我以为只要选个模型架构,数据喂进去就能自动训练出好结果——直到第一次遇到模型死活不收敛的情况。后来才发现, 损失函数(Loss Function)才是训练过程的隐形导演 ,它决定了模型如何评估预测误差,并通过反向传播告诉参数该往哪个方向调整。

常见的新手踩坑场景包括:
- 做分类任务却用了回归的 MSE 损失,导致概率输出异常
- 类别不平衡数据直接套用交叉熵,少数类完全被忽视
- 数据有噪声时使用 L2 损失,模型被异常值带偏
五大主流损失函数对比表
| 损失函数(中 / 英) | 数学公式 | 梯度特性 | 适用任务 |
|---|---|---|---|
| 均方误差 /MSE | $\frac{1}{n}\sum(y-\hat{y})^2$ | 梯度随误差线性增大 | 回归任务 |
| 平均绝对误差 /MAE | $\frac{1}{n}\sum | y-\hat{y} | $ |
| Huber 损失 | $\begin{cases}0.5(y-\hat{y})^2 & | y-\hat{y} | ≤δ \ δ |
| 交叉熵 /Cross-Entropy | $-\sum y\log(\hat{y})$ | 概率误差越大概率更新越大 | 分类任务 |
| 合页损失 /Hinge | $\max(0, 1-y\cdot\hat{y})$ | 仅在边界附近更新 | SVM 分类 |
代码实战:PyTorch & TensorFlow 实现
PyTorch 自定义 Huber 损失
import torch
import torch.nn as nn
class HuberLoss(nn.Module):
def __init__(self, delta=1.0):
super().__init__()
self.delta = delta
def forward(self, y_pred, y_true):
error = y_true - y_pred
abs_error = torch.abs(error)
# 核心逻辑:分段计算损失
quadratic = torch.min(abs_error, self.delta)
linear = abs_error - quadratic
loss = 0.5 * quadratic**2 + self.delta * linear
return loss.mean()
# 训练循环示例
loss_fn = HuberLoss(delta=1.5)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
for x, y in dataloader:
pred = model(x)
loss = loss_fn(pred, y)
optimizer.zero_grad()
loss.backward() # 自动计算梯度
optimizer.step()
TensorFlow 加权交叉熵
import tensorflow as tf
class WeightedCE(tf.keras.losses.Loss):
def __init__(self, pos_weight=2.0):
super().__init__()
self.pos_weight = pos_weight
def call(self, y_true, y_pred):
# 对正样本加权
loss = - (self.pos_weight * y_true * tf.math.log(y_pred) +
(1-y_true) * tf.math.log(1-y_pred))
return tf.reduce_mean(loss)
# 使用示例
model.compile(
optimizer='adam',
loss=WeightedCE(pos_weight=5) # 适用于正样本稀少场景
)
性能优化关键考量
- 学习率与损失函数的搭配
- MSE 的梯度随误差增大而线性增长,建议使用较小的学习率
-
MAE 的梯度恒定,可以适当增大学习率加速收敛
-
异常值处理策略
- 当数据存在 5% 以上的异常值时,Huber 损失比 MSE 收敛更稳定
-
极端场景(如金融风控)可考虑分位数损失(Quantile Loss)
-
分类任务的特例
- 多标签分类需改用 Binary Cross-Entropy
- 类别超过 1000 时,尝试 Label Smoothing 技术
新手避坑三连
问题 1:类别不平衡导致模型偏向多数类
- 解决方案 :
- 使用带权重的交叉熵(见上方代码)
- 采用 Focal Loss 自动降低易分类样本的权重
问题 2:回归任务出现梯度爆炸
- 解决方案 :
- 改用 Huber 或 MAE 损失
- 对输出层做 Batch Normalization
问题 3:二分类模型的输出概率不校准
- 解决方案 :
- 检查是否错误使用了多分类交叉熵
- 在 sigmoid 后添加 temperature 参数调整
思考题:为什么 MAE 对异常值更鲁棒?
想象有两个预测点:
– 点 A 误差 =10,点 B 误差 =100
– 使用 MSE 时,点 B 的梯度是点 A 的 100 倍($2×100$ vs $2×10$)
– 而 MAE 对两点的梯度都是±1
这种特性使得 MAE 不会对异常值过度反应,但代价是收敛速度较慢——有没有两全其美的方法?(提示:结合 Huber 和动态调整 δ 的策略)
正文完
