一、遷移學習
就是把已訓練好的模型參數遷移到新的模型來幫助新模型訓練。
模型的訓練與預測:
深度學習的模型可以划分為 訓練 和 預測 兩個階段。
訓練 分為兩種策略:一種是白手起家從頭搭建模型進行訓練,一種是通過預訓練模型進行訓練。
預測 相對簡單,直接用已經訓練好的模型對數據集進行預測即可。
優點:
1)站在巨人的肩膀上:前人花很大精力訓練出來的模型在大概率上會比你自己從零開始搭的模型要強悍,沒有必要重復造輪子。
2)訓練成本可以很低:如果采用導出特征向量的方法進行遷移學習,后期的訓練成本非常低,用CPU都完全無壓力,沒有深度學習機器也可以做。
3)適用於小數據集:對於數據集本身很小(幾千張圖片)的情況,從頭開始訓練具有幾千萬參數的大型神經網絡是不現實的,因為越大的模型對數據量的要求越大,過擬合無法避免。這時候如果還想用上大型神經網絡的超強特征提取能力,只能靠遷移學習。
遷移學習的幾種方式:
1、Transfer Learning:凍結預訓練模型的全部卷積層,只訓練自己定制的全連接層。
2、Extract Feature Vector:先計算出預訓練模型的卷積層對所有訓練和測試數據的特征向量,然后拋開預訓練模型,只訓練自己定制的簡配版全連接網絡。
3、Fine-tune:凍結預訓練模型的部分卷積層(通常是靠近輸入的多數卷積層),訓練剩下的卷積層(通常是靠近輸出的部分卷積層)和全連接層。
* 注:Transfer Learning關心的問題是:什么是“知識”以及如何更好地運用之前得到的“知識”,這可以有很多方法和手段,eg:SVM,貝葉斯,CNN等。
而fine-tune只是其中的一種手段,更常用於形容遷移學習的后期微調中。
三種遷移學習方式對比
1、第一種和第二種訓練得到的模型本質上並沒有什么區別,但是第二種的計算復雜度要遠遠優於第一種。
2、第三種是對前兩種方法的補充,以進一步提升模型性能。要注意的是,這種方法並不一定能真的對模型有所提升。
本質上來講:這三種遷移學習的方式都是為了讓預訓練模型能夠勝任新數據集的識別工作,能夠讓預訓練模型原本的特征提取能力得到充分的釋放和利用。但是,在此基礎上如果想讓模型能夠達到更低的Loss,那么光靠遷移學習是不夠的,靠的更多的還是模型的結構以及新數據集的豐富程度。
二、實驗:嘗試對模型進行微調,以進一步提升模型性能
1、fine-tune的作用:
拿到新數據集,先用預訓練模型處理,通常用上面的方法一或方法二測試預訓練模型在新數據上的表現,如果表現不錯,可以嘗試fine-tune,進一步解鎖卷積層以繼續訓練。
但是不要期待質的飛躍,另外,如果由於新數據集與原數據集差別太大導致表現很差,一方面可以考慮從頭訓練,另一方面也可以考慮解鎖比較多層的訓練。
2、不同數據集下使用微調
數據集1:數據量少,但數據相似度非常高
在這種情況下,我們所做的只是修改最后幾層或最終的softmax圖層的輸出類別,方法一
數據集2:數據量少,數據相似度低
在這種情況下,我們可以凍結預訓練模型的初始層(比如k層),並再次訓練剩余的(n-k)層。由於新數據集的相似度較低,因此根據新數據集對較高層進行重新訓練具有重要意義。方法三
數據集3:數據量大,數據相似度低
在這種情況下,由於我們有一個大的數據集,我們的神經網絡訓練將會很有效。但是,由於我們的數據與用於訓練我們的預訓練模型的數據相比有很大不同。使用預訓練模型進行的預測不會有效。因此,最好根據你的數據從頭開始訓練神經網絡(Training from scatch)。
數據集4:數據量大,相似度高
這是理想情況。在這種情況下,預訓練模型應該是最有效的。使用模型的最好方法是保留模型的體系結構和模型的初始權重。然后,我們可以使用在預先訓練的模型中的權重來重新訓練該模型。
3.微調的注意事項
1)通常的做法是截斷預先訓練好的網絡的最后一層(softmax層),並用與我們自己的問題相關的新的softmax層替換它。
2)使用較小的學習率來訓練網絡。
3)如果數據集數量過少,我們進來只訓練最后一層,如果數據集數量中等,凍結預訓練網絡的前幾層的權重也是一種常見做法。
注:卷積神經網絡的核心是:
(1)淺層卷積層提取基礎特征,比如邊緣,輪廓等基礎特征。
(2)深層卷積層提取抽象特征,比如整個臉型。
(3)全連接層根據特征組合進行評分分類。
4、實驗操作具體步驟
1、下載預訓練模型
2、預處理:按照預訓練模型原本的預處理方式對數據進行預處理,使用預訓練模型一定要確保讓待訓練的數據盡可能向原數據集靠攏,這樣才能最大程度發揮模型的識圖本領。
3、基模型和定制模型:構建和預訓練里面完全相同的模型。
4、查看固定和恢復節點名
5、訓練過程設置恢復,固定張量的列表
三、代碼詳情
基模型和定制模型
import slim.nets.resnet_v1 as resnet_v1 # 定義模型,因為給出的只有參數,並沒有模型,這里需要指定模型的具體結構 with slim.arg_scope(resnet_v1.resnet_arg_scope()): # logits就是最后預測值,images就是輸入數據,指定num_classes=None是為了使resnet模型最后的輸出層禁用 logits, end_points = resnet_v1.resnet_v1_50(inputs=input_images, num_classes=None) # 自定義的輸出層 with tf.variable_scope("Logits"): # 將原始模型的輸出數據去掉維度為2和3的維度,最后只剩維度1的batch數和維度4的300*300*3 # 也就是將原來的二三四維度全部壓縮到第四維度 net = tf.squeeze(logits, axis=[1, 2]) # 加入一層dropout層 net = slim.dropout(net, keep_prob=0.5, scope='dropout_scope') # 加入一層全連接層,指定最后輸出大小 logits = slim.fully_connected(net, num_outputs=labels_nums, scope='fc')
查看固定和恢復節點名
look_checkpoint.py
import os from tensorflow.python import pywrap_tensorflow model_dir = os.getcwd() # 獲取當前文件工作路徑 print(model_dir)#輸出當前工作路徑 checkpoint_path = r'G:\1-modelused\Siamese_Densenet_Single_Net\output\640model\model3/model_epoch_20.ckpt'#model_dir + "\\ckpt_dir\\model-ckpt-100" print(checkpoint_path)#輸出讀取的文件路徑 # 從checkpoint文件中讀取參數 reader = pywrap_tensorflow.NewCheckpointReader(checkpoint_path) var_to_shape_map = reader.get_variable_to_shape_map() # 輸出變量名稱及變量值 for key in var_to_shape_map: # if key.startswith('DenseNet_121/AuxLogits'): # print(1) # print(key) print("tensor_name: ", key)
訓練過程設置恢復,固定張量的列表
CKPT_FILE = r'.\pretrain\resnet_v1_50.ckpt' #不需要從谷歌訓練好的模型中加載的參數。這里就是最后的全連接層,因為在新的問題中要重新訓練這一層中的參數。 #這里給出的是參數的前綴 CHECKPOINT_EXCLUDE_SCOPES = 'Logits' ## 指定最后的全連接層為可訓練的參數,需要訓練的網絡層參數名稱,在fine-tuning的過程中就是最后的全連接層 TRAINABLE_SCOPES = 'Logits' #獲取所有需要從谷歌訓練好的模型中加載的參數 def get_tuned_variables(): exclusions = [scope.strip() for scope in CHECKPOINT_EXCLUDE_SCOPES.split(',')] variables_to_restore = [] #枚舉inception-v3模型中所有的參數,然后判斷是否需要從加載列表中移除 for var in slim.get_model_variables(): excluded = False for exclusion in exclusions: if var.op.name.startswith(exclusion): excluded = True break if not excluded: variables_to_restore.append(var) return variables_to_restore #獲取所有需要訓練的變量列表。 def get_trainable_variables(): scopes = [scope.strip() for scope in TRAINABLE_SCOPES.split(',')] variables_to_train = [] #枚舉所有需要訓練的參數前綴,並通過這些前綴找到所有的參數。 for scope in scopes: variables = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,scope) variables_to_train.extend(variables) return variables_to_train #定義加載Google訓練好的Inception-v3模型的Saver load_fn = slim.assign_from_checkpoint_fn( CKPT_FILE, get_tuned_variables(), ignore_missing_vars=True ) saver = tf.train.Saver(max_to_keep=100) max_acc = 0.0 with tf.Session() as sess: ckpt = tf.train.get_checkpoint_state('models/resnet_v1/') if ckpt and tf.train.checkpoint_exists(ckpt.model_checkpoint_path): saver.restore(sess, ckpt.model_checkpoint_path) else: sess.run(tf.global_variables_initializer()) sess.run(tf.local_variables_initializer()) # 加載谷歌已經訓練好的模型 print('Loading tuned variables from %s' % CKPT_FILE) load_fn(sess)