一、不用Sequential模型的解決方案:keras函數式API
1.多輸入模型
簡單的問答模型輸入:問題 + 文本片段 輸出:回答(一個詞) |
|
from keras.models import Model from keras import layers from keras import Input text_vocabulary_size = 10000 question_vocabulary_size = 10000 answer_vocabulary_size = 500 text_input = Input(shape=(None,), dtype='int32', name='text') embeded_text = layers.Embedding(text_vocabulary_size,64)(text_input) encoded_text = layers.LSTM(32)(embeded_text) question_input = Input(shape=(None,), dtype = 'int32', name = 'question') embeded_question = layers.Embedding(question_vocabulary_size,32)(question_input) encoded_question = layers.LSTM(16)(embeded_question) concatenated = layers.concatenate([encoded_text,encoded_question],axis=-1) answer = layers.Dense(answer_vocabulary_size,activation='softmax')(concatenated) model = Model([text_input,question_input],answer) model.compile(optimizer='rmsprop', loss = 'categorical_crossentropy', metrics = ['acc']) model.summary() |
|
訓練這種模型需要能夠對網絡的各個頭指定不同的損失函數,例如:年齡預測是標量回歸任務,而性別預測是二分類任務,二者需要不同的損失過程。 |
|
#多輸出模型的編譯選項:多重損失
#方法一
model.compile(optimizer='rmsprop',
loss = ['mse','categorical_crossentropy','binary_crossentropy'])
#方法二
# model.compile(optimizer='rmsprop',
# loss={'age':'mse',
# 'income':'categorical_crossentropy',
# 'gender':'binary_crossentropy'})
|
#多輸出模型的編譯選項:損失加權 #方法一 model.compile(optimizer='rmsprop', loss = ['mse','categorical_crossentropy',
|
不同的損失值具有不同的取值范圍,為了平衡不同損失的貢獻,應該對loss_weights進行設置 |
|
#將數據輸入到多輸出模型中 #方法一 model.fit(posts,[age_targets,income_targets,gender_targets], epochs=10,batch_size=64) #方法二 # model.fit(posts,{'age':age_targets, # 'income':income_targets, # 'gender':gender_targets}, # epochs=10,batch_size=64) |
2.多輸出模型
一個網絡試圖同時預測數據的不同性質 輸入:某個匿名人士的一系列社交媒體發帖 輸出:預測那個人的屬性,比如年齡、性別和收入水平 |
|
from keras import layers from keras import Input from keras.models import Model vocabulary_size = 50000 num_income_groups = 10 posts_input = Input(shape=(None,),dtype='int32',name='posts') embedded_posts = layers.Embedding(256,vocabulary_size)(posts_input) x = layers.Conv1D(128,5,activation='relu')(embedded_posts) x = layers.MaxPooling1D(5)(x) x = layers.Conv1D(256,5,activation='relu')(x) x = layers.Conv1D(256,5,activation='relu')(x) x = layers.MaxPooling1D(5)(x) x = layers.Conv1D(256,5,activation='relu')(x) x = layers.Conv1D(256,5,activation='relu')(x) x = layers.GlobalMaxPooling1D()(x) #global池化主要是用來解決全連接的問題,其主要是將最后一層的特征圖進行整張圖的一個池化, # 形成一個特征點--來源於network in network。 x = layers.Dense(128,activation='relu')(x) age_prediction = layers.Dense(1,name='age')(x) income_prediction = layers.Dense(num_income_groups,activation='softmax',name='income')(x) gender_prediction = layers.Dense(1,activation='sigmoid',name='gender')(x) model = Model(posts_input,[age_prediction,income_prediction,gender_prediction]) model.summary() |
|
3.層組成的有向無環圖
(1)Inception模塊
(2)殘差網絡
如果特征圖的尺寸相同,用恆等殘差連接(identity residual connection)
from keras import layers
x = ...
y = layers.Conv2D(128,3,activation='relu',padding='same')(x)
y = layers.Conv2D(128,3,activation='relu',padding='same')(y)
y = layers.Conv2D(128,3,activation='relu',padding='same')(y)
y = layers.add([y,x])#將原始x與輸出特征相加
如果特征圖的尺寸不同,用線性殘差連接(linear residual connection)
from keras import layers x = ... y = layers.Conv2D(128,3,activation='relu',padding='same')(x) y = layers.Conv2D(128,3,activation='relu',padding='same')(y) y = layers.MaxPooling2D(2,strides=2)(y) residual = layers.Conv2D(128,1,strides=2,padding='same')(x) #使用1*1卷積,將原始x張量線性下采樣為與y具有相同的形狀 y = layers.add([y,residual])#將殘差張量與輸出特征相加
4.共享層權重
如果你對一個層實例調用兩次,而不是每次調用都實例化一個新層,那么每次調用可以重復使用相同的權重。這樣你可以構建具有共享分支的模型,
即幾個分支全部共享相同的知識並執行相同的運算。也就是說,這些分支共享相同的表示,並同時對不同的輸入集合學習這些表示。
舉個例子: 假設一個模型想要評估兩個句子之間的語義相似度。這個模型有兩個輸入(需要比較的兩個句子),並輸出一個范圍在0-1的分數,0表示
毫不相關,1表示兩個句子完全相同或只是換一種表述。這種模型在許多應用中都很有用,其中包括在對話系統中刪除重復的自然語言查詢。
在這種設置下,兩個輸入句子是可以互換的,因為語義相似度是一種對稱關系,A相對於B的相似度對於B相對於A的相似度。因此,學習兩個單獨
的模型來分別處理兩個輸入句子是沒有道理的。相反,你需要用一個LSTM層來處理兩個句子。這個LSTM層的表示(即它的權重)是同時基於兩個輸
入來學習的。我們將其稱為連體LSTM(siamese LSTM)或共享LSTM(shared LSTM)模型。
from keras import layers from keras import Input from keras.models import Model lstm = layers.LSTM(32)#將一個LSTM層實例化一次 #構建模型的左分支,輸入是長度128的向量組成的變長序列 left_input = Input(shape=(None,128)) left_output = lstm(left_input) #構建模型的右分支,如果調用已有的層實例,那么就會重復使用它的權重 right_input = Input(shape=(None,128)) right_output = lstm(right_input) #在上面構建一個分類器 merged = layers.concatenate([left_output,right_output],axis=-1) predictions = layers.Dense(1,activation='sigmoid')(merged) #將模型實例化並訓練,訓練這種模型時,基於兩個輸入對LSTM層的權重進行更新 model = Model([left_input,right_input],predictions) model.fit([left_data,right_data],targets)
5.將模型作為層
在一個輸入張量上調用模型,並得到一個輸出張量
y = model(x)
如果模型具有多個輸入張量和多個輸出張量,那么應該用張量列表來調用模型
y1,y2 = model([x1,x2])
二、使用Keras回調函數和TensorBoard來檢查並監控深度學習模型
1.訓練過程中將回調函數作用域模型
訓練模型時,我們不知道需要多少輪才能得到最佳驗證損失。前面所有例子都采用這樣一種策略:訓練足夠多的輪次, 這時模型已經開始過擬合,根據這第一次運行來確定訓練
所需要的正確輪數,然后使用這個最佳輪數從頭開始再啟動 一次新的訓練。------->這種方法很浪費
處理這個問題的更好方法是,當觀測到驗證損失不再改善時就停止訓練。這可以使用keras回調函數來實現。 回調函數(callback)是在調用fit時傳入模型的一個對象,它在訓練
過程中的不同時間點都會被模型調用。 它可以訪問關於模型狀態與性能的所有可能數據,還可以采取行動:中斷訓練、保存模型、加載一組不同的 權重或改變模型的狀態
回調函數的一些用法如下:
模型檢查點:在訓練過程中的不同時間點保存模型的當前權重
提前終止:如果驗證損失不再改善,則中斷訓練(當然,同時保存在訓練過程中得到的最佳模型)
在訓練過程中動態調節某些參數值:比如優化器的學習率
在訓練過程中記錄訓練指標和驗證指標,或將模型學到的表示可視化(keras進度條就是一個回調函數)
# 1.ModelCheckpoint與EarlyStopping回調函數 import keras callbacks_list = [ keras.callbacks.EarlyStopping( #如果不再改善,就中斷訓練 monitor = 'acc',#監控模型的精度 patience = 1,#如果精度在多於兩輪內不再改善,中斷訓練 ), keras.callbacks.ModelCheckpoint( #在每輪過后保存當前權重 filepath = 'my_model.h5', #目標模型文件的保存路徑 monitor = 'val_loss', save_best_only=True, #這兩個參數的含義是,如果val_loss沒有改善,
|
如果監控的目標指標在設定的輪數內不再改善,可以用EarlyStopping回調函數來中斷訓練。比如, 這個回調函數可以在剛開始過擬合的時候就中斷訓練,從而避免用更少的輪次重新訓練模型。這個 回調函數通常與ModelCheckpoint結合使用,后者可以在訓練過程中持續不斷地保存模型(你也 可以選擇只保存目前的最佳模型,即一輪結束后具有最佳性能的模型) |
# 2.ReduceLROnPlateau回調函數 callbacks_list = [ keras.callbacks.ReduceLROnPlateau( monitor = 'val_loss', #監控模型的驗證損失 factor = 0.1,#觸發時將學習率除以10 patience = 10,#如果驗證損失在10輪內都沒有改善,那么就觸發這個回調函數 ) ] model.fit(x,y, epochs=10, batch_size=32, callbacks = callbacks_list,#由於回調函數要監控驗證損失和驗證精度,
|
如果驗證損失不再改善,你可以使用這個回調函數來降低學習率。在訓練過程中如果出現了 損失平台,那么增大或減小學習率 都是跳出局部最小值的有效策略。 |
# 3.編寫你自己的回調函數
on_epoch_begin # 在每輪開始時被調用 on_epoch_end # 在每輪結束時被調用 on_batch_begin # 在處理每個批量之前被調用 on_batch_end # 在處理每個批量之后被調用 on_train_begin # 在訓練開始時被調用 on_train_end # 在訓練結束時被調用 |
|
2. TensorBoard的可視化框架
TensorBoad具有以下巧妙的功能,都在瀏覽器中實現
在訓練過程中以可視化的方式監控指標
將模型架構可視化
將激活和梯度的直方圖可視化
以三維的形式研究嵌入
三、讓模型性能發揮到極致
1.高級架構模式
(1)批標准化
標准化有利於讓機器學習模型看到的不同樣本彼此之間更加相似,這有助於模型的學習與對新數據的泛化。最常見的就是將數據減去
平均值使其中心為0,然后除以標准差使其標准差為1.實際上,這種做法假設數據服從正態分布,並確保讓該分布的中心為0,同時縮
放到方差為1.
批標准化是Ioffe和 Szegedy在2015年提出的一種層的模型(在Keras中是BatchNormalization),即使在訓練過程中均值和方差
隨時間發生變化,它也可以適應性的將數據標准化。
-
工作原理: 訓練過程中在內部保存已讀取每批數據均值和方差的指數移動平均值。
-
主要效果: 它有助於梯度傳播(這一點和殘差連接很像),因此允許更深的網絡。對於有些特別深的網絡,只有包含多個Batch
-Normalization層時才能進行訓練。
-
例如BatchNormalization廣泛用於Keras內置的許多高級卷積神經網絡架構,如ResNet50,Inception V3和Xception
-
通常在卷積層或密集連接層之后使用
#在卷積層之后使用 conv_model.add(layers.Conv2D(32,3,activation='relu')) conv_model.add(layers.BatchNormalization())
#在Dense層之后使用 dense_model.add(layers.Dense(32,3,activation='relu')) dense_model.add(layers.BatchNormalization())
(2)深度可分離卷積
這個層對輸入的每個通道分別執行空間卷積,然后通過逐點卷積(1*1卷積)將輸出通道混合。這相當於將空間特征學習和通道特征學習分開,
如果你假設輸入中的空間位置高度相關,在不同的通道之間相對獨立,那么這么做是很有意義的。它需要的參數要少很多,計算量也更小、更
快的模型。因為它是一種執行卷積更高效的方法,所以往往能夠使用更少的數據學習到更好的表示,從而得到性能更好的模型。
2.超參數優化
過程如下:
-
選擇一組超參數(自動選擇)
-
構建相應的模型
-
將模型在訓練數據上擬合,並衡量其在驗證數據上的最終性能
-
選擇要嘗試的下一組超參數(自動選擇)
-
重復上述過程
-
最后,衡量模型在測試數據上的性能
這個過程的關鍵在於,給定許多組超參數,使用驗證性能的歷史來選擇下一組需要評估的超參數的算法。有許多不同的技術可供選擇:貝葉斯優化、遺傳算法、簡單隨機搜索等。
3.模型集成
以分類問題為例。想要將一組分類器的預測結果匯集在一起【即分類器集成】,最簡單的方法就是將他們的預測結果取平均值作為預測結果
#使用4個不同的模型計算初始預測
preds_a = model_a.predict(x_val) preds_b = model_b.predict(x_val) preds_c = model_c.predict(x_val) preds_d = model_d.predict(x_val)
final_results = 0.25*(preds_a+preds_b+preds_c+preds_d) #這個新的預測數組應該比任何一個初始預測都更加准確
只有這組分類器中每一個的性能都差不多一樣好的時候,這種方法才奏效。如果其中一個分類器的性能比其他的差很多,
那么最終預測結果可能不如這一組中的最佳分類器那么好。
為了找到一組好的集成權重,你可以使用隨機搜索或簡單的優化算法(比如Nelder-Mead方法)
final_results = 0.5*preds_a + 0.25*preds_b + 0.1*preds_c + 0.15 * preds_d #加權平均
還有許多其他變體,比如你可以對預測結果先取指數再做平均。
集成的模型應該盡可能好,同時盡可能不同。
有一件事基本上是不值得做的,就是對相同的網絡,使用不同的隨機初始化多次獨立訓練,然后集成。如果模型之間的唯一區別是隨機初始化
和訓練數據的讀取順序,那么集成的多樣性很小,與單一模型相比只會有微小的改進。
作者提到一種在實踐中很有效果的方法:將基於樹的方法(比如隨機森林或梯度提升樹)和深度神經網絡進行集成.