电竞比分网-中国电竞赛事及体育赛事平台

分享

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

 南風(fēng)清雨 2018-10-03

選自Medium,作者:Farhan Ahmad,機(jī)器之心編譯,參與:李詩萌、路。

全卷積網(wǎng)絡(luò)自 2012 年出現(xiàn)以來,在圖像分類和圖像檢測(cè)領(lǐng)域取得了巨大成功。本文利用筆記本電腦構(gòu)建了一個(gè)小型全卷積網(wǎng)絡(luò),詳細(xì)介紹了全卷積網(wǎng)絡(luò)的思路、過程等等,值得一看

語義分割是一種學(xué)習(xí)如何識(shí)別圖像中對(duì)象范圍的機(jī)器學(xué)習(xí)技術(shù)。語義分割賦予機(jī)器學(xué)習(xí)系統(tǒng)與人類相似的理解圖像內(nèi)容的能力。它促使機(jī)器學(xué)習(xí)算法定位對(duì)象的精準(zhǔn)邊界,無論是街景圖像中的汽車和行人,還是醫(yī)療圖像中的心臟、肝臟和腎臟。

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

左圖:分割后的 CT 掃描圖像(圖源:Semantic Scholar)。右圖:分割后的街景圖像(圖源:Cityscapes Dataset)

在語義分割方面有一些優(yōu)秀的文章,這篇文章也許是最全面的:資源 | 從全連接層到大型卷積核:深度學(xué)習(xí)的語義分割全指南

這篇文章的主要內(nèi)容是如何針對(duì) MNIST 數(shù)字建立一個(gè)小而快速的語義分割網(wǎng)絡(luò)。

背景

語義分割網(wǎng)絡(luò)有很多類,本文的重點(diǎn)在于全卷積網(wǎng)絡(luò)(FCN)。Berkely 的論文(https://people.eecs./~jonlong/long_shelhamer_fcn.pdf)第一次提出 FCN。FCN 是通過擴(kuò)展普通的卷積網(wǎng)絡(luò)(CNN)建立的,因此 FCN 有更多參數(shù)而且訓(xùn)練時(shí)間更長(zhǎng)。本文所述的工作源于構(gòu)建一個(gè)非常小的 FCN,該 FCN 可以在幾分鐘內(nèi)在普通的筆記本電腦上訓(xùn)練得到。這個(gè)想法的實(shí)現(xiàn)首先需要建立一個(gè)在每張圖像中都包含多個(gè) MNIST 數(shù)字的數(shù)據(jù)集。用于生成此派生數(shù)據(jù)集的代碼在這里:https://github.com/farhanhubble/udacity-connect/blob/master/segmented-generator.ipynb。為避免混淆,我們將該數(shù)據(jù)集命名為 M2NIST(多數(shù)字 MNIST)。

M2NIST

M2NIST 中的每一張圖都是灰度圖(單通道),大小為 64*84 像素,最多包含 MNIST 數(shù)據(jù)集中的 3 個(gè)數(shù)字。如下所示:

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

M2NIST 中的多數(shù)字圖像

M2NIST 數(shù)據(jù)集的標(biāo)簽是分割掩碼。分割掩碼是一個(gè)二進(jìn)制圖像(像素值為 0 或 1),其高度和寬度都和多數(shù)字圖像一致,但是有 10 個(gè)通道,從 0~9 的每一個(gè)數(shù)字都有一個(gè)通道。掩碼中的第 k 個(gè)通道的像素都設(shè)置為 1,這與輸入的多數(shù)字圖像中數(shù)字 k 的位置是一致的。如果數(shù)字 k 沒有出現(xiàn)在多數(shù)字圖像中,就將掩碼中的第 k 個(gè)通道的所有像素設(shè)置為 0。另一方面,如果多數(shù)字圖像包含多個(gè)第 k 個(gè)數(shù)字的實(shí)例,則第 k 個(gè)通道就將所有像素設(shè)置為 1,這是為了與多數(shù)字圖像中的任一實(shí)例一致。例如,上述多數(shù)字圖像的掩碼如下圖所示:

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

上述多數(shù)字圖像的掩碼。只有數(shù)字 2、3 和 9 的通道的一些像素為 1。

為了讓事情變得簡(jiǎn)單,M2NIST 數(shù)據(jù)集結(jié)合了 MNIST 中的數(shù)字,而且并未做任何諸如旋轉(zhuǎn)或縮放這樣的變化。M2NIST 可以保證數(shù)字不會(huì)發(fā)生重疊。

FCN 背后的思路

FCN 背后的思路非常簡(jiǎn)單。與 CNN 類似,F(xiàn)CN 也級(jí)聯(lián)了卷積層和池化層。卷積層和最大池化層降低了輸入圖像的空間維度,還結(jié)合局部模式生成更多抽象「特征」。這種級(jí)聯(lián)就是所謂的將原始輸入編碼為更抽象的編碼特征的編碼器。

在 CNN 中,編碼器后緊跟著一些全連接層,這些全連接層可以將編碼器產(chǎn)生的局部特征混合到全局預(yù)測(cè)結(jié)果中,而全局預(yù)測(cè)可以告訴我們感興趣的對(duì)象是否存在。

CNN = 編碼器 + 分類器

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

典型的 CNN 結(jié)構(gòu)。圖源:https://www./solutions/deep-learning/convolutional-neural-network.html

在 FCN 中,我們想要預(yù)測(cè)掩碼。如果在一張輸入圖像中有 n 類對(duì)象的話,那么掩碼就有 n 個(gè)通道。掩碼中第 k 個(gè)通道的 r 行 c 列的像素預(yù)測(cè)輸入圖中坐標(biāo)為 (r,c) 的像素屬于類別 k 的概率。這也被稱為像素級(jí)密集預(yù)測(cè)。因?yàn)槊總€(gè)像素屬于不同類別的概率和應(yīng)該為 1,所以從通道 1 到 n 在 (r,c) 的值相加的和應(yīng)該為 1。

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

