task 7_修改 FCN(圖像讀取使用開源代碼)

1. FCN 論文學(xué)習(xí)

1.1 寫作背景

卷積網(wǎng)絡(luò)是視覺處理中可以有效生成多層特征的架構(gòu),是最前沿的技術(shù)。因此作者想構(gòu)造一個“全卷積網(wǎng)絡(luò)”,來處理任意尺寸的輸入圖片,并生成相應(yīng)尺寸的輸出。

通過改造當(dāng)下熱門的分類網(wǎng)絡(luò)(VGG,AlexNet,GoogleLeNet等),我們可以讓它們的架構(gòu)應(yīng)用于圖像分類任務(wù)。

該網(wǎng)絡(luò)得到很好的語義分割的效果,另外處理每張圖片的速度也很快只需要五分之一秒。
通過skip-architecture,我們可以把深層的輸出特征(更全面但更粗糙)與淺層的輸出特征(更細(xì)節(jié)但更精確)相結(jié)合。這種操作有利于生成更加準(zhǔn)確、細(xì)節(jié)飽滿的分割結(jié)果。

1.2 FCN架構(gòu)

卷積網(wǎng)絡(luò)里每一層的數(shù)據(jù)都是三維數(shù)組。如果這三維為h × w × d,則h和w是圖片的高和寬,d為圖片的特征或者是通道數(shù)。

第一層輸入圖片,圖片維度為[高, 寬, 色彩通道數(shù)]。網(wǎng)絡(luò)深層的每個數(shù)據(jù)都與網(wǎng)絡(luò)淺層的一片數(shù)據(jù)有關(guān),這就叫做感知野

  • FCN的輸入更靈活。普通的LeNet,AlexNet等卷積網(wǎng)絡(luò)只能接受固定維度的輸入,而FCN可以接受任意維度的輸入
  • FCN的運算更高效。
    • 進行預(yù)測時,在普通的GPU上對某個227227的圖片進行預(yù)測,AlexNet耗費1.2ms;而FCN從500500的圖片中生成10*10的輸出只需要22ms,其中的效率相差了5倍。
    • 進行后向傳播時,AlexNet需要2.4ms,而FCN只需要37ms。

1.3 把粗糙輸出轉(zhuǎn)換為原尺寸圖片

對于如何把coarse output轉(zhuǎn)換得到dense prediction,作者研究過3種方案:

  1. shift-and-stitch
  2. filter rarefaction
  3. deconvolution
    具體見分析三種粗糙圖片轉(zhuǎn)換為原尺寸圖案的方案

3.1 shift-and-stich

另外,此文還詳細(xì)分析了shift-and-stich方案:
shift-and-stich解釋

1.4 Patchwise training is loss sampling

參考[深度學(xué)習(xí)論文閱讀]Fully Convolutional Networks for Semantic Segmentation(FCN網(wǎng)絡(luò))
通常做語義分割的方法都是使用Patchwise訓(xùn)練,就是指將一張圖片中的重要部分裁剪下來進行訓(xùn)練以避免整張照片直接進行訓(xùn)練所產(chǎn)生的信息冗余,這種方法有助于快速收斂。

但是本文章提出直接使用整張圖片也許可能使效果更好而Patchwise可能使信息受損(所以此節(jié)名為Patchwise training is loss sampling)。

這里一個直覺得想法是一整張圖像可能是有空間相關(guān)性的,那么Patchwise就減少了這種相關(guān)性

1.5 Segmentation Architecture(skip Architecture)


FCN的skip Achitecture有三種架構(gòu):FCN-32s、FCN-16s和FCN-8s。skip Achitecture通過把深層數(shù)據(jù)的結(jié)果與淺層的準(zhǔn)確結(jié)果相結(jié)合,再恢復(fù)到原圖的輸出,可以生成更準(zhǔn)確的結(jié)果。

  1. FCN-32s是指用逆卷積把conv7放大到32倍。
  2. FCN-16s是指先用逆卷積把conv7放大到2倍,將放大結(jié)果與pool4的輸出相加,再把相加結(jié)果放大16倍。
  3. 同理,F(xiàn)CN-8s是指用逆卷積把conv7放大到2倍,將放大結(jié)果與pool4的輸出相加,再把相加結(jié)果用逆卷積放大兩倍,與pool3相加。最后把第二次的相加結(jié)果放大8倍到原來的圖像尺寸。

