CHAPTER 11-Training Deep Neural Nets-part2

本篇文章是個人翻譯的,如有商業用途,請通知本人謝謝.


Reusing Pretrained Layers (重用預訓練層)
從零開始訓練一個非常大的DNN通常不是一個好主意,相反,您應該總是嘗試找到一個現有的神經網絡來完成與您正在嘗試解決的任務類似的任務,然後重新使用這個較低層的 網絡:這就是所謂的遷移學習。 這不僅會大大加快培訓速度,還將需要更少的培訓數據。

例如,假設您可以訪問經過培訓的DNN,將圖片分爲100個不同的類別,包括動物,植物,車輛和日常物品。 您現在想要訓練一個DNN來對特定類型的車輛進行分類。 這些任務非常相似,因此您應該嘗試重新使用第一個網絡的一部分(請參見圖11-4)。


如果新任務的輸入圖像與原始任務中使用的輸入圖像的大小不一致,則必須添加預處理步驟以將其大小調整爲原始模型的預期大小。 更一般地說,如果輸入具有類似的低級層次的特徵,則遷移學習將很好地工作。


Reusing a TensorFlow Model
如果原始模型使用TensorFlow進行訓練,則可以簡單地將其恢復並在新任務上進行訓練:


[...# construct the original model 
with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")
    # continue training the model...

完整代碼:


n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 50
n_hidden3 = 50
n_hidden4 = 50
n_outputs = 10

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2")
    hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3")
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4")
    hidden5 = tf.layers.dense(hidden4, n_hidden5, activation=tf.nn.relu, name="hidden5")
    logits = tf.layers.dense(hidden5, n_outputs, name="outputs")

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

learning_rate = 0.01
threshold = 1.0

optimizer = tf.train.GradientDescentOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(loss)
capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var)
              for grad, var in grads_and_vars]
training_op = optimizer.apply_gradients(capped_gvs)

init = tf.global_variables_initializer()
saver = tf.train.Saver()

with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_new_model_final.ckpt")


但是,一般情況下,您只需要重新使用原始模型的一部分(就像我們將要討論的那樣)。 一個簡單的解決方案是將Saver配置爲僅恢復原始模型中的一部分變量。 例如,下面的代碼只恢復隱藏的層1,2和3:

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300 # reused
n_hidden2 = 50  # reused
n_hidden3 = 50  # reused
n_hidden4 = 20  # new!
n_outputs = 10  # new!

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")       # reused
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # reused
    hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # reused
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # new!
    logits = tf.layers.dense(hidden4, n_outputs, name="outputs")                         # new!

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)

[...] # build new model with the same definition as before for hidden layers 1-3 
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]") # regular expression
reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3

init = tf.global_variables_initializer()
saver = tf.train.Saver()

with tf.Session() as sess:
    init.run()
    restore_saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):                                      # not shown in the book
        for iteration in range(mnist.train.num_examples // batch_size): # not shown
            X_batch, y_batch = mnist.train.next_batch(batch_size)      # not shown
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})  # not shown
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,  # not shown
                                                y: mnist.test.labels}) # not shown
        print(epoch, "Test accuracy:", accuracy_val)                   # not shown

    save_path = saver.save(sess, "./my_new_model_final.ckpt")
首先我們建立新的模型,確保複製原始模型的隱藏層1到3.我們還創建一個節點來初始化所有變量。 然後我們得到剛剛用“trainable = True”(這是默認值)創建的所有變量的列表,我們只保留那些範圍與正則表達式“hidden [123]”相匹配的變量(即,我們得到所有可訓練的 隱藏層1到3中的變量)。 接下來,我們創建一個字典,將原始模型中每個變量的名稱映射到新模型中的名稱(通常需要保持完全相同的名稱)。 然後,我們創建一個Saver,它將只恢復這些變量,並且創建另一個Saver來保存整個新模型,而不僅僅是第1層到第3層。然後,我們開始一個會話並初始化模型中的所有變量,然後從 原始模型的層1到3.最後,我們在新任務上訓練模型並保存。

