完全版見github:TransforLearning
零、遷移學習
將一個領域的已經成熟的知識應用到其他的場景中稱為遷移學習。用神經網絡的角度來表述,就是一層層網絡中每個節點的權重從一個訓練好的網絡遷移到一個全新的網絡里,而不是從頭開始,為每特定的個任務訓練一個神經網絡。
假設你已經有了一個可以高精確度分辨貓和狗的深度神經網絡,你之后想訓練一個能夠分別不同品種的狗的圖片模型,你需要做的不是從頭訓練那些用來分辨直線,銳角的神經網絡的前幾層,而是利用訓練好的網絡,提取初級特征,之后只訓練最后幾層神經元,讓其可以分辨狗的品種。節省資源是遷移學習最大意義之一,舉圖像識別中最常見的例子,訓練一個神經網絡。來識別不同的品種的貓,你若是從頭開始訓練,你需要百萬級的帶標注數據,海量的顯卡資源。而若是使用遷移學習,你可以使用Google發布的Inception或VGG16這樣成熟的物品分類的網絡,只訓練最后的softmax層,你只需要幾千張圖片,使用普通的CPU就能完成,而且模型的准確性不差。
使用遷徙學習時要注意,本來預訓練的神經網絡要和當前的任務差距不大,不然遷徙學習的效果會很差。例如如果你要訓練一個神經網絡來識別肺部X光片中是否包含腫瘤,那么使用VGG16的網絡就不如使用一個已訓練好的判斷腦部是否包含腫瘤的神經網絡。后者與當前的任務有相似的場景,很多底層的神經員可以做相同的事,而用來識別日常生活中照片的網絡,則難以從X光片中提取有效的特征。
另一種遷移學習的方法是對整個網絡進行微調(fine turing),假設你已訓練好了識別貓品種的神經網絡,你的網絡能對50種貓按品種進行分類。接下來你想對網絡進行升級,讓其能夠識別100種貓,這時你不應該只訓練網絡的最后一層,而應該逐層對網絡中每個節點的權重進行微調。顯然,只訓練最后幾層,是遷移學習最簡單的1.0版,而對節點權重進行微調,就是更難的2.0版,通過將其他層的權重固定,只訓練一層這樣的逐層訓練,可以更好的完成上述任務。
遷移方式和數據集規模關系

1)右下角場景,待訓練的數據集較小,已訓練的模型和當前任務相似。此時可以只是重新訓練已有模型的靠近輸出的幾層,例如將ImageNet中輸出層原來可以判別一萬種輸出的網絡改的只能判別貓的品種,從而利用已有網絡來做低層次的特征提取。
2)左下角場景,待訓練的數據集較小,已訓練的模型和當前任務場景差距較大。例如你有的已訓練網絡能識別出白天高速路上的違章車輛,你需要訓練一個能識別出夜間違章車輛的模型,由於不管白天夜晚,交通規則是沒有變化的,所以你需要將網絡靠近輸入的那幾層重新訓練,等到新的網絡能夠提取出夜間車輛的基本信息后,就可以借用已有的,在大數據集下訓練好的神經網絡來識別違章車輛,而不用等夜間違章的車輛的照片積累的足夠多之后再重新訓練。
3)左上角場景,待訓練的數據集較大,已有的模型和新模型的數據差異度很高。此時應該做的是從頭開始,重新訓練。
4)右上角場景,待訓練的數據集較大,已有模型的訓練數據和現有的訓練數據類似。此時應該使用原網絡的結構微調。
一、實驗目的
使用google已經訓練好的模型,將最后的全連接層修改為我們自己的全連接層,將原有的1000分類分類器修改為我們自己的5分類分類器,利用原有模型的特征提取能力實現我們自己數據對應模型的快速訓練。實際中對於一個陌生的數據集,原有模型經過不高的迭代次數即可獲得很好的准確率。
二、代碼實戰
實機文件夾如下:

花朵圖片數據下載:
curl -O http://download.tensorflow.org/example_images/flower_photos.tgz
已經訓練好的Inception-v3的1000分類模型下載:
wget https://storage.googleapis.com/download.tensorflow.org/models/inception_dec_2015.zip
遷移學習代碼以及使用指南見github,本次的代碼可以將新保存的模型遷移到自己的數據集上,並對自己的數據進行預測。
新添加的測試部分,直接運行TransferLearning_reload.py即可,輸出如下,

第二行白字的五個值對應第一行的5個分類的概率。
三、問題&建議
1.建議從main函數開始閱讀,跳到哪里讀到那里;
2.我給的注釋很詳盡,原書《TensorFlow實戰Google深度學習框架》也有更為詳盡的注釋,所以這里不多說了
之前本部分對輸入圖片的過程進行了分析,當時水平有限,現在看來很幼稚,實際上分析一下圖上節點即可了解:
- InceptionV3接受二進制數據即可自行解碼,即接收open().read()的二進制流即可
- 保存的模型文件Graph包含了InceptionV3和新的classer,但是兩者是隔離的,這是由於程序中並沒將兩者聯通,是先把InceptionV3的瓶頸張量feed出來,然后是用這個數組去feed新的classer,但是由於saver、InceptionV3、classer使用的sess是同一個,所以最終兩者都保存在了model模型中
