TensorFlow2.0深度学习(八)Keras高层接口

        在TensorFlow 2版本中,Keras被正式确定为TensorFlow的高层API唯一接口,取代了TensorFlow 1版本中自带的tf.layers等高层接口。也就是说,现在只能使用Keras的接口来完成TensorFlow层方式的模型搭建与训练。

1. 常见功能模块

        Keras提供了一系列高层的神经网络类和函数,如常见数据集加载函数,网络层类,模型容器,损失函数类,优化器,经典模型类等等。

1.1 常见网络层类

        对于常见的神经网络层,可以使用张量方式的底层接口函数来实现,这些接口函数一般在tf.nn模块中。更常用地,对于常见的网络层,一般直接使用层方式来完成模型的搭建,在tf.keras.layers命名空间中提供了大量常见网络层的类接口,如全连接层,激活函数层,池化层,卷积层,循环神经网络层等等。对于这些网络层类,只需要在创建时指定网络层的相关函数,并调用__call__方法即可完成前向计算。在调用__call__方法时,Keras会自动调用每个层的前向传播逻辑,这些逻辑一般实现在类的call函数中。

        以Softmax层为例,它既可以使用tf.nn.softmax函数在前向传播逻辑中完成Softmax运算,也可以通过layers.Softmax(axis)类搭建Softmax网络层,其中axis参数指定进行softmax运算的维度。        

1.2 网络容器

        对于常见的网络,需要手动调用每一层的类实例完成前向传播运算,当网络层数变得较深时,这一部分代码显得非常臃肿。可以通过Keras提供的网络容器Sequential将多个网络层封装成一个大网络模型,只需要调用网络模型的实例一次即可完成数据从第一层到最末层的顺序运算。

2. 模型装配、训练与测试

        在训练网络时,一般的流程是通过前向计算获得网络的输出值,再通过损失函数计算网络误差,然后通过自动求导工具计算梯度并更新,同时间隔性地测试网络的性能。对于这种常用的训练逻辑,可以直接通过Keras提高的模型装配与训练高层接口实现,简洁清晰。

2.1 模型装配

        在Keras中,有2个比较特殊的类:keras.Model和keras.layers.Layer类。其中Layer类是网络层的母类,定义了网络层的一些常见功能,如添加权值,管理权值列表等。Model类是网络的母类,除了具有Layer类的功能,还添加了保存、加载模型,训练与测试模型等便捷功能。Sequential也是Model的子类,因此具有Model类的所有功能。

        接下来介绍Model及其子类的模型装配与训练功能。以Sequential容器封装的网络为例,首先创建5层的全连接网络用于MNIST手写数字图片识别:

        创建网络后,正常的流程是通过循环迭代数据集多遍,每次按批产生训练数据,前向计算,然后通过损失函数计算误差值,并反向传播自动计算梯度,更新网络参数。这一部分逻辑由于非常态通用,在keras中提供了compile()和fit()函数方便实现上述逻辑。

        首先通过compile函数指定网络使用的优化器对象,损失函数,评价指标等:

2.2 模型训练

        模型装配完成后,即可通过fit()函数送入待训练的数据和验证用的数据集:

        运行上述代码即可实现网络的训练和验证的功能,fit函数会返回训练过程的数据记录history,其中history.history为字典对象,包含了训练过程中的loss,测量指标等记录项。

2.3 模型测试

        通过Model.predict(x)方法即可完成模型的预测:

其中out即为网络输出。

        如果只是简单的测试模型的性能,可以通过Model.evaluate(db)即可循环测试完db数据集上所有样本,并打印出性能指标。

3. 模型保存与加载

        模型训练完成后,需要将模型保存到文件系统上,从而方便后续的模型测试与部署工作。实际上,在训练时间间隔性地保存模型状态也是非常好的习惯,这一点对于训练大规模的网络尤其重要,一般大规模的网络需要训练数天乃至数周的时长,一旦训练过程被中断,之前训练的进度将全部丢失。如果能够间断的保存模型状态到文件系统,即使发生宕机等意外,也可以从最近一次的网络状态文件中恢复,从而避免浪费大量的训练时间。

        在Keras中,有三种常用的模型保存与加载方法。

3.1 张量方式

        网络的状态主要体现在网络的结构以及网络层内部张量参数上,因此在拥有网络结构源文件的条件下,直接保存网络张量参数到文件上是最轻量级的一种方式。以MNIST手写数字图片识别模型为例,通过调用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!')

        这种保存与加载网络的方式最为轻量级,文件中保存的仅仅是参数张量的数值,并没有其他额外的结构参数。但是它需要使用相同的网络结构才能够恢复网络状态,因此一般在拥有网络源文件的情况下使用。

