简单涨点 | Flow-Mixup: 对含有损坏标签的多标签医学图像进行分类(优于Mixup和Maniflod Mixup)

技术

点击上方 【AI人工智能初学者】 ,选择 【星标】 公众号

期待您我的相遇与进步

picture.image

本文提出一种用于多标签医学图像分类的新正则化方法:FlowMixup,涨点明显!性能优于Mixup、ERM等方法。
作者单位 :浙江大学, 圣母大学

1 、简介

在临床实践中,医学图像解释通常涉及多标签分类,因为患者的犯病的地方往往会出现多种症状或并发症。最近,在医学图像解释上基于深度学习的框架已达到专家级的性能,这其中部分功劳可以归功于大量准确的注释或者说标注。但是,手动注释大量医学图像是不切实际的,而自动注释快速但不精确(可能引入损坏的标签)。

在这项工作中,作者提出了一种新的正则化方法,称为Flow-Mixup ,用于带有损坏标签的多标签医学图像分类。Flow-Mixup指导模型捕获每种异常的鲁棒特征,从而帮助有效处理损坏的标签,并可以应用自动注释。

具体地说,Flow-Mixup通过向模型的隐藏状态添加约束来解耦提取的特征。而且,与其他已知的正则化方法相比,Flow-Mixup更稳定,更有效,理论和经验分析所示。对两个心电图数据集和包含损坏标签的胸部X射线数据集进行的实验验证了Flow-Mixup是有效的,并且对损坏的标签不敏感。

这项工作的3个主要贡献:

  • 1、提出了一种用于多标签医学图像分类的正则化方法Flow-Mixup ,并证明了Flow-Mixup对已损坏标签的鲁棒性;
  • 2、对MixupManifold-Mixup 以及Flow-Mixup 进行了比较,表明使用MixupManifold-Mixup 会产生“correlation conflicts ”现象和“distribution shift ”现象。
  • 3、在几个带有损坏标签的多标签医学图像分类数据集上进行了实验,验证了Flow-Mixup 优于已知的正则化方法。
2 相关正则化方法

2.1 Mixup

Mixup介绍
Mixup是一种运用在计算机视觉中的对图像进行混类增强的算法,它可以将不同类之间的图像进行混合,从而扩充训练数据集。

Mixup原理
假设是一个样本,是该样本对应的标签;是另一个样本,是该样本对应的标签,是由参数为,的贝塔分布计算出来的混合系数,由此可以得到Mixup原理公式为:

picture.image

其中指的是贝塔分布,是混合后的样本,是混合后的样本对应的标签。

需要说明几点

1.在论文作者多组实验中,无论如何设置,的值,期望始终近似为0.5。这可能是由于权重在每个batch样本都会随机产生,在整个训练过程中会有N个batch,权重在N个batch中期望近似为0.5。所以作者认为产生与样本混合权重的Beta分布参数时,算法效果相对较好。Beta分布如下图:

picture.image

2.与没有太多的限制,当=1时,就是两张图片样本混合;当>1时,便是2个图片样本两两对应混合。此外,与可以是同一批样本,也可以是不同批样本。一般在代码实现过程中,两个图片是同一批样本,唯一不同的是,是原始图片样本,而是对在维度进行shuffle后得到的。

Mixup代码实现

mixup代码实现部分如下:


        
          
import numpy as np  
import torch  
import torch.nn as nn  
from loss.focal import FocalLoss  
LOSS=FocalLoss()  
  
def criterion(batch\_x, batch\_y, alpha=1.0, use\_cuda=True):  
    '''  
    batch\_x:批样本数,shape=[batch\_size,channels,width,height]  
    batch\_y:批样本标签,shape=[batch\_size]  
    alpha:生成lam的beta分布参数,一般取0.5效果较好  
    use\_cuda:是否使用cuda  
      
    returns:  
     mixed inputs, pairs of targets, and lam  
    '''  
      
    if alpha > 0:  
     #alpha=0.5使得lam有较大概率取0或1附近  
        lam = np.random.beta(alpha, alpha)  
    else:  
        lam = 1  
    batch_size = batch_x.size()[0]  
    if use_cuda:  
        index = torch.randperm(batch_size).cuda()  
    else:  
        index = torch.randperm(batch_size) #生成打乱的batch\_size索引  
   
 #获得混合的mixed\_batchx数据,可以是同类(同张图片)混合,也可以是异类(不同图片)混合  
 mixed_batchx = lam * batch_x + (1 - lam) * batch_x[index, :]  
   
 """  
 Example:  
 假设batch\_x.shape=[2,3,112,112],batch\_size=2时,  
 如果index=[0,1]的话,则可看成mixed\_batchx=lam*[[0,1],3,112,112]+(1-lam)*[[0,1],3,112,112]=[[0,1],3,112,112],即为同类混合  
 如果index=[1,0]的话,则可看成mixed\_batchx=lam*[[0,1],3,112,112]+(1-lam)*[[1,0],3,112,112]=[batch\_size,3,112,112],即为异类混合  
 """  
    batch_ya, batch_yb = batch_y, batch_y[index]  
    return mixed_batchx, batch_ya, batch_yb, lam  
  
  
