LMFLOSS:专治解决不平衡医学图像分类的新型混合损失函数 (附代码)

 

论文地址:https://arxiv.org/pdf/2212.12741.pdf

代码地址:https://github.com/SanaNazari/LMFLoss

1.是什么?

LMFLOSS是一种用于不平衡医学图像分类的混合损失函数。它是由Focal Loss和LDAM Loss的线性组合构成的,旨在更好地处理不平衡数据集。Focal Loss通过强调难以分类的样本来提高模型的性能,而LDAM Loss则考虑了数据集的类别分布来调整权重。

2.为什么?

先来简单回顾下,对于类别不均衡问题,以往的方法是如何解决的。大体上主要有两种,即以数据为中心驱动和以算法为中心的解决方案。

数据策略

以数据为中心的类别不均衡解决方法主要有两种:过采样欠采样。过采样试图为少数类别生成人工数据点,而欠采样旨在消除多数类别的样本。

算法策略

算法层面的策略,特别是在深度学习领域,主要侧重于开发损失函数来应对类不平衡问题,而不是直接操纵数据。一种简单的方式便是为每个类别都设置相应的权重,以便与多数类别相比,少数类别样本的错误分类受到更严重的惩罚。另一种方法是为每个训练样本自适应地设置一个唯一的权重,以便硬样本获得更高的权重。

作者便提出了一种称为 Large Margin aware Focal (LMF) Loss 的新型损失函数,以缓解医学成像中的类不平衡问题。该损失函数动态地同时考虑硬样本和类分布。

3.怎么样

3.1 Focal Loss

说到类别不均衡的损失函数,不得不提的便是 Focal Loss。对于分类问题,大家常用的便是交叉熵损失 BCE Loss,该损失函数对所有类别均一视同仁,即赋予同等的权重学习。而 Focal Loss 主要就是交叉熵损失改进的,通过引入 \alpha 和 \gamma 两个调节因子来调整样本数量和样本难易程度,以便模型专注于学习少数类。具体公式如下:

3.2 LDAM Loss

《 Learning imbalanced datasets with label-distribution-aware margin loss 》 这篇文章中提出了另一项减轻类不平衡问题的工作,称为标签分布感知边距(LDAM)损失。作者建议对少数类引入比多数类更强的正则化,以减少它们的泛化误差。如此一来,损失函数保持了模型学习多数类并强调少数类的能力。LDAM 损失侧重于每个类的最小边际和获得每个类和统一标签测试错误,而不是鼓励大多数类训练样本与决策边界的大边距。换句话说,它只会鼓励少数群体获得相对较大的利润。此外,作者提出了用于获得多个类别 1、2、...、k 的类别相关边距的公式: \gamma _{j} = \frac{C}{n_{j}1/4^{}}.

这里 j∈1,...,k 表示特定类,n_{j}表示每个类别的样本数,C为固定的常数。现在,让我们定义出一个样本对 (x,y),x 为样本,y为对应的标签,同时给定一个模型 f。考虑下面这个函数映射:x_{y}=f(x)_{y};我们令 u=e^{z_{y}-p_{y}},这里对于每一个类别j∈1,...,k 都有 p_{j}=\frac{C}{n_{j}^{1/4}}。因此,LDAM 损失便可以定义为:

3.3 LMF Loss

Focal Loss 创建了一种机制,可以更加强调模型难以分类的样本;通常,来自少数群体的样本将属于这一类。相比之下,LDAM Loss 通过考虑数据集的类别分布来判断权重。我们假设与单独使用每个功能相比,同时利用这两个功能可以产生有效的结果。因此,作者提出的 Large Margin aware Focal (LMF) 损失是 Focal 损失和由两个超参数加权的 LDAM 的线性组合,公式如下:

这里,α 和 β 是常数,被认为是可以调整的超参数。 因此,本文提出的损失函数在单个框架中联合优化了两个独立的损失函数。通过反复试验,作者发现将相同的权重分配给两个组件会产生良好的结果。

3.4 代码实现

# -*- coding: utf-8 -*-
"""
Created on Wed May 24 17:03:06 2023

@author: Sana
"""

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
from ..builder import LOSSES

class FocalLoss(nn.Module):

    def __init__(self, alpha, gamma=2):
        super().__init__()
        self.alpha = alpha
        self.gamma = gamma

    def forward(self, output, target):
        num_classes = output.size(1)
        assert len(self.alpha) == num_classes, \
            'Length of weight tensor must match the number of classes'
        logp = F.cross_entropy(output, target, self.alpha)
        p = torch.exp(-logp)
        focal_loss = (1 - p) ** self.gamma * logp

        return torch.mean(focal_loss)


