Brief 概述

使用 keras 搭建模型时让人们感受到的简洁性与设计者的用心非常直观的能够在过程中留下深刻的印象,这个模块帮可以让呈现出来的代码极为人性化且一目了然,使用 Tensorflow 模块搭建神经网络模型通常需要百行的代码,自定义模型和函数,唯一受到 tf 封装的厉害功能只有梯度下降的自动取极值,如果是一个初出入门的人,没有一定的基础背景累积,更遑论使用 tf 搭建神经网络。

其大量封装了一系列的复杂深度学习原理成为一个又一个简洁的函数模块,构建的时候即便基础知识差一些也可以非常快的上手搭建工作,是一个对初学者非常友好的接口。不过其封装的函数总量适用的对象远远不止初学者这么简单,其内部还有非常完整的高级功能,同时也满足高级框架的搭建!

Tensorflow_08A_Keras 助攻下的 Sequential 模型

keras 模块原本并非是 Tensorflow 的高级 API,而是一个身在 Google 工作的工程师基于 Tensorflow 和 Theano 开发出来的第三方模块,让其他人也能够快速的构建属于自己的模型,到了后来此模块逐渐受到大众青睐后,Google 于是把它收边进了 Tensorflow high level API 中,现在已经逐渐形成了使用者社群,并有许多巨头以此模块作为基础开发基于他们熟悉的底层框架对应的 API 接口,相信不久的未来 Keras 将成为每种框架的统一接口,让熟悉 Keras 的人们能够在各种框架中根据性能的优劣自由切换。

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])

Tensorflow_08A_Keras 助攻下的 Sequential 模型

 

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])

Tensorflow_08A_Keras 助攻下的 Sequential 模型

 

p.s. 如果对其中的代码有任何不熟悉,请详见前面几回合的内容。

接下来就可以从 Tensorflow 模块中呼叫 keras 搭建一个非常迅捷且轻便的神经网络模型。类似 keras 的 API 模块也有 PrettyTensor 与 layers,不过从 Tensorflow 官网的态度来看,它很可能将在未来被删减,而主推 keras,同时很多更新的功能 keras 也持续同步着,是一个相对稳健的高级 API,故值得一探究竟。

 

Keras Insight

此 API 主要有两种模式可以让我们建构神经网络:

  1. Sequential Model
  2. Functional Model

他们彼此之间在背后代码上的运算过程是一样的,差别主要在于我们使用者书写上的差异,Sequential 的代码看起来和一般的代码没什么区别,基本山看到代码就能了解其含义,而 Functional 方法写起来是让参数带入函数里面的函数,等下面内容提及到代码就会知晓。

既然是搭建神经网络专用的 API,那么整个框架主要的功能也就只围绕着相关的机制操作,主要的机制如下陈列:

  1. .add: 添加一层新的神经网络
  2. .compile: 把上面呢所有添加好的神经网络全部打包
  3. .fit: 训练设定好的神经网络
  4. .evaluate: 计算神经网络的损失值和验证集正确率
  5. .predict: 计算新的数据在此模型的正确率
  6. .save: 把更新到一定阶段的神经网络参数储存起来,如同 checkpoint
  7. .load_model: 重新载入储存好的神经网络参数文档

不论是哪一个神经网络搭建方式,都同样遵循上面一到七的步骤,基本上一个人熟练的情况下,每一个神经网络的搭建都可以在 5 分钟内完成,比起其他的快速搭建方式,keras 同时也是最为完整的一个 API,每个上述列举的方法内还有与之方法对应的细分方法可以调用,可以自行查找官方文档了解。

 

1-1. Sequential Linear Model

keras 可以从 tensorflow 中被呼叫,也可以直接使用 keras 本身的模块,本系列由于跟 tensorflow 相关,因此主要从 tf 中使用 keras,不过模块中的函数名称和代码使用方式基本上是完全相同的。

首先呼叫出 sequential 方法本身,如下代码:

import numpy as np
import tensorflow as tf
import tensorflow.keras as K

接着使用 Sequential 创建一个对象,基于这个对象开始逐层添加神经网络结构至对象中,其中 Dense 方法表示全联接的意思,Dense 里面的数字项表示的是该全联接层有几个输出神经元。

等搭建到了最后一层神经网络结构时,如果是分类问题的话,则激励函数调整成 softmax 相关的方式即可。

