Brief 概述
前面兩章重點描述了使用 Keras 搭建模型的兩種方式,並且妥善的儲存模型到 .h5 文件中,等下一次使用的時候直接呼叫儲存文檔,就可以把整個模型,連同參數一起回傳到新的模型中繼續開始工作。
最後,既然 Keras 已經大致完好的被 Tensorflow 移植過來,那麼 tf 最厲害的可視化工具 Tensorboard 肯定也不能落於人後,接下來將使用非常簡單的方式,讓我們能夠隨後開啓 Tensorboard 觀察我們模型定義的長相,並加上一系列的數據預處理,讓模型訓練的更完善。
p.s. 點擊此 進入 keras 官網,點擊此 進入 keras 的 GitHub 源代碼。
Import Data 導入數據
構建神經網絡之前,最重要的還是數據本身,而這裏將繼續沿用前面面幾個章節中所使用的兩個模型 MNIST 與 CIFAR10,和與其對應的函數代碼,並簡單打印出引入數據集圖像對應標籤的結果。
import gzip
from Code_Session.TF_04 import mnist as M
import pickle
from Code_Session.TF_05 import cifar2cnn as cc
MNIST Dataset
path_mnist = '/Users/kcl/Documents/Python_Projects/01_AI_Tutorials/_2_Image_Datasets/MNIST_data'
mnist = M.MNIST(val_ratio=0.0, data_dir=path_mnist)
print(mnist.img_train.shape)
(60000, 784)
mnist_img_train = M.format_images(mnist.img_train)
mnist_lab_train = mnist.lab_train
mnist_cls_name = [i for i in range(10)]
cc.plot_images(mnist_img_train, mnist.lab_train, mnist_cls_name, size=[3, 4])
CIFAR10 Dataset
path_cifar = '/Users/kcl/Documents/Python_Projects/cifar-10-batches-py'
img_train = cc.merge_batches('data', file_dir=path_cifar)
print(img_train.shape)
(50000, 3072)
file_dir = path_cifar + '/' + 'batches.meta'
def get_class_name(file_dir=file_dir):
with open(file_dir, 'rb') as file:
dic = pickle.load(file, encoding='bytes')
return [w.decode('utf-8') for w in dic[b'label_names']]
cifar_img_train = cc.format_images(img_train)
cifar_lab_train = cc.merge_batches('labels', file_dir=path_cifar)
cc.plot_images(cifar_img_train, cifar_lab_train,
get_class_name(), size=[3, 4])
p.s. 如果對其中的代碼有任何不熟悉,請詳見前面幾回合的內容。
接下來就可以從 Tensorflow 模塊中呼叫 keras 搭建一個非常迅捷且輕便的神經網絡模型。類似 keras 的 API 模塊也有 PrettyTensor 與 layers,不過從 Tensorflow 官網的態度來看,它很可能將在未來被刪減,而主推 keras,同時很多更新的功能 keras 也持續同步着,是一個相對穩健的高級 API,故值得一探究竟。
Load The Model as One Piece 整體模型的載入
new_model = K.models.load_model('save.h5')
Review the Trained Results
關於一個完整的神經網絡,可以查看的內容非常多,而 Keras 也很貼心的幫使用者整理方法,可以很輕鬆的讓我們看清每一層神經之間的參數量,同時使用函數呼叫指定層框架中的參數。
model.summary( )
首先是 summary 函數,調用它來查看我們在上面重新載入回來的模型架構,如下指令。
new_model.summary()
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) (None, 28, 28, 1) 0 _________________________________________________________________ layer1 (Conv2D) (None, 28, 28, 16) 416 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 14, 14, 16) 0 _________________________________________________________________ batch_normalization (BatchNo (None, 14, 14, 16) 64 _________________________________________________________________ layer2 (Conv2D) (None, 14, 14, 36) 14436 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 7, 7, 36) 0 _________________________________________________________________ batch_normalization_1 (Batch (None, 7, 7, 36) 144 _________________________________________________________________ flatten (Flatten) (None, 1764) 0 _________________________________________________________________ dense (Dense) (None, 128) 225920 _________________________________________________________________ dense_1 (Dense) (None, 10) 1290 ================================================================= Total params: 242,270 Trainable params: 242,166 Non-trainable params: 104 _________________________________________________________________
每層之間的參數變化總量都在這個函數的幫助下一覽無遺。
Plot Wrong Predicted Images
接着我們可以使用重新載入的 model 框架中的 predict 函數預測輸入數字圖像的對應類別是什麼,如下代碼:
### Predict MNIST image
rand_img = mnist.img_test.reshape([-1, 28, 28, 1])[3:5004]
rand_lab = mnist.lab_test[3:5004]
lab_name = [i for i in range(10)]
### Predict CIFAR10 image
# rand_img = cifar_img_test[3:100]
# rand_lab = cc.load_binary_data('test_batch', 'labels',
# file_dir=path_cifar).astype(np.int)[3:100]
# lab_name = get_class_name()
# ----------------------------------------------------------------------
pred_oh = new_model.predict(rand_img)
### Only when using MNIST dataset, turn this on
rand_img = rand_img.reshape([-1, 28, 28])
pred_lab = np.argmax(pred_oh, axis=1)
# print(pred_lab)
incorrect = (pred_lab != rand_lab)
cc.plot_images(rand_img[incorrect], rand_lab[incorrect], lab_name,
size=[3, 4], pred_labels=pred_lab[incorrect])
Lookup Weights between Layers
- 首先使用
model.layers
方法呼叫所有的層,以列表 list 的方式回傳 - 指定好選擇的層之後,使用
get_weights()
函數同樣呼叫所有該層的參數集,以列表的方式回傳 - 指定好是哪一個參數集之後,即可得到一個以 numpy list 作爲數據格式的結果
上面的三點說來話長,執行的時候也就不過是下面的一段話,如下代碼:
new_model.layers[4].get_weights()[0].shape
(5, 5, 16, 36)
摳出指定層的指定節點之數字之後,需要進一步圖像話觀察或是特定區域分析權重大小的相關探討,都將變得非常簡單。
還有另一些方法可以呼叫沒層的參數:K.backend
,K.models.Model
如果上面第一個方法行不通了,下面兩個是可以嘗試的切入點。
Tensorboard in Keras
既然 Keras 是依託於 Tensorflow 來執行一切算法與參數的傳遞,那麼 Tensorboard 的使用肯定也是使用者馬上能夠聯想上的一個重要工具,此一工具也非常完好的在 Keras 中被保留和使用,使用方式更是如出一轍的簡單,一個函數 K.callbacks.TensorBoard()
調用即可完成所有的工作(點擊此進入源代碼),不過需要注意安插其他配套代碼的位置與時機,流程如下:
- Construct a model using Sequential or Model function
- Add more layers within a constructed model
- Compile the model
- 1st additional step: Apply K.callbacks.TensorBoard() function
- 2nd additional step: Fit (train) the model with new argument "callbacks=[K...TensorBoard]"
- ... Same procedure ...
詳細代碼將於下面的 Data Preprocessing 環節一同展現,執行完後即得到一個新的文件,使用者即可在終端使用此文件開啓文檔在 Tensorboard 中查看結果。
Data Preprocessing in Keras
說到數據的前期處理,Keras 同樣應該給出了它自己獨有且簡單方便的執行方法,其對應函數爲 K.preprocessing.image.ImageDataGenerator()
,其執行順序如下流程:
- 前期預處理數據時,即使用此函數,並設定一系列參數
- 賦予設定好的函數一個對象,並使用對象的方法 .fit()
- 例行公事的架構神經網絡
- 最後要使用 model.fit() 訓練時,需改成 model.fit_generator()
- 正常執行模型評估
Step 1
此步驟也是耗時最久的一步,先跟 Keras 說明圖像數據要被處理成什麼樣子,再指定是哪些數據要被處理,但是注意一個很重要的一點是,第一步做完後,並沒有任何因爲預處理而佔用的緩存,如下代碼:
import numpy as np
import tensorflow as tf
import tensorflow.keras as K
import matplotlib.pyplot as plt
# First set up what are we going to deal with the images
img_augment = K.preprocessing.image.ImageDataGenerator(
rotation_range=10, width_shift_range=0.2, height_shift_range=0.2,
shear_range=0.15, zoom_range=0.1, channel_shift_range=0.3,
horizontal_flip=True, vertical_flip=False)
# Second appoint the certain images into the preset procedure using .fit
img_augment.fit(cifar_img_train, augment=True)
Step 2
經過第一步驟告知好所有信息後,接下來就讓數據沿着這些信息參數 "流" 出來,經過 .flow 方法流出來的對象是一個需要被遍歷的函數,每次出來的量設定爲一個 batch,放到循環裏面遍歷之後出來的結果就直接是增強過後的數據本身。
# What we will get from the called function below is an "iterable object"
Iteration = img_augment.flow(cifar_img_train,
cifar_lab_train,
batch_size=12)
k = 1
for aug_img, aug_lab in Iteration:
fig, axes = plt.subplots(3, 4)
fig.subplots_adjust(hspace=0.6, wspace=0.6)
for n, ax in enumerate(axes.flat):
ax.imshow(aug_img[n])
xlabel = get_class_name()[aug_lab[n]]
ax.set_xlabel(xlabel)
ax.set_xticks([])
ax.set_yticks([])
plt.show()
print("---------- Separation {} ----------".format(k))
k += 1
---------- Separation 1 ----------
---------- Separation 2 ----------
....
... This process will be iterated all the time~
....
從上面執行的結果我們可以看到,使用遍歷會以每次一個 batch 的速度持續執行,直到 fit 的目標圖像所有都被強化一遍之後纔算結束。
Step 3
接着是我們熟悉的模型構建方法,這裏結合了上面用 Keras 使用 Tensorboard 的代碼環節,並注意在 fit model 的時候使用的函數將不再是 .fit 而是 .fit_generator。這麼一來 model 才能夠接受數據增強所流出來的增強了的數據,而 .flow 後面除了增強的數據外,還接了一個標籤參數,注意使用的標籤是 one hot 形式,放在 .flow 中的標籤會被自動的對應增強的數據被迭代出來。
### Model construction using Sequential method
model = K.models.Sequential()
model.add(K.layers.InputLayer(input_shape=(32, 32, 3)))
model.add(K.layers.Flatten())
model.add(K.layers.Dense(units=128, activation=tf.nn.relu))
model.add(K.layers.Dense(units=128, activation=tf.nn.relu))
model.add(K.layers.Dense(units=10, activation=tf.nn.softmax))
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
# ----------------------------------------------------------------------
### Tensorboard activated by Keras API
tb = K.callbacks.TensorBoard(log_dir='./keras.tb',
histogram_freq=0,
batch_size=128,
write_graph=True,
write_grads=True,
write_images=True)
# embeddings_freq=1,
# embeddings_layer_names=None,
# embeddings_metadata=None)
# Third feed the input images into the model set above letting it "flow"
model.fit_generator(img_augment.flow(cifar_img_train,
cc.one_hot(cifar_lab_train),
batch_size=128),
steps_per_epoch=len(cifar_lab_train)/128, epochs=10,
callbacks=[tb])
Epoch 1/10 391/390 [==============================] - 31s 80ms/step - loss: 1.8821 - acc: 0.3279 Epoch 2/10 391/390 [==============================] - 30s 77ms/step - loss: 1.8726 - acc: 0.3318 Epoch 3/10 391/390 [==============================] - 32s 83ms/step - loss: 1.8704 - acc: 0.3313 Epoch 4/10 391/390 [==============================] - 31s 79ms/step - loss: 1.8581 - acc: 0.3353 Epoch 5/10 391/390 [==============================] - 32s 81ms/step - loss: 1.8555 - acc: 0.3361 Epoch 6/10 391/390 [==============================] - 31s 80ms/step - loss: 1.8506 - acc: 0.3376 Epoch 7/10 391/390 [==============================] - 32s 82ms/step - loss: 1.8516 - acc: 0.3395 Epoch 8/10 391/390 [==============================] - 32s 81ms/step - loss: 1.8442 - acc: 0.3399 Epoch 9/10 391/390 [==============================] - 31s 80ms/step - loss: 1.8419 - acc: 0.3439 Epoch 10/10 391/390 [==============================] - 31s 79ms/step - loss: 1.8327 - acc: 0.3455
Step 4
最後一步例行公事檢測一下模型經過數據增強後的效果,能夠發現其一定程度的提升了分類的準確率,同時也增強了模型泛化的能力。
cifar_img_test = cc.format_images(cc.load_binary_data(
'test_batch', 'data', file_dir=path_cifar) / 255.0)
cifar_lab_test = cc.one_hot(cc.load_binary_data(
'test_batch', 'labels', file_dir=path_cifar).astype(np.int))
cifar_loss, cifar_acc = model.evaluate(cifar_img_test, cifar_lab_test)
print('The loss value: {}'.format(cifar_loss))
print('The accuracy: {}'.format(cifar_acc))
10000/10000 [==============================] - 1s 61us/step The loss value: 1.6989541765213012 The accuracy: 0.4079
p.s. 如果準確率沒有顯著提升,表示數據增強的強度可能過大,或是訓練的 epoch 次數不夠多表示。