3.2 网络方式

        通过Model.save(path)函数可以将模型的结构以及模型的参数保存到一个path文件上,在不需要网络源文件的条件下,通过keras.models.load_model(path)即可恢复网络结构和网络参数。

        首先将MNIST手写数字图片识别模型保存到文件上,并且删除网络对象:

# 保存模型结构与模型参数到文件
network.save('model.h5')
print('saved total model.')
del network # 删除网络对象

此时通过model.h5文件即可恢复出网络的结构和状态:

network = tf.keras.models.load_model('model.h5')

可以看到,model.h5文件除了保存了模型参数外,还保存了网络结构信息,不需要提前创建模型即可直接从文件中恢复出网络network对象。

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 # 删除网络对象

此时在文件系统model-savedmodel目录中出现如下网络文件:

用户无需关心文件的保存格式,只需要通过

# 从文件恢复网络结构与网络参数
network = tf.keras.experimental.load_from_saved_model('model-savedmodel')

即可恢复出网络结构和参数,方便各个平台能够无缝对接训练好的网络模型。

4. 自定义类

        对于需要创建自定义逻辑的网络层,可以通过自定义类来实现。在创建自定义网络层类时,需要继承自layers.Layer基类;创建自定义的网络类,需要继承自keras.Model基类,这样产生的自定义类才能够方便的利用Layer/Model基类提供的参数管理功能,同时也能够与其他的标准网络层类交互使用。

4.1 自定义网络层

        对于自定义的网络层,需要实现初始化__init__方式和前向传播逻辑call方法。以某个具体的自定义网络层为例,假设需要一个没有偏置的全连接层,即bias为0,同时固定激活函数为ReLU()函数。尽管这可以通过标准的Dense层创建,但还是实现这个自定义类。

        首先创建类并继承自Layer基类,创建初始化方法,并调用母类的初始化函数,由于是全连接层,需要设置特征的长度inp_dim和输出特征的长度outp_dim,并通过self.add_variable(name, shape)创建shape大小,名字为name的张量,并设置为需要优化:

class MyDense(layers.Layer):
    # 自定义网络层
    def __init__(self, inp_dim, outp_dim):
        super(MyDense, self).__init__()
        # 创建权值张量并添加到类管理列表中,设置为需要优化
        self.kernel = self.add_variable('w', [inp_dim, outp_dim],trainable=True)

        需要注意的是,self.add_varibable会返回此张量的python引用,而变量名name由TensorFlow内部维护,使用的比较少。

        完成自定义类的初始化工作后,设计自定义类的前向运算逻辑,对于这个例子,只需要完成O=X@W矩阵运算,并通过激活函数即可:

def call(self, inputs, training=None):
    # 实现自定义类的前向计算逻辑
    # X@W
    out = input @ self.kernel
    # 执行激活函数运算
    out = tf.nn.relu(out)
    return out
4.2 自定义网络

        在完成了自定义的全连接层类之后,基于上述的“无偏置的全连接层”来实现MNIST手写数字图片模型的创建。

        自定义的类可以和其他标准类一样,通过Sequential容器方便地包裹成一个网络模型:

network = Sequential([MyDense(784, 256),
                MyDense(256, 128),
                MyDense(128, 64),
                MyDense(64, 32),
                MyDense(32, 10)])
network.build(input_shape=(None, 28*28))
network.summary()

        可以看到,通过堆叠的自定义类,一样可以实现5层的全连接层网络,每层全连接层无偏置张量,同样激活函数固定使用ReLU。

        更普遍地,可以继承基类来实现任意逻辑的自定义网络类。下面来创建自定义网络类,首先创建并继承Model基类,分步创建对应的网络层对象:

class MyModel(keras.Model):
    # 自定义网络类,继承在Model基类
    def __init__(self):
        super(MyModel, self).__init__()
        # 完成网络内需要的网络层的创建工作
        self.fc1 = MyDense(28*28, 256)
        self.fc2 = MyDense(256, 128)
        self.fc3 = MyDense(128, 64)
        self.fc4 = MyDense(64, 32)
        self.fc5 = MyDense(32, 10)

然后实现自定义网络的前向运算逻辑:

