1. 什麼是Keras
不知什麼時候,突然對于Keras是什麼産生了困惑。Keras中文為克拉斯,相傳也是銅管樂器。
其實,Keras的名字源于希臘古典史詩《奧德賽》裡的牛角之門,是真實事物進出夢境和現實的地方。《奧德賽》裡面說,象牙之門内隻是一場無法應驗的夢境,唯有走進牛角之門奮鬥的人,能夠擁有真正的回報。其用意不可謂不深刻。
但事實上,Keras隻是深度學習模組化的一個上層建築,其後端可以靈活使用CNTK、TensorFlow或者Theano。這樣就可以避免不同深度學習架構的差異而集中于模組化過程。并且可以進行CPU和GPU之間的無縫切換。
2. Hello World
在之前的一個快速入門裡,我們隻是大概的講述了如何安裝Keras環境以及一個簡易的Demo,這次我們将會詳細講述一個真正的Hello World.下面我們先看這個示例:
##2.1引用包
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.optimizers import SGD
from keras import metrics
import keras
##2.2生成資料
# Generate dummy data
import numpy as np
x_train = np.random.random((, ))
y_train = keras.utils.to_categorical(np.random.randint(, size=(, )), num_classes=)
x_test = np.random.random((, ))
y_test = keras.utils.to_categorical(np.random.randint(, size=(, )), num_classes=)
##2.3構模組化型
model = Sequential()
# Dense(64) is a fully-connected layer with 64 hidden units.
# in the first layer, you must specify the expected input data shape:
# here, 20-dimensional vectors.
model.add(Dense(, activation='relu', input_dim=))
model.add(Dropout())
model.add(Dense(, activation='relu'))
model.add(Dropout())
model.add(Dense(, activation='softmax'))
sgd = SGD(lr=, decay=, momentum=, nesterov=True)
model.compile(loss='categorical_crossentropy',
optimizer=sgd,
metrics=[metrics.categorical_accuracy,metrics.mae])
##2.4訓練模型
model.fit(x_train, y_train,
epochs=,
batch_size=)
##2.5評估模型
score=model.evaluate(x_test, y_test, batch_size=)
print(score);
先别急,我們先統一說一下上述這段代碼的作用是,通過深度學習方法實作10分類問題的訓練及測試過程。具體來講,就是生成一個1000條訓練樣例,每個樣例20維,再使用同樣20維的測試資料100條,通過一個序列模型使用SGD優化算法進行訓練,其模型層數不多,3個全連接配接層和2個放棄層。就這樣一個簡單的深度學習(算不上深度)模型就搭建完畢了。不過不用擔心,我們會在接下來的部分詳細介紹這部分代碼,俗話說,麻雀雖小,五髒俱全。
2.1 引用包
先來回顧一下這部分代碼:
#引入序列模型
from keras.models import Sequential
#引入全連接配接層、放棄層、激活層(激活層沒有直接用到,但是在全連接配接層裡間接用到了。)
from keras.layers import Dense, Dropout, Activation
#引入SGD優化算法
from keras.optimizers import SGD
#引入了metrics評估子產品
from keras import metrics
#引入了keras
import keras
這樣一寫,我們就清楚很多了,這是編碼的必備。
2.2生成資料
還是先來回顧一下這部分代碼:
# Generate dummy data
#使用numpy來模拟生成資料
import numpy as np
#生成一個1000*20維的向量
x_train = np.random.random((, ))
#生成一個1000*10維的向量
y_train = keras.utils.to_categorical(np.random.randint(, size=(, )), num_classes=)
#同上
x_test = np.random.random((, ))
y_test = keras.utils.to_categorical(np.random.randint(, size=(, )), num_classes=)
别急,我知道這裡可能有些難以了解。
尤其是
keras.utils.to_categorical
這個方法,源碼中,它是這樣寫的:
Converts a class vector (integers) to binary class matrix.
E.g. for use with categorical_crossentropy.
也就是說它是對于一個類型的容器(整型)的轉化為二進制類型矩陣。比如用來計算多類别交叉熵來使用的。
其參數也很簡單:
def to_categorical(y, num_classes=None):
Arguments
y: class vector to be converted into a matrix
(integers from 0 to num_classes).
num_classes: total number of classes.
說的很明白了,y就是待轉換容器(其類型為從0到類型數目),而num_classes則是類型的總數。
這樣這一句就比較容易了解了:
#先通過np生成一個1000*1維的其值為0-9的矩陣,然後再通過```keras.utils.to_categorical```方法擷取成一個1000*10維的二進制矩陣。
y_train = keras.utils.to_categorical(np.random.randint(, size=(, )), num_classes=)
說了這麼多,其實就是使用onehot對類型标簽進行編碼。下面的也都是這樣解釋。
2.3構模組化型
這部分代碼如下:
#建構序列模型
model = Sequential()
# Dense(64) is a fully-connected layer with 64 hidden units.
# in the first layer, you must specify the expected input data shape:
# here, 20-dimensional vectors.
#第一層為全連接配接層,隐含單元數為64,激活函數為relu,在第一層中一定要指明輸入的次元。
model.add(Dense(, activation='relu', input_dim=))
#放棄層,将在訓練過程中每次更新參數時随機斷開一定百分比(rate)的輸入神經元,Dropout層用于防止過拟合。這裡是斷開50%的輸入神經元。
model.add(Dropout())
model.add(Dense(, activation='relu'))
model.add(Dropout())
model.add(Dense(, activation='softmax'))
#執行個體化優化算法為sgd優化算法
sgd = SGD(lr=, decay=, momentum=, nesterov=True)
#對模型進行預編譯,其損失函數為多類别交叉熵,優化算法為sgd,評估方法為多類别準确度和平均絕對誤差。
model.compile(loss='categorical_crossentropy',
optimizer=sgd,
metrics=[metrics.categorical_accuracy,metrics.mae])
這裡才是核心,我們将會分開來講。
2.3.1模型
首先這裡接觸到了model,model在Keras裡有兩種,一種是序貫模型,一種是函數式模型。
-
序貫模型
序貫模型是多個網絡層的線性堆疊,也就是“一條路走到黑”。這也是非常常用的和傻瓜式的方法。
-
函數式(Functional)模型
這已經是進階階段了,目前我們隻能摘取Keras中文文檔中的簡介來告訴大家,在後面我們會單獨講解。
我們起初将Functional一詞譯作泛型,想要表達該類模型能夠表達任意張量映射的含義,但表達的不是很精确,在Keras 2裡我們将這個詞改譯為“函數式”,對函數式程式設計有所了解的同學應能夠快速get到該類模型想要表達的含義。函數式模型稱作Functional,但它的類名是Model,是以我們有時候也用Model來代表函數式模型。
Keras函數式模型接口是使用者定義多輸出模型、非循環有向模型或具有共享層的模型等複雜模型的途徑。一句話,隻要你的模型不是類似VGG一樣一條路走到黑的模型,或者你的模型需要多于一個的輸出,那麼你總應該選擇函數式模型。函數式模型是最廣泛的一類模型,序貫模型(Sequential)隻是它的一種特殊情況。
2.3.2添加層
代碼如下:
model.add(Dense(, activation='relu', input_dim=))
這就是序貫模型的添加層數的方法,很簡單。這裡添加的是Keras的常用層中的全連接配接層(Dense)。它是最常用的層,也是多層感覺機(MLP)的基本構成機關。
下面我們來簡單的介紹一下目前用到的幾個層,他們都屬于常用層(Core)。
2.3.2.1. Dense層
這個層的聲明如下:
keras.layers.core.Dense(units, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None, **kwargs)
Dense就是常用的全連接配接層,所實作的運算是output = activation(dot(input, kernel)+bias)。其中activation是逐元素計算的激活函數,kernel是本層的權值矩陣,bias為偏置向量,隻有當use_bias=True才會添加。
如果本層的輸入資料的次元大于2,則會先被壓為與kernel相比對的大小。
但是我們更關注的是參數的含義:
- units:大于0的整數,代表該層的輸出次元。
- activation:激活函數,為預定義的激活函數名(參考激活函數),或逐元素(element-wise)的Theano函數。如果不指定該參數,将不會使用任何激活函數(即使用線性激活函數:a(x)=x)
- use_bias: 布爾值,是否使用偏置項
- kernel_initializer:權值初始化方法,為預定義初始化方法名的字元串,或用于初始化權重的初始化器。參考initializers
- bias_initializer:權值初始化方法,為預定義初始化方法名的字元串,或用于初始化權重的初始化器。參考initializers
- kernel_regularizer:施加在權重上的正則項,為Regularizer對象
- bias_regularizer:施加在偏置向量上的正則項,為Regularizer對象
- activity_regularizer:施加在輸出上的正則項,為Regularizer對象
- kernel_constraints:施加在權重上的限制項,為Constraints對象
- bias_constraints:施加在偏置上的限制項,為Constraints對象
而這裡我們看到了一個不同,那就是輸入的單元
input_dim
沒有出現在定義中,其實它藏在了
**kwargs
中,因為源碼中這樣寫道:
if 'input_shape' not in kwargs and 'input_dim' in kwargs:
kwargs['input_shape'] = (kwargs.pop('input_dim'),)
這裡就有出現問題了,什麼是
input_dim
和
input_shape
。他們的差別在于,如果是2D張量,則可以使用
input_dim
即可,但是如果是三維的話,可能就需要
input_length
來幫忙了,具體來講,他們之間的聯系是:
input_dim = input_shape(input_dim,)
input_dim, input_length = input_shape(input_length, input_dim,)
#像我們上面例子中的就是一個20維的資料,但是如果是識别手寫體的話,你的原始資料是二維的,比如這個是 (28, 28) = 784,這個是mnist的手寫資料。2維資料可以看成是1維的,那1維的shape就是(28*28,)了。
2.3.2.2. Activation層
這個激活函數在本例中沒有出現,其實不是,它間接的在Dense層中出現過了,預設的激活層常見的有以下幾種:
-
softmax
這是歸一化的多分類,可以把K維實數域壓縮到(0,1)的值域中,并且使得K個數值和為1。
-
sigmoid
這時歸一化的二進制分類,可以把K維實數域壓縮到近似為0,1二值上。
-
relu
這也是常用的激活函數,它可以把K維實數域映射到[0,inf)區間。
-
tanh
這時三角雙曲正切函數,它可以把K維實數域映射到(-1,1)區間。
還有其他激活函數我們就不一一介紹了。
2.3.2.3. Dropout層
它的原型為:
keras.layers.core.Dropout(rate, noise_shape=None, seed=None)
正如上面所述,為輸入資料施加Dropout。Dropout将在訓練過程中每次更新參數時随機斷開一定百分比(rate)的輸入神經元,Dropout層用于防止過拟合。
其參數含義如下:
- rate:0~1的浮點數,控制需要斷開的神經元的比例。
- noise_shape:整數張量,為将要應用在輸入上的二值Dropout mask的shape,例如你的輸入為(batch_size, timesteps, features),并且你希望在各個時間步上的Dropout mask都相同,則可傳入noise_shape=(batch_size, 1, features)。
-
seed:整數,使用的随機數種子
還有幾個常用層,我們會在接下來的講解中講到。這裡不再贅述。
2.3.3優化算法
優化算法最常用的為SGD算法,也就是随機梯度下降算法。這裡我們不多講,因為優化算法我們會單開一章來總結一下所有的優化算法。
這裡我們隻講這裡用到的SGD,它的原型為:
keras.optimizers.SGD(lr=, momentum=, decay=, nesterov=False)
它就是随機梯度下降法,支援動量參數,支援學習衰減率,支援Nesterov動量。這幾個參數就是SGD的幾個改進,優化算法那章我們會講到。
其參數含義如下:
- lr:大于0的浮點數,學習率
- momentum:大于0的浮點數,動量參數
- decay:大于0的浮點數,每次更新後的學習率衰減值
- nesterov:布爾值,确定是否使用Nesterov動量
2.3.4模型的編譯
上面的注釋已經講的很明白了。在訓練模型之前,我們需要通過compile來對學習過程進行配置。compile接收三個參數:
- 優化器optimizer:該參數可指定為已預定義的優化器名,如rmsprop、adagrad,或一個Optimizer類的對象,詳情見optimizers
- 損失函數loss:該參數為模型試圖最小化的目标函數,它可為預定義的損失函數名,如categorical_crossentropy、mse,也可以為一個損失函數。詳情見losses
- 名額清單metrics:對分類問題,我們一般将該清單設定為metrics=[‘accuracy’]。名額可以是一個預定義名額的名字,也可以是一個使用者定制的函數.名額函數應該傳回單個張量,或一個完成metric_name - > metric_value映射的字典.請參考性能評估
下面給出一些樣例:
# For a multi-class classification problem
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
# For a binary classification problem
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
# For a mean squared error regression problem
model.compile(optimizer='rmsprop',
loss='mse')
# For custom metrics
import keras.backend as K
def mean_pred(y_true, y_pred):
return K.mean(y_pred)
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy', mean_pred])
要注意的是,這裡loss和metrics的名額都會在最後的評估中展現。
2.4 模型的訓練
模型訓練都是如出一轍,全是
fit
方法:
fit(self, x, y, batch_size=, epochs=, verbose=, callbacks=None, validation_split=, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=)
本函數将模型訓練nb_epoch輪,其參數有:
- x:輸入資料。如果模型隻有一個輸入,那麼x的類型是numpy array,如果模型有多個輸入,那麼x的類型應當為list,list的元素是對應于各個輸入的numpy array
- y:标簽,numpy array
- batch_size:整數,指定進行梯度下降時每個batch包含的樣本數。訓練時一個batch的樣本會被計算一次梯度下降,使目标函數優化一步。
- epochs:整數,訓練的輪數,每個epoch會把訓練集輪一遍。
- verbose:日志顯示,0為不在标準輸出流輸出日志資訊,1為輸出進度條記錄,2為每個epoch輸出一行記錄
- callbacks:list,其中的元素是keras.callbacks.Callback的對象。這個list中的回調函數将會在訓練過程中的适當時機被調用,參考回調函數
- validation_split:0~1之間的浮點數,用來指定訓練集的一定比例資料作為驗證集。驗證集将不參與訓練,并在每個epoch結束後測試的模型的名額,如損失函數、精确度等。注意,validation_split的劃分在shuffle之前,是以如果你的資料本身是有序的,需要先手工打亂再指定validation_split,否則可能會出現驗證集樣本不均勻。
- validation_data:形式為(X,y)的tuple,是指定的驗證集。此參數将覆寫validation_spilt。
- shuffle:布爾值或字元串,一般為布爾值,表示是否在訓練過程中随機打亂輸入樣本的順序。若為字元串“batch”,則是用來處理HDF5資料的特殊情況,它将在batch内部将資料打亂。
- class_weight:字典,将不同的類别映射為不同的權值,該參數用來在訓練過程中調整損失函數(隻能用于訓練)
- sample_weight:權值的numpy array,用于在訓練時調整損失函數(僅用于訓練)。可以傳遞一個1D的與樣本等長的向量用于對樣本進行1對1的權重,或者在面對時序資料時,傳遞一個的形式為(samples,sequence_length)的矩陣來為每個時間步上的樣本賦不同的權。這種情況下請确定在編譯模型時添加了sample_weight_mode=’temporal’。
- initial_epoch: 從該參數指定的epoch開始訓練,在繼續之前的訓練時有用。
fit函數傳回一個History的對象,其History.history屬性記錄了損失函數和其他名額的數值随epoch變化的情況,如果有驗證集的話,也包含了驗證集的這些名額變化情況。
2.5 模型的評估
模型評估使用的是evaluate方法:
evaluate(self, x, y, batch_size=, verbose=, sample_weight=None)
它最終傳回的是一個score,第0維為編譯中的loss名額,剩下的就是metrics中的名額了。
參數含義如下:
- x:輸入資料,與fit一樣,是numpy array或numpy array的list
- y:标簽,numpy array
- batch_size:整數,含義同fit的同名參數
- verbose:含義同fit的同名參數,但隻能取0或1
- sample_weight:numpy array,含義同fit的同名參數
3. 小結
在本文中,我們庖丁解牛般的詳細的介紹了一個麻雀的五髒六腑,基本上是解說到了最基礎的部分,這也增加了我們對于Keras的了解。在接下來的過程中,我們将會對于每一部分進行一個詳細的講解。
最後緻謝《Keras中文文檔》、百度知道、Stack Overflow以及各位同行的部落格。