根據(jù)試驗,FCN-8s的效果最好,而如果再疊加層數(shù),反而效果變差了,所以論文做到此處就停止了。
這也是FCN提升預(yù)測結(jié)果最關(guān)鍵的部分。

2. FCN重寫

2.1 概括

在參考了github上別人的FCN框架后,我認(rèn)真研究了它的代碼,并結(jié)合自己的想法,重新寫了一遍。
我的代碼主要分為以下幾個模塊:

  1. FCN.py, FCN_down_sizing.py. FCN_down_sizing.py定義了FCN網(wǎng)絡(luò)中downsizing的部分,而FCN.py結(jié)合downsizing的部分來組裝FCN-8s, FCN-16s和FCN-32s
  2. read_MITSceneParsingData.py.用于為每個image找到對應(yīng)的annotation,并把這些關(guān)系組保存為一個.pickle文件,供處理圖片時讀取。
  3. BatchDatsetReader.py.給定image與annotation相互對應(yīng)的關(guān)系組,找到并讀取這些圖片的數(shù)據(jù)。
  4. FCN_train.py, FCN_test.py, FCN_infer.py. 顯然是用于train, test和infer的。

2.2 FCN.py, FCN_down_sizing.py

我把FCN網(wǎng)絡(luò)看做兩個部分:

  1. downsizing,通過卷積使矩陣的尺寸縮小
  2. upscaling,通過逆卷積使圖片恢復(fù)原本的尺寸。

不管是FCN-8s, FCN-16s還是FCN-32s,他們都需要用到把圖片downsize的過程,所以FCN_down_sizing.py定義了FCN中進行downsize的這個部分。
而FCN.py則利用FCN_down_sizing.py的部分組裝成FCN-8s, FCN-16s和FCN-32s(由于時間緣故,只完成了FCN-8s)。

  • downsize的部分是用vgg19搭建的(論文里是用vgg16,但效果差不多)。
  • FCN_down_sizing.py中的get_FCN_8s_net則實現(xiàn)了論文中的sky-architecture

2.2 read_MITSceneParsingData.py

用于從.pickle文件中讀取image-annotation的路徑數(shù)組,供BatchDatsetReader.py讀取圖片數(shù)據(jù)。
如果沒有.pickle文件,則要先生成一個.pickle文件。具體做法是:遍歷images目錄,對每個jpg圖片在annotations目錄中找到對應(yīng)的png圖像分割文件。由此生成image-annotation的文件名集合。
這樣,我們為所有的image都找到了對應(yīng)的annotation的路徑,就可以把它們存儲為.pickle文件,供日后訓(xùn)練用。

2.3 BatchDatsetReader.py

在開始訓(xùn)練之前要讀取所有的圖片和圖片分割。讀取.pickle文件,利用其中的信息可以找到所有的image和annotation并讀取為矩陣的形式。
這樣,image_list的維度為(num_images, height, width, 3), annotation_list的維度為(num_annotations, height, width, 1),且類型都是numpy.ndarray。

  • 由于訓(xùn)練的數(shù)據(jù)并非全部是Rgb的三通道圖,有些是灰度圖(只有單通道)。為了統(tǒng)一處理,要把這些灰度圖轉(zhuǎn)換為三通道的形式。

2.3 ImageReader.py

