U-Net: Convolutional Networks for Biomedical Image Segmentation
引用:Ronneberger O , Fischer P , Brox T . U-Net: Convolutional Networks for Biomedical Image Segmentation[J]. 2015.
U-Net:用於生物醫學圖像分割的卷積網絡
論文地址:https://arxiv.org/pdf/1505.04597v1.pdf
GitHub源碼地址:
https://lmb.informatik.uni-freiburg.de/people/ronneber/u-net/
https://github.com/zhixuhao/unet
https://github.com/yihui-he/u-net
http://blog.leanote.com/post/zongwei/Pa
http://blog.csdn.net/hjimce/article/details/50268555
一、摘要
人們普遍認爲,成功地訓練深度網絡需要數千個帶註釋的訓練樣本。在本文中,我們提出了一種網絡和培訓策略,該策略依賴於數據增強的強大使用來更有效地使用可用的帶註釋的樣本。該體系結構包括一個用於捕獲上下文的收縮路徑和一個能夠實現精確定位的對稱擴展路徑。我們展示了這樣的網絡可以從很少的圖像進行端到端訓練,並且在ISBI挑戰方面優於現有的最佳方法(滑動窗口卷積網絡),用於在電子顯微鏡堆棧中分割神經元結構。使用在透射光顯微鏡圖像(相差和DIC)上訓練過的同一網絡,我們在這些類別中贏得了2015年ISBI細胞跟蹤挑戰賽的冠軍。而且,網絡速度很快。在最新的GPU上,對512x512圖像進行分割所需的時間不到一秒鐘。完整的實現(基於Caffe)和受過培訓的網絡可在
http://lmb.informatik.uni-freiburg.de/people/ronneber/u-net上獲得。
二、相關工作
在過去的兩年中,深度卷積網絡在許多視覺識別任務中的表現都超過了當前的技術水平,例如[7,3]。雖然卷積網絡已經存在很長時間了,但是由於可用訓練集的大小和考慮的網絡的大小,它們的成功是有限的。Krizhevsky et al.[7]的突破是由於在ImageNet數據集上對一個具有8層和數百萬參數的大型網絡進行監督訓練100萬張訓練圖像。從那時起,甚至更大更深的網絡被訓練[12]。
卷積網絡的典型用途是用於分類任務,其中圖像的輸出是單個類別標籤。 然而,在許多視覺任務中,特別是在生物醫學圖像處理中,期望的輸出應包括定位,即,應該將類別標籤分配給每個像素。 此外,在生物醫學任務中通常無法獲得數千個訓練圖像。 因此,Ciresan等。 [1]在滑動窗口設置中訓練了一個網絡,以通過提供圍繞該像素的局部區域(補丁)來預測每個像素的類別標籤。 首先,該網絡可以本地化。 其次,就補丁而言,訓練數據遠大於訓練圖像的數量。 最終的網絡在ISBI 2012上大幅度贏得了EM細分挑戰。
顯然,Ciresan等人的策略。 [1]有兩個缺點。 首先,它很慢,因爲每個補丁程序都必須單獨運行網絡,而且由於補丁程序重疊,因此存在很多冗餘。 其次,在定位精度和上下文使用之間需要權衡。 較大的修補程序需要更多的最大池化層,這會降低定位精度,而較小的修補程序使網絡只能看到很少的上下文。 最近的方法[11,4]提出了一種分類器輸出,該輸出考慮了來自多層的特徵。 良好的本地化和上下文的使用是可能的。
在本文中,我們基於更好的架構,即所謂的“全卷積網絡” [9]。 我們修改並擴展了此架構,使其可以使用很少的訓練圖像併產生更精確的分割。 參見圖1。[9]中的主要思想是通過連續的層來補充通常的合同網絡,其中合併池運算符被上採樣運算符代替。 因此,這些層提高了輸出的分辨率。 爲了定位,將收縮路徑中的高分辨率特徵與上採樣的輸出結合在一起。 然後,連續的卷積層可以根據此信息學習組裝更精確的輸出。
圖1: U-net架構(例如最低分辨率爲32x32像素)。每個藍色框對應一個多通道特徵映射。通道的數量顯示在盒子的頂部。x-y尺寸在框的左下角提供。白框表示複製的功能映射。箭頭表示不同的操作。
我們架構的一項重要修改是,在上採樣部分中,我們還擁有大量特徵通道,這些特徵通道使網絡可以將上下文信息傳播到更高分辨率的層。 結果,擴展路徑或多或少地相對於收縮路徑對稱,並且產生u形結構。 網絡沒有任何完全連接的層,僅使用每個卷積的有效部分,即,分割圖僅包含像素,在輸入圖像中可以使用其完整上下文。 這種策略允許通過重疊拼貼策略對任意大圖像進行無縫分割(參見圖2)。 爲了預測圖像邊界區域中的像素,可通過鏡像輸入圖像來推斷缺失的上下文。 這種平鋪策略對於將網絡應用於大圖像非常重要,因爲否則分辨率會受到GPU內存的限制。
圖2:用於任意大圖像的無縫分割(此處爲EM堆棧中的神經元結構的分割)的重疊拼貼策略。 對黃色區域中的分割的預測需要藍色區域內的圖像數據作爲輸入。 丟失的輸入數據通過鏡像推斷 。
至於我們的任務,幾乎沒有可用的訓練數據,我們通過對可用的訓練圖像應用彈性變形來使用過多的數據增強。 這允許網絡學習此類變形的不變性,而無需在帶註釋的圖像語料庫中查看這些轉換。 這在生物醫學分割中尤其重要,因爲變形曾經是組織中最常見的變化,並且可以有效地模擬實際變形。 Dosovitskiy等人已經證明了數據增強對於學習不變性的價值。 [2]在無監督特徵學習的範圍內。
許多細胞分割任務的另一個挑戰是分離相同類的觸摸對象;參見圖3。爲此,我們建議使用加權損失,即接觸細胞之間的分離背景標籤在損失函數中獲得較大的權重。
圖3: 玻璃上的HeLa細胞通過DIC(差示干涉對比)顯微鏡觀察記錄。(一)原始圖像。(b)與地面真相分割重疊。不同的顏色表示HeLa細胞的不同實例。(c)生成的分割蒙版(白色:前景,黑色:背景)。(d)地圖用像素方式減重,迫使網絡學習邊界像素。
所得的網絡適用於各種生物醫學分割問題。 在本文中,我們顯示了關於EM堆棧中神經元結構分割的結果(一場持續的競爭始於ISBI 2012),在此我們勝過了Ciresan等人的網絡。 [1]。 此外,我們在2015年ISBI細胞追蹤挑戰賽的光學顯微鏡圖像中顯示了細胞分割的結果。在這裏,我們在兩個最具挑戰性的2D透射光數據集上大獲全勝。
三、網絡結構
網絡架構如圖1所示。它由一個收縮路徑(左側)和一個擴展路徑(右側)組成。收縮路徑遵循卷積網絡的典型架構。它由兩個3x3卷積(未填充卷積)的重複應用組成,每個卷積後跟一個整流線性單位(ReLU)和一個2x2最大合併運算,步長爲2用於下采樣。在每個降採樣步驟中,我們將特徵通道的數量增加一倍。擴展路徑中的每個步驟都包括對特徵圖進行升採樣,然後是將特徵通道數量減半的2x2卷積(“向上卷積”),與來自收縮路徑的相應裁剪的特徵圖的串聯以及兩個3x3卷積,每個後跟一個ReLU。由於每次卷積中都會丟失邊界像素,因此有必要進行裁剪。在最後一層,使用1x1卷積將每個64分量特徵向量映射到所需的類數。該網絡總共有23個卷積層。
爲了無縫拼接輸出分割圖(請參見圖2),重要的是選擇輸入圖塊大小,以便將所有2x2最大合併操作應用於x和y大小均等的圖層。
四、U-Net網絡
參考鏈接:https://blog.csdn.net/maliang_1993/article/details/82084983
參考鏈接:https://blog.csdn.net/xxiaozr/article/details/81191890
參考鏈接:https://blog.csdn.net/hduxiejun/article/details/71107285
參考鏈接:https://blog.csdn.net/mieleizhi0522/article/details/82025509
參考鏈接:https://www.cnblogs.com/ys99/p/10889695.html
參考鏈接:https://www.jianshu.com/p/7db4c8f8113a
參考鏈接:https://www.zhihu.com/question/268331470/answer/762639159
參考鏈接:https://blog.csdn.net/fabulousli/article/details/78633531
1. U-Net網絡大體是什麼
大意上可以理解爲:普遍認爲深度網絡需要大量已標籤數據集,這個網絡(U-Net)可以依靠數據增強來事先少量數據集訓練網絡。而且,這個網絡訓練得很快,運用GPU運行,512*512的圖片只需要不用一秒即可。並且該網絡屬於端對端網絡,即輸入圖片,輸出分割開的圖片。
而U-Net提出的原因主要是:
卷積神經網絡已經存在很久了,但因爲缺少可用的訓練數據集而沒被大量使用,直到ImageNet數據集(百萬張圖片)的出現,傳統的卷積網絡的目標是分類,即對每個圖片給予一個標籤。但是對於很多視覺任務,特別是醫療圖像方向,目標應該包括定位,以及對每個像素塊給予一個標籤。而且,醫療圖像的訓練集都不大。
2. 語義分割
對圖片的每個像素都做分類。較爲重要的語義分割數據集有:VOC2012 以及 MSCOCO。
深度學習最初流行的分割方法是,打補丁式的分類方法 ( patch classification ) 。逐像素地抽取周圍像素對中心像素進行分類。由於當時的卷積網絡末端都使用全連接層 ( full connected layers ) ,所以只能使用這種逐像素的分割方法。
關於語義分割:https://blog.csdn.net/yql_617540298/article/details/83143182
3. U-Net網絡
參考鏈接:https://www.cnblogs.com/ChinaField-blog/p/10672648.html
參考鏈接:https://www.lizenghai.com/archives/27533.html
contracting path是典型的卷積網絡架構:
- 架構中含有着一種重複結構,每次重複中都有2個33卷積層(無padding)、非線性ReLu層和一個22 max pooling層(stride爲2)。(圖中的藍箭頭、紅箭頭、沒畫ReLu)
- 每一次下采樣後我們都把特徵通道的數量加倍
expansive path也使用了一種相同的排列模式:
- 每一步都首先使用反捲積(up-convolution),每次使用反捲積都將特徵通道數量減半,特徵圖大小加倍。
- 反捲積過後,將反捲積的結果與contracting path中對應步驟的特徵圖拼接起來。
- contracting path中的特徵圖尺寸稍大,將其修建過後進行拼接。
- 對拼接後的map再進行2次3*3的卷積。
- 最後一層的卷積核大小爲1*1,將64通道的特徵圖轉化爲特定類別數量(分類數量,二分類爲2)的結果。
爲了允許輸出分割圖的無縫拼接(上圖),重要的是要選擇輸入塊大小,以便將所有2*2最大池化操作應用到具有對應x和y大小的層。
4. Pytorch實現U-Net
如何在本機上安裝Pytorch環境
目前大部分都有anaconda,所以直接通過anaconda建立一個虛擬環境即可。
詳細參考:https://www.cnblogs.com/zhouzhiyao/p/11784055.html
安裝環境:win10+python3.6.2
在cmd中輸入:
conda create -n pytorch python=3.6
直接選擇輸入Y
之後安靜的等待安裝
安裝成功後,可以查看虛擬環境
conda info --envs
進入pytorch虛擬環境中
conda activate pytorch
之後,pytorch官方網站:http://pytorch.org/
根據自己的環境選擇如何安裝pytorch
或者:只用採用pip安裝。
pip install torch==1.4.0+cpu torchvision==0.5.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchsummary import summary
class unetConv2(nn.Module):
def __init__(self,in_size,out_size,is_batchnorm):
super(unetConv2,self).__init__()
if is_batchnorm:
self.conv1=nn.Sequential(
nn.Conv2d(in_size,out_size,kernel_size=3,stride=1,padding=0),
nn.BatchNorm2d(out_size),
nn.ReLU(inplace=True),
)
self.conv2=nn.Sequential(
nn.Conv2d(out_size,out_size,kernel_size=3,stride=1,padding=0),
nn.BatchNorm2d(out_size),
nn.ReLU(inplace=True),
)
else:
self.conv1=nn.Sequential(
nn.Conv2d(in_size,out_size,kernel_size=3,stride=1,padding=0),
nn.ReLU(inplace=True),
)
self.conv2=nn.Sequential(
nn.Conv2d(out_size,out_size,kernel_size=3,stride=1,padding=0),
nn.ReLU(inplace=True)
)
def forward(self, inputs):
outputs=self.conv1(inputs)
outputs=self.conv2(outputs)
return outputs
class unetUp(nn.Module):
def __init__(self,in_size,out_size,is_deconv):
super(unetUp,self).__init__()
self.conv=unetConv2(in_size,out_size,False)
if is_deconv:
self.up=nn.ConvTranspose2d(in_size,out_size,kernel_size=2,stride=2)
else:
self.up=nn.UpsamplingBilinear2d(scale_factor=2)
def forward(self, inputs1,inputs2):
outputs2=self.up(inputs2)
offset=outputs2.size()[2]-inputs1.size()[2]
padding=2*[offset//2,offset//2]
outputs1=F.pad(inputs1,padding) #padding is negative, size become smaller
return self.conv(torch.cat([outputs1,outputs2],1))
class unet(nn.Module):
def __init__(self,feature_scale=4,n_classes=21,is_deconv=True,in_channels=3,is_batchnorm=True):
super(unet,self).__init__()
self.is_deconv=is_deconv
self.in_channels=in_channels
self.is_batchnorm=is_batchnorm
self.feature_scale=feature_scale
filters=[64,128,256,512,1024]
filters=[int(x/self.feature_scale) for x in filters]
#downsample
self.conv1=unetConv2(self.in_channels,filters[0],self.is_batchnorm)
self.maxpool1=nn.MaxPool2d(kernel_size=2)
self.conv2=unetConv2(filters[0],filters[1],self.is_batchnorm)
self.maxpool2=nn.MaxPool2d(kernel_size=2)
self.conv3=unetConv2(filters[1],filters[2],self.is_batchnorm)
self.maxpool3=nn.MaxPool2d(kernel_size=2)
self.conv4=unetConv2(filters[2],filters[3],self.is_batchnorm)
self.maxpool4=nn.MaxPool2d(kernel_size=2)
self.center=unetConv2(filters[3],filters[4],self.is_batchnorm)
#umsampling
self.up_concat4=unetUp(filters[4],filters[3],self.is_deconv)
self.up_concat3=unetUp(filters[3],filters[2],self.is_deconv)
self.up_concat2=unetUp(filters[2],filters[1],self.is_deconv)
self.up_concat1=unetUp(filters[1],filters[0],self.is_deconv)
#final conv (without and concat)
self.final=nn.Conv2d(filters[0],n_classes,kernel_size=1)
def forward(self, inputs):
conv1=self.conv1(inputs)
maxpool1=self.maxpool1(conv1)
conv2=self.conv2(maxpool1)
maxpool2=self.maxpool2(conv2)
conv3=self.conv3(maxpool2)
maxpool3=self.maxpool3(conv3)
conv4=self.conv4(maxpool3)
maxpool4=self.maxpool4(conv4)
center=self.center(maxpool4)
up4=self.up_concat4(conv4,center)
up3=self.up_concat3(conv3,up4)
up2=self.up_concat2(conv2,up3)
up1=self.up_concat1(conv1,up2)
final=self.final(up1)
return final
if __name__=="__main__":
model=unet(feature_scale=1)
print(summary(model,(3,572,572)))