def mixup\_criterion(criterion, inputs, batch\_ya, batch\_yb, lam):  
    return lam * criterion(inputs, batch_ya) + (1 - lam) * criterion(inputs, batch_yb)  
##########################################################################  
  
#####################修改位置3:train.py文件修改代码如下######################  
if torch.cuda.is_available() and DEVICE.type=="cuda":  #add  
 inputs, targets = inputs.cuda(), targets.cuda()     
else:  
 inputs = inputs.to(DEVICE)  
 targets = targets.to(DEVICE).long()                     
   
if cfg['USE\_MIXUP']:  
 inputs, targets_a, targets_b, lam = mixup.mixup_data(  
  inputs,targets,cfg["MIXUP\_ALPHA"], torch.cuda.is_available())    
   
 #映射为Variable  
 inputs, targets_a, targets_b = map(Variable, (inputs,targets_a,targets_b))  
 #抽取特征,BACKBONE为粗特征抽取网络  
 features = BACKBONE(inputs)     
 #抽取特征,HEAD为精细的特征抽取网络  
 outputs = mixup.mixup_criterion(HEAD, features, targets_a, targets_b, lam)  
 loss = mixup.mixup_criterion(LOSS, outputs, targets_a, targets_b, lam)               
else:  
 features = BACKBONE(inputs)    
 outputs = HEAD(features, targets)                       
 loss = FocalLoss(outputs, labels)  

      

2.2 Manifold Mixup

基于Mixup,后续涌现出一些很棒的工作。Manifold mixup通过对Hidden States进行插值,取得了优于Mixup的效果。

Maniflod Mixup的具体流程如下:

  • 1、随机选网络的某一层第k层(包括输入层);
  • 2、传2个Batch的数据给网络,前向传播到第k层,得到隐藏表征hidden reprense 和;
  • 3、使用Mixup:

picture.image

picture.image

  • 4、继续前向传播直至得到输出;
  • 5、计算损失和梯度:

picture.image


        
          
def mixup\_process(out, target\_reweighted, lam):  
    indices = np.random.permutation(out.size(0))  
    out = out*lam + out[indices]*(1-lam)  
    target_shuffled_onehot = target_reweighted[indices]  
    target_reweighted = target_reweighted * lam + target_shuffled_onehot * (1 - lam)  
      
    #t1 = target.data.cpu().numpy()  
    #t2 = target[indices].data.cpu().numpy()  
    #print (np.sum(t1==t2))  
    return out, target_reweighted  
  
class PreActResNet(nn.Module):  
    def \_\_init\_\_(self, block, num\_blocks, initial\_channels, num\_classes,  per\_img\_std= False, stride=1):  
        super(PreActResNet, self).__init__()  
        self.in_planes = initial_channels  
        self.num_classes = num_classes  
        self.per_img_std = per_img_std  
        #import pdb; pdb.set\_trace()  
        self.conv1 = nn.Conv2d(3, initial_channels, kernel_size=3, stride=stride, padding=1, bias=False)  
        self.layer1 = self._make_layer(block, initial_channels, num_blocks[0], stride=1)  
        self.layer2 = self._make_layer(block, initial_channels*2, num_blocks[1], stride=2)  
        self.layer3 = self._make_layer(block, initial_channels*4, num_blocks[2], stride=2)  
        self.layer4 = self._make_layer(block, initial_channels*8, num_blocks[3], stride=2)  
        self.linear = nn.Linear(initial_channels*8*block.expansion, num_classes)  
  
    def \_make\_layer(self, block, planes, num\_blocks, stride):  
        strides = [stride] + [1]*(num_blocks-1)  
        layers = []  
        for stride in strides:  
            layers.append(block(self.in_planes, planes, stride))  
            self.in_planes = planes * block.expansion  
        return nn.Sequential(*layers)  
  
    def compute\_h1(self,x):  
        out = x  
        out = self.conv1(out)  
        out = self.layer1(out)  
        return out  
  
    def compute\_h2(self,x):  
        out = x  
        out = self.conv1(out)  
        out = self.layer1(out)  
        out = self.layer2(out)  
        return out  
  
    def forward(self, x, target= None, mixup=False, mixup\_hidden=False, mixup\_alpha=None):  
        #import pdb; pdb.set\_trace()  
        if self.per_img_std:  
            x = per_image_standardization(x)  
          
        if mixup_hidden:  
            layer_mix = random.randint(0,2)  
        elif mixup:  
            layer_mix = 0  
        else:  
            layer_mix = None     
          
        out = x  
          
        if mixup_alpha is not None:  
            lam = get_lambda(mixup_alpha)  
            lam = torch.from_numpy(np.array([lam]).astype('float32')).cuda()  
            lam = Variable(lam)  
          
        if target is not None :  
            target_reweighted = to_one_hot(target,self.num_classes)  
          
        if layer_mix == 0:  
                out, target_reweighted = mixup_process(out, target_reweighted, lam=lam)  
  
        out = self.conv1(out)  
        out = self.layer1(out)  
  
        if layer_mix == 1:  
            out, target_reweighted = mixup_process(out, target_reweighted, lam=lam)  
  
        out = self.layer2(out)  
  
        if layer_mix == 2:  
            out, target_reweighted = mixup_process(out, target_reweighted, lam=lam)  
  
          
        out = self.layer3(out)  
        if  layer_mix == 3:  
            out, target_reweighted = mixup_process(out, target_reweighted, lam=lam)  
  
        out = self.layer4(out)  
        out = F.avg_pool2d(out, 4)  
        out = out.view(out.size(0), -1)  
        out = self.linear(out)  
          
        if target is not None:  
            return out, target_reweighted  
        else:   
            return out  

      

