tf.keras 搭建网络八股

keras介绍

tf.keras 是 tensorflow2 引入的高封装度的框架,可以用于快速搭建神经网络模型,keras 为支持快速实验而生,能够把想法迅速转换为结果,是深度学习框架之中最终易上手的一个,它提供了一致而简洁的 API,能够极大地减少一般应用下的工作量,提高代码地封装程度和复用性。

深度学习编程框架中的 API 众多,就算是从业很久的算法工程师也不可能记住所有的 API。由于本课程时间有限,只覆盖了 tensorflow2 系列中的最常用的几个 API,仍然还有很多需要在今后的实践中继续学习,这时我们就需要参考tensorflow 的官方文档,通过阅读源码和注释的方法学习 API。
通常有两种方法,以下将分别介绍:

第一种方法: pycharm 集成开发环境中查看框架源码

Pycharm

如上图,将鼠标放置在函数上按住 Ctrl 键,会显示函数的基本信息,包括封装函数的类,函数入口参数,函数功能等。
按住 Ctrl 键点击函数会跳转到函数的源代码部分,使用者可以根据源码和注释进一步了解函数的实现方法。

在 tensorflow 官网中查询函数文档

附上官网网址:https://tensorflow.google.cn/api_docs/python/tf/keras/Model#fit

官网查看

在左侧选择想要查看的API即可。

搭建神经网络六部法

六部格式,定下神经网络训练的基本框架

  • 第一步:import 相关模块,如 import tensorflow as tf。
  • 第二步:指定输入网络的训练集和测试集,如指定训练集的输入 x_train 和标签y_train,测试集的输入 x_test 和标签 y_test。(要使得数据格式符合神经网络的输入格式)
  • 第三步:逐层搭建网络结构model = tf.keras.models.Sequential()
  • 第四步:在 model.compile()中配置训练方法,选择训练时使用的优化器损失函数和最终评价指标
  • 第五步:在 model.fit()中执行训练过程,告知训练集和测试集的输入值标签、每个 batch 的大小(batchsize)和数据集的迭代次数(epoch)。
  • 第六步:使用 model.summary()打印网络结构,统计参数数目。

各个函数用法介绍

tf.keras.models.Sequential()

Sequential 函数是一个容器,描述了神经网络的网络结构,在 Sequential函数的输入参数中描述从输入层到输出层的网络结构。可以在其中定义激活函数、正则化等。

  • 拉直层:tf.keras.layers.Flatten()
    拉直层可以变换张量的尺寸,把输入特征拉直为一维数组,是不含计算参数的层。

  • 全连接层:tf.keras.layers.Dense( 神经元个数,activation=”激活函数”,kernel_regularizer=”正则化方式”)
    其中:
    activation(字符串给出)可选 relusoftmaxsigmoidtanh
    kernel_regularizer 可选 tf.keras.regularizers.l1()tf.keras.regularizers.l2()

  • 卷积层:tf.keras.layers.Conv2D( filter = 卷积核个数, kernel_size = 卷积核尺寸,strides = 卷积步长,padding = “valid” or “same”)

  • LSTM 层:tf.keras.layers.LSTM()。


Model.compile( optimizer = 优化器, loss = 损失函数, metrics = [“准确率”])

Compile 用于配置神经网络的训练方法,告知训练时使用的优化器损失函数准确率评测标准

  1. optimizer 可以是字符串形式给出的优化器名字,也可以是函数形式,使用函数形式可以设置学习率、动量和超参数。

optimizer

注:前期建议使用字符串形式,后面再使用函数设置学习率。
更多optimizer的调用可以去官方文档:tf.keras下的Model查看。

  1. Loss 可以是字符串形式给出的损失函数的名字,也可以是函数形式。

Loss可选性

注:损失函数常需要经过 softmax 等函数将输出转化为概率分布的形式。from_logits 则用来标注该损失函数是否需要转换为概率的形式,取 False 时表示转化为概率分布,取 True 时表示没有转化为概率分布,直接输出。

  1. Metrics 标注网络评测指标。

Metrics可选项

注:我们这里选择第三个,即y为概率分布,y_为数值。


model.fit()