training中,通常的程序邏輯是這樣的:

  1. 生成image-annotation的文件名集合。遍歷images目錄,對每個jpg圖片在annotations目錄中找到對應(yīng)的png圖像分割文件。這樣,我們為所有的image都找到了對應(yīng)的annotation的路徑,就可以把它們存儲為.pickle文件,供日后訓(xùn)練用。
  2. 在開始訓(xùn)練之前要讀取所有的圖片和圖片分割。讀取.pickle文件,利用其中的信息可以找到所有的image和annotation并讀取為矩陣的形式。這樣,image_list的維度為(num_images, height, width, 3), annotation_list的維度為(num_annotations, height, width, 1),且類型都是numpy.ndarray。
    • 由于訓(xùn)練的數(shù)據(jù)并非全部是Rgb的三通道圖,有些是灰度圖(只有單通道)。為了統(tǒng)一處理,要把這些灰度圖轉(zhuǎn)換為三通道的形式。重寫時在此栽過跟頭。

3. 遇到的問題

3.1 問題1 image維度不統(tǒng)一

image數(shù)據(jù)大部分是三維的(h, w, 3),但有少部分是灰度圖,也就是二維的(h, w)
annotation數(shù)據(jù)則都是二維的(h, w)

因此處理image數(shù)據(jù)時,如果遇到二維的圖片,要先轉(zhuǎn)為三維且有3個通道的圖片。

3.2 問題2 scipy.misc.imresize is deprecated

問題描述
原作者的代碼中,圖片的變形使用的是scipy.misc.imresize函數(shù)。
但我發(fā)現(xiàn)這個函數(shù)除了對圖片變形,還會自行做一些多余的動作。它會把數(shù)組里的值標(biāo)準(zhǔn)歸一化到[0, 255]的區(qū)間內(nèi),破壞圖片原本的信息。

arr = np.array([[[100, 2, 220], [3, 4, 5]], [[1, 2, 3], [3, 4, 5]]])

print(type(arr))
print(arr.shape)

resize_size = 4
arr = misc.imresize(arr, [resize_size, resize_size], interp='nearest')
print(type(arr))
print(arr.shape)
print(arr)

輸出

<class 'numpy.ndarray'>
(2, 2, 3)
<class 'numpy.ndarray'>
(4, 4, 3)
[[[115   1 255]
  [115   1 255]
  [  2   3   5]
  [  2   3   5]]

 [[115   1 255]
  [115   1 255]
  [  2   3   5]
  [  2   3   5]]

 [[  0   1   2]
  [  0   1   2]
  [  2   3   5]
  [  2   3   5]]

 [[  0   1   2]
  [  0   1   2]
  [  2   3   5]
  [  2   3   5]]]

解決方法
最后查閱官方文檔才知道這個函數(shù)已經(jīng)被廢止。
于是我將對圖片的操作都改用skimage庫實現(xiàn)了。而對圖片的變形則使用skimage.transform.resize函數(shù)。

3.3 問題3 ValueError: could not broadcast input array from shape (224,224,3) into shape (224,224)

問題描述

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    reader = ImageReader("train")
  File "/root/Desktop/FCN/ImageReader.py", line 58, in __init__
    self.image_list = np.array([self.readImage(record["image"]) for record in self.records])
ValueError: could not broadcast input array from shape (224,224,3) into shape (224,224)

在改用skimage庫操作圖片后,出現(xiàn)了無法把元素合并到一個數(shù)組的問題。對image里的圖片的操作失敗了。

查閱stackoverflow的問題發(fā)現(xiàn)原來是元素的維度并不統(tǒng)一。

我原以為所有image里的圖片都是三通道的,也就是(h, w, 3)的。這樣如果我要得到固定尺寸的圖片(比如224 * 224),只需調(diào)用skimage.transform.resize,就能把圖片轉(zhuǎn)為(224, 224, 3)。理應(yīng)所有圖片都會被轉(zhuǎn)換成(224, 224, 3)的維度??墒羌热粓D片們無法共容在一個數(shù)組里,說明有的圖片沒有轉(zhuǎn)換成這種維度

問題原因
原來,image里并不是所有圖片都是(h, w, 3)形式的,有的圖片是灰度圖(在20210張圖片中有4張是灰度圖),也就是(h, w)形式。而我的代碼沒有考慮到這一點,導(dǎo)致這幾張灰度圖被轉(zhuǎn)換后的維度錯誤。