包含數(shù)字 2、3 和 9 的 M2NIST 圖像帶有通道 ID 的掩碼。通道 0 到 9 在 (r,c) 處的值相加的和等于 1。

讓我們來了解一下 FCN 是如何完成像素級(jí)密集預(yù)測(cè)的。首先,F(xiàn)CN 使用轉(zhuǎn)置卷積從編碼器階段逐漸擴(kuò)展輸出特征。轉(zhuǎn)置卷積可以將特征重新分配至來源的像素位置。為了更好地理解轉(zhuǎn)置卷積,請(qǐng)參閱下文:https:///up-sampling-with-transposed-convolution-9ae4f2df52d0

要強(qiáng)調(diào)的重要一點(diǎn)是轉(zhuǎn)置卷積不會(huì)撤銷卷積操作。轉(zhuǎn)置卷積使用和卷積結(jié)合多個(gè)值一樣的方式重新分配一些卷積操作的輸出,但方向相反。

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

轉(zhuǎn)置卷積將一個(gè)值從它來源的位置重新分配到(多個(gè))位置。圖源:https:///up-sampling-with-transposed-convolution-9ae4f2df52d0

使用多重轉(zhuǎn)置卷積重復(fù)擴(kuò)展或進(jìn)行所謂的上采樣,直到特征的高度和寬度與輸入圖像一致。這樣的操作提供了每個(gè)像素位置的特征,并構(gòu)成了 FCN 的解碼器階段。

FAN = 編碼器 + 解碼器

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

典型 FCN 架構(gòu)。第一個(gè)階段是編碼器階段,與 CNN 相似,編碼器階段減少了輸入的高度(H)和寬度(W),并增加了通道(C)的厚度或數(shù)量。第二個(gè)階段是解碼器階段,該階段使用了轉(zhuǎn)置卷積(反卷積)對(duì)來自編碼器的特征進(jìn)行上采樣,直至其尺寸與輸入圖像一致。上圖展現(xiàn)了每一層后的輸出 H 和 W。輸出的通道厚度(數(shù)量)并未展示出來,但可以量化表示。圖源:https://www.doc./~jce317/semantic-segmentation.html

解碼器的輸出是形狀為 H*W*C 的體(volume),其中 H 和 W 是輸入圖像的維度,C 是超參數(shù)。之后 C 通道會(huì)以像素級(jí)的方式和 n 個(gè)通道組合在一起,n 是對(duì)象類別的數(shù)量。特征值的像素級(jí)結(jié)合會(huì)使用普通的 1*1 卷積執(zhí)行。1*1 卷積常用于這種「降維」操作。