然而,由于“correlation conflicts ”现象和“distribution shift ”现象,这两种方法由于混淆忽略了异常之间的特征相关性,因此都不太适合多标签分类,而Manifold Mixup在训练中往往是不稳定的。因此本文提出了一种用于多标签医学图像分类的Flow-Mixup方法,避免了Mixup和Manifold Mixup的缺点。

3 Flow-Mixup

3.1 Flow-Mixup概述

在本文中提出了一种新的正则化方法Flow-Mixup,用于多标签医学图像的分类。考虑到深度学习分类器,其中是一个非线性函数,是一个线性函数。采用Flow-Mixup的正向训练过程有以下几个步骤:

首先 ,在训练前选择隐藏状态s将模型分为非线性部分和线性部分、,为模型输出。

其次 ,将数据(例如图像)转发到选定的隐藏状态,并将一个新的混合模块应用到隐藏状态的特征(混合模块如下图所示)。

picture.image

最后 ,经过Mixing module的处理后,该特征继续进行前向传播,直到输出。在Mixing module中,Flow-Mixup将模型的前端部分限制为学习非线性函数,其余部分为线性函数。

在处理多标签医学图像时,非线性函数提取异常特征时,模型的线性函数会将异常特征投影到标签空间。当非线性部分的输出被输入到线性部分时,非线性部分的约束被保持,而线性部分要求其输入位于线性可分空间。不同于Mixup,特殊的Mixing module引入了额外的Flow维度,从而允许在一个模型中同时使用几个Mixing module。

3.2 Mixing Module

在深度学习模型中,图像的张量一般有4个维度:batch维度、channel维度、width和height维度。本文提出的Flow-Mixup引入了一个新的维数,称为Flow维数。如图1左图所示,假设原特征z在经过Mixing Module处理前的Flow维数为1,则Mixing Module的输出的Flow维数为2。Flow大小通过特征连接操作增加。在特征馈送到Mixing Module后,第1步是复制这些特征。然后,对特征拷贝进行Mixing处理,然后沿着Flow维串接成原始特征。Mixing Module的正向过程定义为:

picture.image

其中,Mixing操作将一个特征副本变换为2个小副本,,并对其采用标准Mixing,如图1右侧所示:

和是通过对进行随机索引Shuffle得到的。p从beta分布pB(α,α)中随机抽样,是控制Mixing度的超参数。表示流向级联,导致Flow size增大。根据式(2),将特征z转换为具有双流量大小的。由于在前向传播过程中Flow大小增加了1倍,Mixing Module在后向传播过程中应将梯度减半,以保持梯度的大小。混合模的反向传播定义为:

picture.image

其中表示原始特征的梯度,表示混合特征的梯度(如图1所示)。这样,Mixing Module可以同时应用于几种隐藏状态,同时保留原始特征,如图2(b)所示。

picture.image

注意,正则化方法不能完全限制后续层为线性函数,因此应用几个Mixing Module有助于加强线性约束。在实现中,如果隐藏状态是最后一种状态(图2(b))或只有一种状态(图2(a))应用Mixing Module,则可选择计算原始特征向输出层的前向传播。如果原始特性没有前向传播,Mixing Module则退化为普通的Mixup操作:

