Keras 簡介
本文的代碼示例全都使用Keras 實作。Keras 是一個Python 深度學習架構,可以友善地定 義和訓練幾乎所有類型的深度學習模型。Keras 最開始是為研究人員開發的,其目的在于快速 實驗。
Keras 具有以下重要特性:
- 相同的代碼可以在 CPU 或 GPU 上無縫切換運作。
- 具有使用者友好的 API,便于快速開發深度學習模型的原型。
- 内置支援卷積網絡(用于計算機視覺)、循環網絡(用于序列處理)以及二者的任意 組合。
- 支援任意網絡架構:多輸入或多輸出模型、層共享、模型共享等。這也就是說,Keras 能夠建構任意深度學習模型,無論是生成式對抗網絡還是神經圖靈機。 Keras 基于寬松的MIT 許可證釋出,這意味着可以在商業項目中免費使用它。它與所有版 本的 Python 都相容(截至 2017 年年中,從 Python 2.7 到 Python 3.6 都相容)。 Keras 已有200 000 多個使用者,既包括創業公司和大公司的學術研究人員和工程師,也包括 研究所學生和業餘愛好者。Google、Netflix、Uber、CERN、Yelp、Square 以及上百家創業公司都在 用 Keras 解決各種各樣的問題。Keras 還是機器學習競賽網站Kaggle 上的熱門架構,最新的深 度學習競賽中,幾乎所有的優勝者用的都是 Keras 模型,如圖 3-2 所示。
Keras、TensorFlow、Theano 和 CNTK
Keras 是一個模型級(model-level)的庫,為開發深度學習模型提供了高層次的構模組化塊。 它不處理張量操作、求微分等低層次的運算。相反,它依賴于一個專門的、高度優化的張量庫 來完成這些運算,這個張量庫就是Keras 的後端引擎(backend engine)。 Keras 沒有選擇單個張 量庫并将Keras 實作與這個庫綁定,而是以子產品化的方式處理這個問題(見圖3-3)。是以,幾 個不同的後端引擎都可以無縫嵌入到 Keras 中。目前,Keras 有三個後端實作:TensorFlow 後端、 Theano 後端和微軟認知工具包(CNTK,Microsoft cognitive toolkit)後端。未來 Keras 可能會擴 展到支援更多的深度學習引擎。
TensorFlow、CNTK 和 Theano 是當今深度學習的幾個主要平台。Theano 由蒙特利爾大學的 MILA 實驗室開發,TensorFlow 由 Google 開發,CNTK 由微軟開發。你用Keras 寫的每一段代 碼都可以在這三個後端上運作,無須任何修改。也就是說,你在開發過程中可以在兩個後端之 間無縫切換,這通常是很有用的。例如,對于特定任務,某個後端的速度更快,那麼我們就可以無縫切換過去。我們推薦使用TensorFlow 後端作為大部分深度學習任務的預設後端,因為它 的應用最廣泛,可擴充,而且可用于生産環境。 通過 TensorFlow(或 Theano、CNTK), Keras 可以在 CPU 和GPU 上無縫運作。在 CPU 上運作 時,TensorFlow 本身封裝了一個低層次的張量運算庫,叫作Eigen;在GPU 上運作時,TensorFlow 封裝了一個高度優化的深度學習運算庫,叫作 NVIDIA CUDA 深度神經網絡庫(cuDNN)。
使用 Keras 開發:概述
你已經見過一個Keras 模型的示例,就是MNIST 的例子。典型的Keras 工作流程就和那個 例子類似。 (1) 定義訓練資料:輸入張量和目标張量。 (2) 定義層組成的網絡(或模型),将輸入映射到目标。 (3) 配置學習過程:選擇損失函數、優化器和需要監控的名額。 (4) 調用模型的 fit 方法在訓練資料上進行疊代。 定義模型有兩種方法:一種是使用 Sequential 類(僅用于層的線性堆疊,這是目前最常 見的網絡架構),另一種是函數式 API(functional API,用于層組成的有向無環圖,讓你可以構 建任意形式的架構)。 前面講過,這是一個利用 Sequential 類定義的兩層模型(注意,我們向第一層傳入了輸 入資料的預期形狀)。
from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(32, activation='relu', input_shape=(784,)))
model.add(layers.Dense(10, activation='softmax')) 下面是用函數式 API 定義的相同模型。
input_tensor = layers.Input(shape=(784,)) x = layers.Dense(32, activation='relu')(input_tensor) output_tensor = layers.Dense(10, activation='softmax')(x)
model = models.Model(inputs=input_tensor, outputs=output_tensor)
利用函數式API,你可以操縱模型處理的資料張量,并将層應用于這個張量,就好像這些 層是函數一樣。
一旦定義好了模型架構,使用 Sequential 模型還是函數式API 就不重要了。接下來的步 驟都是相同的。 配置學習過程是在編譯這一步,你需要指定模型使用的優化器和損失函數,以及訓練過程 中想要監控的名額。下面是單一損失函數的例子,這也是目前最常見的。
from keras import optimizers
model.compile(optimizer=optimizers.RMSprop(lr=0.001),
loss='mse',
metrics=['accuracy'])
最後,學習過程就是通過 fit() 方法将輸入資料的Numpy 數組(和對應的目标資料)傳 入模型,這一做法與 Scikit-Learn 及其他機器學習庫類似。
model.fit(input_tensor, target_tensor, batch_size=128, epochs=10)
準備資料
你不能将整數序列直接輸入神經網絡。你需要将清單轉換為張量。轉換方法有以下兩種。
- 填充清單,使其具有相同的長度,再将清單轉換成形狀為 (samples, word_indices) 的整數張量,然後網絡第一層使用能處理這種整數張量的層。
- 對清單進行 one-hot 編碼,将其轉換為 0 和 1 組成的向量。舉個例子,序列 [3, 5] 将會 被轉換為10 000 維向量,隻有索引為3 和 5 的元素是1,其餘元素都是0。然後網絡第 一層可以用 Dense 層,它能夠處理浮點數向量資料。
将整數序列編碼為二進制矩陣
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1.
return results
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
樣本現在變成了這樣: >>> x_train[0] array([ 0., 1., 1., ..., 0., 0., 0.])
你還應該将标簽向量化,這很簡單。
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
建構網絡
輸入資料是向量,而标簽是标量(1 和 0),這是你會遇到的最簡單的情況。有一類網絡在這種問題上表現很好,就是帶有relu 激活的全連接配接層(Dense)的簡單堆疊,比如 Dense(16, activation='relu')。 傳入 Dense 層的參數(16)是該層隐藏單元的個數。一個隐藏單元(hidden unit)是該層 表示空間的一個次元。我們在第2 章講過,每個帶有 relu 激活的 Dense 層都實作了下列張量 運算:
output = relu(dot(W, input) + b)
16 個隐藏單元對應的權重矩陣 W 的形狀為 (input_dimension, 16),與 W 做點積相當于 将輸入資料投影到16 維表示空間中(然後再加上偏置向量 b 并應用 relu 運算)。你可以将表 示空間的次元直覺地了解為“網絡學習内部表示時所擁有的自由度”。隐藏單元越多(即更高維 的表示空間),網絡越能夠學到更加複雜的表示,但網絡的計算代價也變得更大,而且可能會導 緻學到不好的模式(這種模式會提高訓練資料上的性能,但不會提高測試資料上的性能)。
對于這種 Dense 層的堆疊,你需要确定以下兩個關鍵架構:
- 網絡有多少層;
- 每層有多少個隐藏單元。
- 現在你隻需要相信我選擇的下列架構: 兩個中間層,每層都有 16 個隐藏單元;
- 第三層輸出一個标量,預測目前評論的情感。 中間層使用 relu 作為激活函數,最後一層使用 sigmoid 激活以輸出一個 0~1 範圍内的機率 值(表示樣本的目标值等于1 的可能性,即評論為正面的可能性)。relu(rectified linear unit, 整流線性單元)函數将所有負值歸零(見圖3-4),而 sigmoid 函數則将任意值“壓縮”到 [0, 1] 區間内(見圖 3-5),其輸出值可以看作機率值。
下圖顯示了網絡的結構。
模型定義
from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,))) model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
什麼是激活函數?為什麼要使用激活函數?
如果沒有 relu 等激活函數(也叫非線性), Dense 層将隻包含兩個線性運算——點積 和加法:
output = dot(W, input) + b
這樣 Dense 層就隻能學習輸入資料的線性變換(仿射變換):該層的假設空間是從輸 入資料到16 位空間所有可能的線性變換集合。這種假設空間非常有限,無法利用多個表示 層的優勢,因為多個線性層堆疊實作的仍是線性運算,添加層數并不會擴充假設空間。 為了得到更豐富的假設空間,進而充分利用多層表示的優勢,你需要添加非線性或激 活函數。relu 是深度學習中最常用的激活函數,但還有許多其他函數可選,它們都有類似 的奇怪名稱,比如 prelu、elu 等。
最後,你需要選擇損失函數和優化器。由于你面對的是一個二分類問題,網絡輸出是一 個機率值(網絡最後一層使用sigmoid 激活函數,僅包含一個單元),那麼最好使用 binary_ crossentropy(二進制交叉熵)損失。這并不是唯一可行的選擇,比如你還可以使用mean_ squared_error(均方誤差)。但對于輸出機率值的模型,交叉熵(crossentropy)往往是最好 的選擇。交叉熵是來自于資訊論領域的概念,用于衡量機率分布之間的距離,在這個例子中就 是真實分布與預測值之間的距離。
編譯模型
from keras import optimizers
model.compile(optimizer=optimizers.RMSprop(lr=0.001),
loss='binary_crossentropy',
metrics=['accuracy'])
實戰:
将資料集劃分成訓練集和測試集,訓練之後選用測試集驗證損失率和分類準确率
from keras.datasets import imdb
import numpy as np
from keras import models
from keras import layers
import matplotlib.pyplot as plt
def main():
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
word_index = imdb.get_word_index()
# print(word_index)
reverse_word_index = dict(
[(value,key) for (key,value) in word_index.items()]
)
decoded_review = ' '.join(
[reverse_word_index.get(i-3,'?') for i in train_data[0]]
)
print(decoded_review)
def vectorize_sequences(sequences,dimension = 10000):
results = np.zeros((len(sequences),dimension))
for i,sequences in enumerate(sequences):
results[i,sequences] = 1.
return results
def train():
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
x_val = x_train[:10000]
partial_train_data = x_train[10000:]
y_val = y_train[:10000]
partial_train_labels = y_train[10000:]
model = models.Sequential()
model.add(layers.Dense(16,activation='relu',input_shape=(10000,)))
model.add(layers.Dense(16,activation='relu'))
model.add(layers.Dense(1,activation='sigmoid'))
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
history = model.fit(partial_train_data,
partial_train_labels,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))
history_dict = history.history
print(history_dict)
print(history_dict.keys())
loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
epochs = range(1,len(loss_values) + 1)
plt.plot(epochs,loss_values,'bo',label = 'Training loss')
plt.plot(epochs,val_loss_values,'b',label ='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
if __name__ == '__main__':
# main()
train()
# print(train_data[0])