Skip to content

Instantly share code, notes, and snippets.

@YimianDai
Last active August 7, 2019 23:36
Show Gist options
  • Save YimianDai/1cba38c44bf71e3739915613198f187e to your computer and use it in GitHub Desktop.
Save YimianDai/1cba38c44bf71e3739915613198f187e to your computer and use it in GitHub Desktop.
FocalLoss

原理

  1. α:为了解决 Class Imbalance 问题而引入,对于样本数过多的那一类样本,α 应设得较低,这样就降低了该类样本对于 Loss 的重要性 (alpha 越大,就是对正类的损失惩罚越大,这样的话,文章中 alpha = 0.25,所以是正类惩罚少,负类惩罚多)
  2. γ:为了降低易分样本的权重,专注于训练难分负样本而引入,(1 - Pt)∈(0,1),当样本易分类时,(1 - Pt)很小,(1 - Pt)^γ 会变得更小,这样就减少了简单易分类样本的重要性,相对增加了那些误分类样本的重要性

代码

"""
    sparse_label : bool, default True
        Whether label is an integer array instead of probability distribution.
"""
        self._sparse_label = sparse_label
  1. 默认值是 True,就是我们常用的 Label 样式,如果是二分类,那么里面每一个元素都是 0 或 1
        if sparse_label and (not isinstance(num_class, int) or (num_class < 1)):
            raise ValueError("Number of class > 0 must be provided if sparse label is used.")
  1. 这表明,如果要 sparse_label 为 True,必须要指定 num_class,对于 Sigmoid 的输出,照理说 num_class 肯定等于 1 才对?
        if self._sparse_label:
            one_hot = F.one_hot(label, self._num_class)
        else:
            one_hot = label > 0
  1. 但是在我们二分类的情形里面,希望 one_hot 是同 label 一样大小尺寸的矩阵,或者在 label 就是 one_hot,因此需要 self._sparse_label 是 False
        if not self._from_logits:
            pred = F.sigmoid(pred)
  1. 从这里可以看到,Focal Loss 期待的输入是 sigmoid 的输出,注意不是 Softmax
        pt = F.where(one_hot, pred, 1 - pred)
  1. 从上面看出,one_hot 是一个指示器,如果真实的 label 是 1,那么就用 pred(这边 pred 是对正类的预测概率),如果真实的 label 是 0,那么就用 1 - pred
        alpha = F.where(one_hot, self._alpha * t, (1 - self._alpha) * t)
  1. 可以看到正类的前的权重是 alpha,是 0.25;负类的是 1 - alpha,是 0.75

$$ \mathrm{FL}\left(p_{\mathrm{t}}\right)=-\left(1-p_{\mathrm{t}}\right)^{\gamma} \log \left(p_{\mathrm{t}}\right) $$

如果 $p_t$ 越接近 1,说明我们的预测越正确,那么 $(1 - p_t)^{\gamma}$ 就越小,如果 $\gamma$ 越大,那么预测正确但由于预测概率还没有为 1 所产生的 loss 就越小

pred 应该为 N x 1 x H x W 或者 N x H x W,

Softmax output, nclass = 2, sparse_label = True

假设我是 二分类的 Softmax output,那么 pred 就是一个 N x 2 x H x W 的张量。

        if not self._from_logits:
            pred = F.sigmoid(pred)

经过上面代码后,因为 Sigmoid 是对自己的 prediction score,相当于 pred 里面是各自 prediction score 的概率

        if self._sparse_label:
            one_hot = F.one_hot(label, self._num_class)
        else:
            one_hot = label > 0

label 是一个 N x H x W 的张量,经过 F.one_hot 后得到的 one_hot 是一个 N x H x W x 2 的矩阵,这个形状与 N x 2 x H x W 的 pred 的形状是不同的。如果要想把 one_hot 变成一个 N x 2 x H x W 的张量,则要添加下面代码

            one_hot = one_hot.swapaxes(1,3)

假设是第一个样本的最左上角的像素标签是 0,也就是 label[0, 0, 0] = 0,那么经过 F.one_hot 我们就可以得到 one_hot[0, 0, 0, 0] = 1, one_hot[0, 1, 0, 0] = 0]

        pt = F.where(one_hot, pred, 1 - pred)

对于 label[0, 0, 0] 这个位置,pt[0,0,0,0] 照理说应该为背景的概率,但是 where 函数返回的是 前景的概率,

pt[0,1,0,0] 照理说应该为前景的概率,但是 where 函数返回的是 背景的概率,

不对不对,就不应该用 softmax

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment