TensorFlow DAY3: 高阶 API(Keras,Estimator)
TensorFlow 作为深度学习框架,当然是为了帮助我们更便捷地构建神经网络。所以,本次实验将会了解如何使用 TensorFlow 来构建神经网络,并学会 TensorFlow 构建神经网络的重要函数和方法。
知识点
- Keras 顺序模型
- Keras 函数模型
- Keras 模型存储及推理
- Estimator 高阶 API
Keras 本来是一个用 Python 编写的独立高阶神经网络 API,它能够以 TensorFlow,CNTK,或者 Theano 作为后端运行。目前,TensorFlow 已经吸纳 Keras,并组成了 tf.keras
模块 。官方介绍,tf.keras
和单独安装的 Keras 略有不同,但考虑到未来的发展趋势,实验以学习 tf.keras
为主。
通过查阅官方文档,你可以发现 tf.keras
下又包含 10 多个子模块,而这些子模块下又集成了大量的类和函数。由于 Keras 先前是独立发布的,实际上我们在仅使用 tf.keras
的情况下,就几乎能够满足深度神经网络搭建的全部需求。
下面,我们对 Keras 进行快速上手介绍。Keras 的核心数据结构是 Model,一种组织网络层的方式。其中有两种模式,最简单且最常用的模型是 Sequential 顺序模型,它是由多个网络层线性堆叠的栈。对于更复杂的结构,可以使用 Keras 函数式 API,它允许构建任意的神经网络图。
Keras 顺序模型
使用 Keras 创建一个 Sequential 顺序模型非常简单,只需要 1 行代码 tf.keras.models.Sequential()
就可以搞定。
import tensorflow as tfmodel = tf.keras.models.Sequential() # 定义顺序模型
model
当然,上面这行代码肯定还不是一个神经网络模型,只有向其中添加不同结构的神经网络层,才能真正成为一个我们需要的神经网络模型。这里我们选择前面实验已经构建过的简单全连接神经网络。
Keras 中的神经网络层全部位于 tf.keras.layers
下方。这里包含了各自经典神经网络所需的功能层,例如我们这里需要的全连接层 tf.keras.layers.Dense
。
Dense(units, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)
- units: 正整数,输出空间维度。
- activation: 激活函数。若不指定,则不使用激活函数 (即线性激活:
a(x) = x
)。 - use_bias: 布尔值,该层是否使用偏置项量。
- kernel_initializer:
kernel
权值矩阵的初始化器。 - bias_initializer: 偏置项量的初始化器。
- kernel_regularizer: 运用到
kernel
权值矩阵的正则化函数。 - bias_regularizer: 运用到偏置项的正则化函数。
- activity_regularizer: 运用到层的输出的正则化函数。
- kernel_constraint: 运用到
kernel
权值矩阵的约束函数。 - bias_constraint: 运用到偏置项量的约束函数。
Dense 实现操作: output = activation(dot(input, kernel) + bias)
,其中 activation
是按逐个元素计算的激活函数,kernel
是由网络层创建的权值矩阵,以及 bias
是其创建的偏置项 (只在 use_bias
为 True
时才有用)。通过参数你就会发现,这是一个封装了很多功能的高阶 API。
下面,我们就向上面定义好的顺序模型 model
中添加 2 个全连接层。
# 添加全连接层
model.add(tf.keras.layers.Dense(units=30, activation=tf.nn.relu)) # 输出 30,relu 激活
model.add(tf.keras.layers.Dense(units=10, activation=tf.nn.softmax)) # 输出 10,softmax 激活
你可以发现,一开始我们定义顺序模型就像是「打地基」,而只需要使用 add
就可以「盖楼房」了。上面的代码中,激活函数 tf.nn.relu
也可以用名称 'relu'
代替,即可写作 tf.keras.layers.Dense(units=30, activation='relu')
,具体参考 官方文档。
添加完神经网络层之后,就可以使用 model.compile
来编译顺序模型。这时,需要通过参数指定优化器,损失函数,以及评估方法。
# adam 优化器 + 交叉熵损失 + 准确度评估
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])
其中,参数可以使用如上所示的名称或者 TensorFlow 实例。如果使用实例的优化器和损失函数,需要从 TensorFlow 支持的 全部优化器列表 和 全部损失函数列表 中选择。如果使用名称,需要从 Keras 支持的 名称列表 中选择。
特别注意的是,这里的损失函数是不能胡乱选的。你需要根据网络的输出形状和真实值的形状来决定。如果不匹配就会报错。由于实验示例网络最后通过了 tf.nn.softmax
激活,那么对于单个样本输入最终就会得到一个概率最大的整数类型的输出。此时就选择了 sparse_categorical_crossentropy
具有整数输出类型的交叉熵损失函数。随着实验后续深入,见的越多就会有使用经验了。
PS:上面指的是你最终拿到的真实标签是one-hot还是说是整数值,这个sparse_categorical_crossentropy 最终也会把这个整数值转换成one-hot进行运算
接下来,我们定义数据,同样选择 DIGITS 数据集。
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_digits
import numpy as np# 准备 DIGITS 数据
digits = load_digits()
X_train, X_test, y_train, y_test = train_test_split(digits.data, digits.target, test_size=0.2, random_state=1)X_train.shape, X_test.shape, y_train.shape, y_test.shape
这里无需像前面的实验对目标值进行独热编码,原因在于上面定义的网络最终输出本身就是样本类别整数。
定义完数据之后,使用 model.fit
就可以开始模型训练了。这个过程和使用 scikit-learn 训练模型一样直观。我们需要指定小批量 batch_size
大小,和全数据迭代次数 epochs
参数。
# 模型训练
model.fit(X_train, y_train, batch_size=64, epochs=5)
你可以看到每一个 Epoch 之后模型在训练数据上的损失和分类准确度。使用 model.evaluate
即可评估训练后模型在测试集上的损失和分类准确度。
# 模型评估
model.evaluate(X_test, y_test)
实际使用过程中,我们一般会直接将测试数据通过 validation_data
参数传入训练过程。那么,每一个 Epoch 之后都会同时输出在训练集和测试集上的分类评估结果。
# 使用参数传入测试数据
model.fit(X_train, y_train, batch_size=64, epochs=5,validation_data=(X_test, y_test))
可以看到,区别于前面实验中较为复杂的模型定义过程,使用 Keras 提供的顺序模型只需要数行左右的代码即可完成。除此之外,你不需要初始化权重,编写前向计算图,设计 Mini Batch 机制,自定义评估方法和过程等。基本上把搭建神经网络中复杂的过程都省略了,只留下了直观的构建方法。这也就是 Keras 深受欢迎的原因,也是 TensorFlow 将 Keras 纳入的原因。
前面虽然我们仅使用了少数几个方法,但是你会发现每个方法涉及到的参数都非常多。这些参数可以帮助开发者在高阶 API 上进一步实现高度自定义过程。对于初学者而言,不要想一次性全部了解,随着后续对深度学习的逐渐深入,慢慢的都会熟悉起来。实际情况中,就算熟悉的开发者也不可能记得住每个 API 及参数,很多时候都是需要使用时才查阅官方文档。退一步讲,由于 TensorFlow API 变化更新的速度非常快,就算这次记住了,下次说不定还是需要查阅官方文档。
Keras 函数模型
除了顺序模型,Keras 也提供函数式 API。和顺序模型最大的不同在于,函数模型可以通过多输入多输出的方式。并且所有的模型都是可调用的,就像层一样利用函数式模型的接口,我们可以很容易的重用已经训练好的模型。
下面,我们通过函数式 API 来重写上面的模型结构。
inputs = tf.keras.Input(shape=(64,)) # 输入层
x = tf.keras.layers.Dense(units=30, activation='relu')(inputs) # 中间层
outputs = tf.keras.layers.Dense(units=10, activation='softmax')(x) # 输出层# 函数式 API 需要指定输入和输出
model = tf.keras.Model(inputs=inputs, outputs=outputs)
model
模型编译和训练和顺序模型基本一致。
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])model.fit(X_train, y_train, batch_size=64, epochs=20,validation_data=(X_test, y_test))
通过上面的示例,相信你已经看出了顺序模型和函数式模型的区别。函数式模型看起来更符合建模流出,有清晰的输入和输出,灵活性也更好一些。实际上对于二者,主要是看个人使用习惯,初学者可以根据自己的偏好自由选择。
Keras 模型存储及推理
使用 Keras 训练完成的模型可以非常方便地保存下来,保存模型的目的主要有 2 个:
- 对于较大的训练任务,保存模型可以方便后续恢复重新使用。
- 保存后的模型可以方便模型部署。
TensorFlow 模型一般包含 3 类要素,分别是:模型权重值、模型配置乃至优化器配置。
如果只需要保存模型权重值,可以使用 tf.keras.Model.save_weights
,并指定存放路径。
model.save_weights('./weights/model') # 保存检查点名称为 model,路径为 ./weights
!ls './weights/' # 直接运行查看目录下文件
model.load_weights('./weights/model') # 恢复检查点
默认情况下,该方法会以 TensorFlow 检查点文件格式 保存模型的权重。检查点文件是 TensorFlow 特有的模型权重保存方法,其默认会以每 10 分钟(600 秒)写入一个检查点,训练时间较短则只保存一个检查点。检查点默认情况下只保存 5 个,即模型训练过程中不同时间点的版本状态。
我们一般会在大型任务训练时设置检查点保存。这样做的好处在于一旦因为意外情况导致训练终止,TensorFlow 可以加载检查点状态,避免又需要从头开始训练。
如果我们需要模型推理,一般情况会使用 model.save
保存完整的模型,即包含模型权重值、模型配置乃至优化器配置等。例如,下面将模型存为 Keras HDF5 格式,其为 Keras 多后端实现的默认格式。
model.save('model.h5') # 保存完整模型
接下来,可以使用 tf.keras.models.load_model
重载模型。
model_ = tf.keras.models.load_model('model.h5') # 调用模型
model.summary()
可以用来查看 Keras 模型结构,包含神经网络层和参数等详细数据。
model_.summary()
然后,使用 predict
方法就可以完成模型推理了。
preds = model_.predict(X_test[:3]) # 预测前 3 个测试样本
preds
预测结果为神经网络通过 Softmax 激活后的输出值。所以我们通过 NumPy 找出每个样本输出最大概率及其对应的索引,其索引也就是最终的预测目标了。同样,可以输出测试数据真实标签进行对比。
np.argmax(preds, axis=1), np.max(preds, axis=1) # 找出每个样本预测概率最大值索引及其概率
y_test[:3] # 直接运行查看前 3 个测试样本真实标签
一般情况下,模型的预测是正确的。如果错误可以检查上方模型的评估情况,合理增大 Epoch 以提高模型准确度。
Estimator 高阶 API
上面已经对使用 tf.keras
构建神经网络进行了学习。当然,随着后续学习的深入会逐步介绍更多用途的神经网络层,以及更丰富的用法。本小节中,我们会对 TensorFlow 另一个非常重要的组件 Estimator 进行介绍。