在正向传播计算和在反向传播。

  1. 方法对比 =========

4.1 与Mixup对比

正如在以前的工作中所讨论的,异常的特征可以是相关的,可能不是线性可分的。换句话说,异常的内在相关性可能与Mixup的线性约束相冲突。因此,用Mixup正则化训练多标签图像分类器可能会导致性能下降。

picture.image

如图3所示,在将数据流形映射到满足Mixup线性约束的低维空间后,由于2类的边界不能处理这2类的数据,就会发生“Correlation conflicts ”现象。与此相反,在Flow-Mixup中,异常的相关特征可以首先被非线性函数解耦为异常特有的特征,这些特征存在于线性可分空间中。

4.2 与Maniflod Mixup对比

Maniflod Mixup允许在训练过程中应用Mixup操作几个隐藏的状态。然而,这种Mixup操作不能同时进行。Maniflod Mixup在每次训练迭代中随机选择其中一种隐藏状态进行Mixing操作,因此存在以下2个缺点:

  • 1、每次迭代的参数更新都会影响最终的参数。因此,很难确切地知道数据混合应用到一个隐藏状态的程度,由于Mixing操作是用概率来融合的。因此,也很难确定Mixing操作的超参数。
  • 2、由于训练条件到隐藏状态(是否使用混合操作)是多变的,因此训练过程是不稳定的,存在“Distribution shift ”现象。

Distribution shift ”是指客观特征分布发生变化。理想情况下,在隐藏状态上使用Mixing操作将限制特征存在于线性可分空间。然而,Maniflod Mixup不断将约束改变为隐藏状态,导致训练过程不稳定,性能下降。

为了观察模型训练中出现的“Distribution shift ”现象,作者比较CIFAR-10训练集上的特征分布,如图所示:

picture.image

在CIFAR-10的训练集上使用Mixing和不Mixing训练PreAct-ResNet-32模型。然后收集每个残差块的输出和模型输出。为了避免对分类结果的影响,对每个块输出和模型输出的收集特征使用k-means聚类算法(划分为k=10个类)。然后计算的平均值(类似于方差分析中的)来观察特征分布。,其中SSI为簇内平方和,SST为总平方和。表示来自群间方差的总方差的百分比。越高,簇的边界越清晰。定义如下:

picture.image

其中C为簇个数,N为图像个数,为属于第C个簇的图像个数。是第j幅图像在第隐藏状态下的特征。表示一个数据在第i个隐藏状态下的特征尺寸,即,其中D、H、W分别为通道、高和宽。和分别表示第个隐藏状态下的数据平均特征和第个簇在第个隐藏状态下的数据平均特征。

从图4可以看出,使用Mixup学习到的特征的明显高于没有进行任何Mixing操作的特征。因此,在使用Manifold Mixup时,由于Mixing和不Mixing的客观特征分布有很大的不同,因此会产生“Distribution shift ”现象。

5 实验

作者在ChestX-ray14数据集和阿里巴巴天池云大赛的2个心电图记录数据集上进行实验。

picture.image

picture.image

实验结论:

picture.image

picture.image

在CXR分类中最好表现、ECG分类的最佳表现。可以看出,Flow-Mixup在处理不同程度的标签损坏方面优于其他正则化方法。同时与其他正则化方法相比,Flow-Mixup方法的性能验证了Flow-Mixup方法的性能。

6 参考

[1].https://blog.csdn.net/sinat\_36618660/article/details/101633504

[2].Manifold Mixup: Better Representations by Interpolating Hidden States

[3].Flow-Mixup: Classifying Multi-labeled Medical Images with Corrupted Labels

原文获取方式,扫描下方二维码

回复【 Flow-Mixup 】即可获取论文与源码

picture.image

声明:转载请说明出处

扫描下方二维码关注【AI人工智能初学者】公众号,获取更多实践项目源码和论文解读,非常期待你我的相遇,让我们以梦为马,砥砺前行!!!

picture.image 点“在看”给我一朵小黄花呗 picture.image

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动 GPU Scale-up 互联技术白皮书
近日,字节跳动正式发布基于以太网极致优化的 GPU Scale-up 互联技术白皮书,推出 EthLink 的创新网络方案,旨在为 AI 集群提供低延迟、高带宽的高速互联传输,满足 AI 应用对 GPU 之间高效通信的需求。这一举措标志着字节跳动在 AI 基础设施领域的突破,有望推动通用人工智能(AGI)和大语言模型(LLM)的进一步发展。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论