解決方法
對于這幾張灰度圖,需要將其轉(zhuǎn)換為三通道的形式。只需要把單通道上的值重復(fù)三次作為三個通道的值即可。

3.4 問題4 圖片轉(zhuǎn)換后內(nèi)容被破壞

問題描述


在給skimage.transform.resize添加reserve_range = True設(shè)置后,發(fā)現(xiàn)轉(zhuǎn)換后的圖片內(nèi)容完全被破壞。似乎維持值的范圍會破壞圖片的可見性。

問題原因

查閱了stackoverflow
原來pyplot.imshow只能顯示[0.0, 1.0]范圍的圖片,而reserve_range = True會使圖片仍然在[0, 255]范圍內(nèi),且數(shù)據(jù)類型為float64,被以[0.0, 1.0]的范圍來看待,這就無法正確顯示了。

另外,查閱官方文檔reserve_range參數(shù)

preserve_range : bool, optional
Whether to keep the original range of values. Otherwise, the input image is converted according to the conventions of img_as_float.
確實如果不設(shè)置reserve_range = True,函數(shù)會把值的范圍標(biāo)準(zhǔn)歸一化到[0.0, 1.0]內(nèi),也就是img_as_float.

問題解決

顯示圖片時先使用image = np.copy(old_image).astype('uint8'),把類型從float64轉(zhuǎn)換為uint8即可。

3.5 問題5 查看源代碼的卷積核維度

通過在源代碼中添加如下代碼可輸出各層卷積核的維度

輸出:

僅截取部分輸出

根據(jù)輸出,我發(fā)現(xiàn)源代碼使用的是VGG-19,而論文中使用的是VGG-16。兩者的效果應(yīng)該差不多,為了保持一致,我依舊按照VGG-19來疊加。

3.6 問題6 tf.layers.conv2d_transpose的放大倍數(shù)

tf.layers.conv2d_transpose只能指定strides來調(diào)整輸出圖片的尺寸。
strides = [2, 2]時放大兩倍,strides = [8, 8]時放大8倍

3.7 問題7 numpy array的特殊索引方式

代碼中此段是用來打亂images和annotations的,開始看的時候不懂,感覺這不符合Python的語法,后來查了官方文檔發(fā)現(xiàn),原來這是numpy.ndarray重載過的行為,是numpy.ndarray的特殊的索引方式。

基本用法如下:


所以,通過傳遞一

3.8 mean_iou過低

問題描述
發(fā)現(xiàn)不管如何調(diào)優(yōu),mean_iou的數(shù)值很低。
問題原因
原來是因為在縮放annotation的過程中,使用的方法會拉扯那些值,使得annotation出現(xiàn)了本來不存在的分類。比如原本annotation里的值只有3,4,5,經(jīng)過縮放,值被拉扯,變成了二點幾、三點幾、和六點幾,等等。而圖原本是沒有6這個分類的,這就導(dǎo)致mean_iou的計算出現(xiàn)巨大的偏差。
解決方法
圖片縮放函用回scipy.sisc.imresize,因為這個函數(shù)有按nearest模式縮放的功能,在縮放圖片的同時不改變圖片內(nèi)的值的種類。

感想

  1. 在測試的時候圖方便,總是讀取整個數(shù)據(jù)集,其中等待浪費了很長時間,經(jīng)常讀取完以后才發(fā)現(xiàn)bug。以后應(yīng)該先只讀取一部分,保證代碼正確運行,再讀取整個數(shù)據(jù)集。
  2. 代碼對內(nèi)存的優(yōu)化不好。由于每次測試都讀取整個數(shù)據(jù)集,有時候出現(xiàn)內(nèi)存用爆導(dǎo)致Memory Error的情況,以后編程要注意節(jié)省內(nèi)存空間
  3. 第一次從頭到尾地進行編程,有些手忙腳亂。以后應(yīng)當(dāng)先分析數(shù)據(jù)集的成分,構(gòu)建讀取器,再構(gòu)建神經(jīng)網(wǎng)絡(luò),讀取部分?jǐn)?shù)據(jù)進行測試。保證無bug后才對。

參考

相關(guān)閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容