如何使用Keras函數式API進行深度學習?
可以這樣說,Keras Python庫使得創建深度學習模型變得快速且簡單。
序列API使得你能夠為大多數問題逐層創建模型。當然它也是有局限性的,那就是它并不能讓你創建擁有共享層或具有多個輸入或輸出的模型。
Keras中的的函數式API是創建模型的另一種方式,它具有更多的靈活性,包括創建更為復雜的模型。
在本教程中,你將了解如何用Keras中更具靈活性的函數式API來定義深度學習模型。
完成本教程后,你將明白:
序列API和函數式API之間的區別。
如何使用函數式API定義簡單的多層感知器、卷積神經網絡以及循環神經網絡模型。
如何定義具有共享層和多個輸入和輸出的更為復雜的模型。
教程概述
本教程涵蓋六部分內容,分別是:
1.Keras序列模型
2.Keras函數式模型
3.標準網絡模型
4.共享層模型
5.多個輸入和輸出模型
6.最佳實踐
1. Keras序列模型
Keras提供了一個序列模型API。
這是一種創建深度學習模型的方法,其中創建了一個序列類的實例,還創建了模型層并將其添加到其中。
例如,可以將層定義為數組的形式并傳遞給序列:
from keras.models import Sequential
from keras.layers import Dense
model = Sequential([Dense(2, input_dim=1), Dense(1)])
另外,層也是可以分段添加的:
model = Sequential()
model.add(Dense(2, input_dim=1))
model.add(Dense(1))
可以這樣說,在大多數情況下,序列模型API是非常適合用于開發深度學習模型的,但同時也具有一定的局限性。
例如,定義一個可能具有多個不同輸入源、且能生成多個輸出目標或重用層的模型,并不是一件簡單的事情。
Keras函數式API提供了定義模型的一種更為靈活的方式。
尤其是,它使得你能夠定義具有多個輸入或輸出以及共享層的模型。不僅如此,它還使得你能夠定義特定的非循環網絡圖。
模型是通過創建層的實例并將它們直接地成對相互連接來定義的,然后定義一個Model,指定層作為模型的輸入和輸出。
接下來了解一下Keras函數式API的三個獨特方面:
定義輸入
與序列模型不同的是,你必須創建并定義一個獨立的輸入層來指定輸入數據的形狀。
輸入層接受一個形狀參數,即一個元組,它表示的是輸入數據的維數。。
如果輸入數據是一維的,例如多層感知器,那么這個形狀必須能夠明確地為在訓練網絡過程中進行分割數據時使用的小批量大小的形狀留下空間。因此,這個形狀數組總是用最后一個維度(2)定義,例如:
from keras.layers import Input
visible = Input(shape=(2,))
連接層
模型中的層是成對連接的。
這是通過在定義每個新層時指定輸入的出處完成的。這里使用括號表示法,以便在創建層之后,就指定了來自當前層輸入出處的層。
讓我們用一個簡短的例子來說明這一點。我們可以如上所述那樣創建輸入層,然后創建一個隱藏層作為密集層,只接受來自輸入層的輸入。
hidden = Dense(2)(visible)
注意可見性,在創建密集層之后,將輸入層的輸出作為輸入與密集的隱藏層進行連接。
就是這種方式能夠將各個層逐次連接起來,從而使得函數式API具有靈活性。例如,你可以看到開始定義層的臨時圖表是多么容易。
創建模型
在創建了所有模型層并將它們連接在一起之后,你就必須定義模型了。
與序列API一樣,模型是你可以進行總結、擬合、評估和用來進行預測的東西。
Keras提供了一個Model類,你可以使用它從已創建的層中創建一個模型。要求就是你只能指定輸入和輸出層。例如:
from keras.models import Model
model = Model(inputs=visible, outputs=hidden)
既然我們已經了解Keras 函數式API的所有關鍵部分,那么接下來我們就來定義一套不同的模型并就其做一些實踐。
每個示例都是可執行的,可展示結構并創建圖表的簡圖。這樣做的好處是,你可以清楚地知曉你所定義的是什么。
我希望,在將來你想要使用函數式API定義自己的模型時,這些示例能夠為你提供模板。
當開始使用函數式API時,最好先去了解一些標準的神經網絡模型是如何進行定義的。
在本節中,我們將定義一個簡單的多層感知器、卷積神經網絡和循環神經網絡。
這些例子將為理解接下來更為詳細的示例奠定基礎。
多層感知器
在本節中,我們定義了一個用于二元分類(binary classification)的多層感知器模型。
該模型有10個輸入、3個分別具有10、20和10個神經元的隱藏層、以及一個只有一個輸出的輸出層。在每個隱層中都使用了糾正線性激活函數(Rectified linear activation functions),而在輸出層中使用了一個sigmoid激活函數,以用于二元分類。
# Multilayer Perceptron
from keras.utils import plot_model
visible = Input(shape=(10,))
hidden1 = Dense(10, activation='relu')(visible)
hidden2 = Dense(20, activation='relu')(hidden1)
hidden3 = Dense(10, activation='relu')(hidden2)
output = Dense(1, activation='sigmoid')(hidden3)
model = Model(inputs=visible, outputs=output)
# summarize layers
print(model.summary())
# plot graph
plot_model(model, to_file='multilayer_perceptron_graph.png')
運行該示例,展示出該網絡的結構:
該模型圖的一個簡圖也被創建并保存到文件中。
多層感知器網絡圖
卷積神經網絡
在本節中,我們將定義一個用于圖像分類的卷積神經網絡。
該模型接收一個規格為64×64的黑白圖像作為輸入,然后有一個卷積層和池化層的序列作為特征提取器,隨后是一個用以解釋特征的完全連接層,以及一個用于兩個類預測的sigmoid激活函數。
# Convolutional Neural Network
from keras.layers.convolutional import Conv2D
from keras.layers.pooling import MaxPooling2D
visible = Input(shape=(64,64,1))
conv1 = Conv2D(32, kernel_size=4, activation='relu')(visible)
pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
conv2 = Conv2D(16, kernel_size=4, activation='relu')(pool1)
pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
hidden1 = Dense(10, activation='relu')(pool2)
output = Dense(1, activation='sigmoid')(hidden1)
plot_model(model, to_file='convolutional_neural_network.png')
運行該示例,總結模型層:
該模型圖的簡圖也被創建并保存到文件。
卷積神經網絡圖
循環神經網絡
在本節中,我們將定義一個用于序列分類的長短型記憶循環神經網絡。
該模型期望以一個特征的100個時間步長作為輸入。該模型有一個單獨的LSTM隱藏層以從序列中提取特征,然后是一個完全連接層用以解釋LSTM輸出,接下來是一個用于進行二元預測的輸出層。
# Recurrent Neural Network
from keras.layers.recurrent import LSTM
visible = Input(shape=(100,1))
hidden1 = LSTM(10)(visible)
hidden2 = Dense(10, activation='relu')(hidden1)
output = Dense(1, activation='sigmoid')(hidden2)
plot_model(model, to_file='recurrent_neural_network.png')
運行該示例,總結模型層。
循環神經網絡圖
多個層可以共享來自一個層的輸出。
例如,可能有多個不同的特征提取層是來自于同一個輸入的,或者有多個層是用于解釋來自一個特征提取層的輸出的。
我們來看看這兩個例子。
共享輸入層
在本節中,我們定義了具有不同大小內核的多個卷積層來解釋圖像輸入。
該模型采用大小為64×64像素的黑白圖像。有兩個CNN特征提取子模型共享該輸入; 第一個內核大小為4,第二個內核大小為8。這些特征提取子模型的輸出被壓縮成向量,連接到一個長向量中,并傳遞到一個完全連接層,以便在最終輸出層進行二二元分類之前進行解釋。
# Shared Input Layer
from keras.layers import Flatten
from keras.layers.merge import concatenate
# input layer
# first feature extractor
flat1 = Flatten()(pool1)
# second feature extractor
conv2 = Conv2D(16, kernel_size=8, activation='relu')(visible)
flat2 = Flatten()(pool2)
# merge feature extractors
merge = concatenate([flat1, flat2])
# interpretation layer
hidden1 = Dense(10, activation='relu')(merge)
# prediction output
plot_model(model, to_file='shared_input_layer.png')
具有共享輸入的神經網絡圖
共享特征提取層
在本節中,我們將用兩個并行子模型來解釋LSTM特性提取器的輸出,以進行序列分類。
該模型的輸入是一個特征的100個時間步長。一個具有10個記憶單元的LSTM層以解釋該序列。第一個解釋模型是一個淺的單完全連接層,第二個是一個深度3層模型。兩個解釋模型的輸出都被連接到一個長向量中,傳遞到輸出層用以進行二元預測。
# Shared Feature Extraction Layer
# define input
# feature extraction
extract1 = LSTM(10)(visible)
# first interpretation model
interp1 = Dense(10, activation='relu')(extract1)
# second interpretation model
interp11 = Dense(10, activation='relu')(extract1)
interp12 = Dense(20, activation='relu')(interp11)
interp13 = Dense(10, activation='relu')(interp12)
# merge interpretation
merge = concatenate([interp1, interp13])
# output
output = Dense(1, activation='sigmoid')(merge)
plot_model(model, to_file='shared_feature_extractor.png')
共享特征提取層的神經網絡圖
函數式API也可用于開發具有多個輸入的更復雜的模型,可能具有不同的形式。它也可以用于開發產生多個輸出的模型。
我們將在本節中查看每個示例。
多輸入模型
我們將開發一個圖像分類模型,它將兩個版本的圖像作為輸入,每個版本的大小不同。具體是黑白64×64版本和彩色32×32版本。單獨的特征提取CNN模型在每個模型上運行,然后將兩個模型的結果連接起來進行解釋和最終預測。
請注意,在創建Model()實例時,我們將兩個輸入層定義為數組。具體如下:
model = Model(inputs=[visible1, visible2], outputs=output)
完整的示例如下所示:
# Multiple Inputs
# first input model
visible1 = Input(shape=(64,64,1))
conv11 = Conv2D(32, kernel_size=4, activation='relu')(visible1)
pool11 = MaxPooling2D(pool_size=(2, 2))(conv11)
conv12 = Conv2D(16, kernel_size=4, activation='relu')(pool11)
pool12 = MaxPooling2D(pool_size=(2, 2))(conv12)
flat1 = Flatten()(pool12)
# second input model
visible2 = Input(shape=(32,32,3))
conv21 = Conv2D(32, kernel_size=4, activation='relu')(visible2)
pool21 = MaxPooling2D(pool_size=(2, 2))(conv21)
conv22 = Conv2D(16, kernel_size=4, activation='relu')(pool21)
pool22 = MaxPooling2D(pool_size=(2, 2))(conv22)
flat2 = Flatten()(pool22)
# merge input models
# interpretation model
plot_model(model, to_file='multiple_inputs.png')
該模型圖的簡圖被創建并保存到文件。
具有多個輸入的神經網絡圖
多輸出模型
在本節中,我們將開發出一種可以進行兩種不同類型預測的模型。給定一個特征的100時間步長的輸入序列,該模型將對序列進行分類并輸出具有相同長度的新序列。
LSTM層解釋輸入序列,并返回每個時間步長的隱藏狀態。第一個輸出模型創建一個堆棧LSTM,解釋特征,并進行二元預測。第二個輸出模型使用相同的輸出層對每個輸入時間步長進行實值預測。
# Multiple Outputs
from keras.layers.wrappers import TimeDistributed
extract = LSTM(10, return_sequences=True)(visible)
# classification output
class11 = LSTM(10)(extract)
class12 = Dense(10, activation='relu')(class11)
output1 = Dense(1, activation='sigmoid')(class12)
# sequence output
output2 = TimeDistributed(Dense(1, activation='linear'))(extract)
model = Model(inputs=visible, outputs=[output1, output2])
plot_model(model, to_file='multiple_outputs.png')
具有多個輸出的神經網絡圖
在本節中,我會給你一些建議,以便在定義自己的模型時充分利用函數式API。
一致的變量名:對輸入(可見)、輸出層(輸出),甚至是隱藏層(hidden1,hidden2)使用相同的變量名稱。它將有助于正確地將它們聯系起來。
回顧層總結:堅持歸納模型總結并回顧層輸出,以確保模型按預期那樣連接在一起。
回顧圖表簡圖:堅持創建模型圖的簡圖,并對其進行回顧,以確保所有的東西都按照你的意愿放在一起。
命名層:你可以為在回顧模型圖的總結和簡圖時使用的層分配名稱。例如:Dense(1,命名 ='hidden1')。
單獨的子模型:考慮分離子模型的開發,并在最后將子模型組合在一起。