购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第3章
TensorFlow构建模型的方法

第2章我们用TensorFlow的自动微分及优化器等方法,实现了一个比较简单的回归问题。在本章,我们将继续对如何高效构建模型、训练模型做进一步说明。根据构建模型的方式,本章将分为如下3种方法:

❑低阶API建模

❑中阶API建模

❑高阶API建模

3.1 利用低阶API构建模型

用TensorFlow低阶API构建模型主要包括张量、计算图及自动微分等操作,这种方法灵活性高,如果构建模型继承tf.Module,还可以轻松实现保存模型及跨平台部署。为提高模型运行效率,我们还可以使用@tf.function装饰相关函数,将其转换为自动图。为了更好地掌握本节的相关内容,这里以分类项目为例进行举例说明。

3.1.1 项目背景

这里以CIFAR-10为数据集,数据导入和预处理使用自定义函数,为更有效地处理数据,这里使用tf.data工具。有关tf.data的详细使用将在第4章介绍,这里不再详述。构建模型只使用TensorFlow的低阶API,如tf.Variable、tf.nn.relu、自动微分等。然后自定义训练过程,最后保存和恢复模型。

CIFAR-10为小型数据集,一共包含10个类别的RGB彩色图像:飞机(airplane)、汽车(automobile)、鸟(bird)、猫(cat)、鹿(deer)、狗(dog)、蛙(frog)、马(horse)、船(ship)和卡车(truck)。图像的尺寸为32×32(像素,后续如无特殊要求,单位均为像素),3个通道,数据集中一共有50000张训练图像和10000张测试图像。CIFAR-10数据集有3个版本,这里使用Python版本。

3.1.2 导入数据

1)导入需要的模块。

2)定义导入单批次的函数。因数据源分成几个批次,这里定义一个导入各批次的函数。

3)导入整个数据集。

4)指定数据文件所在路径。

5)说明类别及对应索引,并随机可视化其中5张图像。

随机抽取CIFAR-10数据集中5张图的结果如图3-1所示。

图3-1 随机抽取CIFAR-10数据集中5张图的结果

3.1.3 预处理数据

1)对数据进行简单处理。对数据进行规范化,并设计相关超参数等。

2)使用TensorFlow的数据预处理工具tf.data,将预处理过程打包为一个管道。

dataset中shuffle()、repeat()、batch()、prefetch()等函数的主要功能分析如下。

1)repeat(count=None)表示重复此数据集count次,实际上,我们看到的repeat往往是接在shuffle后面的。为何要这么做,而不是先repeat再shuffle呢?如果shuffle在repeat之后,epoch与epoch之间的边界就会模糊,出现未遍历完数据、已经计算过的数据又出现的情况。

2)shuffle(buffer_size, seed=None, reshuffle_each_iteration=None)表示将数据打乱,数值越大,混乱程度越大。为了完全打乱,buffer_size应等于数据集的数量。

3)batch(batch_size, drop_remainder=False)表示按照顺序取出batch_size大小数据,最后一次输出可能小于batch,如果程序指定了每次必须输入批次的大小,那么应将drop_remainder设置为True以防止产生较小的批次,默认为False。

4)prefetch(buffer_size)表示使用一个后台线程以及一个buffer来缓存batch,以提前为模型的执行程序准备好数据。一般来说,buffer的大小应该至少和每一步训练消耗的batch数量一致,也就是与GPU/TPU的数量相同。我们也可以使用AUTOTUNE来设置。此时创建一个Dataset后,系统便可从该数据集中预提取元素。

注意 examples.prefetch(2)表示将预提取2个元素(2个示例),而examples.batch(20). prefetch(2)表示将预提取2个元素(2个批次,每个批次有20个示例)。buffer_size表示预提取时将缓存的最大元素数返回Dataset。

使用prefetch可以把数据处理与模型训练的交互方式由图3-2变为图3-3。

图3-2 未使用prefetch的数据处理流程

