unet论文_注意力医学分割:Attention U-Net论文笔记

d1a04e98ba4b9cc5fc9c9575796c7561.png

Attention U-Net

原文:Attention U-Net:Learning Where to Look for the Pancreas [Cited by 440]

论文链接: https://arxiv.org/abs/1804.03999

pytorch official code: https://github.com/ozan-oktay/Attention-Gated-Networks

笔记时间:2020.12.5

写在前面

方法部分感觉写的有点乱,如果有写不清楚的欢迎指出。我改~

这篇文章应该是比较早的把软attention的思想引入到医学图像当中的。

注意力

注意力分为Hard AttentionSoft Attention

硬注意力:一次选择一个图像的一个区域作为注意力,设成1,其他设为0。他是不能微分的,无法进行标准的反向传播,因此需要蒙特卡洛采样来计算各个反向传播阶段的精度。 考虑到精度取决于采样的完成程度,因此需要其他技术(例如强化学习)。

软注意力:加权图像的每个像素。 高相关性区域乘以较大的权重,而低相关性区域标记为较小的权重。权重范围是(0-1)。他是可微的,可以正常进行反向传播。

c8ba0c5470b683f2d6b5f59f171a7ca4.png

观察上图,这是一个图像生成标题的任务。上面是soft 下面是hard,我们可以看到,soft attention的权重是每次被放置在整张图像上,注意力关注的部分(越白)的数值越接近1,越黑越接近0

unet需要attention的原因

acf642408e567cfcdda4f3f163966dd7.png

在传统的unet中,为了避免在decoder时丢失大量的空间精确细节信息,使用了skip的手法,直接将encoder中提取的map直接concat到decoder相对应的层。但是,提取的low-level feature有很多的冗余信息(刚开始提取的特征不是很好)。

软注意力的使用,可以有效抑制无关区域中的激活,减少冗余的部分的skip

Abstract

开题点睛,创新点在于提出了一种注意力门attention gate (AG)模型。用该模型进行训练时,能过抑制模型学习与任务无关的部分,同时加重学习与任务有关的特征。(集中注意力到有用的地方,提取有用的东西,甩掉没用的东西)

AG即插即用,可以直接集成到网络模型当中。

Introduction

在医学分割中,当目标器官形状和大小在不同患者间差异较大时。还是需要多级级联cnn。胆识级联模型中所有模型都会重复的提取相似的低级特征。

如果使用文章所提的attention gate的方法,就可以代替,级联网络的使用。能够注意与学习任务有关的特征。

Methodogy

文章中用了不少数学公式来讲方法,并且时3D形式的。这里结合2D的代码来看看是怎么实现这个注意力门的。

代码参考:https://github.com/LeeJunHyun/Image_Segmentation

    def forward(self,x):
        # encoding path
        x1 = self.Conv1(x) #1*3*512*512 ->conv(3,64)->conv(64,64)-> 1*64*512*512

        x2 = self.Maxpool(x1) #1*64*512*512 -> 1*64*256*256
        x2 = self.Conv2(x2)  #1*64*256*256 ->conv(64,128)->conv(128,128)-> 1*128*256*256
        x3 = self.Maxpool(x2) #1*128*256*256 -> 1*128*128*128
        x3 = self.Conv3(x3) #1*128*128*128 ->conv(128,256)->conv(256,256)->  1*256*128*128

        x4 = self.Maxpool(x3)#1*256*128*128 -> 1*256*64*64
        x4 = self.Conv4(x4) #1*256*64*64 ->conv(256,512)->conv(512,512)-> 1*512*64*64

        x5 = self.Maxpool(x4)#1*512*64*64 -> 1*512*32*32
        x5 = self.Conv5(x5) #1*512*32*32->conv(512,1024)->conv(1024,1024)-> 1*1024*32*32

        # decoding + concat path
        d5 = self.Up5(x5)  #1*1024*32*32 ->Upsample-> 1*1024*64*64 -> conv(1024,512) ->1*512*64*64
        x4 = self.Att5(g=d5,x=x4) #2(1*512*64*64) -> 1*1*64*64 ->1*512*64*64
        d5 = torch.cat((x4,d5),dim=1) #1*1024*64*64       
        d5 = self.Up_conv5(d5)  #1*1024*64*64 ->conv(1024,512)->conv(512,512)-> 1*512*64*64
        
        d4 = self.Up4(d5)
        x3 = self.Att4(g=d4,x=x3)
        d4 = torch.cat((x3,d4),dim=1)
        d4 = self.Up_conv4(d4)

        d3 = self.Up3(d4)
        x2 = self.Att3(g=d3,x=x2)
        d3 = torch.cat((x2,d3),dim=1)
        d3 = self.Up_conv3(d3)

        d2 = self.Up2(d3)
        x1 = self.Att2(g=d2,x=x1)
        d2 = torch.cat((x1,d2),dim=1)
        d2 = self.Up_conv2(d2)

        d1 = self.Conv_1x1(d2)

        return d1

