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 次数不够多表示。