focal loss dice loss源码_读解 SKU 目标检测源码(3) Focal loss

d46e34fdb25046ee419e06e635f477cb.png

Focal loss

今天的大餐就是 Focal loss,也就是 retinanet 的巧妙之处。

bbc6403a37de9cc9650ae297e0100c21.png

甜点

最近看了 Face++ 的目标检测效果,现在目标检测在算法上效果还是不错,大部分都能实时检测并可以明确标注出来。但是如果看细节还是存在一些问题,特别是小物体检测是有些跳动,也就是时而检测到,时而不能检测到。还是存在一些误检出问题,就是把 A 物体检测为 B 物体,所以还是存在优化空间,所以这也是我们今天努力的动力。其实这里想说目标检测是计算机视觉应用基础,可以为其他模型提供数据,所以目标检测是非常重要,并值得我们花费力气去研究。

eedbecd4b1fb65e1d2b87b75478a2807.png

开胃菜

新增的开胃菜环节,这个环节我们一起共同复习一些本次分享用到基础知识或概念,帮助我们消化大餐的内容。

如果想要确定一个有 n 种等可能(也就是每种情况发生概率都是相等的)的事件,需要有 log2 n 的信息量。对于一个发生概率是 1/n 的随机事件,我们需要 log2 n的信息量来消除不确定性,即熵为 log2 n

我们知道信息熵是和事件概率成反比的,如果收到一条太阳从东边升起的信息。那么这条信息熵就几乎为 0 ,因为我们都知道太阳从东方升起。

接下来我们对于概率不同事件进行说明,如果晴天和雨天的概率分别是 75% 和 25% ,所以要确定他们所提供的信息量也就不同,具体的计算可以看下表

a0dafefe08e4fa7c0befe069981eab96.png

从而可以推导出信息熵公式,

2c643f7e50e368f777fb5f157fa319e0.png

交叉熵

交叉熵通常用于分类问题的成本函数。其实所谓损失函数就衡量真实值和预测值之间差距。如果我们目标是服从一定分布的随机变量的话,我们的真实值是描述随机变量的概率分布p(x)而q(x)是我们根据已知数据预测出的概率,交叉熵作为一种尺度可以衡量出这两个概率分布之间差距。

25bc56759f76298e5dad365e077f5ea6.png

那么结论也就是交叉熵越小,两个分布就越相近,有了这些特征所以交叉熵可以用做分类问题的损失函数。

为什么需要 Focal loss

在 one-stage 目标检测模型中,通常是将图片切分为许多大小网格,然后每个网格又产生多个先验框,通过训练找出有目标的先验框,随后使用回归对这些有物体的先验框的大小进行调整。由于是从大量先验框中仅找到几个有目标物体存在先验框,所以如果将不包含物体先验框视为负样本,包含物体的先验框视为正样本话,就存在正负样本不均衡的问题。不平衡的正负样本并不利于训练,为了优化这个问题才提出了 Focal loss 概念。

在 Focal loss 出现之前我们是通过调整正负样本比例来缓解问题。这是 Focal loss 出现的背景

Focal loss 特点

  • 控制正负样本的权重
  • 控制容易分类和难分类样本的权重

控制正负样本的权重

focal Loss 是在交叉熵损失函数基础上进行改进。所以我们先列出交叉熵公式

2fa16357fffea9f579fce1a99819565c.png

41d9cd3be2139081c232e5964ec8ae62.png

也可以根据 y 不同情况,对上面对交叉熵分类换一种写法

0c92c517465b77deb051cadc089d2916.png

可以用 $p_t$ 来简化交叉熵 loss

39a828cebc366e7a457dcbd1b83933f5.png

通过对损失函数前添加一个系数 alpha 来调节正负样本对损失函数的影响。这里系数 alpha 有点类似之间定义 Pt。

5bb4f1fedcc416148504f25e73879650.png

根据真实值不同情况,取不同 alpha 值,也就是真实值是 1 表示整样本时损失函数值乘以 alpha,对于负样本也就是 y = 0 时,损失函数乘以 (1 - alpha) 这样如果 alpha 取之在 0 到 0.5 之间,就会降低正样本的对损失函数影响。

6226ae4623ba8e6a5076b31200c80ddf.png

