文章目录
1.常见功能模块
Keras 提供了一系列高层的神经网络类和函数,如常见数据集加载函数,网络层类,
模型容器,损失函数类,优化器类,经典模型类等等
1.1 网络层类
对于常见的神经网络层,可以使用张量方式的底层接口函数来实现,这些接口函数一般在 tf.nn
模块中。更常用地,对于常见的网络层,我们一般直接使用层方式来完成模型的搭建,在 tf.keras.layers
命名空间(下文使用 layers 指代 tf.keras.layers)中提供了大量常见网络层的类接口,如全连接层,激活含水层,池化层,卷积层,循环神经网络层等等。对于这些网络层类,只需要在创建时指定网络层的相关参数,并调用__call__
方法即可完成前向计算。在调用__call__
方法时,Keras 会自动调用每个层的前向传播逻辑,这些逻辑一般实现在类的 call 函数中。
我们以 Softmax 层为例,它既可以使用 tf.nn.softmax
函数在前向传播逻辑中完成Softmax 运算,也可以通过 layers.Softmax(axis)
类搭建 Softmax 网络层,其中 axis 参数指定进行 softmax 运算的维度。首先导入相关的子模块:
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
x = tf.constant([2,1,0.1])
layer = layers.Softmax(axis = -1)
layer(x)
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([0.6590012 , 0.24243298, 0.09856589], dtype=float32)>
1.2 网络容器
通过Keras提供的网络容器 Sequential 将多个网络层封装成一个大网络模型,只需要调用网络模型的实例一次即可完成数据从第一层到最末层的顺序运算。
from tensorflow.keras import layers, Sequential
network = Sequential([
layers.Dense(3,activation = None),
layers.ReLU(),
layers.Dense(2,activation=None),
layers.ReLU()
])
x = tf.random.normal([4,3])
network(x)
<tf.Tensor: shape=(4, 2), dtype=float32, numpy=
array([[0. , 0. ],
[3.074291 , 3.12873 ],
[0.8761316 , 0.89162457],
[0.91199094, 1.1000608 ]], dtype=float32)>
Sequential 容器也可以通过 add()
方法继续追加新的网络层,实现动态创建网络的功能:
layer_num = 2
network = Sequential([]) # 创建空网格
for _ in range(layer_num):
network.add(layers.Dense(3))# 添加全连接层
network.add(layers.ReLU())# 添加激活函数层
network.build(input_shape=(None, 4)) # 创建网络参数
network.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_6 (Dense) (None, 3) 15
_________________________________________________________________
re_lu_6 (ReLU) (None, 3) 0
_________________________________________________________________
dense_7 (Dense) (None, 3) 12
_________________________________________________________________
re_lu_7 (ReLU) (None, 3) 0
=================================================================
Total params: 27
Trainable params: 27
Non-trainable params: 0
_________________________________________________________________
上述代码通过指定任意的 layers_num 参数即可创建对应层数的网络结构,在完成网络创建
时,很多类并没有创建内部权值张量等成员变量,此时通过调用类的 build 方法并指定输
入大小,即可自动创建所有层的内部张量。通过 summary()
函数可以方便打印出网络结构
和参数量。
当我们通过 Sequential
容量封装多层网络层时,所有层的参数列表将会自动并入Sequential
容器的参数列表中,不需要人为合并网络参数列表。Sequential 对象的trainable_variables
和 variables
包含了所有层的待优化张量列表和全部张量列表:
for p in network.trainable_variables:
print(p.name, p.shape)
dense_6/kernel:0 (4, 3)
dense_6/bias:0 (3,)
dense_7/kernel:0 (3, 3)
dense_7/bias:0 (3,)
2.模型训练与测试
2.1 获取数据
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics
from tensorflow import keras
def preprocess(x, y):
"""
x is a simple image, not a batch
"""
x = tf.cast(x, dtype=tf.float32) / 255.
x = tf.reshape(x, [28*28])
y = tf.cast(y, dtype=tf.int32)
y = tf.one_hot(y, depth=10)
return x,y
batchsz = 128
(x, y), (x_val, y_val) = datasets.mnist.load_data()
print('datasets:', x.shape, y.shape, x.min(), x.max())
db = tf.data.Dataset.from_tensor_slices((x,y))
db = db.map(preprocess).shuffle(60000).batch(batchsz)
ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))
ds_val = ds_val.map(preprocess).batch(batchsz)
sample = next(iter(db))
print(sample[0].shape, sample[1].shape)
datasets: (60000, 28, 28) (60000,) 0 255
(128, 784) (128, 10)
2.2 创建模型
在 Keras 中,有 2 个比较特殊的类:keras.Model
和 keras.layers.Layer
类。其中 Layer类是网络层的母类,定义了网络层的一些常见功能,如添加权值,管理权值列表等。Model 类是网络的母类,除了具有 Layer 类的功能,还添加了保存、加载模型,训练与测试模型等便捷功能。Sequential 也是 Model 的子类,因此具有 Model 类的所有功能。
接下来介绍 Model 及其子类的模型装配与训练功能。我们以 Sequential 容器封装的网络为例,首先创建 5 层的全连接网络用于 MNIST 手写数字图片识别:
# 创建 5 层的全连接层网络
network = Sequential([layers.Dense(256, activation='relu'),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(32, activation='relu'),
layers.Dense(10)])
network.build(input_shape=(None, 28*28))
network.summary()
Model: "sequential_10"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_33 (Dense) (None, 256) 200960
_________________________________________________________________
dense_34 (Dense) (None, 128) 32896
_________________________________________________________________
dense_35 (Dense) (None, 64) 8256
_________________________________________________________________
dense_36 (Dense) (None, 32) 2080
_________________________________________________________________
dense_37 (Dense) (None, 10) 330
=================================================================
Total params: 244,522
Trainable params: 244,522
Non-trainable params: 0
_________________________________________________________________
创建网络后,正常的流程是通过循环迭代数据集多遍,每次按批产生训练数据,前向计算,然后通过损失函数计算误差值,并反向传播自动计算梯度,更新网络参数。这一部分逻辑由于非常通用,在 keras 中提供了 compile()
和 fit()
函数方便实现上述逻辑。首先通过compile
函数指定网络使用的优化器对象,损失函数,评价指标等:
# 导入优化器,损失函数模块
from tensorflow.keras import optimizers, losses
# 采用 Adam 优化器,学习率为 0.01;采用交叉熵损失函数,包含 Softmax
network.compile(optimizer=optimizers.Adam(lr=0.01),
loss=losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy'] # 设置测量指标为准确率
)
2.3 模型训练
模型装配完成后,即可通过 fit()
函数送入待训练的数据和验证用的数据集:
# 指定训练集为 train_db,验证集为 val_db,训练 5 个 epochs,每 2 个 epoch 验证一次
# 返回训练信息保存在 history 中
history = network.fit(db, epochs=5, validation_data=ds_val, validation_freq=2)
Epoch 1/5
469/469 [==============================] - 2s 3ms/step - loss: 0.2703 - accuracy: 0.9182
Epoch 2/5
469/469 [==============================] - 2s 4ms/step - loss: 0.1338 - accuracy: 0.9628 - val_loss: 0.1072 - val_accuracy: 0.9691
Epoch 3/5
469/469 [==============================] - 2s 4ms/step - loss: 0.1106 - accuracy: 0.9697
Epoch 4/5
469/469 [==============================] - 2s 5ms/step - loss: 0.0945 - accuracy: 0.9748 - val_loss: 0.1062 - val_accuracy: 0.9704
Epoch 5/5
469/469 [==============================] - 2s 3ms/step - loss: 0.0873 - accuracy: 0.9773
可以看到通过 compile&fit 方式实现的代码非常简洁和高效,大大缩减了开发时间。但是因
为接口非常高层,灵活性也降低了,是否使用需要用户自行判断。
2.4 模型测试
通过 Model.predict(x)
方法即可完成模型的预测:
x,y = next(iter(ds_val))
pred = network.predict(x) # [b, 10]
y = tf.argmax(y, axis=1)
pred = tf.argmax(pred, axis=1)
print((y == pred).numpy().sum() / y.shape)
[0.9921875]
3.模型保存与加载
3.1 张量方式
网络的状态主要体现在网络的结构以及网络层内部张量参数上,因此在拥有网络结构源文件的条件下,直接保存网络张量参数到文件上是最轻量级的一种方式。
通过调用 Model.save_weights(path)
方法即可讲当前的网络参数保存到 path 文件上:
network.save_weights('weights.ckpt')
上述代码将 network 模型保存到 weights.ckpt 文件上,在需要的时候,只需要先创建好网络对象,然后调用网络对象的 load_weights(path)
方法即可将指定的模型文件中保存的张量数值写入到当前网络参数中去:
network.save_weights('weights.ckpt')
print('saved weights.')
del network
network = Sequential([layers.Dense(256, activation='relu'),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(32, activation='relu'),
layers.Dense(10)])
network.compile(optimizer=optimizers.Adam(lr=0.01),
loss=tf.losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
network.load_weights('weights.ckpt')
print('loaded weights!')
network.evaluate(ds_val)
saved weights.
loaded weights!
79/79 [==============================] - 0s 6ms/step - loss: 0.1163 - accuracy: 0.9701
[0.11633136123418808, 0.9700999855995178]
3.2 网络方式
我们来介绍一种不需要网络源文件,仅仅需要模型参数文件即可恢复出网络模型的方式。通过 Model.save(path)
函数可以将模型的结构以及模型的参数保存到一个 path 文件上,在不需要网络源文件的条件下,通过 keras.models.load_model(path)
即可恢复网络结构和网络参数。
network.save('model.h5')
print('saved total model.')
del network
print('loaded model from file.')
network = tf.keras.models.load_model('model.h5', compile=False)
saved total model.
loaded model from file.
3.3 SavedModel方式
TensorFlow 之所以能够被业界青睐,除了优秀的神经网络层 API 支持之外,还得益于它强大的生态系统,包括移动端和网页端的支持。当需要将模型部署到其他平台时,采用TensorFlow 提出的 SavedModel 方式更具有平台无关性。
通过 tf.keras.experimental.export_saved_model(network, path)
即可将模型以 SavedModel方式保存到 path 目录中:
# 保存模型结构与模型参数到文件
tf.keras.experimental.export_saved_model(network, 'model-savedmodel')
print('export saved model.')
del network # 删除网络对象
# 从文件恢复网络结构与网络参数
network = tf.keras.experimental.load_from_saved_model('model-savedmodel')
即可恢复出网络结构和参数,方便各个平台能够无缝对接训练好的网络模型。