上面的代码时forward的整体框架,unet的框架就不多做介绍,直接看attention的实现。对于一张输入为1x3x512x512(1是batchsize,3是通道)的2D图,执行到x5的时候(经过五次下采样)已经是最小的feature map了(1x1024x32x32)。对其进行上采样得到d5(1x512x64x64)。

对d5和x4执行Att5(g=d5,x=x4)

self.Att5 = Attention_block(F_g=512,F_l=512,F_int=256)

class Attention_block(nn.Module):
    def __init__(self,F_g,F_l,F_int):
        super(Attention_block,self).__init__()
        self.W_g = nn.Sequential(
            nn.Conv2d(F_g, F_int, kernel_size=1,stride=1,padding=0,bias=True),
            nn.BatchNorm2d(F_int)
            )
        
        self.W_x = nn.Sequential(
            nn.Conv2d(F_l, F_int, kernel_size=1,stride=1,padding=0,bias=True),
            nn.BatchNorm2d(F_int)
        )

        self.psi = nn.Sequential(
            nn.Conv2d(F_int, 1, kernel_size=1,stride=1,padding=0,bias=True),
            nn.BatchNorm2d(1),
            nn.Sigmoid()
        )
        
        self.relu = nn.ReLU(inplace=True)
        
    def forward(self,g,x):
        g1 = self.W_g(g) #1x512x64x64->conv(512,256)/B.N.->1x256x64x64
        x1 = self.W_x(x) #1x512x64x64->conv(512,256)/B.N.->1x256x64x64
        psi = self.relu(g1+x1)#1x256x64x64di
        psi = self.psi(psi)#得到权重矩阵  1x256x64x64 -> 1x1x64x64 ->sigmoid 结果到(0,1)

        return x*psi #与low-level feature相乘,将权重矩阵赋值进去

858102ed52c1d1dc23c3bd7817af6ab4.png

x4是从上往下下采样得到的图。d5是x4下一层map上采样的图。要对d5和x4执行Att5(g=d5,x=x4)。

图中的xl对应代码中的x4,g代表代码中的d5。(想象一下unet的图,xl就是左边的东西,g是右边的对应大小的东西)

g(1x512x64x64)/ x4(1x512x64x64)

注意力模块执行步骤:

  1. 对g做1*1卷积得到 1x256x64x64
  2. 对xl做1*1卷积得到 1x256x64x64
  3. 讲1,2步结果相加(为什么要加起来呢?为了突出特征,如果在两个图中某个点两者都有,加起来,会更为突出)
  4. 对第3步结果relu
  5. 对第4步结果做conv(256,1)卷积,将256通道降到1通道。得到1x1x64x64的图
  6. 对第5步结果进行sigmoid,使得值落在(0,1)区间,值越大,越是重点。(这个得到的就是注意力权重)
  7. 这里因为图的大小一样,所以不需要resampler。
  8. 最后和xl相乘,把注意力权重赋到low-level feature中。

attention出来的结果在和上采样的结果(x4)进行concat(这里就和unet一样了。区别就是unet是skip的是直接过来的low-level feature,而我这里concat的是low-level feature是先经过注意力机制赋予权重(0-1)的map)