model = K.models.Sequential()
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))

建构的方式还有另一种代码写法,可以参考 官网的示范,不過為了不讓自己的記憶混淆,建议从一而终。

完成神经网络的构建之后,接下来把整个框架使用 compile 打包起来,在参数部分设定需要使用的梯度下降函数和损失函数的使用算法。如果对于梯度下降算法有更细节调整的需要,可以进一步引入下面模块,使用对象的方式设定好之后再传入 .compile 方法中。

opt = K.optimizers.Adam(lr=1e-3)
model.compile(optimizer=opt,      # option no.1
              # optimizer='adam'  # option no.2
              loss='categorical_crossentropy',
              metrics=['accuracy'])

最后输入我们期望训练的数据开始训练模型,并试图让损失函数降到最低。输入数据标签如果是分类问题,那就必须是 one hot 形式,否则会报错。在参数像中调整好 epochs 的次数后就可以开始训练。

 

1-1-1. Train MNIST Dataset

首先使用上面搭建好的神经网络模型运行 MNIST 数据集,以 "图像" 与 "one hot" 形式作为输入尤为重要:

model.fit(M.format_images(mnist.img_train), 
          cc.one_hot(mnist_lab_train, class_num=10), 
          epochs=5)
Epoch 1/5
60000/60000 [==============================] - 5s 86us/step - loss: 0.2346 - acc: 0.9306
Epoch 2/5
60000/60000 [==============================] - 5s 79us/step - loss: 0.0981 - acc: 0.9696
Epoch 3/5
60000/60000 [==============================] - 5s 78us/step - loss: 0.0683 - acc: 0.9778
Epoch 4/5
60000/60000 [==============================] - 5s 79us/step - loss: 0.0514 - acc: 0.9833
Epoch 5/5
60000/60000 [==============================] - 5s 79us/step - loss: 0.0424 - acc: 0.9862

完成训练后接下来使用验证集测试训练模型的结果,同样的输入参数需要使用图像数据格式(不能是拉直状态),并且标签使用 one hot 格式。

mnist_loss, mnist_acc = model.evaluate(M.format_images(mnist.img_test), 
                                       cc.one_hot(mnist.lab_test))
print('The loss value: {}'.format(mnist_loss))
print('The accuracy: {}'.format(mnist_acc))
10000/10000 [==============================] - 0s 32us/step
The loss value: 0.08600640584569191
The accuracy: 0.9754

1-1-2. Train CIFAR10 Dataset

接下来是完全一摸一样的操作,重复一遍套用在 CIFAR10 数据集上,唯一的差别是数据本身多了一个颜色通道,同样多的图片张数却要多出三倍的运算量。

完整的代码如下:

model.fit(cc.format_images(img_train),
          cc.one_hot(cifar_lab_train, class_num=10),
          epochs=5, batch_size=128)
Epoch 1/5
50000/50000 [==============================] - 8s 170us/step - loss: 1.8626 - acc: 0.3288
Epoch 2/5
50000/50000 [==============================] - 8s 156us/step - loss: 1.6912 - acc: 0.3922
Epoch 3/5
50000/50000 [==============================] - 8s 159us/step - loss: 1.6262 - acc: 0.4178
Epoch 4/5
50000/50000 [==============================] - 8s 159us/step - loss: 1.5786 - acc: 0.4355
Epoch 5/5
50000/50000 [==============================] - 8s 160us/step - loss: 1.5457 - acc: 0.4477

接着同样步骤使用验证集的数据检测训练完成的模型的准确率,切记同样需要使用非拉直状态的图像数据和 one hot 形式的标签数据作为参数输入。

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 57us/step
The loss value: 1.5932202987670898
The accuracy: 0.4278

 

1-2. Sequential CNN Model

线性模型构建的方式使用了全联接层的方法,而论及卷积神经网络则需要使用到卷积核扫描之,建构神经网络的方法从核心概念来看是类似的,不过多了一个卷积层构建的函数调用,首先同样导入需要用到的模块:

import numpy as np
import tensorflow as tf
import tensorflow.keras as K

接着同样使用模块的功能开始创建神经网络,如果一开始设定神经层的格式需要修改,可以参考下面方法,分作两种输入数据的格式。

1-2-1. MNIST input

mnist_img_size = 28

cnn_model = K.models.Sequential()

