赞
踩
目录
参考来源:主页 - Keras 中文文档
Keras 提供了一系列高层的神经网络相关类和函数,如经典数据集加载函数、 网络层
类、 模型容器、 损失函数类、 优化器类、 经典模型类等
导入keras,只能从tensorflow中导入,否则导入的是标准的Keras库 :
from tensorflow import keras
对于常见的网络层,一般直接使用层方式来完成模型的搭建,在 tf.keras.layers 命名空间中提供了大量常见网络层的类,如全连接层、 激活函数层、 池化层、 卷积层、 循环神经网络层
网络层类,只需要在创建时指定网络层的相关参数, 并调用__call__方法即可完成前向计算。在调用__call__方法时, Keras 会自动调用每个层的前向传播逻辑,这些逻辑一般实现在类的
call 函数中
eg: 通过 layers.Softmax(axis)类搭建 Softmax 网络层
- import tensorflow as tf
- from tensorflow import keras # 导入keras,只能从tf中导入,否则导入的是标准的Keras库
- from tensorflow.keras import layers # 导入网络层类
-
- # 创建 Softmax 层,并调用__call__方法完成前向计算
- x = tf.constant([2., 1.0, 0.1]) # 创建张量
- layer = layers.Softmax(axis=-1) # 创建Softmax层
- out = layer(x) # 向前计算,其实是调用的call
- print(out)
直接调用tf.nn.softmax()函数也是一样的效果
out = tf.nn.softmax(x)
对于常见的网络,需要手动调用每一层的类实例完成前向传播运算,当网络层数变得
较深时, 这一部分代码显得非常臃肿。可以通过 Keras 提供的网络容器 Sequential 将多个
网络层封装成一个大网络模型,只需要调用网络模型的实例一次即可完成数据从第一层到
最末层的顺序传播运算
eg: 2层的全连接层和激活函数层
- import tensorflow as tf
- from tensorflow import keras # 导入keras,只能从tf中导入,否则导入的是标准的Keras库
- 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])
- out = network(x) # 调用call(),输入从第一层开始, 逐层传播至输出层,并返回输出层的输出
-
- network.build(input_shape=(4, 4)) # 创建网络层参数
- network.summary() # 打印出网络结构和参数量
-

通过 add()方法继续追加新的网络层, 实现动态创建网络的功能
- layers_num = 2 # 堆叠 2 次
- network = Sequential([]) # 先创建空的网络容器
- for _ in range(layers_num):
- network.add(layers.Dense(3)) # 添加全连接层
- network.add(layers.ReLU()) # 添加激活函数层
注意,在通过Sequential容器创建对应层数的网络结构,在完成网络创建时, 网络层类并没有创建内部权值张量等成员变量,此时通过调用类的 build 方法并指定输入大小,即可自动创建所有层的内部张量。
通过 summary()函数可以方便打印出网络结构和参数量
输出结果:

