引言
??本實驗基于FNC(全卷積神經(jīng)網(wǎng)絡)及PASCAL-VOC數(shù)據(jù)集做圖像語義分割刀崖。圖像語義分割(Semantic Segmentation)是圖像處理和是機器視覺技術中關于圖像理解的重要一環(huán)撵割,也是 AI 領域中一個重要的分支。語義分割即是對圖像中每一個像素點進行分類宠叼,確定每個點的類別(如屬于背景咱筛、人或車等),從而進行區(qū)域劃分富蓄。目前,語義分割已經(jīng)被廣泛應用于自動駕駛慢逾、無人機落點判定等場景中立倍。
??圖像分類是圖像級別的:圖像中的局部抽象特征對物體的大小、位置和方向等敏感性更低侣滩,從而有助于分類性能的提高口注。這些抽象的特征對分類很有幫助,可以很好地判斷出一幅圖像中包含什么類別的物體君珠。
??圖像語義分割是像素級別的:與分類不同的是寝志,語義分割需要判斷圖像每個像素點的類別,進行精確分割策添。但是由于CNN在進行convolution和pooling過程中丟失了圖像細節(jié)材部,即feature map size逐漸變小,所以不能很好地指出物體的具體輪廓唯竹、指出每個像素具體屬于哪個物體乐导,無法做到精確的分割。
??Jonathan Long等人提出了Fully Convolutional Networks(FCN)用于圖像語義分割浸颓。自從提出后物臂,F(xiàn)CN已經(jīng)成為語義分割的基本框架,后續(xù)算法其實都是在這個框架中改進而來产上。本實驗就基于FNC做圖像語義分割棵磷。
卷積神經(jīng)網(wǎng)絡
??卷積神經(jīng)網(wǎng)絡(CNN)是一種經(jīng)典的深度學習架構,受生物自然視覺認知機制啟發(fā)而來晋涣。1959年仪媒,Hubel & Wiesel 發(fā)現(xiàn),貓對于物體的感知在視覺皮層中是分層處理的谢鹊。受此啟發(fā)算吩,1980年 Kunihiko Fukushima 提出了CNN的前身——neocognitron 。
??20世紀 90 年代撇贺,LeCun等人發(fā)表論文赌莺,確立了CNN的現(xiàn)代結構,后來又對其進行完善松嘶。他們設計了一種多層的人工神經(jīng)網(wǎng)絡艘狭,取名叫做LeNet-5,可以對手寫數(shù)字做分類翠订。和其他神經(jīng)網(wǎng)絡一樣巢音, LeNet-5 也能使用反向傳播算法訓練。
??卷積神經(jīng)網(wǎng)絡能夠得出原始圖像的有效特征尽超,因而其能夠直接從原始像素中官撼,經(jīng)過極少的預處理,識別視覺層面的規(guī)律似谁。然而傲绣,由于當時缺乏大規(guī)模訓練數(shù)據(jù)掠哥,計算機的計算能力不足等因素,卷積神經(jīng)網(wǎng)絡并未得到科學界的重視。
??進入21世紀后,隨著科學的發(fā)展志鞍,計算機的計算能力也逐漸得到了巨大的提升古涧,而卷積神經(jīng)網(wǎng)絡的在圖像分類問題上的應用也開始逐漸得到人們的重視。ImageNet是被譽為圖像分類領域里的奧林匹克比賽,2012年,通過卷積神經(jīng)網(wǎng)絡構建的圖像分類算法在這場比賽中脫穎而出,將圖像分類識別的準確度一下提升了許多牵咙,卷積神經(jīng)網(wǎng)絡,也因此死灰復燃攀唯,再次得到了學術界的關注洁桌。
??此后,卷積神經(jīng)網(wǎng)絡也在不斷地被計算機科學家們優(yōu)化著革答,出現(xiàn)了諸如GoogLeNet战坤、VGG等較之前更為優(yōu)秀結構。計算機圖像分類能力也因此得到巨大提升残拐,這是這種強大的圖像分類途茫、處理能力的誕生,才推動了現(xiàn)代深度增強學習的發(fā)展溪食。
??卷積神經(jīng)網(wǎng)絡的基本結構一般包括三層囊卜,特征提取層,特征映射層错沃,以及全連接層栅组。對特征提取層來講,每一個神經(jīng)元的輸入與前一層的局部接受域相連枢析,并提取該局部的特征玉掸。當該局部特征被提取后,它與其它特征間的位置關系也隨之確定下來醒叁;對特征映射層來講司浪,網(wǎng)絡的每個計算層由多個特征映射組成,每個特征映射是一個平面把沼,平面上所有神經(jīng)元的權值相等啊易。特征映射結構采用影響函數(shù)核小的sigmoid函數(shù)作為卷積網(wǎng)絡的激活函數(shù),使得特征映射具有位移不變性饮睬。此外租谈,由于一個映射面上的神經(jīng)元共享權值,因而減少了網(wǎng)絡自由參數(shù)的個數(shù)捆愁。卷積神經(jīng)網(wǎng)絡中的每一個卷積層都緊跟著一個用來求局部平均與二次提取的計算層割去,這種特有的兩次特征提取結構減小了特征分辨率窟却。最后,還包括一層全連接層劫拗。
??卷積神經(jīng)網(wǎng)絡主要用來識別位移间校、縮放及其他形式扭曲不變性的二維圖形。由于卷積神經(jīng)網(wǎng)絡的特征檢測層通過訓練數(shù)據(jù)進行學習页慷,所以在使用卷積神經(jīng)網(wǎng)絡時,避免了顯示的特征抽取胁附,而隱式地從訓練數(shù)據(jù)中進行學習酒繁;再者由于同一特征映射面上的神經(jīng)元權值相同,所以網(wǎng)絡可以并行學習控妻,這也是卷積網(wǎng)絡相對于神經(jīng)元彼此相連網(wǎng)絡的一大優(yōu)勢州袒。卷積神經(jīng)網(wǎng)絡以其局部權值共享的特殊結構在語音識別和圖像處理方面有著獨特的優(yōu)越性,其布局更接近于實際的生物神經(jīng)網(wǎng)絡弓候,權值共享降低了網(wǎng)絡的復雜性郎哭,特別是多維輸入向量的圖像可以直接輸入網(wǎng)絡這一特點避免了特征提取和分類過程中數(shù)據(jù)重建的復雜度。
??卷積神經(jīng)網(wǎng)絡的三種組成部分:卷積層菇存、池化層夸研,以及全連接層如圖2-1所示,該圖展示了一個較為完整的卷積神經(jīng)網(wǎng)絡結構圖依鸥。
卷積層
??卷積神經(jīng)網(wǎng)絡處理的對象是圖片亥至,而圖片在計算機上又是以像素值的形式存取的。對于彩色圖片來說贱迟,通常其具有RGB三個通道姐扮,對于灰色通道來講,其只有一個灰度值通道衣吠。當知道了圖像每一個像素點的值茶敏,我們就能還原出圖像的原貌。而卷積神經(jīng)網(wǎng)絡缚俏,就是對這些像素值進行處理的惊搏,本質上還是進行數(shù)值的計算。
??圖2-2展示了一個簡明的卷積計算袍榆。左側方框中胀屿,標有0和1的方框是我們的數(shù)據(jù),這里包雀,我們將其當作圖像像素值宿崭,圖中較深陰影部分,即為卷積核才写,卷積核在圖像滾動葡兑,每滾動一次奖蔓,計算一次卷積核的值與對應位置像素值的乘積,再相加讹堤。得到圖片右側的卷積特征(Convolved Feature)吆鹤。
池化層
??在通過卷積獲得了特征 (features) 之后,下一步我們希望利用這些特征去做分類洲守。理論上講疑务,人們可以用所有提取得到的特征去訓練分類器,例如 softmax 分類器梗醇,但這樣做知允,面臨計算量的挑戰(zhàn)。例如:對于一個 96×96 像素的圖像叙谨,假設我們已經(jīng)學習得到了400個定義在8×8輸入上的特征温鸽,每一個特征和圖像卷積都會得到一個 (96 ? 8 + 1) × (96 ? 8 + 1) = 7921 維的卷積特征,由于有 400 個特征手负,所以每個樣例都會得到一個 892 × 400 = 3,168,400 維的卷積特征向量涤垫。學習一個擁有超過 3 百萬特征輸入的分類器十分不便,并且容易出現(xiàn)過擬合 (over-fitting)竟终。
??為了解決這個問題蝠猬,我們需要對不同位置的特征進行聚合統(tǒng)計,例如衡楞,人們可以計算圖像一個區(qū)域上的某個特定特征的平均值 (或最大值)吱雏。這些概要統(tǒng)計特征不僅具有低得多的維度 (相比使用所有提取得到的特征),同時還會改善結果(不容易過擬合)瘾境。這種聚合的操作就叫做池化 (pooling)歧杏,有時也稱為平均池化或者最大池化。
全連接層
??為了使我們的卷積神經(jīng)網(wǎng)絡能夠起到分類器的作用迷守,一般地犬绒,我們需要在卷積神經(jīng)網(wǎng)絡的后面來添加全連接層。如果說卷積層兑凿、池化層等操作是將原始數(shù)據(jù)映射至隱層特征空間的話凯力,全連接層則起到將學到的“分布式特征表示”映射到樣本標記空間的作用。
優(yōu)化算法與反向傳播算法
??以梯度下降法為代表的很多優(yōu)化算法可以用來解決上述的最小化問題礼华。諸如AdamOptimizer咐鹤,RMSPropOptimizer等優(yōu)化器,在神經(jīng)網(wǎng)絡框架TensorFlow圣絮、Keras中都可以直接調用祈惶,本課題中,我們選用AdamOptimizer對神經(jīng)網(wǎng)絡進行優(yōu)化。
??Adam 的全稱為Adaptive moment estimation捧请,即自適應矩估計凡涩。在概率論中,矩的含義是:如果一個隨機變量 X 服從某個分布活箕,X 的一階矩是筑舅,也就是樣本平均值误墓,X 的二階矩即為,也就是樣本平方的平均值。Adam 算法根據(jù)損失函數(shù)對每個參數(shù)的梯度的一階矩估計和二階矩估計動態(tài)調整針對于每個參數(shù)的學習速率。Adam 也是基于梯度下降的方法狠半,但是每次迭代參數(shù)的學習步長都有一個確定的范圍,不會因為很大的梯度導致很大的學習步長,參數(shù)的值比較穩(wěn)定。
??在優(yōu)化過程中,這些算法通常需要求解偏導數(shù)抛虫。反向傳播算法就是用來計算這些偏導數(shù)的。
1986年伞矩,深度學習之父Hinton湿诊,和他的合作者發(fā)表了論文Learning Representations by Back-propagating errors, 首次系統(tǒng)地描述了如何利用反向傳播算法(Backpropagation)有訓練神經(jīng)網(wǎng)絡. 從這一年開始,反向傳播算法在有監(jiān)督的神經(jīng)網(wǎng)絡算法中占著核心地位枫吧。它描述了如何利用錯誤信息浦旱,從最后一層(輸出層)開始到第一個隱層,逐步調整權值參數(shù)九杂,達到學習的目的颁湖。
??反向傳播算法是目前用來訓練人工神經(jīng)網(wǎng)絡(ANN)的最常用且最有效的算法。其主要思想是:
??(1)將訓練集數(shù)據(jù)輸入到ANN的輸入層例隆,經(jīng)過隱藏層甥捺,最后達到輸出層并輸出結果,這是ANN的前向傳播過程镀层;
??(2)由于ANN的輸出結果與實際結果有誤差镰禾,則計算估計值與實際值之間的誤差皿曲,并將該誤差從輸出層向隱藏層反向傳播,直至傳播到輸入層吴侦;
??(3)在反向傳播的過程中屋休,根據(jù)誤差調整各種參數(shù)的值;不斷迭代上述過程备韧,直至收斂劫樟。
FCN網(wǎng)絡結構
??1.image經(jīng)過多個conv和+一個max pooling變?yōu)閜ool1 feature,寬高變?yōu)?/2 (conv只提特征织堂,pool進行下降操作)
??2.pool1 feature再經(jīng)過多個conv+一個max pooling變?yōu)閜ool2 feature叠艳,寬高變?yōu)?/4
??3.pool2 feature再經(jīng)過多個conv+一個max pooling變?yōu)閜ool3 feature,寬高變?yōu)?/8
??4.pool3 feature再經(jīng)過多個conv+一個max pooling變?yōu)閜ool4 feature易阳,寬高變?yōu)?/16
??5.pool4 feature再經(jīng)過多個conv+一個max pooling變?yōu)閜ool5 feature附较,寬高變?yōu)?/32
??直接對pool4 feature進行32倍上采樣獲得32x upsampled feature,再對32x upsampled feature每個點做softmax prediction獲得32x upsampled feature prediction(即分割圖)潦俺。
FCN網(wǎng)絡結構的Python代碼解析
??對輸入圖像做卷積拒课,卷積核個數(shù)為64個,卷積核大小為(3, 3),過濾的模式為same事示,做兩次這樣的卷積捕发,之后為空域信號施加最大值池化,池化窗口大小為(2, 2)將使圖片在兩個維度上均變?yōu)樵L的一半很魂,激活函數(shù)選擇ReLu,得到layer_3:
layer_1 = Convolution2D(64, (3, 3), activation='relu', padding='same', name='Conv1Block1')(input_image)
layer_2 = Convolution2D(64, (3, 3), activation='relu', padding='same', name='Conv2Block1')(layer_1)
layer_3 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock1')(layer_2)
??之后再對其做一次類似的操作檐涝,選取卷積核個數(shù)為128個遏匆,激活函數(shù)選擇ReLu,得到layer_6:
layer_4 = Convolution2D(128, (3, 3), activation='relu', padding='same', name='Conv1Block2')(layer_3)
layer_5 = Convolution2D(128, (3, 3), activation='relu', padding='same', name='Conv2Block2')(layer_4)
layer_6 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock2')(layer_5)
??之后再對其做一次類似的操作谁榜,選取卷積核個數(shù)為256個幅聘,做三次卷積一次最大池化,激活函數(shù)選擇ReLu窃植,得到layer_10:
layer_7 = Convolution2D(256, (3, 3), activation='relu', padding='same', name='Conv1Block3')(layer_6)
layer_8 = Convolution2D(256, (3, 3), activation='relu', padding='same', name='Conv2Block3')(layer_7)
layer_9 = Convolution2D(256, (3, 3), activation='relu', padding='same', name='Conv3Block3')(layer_8)
layer_10 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock3')(layer_9)
??之后再對其做一次類似的操作帝蒿,選取卷積核個數(shù)為512個,做三次卷積一次最大池化巷怜,激活函數(shù)選擇ReLu葛超,得到layer_14:
layer_11 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv1Block4')(layer_10)
layer_12 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv2Block4')(layer_11)
layer_13 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv3Block4')(layer_12)
layer_14 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock4')(layer_13)
??之后再對其做一次類似的操作,選取卷積核個數(shù)為512個延塑,做三次卷積一次最大池化绣张,激活函數(shù)選擇ReLu,得到layer_18:
layer_15 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv1Block5')(layer_14)
layer_16 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv2Block5')(layer_15)
layer_17 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv3Block5')(layer_16)
layer_18 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock5')(layer_17)
??之后我們再次對其卷積关带,不做全連接侥涵,來獲取對每個像素的預測分數(shù),選取卷積核個數(shù)為4096個,大小為(7, 7)做第一次卷積芜飘,之后再選取卷積核個數(shù)同樣為4096個大小為(1, 1)得到layer_20务豺,再做一個卷積核為21個,大小(1, 1)的卷積嗦明,輸出得到layer_21:
#Making the network fully convolutional
layer_19=Convolution2D(4096,kernel_size=(7,7),padding="same",activation="relu",name="FullConv1")(layer_18)
layer_20=Convolution2D(4096,kernel_size=(1,1),padding="same",activation="relu",name="FullConv2")(layer_19)
#For obtaining the semantic segmentation scores
layer_21=Convolution2D(21,kernel_size=(1,1),padding="same",activation="relu",name="Score1")(layer_20)
??之后進行裝置卷積:
layer_22 = Deconvolution2D(21,kernel_size=(4,4),strides = (2,2),padding = "valid",activation=None,name = "Score2")(layer_21)
??裁剪一下:
layer_23 = Cropping2D(cropping=((0,2),(0,2)))(layer_22)
??跳躍結構(Skip Architecture)笼沥,將全卷積后的結果上采樣后得到的結果通常是很粗糙的。所以這一結構主要是用來優(yōu)化最終結果的招狸,思路就是將不同池化層的結果進行上采樣敬拓,然后結合這些結果來優(yōu)化輸出:
skip_con=Convolution2D(21,kernel_size=(1,1),padding="same",activation=None, name="SkipConn")
summed=add(inputs = [skip_con(layer_14),layer_23])
??上采樣得到輸出:
layer_24=Deconvolution2D(21,kernel_size=(32,32),strides=(16,16),padding="valid",activation = None,name = "Upsampled")
??裁剪一下使其適應輸出:
crop = Cropping2D(cropping = ((0,16),(0,16)))
??定義網(wǎng)絡模型:
model = Model(inputs = input_image, outputs = crop(layer_24(summed)))
??之后加載訓練好的模型即可跑前向網(wǎng)絡,獲得預測輸出結果裙戏,并將其顯示出來:
model = Model(inputs = input_image, outputs = crop(layer_24(summed)))
model.summary()
model.load_weights("weights.h5")
test_image = Image.open('TestingImages/2007_000170.jpg')
test_image = test_image.resize((512,512))
image_arr = np.array(test_image).astype(np.float32)
image_arr = np.expand_dims(image_arr, axis=0)
preds=model.predict(image_arr)
imclass = np.argmax(preds, axis=3)[0,:,:]
plt.figure(figsize = (15, 7))
plt.subplot(1,3,1)
plt.imshow( np.asarray(test_image) )
plt.subplot(1,3,2)
plt.imshow( imclass )
plt.subplot(1,3,3)
plt.imshow( np.asarray(test_image) )
masked_imclass = np.ma.masked_where(imclass == 0, imclass)
plt.imshow( imclass, alpha=0.5 )
plt.show()
??完整Python代碼如下所示:
import numpy as np
from keras.models import Model
from keras.layers import Convolution2D, MaxPooling2D, Deconvolution2D, Cropping2D
from keras.layers import Input, add
import matplotlib.pyplot as plt
from PIL import Image
input_image = Input(shape = (512,512,3))
# Block 1
layer_1 = Convolution2D(64, (3, 3), activation='relu', padding='same', name='Conv1Block1')(input_image)
layer_2 = Convolution2D(64, (3, 3), activation='relu', padding='same', name='Conv2Block1')(layer_1)
layer_3 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock1')(layer_2)
# Block 2
layer_4 = Convolution2D(128, (3, 3), activation='relu', padding='same', name='Conv1Block2')(layer_3)
layer_5 = Convolution2D(128, (3, 3), activation='relu', padding='same', name='Conv2Block2')(layer_4)
layer_6 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock2')(layer_5)
#Block 3
layer_7 = Convolution2D(256, (3, 3), activation='relu', padding='same', name='Conv1Block3')(layer_6)
layer_8 = Convolution2D(256, (3, 3), activation='relu', padding='same', name='Conv2Block3')(layer_7)
layer_9 = Convolution2D(256, (3, 3), activation='relu', padding='same', name='Conv3Block3')(layer_8)
layer_10 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock3')(layer_9)
#Block 4
layer_11 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv1Block4')(layer_10)
layer_12 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv2Block4')(layer_11)
layer_13 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv3Block4')(layer_12)
layer_14 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock4')(layer_13)
#Block 5
layer_15 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv1Block5')(layer_14)
layer_16 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv2Block5')(layer_15)
layer_17 = Convolution2D(512, (3, 3), activation='relu', padding='same', name='Conv3Block5')(layer_16)
layer_18 = MaxPooling2D((2, 2), strides=(2, 2), name='MaxPoolBlock5')(layer_17)
#Making the network fully convolutional
layer_19 = Convolution2D(4096,kernel_size=(7,7),padding = "same",activation = "relu",name = "FullConv1")(layer_18)
layer_20 = Convolution2D(4096,kernel_size=(1,1),padding = "same",activation = "relu",name = "FullConv2")(layer_19)
#For obtaining the semantic segmentation scores
layer_21 = Convolution2D(21,kernel_size=(1,1),padding="same",activation="relu",name = "Score1")(layer_20)
layer_22 = Deconvolution2D(21,kernel_size=(4,4),strides = (2,2),padding = "valid",activation=None,name = "Score2")(layer_21)
#Cropping the image to make it compatible for addition
layer_23 = Cropping2D(cropping=((0,2),(0,2)))(layer_22)
#Adding a skip connection
skip_con = Convolution2D(21,kernel_size=(1,1),padding = "same",activation=None, name = "SkipConn")
#Adding the layers
summed = add(inputs = [skip_con(layer_14),layer_23])
#Upsampling the output
layer_24 = Deconvolution2D(21,kernel_size=(32,32),strides = (16,16),padding = "valid",activation = None,name = "Upsampled")
#Cropping the output to match the input size
crop = Cropping2D(cropping = ((0,16),(0,16)))
#Defining the model with the layers
model = Model(inputs = input_image, outputs = crop(layer_24(summed)))
model.summary()
model.load_weights("weights.h5")
test_image = Image.open('TestingImages/2007_000256.jpg')
test_image = test_image.resize((512,512))
image_arr = np.array(test_image).astype(np.float32)
image_arr = np.expand_dims(image_arr, axis=0)
preds=model.predict(image_arr)
imclass = np.argmax(preds, axis=3)[0,:,:]
plt.figure(figsize = (15, 7))
plt.subplot(1,3,1)
plt.imshow( np.asarray(test_image) )
plt.subplot(1,3,2)
plt.imshow( imclass )
plt.subplot(1,3,3)
plt.imshow( np.asarray(test_image) )
masked_imclass = np.ma.masked_where(imclass == 0, imclass)
plt.imshow( imclass, alpha=0.5 )
plt.show()
??其網(wǎng)絡結構如圖2-3所示:
??其實驗結果如圖2-4所示:
??完整實驗代碼乘凸,公眾號后臺回復:數(shù)字圖像作業(yè)二。
我的微信公眾號名稱:深度學習與先進智能決策
微信公眾號ID:MultiAgent1024
公眾號介紹:主要研究分享深度學習累榜、機器博弈营勤、強化學習等相關內容!期待您的關注壹罚,歡迎一起學習交流進步葛作!