news 2026/4/22 21:46:23

021、损失函数改进(三):Distribution Focal Loss与不确定性建模

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
021、损失函数改进(三):Distribution Focal Loss与不确定性建模

从一次深夜调试说起

上周在部署YOLO模型到边缘设备时遇到一个诡异现象:同一个检测框,在白天光照充足时置信度0.92,到了黄昏就掉到0.67。阈值设0.7吧,漏检;设0.6吧,误检满天飞。这让我开始怀疑,我们是不是太过信任模型输出的那个单一置信度值了?

传统检测任务中,我们习惯用sigmoid把输出压缩到[0,1],然后当成概率直接使用。但现实世界的模糊性、遮挡、光照变化,真的能用一个标量完全表达吗?这个问题引出了今天要讨论的核心:不确定性建模。

传统Focal Loss的局限

Focal Loss大家都很熟了,解决正负样本不平衡确实有效。但仔细想想,它只关心“当前预测与真实标签的差距”,没考虑“模型对这个预测有多大把握”。举个例子:

# 常见的分类头输出cls_output=self.conv(x)# [B, C, H, W]cls_score=torch.sigmoid(cls_output)# 直接当概率用# Focal Loss计算ce_loss=-label*torch.log(score)-(1-label)*torch.log(1-score)pt=label*score+(1-label)*(1-score)fl_loss=((1-pt)**self.gamma)*ce_loss

问题在哪?cls_score这个值既包含了“是什么类别”的信息,又隐含了“有多确定”的信息,两者混在一起。当模型遇到难样本时,它可能不是“预测错了”,而是“真的不确定”。

Distribution Focal Loss:让模型学会说“不知道”

DFL的核心思想很直观:我们不直接预测一个概率值,而是预测一个概率分布。比如,原来输出0.7,现在输出[0.1, 0.2, 0.4, 0.2, 0.1]表示概率分布在0.7附近。

实现细节(踩坑记录)

classDistributionFocalLoss(nn.Module):def__init__(self,bins=10,gamma=2.0):super().__init__()self.bins=bins# 把[0,1]区间分成多少份self.gamma=gamma# 生成离散的锚点,这里注意要均匀分布# 我试过对数间隔,效果反而变差self.anchors=torch.linspace(0,1,bins)defforward(self,pred_dist,target_score):""" pred_dist: [B, bins, H, W] 每个位置预测一个分布 target_score: [B, H, W] 真实标签(连续值) """# 找到目标值最近的两个锚点# 这里有个坑:target_score可能超出[0,1],记得clamp一下target_score=target_score.clamp(0,1)# 计算权重idx=(target_score*(self.bins-1)).long()weight_right=target_score*(self.bins-1)-idx.float()weight_left=1-weight_right# 提取对应位置的预测概率# 注意维度对齐,我在这里debug了半小时pred_left=pred_dist.gather(1,idx.unsqueeze(1)).squeeze(1)pred_right=pred_dist.gather(1,(idx+1).clamp(max=self.bins-1).unsqueeze(1)).squeeze(1)# 双线性加权损失loss_left=-weight_left*torch.log(pred_left+1e-8)loss_right=-weight_right*torch.log(pred_right+1e-8)# 加上Focal weightingpt_left=pred_left.detach()pt_right=pred_right.detach()focal_weight_left=(1-pt_left)**self.gamma focal_weight_right=(1-pt_right)**self.gammareturn(focal_weight_left*loss_left+focal_weight_right*loss_right).mean()

实际部署时发现,bins数量不是越多越好。我测试过bins=5,10,20,50,发现bins=10在精度和计算量之间取得最好平衡。bins=50时训练不稳定,容易过拟合到噪声。

不确定性怎么用?

预测出分布后,我们得到两个宝贵信息:期望值(作为最终得分)和方差(作为不确定性度量)。

# 计算期望和方差expectation=(pred_dist*self.anchors.view(1,-1,1,1)).sum(dim=1)variance=(pred_dist*(self.anchors.view(1,-1,1,1)-expectation.unsqueeze(1))**2).sum(dim=1)# 应用场景1:动态阈值base_thresh=0.5dynamic_thresh=base_thresh-0.3*variance# 不确定性高时降低阈值keep_mask=expectation>dynamic_thresh# 应用场景2:不确定性加权NMSdefuncertainty_aware_nms(boxes,scores,variances,iou_thresh):# 不确定性大的框权重降低weights=torch.exp(-2*variances)# 简单加权函数weighted_scores=scores*weights# 用加权分数做NMSreturntraditional_nms(boxes,weighted_scores,iou_thresh)

在交通场景测试中,这种动态阈值策略将黄昏时段的mAP提升了3.2%。特别是对于远处小车辆,模型现在更倾向于“有保留地检测”而不是“武断地忽略”。

