Keras是一套基於Tensorflow、Theano及CNTK后端的高層神經網絡API,可以非常友好地支持快速實驗,本文從零開始介紹了如何使用Keras搭建MLP並給出兩個示例。
基於Ubuntu安裝Keras
具體安裝過程在官方中英文文檔中有詳細說明
中文 https://keras-cn.readthedocs.io/en/latest/for_beginners/keras_linux/
英文 https://keras.io/#installation
Keras基礎知識
Sequential models
可以認為Keras中的所謂模型(models)就是某個具體的網絡結構,如MLP、CNN、LSTM等,在Keras中它們被分為兩種:序貫模型(Sequential)和函數式模型(Model)。
Sequential模型就是一個直接由若干網絡層線性堆疊起來的網絡,使用如下代碼創建一個Sequential對象:
from keras.models import Sequential
model = Sequential()
Sequential的構造函數可以接收一個由layer組成的list,用以初始化該model
model = Sequential([
Dense(32, input_shape=(784,)),#一個Dense層
Activation('relu'),#為上一個Dense層附加激活函數
Dense(10),#再添加一個Dense層
Activation('softmax'),#為上一個Dense層附加激活函數
])
也可以使用.add()方法對該model初始化:
model.add(Dense(32, input_shape=(784,)))
model.add(Activation('relu'))
Dense layer
Dense layer即全連接層,構造方法如下:
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)
units:大於0的整數,表示該層輸出維度。
activation:激活函數。
use_bias:是否使用偏置。
kernel_initializer:權值矩陣初始化方法。
bias_initializer:偏置的初始化方法。
regularizer與constraint:正則項與約束項。
我們可以使用前文提到的.add()方法為model添加全連接層,只有輸入層需要指定輸入數據維度,之后的層不需再指定前一層的維度。
compile & fit
model的結構設置好之后使用compile方法編譯:
model.compile(
self,
optimizer,
loss,
metrics=None,
sample_weight_mode=None)
optimizer:預定義的優化器名稱或自定義的優化器對象。
loss:預定義的損失函數名稱或自定義的目標函數。
編譯后使用fit方法訓練模型:
model.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)
x:作為輸入數據的numpy array。
y:作為標簽的numpy array。
batch_size:梯度下降時每個batch包含的樣本數。
epochs:訓練總輪數。
verbose:日志顯示,0為不在標准輸出流輸出日志信息,1為輸出進度條記錄,2為每個epoch輸出一行記錄。
訓練一個線性回歸模型
一個線性回歸模型可以被認為是一個MLP的特例,即一個輸入與輸出只有1維且沒有激活函數(或激活函數為線性函數a(x)=x)的MLP,我們首先生成一些離散的點,使這些點大致成線性關系,再構建一個Sequential model,最后在這些點上訓練該model以得到它們的線性關系。
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras import optimizers
data_size = 50 # 生成的數據集大小
batch_size = 40
learning_rate = 0.01
epochs = 300
# 在正負1之間均勻產生data_size個點
x_train = np.linspace(-1, 1, data_size)
# 使得數據點呈y=3x+5的關系,添加小范圍正態隨機數以獲得數據的隨機波動效果
y_train = 3 * x_train + 5 + np.random.rand(*x_train.shape)*0.3
# 創建一個Sequential模型
model = Sequential()
# 添加一個全連接層,輸入數據維度為1,含一個輸出單元,RandomUniform表示權值在正負0.05間均勻隨機初始化
model.add(Dense(units=1, input_dim=1, kernel_initializer='RandomUniform'))
# 打印查看當前的權重
print(model.layers[0].get_weights())
# 創建一個SGD對象以調整學習速率
sgd = optimizers.SGD(lr=learning_rate)
# 編譯model,優化器使用剛創建的SGD對象,損失函數使用最小均方差mse
model.compile(optimizer=sgd, loss='mse')
# 使用之前生成的數據訓練
history = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, verbose=1)
# 再次打印權重,可以看到其值在3與5附近
print(model.layers[0].get_weights())
結果:
...
Epoch 298/300
50/50 [==============================] - 0s 36us/step - loss: 0.0325
Epoch 299/300
50/50 [==============================] - 0s 45us/step - loss: 0.0264
Epoch 300/300
50/50 [==============================] - 0s 56us/step - loss: 0.0260
[array([[2.9937706]], dtype=float32), array([5.0084023], dtype=float32)]
有趣的是,當我們調整數據點的范圍,將
x_train = np.linspace(-1, 1, data_size)
改為
x_train = np.linspace(-25, 25, data_size)
保持其他部分不變,再次運行程序訓練,loss會振盪到無窮到導致訓練失敗,這是學習率過大造成的,將learning_rate由0.01改為0.003,再次訓練,即可得到正確結果。
我們還可以加入一個隱層,調整學習率與epochs后也可以得到很好的訓練結果:
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras import optimizers
data_size = 50
batch_size = 40
learning_rate = 0.0003
epochs = 500
x_train = np.linspace(-25, 25, data_size)
y_train = 3 * x_train + 5 + np.random.rand(*x_train.shape)*0.3
model = Sequential()
#這里的units為4,即隱層單元個數為4
model.add(Dense(units=4, input_dim=1, kernel_initializer='RandomUniform'))
#再添加輸出層,維度為1
model.add(Dense(units=1, kernel_initializer='RandomUniform'))
print(model.layers[0].get_weights())
print(model.layers[1].get_weights())
sgd = optimizers.SGD(lr=learning_rate)
model.compile(optimizer=sgd, loss='mse')
history = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, verbose=1)
print(model.layers[0].get_weights())
print(model.layers[1].get_weights())
結果:
Epoch 498/500
50/50 [==============================] - 0s 119us/step - loss: 0.0169
Epoch 499/500
50/50 [==============================] - 0s 156us/step - loss: 0.0168
Epoch 500/500
50/50 [==============================] - 0s 85us/step - loss: 0.0182
[array([[ 0.8122992 , -0.08030439, -0.05526135, -1.0514123 ]],
dtype=float32), array([ 1.110988 , -0.11047827, -0.07585457, -1.4386528 ], dtype=float32)]
[array([[ 1.3721825 ],
[-0.13642032],
[-0.09367472],
[-1.7768502 ]], dtype=float32), array([0.968823], dtype=float32)]
結果中兩個list中的第一個array是權值,第二個array是偏置。
訓練一個用於識別MNIST的MLP
在Keras安裝路徑下的examples文件夾中可以找到mnist_mlp.py:
from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.optimizers import RMSprop
batch_size = 128
num_classes = 10
epochs = 20
#讀取mnist的訓練與測試數據
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
#整型轉為浮點型
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
#歸一到(0,1)區間內
x_train /= 255
x_test /= 255
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
#使用了兩個隱層,每個隱層512個單元,激活函數選用relu,添加了Dropout防止過擬合,最后通過softmax輸出
model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(num_classes, activation='softmax'))
model.summary()
#損失函數選用多分類對數損失函數,優化器選用RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=RMSprop(),
metrics=['accuracy'])
history = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
結果:
...
59392/60000 [============================>.] - ETA: 0s - loss: 0.0183 - acc: 0.99
59776/60000 [============================>.] - ETA: 0s - loss: 0.0182 - acc: 0.99
60000/60000 [==============================] - 15s 253us/step - loss: 0.0182 - acc: 0.9953 - val_loss: 0.1072 - val_acc: 0.9842
Test loss: 0.10721807914278024
Test accuracy: 0.9842