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的核心差异:

交叉熵 vs Focal Loss 核心逻辑 交叉熵损失 对所有样本一视同仁 易分类样本贡献大量梯度 适合类别均衡场景 Focal Loss 降低易分类样本权重 聚焦难分类样本 适合类别不平衡场景 vs 无额外参数 需调 α 和 γ 参数 结论:数据均衡用交叉熵,数据不平衡用Focal Loss

3.5 实战案例:从交叉熵切换到Focal Loss

我记得有一次做交通场景检测,数据集里“汽车”有10万张,“救护车”只有200张。用交叉熵训练,救护车的召回率只有12%。

切换步骤:

  1. 在YOLOv5的loss.py里找到分类损失部分
  2. 把CrossEntropyLoss替换成FocalLoss
  3. 先试γ=1.0,α=0.25
  4. 观察验证集上稀有类别的召回率
  5. 如果召回率提升不明显,γ加到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。