训练技巧(血泪教训)

  1. 初始化很重要:pred_dist的最后一层初始化用很小的正数,我习惯用nn.init.constant_(conv.bias, 0.01)。全零初始化会导致训练初期梯度爆炸。

  2. 标签平滑的配合使用:硬标签0/1不适合DFL。我用的平滑策略:

    # 原来:positive=1.0, negative=0.0# 现在:positive=0.95, negative=0.05# 极端困难样本甚至可以给0.8,让模型知道这是模糊情况
  3. 损失权重需要调:DFL损失通常比分类损失小一个数量级。我的经验是分类损失权重1.0,DFL损失权重0.1开始,根据验证集调整。

  4. 推理时别忘转换:训练时用分布,推理时要取期望。这个步骤容易忘,我吃过亏——直接取分布最大值,结果指标掉点。

部署考量

边缘设备上,DFL增加的计算量主要在于:

  • 额外卷积层输出bins个通道(bins=10时增加10倍通道)
  • 期望计算(一次加权求和)

实测在Jetson Nano上,bins=10导致推理速度下降约15%。我的优化策略:

# 训练时用完整分布,部署时用简化计算# 技巧:用两个高斯分量近似整个分布ifis_training:output=full_distribution_head(x)else:# 部署时只输出均值和方差mean=conv_mean(x)var=torch.sigmoid(conv_var(x))*0.5# 限制方差范围# 需要时再重建近似分布

个人建议

  1. 先在小数据集上验证:别直接上COCO。我在VisDrone上先跑通,确认有效后再迁移到主数据集,节省大量时间。

  2. 可视化分布变化:训练过程中定期可视化难样本的预测分布。我见过三种典型模式:

    • 尖锐单峰(模型很确定)
    • 平坦分布(模型很困惑)
    • 双峰分布(模型在两个答案间犹豫)
      第三种情况最有意思,往往对应真实世界的模糊样本。
  3. 结合具体业务:医疗影像中,不确定性高的样本应该交给医生复核;自动驾驶中,不确定性高的检测应该触发保守策略(如减速)。单纯追求mAP提升可能不是最终目标。

  4. 别神话DFL:它解决的是“认知不确定性”,对于数据本身的噪声(偶然不确定性)还需要其他手段。我现在的方案是DFL+MC Dropout,一个管认知,一个管偶然。

最后说句实话,改进损失函数就像调参——没有银弹。DFL在我这个项目里有效,可能是因为数据集中包含大量“边界情况”。如果你的数据都很清晰,传统Focal Loss可能更简单高效。多实验,多分析,找到适合你问题的那个解法,这才是工程师的价值所在。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 21:43:01

华为AC+AP融合组网:基于有线口配置实现多楼层用户统一接入实战

1. 多楼层组网为什么需要统一接入? 想象一下这样的场景:一栋五层的办公楼,每层都有几十台电脑、打印机、手机和平板电脑。如果每层楼的网络都独立配置,管理员需要分别维护五个不同的网络系统,光是IP地址冲突就够头疼的…

作者头像 李华
网站建设 2026/4/18 10:36:47

如何用WeChatMsg永久保存微信聊天记录:完整指南与实战教程

如何用WeChatMsg永久保存微信聊天记录:完整指南与实战教程 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/W…

作者头像 李华
网站建设 2026/4/18 14:42:28

5分钟掌握微信小程序ECharts:数据可视化的终极指南

5分钟掌握微信小程序ECharts:数据可视化的终极指南 【免费下载链接】echarts-for-weixin 基于 Apache ECharts 的微信小程序图表库 项目地址: https://gitcode.com/gh_mirrors/ec/echarts-for-weixin 想在微信小程序中展示专业的数据图表吗?echar…

作者头像 李华
网站建设 2026/4/17 20:23:43

高效获取网盘直链:八大平台下载助手使用指南

高效获取网盘直链:八大平台下载助手使用指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅…

作者头像 李华
网站建设 2026/4/19 0:37:34

BilibiliDown终极指南:如何轻松下载B站视频并管理个人收藏库

BilibiliDown终极指南:如何轻松下载B站视频并管理个人收藏库 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mir…

作者头像 李华
网站建设 2026/4/19 0:13:21

Ubuntu 22.04 LTS 与 Windows 10 双系统:从分区到引导的完整部署指南

1. 准备工作:双系统安装前的关键步骤 在开始安装双系统之前,有几个重要的准备工作需要完成。首先需要下载Ubuntu 22.04 LTS的系统镜像,建议从Ubuntu官网下载最新稳定版本。我通常会选择使用BT下载,因为这种方式下载速度更快&#…

作者头像 李华