训练集的输入特征, 训练集的标签, batch_size, epochs,
validation_data = (测试集的输入特征,测试集的标签),
validataion_split = 从训练集划分多少比例给测试集,
validation_freq = 测试的 epoch 间隔次数

fit 函数用于执行训练过程。


model.summary()

summary 函数用于打印网络结构参数统计

上图是 model.summary()对鸢尾花分类网络的网络结构和参数统计,对于一个输入为 4 输出为 3 的全连接网络,共有 15 个参数。

iris 数据集代码复现

先给出简化后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import tensorflow as tf
from sklearn import datasets
import numpy as np

x_train = datasets.load_iris().data
y_train = datasets.load_iris().target

np.random.seed(116)
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)

model = tf.keras.models.Sequential([
tf.keras.layers.Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())
])

model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.1),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

model.fit(x_train, y_train, batch_size=32, epochs=500, validation_split=0.2, validation_freq=20)

model.summary()

空间,使用tf.keras后,原本上百行的代码只需要24行。究其原因,在于tf.keras将原本很复杂的底层代码封装成了一个个接口函数,而我们只需要填上对应的参数即可。

下面我们具体分析一下上代码,将六部法套入其中:

第一步:import 相关模块:

1
2
3
import tensorflow as tf
from sklearn import datasets
import numpy as np

第二步:指定输入网络地训练集和测试集:

1
2
x_train = datasets.load_iris().data
y_train = datasets.load_iris().target

其中测试集的输入特征 x_test 和标签 y_test 可以像 x_train 和 y_train 一样直接从数据集获取,也可以如上述在 fit 中按比例从训练集中划分,本例选择从训练集中划分,所以只需加载 x_train,y_train 即可。

1
2
3
4
5
np.random.seed(116)
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)

以上代码实现了数据集的乱序。


第三步:逐层搭建网络结构:

1
2
3
model = tf.keras.models.Sequential([
tf.keras.layers.Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())
])

如上所示,本例使用了单层全连接网络,第一个参数表示神经元个数,第二个参数表示网络所使用的激活函数,第三个参数表示选用的正则化方法。

使用 Sequential 可以快速搭建网络结构,但是如果网络包含跳连等其他复杂网络结构,Sequential 就无法表示了。这就需要使用 class 来声明网络结构。

1
2
3
4
5
6
7
class MyModel(Model):
def __init__(self):
super(MyModel, self).__init__()
//初始化网络结构
def call(self, x):
y = self.d1(x)
return y

使用 class 类封装网络结构,如上所示是一个 class 模板MyModel 表示声明的神经网络的名字,括号中的Model表示创建的类需要继承 tensorflow 库中的 Model 类。类中需要定义两个函数__ init__()函数为类的构造函数,用于初始化类的参数,spuer(MyModel,self).__ init__()这行表示初始化父类的参数。之后便可初始化网络结构,搭建出神经网络所需的各种网络结构块。call()函数中调用__init__()函数中完成初始化的网络块,实现前向传播并返回推理值。
使用 class 方式搭建鸢尾花网络结构的代码如下所示。

class-鸢尾花

搭建好网络结构后只需要使用 Model=MyModel()构建类的对象,就可以使用该模型了。

下面给出使用class类搭建鸢尾花数据集的神经网络:

class

下面给出源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import tensorflow as tf
###########################################
from tensorflow.keras.layers import Dense
from tensorflow.keras import Model
###########################################
from sklearn import datasets
import numpy as np

x_train = datasets.load_iris().data
y_train = datasets.load_iris().target

np.random.seed(116)
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)

##################################################
class IrisModel(Model):
def __init__(self):
super(IrisModel, self).__init__()
self.d1 = Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())

def call(self, x):
y = self.d1(x)
return y

model = IrisModel()
#################################################

model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.1),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

model.fit(x_train, y_train, batch_size=32, epochs=500, validation_split=0.2, validation_freq=20)
model.summary()


注: 和原本的代码相比,改变的地方在代码中标出。
①import 中添加了 Model 模块和 Dense 层、Flatten 层。
②使用 class 声明网络结构,model = IrisModel()初始化模型对象。


第四步:在 model.compile()中配置训练方法:

1
2
3
4
model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.1),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