# To define the format of input data. Dimension scale and size.
cnn_model.add(K.layers.InputLayer(input_shape=(mnist_img_size**2,)))
cnn_model.add(K.layers.Reshape((mnist_img_size, mnist_img_size, 1)))

1-2-2. CIFAR10 input

cifar_img_size = 32

cnn_model = K.models.Sequential()

# To define the format of input data. Dimension scale and size.
cnn_model.add(K.layers.InputLayer(input_shape=(cifar_img_size, cifar_img_size, 3)))

上面两种输入模式根据执行需求自行选择一个执行。


接着开始 CNN 卷积神经网络的搭建过程,都是使用 .layers 模块里面的方法,为了达到加深印象的目的,每一次呼叫指令的时候都还是把其来源输入一遍,如下代码:

# 1st convolutional layer with ReLU-activation and max-pooling.
cnn_model.add(K.layers.Conv2D(kernel_size=3, strides=1, filters=16, 
                              padding='same', activation='relu', name='layer_conv1'))
cnn_model.add(K.layers.MaxPooling2D(pool_size=2, strides=2))
cnn_model.add(K.layers.BatchNormalization())

# 2nd convolutional layer with ReLU-activation and max-pooling.
cnn_model.add(K.layers.Conv2D(kernel_size=3, strides=1, filters=36, 
                              padding='same', activation='relu', name='layer_conv2'))
cnn_model.add(K.layers.MaxPooling2D(pool_size=2, strides=2))
cnn_model.add(K.layers.BatchNormalization())

# Flatten the 4D tensor into 2D tensor for next fully connected step
cnn_model.add(K.layers.Flatten())

# 1st FC layer with 128 output units
cnn_model.add(K.layers.Dense(units=128, activation='relu'))
cnn_model.add(K.layers.BatchNormalization())

# 2nd FC layer connecting to classification answers
cnn_model.add(K.layers.Dense(units=10, activation='softmax'))

1-2-1. Train MNIST Dataset

框架构建好后,接着开始训练模型,方法与上面线性模型相同,不过输入数据的时候需要特别注意自己先前在模型搭建的时候设定的数据规格,如果有任何一点不一样的话将报错。

opt = K.optimizers.Adam(lr=1e-3)
cnn_model.compile(optimizer=opt,
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])

cnn_model.fit(x=mnist.img_train, 
              y=cc.one_hot(mnist.lab_train),
              epochs=2, batch_size=128)
Epoch 1/2
60000/60000 [==============================] - 43s 724us/step - loss: 0.1180 - acc: 0.9643
Epoch 2/2
60000/60000 [==============================] - 43s 715us/step - loss: 0.0366 - acc: 0.9887

如同在线性模型训练完后所使用验证集准确率测试操作,也使用 evaluate 函数检测准模型准确率。

mnist_loss, mnist_acc = cnn_model.evaluate(mnist.img_test, 
                                           cc.one_hot(mnist.lab_test))
print('The loss value: {}'.format(mnist_loss))
print('The accuracy: {}'.format(mnist_acc))
10000/10000 [==============================] - 3s 297us/step
The loss value: 0.04114889272502623
The accuracy: 0.9879

1-2-2. Train CIFAR10 Dataset

同样步骤训练 CIFAR10 数据集,代码如下:

opt = K.optimizers.Adam(lr=1e-3)
cnn_model.compile(optimizer=opt,
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])

cnn_model.fit(x=cifar_img_train, y=cc.one_hot(cifar_lab_train),
              epochs=3, batch_size=128)
Epoch 1/3
50000/50000 [==============================] - 59s 1ms/step - loss: 1.2917 - acc: 0.5494
Epoch 2/3
50000/50000 [==============================] - 57s 1ms/step - loss: 0.9213 - acc: 0.6799
Epoch 3/3
50000/50000 [==============================] - 52s 1ms/step - loss: 0.7637 - acc: 0.7344

如同在线性模型训练完后所使用验证集准确率测试操作,也使用 evaluate 函数检测准模型准确率。

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 = cnn_model.evaluate(cifar_img_test, cifar_lab_test)
print('The loss value: {}'.format(cifar_loss))
print('The accuracy: {}'.format(cifar_acc))
10000/10000 [==============================] - 4s 410us/step
The loss value: 0.994779656124115
The accuracy: 0.6507

下一章将继续讲述如何使用第二种 Functional 的方法搭建神经网络。

e