大多數(shù)情況下 C>n,所以可以將該操作稱為降維。值得一提的是,在大多數(shù)實(shí)現(xiàn)中,降維應(yīng)用于編碼器階段的輸出而非解碼階段的輸出。這是為了減小網(wǎng)絡(luò)的尺寸(https:///pdf/1409.4842.pdf)。

無論使用解碼器對(duì)編碼器的輸出進(jìn)行上采樣,然后將解碼器輸出維度降為 n 還是將編碼器的輸出維度直接降為 n 然后用解碼器對(duì)降維后的輸出進(jìn)行上采樣,最終結(jié)果都是 H*W*n。然后用 Softmax 分類器以像素為單位預(yù)測(cè)每個(gè)像素所屬 n 類中任一類的概率。

舉一個(gè)具體的例子,假設(shè)編碼器的輸出是 14*14*512,如上面的 FCN 圖所示,類別數(shù)量 n 是 10。一種選擇是先使用 1*1 的卷積降低厚度。這一步操作后輸出結(jié)果變?yōu)?14*14*10,然后進(jìn)行上采樣,結(jié)果變?yōu)?28*28*10、56*56*10 等等,直到得到 H*W*10 的輸出。第二個(gè)選擇是先進(jìn)行上采樣,得到 28x28x512、56x56x512 等,直到 HxWx512 的輸出,再使用 1*1 的卷積降低厚度至 H*W*10。顯而易見,第二個(gè)選擇會(huì)消耗更多內(nèi)存,因?yàn)楹穸葹?512 的中間輸出明顯會(huì)比第一種選擇產(chǎn)生的厚度為 10 的中間輸出消耗更多內(nèi)存。

以編碼器-解碼器架構(gòu)思想為基礎(chǔ),我們接下來了解一下如何重新利用 CNN 的部分組件使其成為 FCN 的編碼器。

重新利用 MNIST 分類器

一般而言,F(xiàn)CN 是通過擴(kuò)展現(xiàn)有 CNN 分類網(wǎng)絡(luò)(如 Vgg、Resnet 或 GoogleNet)來建立的。FCN 的建立不僅再利用了這些 CNN 架構(gòu),還利用了這些架構(gòu)預(yù)訓(xùn)練期間的權(quán)重,這顯著地減少了 FCN 的訓(xùn)練時(shí)間。

