分类中的样本不平衡问题——Asymmetric Loss 收到采集条件真实情况的限制不管是二分类的正负样本还是多分类中都会普遍存在样本不平衡的问题。Asymmetric Loss非对称损失函数简称 ASL是一种专门设计用于解决类别不平衡问题的损失函数尤其在多标签分类任务如图像标签识别、社交网络文本分类等中表现优异。简单来说它的核心思想是“区别对待”正样本和负样本让模型把更多的注意力集中在那些“难以分类”或“稀有”的样本上。非对称体现在两个方面1. 调节因子的数值设定不同最直观的非对称调节因子 γγ 的作用是“动态调节损失值的权重”。它并不是对所有样本施加同样的惩罚而是根据模型当前预测的“自信程度概率”来区别对待。在 ASL 的原始论文设定中正负样本的调节因子并不相等且通常存在巨大的量级差异因为概率 pt​ 是一个 0 到 1 之间的小数当它被赋予一个大于 0 的指数即 γ 时数值会变得更小。 γ 越大这个数值缩水的程度就越夸张。正样本的调节因子 γ 通常设为 0这意味着模型对正样本不施加额外的降权惩罚。因为正样本本身就很稀少我们希望模型对每一个正样本都给予充分的重视和梯度反馈。负样本的调节因子 γ− 通常设为 2或更大这意味着模型对负样本施加了强烈的降权惩罚。因为负样本铺天盖地我们需要通过较大的 γ−γ−​ 来强力压制那些容易分类的负样本。2 . 处理逻辑与目的的非对称机制的非对称除了数值不同正负样本在公式中的数学处理方式和物理意义也是完全不一样的对正样本Focusing on Hard PositivesASL 对正样本的处理逻辑类似于 Focal Loss。它的目的是“聚焦”。当正样本预测得很准时损失变小当正样本预测得很差Hard Positive时损失变大。它是在引导模型去“死磕”那些难认出的稀有标签。对负样本Truncation / MarginASL 对负样本的处理引入了一个概率截断机制Margin, m 。如果模型预测某个正负样本的概率小于 m 即模型非常确信它是负样本ASL 会直接将这部分负样本的损失截断为 0相当于在训练时直接“无视”它们。它是在引导模型去“忽略”那些毫无营养的简单背景。下面是具体的实现步骤概率计算与非对称裁剪 (Asymmetric Clipping)xs_pos torch.sigmoid(x) xs_neg 1 - xs_pos # Asymmetric Clipping if self.clip is not None and self.clip 0: xs_neg (xs_neg self.clip).clamp(max1)原理解读首先通过 Sigmoid 将模型输出的 logits (x) 转换为正样本的概率xs_pos那么负样本的概率就是xs_neg 1 - xs_pos。代码亮点这里实现了 ASL 独有的概率位移Probability Margin Shifting。代码对负样本概率进行了(xs_neg clip)的操作然后截断在 1 以内。这相当于给简单负样本设定了一个硬阈值边界让原本高概率是负样本的截断为1此时负样本的交叉熵的log部分就是1所以loss就是1就不会反向传播。也可以把clip理解为一个阈值负样本错分为正样本的概率比clip还低时那么就可以忽略这个样本不参与梯度传播因为已经足够确信它是负样本了。基础交叉熵计算与掩码分离 (Basic CE Masking)los_pos y * torch.log(xs_pos.clamp(minself.eps)) los_neg (1 - y) * torch.log(xs_neg.clamp(minself.eps)) loss los_pos los_neg原理解读这是标准的二元交叉熵BCE计算过程。代码亮点交叉指的是权重和log部分分别是真实标签和预测值掩码则指的是利用标签y0或1矩阵作为天然的掩码Mask。因为当y1时losslos_pos当y0时losslos_neg。所以los_pos实际上只保留了真实为正类的样本的损失而los_neg只保留了真实为负类的样本的损失。两者相加后就得到了一个包含所有样本基础损失的矩阵。非对称聚焦权重 (Asymmetric Focusing)pt xs_pos * y xs_neg * (1 - y) one_sided_gamma self.gamma_pos * y self.gamma_neg * (1 - y) one_sided_w torch.pow(1 - pt, one_sided_gamma) loss * one_sided_w原理解读这是 Focal Loss 核心调制因子 (1−pt)γ的非对称升级版。ASL 的动态权重部分和 Focal Loss 原理基本相同都通过幂函数降低容易样本的权重、突出困难样本。代码亮点pt巧妙地提取了每个样本对应的正确类别概率正样本取xs_pos负样本取xs_neg。含义是分对的概率/自信程度。one_sided_gamma同样利用标签y作为掩码为每一个样本动态分配 γ 值正样本获得gamma_pos负样本获得gamma_neg。pt是自信程度那么1-pt就是犹豫程度使用pow可以使得犹豫程度低的压低权重犹豫程度高的接近1则不被抑制。针对负样本gamma_neg在多标签分类中一张图通常只有少数几个正标签剩下的绝大多数都是负标签。因为负样本数量极多且其中大部分是毫无难度的“简单负样本”所以必须设置一个较大的gamma_neg如默认值 4以强力抑制它们防止模型被负样本主导。针对正样本gamma_pos正样本本身就是稀缺资源。如果给正样本设置过大的 γγ 会导致大量正样本的损失被削弱模型反而学不到东西。因此gamma_pos通常设置得较小甚至为 0即不对正样本做 Focal 降权保持标准交叉熵的学习强度。抑制程度就是gamma控制正/负样本分别对应一个gammagamma越大抑制越厉害。最后将这个非对称权重one_sided_w乘到基础loss上完成了对难易样本的动态降权正负失衡 vs 类间失衡。ASL 的原始设计并不直接处理这种类间的不平衡它默认所有正样本无论是人还是长颈鹿在训练初期都同样稀缺。对于4分类pt [0.1, 0.8, 0.05, 0.05]那么1 - pt [0.9, 0.2, 0.95, 0.95]真实标签y[0,1,0,0],one_sided_gamma [4, 1, 4, 4]指数之后的权重张量one_sided_w [0.6561, 0.2, 0.8145, 0.8145]。可以看到索引023处本来错误率很高加权之后权重相对索引1被降低了所以模型可以更专注于正样本索引1.梯度控制优化 (Gradient Control)if self.disable_torch_grad_focal_loss: torch.set_grad_enabled(False) # ... 计算 one_sided_w ... if self.disable_torch_grad_focal_loss: torch.set_grad_enabled(True)工程细节这是一个非常高级的工程优化。在计算聚焦权重 one_sided_w 时如果开启了此选项会临时关闭 PyTorch 的自动求导功能计算出one_sided_w之后乘到loss之前再打开梯度。因为one_sided_w是一个常数权重但是PyTorch 并不知道你的“数学意图”。它会忠实地把torch.pow和*这两个操作都加入计算图。它不仅会计算基础 BCE Loss 对模型预测概率 pt 的梯度还会额外计算 one_sided_w 对 pt 的梯度然后利用链式法则把它们加起来。这样反向传播时需要多做一次复杂的链式求导拖慢了训练速度还容易显存爆炸。所以临时设置torch.set_grad_enabled(False)我们通常只需要让基础 BCE Loss 参与梯度回传而不需要对这个复杂的权重项再求一次高阶导数。这不仅能节省显存还能加快训练速度。