一文上手最新Tensorflow2.0系列|「tf.keras」API 使用

2019-08-20     人工智慧遇見磐創

Keras是一個基於Python編寫的高層神經網絡API,Keras強調用戶友好性、模塊化以及易擴展等,其後端可以採用TensorFlow、Theano以及CNTK,目前大多是以TensorFlow作為後端引擎。考慮到Keras優秀的特性以及它的受歡迎程度,TensorFlow將Keras的代碼吸收了進來,並將其作為高級API提供給用戶使用。「tf.keras」不強調原來Keras的後端可互換性,而是在符合Keras標準的基礎上讓其與TensorFlow結合的更緊密(例如支持TensorFlow的eager execution模式,支持「tf.data」,以及支持TPU訓練等)。「tf.keras」提高了TensorFlow的易用性,同時也保持了TensorFlow的靈活性和性能。

1. 基本模型的搭建和訓練

對於一些基本的網絡模型,我們可以使用「tf.keras.Sequential」來創建,通過這種方式創建的模型又稱為「順序模型」,因為這種方式創建的模型是由多個網絡層線性堆疊而成的。

首先導入需要的包:

import tensorflow as tf
from tensorflow.keras import layers

然後我們創建一個Sequential Model:

model = tf.keras.Sequential([
# 添加一個有64個神經元的全連接層,「input_shape」為該層接受的輸# 入數據的維度,「activation」指定該層所用的激活函數
layers.Dense(64, activation='relu', input_shape=(32,)),
# 添加第二個網絡層
layers.Dense(64, activation='relu'),
# 添加一個softmax層作為輸出層,該層有十個單元
layers.Dense(10, activation='softmax'),
])

上面的代碼中,我們在定義這個順序模型的同時添加了相應的網絡層,除此之外我們也可以使用「add」方法逐層的添加:

model = tf.keras.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(32,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

「tf.keras.layers」用於生成網絡層,包括全連接層(tf.keras.layers.Dense())、Dropout層(tf.keras.layers.Dropout)以及卷積網絡層(例如二維卷積:tf.keras.layers.Conv2D)等等。創建好網絡結構後,我們需要對網絡進行編譯:

model.compile(optimizer=tf.keras.optimizers.Adam(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])

在編譯模型的時候我們需要設置一些必須的參數。例如「optimizer」用來指定我們想使用的優化器以及設定優化器的學習率。例如Adam優化器「tf.keras.optimizer.Adam」、SGD優化器「tf.keras.optimizer.SGD」等,在15行代碼中我們使用了Adam優化器,並設置了學習率為「0.001」。

「loss」參數用來設置模型的損失函數(又稱目標函數),例如均方誤差損失函數(mean_squared_error)、對數損失函數(binary_ crossentropy)以及多分類的對數損失函數(categorical_crossentropy)等等。

「metrics」用來設定模型的評價函數,模型的評價函數與損失函數相似,不過評價函數隻用來顯示給用戶查看,並不用於模型的訓練。除了自帶的一些評價函數以外,我們還可以自定義評價函數。

編譯好模型之後我們就可以開始訓練了,這裡我們使用numpy生成一組隨機數作為訓練數據:

import numpy as np
data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))
print(data[0])
print(labels[0])
model.fit(data, labels, epochs=2, batch_size=32)

代碼中我們首先隨機生成了樣本數據和類標。使用「model.fit」來執行模型的訓練,其中參數「data」和「labels」分別為訓練數據和類標,「epochs」為訓練的回合數(一個回合即在全量數據集上訓練一次),「batch_size」為訓練過程中每一個批次數據的大小。輸出結果:

輸出結果


在訓練模型的工程中,為了更好地調節參數,方便模型的選擇和優化,我們通常會準備一個驗證集,這裡我們同樣隨機生成一個驗證集:

val_data = np.random.random((100, 32))
val_labels = np.random.random((100, 10))

model.fit(data, labels, epochs=2, batch_size=50,
validation_data=(val_data, val_labels))

輸出結果:

增加驗證集後的輸出結果


和上圖相比,這裡多了「val_loss」和「val_accuracy」,分別為驗證集上的損失和準確率。

上面的例子中我們直接在NumPy數據上訓練的模型,我們也可以使用「tf.data」將其轉為「Dataset」後再傳遞給模型去訓練:

# 創建訓練集Dataset
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(50)
# 創建驗證集Dataset
val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))
val_dataset = val_dataset.batch(50)
model.fit(dataset, epochs=2, validation_data=val_dataset)

模型訓練好之後,我們希望用測試集去對模型進行評估,這裡我們可以使用「model.evaluate」對模型進行評估:

