Brief 概述
从上一章节的范例中我们成功使用 Keras 搭建全联接神经网络和卷积神经网络,并分别使用此二网络对最基础的两个数据集 MNIST 与 CIFAR10 做分类,旨在让大家了解到使用 Keras 搭建神经网络的简洁性和人性化的一面。
Keras 从本来独立发展的第三方模块,到后来被 Tensorflow 认可后在随后的 tf 升级版本中被纳入 tf 的高级 API 行列中,确实证明了其设计理念的合理性和必要性,让建构神经网络的知识门槛降低,更多人将能够通过此 API 入手亲自搭建神经网络。
继续上节的介绍,完整了解了 Sequential 的搭建方式之后,接下来将要使用第二种搭建方式 Functional 方法,他们的功能一模一样,为什么要学两种的理由将在模型的储存中跟大家说明。
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,故值得一探究竟。
2. Functional Model
跟 Sequential 模式比起来,Functional 模式在建构神经网络上的意思也是相近的,不过差别在于它的构建方式如同函数呼叫一般的使用,首先引入需要用到的模块:
import numpy as np
import tensorflow as tf
import tensorflow.keras as K
mnist_img_size = 28
cifar_img_size = 32
2-1. Model Construction
每层用来构建神经网络的函数,在 Keras 中在还没有被指向一个对象之前,都可以作为一个新的函数本身,让其后面加上一对 () 传入参数至其中,直接开始新一轮的计算,代码如下:
### When taking MNIST as input, define the shape of (28, 28, 1)
inputs = K.layers.Input(shape=(mnist_img_size, mnist_img_size, 1))
### When taking CIFAR as input, define the shape of (32, 32, 3)
# inputs = K.layers.Input(shape=(cifar_img_size, cifar_img_size, 3))
# ----------------------------------------------------------------------
# Construct the structure using Functionnal Model from now
net = inputs
net = K.layers.Conv2D(kernel_size=5, strides=1, filters=16, padding='same',
activation='relu', name='layer1')(net)
net = K.layers.MaxPooling2D(pool_size=2, strides=2)(net)
net = K.layers.BatchNormalization()(net)
net = K.layers.Conv2D(kernel_size=5, strides=1, filters=36, padding='same',
activation='relu', name='layer2')(net)
net = K.layers.MaxPooling2D(pool_size=2, strides=2)(net)
net = K.layers.BatchNormalization()(net)
# Fully connected part
net = K.layers.Flatten()(net)
net = K.layers.Dense(units=128, activation='relu')(net)
net = K.layers.Dense(units=10, activation='softmax')(net)
outputs = net
# ----------------------------------------------------------------------
# Compile the structure into a Model
func_model = K.models.Model(inputs=inputs, outputs=outputs)
func_model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
2-2. Train Model
与 Sequential 的操作流程一般,根据需要使用的函数模型来执行代码,其他的标注起来不启动。
### Train the model using MNIST:
func_model.fit(x=mnist_img_train.reshape([-1, 28, 28, 1]),
y=cc.one_hot(mnist.lab_train),
epochs=2, batch_size=128)
### Train the model using CIFAR10:
# func_model.fit(x=cifar_img_train,
# y=cc.one_hot(cifar_lab_train),
# epochs=3, batch_size=128)
Epoch 1/2 60000/60000 [==============================] - 83s 1ms/step - loss: 0.1151 - acc: 0.9657 Epoch 2/2 60000/60000 [==============================] - 79s 1ms/step - loss: 0.0371 - acc: 0.9891
2-3. Evaluate Model
训练完的模型,使用下面的方法开启测试集,测试结果。
### Evaluate Model using MNIST:
mnist_loss, mnist_acc = func_model.evaluate(mnist.img_test.reshape([-1, 28, 28, 1]),
cc.one_hot(mnist.lab_test))
print('The loss value: {}'.format(mnist_loss))
print('The accuracy: {}'.format(mnist_acc))
# ----------------------------------------------------------------------
### Evaluate Model using CIFAR10:
# 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 = func_model.evaluate(cifar_img_test, cifar_lab_test)
# print('The loss value: {}'.format(cifar_loss))
# print('The accuracy: {}'.format(cifar_acc))
10000/10000 [==============================] - 6s 634us/step The loss value: 0.03831068623477895 The accuracy: 0.9879
Save and Restore Training Results
储存的记录点是我们在做事情的时候非常重要的辅助工具,训练模型的过程当然也需要这项功能。然而如果回顾前面使用 Tensorflow 储存成 checkpoint 与重新呼叫储存参数的方式,非得有几次函数调用的过程才能够完成存档,设计的逻辑并不怎么人性化,是一个比较繁琐的过程。
而 Keras 的储存方式被设计得非常人性化,容易理解之外也容易操作,然而一个深度学习框架有两个部分组成,分别是运算结构与被运算的参数,因此也衍生出两种主要的思路用来储存训练结果:
- 整体储存整体载入
- 分开储存分开载入
Crutial Note:
ValueError: You are trying to load a weight file containing N layers into a model with M layers.
是一个版本更新的问题,普遍出现在 Keras 2.X 的版本中,如果使用了此系列版本的人想避开这个问题的话,换成 Functional Model 来搭建自己的神经网络框架之后则能够解决问题。
有些 Keras 版本里面,只有 Functional Model 可以顺利把储存好的模型和训练参数重新呼叫回模型内,因此 Functional Model 在储存这点上是比 Sequential 设计来的完备的。
1. Save and Load The Model as One Piece 整体储存与载入
储存与载入的过程一共各一行代码即完成所有的工作,储存方式如下:
func_model.save('save.h5')
# In order to save the RAM space, we can delete the useless object
del func_model
重新载入回整个框架与参数的方式如下:
new_model = K.models.load_model('save.h5')
执行完毕后, new_model 对象即附有完整的神经网络框架功能,并执行预测等计算。
2. Save and Load The Model Separately 分开储存与载入
在 Keras 中,框架的保存使用 json 形式完成,数据的保存使用 save_weights() 函数实现,保存方式如下代码:
func_model.save_weights('save_weights.h5')
model_json = func_model.to_json()
# print(model_json)
with open('model.json', 'w') as file:
file.write(model_json)
重新唤回模型之后,就可以使用 load_weights() 函数把参数完整叫回来,继续上次训练结果开始预估新的数据。
with open('model.json', 'r') as file:
json_string = file.read()
# print(json_string)
new_model = K.models.model_from_json(json_string)
new_model.load_weights('save_weights.h5')
接着还有一段续集,将主要描述如何使用 Keras 开启可视化好帮手 "Tensorboard",与如何使用数据增强,适当且简单的改变图片的性质。