U-net理解篇(附上详解pytorch代码)(7)

Unet介绍

        Unet的提出的初衷是为了解决医学图像分割的问题,在2015年2015年的ISBI cell tracking比赛中获得了多个第一,现在大多数医疗影像语义分割任务都使用baseline,Unet可以说是最经典的图像分割的网络之一。

Unet结构介绍

   总得来说Unet结构是,对进行原图片进行卷积+池化实现的下采样,下采样主要是通过池化实现,进行四个下采样后,再进下上采样(可以通过反向卷积实现,也可以通过双线性插值实现)。每次上采样后,与左半部分卷积下采样部分相同通道数的张量进行拼接(注意拼接的时候需要对一张图进行裁剪或者填充使两张图高宽相同,拼接后图的通道数翻倍),然后再进行上采样再拼接,进行几次这样的操作后,用1*1的卷积核卷积输出使得输出的通道等于分割类别的通道数。

(其中的细节就是其中除了输出卷积只卷积一次,都是进行双层卷积操作,与resnet的类似的地方就是,residual基本模块都是由两次的卷积构成,不是进行三次也不是一次,估计是经过实验这种设计网络的习惯比较好。)

对U-NET的理解

        我们知道卷积是用多通道的卷积核,如3*3的卷积,如果要使得张量从[b,3,h,w]->[b,c,h,w],卷积核就是c个3*3卷积核,每个卷积核与原来三个通道图分别进行卷积后再进行累加。卷积后的c个通道,每个通道多有其对应的特征。

最大池化

        一张图经过网络输出后得到32个通道,我们希望32个通道的每个通道的比较亮(像素值比较高的元素,是这个分类的这个通道对应类别物体),所以这就是每次卷积后进行最大值池化的原因,能让网络训练过程种只关注每个通道最大值(当然结果最好就是这个通道是车辆类别,那就只让车辆对应的像素为255,其它像素为0)。

U型网络结构设计理解

        这样结构设计,似乎可以让网络的神经元连接变得多得更有多样性,似乎条条大路通罗马。另外个人认为unet与resnet思想有异曲同工之妙,比如当如图绿色箭头输出可以是全零时,也就是这条线断掉,它就是个进行四层卷积的网络,网络也就是当训练集比较少的时候,它不会因为数据集太少导致网络无法拟合数据集,当然数据集多的时候,为了拟合的更好,更深层次的参数也会得到更新,一层。从U-net设计初衷来看,就是为了让网络适用于简单的数据,与这样的理解也符合,本人试过比较少的数据集比如100张,图像二值分类问题效果也比较好,能达到95%以上的accuracy。

 Unet代码(unet_model.py)

class DoubleConv(nn.Module):  #双层卷积模块
    def __init__(self,ch_in,ch_out):
        super(DoubleConv,self).__init__()
        self.double_conv= nn.Sequential(
            nn.Conv2d(ch_in,ch_out,kernel_size=3,padding=1),
            nn.BatchNorm2d(ch_out),
            nn.ReLU(inplace=True) , #inplace=True 表示原张量内存进行操作
            nn.Conv2d(ch_out,ch_out,kernel_size=3,padding=1),
            nn.BatchNorm2d(ch_out),
            nn.ReLU(inplace=True)
         
        )

    def forward(self,x):
        return self.double_conv(x)



class Down(nn.Module):  #双层卷积+最大值池化的下采样模块
    def __init__(self,ch_in,ch_out):
        super(Down,self).__init__()  #继承父类
        self.maxpool_conv =nn.Sequential(
            nn.MaxPool2d(kernel_size=2),
            DoubleConv(ch_in,ch_out)
        )
    def forward(self,x):
        return self.maxpool_conv(x)
"""
转置卷积:定义*表示卷积
  Y=C*X   :表示卷积核C卷积X得到 Y
  X=C'*Y  :C‘就是转置卷积核     就是用C’进行反向卷积操作,以实现图像分辨率的恢复
 """

class Up(nn.Module):  #转置卷积(逆卷积)上采样和双层卷积操作
    def __init__(self,ch_in,ch_out):
        super(Up,self).__init__()
        self.up = nn.ConvTranspose2d(ch_in,ch_in//2,kernel_size=2,stride=2)  #对x1进行运算通道数是x2的一半
        self.conv =DoubleConv(ch_in,ch_out)

    def forward(self,x1,x2):
        x1= self.up(x1)                                  #x1 [batch,ch,h,w]

        diffY=torch.tensor([x2.size()[2]-x1.size()[2]])  #计算x1与x2的高度h差
        diffX=torch.tensor(x2.size()[3]-x1.size()[3])    #计算x1与x2的宽度w差
        # x2 =x2[:,:,diffY//2:x1.size()[2]+diffY//2,diffX//2:x1.size()[3]+diffX//2] #裁剪x2操作
        x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2,    #填充x1操作
                        diffY // 2, diffY - diffY // 2])


        x= torch.cat([x2,x1],dim=1)  #将两个图片在通道维度上进行拼接 拼接后通道数翻倍
        return  self.conv(x)                #最后进行双层卷积运算并返回



class OutConv(nn.Module):  #最后输出使用1*1的卷积核进行卷积
    def __init__(self, in_channels, out_channels):
        super(OutConv, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)

    def forward(self, x):
        return self.conv(x)


class UNet(nn.Module):
    def __init__(self,ch_in,n_classes):
        super(UNet,self).__init__()

        self.inc =DoubleConv(ch_in,64)
        self.down1= Down(64,128)
        self.down2= Down(128,256)
        self.down3= Down(256,512)
        self.down4= Down(512,1024)


        self.up1=Up(1024,512)
        self.up2=Up(512,256)
        self.up3=Up(256,128)
        self.up4=Up(128,64)

        self.outc =OutConv(64,n_classes)

    def forward(self,x):
        x1=self.inc(x)
        x2=self.down1(x1)
        x3=self.down2(x2)
        x4=self.down3(x3)
        x5=self.down4(x4)
        x =self.up1(x5,x4)
        x = self.up2(x,x3)
        x = self.up3(x,x2)
        x = self.up4(x, x1)
        x = self.outc(x)
        return  x





if __name__ == '__main__':  #网络测试
    temp = torch.randn([2,3,512,512])
    net=UNet(3,1)
    out= net(temp)
    print("out shape",out.shape)

总结

下一章将介绍Unet基于Camvid数据集的实战效果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值