# 模型評估,測試集為NumPy數據
model.evaluate(data, labels, batch_size=50)
# 模型評估,測試集為Dataset數據
model.evaluate(dataset, steps=30)

結果:

模型評估結果


最後我們可以使用「model.predict」對新的數據進行預測:

result = model.predict(data, batch_size=50)
print(result[0])

輸出結果:

使用訓練好的模型預測新的數據


2. 搭建高級模型

2.1. 函數式API

對於一些基本的網絡結構,我們可以使用「tf.keras.Sequential」來搭建,但更多的時候我們面臨的是一些比較複雜的網絡結構。例如模型可能有多輸入或多輸出,模型中的一些網絡層需要共享等等。對於這種網絡模型的結構較為複雜的情況,我們需要使用到函數式API。

我們實現一個簡單的例子:

# 單獨的一個輸入層
inputs = tf.keras.Input(shape=(32,))
# 網絡層可以像函數一樣被調用,其接收和輸出的均為張量
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
# 輸出層
predictions = layers.Dense(10, activation='softmax')(x)

接下來使用上面定義的網絡層來創建模型:

# 創建模型
model = tf.keras.Model(inputs=inputs, outputs=predictions)
# 編譯模型
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
# 訓練模型
model.fit(data, labels, epochs=2, batch_size=50)

2.2. 實現自定義的模型類和網絡層

通過繼承「tf.keras.Model」和「tf.keras.layers.Layer」我們可以實現自定義的模型類以及網絡層,這為我們構建自己的網絡結構提供了非常好的靈活性。例如我們定義一個簡單的前饋網絡模型:

class MyModel(tf.keras.Model):

def __init__(self, num_classes=10):
super(MyModel, self).__init__(name='my_model')
# 分類任務的類別數
self.num_classes = num_classes
# 定義我們自己的網絡層
self.dense_1 = layers.Dense(32, activation='relu')
self.dense_2 = layers.Dense(num_classes, activation='sigmoid')

def call(self, inputs):
# 使用「__init__」方法中定義的網絡層來構造網絡的前饋過程
x = self.dense_1(inputs)
return self.dense_2(x)

我們需要在「__init__」方法中定義好我們模型中所有的網絡層,並作為模型類的屬性。在「call」方法中我們可以定義模型的正向傳遞過程。之後就可以調用這個模型。

model = MyModel(num_classes=10)
# 編譯模型
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
# 訓練模型
model.fit(data, labels, batch_size=50, epochs=5)

以上是我們自定義一個簡單的網絡模型的例子,通過繼承「tf.keras.layers.Layer」類我們還可以實現自定義的網絡層。事實上除了研究人員,對於絕大多數用戶來說,我們一般不會需要自定義模型類或網絡層。

3. 回調函數

回調函數會在模型的訓練階段被執行,可以用來自定義模型訓練期間的一些行為,例如輸出模型內部的狀態等。我們可以自己編寫回調函數也可以使用內置的一些函數,例如:

  • tf.keras.callbacks.ModelCheckpoint:定期保存模型。
  • tf.keras.callbacks.LearningRateScheduler:動態的改變學習率。
  • tf.keras.callbacks.EarlyStopping:當模型在驗證集上的性能不再提升時終止訓練。
  • tf.keras.callbacks.TensorBoard:使用TensorBoard來監測模型。

回調函數的使用方式如下:

callbacks = [
# 當驗證集上的損失「val_loss」連續兩個訓練回合(epoch)都沒有變化,則提前結束訓練
tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
# 使用TensorBoard保存訓練的記錄,保存到「./logs」目錄中
tf.keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=50, epochs=5, callbacks=callbacks,
validation_data=(val_data, val_labels))

4. 模型的保存和恢復

我們可以使用「model.save()」和「tf.keras.models.load_model()」來保存和加載由「tf.keras」訓練的模型:

# 創建一個簡單的模型
model = tf.keras.Sequential([
layers.Dense(10, activation='softmax', input_shape=(32,)),
layers.Dense(10, activation='softmax')
])
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(data, labels, batch_size=32, epochs=5)

# 將整個模型保存為HDF5文件
model.save('my_model')
# 加載保存的模型
model = tf.keras.models.load_model('my_model')

通過「model.save()」保存的是一個完整的模型信息,包括模型的權重以及結構等。除了保存完整的模型,我們還可以單獨保存模型的權重信息或者模型的結構。

# 將模型的權重參數保存為HDF5文件
model.save_weights('my_model.h5', save_format='h5')
# 重新加載
model.load_weights('my_model.h5')

# 將模型的結構保存為JSON文件
json_string = model.to_json()

文章來源: https://twgreatdaily.com/zh-tw/04VGt2wBvvf6VcSZ-Lc3.html