3ccb4d62ac7dce315605f3a9b89ff818.png

在上图中。在3、6、10和150个epoch时,其中红色突出显示较高的注意力。随着训练的进行,网络学会了专注于期望的区域。

参考

https://towardsdatascience.com/a-detailed-explanation-of-the-attention-u-net-b371a5590831

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来解答你的问题。 首先,我们需要先了解一下Attention U-Net模型的结构。它是一种基于U-Net的图像分割模型,它利用了注意力机制来增强模型的分割能力。具体来说,Attention U-Net模型包含了编码器和解码器两个部分,其中编码器部分采用了类似U-Net的结构,而解码器部分则引入了注意力机制。 下面是使用PyTorch实现Attention U-Net模型的代码: ```python import torch import torch.nn as nn class ConvBlock(nn.Module): def __init__(self, in_channels, out_channels): super(ConvBlock, self).__init__() self.conv = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True) ) def forward(self, x): return self.conv(x) class UpConvBlock(nn.Module): def __init__(self, in_channels, out_channels): super(UpConvBlock, self).__init__() self.up = nn.Sequential( nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True), nn.Conv2d(in_channels, out_channels, kernel_size=1), ) self.conv = ConvBlock(out_channels*2, out_channels) def forward(self, x, x_down): x_up = self.up(x) x_concat = torch.cat([x_down, x_up], dim=1) return self.conv(x_concat) class AttentionBlock(nn.Module): def __init__(self, in_channels): super(AttentionBlock, self).__init__() self.conv = nn.Conv2d(in_channels, in_channels//2, kernel_size=1) self.activation = nn.Sigmoid() def forward(self, x): x_gap = nn.AdaptiveAvgPool2d((1, 1))(x) x_conv = self.conv(x_gap) x_activation = self.activation(x_conv) x_attention = x * x_activation return x_attention class AttentionUNet(nn.Module): def __init__(self, in_channels, out_channels): super(AttentionUNet, self).__init__() self.down1 = ConvBlock(in_channels, 64) self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2) self.down2 = ConvBlock(64, 128) self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2) self.down3 = ConvBlock(128, 256) self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2) self.down4 = ConvBlock(256, 512) self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2) self.bridge = ConvBlock(512, 1024) self.up1 = UpConvBlock(1024, 512) self.att1 = AttentionBlock(512) self.up2 = UpConvBlock(512, 256) self.att2 = AttentionBlock(256) self.up3 = UpConvBlock(256, 128) self.att3 = AttentionBlock(128) self.up4 = UpConvBlock(128, 64) self.out = nn.Conv2d(64, out_channels, kernel_size=1) def forward(self, x): x1 = self.down1(x) x_pool1 = self.pool1(x1) x2 = self.down2(x_pool1) x_pool2 = self.pool2(x2) x3 = self.down3(x_pool2) x_pool3 = self.pool3(x3) x4 = self.down4(x_pool3) x_pool4 = self.pool4(x4) x_bridge = self.bridge(x_pool4) x_up1 = self.up1(x_bridge, x4) x_att1 = self.att1(x_up1) x_up2 = self.up2(x_att1, x3) x_att2 = self.att2(x_up2) x_up3 = self.up3(x_att2, x2) x_att3 = self.att3(x_up3) x_up4 = self.up4(x_att3, x1) x_out = self.out(x_up4) return x_out ``` 在上面的代码中,我们定义了ConvBlock、UpConvBlock、AttentionBlock以及AttentionUNet这几个模块。ConvBlock和UpConvBlock分别用于构建编码器和解码器中的卷积块和上采样卷积块,AttentionBlock用于实现注意力机制,AttentionUNet则是整个Attention U-Net模型的实现。 在AttentionUNet中,我们首先定义了编码器部分,它由4个卷积块和4个池化层组成。接着是桥接器,它是一个卷积块,用于将编码器的输出转换为解码器的输入。解码器部分由4个上采样卷积块和4个注意力块组成,注意力块用于增强模型的分割能力。最后,我们通过一个卷积层将模型的输出转换为与分割目标相同大小的二进制掩模。 希望这个代码能够对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值