如上所示,本例使用 SGD 优化器,并将学习率设置为 0.1 ,选择SparseCategoricalCrossentrop 作为损失函数。由于神经网络输出使用了softmax 激活函数,使得输出是概率分布,而不是原始输出,所以需要将
from_logits 参数设置为 False。鸢尾花数据集给的标签是 0,1,2 这样的数值,而网络前向传播的输出为 概率分布,所以 metrics 需要设置为 sparse_categorical_accuracy


第五步:在 model.fit()中执行训练过程:

1
model.fit(x_train, y_train, batch_size=32, epochs=500, validation_split=0.2, validation_freq=20)

在 fit 中执行训练过程,x_train,y_train 分别表示网络的输入特征和标签,batch_size 表示一次喂入神经网络的数据量,epochs 表示数据集的迭代次数,validation_split 表示数据集中测试集的划分比例,validation_freq 表示每迭
代 20 次在测试集上测试一次准确率。


第六步:使用 model.summary()打印网络结构,统计参数数目:

model.summary()

实例:MNIST数据集

MNIST是一个入门的手写数字识别的数据集。

MNIST数据集的介绍

MNIST 数据集一共有 7 万张图片,是 28×28 像素的 0 到 9 手写数字数据集,其中 6 万张用于训练,1 万张用于测试。每张图片包括 784(28×28) 个像素点,使用全连接网络时可将 784 个像素点组成长度为 784 的一维数组,作为输入特征。数据集图片如下所示。

MNIST数据集

数据集的导入:

keras 函数库中提供了使用 mnist 数据集的接口,代码如下所示,可以使用load_data()直接从 mnist 中读取测试集和训练集。

1
2
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

注:这里已经导入了测试数据。


以二位数组的形式打印出来

使用代码print("x_train.shape:\n", x_train.shape)

二维数组

上述代码将读取的x_train的第一层打印出来,是一个28x28的二维数组。
对比矩阵图和PIL得到的图,可以知道在MNIST数据集里是使用灰度值表示具体的数字。

注:在MNIST里,0为黑色,255位白色,介于二者之间的呈现不同的颜色。以数值表示颜色的深浅就是灰度值。


MNIST数据集其它部分的shape

shape

这里我们可以看到,x都是三维矩阵,分别为60000和10000个图片像素矩阵。
而y都是一维数组,每一个表示x图片代表的正确数字,是以数值的形式给出。


使用六部法训练MNIST数据集

先来给出源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import tensorflow as tf

mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])

model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1)
model.summary()

运行结果

result

六部法


使用class定义模型:

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras import Model

mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0


class MnistModel(Model):
def __init__(self):
super(MnistModel, self).__init__()
self.flatten = Flatten()
self.d1 = Dense(128, activation='relu')
self.d2 = Dense(10, activation='softmax')

def call(self, x):
x = self.flatten(x)
x = self.d1(x)
y = self.d2(x)
return y


model = MnistModel()

model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1)
model.summary()

注:与源代码相比,变化的地方只是模型建立区域和开头导入的几个模块。
model


注意事项:

我们这里在导入数据集后进行了归一化,也就是同除255得到[0,1]的数据,利于模型的训练。
同时我们在实际进行手写输入的识别时除了要进行矩阵的变换之外,还要进行归一化处理。

MNIST数据集的图片是黑底白字,所以实际测试时也要是这个样式。

实践二:Fashion_mnist 数据集

Fashion_mnist 数据集具有 mnist 近乎所有的特征,包括 60000 张训练图片和 10000 张测试图片,图片被分为十类,每张图像为 28×28 的分辨率。

数据集类别

图片的示例:
数据集图片

代码实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import tensorflow as tf

fashion = tf.keras.datasets.fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion.load_data()
x_train, x_test = x_train/255, x_test/255

model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])

model.compile(
optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy']
)

model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1)

model.summery()



写在最后:

到此,本篇内容已经学习完毕,最重要的收获就是通过tf.keras进行神经网络的构建与训练,有了前面的基础,这里就可以直接调用API了,也可以理解调用的背后具体的过程了,对于API给定的一个个参数也知道了是什么含义。

这只是最简单的神经网络的学习,却是以后学习的根基,毕竟构建神经网络的六部法是不会变的,适用于以后的所有实践,只是更加细化。