3. 分类损失:交叉熵与Focal Loss在YOLO中的实战对比
分类损失,说白了就是让模型学会“这东西到底是什么”。在YOLO里,每个预测框都要输出一个类别概率分布,比如是“人”、“车”还是“狗”。选不对损失函数,模型可能连猫和狗都分不清。今天我就带你看看交叉熵和Focal Loss这对冤家,在YOLO里到底该怎么选。
3.1 交叉熵损失:最朴素的分类准则
交叉熵损失(Cross-Entropy Loss)是分类任务的标配。它的公式很简单:
L_CE = - Σ y_i * log(p_i)
其中 y_i 是真实标签(one-hot向量),p_i 是模型预测的概率。说白了,就是让模型对正确类别的预测概率尽量接近1。
在YOLOv3/v5中的实现:
# YOLOv5 分类损失简化版
class CrossEntropyLoss:
def __call__(self, pred, target):
# pred: [N, C] 未经过softmax的logits
# target: [N] 类别索引
loss = F.cross_entropy(pred, target, reduction='mean')
return loss
嗯,这里要注意一点。YOLO里的分类损失只对正样本(有目标的网格)计算。负样本不参与分类损失,它们只贡献置信度损失。我刚开始调YOLO时,有次把负样本也加进了分类损失,结果模型疯狂预测“背景”类别,精度直接崩了。
我的经验:交叉熵在类别均衡的数据集上表现很好。如果你手里的数据每个类别样本数差不多,直接用交叉熵就行,没必要折腾。
3.2 Focal Loss:专治类别不平衡
Focal Loss是何恺明团队在RetinaNet里提出的。它的核心思想是:让模型更关注难分类的样本,减少易分类样本的权重。公式长这样:
L_FL = - α_t * (1 - p_t)^γ * log(p_t)
相比交叉熵,多了两个参数:
- α(alpha):类别权重,平衡正负样本比例
- γ(gamma):聚焦参数,控制难易样本的权重衰减速度
YOLOv5中的Focal Loss实现:
# YOLOv5 Focal Loss 简化版
class FocalLoss:
def __init__(self, alpha=0.25, gamma=1.5):
self.alpha = alpha
self.gamma = gamma
def __call__(self, pred, target):
ce_loss = F.cross_entropy(pred, target, reduction='none')
pt = torch.exp(-ce_loss) # 预测正确的概率
focal_weight = self.alpha * (1 - pt) ** self.gamma
loss = focal_weight * ce_loss
return loss.mean()
关键点:当 γ=0 时,Focal Loss退化为交叉熵。γ越大,对易分类样本的惩罚越小。
3.3 实战对比:什么时候该换?
我在项目中遇到过好几次这样的场景:用交叉熵训练,模型对常见类别(比如“人”)精度很高,但对稀有类别(比如“雨伞”)几乎检测不到。这就是典型的类别不平衡问题。
下面这张表是我自己总结的选型指南:
| 场景 | 推荐损失 | 原因 |
|---|---|---|
| 类别均衡(每类样本数接近) | 交叉熵 | 简单高效,无需调参 |
| 类别严重不平衡(1:100以上) | Focal Loss | 自动降低易分类样本权重 |
| 小目标检测任务 | Focal Loss | 小目标通常更难分类 |
| 实时性要求极高 | 交叉熵 | Focal Loss有额外计算开销 |
| 模型已经过拟合 | Focal Loss | 降低对易分类样本的过拟合风险 |
参数调优建议:
- α(alpha):一般设为0.25~0.75。正样本少就设大一点,比如0.75
- γ(gamma):YOLOv5默认是1.5。我试过2.0,对极端不平衡效果更好,但训练会慢一些
避坑指南:我曾经在YOLOv5里直接套用RetinaNet的默认参数(α=0.25, γ=2.0),结果mAP掉了3个点。后来发现YOLO的正负样本比例和RetinaNet不一样,需要重新调。建议从γ=1.0开始,每次加0.5,观察验证集精度变化。
3.4 核心逻辑:一张图看懂
下面这张SVG图展示了交叉熵和Focal Loss的核心差异:
3.5 实战案例:从交叉熵切换到Focal Loss
我记得有一次做交通场景检测,数据集里“汽车”有10万张,“救护车”只有200张。用交叉熵训练,救护车的召回率只有12%。
切换步骤:
- 在YOLOv5的loss.py里找到分类损失部分
- 把CrossEntropyLoss替换成FocalLoss
- 先试γ=1.0,α=0.25
- 观察验证集上稀有类别的召回率
- 如果召回率提升不明显,γ加到1.5再试
结果对比:
| 指标 | 交叉熵 | Focal Loss (γ=1.5, α=0.5) |
|---|---|---|
| 救护车召回率 | 12% | 67% |
| 汽车召回率 | 95% | 93% |
| 整体mAP | 0.72 | 0.78 |
你看,Focal Loss把救护车的召回率从12%拉到了67%,整体mAP也涨了6个点。虽然汽车召回率掉了2%,但这是可以接受的——毕竟我们更关心稀有类别的检测能力。
小技巧:如果你不想改代码,YOLOv5官方已经内置了Focal Loss支持。在训练命令里加
--focal_loss_gamma 1.5 就行。但注意,这只对分类损失生效,置信度损失还是用BCE。