图3-3 使用prefetch后的数据处理流程

3.1.4 构建模型

使用tf.Module封装变量及其计算,可以使用任何Python对象,有利于保存模型和跨平台部署使用。因此,可以基于TensorFlow开发任意机器学习模型(而非仅仅神经网络模型),并实现跨平台部署使用。

1)构建模型。构建一个继承自tf.Module的模型,修改基类的构造函数,把需要初始化的变量放在__init__构造函数中,把参数变量的正向传播过程放在__call__方法中。__call__方法在模型实例化时将被自动调用。为提供更好的运行效率,通常用@tf.function进行装饰。

2)定义损失函数和评估函数。

3.1.5 训练模型

1)实现反向传播。这里使用自动微分机制,并使用优化器实现梯度的自动更新,具体过程如下:

❑打开一个GradientTape()作用域;

❑在此作用域内,调用模型(正向传播)并计算损失;

❑在作用域之外,检索模型权重相对于损失的梯度;

❑根据梯度使用优化器来更新模型的权重;

❑利用优化器进行反向传播(更新梯度)。

2)定义训练过程。

这是最后6次的运行结果:

3)可视化运行过程。

随着迭代次数增加,模型准确率的变化如图3-4所示。

图3-4 随着迭代次数增加,模型准确率的变化

3.1.6 测试模型

对模型进行测试。

运行结果如下:

构建模型只使用了全连接层,没有对网络进行优化,但测试能达到这个效果也不错。后续我们将采用数据增强、卷积神经网络等方法进行优化。

3.1.7 保存恢复模型

1)保存模型。

2)恢复模型。

3)利用恢复的模型进行测试。

运行结果如下:

由结果可知,它与原模型的测试结果完全一样!

3.2 利用中阶API构建模型

用TensorFlow的中阶API构建模型,主要使用TensorFlow或tf.keras提供的各种模型层、损失函数、优化器、数据管道、特征列等,无须自己定义网络层、损失函数等。如果定制性要求不高,这种方法将大大提高构建模型的效率。利用中阶API构建模型时需要继承tf.Module,它是各种模型层的基类。为更好地掌握相关内容,还是使用3.1节的数据集,架构相同,只是把定义层改为直接使用tf.keras提供的层、优化器、评估函数等。

利用中阶API构建模型的导入数据、预处理数据等部分,与3.1节的相应部分一样,这里不再赘述。下面主要介绍两种方法的不同之处。

3.2.1 构建模型

用TensorFlow的中阶API构建模型,需要使用TensorFlow或tf.keras提供的各种模型层、损失函数、优化器、数据管道、特征列等,无须自己定义网络层、损失函数等。因数据为图像,这里使用全连接层,故第一层使用Flatten层,把数据展平。

3.2.2 创建损失评估函数

损失函数使用tf.keras.metrics.Mean类,评估函数使用tf.keras.metrics.SparseCategoricalAccuracy类,使用该类标签无须转换为独热编码。

3.2.3 训练模型

1)定义训练函数。

2)训练模型。

最后6次的运行结果:

3)可视化训练结果。

使用中阶API构建模型的可视化结果如图3-5所示。

图3-5 使用中阶API构建模型的可视化结果

4)测试模型。

运行结果如下:

利用中阶API构建模型、实现训练,比直接使用低阶API简化了不少,但编码量还是比较大,尤其是定义评估函数、训练过程等,接下来我们介绍一种更简单、高效的方法,即利用高阶API构建模型。

3.3 利用高阶API构建模型

TensorFlow的高阶API主要是指tf.keras.models提供的模型的类接口。目前tf.keras为官方推荐的高阶API。使用ff.keras接口构建模型的方法有3种。

❑序列API(Sequential API)模式,把多个网络层线性堆叠以构建模型。

❑函数式API(Functional API)模式,可构建任意结构模型。

❑子类模型API(Model Subclassing API)模式,使用继承Model基类的子类模型构建自定义模型。