任務越相似,您可以重複使用的層越多(從較低層開始)。 對於非常相似的任務,您可以嘗試保留所有隱藏的圖層,然後替換輸出圖層。


Reusing Models from Other Frameworks
如果模型是使用其他框架進行訓練的,則需要手動加載權重(例如,如果使用Theano訓練,則使用Theano代碼),然後將它們分配給相應的變量。 這可能是相當乏味的。 例如,下面的代碼顯示瞭如何複製使用另一個框架訓練的模型的第一個隱藏層的權重和偏置:



Freezing the Lower Layers
第一個DNN的低層可能已經學會了檢測圖片中的低級特徵,這將在兩個圖像分類任務中有用,因此您可以按照原樣重新使用這些圖層。 在訓練新的DNN時,“凍結”權重通常是一個好主意:如果下層權重是固定的,那麼上層權重將更容易訓練(因爲他們不需要學習一個移動的目標)。 要在訓練期間凍結較低層,最簡單的解決方案是給優化器列出要訓練的變量,不包括來自較低層的變量:


第一行獲得隱藏層3和4以及輸出層中所有可訓練變量的列表。 這留下了隱藏層1和2中的變量。接下來,我們將這個受限制的可列表變量列表提供給optimizer的minimize()函數。噹噹! 現在,圖層1和圖層2被凍結:在訓練過程中不會發生變化(通常稱爲凍結圖層)。


Caching the Frozen Layers (緩存凍層)

由於凍結層不會改變,因此可以爲每個訓練實例緩存最上面的凍結層的輸出。 由於訓練貫穿整個數據集很多次,這將給你一個巨大的速度提升,因爲每個訓練實例只需要經過一次凍結層(而不是每個時期一次)。 例如,你可以先運行整個訓練集(假設你有足夠的內存):

hidden2_outputs = sess.run(hidden2, feed_dict={X: X_train})
然後在訓練過程中,不是建立批次的訓練實例,而是從隱藏層2建立成批的輸出,並將它們提供給訓練操作:

最後一行運行先前定義的訓練操作(凍結圖層1和2),並從第二個隱藏層(以及該批次的目標)爲其輸出一批輸出。 因爲我們給TensorFlow隱藏層2的輸出,所以它不會去評估它(或者它所依賴的任何節點)。


Tweaking, Dropping, or Replacing the Upper Layers (調整,刪除或替換上層)

原始模型的輸出層通常應該被替換,因爲對於新的任務來說,最有可能沒有用處,甚至可能沒有適合新任務的輸出。
類似地,原始模型的上層隱藏層不太可能像下層一樣有用,因爲對於新任務來說最有用的高層特徵可能與對原始任務最有用的高層特徵明顯不同。 你想找到正確的層數來重用。
嘗試先凍結所有複製的圖層,然後訓練模型並查看它是如何執行的。 然後嘗試解凍一個或兩個頂層隱藏層,讓反向傳播調整它們,看看性能是否提高。 您擁有的訓練數據越多,您可以解凍的層數就越多。

如果仍然無法獲得良好的性能,並且您的訓練數據很少,請嘗試刪除頂部的隱藏層,並再次凍結所有剩餘的隱藏層。 您可以迭代,直到找到正確的層數重複使用。 如果您有足夠的訓練數據,您可以嘗試替換頂部的隱藏層,而不是放下它們,甚至可以添加更多的隱藏層。


Model Zoos
你在哪裏可以找到一個類似於你想要解決的任務訓練的神經網絡? 首先看看顯然是在你自己的模型目錄。 這是保存所有模型並組織它們的一個很好的理由,以便您以後可以輕鬆地檢索它們。 另一個選擇是在模型動物園中搜索。 許多人爲了各種不同的任務而訓練機器學習模型,並且善意地向公衆發佈預訓練模型。

TensorFlow在https://github.com/tensor流/ models中有自己的模型動物園。 特別是,它包含了大多數最先進的圖像分類網絡,如VGG,Inception和ResNet(參見第13章,檢查模型/ slim目錄),包括代碼,預訓練模型和 工具來下載流行的圖像數據集。