原始論文(https://people.eecs./~jonlong/long)中是這樣描述如何將 CNN 轉(zhuǎn)換為 FCN 的:

通過丟棄最終的分類器層斷開每一個(gè)網(wǎng)絡(luò),然后將所有的全連接層轉(zhuǎn)換為卷積層。

用于建立 FCN 的 CNN 結(jié)構(gòu)很簡(jiǎn)單:卷積層-最大池化層-卷積層-最大池化層-全連接層-全連接層。該 CNN 結(jié)構(gòu)和訓(xùn)練代碼在此:https://github.com/farhanhubble/udacity-connect/blob/master/mnist.ipynb。保存訓(xùn)練好的網(wǎng)絡(luò),這樣才可以對(duì)其進(jìn)行再利用。該網(wǎng)絡(luò)定義如下:

l1_1 = tf.layers.conv2d(input_2d,8,5,1,

activation=tf.nn.relu, name='conv1')

l1_2 = tf.layers.max_pooling2d(l1_1,2,2,name='pool1')

l1_3 = tf.layers.conv2d(l1_2,8,3,1,

activation=tf.nn.relu,name='conv2')

l1_4 = tf.layers.max_pooling2d(l1_3,2,2,name='poool2')

l1_5 = tf.layers.flatten(l1_4, name='flatten')

l2 = tf.layers.dense(l1_5, 32,

activation=tf.nn.relu,name='dense32')

out = tf.layers.dense(l2,10,name='dense10')

為了「斷開」該網(wǎng)絡(luò),我們移除了最后的分類器層 dense10。然后用 1*1 的卷積層代替了僅剩的全連接層 dense32。這是我們之前并未提及但原文中進(jìn)行了的操作。在上述代碼中相當(dāng)于移除了 flatten 和 dens32 層,插入了新的 1*1 卷積,并將輸出通道數(shù)設(shè)置為 32。這等同于丟棄了最后一個(gè)最大池化層 pool2 后的所有層,再添加一個(gè) 1*1 的卷積層。

用于構(gòu)建 FCN 初始版本的代碼地址:https://github.com/farhanhubble/udacity-connect/blob/4408cc1e8917f37e287d09177d6e4585bfe164ff/FCN-mnist.ipynb(最后更新的代碼(https://github.com/farhanhubble/udacity-connect/)看似不同但重點(diǎn)一致)。

在下面的代碼片段中,通過 get_tensor_by_name() 提取了最后的最大池化層的輸出,然后將其饋送到輸出厚度為 32 的 1*1 卷積中。該卷積層是原始 CNN 種 dense32 層的「替代」。接下來再用 1*1 卷積將輸出厚度減少到 10。這是之前討論過的降維。

#Load pre-trained CNN for MNIST.

encoder = load_graph('checkpoints/frozen_graph.pb')

# Get required tensors from our pre-trained encoder(CNN).

maxpool2_out = encoder.get_tensor_by_name('poool2/MaxPool:0')

## Helper functions to reduce clutter.

_conv1x1 = lambda tensor, nb_filters :\

tf.layers.conv2d(tensor,

nb_filters,

1,

1,

activation=tf.nn.relu)

# Create a 32-deep 1x1 convolution layer in place of the 32-wide fully-connected layer.

enc_l1 = _conv1x1(maxpool2_out,32)

# Reduce number of channels to 10.

enc_l2 = _conv1x1(enc_l1,10)

這就完成了 FCN 的編碼器階段。為了建立解碼器階段,我們需要考慮如何縮放編碼器輸出的寬度與高度以及縮放的尺度。

盡管編碼器中的卷積層和最大池化層來自于用于分類 28*28 的 MNIST 圖像的 CNN,但也可以輸入任意大小的圖像。輸入圖像的高度和寬度對(duì)卷積層和最大池化層沒什么影響,但對(duì)全連接層影響較大,不過因?yàn)橐呀?jīng)斷開最后的全連接層并將所有全連接層轉(zhuǎn)換為 1*1 的卷積層,因此避免了影響。

當(dāng)將 64*84*1 的 M2NIST 圖像輸入到編碼器時(shí),第一個(gè)卷積層(來自原始的 CNN)的卷積核大小 k=5,步長(zhǎng) s=1,輸出深度 f=8,產(chǎn)生的輸出大小為 60*40*8。k=2、s=2 的最大池化層將輸出大小減半為 30*40*8。k=3、s=1、f=8 的下一個(gè)卷積層產(chǎn)生了大小為 28*38*8 的輸出,緊跟著的下一個(gè)最大池化層再度將輸出減半為 14*19*8。

總而言之:

FCN 借用的 CNN 部分得到大小為 64*84*1 的輸入,輸出了 14*19*8 的特征。

編碼器的下一層(dense32 的替代品)是輸出厚度 f=32 的 1*1 的卷積層。該卷積層將 14*19*8 的特征重新結(jié)合到 14*19*32 的新特征中。

將這些特征的厚度減?。ń稻S)。這用了厚度 f=10 的 1*1 卷積。所以編碼器最終輸出的特征形狀為 14*19*10。然后通過解碼器對(duì)這些特征進(jìn)行上采樣,直到特征變?yōu)?64*84*10。

解碼器要將 14*19*10 的特征上采樣為 64*84*10 的特征。

上采樣要分階段完成,以避免最終輸出(掩碼)的 ugly pattern。在(前期)實(shí)現(xiàn)中,把 14*19*10 的特征上采樣到 30*40*10,然后再上采樣為 64*84*10。

用類似于卷積的轉(zhuǎn)置卷積進(jìn)行上采樣,以卷積核大小 k、步長(zhǎng) s 和濾波器數(shù)量(厚度)f 作為參數(shù)。每一個(gè)轉(zhuǎn)置卷積的濾波器數(shù)量 f 都設(shè)置為 10,因?yàn)槲覀儾挥酶淖兒穸取?/p>

