學習資料:
本節代碼
什麼是遷移學習短視頻
Stanford 遷移學習閱讀
在上次的動畫簡介中,我們大概了解了一些遷移學習的原理和為什麼要使用遷移學習. 如果用一句話來概括遷移學習, 那務必就是:「為了偷懶, 在訓練好了的模型上接著訓練其他內容, 充分使用原模型的理解力」.有時候也是為了避免再次花費特別長的時間重複訓練大型模型.
CNN 通常都是大型模型, 下面我們拿 CNN 來舉個例子. 我訓練好了一個區分男人和女人的 CNN.接著來了個任務, 說我下個任務是區分照片中人的年齡. 這看似完全不相干的兩個模型, 但是我們卻可以運用到遷移學習,讓之前那個 CNN 當我們的初始模型, 因為區分男女的 CNN 已經對人類有了理解.基於這個理解開始訓練, 總比完全重新開始訓練強. 但是如果你下一個任務是區分飛機和大象.這個 CNN 可能就沒那麼有用了, 因為這個 CNN 可能並沒有對飛機大象有任何的理解.
這一次, 我們就來遷移一個圖片分類的 CNN (VGG).這個 VGG 在1000個類別中訓練過.我們提取這個 VGG 前面的 Conv layers, 重新組建後面的 fully connected layers, 讓它做一個和分類完全不相干的事.我們在網上下載那1000個分類數據中的貓和老虎的圖片, 然後偽造一些貓和老虎長度的數據.最後做到讓遷移後的網絡分辨出貓和老虎的長度 (regressor)。
為了達到這次的目的, 我們不需要下載所有的1000個分類的所有圖片, 只要找到自己感興趣的類就好 (老虎和貓).我選老虎和貓的目的就是因為他們是近親, 還是有點像的, 可以增加點難度. 如果是飛機和大象的話, 學習難度就被降低了.
上圖是這個網址,你能在 Download 的那個 tag 中, 找到所有圖片的 urls, 我將所有老虎和貓的 urls 文件給大家放在下面:
我們可以編一個 Python功能逐個下載裡面的圖片. 這個功能我定義成 download(). 下載好後就會被放在 data 這個文件夾中了.
因為有些圖片url已經過期了, 所以我自己也手動過濾了一遍, 將不是圖片的和404的圖片給清理掉了. 因為只有兩個類,圖片不是很多, 比較好清理. 有網友說一些很多連結和圖片已經」失聯」, 我把我收集到的圖片數據打包放在我的百度雲,如果用代碼下圖片感到有困難的同學們, 請直接在我百度雲下載吧.
因為現在我們不是預測分類結果了, 所以我偽造了一些體長的數據. 老虎通常要比貓長, 所以它們的 distribution 就差不多是下面這種結構(單位cm)
處理好圖片後, 我們可以開始弄 VGG 的 pre-trained model. 我使用的是machrisaa 改寫的VGG16 的代碼.和他提供的 VGG16 train 好了的 model parameters, 你可以在這裡下載 這些 parameters(有網友說這個文件下載不了,我把它放在了百度雲共享了).做好準備, 這個 parameter 文件有500+MB.可見一般 CNN 的 model 有多大.
為了做遷移學習, 我對他的 tensorflow VGG16 代碼進行了改寫.保留了所有 Conv 和 pooling 層, 將後面的所有 fc 層拆了, 改成可以被 train 的兩層, 輸出一個數字, 這個數字代表了這隻貓或老虎的長度.
class Vgg16:
def __init__():
# ...前面的層
pool5 = self.max_pool(conv5_3, 'pool5')
# pool5 是最後的 conv 出來的結果
self.flatten = tf.reshape(pool5, [-1, 7*7*512])
self.fc6 = tf.layers.dense(self.flatten, 256, tf.nn.relu, name='fc6')
self.out = tf.layers.dense(self.fc6, 1, name='out')
在 self.flatten 之前的 layers, 都是不能被 train 的. 而 tf.layers.dense() 建立的 layers 是可以被 train 的.到時候我們 train 好了, 再定義一個 Saver 來保存由 tf.layers.dense() 建立的 parameters.
class Vgg16:
...
def save(self, path='./for_transfer_learning/model/transfer_learn'):
saver = tf.train.Saver()
saver.save(self.sess, path, write_meta_graph=False)
因為有了訓練好了的 VGG16, 你就能將 VGG16 的 Conv 層想像成是一個 feature extractor, 提取或壓縮圖片中的特徵.和 Autoencoder 中的 encoder 類似.用這些提取的特徵來訓練後面的 regressor. 具體代碼在這,下面是簡寫版.
def train():
xs, ys = ...
vgg = Vgg16(vgg16_npy_path='./for_transfer_learning/vgg16.npy')
print('Net built')
for i in range(100):
b_idx = np.random.randint(0, len(xs), 6)
train_loss = vgg.train(xs[b_idx], ys[b_idx])
print(i, 'train loss: ', train_loss)
vgg.save('./for_transfer_learning/model/transfer_learn')
這裡我只 train 了 100次, 如果是重新開始 train 一個 CNN, 100次絕對少了. 而且我使用的是只有 CPU 的電腦,不好意思, 我暫時沒有合適的 GPU… 所以你暫時在 莫煩Python 中基本找不到關於圖像處理的教程… 不過!正因為 transfer learning 讓我不用從頭 train CNN, 所以我做了這個教程!否則, 我想用我的 CPU, 估計得一周才能 train 出來這個 VGG 吧.
我們現在已經遷移好了, train 好了後面的 fc layers, 也保存了後面的 fc 參數. 接著我們提取原始的 VGG16 前半部分參數和 train 好的後半部分參數.進行測試.
def eval():
vgg = Vgg16(vgg16_npy_path='./for_transfer_learning/vgg16.npy',
restore_from='./for_transfer_learning/model/transfer_learn')
vgg.predict(
['./for_transfer_learning/data/kittycat/000129037.jpg',
'./for_transfer_learning/data/tiger/391412.jpg'])
我輸入了一張貓, 一張老虎的圖, 這個 VGG 給我預測除了他們的長度.
可以想像, 要讓 VGG 達到這個目的, VGG必須懂得區分哪些是貓, 哪些是老虎, 而這個認知, 在原始的 VGG conv 層中就已經學出來了.所以如果我們拆了後面的層, 將後面的 classifier 變成 regressor, 花費相當少的時間就能訓練好.
遷移學習的玩法除了這樣, 還有很多種其他的玩法, 我在這個短視頻中介紹了一些.遷移學習還有一些細節的地方也可以在這裡關注一下,比如什麼時候要稍微 train 一下前面的 conv layers, 什麼時候要完全固定住前面的 conv layers.
記得把公號加星標,會第一時間收到通知。
創作不易,如果覺得有點用,希望可以隨手轉發或者」在看「,拜謝各位老鐵