另一個流行的模型動物園是Caffe模型動物園。 它還包含許多在各種數據集(例如,ImageNet,Places數據庫,CIFAR10等)上訓練的計算機視覺模型(例如,LeNet,AlexNet,ZFNet,GoogLeNet,VGGNet,開始)。 Saumitro Dasgupta寫了一個轉換器,可以在https://github.com/ethereon/ca‰etensorflow.

Unsupervised Pretraining(無監督的預訓練)

假設你想要解決一個複雜的任務,你沒有太多的標記的訓練數據,但不幸的是,你不能找到一個類似的任務訓練模型。 不要失去所有希望! 首先,你當然應該嘗試收集更多的有標籤的訓練數據,但是如果這太難或太昂貴,你仍然可以進行無監督的訓練(見圖11-5)。 也就是說,如果你有很多未標記的訓練數據,你可以嘗試逐層訓練圖層,從最低層開始,然後上升,使用無監督的特徵檢測器算法,如限制玻爾茲曼機器(RBMs;見附錄E )或自動編碼器(見第15章)。 每個圖層都被訓練成先前訓練過的圖層的輸出(除了被訓練的圖層之外的所有圖層都被凍結)。 一旦所有圖層都以這種方式進行了訓練,就可以使用監督式學習(即反向傳播)對網絡進行微調。

這是一個相當漫長而乏味的過程,但通常運作良好。 實際上,這是Geoffrey Hinton和他的團隊在2006年使用的技術,導致了神經網絡的復興和深度學習的成功。 直到2010年,無監督預訓練(通常使用RBMs)是深網的標準,只有在消失梯度問題得到緩解之後,純訓練DNN才更爲普遍。 然而,當您有一個複雜的任務需要解決時,無監督訓練(現在通常使用自動編碼器而不是RBM)仍然是一個很好的選擇,沒有類似的模型可以重複使用,而且標記的訓練數據很少,但是大量的未標記的訓練數據。(另一個選擇是提出一個監督的任務,您可以輕鬆地收集大量標記的訓練數據,然後使用遷移學習,如前所述。 例如,如果要訓練一個模型來識別圖片中的朋友,你可以在互聯網上下載數百萬張臉並訓練一個分類器來檢測兩張臉是否相同,然後使用此分類器將新圖片與你朋友的每張照片做比較。)


Pretraining on an Auxiliary Task (在輔助任務上預訓練)
最後一種選擇是在輔助任務上訓練第一個神經網絡,您可以輕鬆獲取或生成標記的訓練數據,然後重新使用該網絡的較低層來完成您的實際任務。 第一個神經網絡的下層將學習可能被第二個神經網絡重複使用的特徵檢測器。

例如,如果你想建立一個識別面孔的系統,你可能只有幾個人的照片 - 顯然不足以訓練一個好的分類器。 收集每個人的數百張照片將是不實際的。 但是,您可以在互聯網上收集大量隨機人員的照片,並訓練第一個神經網絡來檢測兩張不同的照片是否屬於同一個人。 這樣的網絡將學習面部優秀的特徵檢測器,所以重複使用它的較低層將允許你使用很少的訓練數據來訓練一個好的面部分類器。

收集沒有標籤的訓練樣本通常是相當便宜的,但標註它們卻相當昂貴。 在這種情況下,一種常見的技術是將所有訓練樣例標記爲“好”,然後通過破壞好的訓練樣例產生許多新的訓練樣例,並將這些樣例標記爲“壞”。然後,您可以訓練第一個神經網絡 將實例分類爲好或不好。 例如,您可以下載數百萬個句子,將其標記爲“好”,然後在每個句子中隨機更改一個單詞,並將結果語句標記爲“不好”。如果神經網絡可以告訴“狗睡覺”是好的 句子,但“他們的狗”是壞的,它可能知道相當多的語言。 重用其較低層可能有助於許多語言處理任務。

另一種方法是訓練第一個網絡爲每個訓練實例輸出一個分數,並使用一個損失函數確保一個好的實例的分數大於一個壞實例的分數至少一定的邊際。 這被稱爲最大邊際學習.






發佈了77 篇原創文章 · 獲贊 25 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章