步長(zhǎng)取決于最終維度和初始維度的比例。對(duì)第一個(gè)轉(zhuǎn)置卷積而言,高度的比例是(30/14),寬度的比例是(40/19),二者的值都約為 2,故 s=2。在第二個(gè)轉(zhuǎn)置卷積中,該比例分別是 64/30 和 84/40,所以還是 s=2。

確定卷積核大小是一件有點(diǎn)棘手且相當(dāng)依賴經(jīng)驗(yàn)的事。對(duì)第一個(gè)轉(zhuǎn)置卷積來說,k=1 將維度從 14*19*10 加倍為 28*38*10。為了得到 30*40*10,用 k=2 和 k=3 試了一下,但是都沒得到令人滿意的結(jié)果。最終,當(dāng) k=4 的時(shí)候起作用了。對(duì)第二個(gè)轉(zhuǎn)置卷積而言,卷積核大小通常為 k=6。

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

解碼器階段,卷積核大?。╧)和步長(zhǎng)(s)的值都經(jīng)過仔細(xì)選擇。

解碼器代碼僅需調(diào)用 TensorFlow API 的兩行:

## Helper functions to reduce clutter.

_upsample = lambda tensor, kernel_sz, stride, nb_filters :\

tf.layers.conv2d_transpose(tensor,

nb_filters,

kernel_sz,

stride,

padding='valid',

kernel_regularizer=

tf.contrib.layers.l2_regularizer(1e-3))

# Decoder with Two-stage up-sampling.

dec_l1 = _upsample(enc_l2,4,2,nb_classes)

dec_l2 = _upsample(enc_l3,6,2,nb_classes)

為了進(jìn)行像素級(jí)的概率計(jì)算,將解碼器的輸出饋送到 Softmax 層。沿著厚度(通道)應(yīng)用 Softmax。

logits = tf.reshape(dec_l2,[-1,nb_classes], name='logits')

labels_ph = tf.placeholder(dtype=tf.int32,

shape=(None,

y.shape[1],

y.shape[2],

y.shape[3]),

name='segmentation_labels')

lr_ph = tf.placeholder(dtype=tf.float32, name='learning_rate')

labels_flat = tf.reshape(labels_ph,[-1,num_classes])

cost_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(

labels=labels_flat, logits=logits))

# Apply an optimizer

optimizer_op = tf.train.AdamOptimizer(learning_rate=lr_ph).minimize(cost_op)

在配備英偉達(dá) 1050 Ti GPU 的筆記本電腦上用交叉熵?fù)p失函數(shù)訓(xùn)練 100~400 個(gè) epoch 后得到 FCN。一般而言,訓(xùn)練 2000 個(gè)樣本僅需幾分鐘。

在這個(gè)初始設(shè)計(jì)中存在高偏差問題,該問題會(huì)在之后的迭代中解決。此外,還有一些邏輯和編程上的錯(cuò)誤導(dǎo)致網(wǎng)絡(luò)采取次優(yōu)行為。下圖是最佳早期設(shè)計(jì)的圖示:

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

早期 FCN 網(wǎng)絡(luò)的預(yù)測(cè)。第一列是輸入,接下來的 10 列是 10 個(gè)數(shù)字的預(yù)測(cè)掩碼。由于網(wǎng)絡(luò)設(shè)計(jì)的錯(cuò)誤產(chǎn)生了白色的背景。有一些數(shù)字能清晰地分割開,另一些則模糊不清。

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

上述樣本的真值。第一列是輸入,剩下的 10 列是已知掩碼。

在修正了錯(cuò)誤之后,該網(wǎng)絡(luò)能夠執(zhí)行近乎完美的分割。下圖是輸出的預(yù)測(cè)值:

重新發(fā)現(xiàn)語義分割,一文簡(jiǎn)述全卷積網(wǎng)絡(luò)

修改后的設(shè)計(jì)的預(yù)測(cè)結(jié)果。還是有一些模糊不清,但總體結(jié)果很好。

總結(jié)

所有的研究和實(shí)驗(yàn)大概花費(fèi)了兩周時(shí)間完成,并得到了可接受的結(jié)果。該問題很值得重新研究,因?yàn)樾〕叽缇W(wǎng)絡(luò)可以完成成百甚至上千次的實(shí)驗(yàn),而這在之前是不可能完成的,至少在沒有大量算力的情況下是不可能完成的。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多