参考文章:
    https://blog.csdn.net/weixin_44791964/article/details/102853782
    参考讲解视频:
    https://www.bilibili.com/video/BV1E7411J72R?from=search&seid=3954447457476218575&spm_id_from=333.337.0.0
    什么是Focal loss
    Focal loss是何恺明大神提出的一种新的loss计算方案。
    其具有两个重要的特点。

    1、控制正负样本的权重

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

    正负样本的概念如下:
    一张图像可能生成成千上万的候选框,但是其中只有很少一部分是包含目标的的,有目标的就是正样本,没有目标的就是负样本。

    容易分类和难分类样本的概念如下:
    假设存在一个二分类,样本1属于类别1的pt=0.9,样本2属于类别1的pt=0.6,显然前者更可能是类别1,其就是容易分类的样本;后者有可能是类别1,所以其为难分类样本。
    image.png

    1. def focal(alpha=0.25, gamma=2.0):
    2. def _focal(y_true, y_pred):
    3. # y_true [batch_size, num_anchor, num_classes+1]
    4. # y_pred [batch_size, num_anchor, num_classes]
    5. labels = y_true[:, :, :-1]
    6. anchor_state = y_true[:, :, -1] # -1 是需要忽略的, 0 是背景, 1 是存在目标
    7. classification = y_pred
    8. # 找出存在目标的先验框
    9. indices_for_object = backend.where(keras.backend.equal(anchor_state, 1))
    10. labels_for_object = backend.gather_nd(labels, indices_for_object)
    11. classification_for_object = backend.gather_nd(classification, indices_for_object)
    12. # 计算每一个先验框应该有的权重
    13. alpha_factor_for_object = keras.backend.ones_like(labels_for_object) * alpha
    14. alpha_factor_for_object = backend.where(keras.backend.equal(labels_for_object, 1), alpha_factor_for_object, 1 - alpha_factor_for_object)
    15. focal_weight_for_object = backend.where(keras.backend.equal(labels_for_object, 1), 1 - classification_for_object, classification_for_object)
    16. focal_weight_for_object = alpha_factor_for_object * focal_weight_for_object ** gamma
    17. # 将权重乘上所求得的交叉熵
    18. cls_loss_for_object = focal_weight_for_object * keras.backend.binary_crossentropy(labels_for_object, classification_for_object)
    19. # 找出实际上为背景的先验框
    20. indices_for_back = backend.where(keras.backend.equal(anchor_state, 0))
    21. labels_for_back = backend.gather_nd(labels, indices_for_back)
    22. classification_for_back = backend.gather_nd(classification, indices_for_back)
    23. # 计算每一个先验框应该有的权重
    24. alpha_factor_for_back = keras.backend.ones_like(labels_for_back) * (1 - alpha)
    25. focal_weight_for_back = classification_for_back
    26. focal_weight_for_back = alpha_factor_for_back * focal_weight_for_back ** gamma
    27. # 将权重乘上所求得的交叉熵
    28. cls_loss_for_back = focal_weight_for_back * keras.backend.binary_crossentropy(labels_for_back, classification_for_back)
    29. # 标准化,实际上是正样本的数量
    30. normalizer = tf.where(keras.backend.equal(anchor_state, 1))
    31. normalizer = keras.backend.cast(keras.backend.shape(normalizer)[0], keras.backend.floatx())
    32. normalizer = keras.backend.maximum(keras.backend.cast_to_floatx(1.0), normalizer)
    33. # 将所获得的loss除上正样本的数量
    34. cls_loss_for_object = keras.backend.sum(cls_loss_for_object)
    35. cls_loss_for_back = keras.backend.sum(cls_loss_for_back)
    36. # 总的loss
    37. loss = (cls_loss_for_object + cls_loss_for_back)/normalizer
    38. return loss
    39. return _focal