这里先使用序列API按层顺序构建模型,其他构建方法将在第7章详细介绍。

数据导入与数据预处理过程与3.1节一样,这里不再赘述。

3.3.1 构建模型

这里主要使用tf.keras.models及tf.keras.layers高阶类,构建模型采用序列API方法,这种方法就像搭积木一样,非常直观和简单。首先实例化Sequential类,然后使用add方法把各层按序叠加在一起,并基于优化器、损失函数等方法编译模型,最后输入数据训练模型。这个过程可用图3-6来直观描述。

图3-6 序列API构建模型流程图

导入需要的库,构建模型。

显示模型各层及其结构,如图3-7所示。

图3-7 显示模型各层及其结构

构建Sequential模型时,第一层需要明确输入数据的形状,其他各层只要指明输出形状即可,输入形状可自动推导。因此,Sequential的第一层需要接收一个关于输入数据形状(shape)的参数。那么,如何指定第一层的输入形状呢?有以下几种方法来为第一层指定输入数据的形状。

❑传递一个input_shape参数给第一层。它是一个表示形状的元组(一个由整数或None组成的元组,其中None表示可能为任何正整数)。input_shape中不包含数据批次(batch)大小。

❑有些2维层,如全连接层(Dense),支持通过指定其输入维度(input_dim)来隐式指定输入数据的形状,input_dim是一个整数类型的数据。一些3维的时域层支持通过参数input_dim和input_length来指定shape。

❑对于某些网络层,如果需要为输入指定一个固定大小的批量值(batch_size),可以传递batch_size参数到一个层中。例如你想指定输入张量的batch大小为32,数据shape为(6, 8),则需要传递batch_size=32和input_shape=(6, 8)给一个层,此时每一批输入的形状就为(32, 6, 8)。

3.3.2 编译及训练模型

在训练模型之前,我们需要对模型进行编译配置,这是通过compile方法完成的。它接收3个参数。

❑优化器(optimizer):可以是内置优化器的字符串标识符,如rmsprop或adagrad,也可以是Optimizer类的对象。

❑损失函数(loss):模型最小化的目标函数。它可以是内置损失函数的字符串标识符,如categorical_crossentropy或mse等,也可以是自定义的损失函数。

❑评估标准(metrics):可以是内置的标准字符串标识符,也可以是自定义的评估标准函数。对于分类问题,一般设置metrics=['accuracy']。

1)编译模型。

2)训练模型。这里采用回调机制定时保存模型。

最后两次的迭代结果:

最后,根据模型训练的结果自动保存最好的那个模型。

3)可视化运行结果。

使用高阶API构建模型的可视化结果如图3-8所示。

图3-8 使用高阶API构建模型的可视化结果

3.3.3 测试模型

输入如下代码测试模型。

运行结果如下:

3.3.4 保存恢复模型

1)选择最好的模型参数恢复模型。

2)查看网络结构。

运行结果与图3-7的网络结构完全一致。

3)检查其准确率(accuracy)。

运行结果如下:

由结果可知,其结果与预测结果完全一致!

3.4 小结

本章介绍了几种常用的API构建网络和训练模型的方法。这些API从封装程度来划分,可分为低阶API、中阶API和高阶API。低阶API基本用TensorFlow实现,如自定义层、损失函数等;中阶API使用层模块(如tf.keras.layers.Dense)、内置的损失函数、优化器等构建模型;高阶API的封装程度最高,使用tf.keras构建模型、训练模型。这3种方法各有优缺点,低阶API代码量稍多一些,但定制能力较高,而用高阶API构建模型和训练模型的代码比较简洁,但定制能力稍弱。实际上,我们的大部分任务基本都可以用高阶API来实现,对初学者来说,使用高阶API构建模型、训练模型是首选。 ZJkW3AM/06U53lHv0lNXflQ01FYzkeAwhnGPZ2PkG68DYAGEtZANOjP9UbAwSpOI

点击中间区域
呼出菜单
上一章
目录
下一章
×