YOLOFuse 中 Focal Loss 的改进:应对正负样本极度不平衡的实战方案
在夜间监控、自动驾驶夜视辅助或消防热成像识别等实际场景中,单一可见光图像常常因光照不足、烟雾遮挡而失效。此时,红外(IR)图像凭借其对热辐射的敏感性,成为补全感知能力的关键模态。将 RGB 与 IR 图像融合进行目标检测,已成为提升复杂环境下系统鲁棒性的主流技术路径。
YOLOFuse 正是基于这一需求构建的多模态目标检测框架,它依托 Ultralytics YOLO 的高效结构,实现了双流输入下的端到端训练和推理。然而,在真实数据中,一个棘手的问题始终存在:背景区域远超目标区域——尤其是在红外图像中,人体或车辆这类目标往往只占极小比例。这种严重的正负样本不平衡,极易导致模型“学会”永远预测为背景,从而造成大量漏检。
传统的交叉熵损失函数对此束手无策,因为它对所有样本一视同仁。为此,YOLOFuse 引入并优化了Focal Loss,通过动态调节损失权重,使模型真正“关注”那些难分类的小目标。这项看似细微的损失函数改动,却带来了 mAP@50 提升超过 2% 的显著收益,尤其在 LLVIP 这类夜间行人数据集上表现突出。
Focal Loss 最早由 FAIR 在 RetinaNet 论文中提出,初衷正是为了解决单阶段检测器中前景/背景极端失衡的问题。它的核心思想很直观:让模型少去纠结已经分得很清楚的简单样本,把学习资源留给那些容易搞错的困难样本。
标准交叉熵损失在面对海量负样本时会迅速被主导,即使某个锚框明明没框住人,只要它的置信度稍微下降一点,累积起来的梯度也能压倒稀少的正样本信号。结果就是模型收敛得很快,但性能却卡在一个偏低的水平上。
Focal Loss 在此基础上引入两个关键参数:
- 聚焦因子 γ(gamma):控制易分样本的衰减程度;
- 平衡因子 α(alpha):调节正负类别的初始权重。
其数学表达如下:
$$
FL(p_t) = -\alpha_t (1 - p_t)^\gamma \log(p_t)
$$
其中 $ p_t $ 是模型对真实类别的预测概率。当 $ p_t \to 1 $,说明这个样本很容易被正确分类,此时 $ (1-p_t)^\gamma \to 0 $,该项损失自动衰减;反之,当 $ p_t \to 0 $,即模型犹豫不决甚至判断错误时,$ (1-p_t)^\gamma \approx 1 $,损失保持高位,反向传播的力度更强。
这就形成了一个“越不会就越要学”的正反馈机制。训练后期,大多数简单背景已经被准确过滤,剩下的主要是边界模糊、尺度微小的目标区域,而这些正是 Focal Loss 发力的地方。
在 YOLOFuse 中,该损失被应用于分类分支,替代原始的BCEWithLogitsLoss。经过实验验证,设置gamma=2.0和alpha=0.25能取得最佳效果——前者抑制易分负样本,后者赋予稀缺的正样本更高话语权。最终在 LLVIP 数据集上,mAP@50 从基准的 92.3% 提升至94.7%~95.5%,且几乎不增加推理开销。
下面是其实现代码,专为多标签二分类设计(适用于 YOLO 每个 anchor 预测多个类别的设定),数值稳定,可直接集成进训练流程:
import torch import torch.nn as nn import torch.nn.functional as F class FocalLoss(nn.Module): def __init__(self, alpha=0.25, gamma=2.0, reduction='mean'): super(FocalLoss, self).__init__() self.alpha = alpha self.gamma = gamma self.reduction = reduction def forward(self, inputs, targets): """ Args: inputs: 模型原始输出 logit (B, C, H, W) targets: one-hot 编码标签 (B, C, H, W) """ p = torch.sigmoid(inputs) ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction='none') p_t = p * targets + (1 - p) * (1 - targets) alpha_t = self.alpha * targets + (1 - self.alpha) * (1 - targets) loss = alpha_t * (1 - p_t) ** self.gamma * ce_loss if self.reduction == 'mean': return loss.mean() elif self.reduction == 'sum': return loss.sum() else: return loss值得注意的是,该实现使用sigmoid+binary_cross_entropy_with_logits处理多类别问题,而非 softmax,这更符合 YOLO 系列的解码逻辑。同时,alpha=0.25并非随意设定——它意味着正样本的权重是负样本的三倍,正好对应典型目标检测中正负样本比例约为 1:3 的经验统计。
当然,Focal Loss 的成功离不开 YOLOFuse 整体架构的支持。该框架采用双编码器-单解码器结构,分别提取 RGB 与 IR 特征,并支持多种融合策略:
- 早期融合:将 RGB 与 IR 通道拼接后送入统一主干,信息交互最早,但参数量较大(约 5.20MB);
- 中期特征融合:在主干中间层(如 stage2/stage3 输出)进行加权融合,兼顾精度与效率,仅增加 2.61MB 参数;
- 决策级融合:各自完成检测后再合并结果,灵活性高但延迟较高。
整个流程清晰简洁:
[RGB Image] → Backbone → Feature Map → } }→ Fusion Module → Fused Features → YOLO Head → Detections [IR Image ] → Backbone → Feature Map → }其中,中期融合因其出色的性价比成为推荐配置。结合 Focal Loss 后,不仅提升了整体精度,还特别改善了小目标漏检问题——这正是两类技术协同作用的结果:融合模块增强了特征表达能力,而 Focal Loss 确保这些增强后的信号能有效传递到训练过程中。
以注意力机制驱动的中期融合为例,以下是一个轻量化的 SE-like 实现:
import torch import torch.nn as nn class AttentionFusion(nn.Module): def __init__(self, channels): super(AttentionFusion, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.fc = nn.Sequential( nn.Linear(channels * 2, channels, bias=False), nn.ReLU(), nn.Linear(channels, channels * 2, bias=False), nn.Sigmoid() ) def forward(self, rgb_feat, ir_feat): fused = torch.cat([rgb_feat, ir_feat], dim=1) weights = self.avg_pool(fused) weights = self.fc(weights.view(weights.size(0), -1)) w_rgb, w_ir = torch.split(weights.unsqueeze(-1).unsqueeze(-1), [rgb_feat.size(1), ir_feat.size(1)], dim=1) out = w_rgb * rgb_feat + w_ir * ir_feat return out该模块会自动学习不同模态的重要性权重。例如,在完全黑暗环境中,IR 特征应获得更高关注;而在白天强光下,RGB 可能更具判别力。这种自适应机制进一步释放了多模态融合的潜力。
在实际部署中,YOLOFuse 的工程友好性也值得称道。项目预设目录结构清晰,开箱即用:
+------------------+ +------------------+ | RGB Camera | | IR Camera | +------------------+ +------------------+ ↓ ↓ /root/YOLOFuse/datasets/images/ /root/YOLOFuse/datasets/imagesIR/ ↓ ↓ +-------------------------------+ | YOLOFuse Model | | Dual-Stream Backbone + Fusion| | + Focal Loss Training | +-------------------------------+ ↓ /root/YOLOFuse/runs/fuse/ ↓ Detection Results社区镜像已预装 PyTorch、CUDA 和 Ultralytics 等全部依赖,避免了版本冲突带来的调试噩梦。首次运行若提示/usr/bin/python: No such file or directory,只需执行一条软链接命令即可修复:
ln -sf /usr/bin/python3 /usr/bin/python推理测试极为简便:
cd /root/YOLOFuse python infer_dual.py输出结果自动保存至runs/predict/exp,支持可视化查看融合检测效果。自定义训练也只需准备成对数据(同名 RGB/IR 图像)、上传至指定目录并修改配置文件路径即可启动:
python train_dual.py模型与日志将自动归档至runs/fuse,便于追踪迭代过程。
值得一提的是,YOLOFuse 采用了“单标注复用”策略:仅需基于 RGB 图像制作 YOLO 格式的标签文件,IR 图像共享同一套标注框。这是因为 LLVIP 等主流多模态数据集中,RGB 与 IR 图像严格对齐,空间坐标一致。这一设计直接将标注成本降低近 50%,极大提升了实用价值。
面对现实挑战,YOLOFuse 提供了一套完整的技术闭环:
- 针对正负样本极度不平衡:引入 Focal Loss,设置
gamma=2.0,alpha=0.25,动态削弱易分负样本影响,mAP@50 提升约 2.4%; - 针对双模态标注成本高:采用单标注复用机制,无需重复标注 IR 图像,节省人力;
- 针对环境配置复杂:提供预装镜像,实现“一键运行”,大幅缩短调试周期。
根据具体需求,还可灵活选择融合策略:
- 显存受限?选中期融合(2.61MB,低延迟);
- 追求极致精度?尝试早期或决策级融合;
- 数据未对齐?建议先做配准处理再使用;
- 单模态任务?不如直接用原版 YOLOv8 更高效。
此外,在损失函数调参方面,推荐从γ=2.0, α=0.25开始微调,通常能快速收敛到较优状态。对于边缘部署场景,导出 ONNX 模型后可结合 TensorRT 或 OpenVINO 加速推理。
可以预见,随着多模态传感器成本持续下降,RGB-IR 融合检测将在安防、无人系统、工业巡检等领域广泛应用。而 YOLOFuse 所展现的轻量化设计思路——通过中期特征融合控制模型体积,借助 Focal Loss 提升难例学习能力,并辅以工程友好的部署流程——为开发者提供了一条切实可行的落地路径。
这不是一次炫技式的算法堆砌,而是面向真实场景的深思熟虑:如何在有限资源下最大化检测性能?如何降低数据准备门槛?如何让研究成果快速转化为生产力?
答案就藏在这套简洁而高效的系统之中。