class LDAMLoss(nn.Module):

    def __init__(self, cls_num_list, max_m=0.5, weight=None, s=30):
        """
        max_m: The appropriate value for max_m depends on the specific dataset and the severity of the class imbalance.
        You can start with a small value and gradually increase it to observe the impact on the model's performance.
        If the model struggles with class separation or experiences underfitting, increasing max_m might help. However,
        be cautious not to set it too high, as it can cause overfitting or make the model too conservative.

        s: The choice of s depends on the desired scale of the logits and the specific requirements of your problem.
        It can be used to adjust the balance between the margin and the original logits. A larger s value amplifies
        the impact of the logits and can be useful when dealing with highly imbalanced datasets.
        You can experiment with different values of s to find the one that works best for your dataset and model.

        """
        super(LDAMLoss, self).__init__()
        m_list = 1.0 / np.sqrt(np.sqrt(cls_num_list))
        m_list = m_list * (max_m / np.max(m_list))
        m_list = torch.cuda.FloatTensor(m_list)
        self.m_list = m_list
        assert s > 0
        self.s = s
        self.weight = weight

    def forward(self, x, target):
        index = torch.zeros_like(x, dtype=torch.uint8)
        index.scatter_(1, target.data.view(-1, 1), 1)

        index_float = index.type(torch.cuda.FloatTensor)
        batch_m = torch.matmul(self.m_list[None, :], index_float.transpose(0, 1))
        batch_m = batch_m.view((-1, 1))
        x_m = x - batch_m

        output = torch.where(index, x_m, x)
        return F.cross_entropy(self.s * output, target, weight=self.weight)

@LOSSES.register_module()
class LMFLoss(nn.Module):
    def __init__(self, cls_num_list, weight, alpha=1, beta=1, gamma=2, max_m=0.5, s=30):
        super().__init__()
        self.focal_loss = FocalLoss(weight, gamma)
        self.ldam_loss = LDAMLoss(cls_num_list, max_m, weight, s)
        self.alpha = alpha
        self.beta = beta

    def forward(self, output, target):
        focal_loss_output = self.focal_loss(output, target)
        ldam_loss_output = self.ldam_loss(output, target)
        total_loss = self.alpha * focal_loss_output + self.beta * ldam_loss_output
        return total_loss

参考:Focal Loss 后继之秀 | LMFLOSS:专治解决不平衡医学图像分类的新型混合损失函数

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
如果数据集中存在类别不平衡的情况,我们需要使用自定义的损失函数解决这个问题。对于二分类问题,XGBoost支持自定义损失函数来处理类别不平衡的情况。下面是一个自定义不平衡分类的XGBoost损失函数的示例代码: ```python import numpy as np import xgboost as xgb # 自定义损失函数 def custom_loss(y_pred, y_true): alpha = 1.0 # 正样本权重 beta = 0.1 # 负样本权重 grad = np.zeros_like(y_pred) hess = np.zeros_like(y_pred) y_true = y_true.get_label() for i in range(len(y_pred)): if y_true[i] == 1: grad[i] = (1 - y_pred[i]) * alpha hess[i] = y_pred[i] * (1 - y_pred[i]) * alpha else: grad[i] = -y_pred[i] * beta hess[i] = y_pred[i] * (1 - y_pred[i]) * beta return grad, hess # 加载数据 dtrain = xgb.DMatrix('train_data.txt') dtest = xgb.DMatrix('test_data.txt') # 定义参数 params = {'max_depth': 3, 'eta': 0.1, 'objective': 'binary:logistic'} # 训练模型 bst = xgb.train(params, dtrain, num_boost_round=10, obj=custom_loss) # 预测结果 preds = bst.predict(dtest) # 输出结果 print(preds) ``` 在上述代码中,我们定义了一个名为`custom_loss`的自定义损失函数,该函数接受两个参数:`y_pred`表示模型预测的结果,`y_true`表示实际的标签值。在该函数中,我们定义了正样本和负样本的权重,然后分别计算出正样本和负样本的梯度和二阶导数,并返回给XGBoost训练模型时使用。 接下来,我们使用`xgb.DMatrix`加载训练数据和测试数据,定义了一些参数,并使用`xgb.train`函数训练模型。在训练模型时,我们将自定义损失函数作为`obj`参数传递给函数。最后,我们使用训练好的模型对测试数据进行预测,并输出预测结果。 需要注意的是,自定义不平衡分类的XGBoost损失函数需要满足一定的条件,如对梯度和二阶导数的计算等,可以参考XGBoost官方文档中关于自定义损失函数的说明。同时,需要根据具体问题和数据特征来定义合适的正样本和负样本权重,以达到最优的预测效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值