所以就是通过调整 alpha 的值来改变正负样本损失的权重。

控制容易分类和难分类样本的权重

在 retinanet 中,也是通过系数控制不同(难易情况)情况下,样本对对于损失值影响程度。从而达到控制容易分类和难分类样本的权重。也就是让分类器将更多精力花费在那些难于分类的样本上。

358449395243a93fb06ecf36736aeed7.png

当输入样本属于正样本时, Pt 就有 Pt = p,这时如果 p 接近 1 就表示分类器越容易对该样本进行正确分类,那么也就是说我们分类器已经可以很好对该样本进行分类了,而这个样本对于分类器来说属于容易分类样本,所以权重就越小1 - p 就越小,反之亦然。还是说一下吧,也就是 p 越小 1 - p 就越大说明该样本对于分类器属于不容易分类样本,所以该样本就获得较大权重,对损失值给出较大影响。

f49143a41b6b3faa41049cdea86188ee.png

作为 developer 我们可能更喜欢 code 而非理论和公式,将他实现才是我们的任务和我们的喜欢表达形式。

# coding=utf-8
import numpy as np

alpha = 0.25
y_true = np.array([1,0,1,0])
y_pred = np.array([0.95,0.05,0.5,0.5])
# alpha 
alpha_weight = [alpha if y==1 else 1-alpha for y in y_true]
print(alpha_weight)

pt = np.zeros(4)
index_1 = np.argwhere(y_true==1)
index_0 = np.argwhere(y_true==0)

pt[index_1] = 1 - y_pred[index_1]
pt[index_0] = 1 - y_pred[index_0]

weights = pt*alpha_weight

print(weights)

这里不想多解释了,结合上面公式一看大家都应明白,其实这些代码就是对上面实现

[0.25, 0.75, 0.25, 0.75]
[0.0125 0.7125 0.125  0.375 ]

运行看结果吧,从 alpha 值来看根据样本是正样本还是负样本而不同,而 weights 输出来看对那些分对样本权重较小,而对于那些分错样本则权重较大

源码解析

def focal(alpha=0.25, gamma=2.0):

这里就不解释了,大家自己看注解吧,唯一需要注意就是最后正则化。

def _focal(y_true, y_pred):
        """ Compute the focal loss given the target tensor and the predicted tensor.

        As defined in https://arxiv.org/abs/1708.02002

        Args
            y_true: Tensor of target data from the generator with shape (B, N, num_classes).
            y_pred: Tensor of predicted data from the network with shape (B, N, num_classes).

        Returns
            The focal loss of y_pred w.r.t. y_true.
        """
        labels = y_true
        classification = y_pred

        # filter out "ignore" anchors
        anchor_state = keras.backend.max(labels, axis=2)  # -1 for ignore, 0 for background, 1 for object
        indices = backend.where(keras.backend.not_equal(anchor_state, -1))
        labels = backend.gather_nd(labels, indices)
        classification = backend.gather_nd(classification, indices)

        # compute the focal loss
        # 创建一个形状和 labels 一样全部为 1 的 tensor,然后 * alpha
        alpha_factor = keras.backend.ones_like(labels) * alpha
        # 然后根据真实值为 1 或其他不同情况对 alpha 取不同的值
        alpha_factor = backend.where(keras.backend.equal(labels, 1), alpha_factor, 1 - alpha_factor)
        # 定义正负样本难易程度的样本系数
        focal_weight = backend.where(keras.backend.equal(labels, 1), 1 - classification, classification)
        # 
        focal_weight = alpha_factor * focal_weight ** gamma

        cls_loss = focal_weight * keras.backend.binary_crossentropy(labels, classification)

        # 进行标准化,所谓标准化就是用损失值除以正样本的数量,因为所有样本数量过大,fc 的值主要是由正样本提供,在 fc 负样本个贡献较小,所以是除以
        # 这样本数量而不是除以全部样本数量。
        # compute the normalizer: the number of positive anchors
        normalizer = backend.where(keras.backend.equal(anchor_state, 1))
        normalizer = keras.backend.cast(keras.backend.shape(normalizer)[0], keras.backend.floatx())
        normalizer = keras.backend.maximum(1.0, normalizer)

        return keras.backend.sum(cls_loss) / normalizer

    return _focal
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值