def call(self, inputs, training=None):
    # 自定义前向运算逻辑
    x = self.fc1(inputs)
    x = self.fc2(x)
    x = self.fc3(x)
    x = self.fc4(x)
    x = self.fc5(x)
    return x

        这个例子可以直接使用第一种方式通过Sequential容器包裹。但是由于Sequential在前向传播时依次调用每个网络层的前向传播函数,灵活性一般,而自定义网络的前向逻辑可以任意定制,两者各有优缺点。

5. 模型乐园

        对于常用的网络模型,如ResNet,VGG等,不需要手动创建网络,可以直接从keras.applications子模块下一行代码即可创建并使用这些经典模型,同时还可以通过设置weights参数加载预训练的网络参数,非常方便。

        以ResNet50迁移学习为例,一般将ResNet50去掉最后一层后的网络作为新任务的特征提取子网络,即利用ImageNet上面预训练的特征提取方法迁移到自定义的数据集上,并根据自定义任务的类别追加一个对应数据类别数的全连接分类层,从而可以在预训练网络的基础上可以快速、高效地学习新任务。

        首先利用Keras模型加载ImageNet预训练的ResNet50网络:

# 加载ImageNet预训练网络模型,并去掉最后一层
resnet = keras.application.ResNet50(weights='imagenet', include_top=False)
resnet.summary()
# 测试网络的输出
x = tf.random.normal([4,224,224,3])
out = resnet(x)
out.shape

        上述代码自动从服务器下载模型结构和在ImageNet数据集上预训练好的网络参数,由于去掉最后一层,网络的输出大小为[b,7,7,2048]。对于某个具体的任务,需要设置自定义的输出节点数,以100类的分类任务为例,在ResNet50基础上重新构建新网络。新建一个池化层,将特征从[b,7,7,2048]降维到[b,2048]:

# 新建池化层
global_average_layer = layers.GlobalAveragePooling2D()
# 利用上一层的输出作为本层的输入,测试其输出
x = tf.random.normal([4,7,7,2048])
out = global_average_layer(x) # 池化层降维
print(out.shape)

最后新建一个全连接层,并设置输出节点数为100:

# 新建全连接层
fc = layers.Dense(100)
# 利用上一层的输出作为本层的输入,测试其输出
x = tf.random.normal([4, 2048])
out = fc(x)
print(out.shape)

在得到预训练的ResNet50特征层和新建的池化层、全连接层后,重新利用Sequential容器封装成一个新的网络:

# 重新包裹成自己的网络模型
mynet = Sequential([resnet, global_average_layer, fc])
mynet.summary()

新的网络模型的结构信息:

通过设置resnet.trainable=False可以选择冻结ResNet部分的网络参数,只训练新建的网络层,从而快速、高效完成网络模型的训练。

6. 测量工具

        在网络的训练过程中,经常需要统计准确率,召回率等信息,除了可以通过手动计算并平均方式获取统计数据外,Keras提供了一些常用的测量工具keras.metrics,专门用于统计训练过程中需要的指标数据。

        Keras的测量工具的使用一般有4个基本操作流程:新建测量器,写入数据,读取统计数据和清零测量器。

6.1 新建测量器

        在keras,metrics模块下,提供了较多的常用测量类,如统计平均值的Mean类,统计准确率的Accuracy类,统计余弦相似度的CosineSimilarity类等等。下面以统计误差值为例,在前向运算时,会得到每一个batch的平均误差,但是希望统计一个epoch的平均误差,因此选择使用Mean测量器:

# 新建平均测量器,适合Loss数据
loss_meter = metrics.Mean()
6.2 写入数据

        通过测量器的update_state函数可以写入新的数据:

# 记录采样的数据
loss_meter.update_state(float(loss))

上述采样代码放置在每个batch运算完成后,测量器会自动根据采样的数据来统计平均值。

6.3 读取统计信息

        在采用多次后,可用测量器的result()函数获取统计值:

# 打印统计的平均loss
print(step, 'loss:', loss_meter.result())
6.4 清除

       通过reset_states()即可实现。例如,在每次读取完平均误差后,清零统计信息,以便下一轮统计的开始:

if step % 100 == 0:
    # 打印统计的平均loss
    print(step, 'loss:', loss_meter.result())
    loss_meter.reset_states() # 清零测量器

7. 可视化

        在网络训练的过程中,通过Web端监控网络的训练进度,可视化网络的训练结果对于提高开发效率是非常重要的。TensorFlow提供了一个专门的可视化工具,叫做TensorBoard,他通过TensorFlow将监控数据写入到文件系统,并利用Web后端监控对应的文件目录,从而可以允许用户从远程查看网络的监控数据。

        TensorBoard的使用需要训练部分和浏览器交互工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值