瑕疵样本失衡怎么用 Focal Loss:别把训练损失塞进推理链路

发布时间:2026/10/25 14:53:48
瑕疵样本失衡怎么用 Focal Loss:别把训练损失塞进推理链路

一张图里背景很多、裂纹很少,普通分类损失容易花大量更新去处理已经分对的背景。Focal Loss 的用途是改变训练时各样本的贡献;部署后,推理通常只执行前向网络,并不逐点计算这个损失。把它描述成 NPU 上线以后必跑的“定点加速算子”,会把训练和部署混在一起。

原始依据是 2017 年的 Focal Loss 论文。它不是 2026 年的新方法。近期 RKNN Toolkit2 v2.5.0列出了 QAT 等能力,实际使用仍要核对目标平台、转换算子和量化流程,不能由此推断设备支持在线反向传播。

调制因子会改变梯度,不只是乘一个权重

二分类标签取y∈{0,1},p=sigmoid(z)。令正样本的pt=p,负样本的pt=1-p,损失为L=-αt(1-pt)^γ ln(pt)。当 γ 为 0,它退化为带类别权重的交叉熵。

对 logit 求导时,(1-pt)^γ本身也随 z 变化。只写αt(1-pt)^γ(p-y)会漏掉一项。先用浮点参考和数值微分核对解析式,再决定是否值得做定点近似,比先写 LUT 更容易定位错误。

import math def focal(z, y, gamma=2.0, alpha=0.25): p = 1.0 / (1.0 + math.exp(-z)) pt, a = (p, alpha) if y == 1 else (1.0 - p, 1.0-alpha) return -a * (1.0-pt)**gamma * math.log(pt) def gradient(z, y, gamma=2.0, alpha=0.25): p = 1.0 / (1.0 + math.exp(-z)) pt, a = (p, alpha) if y == 1 else (1.0-p, 1.0-alpha) dpt = p*(1.0-p) * (1 if y == 1 else -1) dL = a*(gamma*(1.0-pt)**(gamma-1)*math.log(pt) - (1.0-pt)**gamma/pt) return dL*dpt worst = 0.0 for y in (0, 1): for z in (-6.0, -2.0, 0.0, 2.0, 6.0): h = 1e-5 numeric = (focal(z+h, y)-focal(z-h, y))/(2*h) worst = max(worst, abs(numeric-gradient(z, y))) assert worst < 1e-8 assert abs(focal(0, 1, gamma=0)-0.25*math.log(2)) < 1e-12 print("gradient finite-difference max error: %.3g" % worst)

例子只覆盖中等范围的 logit。生产训练应使用稳定的 logits 形式,避免极端概率导致log(0);混合精度、梯度缩放和 reduction 规则也要纳入测试。这里没有训练任何检测器。

先判断是样本失衡,还是缺陷根本没有被看见

如果原图上的裂纹只有两三个像素,失焦、曝光和缩放就可能改变标签含义。增加高分辨率特征层可能保留空间信息,也会增加显存和计算量;它不能恢复采集时已经丢失的光学细节。

训练集还容易出现相邻帧泄漏。按图随机切分,可能让同一零件的几乎相同图像同时进入训练和验证。应按工件、批次或产线时间切分,并单独保留新材质、新光照的验证样本。

验收时分别报告缺陷召回、误剔除率和复核成本。mAP 需要明确 IoU、类别和置信阈值,不能把一个固定阈值下的召回率叫 mAP。损失下降只说明优化目标变化,不能自动解释漏检减少。

只有在浮点参考、数据划分和检测指标都稳定之后,定点损失才值得讨论。本文验证的是导数,尚未验证训练收敛、量化感知训练、转换兼容性或板端性能。