Layer 列为每层的名字,这个名字由 TensorFlow 内部维护,与 Python 的对象名并不一样。 Param#列为层的参数个数, Total params 项统计出了总的参数量, Trainable params
为总的待优化参数量, Non-trainable params 为总的不需要优化的参数量
当通过 Sequential 容量封装多个网络层时, 每层的参数列表将会自动并入Sequential 容器的参数列表中。 Sequential 对象的 trainable_variables 和 variables 包含了所有层的待优化张量列表
和全部张量列表
- # 打印网络的待优化参数名与shape
- for item in network.trainable_variables:
- print(item.name, item.shape)
-
- '''
- dense/kernel:0 (3, 3)
- dense/bias:0 (3,)
- dense_1/kernel:0 (3, 2)
- dense_1/bias:0 (2,)
- '''
在训练网络时,一般的流程是通过前向计算获得网络的输出值, 再通过损失函数计算网络误差,然后通过自动求导工具计算梯度并更新,同时间隔性地测试网络的性能
对于这种常用的训练逻辑,可以直接通过 Keras 提供的模型装配与训练等高层接口实现
在 Keras 中,有 2 个比较特殊的类: keras.Model 和 keras.layers.Layer 类。其中 Layer类是网络层的母类,定义了网络层的一些常见功能,如添加权值、 管理权值列表等。
Model 类是网络的母类,除了具有 Layer 类的功能,还添加了保存模型、加载模型、 训练
与测试模型等便捷功能
Sequential 也是 Model 的子类, 因此具有 Model 类的所有功能
在创建网络后,正常的流程是循环迭代数据集多个 Epoch,每次按批产生训练数据、 前向计
算,然后通过损失函数计算误差值,并反向传播自动计算梯度、 更新网络参数
在 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'] # 设置测量指标为准确率
- )
模型装配完成后,可通过 fit()函数送入待训练的数据集和验证用的数据集,这一步称为模型训练
- # 指定训练集为 train_db,验证集为 val_db,训练 5 个 epochs,每 2 个 epoch 验证一次
- # 返回训练轨迹信息保存在 history 对象中
- history = network.fit(train_db, epochs=5, validation_data=val_db,
- validation_freq=2)
其中 train_db 为 tf.data.Dataset 对象;epochs 参数指定训练迭代的 Epoch 数量;validation_data 参数指定用于验证(测试)的数据集和验证的频率validation_freq
上述代码即可实现网络的训练与验证的功能, fit 函数会返回训练过程的数据记录history, 其中 history.history 为字典对象,包含了训练过程中的 loss、 测量指标等记录项,可以直接查看这些训练数据:history.history
fit()函数的运行代表了网络的训练过程,因此会消耗相当的训练时间,并在训练结束后才返回,训练中产生的历史数据可以通过返回值对象取得
可以看到通过 compile&fit 方式实现的代码非常简洁和高效,大大缩减了开发时间
Model 基类除了可以便捷地完成网络的装配与训练、验证,还可以预测和测试。通过 Model.predict(x)方法即可完成模型的预测
- x,y = next(iter(db_train))
- print('predict x:', x.shape) # 打印当前 batch 的形状
- out = network.predict(x) # 模型预测,预测结果保存在 out 中
- print(out)
out 即为网络的输出,通过上述代码即可使用训练好的模型去预测新样本的标签信息
可以通过 Model.evaluate()测试模型的性能指标
network.evaluate(db_test) # 模型测试,测试在 db_test 上的性能表
模型训练完成后,需要将模型保存到文件系统上,从而方便后续的模型测试与部署工作。 如果能够间断地保存模型状态到文件系统,即使发生宕机等意外,也可以从最近一次的网络状态文件中恢复,从而避免浪费大量的训练时间和计算资源
网络的状态主要体现在网络的结构以及网络层内部张量数据上,因此在拥有网络结构源文件的条件下,直接保存网络张量参数到文件系统上是最轻量级的一种方式
通过调用 Model.save_weights(path)方法即可将当前的网络参数保存到 path 文件上
- # 保存模型参数到文件上
- network.save_weights('weights.ckpt')
- ....
- # 重新搭建相同的网络结构
- ...
- # 从参数文件中读取数据并写入当前网络
- network.load_weights('weights.ckpt')
这种保存与加载网络的方式最为轻量级, 文件中保存的仅仅是张量参数的数值,并没有其它额外的结构参数。 但是它需要使用相同的网络结构才能够正确恢复网络状态,因此一般在拥有网络源文件的情况下使用
通过 Model.save(path)函数可以将模型的结构以及模型的参数保存到 path 文件上,在需要网络源文件的条件下,通过 keras.models.load_model(path)即可恢复网络结构和网络参数
- # 保存模型结构与模型参数到文件
- network.save('model.h5')
-
- del network # 删除网络对象
-
- # 从文件恢复网络结构与网络参数
- network = keras.models.load_model('model.h5')
model.h5 文件除了保存了模型参数外,还应保存了网络结构信息,不需要提前创建模型即可直接从文件中恢复出网络 network 对象
通过 tf.saved_model.save (network, path)即可将模型以 SavedModel 方式保存到 path 目录中
- # 保存模型结构与模型参数到文件
- tf.saved_model.save(network, 'model-savedmodel')
- del network # 删除网络对象
- network = tf.saved_model.load('model-savedmodel')
用户无需关心文件的保存格式,只需要通过 tf.saved_model.load 函数即可恢复出模型对象
对于需要创建自定义逻辑的网络层,可以通过自定义类来实现。在创建自定义网络层类时,需要继承自 layers.Layer 基类; 创建自定义的网络类时,需要继承自 keras.Model 基类, 这样建立的自定义类才能够方便的利用 Layer/Model 基类提供的参数管理等功能,同时也能够与其他的标准网络层类交互使用
对于自定义的网络层, 至少需要实现初始化__init__方法和前向传播逻辑 call 方法
假设需要一个没有偏置向量的全连接层,即 bias 为0, 同时固定激活函数为 ReLU 函数
- # 自定义网络层
- class MyDence(layers.Layer):
-
- # 自定义类的初始化
- def __init__(self, inp_dim, outp_dim):
- # 继承父类,并定义新功能 super
- super(MyDence, self).__init__()
- # 创建权值张量并添加到类管理列表中,设置为需要优化
- self.kernel = self.add_variable('w', [inp_dim, outp_dim], trainable=True)
- # self.bias = self.add_variable('b', [outp_dim])
-
- # 自定义类的向前计算逻辑
- def call(self, inputs, training=None):
- # X@W
- out = inputs @ self.kernel
- # 添加激活函数relu
- out = tf.nn.relu(out)
- return out
-
- # 创建自定义层类的实例化
- net = MyDence(4, 3) # 输入为4,输出为3个节点

首先创建类,并继承自 Layer 基类。 创建初始化方法,并调用母类的初始化函数, 由于是全连接层, 因此需要设置两个参数:输入特征的长度 inp_dim 和输出特征的长度outp_dim,并通过 self.add_variable(name, shape)创建 shape 大小,名字为 name 的张量
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。