本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com
U-Net是2015年提出的CNN模型,专门解决图像象素级别的预测问题
本文讲解U-Net的模型结构、配置、原理,并展示一个U-Net的代码实现例子
通过本文,可以快速了解什么是U-Net,以及如何在pytroch中实现一个U-NET卷积神经网络
本节讲解U-Net是什么,以及U-Net的模型结构
U-Net卷积神经网络是什么
U-Net是2015年提出的CNN模型,它主要用于解决图像象素级别的预测问题,例如图像分割、边缘提取等
例如,图像语义分割就是一个像素级别的问题:输入一张图片,然后输出每个像素所属类别的概率
U-Net原文:《U-Net: Convolutional Networks for Biomedical Image Segmentation》
U-Net原文地址: https://arxiv.org/pdf/1505.04597
U-Net原文提供的结构图如下:
笔者整理后的U-Net网络结构图如下:
U-Net是一个具有对称结构的网络,它的结构为输入层、收缩路径、扩张路径与输出层组成
输入层 :输入层由两个3x3的卷积层组成,它将输入图片的通道扩展到64
输入层卷积的配置:Size=3×3,输出通道=64,填充=0,步幅=1,带ReLu
收缩路径 :收缩路径由4个下采样模块(D1-D4)组成
收缩路径通过各个下采样模块将FeatureMap不断缩小,同时扩大输出通道
经过4个下采样模块后,FeatureMap已足够小(28×28),此时停止收缩
(收缩路径中的下采样模块详细计算见下文)
扩张路径 :收缩路径由4个上采样模块(U1-U4)组成
扩张路径通过各个上采样模块将FeatureMap不断扩大,同时缩小输出通道,
经过4个上采样模块后,FeatureMap已足够大(388×388),此时停止扩张
(扩张路径中的上采样模块详细计算见下文)
输出层 :输出层由一个1×1的卷积构成
1×1的卷积的输出通道为k,k是类别的个数,特别地,二分类任务时,k=2
输出层的主要目的是将结果步映射为对应k个类别的特征,如果进一步softmax,就是类别的概率
U-Net的预测
U-Net在预测时采用的是分块预测,即并不是把整张图片直接进行预测,而是分块进行预测
并且在对每块进行预测时,会把周边的内容一起作为输入,因为多裁一些内容更有助于预测
这就是为什么U-Net目标预测大小为388×388,但输入却是572×572的原因
此外,U-Net中并不包含全连接层,所以输入即使不是572×572,也是可兼容的
如何理解U-Net
U-Net的主要用于象素级别的预测,而每个象素的输出往往至少依赖于周边一定范围内的象素
所以U-net是希望能保持FeatureMap不变的情况下,如何将每一象素合理地综合周边象素的信息
那么U-Net是如何让每一个象素合理地融合周边信息的呢?
U-Net的主要思想是,先生成由细到粗的特征信息,然后再将信息由细到粗地组装起来
U-Net通过收缩路径进行下采样不断收缩FeatureMap,生成一系列随着层数越深越趋向全局的特征信息
可以认为,收缩路径是一个从清晰到模糊、从局部到全局的过程,特征信息也随着层数加深而由细到粗
接着,U-Net再通过扩张路径,来将收缩路径中所生成的不同粒度的特征信息,由粗到细重新组装起来
可以认为,扩张路径是一个恢复、重组的过程,它逐步扩大FeatureMap并将对应粒度的信息进行吸收
总的来说,U-Net先生成由细到粗的特征信息,再从粗到细地将信息进行融合、组装
从而获得综合了不同粒度信息的最终输出
对U-Net的一些细节的理解
1. U-Net-通道数的意义
(1)输入层的通道数(64)与输出层的通道数是一致的,它代表每个象素需要多少个特征来拟合类别
(2)收缩路径中的通道数成倍增加,主要是为了避免FeatureMap在减小的时候丢失信息
2. U-Net-扩张路径由粗到细组装特征信息的意义
在U-Net的扩张路径中,信息由粗到细地逐步进行组装
这样可以使得最终的结果更倾向于越细粒度的信息,因为越后组装的信息,保留得更原始
本节讲解U-Net的采样模块和跳跃连接等相关细节
U-net的采样模块
U-Net的下采样模块
设下采样模块的输入Size为n×n×m,则它的输出Size为(n/2-4)×(n/2-4)×(2m)
可以简单地理解为:下采样模块将FeatureMap扩大到近似2倍,并把通道数减少一半
U-Net中的下采样模块结构如下:
U-Net的下采样模块先将n×n×m的输入进行2x2的最大池化,将FeatureMap降为一半
之后再经过两个带ReLu的3x3卷积,卷积核配置为:输出通道=2m,填充=0,步幅=1
最后得到输出的Size为:(n/2-4)×(n/2-4)×(2m)
U-Net的上采样模块
设上采样模块输入为n×n×m,它同时需要一个2n×2n×(m/2)的补偿输入
在经过上采样模块的运算后,得到Size为(2n-4)×(2n-4)×(m/2)的输出
即上采样模块将FeatureMap扩大为近似2倍,同时把通道数减小为一半
U-Net中的上采样模块结构如下:
上采样模块由下往上看,它先通过上采样层,将输入的FeatureMap扩大为2倍,通道缩小一半
然后与补偿输入按通道进行拼接,使得通道数与原输入一致(此时FeatureMap是原输入的2倍)
之后经过两个带ReLu的3x3卷积,卷积核配置为:输出通道=m/2,填充=0,步幅=1
最后得到输出的Size为:(2n-4)×(2n-4)×(m/2)
Unet中的跳跃连接
扩张路径中的上采样模块,与收缩路径中的下采样模块相连接,
它指的是将下采样模块中的输出裁剪出相应大小的FeatureMap,作为上采样模块的补偿输入
例如U1的输入为(28×28×1024),在经过上采样后的FeatureMap为(56×56×512)
然后此时从D3的输出(64×64×512)中裁剪出对应大小的FeatureMap(56×56×512)
两者经过通道合并后(56×56×512)再进一步通过卷积来压缩通道,得到(52×52×512)的输出
本节讲解如何实现一个U-Net
U-Net代码实现
下面使用pytorch来实现一个U-net模型
具体代码如下:
# 本代码用于实现U-net模型
# 转载请说明来自 《老饼讲解-深度学习》 www.bbbdata.com
from torch import nn
import torch
# 下采样模块
class DownSample(nn.Module):
def __init__(self,in_channel):
super(DownSample, self).__init__()
self.nn_stack=nn.Sequential(
nn.MaxPool2d(kernel_size=2,stride=2,padding=0),
nn.Conv2d(in_channel,in_channel*2, kernel_size=3,stride=1,padding=0),
nn.ReLU(inplace=True),
nn.Conv2d(in_channel*2,in_channel*2, kernel_size=3,stride=1,padding=0),
nn.ReLU(inplace=True),
)
def forward(self, x):
y = self.nn_stack(x)
return y
# 上采样模块
class UpSample(nn.Module):
def __init__(self,in_channel):
super(UpSample, self).__init__()
self.upCov = nn.ConvTranspose2d(in_channel,int(in_channel/2), 2,stride=2)
self.cov=nn.Sequential(
nn.Conv2d(in_channel,int(in_channel/2), kernel_size=3,stride=1,padding=0),
nn.ReLU(inplace=True),
nn.Conv2d(int(in_channel/2),int(in_channel/2), kernel_size=3,stride=1,padding=0),
nn.ReLU(inplace=True),
)
def forward(self, x,S):
y = self.upCov(x)
y = torch.cat((y,S),dim=1)
y = self.cov(y)
return y
# Unet卷积神经网络的结构
class Unet(nn.Module):
def __init__(self,in_channel,num_classes):
super(Unet, self).__init__()
#--------------输入层-------------------
self.input_layer=nn.Sequential(
nn.Conv2d(in_channel,64, kernel_size=3,stride=1,padding=0),
nn.ReLU(inplace=True),
nn.Conv2d(64,64, kernel_size=3,stride=1,padding=0),
nn.ReLU(inplace=True),
)
# 输出568*568*64
#--------------下采样层-------------------
self.D1 = DownSample(in_channel=64) # 输出280*280*128
self.D2 = DownSample(in_channel=128) # 输出136*136*256
self.D3 = DownSample(in_channel=256) # 输出64*64*512
self.D4 = DownSample(in_channel=512) # 输出28*28*1024
#--------------上采样层-------------------
self.U1 = UpSample(in_channel=1024) # 输出52*52*512
self.U2 = UpSample(in_channel=512) # 输出100*100*256
self.U3 = UpSample(in_channel=256) # 输出196*196*128
self.U4 = UpSample(in_channel=128) # 输出388*388*64
#--------------输出层-------------------
self.output_layer= nn.Conv2d(64,num_classes, kernel_size=1,stride=1,padding=0)
def forward(self, x):
xx = self.input_layer(x)
d1 = self.D1(xx)
d2 = self.D2(d1)
d3 = self.D3(d2)
d4 = self.D4(d3)
u1 = self.U1(d4,d3[:,:,:56,:56])
u2 = self.U2(u1,d2[:,:,:104,:104])
u3 = self.U3(u2,d1[:,:,:200,:200])
u4 = self.U4(u3,xx[:,:,:392,:392])
y = self.output_layer(u4)
return y
# ------测试模型---------
x = torch.rand(1,1,572,572)
model = Unet(in_channel =1,num_classes=2)
y= model(x)
上面就是依U-Net原文描述构建的模型结构,具体使用中可以加入Bn层等常用的技术~
好了,以上就是UNet卷积神经网络了~
End