1. 程式人生 > >Keras入門(2)——麻雀雖小,五臟俱全

Keras入門(2)——麻雀雖小,五臟俱全

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((1000, 20))
y_train = keras.utils.to_categorical(np.random.randint(10
, size=(1000, 1)), num_classes=10) x_test = np.random.random((100, 20)) y_test = keras.utils.to_categorical(np.random.randint(10, size=(100, 1)), num_classes=10) ##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(64, activation='relu', input_dim=20)) model.add(Dropout(0.5)) model.add(Dense(64, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(10, activation='softmax')) sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True) model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=[metrics.categorical_accuracy,metrics.mae]) ##2.4訓練模型 model.fit(x_train, y_train, epochs=20, batch_size=128) ##2.5評估模型 score=model.evaluate(x_test, y_test, batch_size=128) 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, 20))
#生成一個1000*10維的向量
y_train = keras.utils.to_categorical(np.random.randint(10, size=(1000, 1)), num_classes=10)
#同上
x_test = np.random.random((100, 20))
y_test = keras.utils.to_categorical(np.random.randint(10, size=(100, 1)), num_classes=10)

別急,我知道這裡可能有些難以理解。
尤其是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(10, size=(1000, 1)), num_classes=10)

說了這麼多,其實就是使用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(64, activation='relu', input_dim=20))
#放棄層,將在訓練過程中每次更新引數時隨機斷開一定百分比(rate)的輸入神經元,Dropout層用於防止過擬合。這裡是斷開50%的輸入神經元。
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
#例項化優化演算法為sgd優化演算法
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
#對模型進行預編譯,其損失函式為多類別交叉熵,優化演算法為sgd,評估方法為多類別準確度和平均絕對誤差。
model.compile(loss='categorical_crossentropy',
              optimizer=sgd,
              metrics=[metrics.categorical_accuracy,metrics.mae])

這裡才是核心,我們將會分開來講。

2.3.1模型

首先這裡接觸到了model,model在Keras裡有兩種,一種是序貫模型,一種是函式式模型。

  1. 序貫模型
    序貫模型是多個網路層的線性堆疊,也就是“一條路走到黑”。這也是非常常用的和傻瓜式的方法。

  2. 函式式(Functional)模型
    這已經是高階階段了,目前我們只能摘取Keras中文文件中的簡介來告訴大家,在後面我們會單獨講解。
    我們起初將Functional一詞譯作泛型,想要表達該類模型能夠表達任意張量對映的含義,但表達的不是很精確,在Keras 2裡我們將這個詞改譯為“函式式”,對函數語言程式設計有所瞭解的同學應能夠快速get到該類模型想要表達的含義。函式式模型稱作Functional,但它的類名是Model,因此我們有時候也用Model來代表函式式模型。
    Keras函式式模型介面是使用者定義多輸出模型、非迴圈有向模型或具有共享層的模型等複雜模型的途徑。一句話,只要你的模型不是類似VGG一樣一條路走到黑的模型,或者你的模型需要多於一個的輸出,那麼你總應該選擇函式式模型。函式式模型是最廣泛的一類模型,序貫模型(Sequential)只是它的一種特殊情況。

2.3.2新增層

程式碼如下:

model.add(Dense(64, activation='relu', input_dim=20))

這就是序貫模型的新增層數的方法,很簡單。這裡新增的是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_diminput_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層中出現過了,預設的啟用層常見的有以下幾種:

  1. softmax
    這是歸一化的多分類,可以把K維實數域壓縮到(0,1)的值域中,並且使得K個數值和為1。

  2. sigmoid
    這時歸一化的二元分類,可以把K維實數域壓縮到近似為0,1二值上。

  3. relu
    這也是常用的啟用函式,它可以把K維實數域對映到[0,inf)區間。

  4. 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=0.01, momentum=0.0, decay=0.0, 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=32, epochs=10, verbose=1, callbacks=None, validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0)

本函式將模型訓練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=32, verbose=1, 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以及各位同行的部落格。