年度归档:2021年

一、纯手工

定制化程度高,非常灵活,如自定义层、损失函数、优化器、性能评估、训练模型方法等,但编程难度较高。
下载所有实例使用数据(提取码为:fg29)

具体实例

二、继承tf.Module

定制化程度高,如自定义层、损失函数、优化器、性能评估、训练模型方法等,且可以使用保存或恢复模型。
具体实例

三、继承tf.Module且使用AutoGraph

定制化程度高,如自定层、损失函数、优化器、性能评估、训练模型方法等,且可以使用保存或恢复模型。通过使用Autograph可达到静态图的性能,极大提升训练模型效率。不过使用Autograph时要注意对应的编码规范,如被@tf,function装饰的函数尽可能tensorflow函数,如tf.print,tf.range等(控制语句无此限制),避免在被@tf,function装饰的函数内部定义tf.Variabe等。
具体实例

四、网络层和模型使用子类方法构建

用子类方法构建网络层、网络模型是tensorflow2才有的,这种风格类似于PyTorch构建方法。TensorFlow1构建网络一般使用Sequential按层顺序构建模型,使用函数式API构建任意结构模型。当然,这两种方法在TensorFlow2也是常用方法。构建网络层时继承
tf.keras.layers.Layer,构建网络模型继承tf.keras.Model, 然后使用compile编辑模型,用fit训练模型。这样构建的网络既有一定的灵活性,又不失简洁性。
具体实例

五、使用tf,keras的Sequential模型按层顺序构建模型

最简洁,构建模型很简单,可是直接使用内置的层、损失函数、优化器、评估指标等,训练也可直接使用函数fit即可。Keras 提供了称为tf.keras.Model 的全功能模型类。它继承自 tf.keras.layers.Layer,使用SaveModel保存模型,可以非常方便移植到其他非Python环境(具体环境可参考下图)。构建这个模型相对较简单,有兴趣的朋友作为练习。

1.机器学习一般流程

机器学习、深度学习的一般流程:
(1)分析业务需求
(2)确定数据源
(3)构建数据处理管道(pipeline)
(4)构建模型
(5)训练模型
(6)评估、优化模型
其中构建数据处理管道,在整个过程中,从时间上来说往往占60%左右,面对大数据其挑战更大。如何解决质量问题?如何解决内存瓶颈问题?如何解决处理效率问题等等。
接下来我们重点介绍如何使用TensorFlow2提供的tf.data工具有效构建数据流。

2.为何要构建数据管道?

使用tf.data API,我们可以使用简单的代码来构建复杂的输入 pipeline。
实现从从内存读取数据、从分布式文件系统中读取数据、进行预处理、合成为 batch、训练中使用数据集等。使用 tf.data API 可以轻松处理大量数据、不同的数据格式以及复杂的转换。
如果数据比较小,我们可以一次性处理后直接加入内存就可以了;但如果数据比较大,而且在数据训练过程中还需要一些动态的处理方法,如分批处理、添加数据增强方法、数据采样等等,此时,通过构建一个数据流就显得非常必要。
数据流可有效提高我们管理数据得效率,此外还可以帮助我们解耦数据的预处理和数据执行的过程,能够帮我们更高效的应用硬件资源,例如当分布式训练的时候,一个好的数据流能够帮我们高效的分发数据到不同的硬件上,从而提高整体的训练效率。
一个合理的数据流,能够让你模型训练更加的高效。数据流的本质就是 ETL。一般来说,数据流由三部分组成,具体如下:
(1)抽取、初始化源数据 (E:即Extract)
(2)添加各种预处理过程 (T: 即Transform)
(3)遍历数据流,把大数据导入模型、训练数据等 (L:Load)
在 Tensorflow2里,我们使用 tf.data 来构建数据管道。

3.tf.data简介

tf.data是TensorFlow提供的构建数据管道的一个工具,与PyTorch的utils.data类似,使用tf.data构建数据集(Dataset),构建和管理数据管道非常方便,它提供了很多操作,如:
shuffle、repeat、map、batch、padded_batch、prefetch等等,这些操作功能很实用,但使用的顺序是有讲究的,如果次序不当,将严重影响数据流的效率和质量,这些操作的顺利大致为:
(1)map->shuffle -> repeat -> map(parse) -> batch -> prefetch ;
(2)有些 map 操作放在 batch 前,有些 map 操作放在 batch 后;
(3)尽量把进行数据过滤和采样放数据流的前面,以提高后续处理效率;
(4)使用 AUTOTUNE 来设置并行执行的数量,不要去手动调节;
(5)使用 cache / interleave / prefetch 这些空间换时间的操作。

为便于更好理解这些操作,通过以下示例进行具体说明。

3.1 生成数据集

这里以手工创建一个非常简单的数据集,该数据包含10个样本,每个样本对应一个标签。

运行结果:
[0.1,0]
[0.4,0]
[0.6,1]
[0.2,0]
[0.8,1]
[0.8,1]
[0.4,0]
[0.9,1]
[0.3,0]
[0.2,0]

3.2 map

map对dataset中每个元素做出来,这里每个元素为[x,y],函数为one-hot
该函数把标签转换为one-hot编码。
介绍map()这一核心函数。该函数的输入参数map_func应为一个函数,在该函数中实现我们需要的对数据的变换。
具体应用场景如图片加载、数据增强、标签one hot化等。下面以one hot化和添加噪声为例具体说明。
one hot化的函数实现如下

运行结果
[0.1,[1 0]]
[0.4,[1 0]]
[0.6,[0 1]]
[0.2,[1 0]]
[0.8,[0 1]]
[0.8,[0 1]]
[0.4,[1 0]]
[0.9,[0 1]]
[0.3,[1 0]]
[0.2,[1 0]]

3.3 shuffle

shuffle()是随机打乱样本次序,参数buffer_size建议设为样本数量,过大会浪费内存空间,过小会导致打乱不充分。

运行结果
[0.6,[0 1]]
[0.2,[1 0]]
[0.3,[1 0]]
[0.8,[0 1]]
[0.4,[1 0]]
[0.8,[0 1]]
[0.2,[1 0]]
[0.4,[1 0]]
[0.1,[1 0]]
[0.9,[0 1]]

3.4 repeat

使用repeat方法,repeat的功能就是将整个序列或数据集重复多次, 完成整个数据集的一次训练是一个epoch,使用repeat(5)就可以将之变成5个epoch 如果直接调用repeat()的话,生成的序列就会无限重复下去,没有结束,因此也不会抛出tf.errors.OutOfRangeError异常。

运行结果
[0.8,[0 1]]
[0.8,[0 1]]
[0.1,[1 0]]
[0.9,[0 1]]
[0.2,[1 0]]
[0.2,[1 0]]
[0.4,[1 0]]
[0.3,[1 0]]
[0.6,[0 1]]
[0.4,[1 0]]
[0.4,[1 0]]
[0.2,[1 0]]
[0.4,[1 0]]
[0.3,[1 0]]
[0.8,[0 1]]
[0.2,[1 0]]
[0.8,[0 1]]
[0.9,[0 1]]
[0.6,[0 1]]
[0.1,[1 0]]

3.5 batch

batch()是使数据集一次获取多个样本

运行结果
[[0.4 0.3 0.6 0.2],[[1 0]
[1 0]
[0 1]
[1 0]]]
[[0.4 0.2 0.8 0.9],[[1 0]
[1 0]
[0 1]
[0 1]]]
[[0.8 0.1 0.6 0.9],[[0 1]
[1 0]
[0 1]
[0 1]]]
[[0.2 0.2 0.4 0.1],[[1 0]
[1 0]
[1 0]
[1 0]]]
[[0.3 0.8 0.8 0.4],[[1 0]
[0 1]
[0 1]
[1 0]]]

3.6map

map()函数,该函数的输入参数map_func应为一个函数,在该函数中实现我们需要的对数据的变换。具体应用场景如图片加载、数据增强、标签one hot化等。
对数据进行固定形式上的变化,可将函数直接作为参数输入。但是,包含随机信息的数据变化则需要tf.py_function辅助实现,
如数据增强中数据添加随机噪声、图像的随机翻转都属于包含随机信息。

运行结果
[[1.5635917 1.6635917 2.2635917 2.0635917],[[1 0]
[1 0]
[0 1]
[0 1]]]
[[1.29330552 0.89330552 1.39330552 0.79330552],[[0 1]
[1 0]
[0 1]
[1 0]]]
[[-0.04628853 -0.24628853 -0.04628853 -0.24628853],[[1 0]
[1 0]
[1 0]
[1 0]]]
[[-0.46844772 -0.96844772 -1.16844772 -0.66844772],[[0 1]
[1 0]
[1 0]
[0 1]]]
[[0.40181042 0.20181042 0.80181042 0.90181042],[[1 0]
[1 0]
[0 1]
[0 1]]]
在map()函数中,还有个很重要的参数num_parallel_calls,可以将数据加载与变换过程并行到多个CPU线程上。由于python语言本身的全局解释锁,想要实现真正的并行计算是非常困难的,所以这个参数实际上非常实用,通常的使用情景是网络训练时,GPU做模型运算的同时CPU加载数据。 还可以直接设置num_parallel_calls=tf.data.experimental.AUTOTUNE,这样会自动设置为最大的可用线程数,可充分利用机器算资源。

3.7 prefetch

prefetch(buffer_size)创建一个Dataset,从源数据集中预提取元素的,注意:examples.prefetch(2) 将预取2个元素(2个示例),
而examples.batch(20).prefetch(2) 将预取2个元素(2个批次,每个20个示例),buffer_size 表示预取时将缓冲的最大元素数,返回 Dataset。
使用prefetch可有效使用读取数据与模型处理之间松耦合。如下图所示

从上图可以看出,使用prefetch函数之后,读取数据与训练数据就可并发处理了,这就大大提升数据处理效率。

4.tf.data读取输入数据

tf.data的架构如下图所示:

从上图可知,Dataset是一个基类,这个类可实例化成迭代器(Iterator),

4.1 Dataset类

Dataset类下有多个子类,常见的有TextLineDataset、tf.data.FixedLengthRecordDataset、TFRecordDataset等,可用使用这些Dateset的子类获取数据,此外,Dataset还有很多方法,如from_tensor_slices、list_files、map、batch、repeat等等,Dataset的这些方法或子类通常用来读取或处理数据,当使用场景有些不同,tf.data常见的读取数据方式有以下几种:
(1)直接从内存中读取(如NumPy数据),tf.data.Dataset.from_tensor_slices()
(2)使用一个 python 生成器 (generator) 初始化,从生成器中读取数据可以使用
tf.data.Dataset.from_generator()
(3)从 TFrecords格式文件读取数据, 可使用tf.data.TFRecordDataset()
(4)读取文本数据,可使用tf.data.TextLineDataset()
(5)从二进制文件读取数据,可用tf.data.FixedLengthRecordDataset()
(6)读取cvs数据,可使用tf.data.experimental.make_csv_dataset()
(7)从文件集中读取数据,可使用tf.data.Dataset.list_files()

4.2对象Iterator

Iterator是Dataset中迭代方法的实例化,主要对数据进行访问,包括四种迭代方法,单次、可初始化、可重新初始化、可馈送等,可实现对数据集中元素的快速迭代,供模型训练使用。

1.背景说明

我们平时在网上、书上看到的有关深度学习的案例大多使用处理好、教规格的数据集,如MNIST, CIFAR10/100等数据集,这些数据集一般无需(或很少)做预处理,就看导入到模型中使用。但是很多情况下需要处理自己的数据集,这些数据集往往不规范,而且一般比较大。如何把自己的数据集转换成这些TensorFlow框架能够使用的数据形式至关重要,接下来将对TensorFlow2中官方推荐数据格式及预处理方法进行说明,小数据集处理方法很多,处理也比较方便,这里就不展开来说。这里主要介绍大中数据集,对大中数据集TensorFlow建议转换为TFRecord格式,使用TFRecord格式有哪些优势呢?

2.TFRecord的优势

正常情况下我们用于训练的文件夹内部往往会存着成千上万的图片或文本等文件,这些文件通常被散列存放。这种存储方式有一些缺点:
(1)占用磁盘空间;
(2)在一个个读取的时候会非常耗时;
(3)占用大量内存空间(有的大型数据不足以一次性加载)。
此时 TFRecord 格式的文件存储形式会很合理的帮我们存储数据。TFRecord 内部使用了 “Protocol Buffer” 二进制数据编码方案,它只占用一个内存块,只需要一次性加载一个二进制文件的方式即可,简单,快速,尤其对大型训练数据很友好。而且当我们的训练数据量比较大的时候,可以将数据分成多个 TFRecord 文件,来提高处理效率。

3.使用TFRecord的一般步骤

3.1 Exmple含义

freocrd的核心是其包含一系列的example,每个example可以认为是一条样本。example是tensorflow中的对象类型,用法是tf.train.example。
Example含义: 如下图,假设有样本 (example): (x,y) :输入x 和 输出y一起叫做样本。这里每个x是六维向量,每个y是一维向量。
(1)表征 (representation):x集合了代表个人的全部特征。
其中特征 (feature): x 中的某个维:如学历,年龄,职业。是某人的一个特点。
(2)标签 (label):y为输出。

对上表的数据的存储,通常我们把输入特征x与标签y分开进行保存。假设有100个样本,把所有输入存储在100x6的numpy矩阵中,把标签存储在100x1的向量中。
Example协议块格式:

以TFRecord方式存储,输入和标签将以字典方式存放在一起,具体格式请参考下图。

3.2 TFRecord文件数据格式

3.3 加载TFRecord文件流程

TFRecord做好了,可以通过tf.data来生成一个迭代器,每次调用都返回一个大小为batch_size的batch。读取TFRecord可以通过tensorflow两个个重要的函数实现如下图所示,分别是读取器(Reader): tf.data.TFRecordDataset和解码器(Decoder):tf.io.parse_single_example解析器。如下图:

4.代码实例

整个过程为:
(1)先把源数据(可以是文本、图像、音频、Embedding等,这里是小猫、小狗图片)导入内存(如NumPy)
(2)把内存数据转换为TFRecord格式数据
(3)读取TFRecord数据
(4)构建模型
(5)训练模型

4.1 导入数据

导入模块及数据,这里是windows环境,如果是linux环境,请修改路径格式。
数据集下载

4.2 把数据转换为TFRecord格式

构建example的时候,这个tf.train.Feature()函数可以接收三种数据:
• bytes_list: 可以存储string 和byte两种数据类型。
• float_list: 可以存储float(float32)与double(float64) 两种数据类型。
• int64_list: 可以存储:bool, enum, int32, uint32, int64, uint64。
对于只有一个值(比如label)可以用float_list或int64_list,而像图片、视频、embedding这种列表型的数据,通常转化为bytes格式储存

4.3 从TFRecord读取数据

这里使用 tf.data.TFRecordDataset 类来读取 TFRecord 文件
使用 TFRecordDataset 对于标准化输入数据和优化性能十分有用。可以使用tf.io.parse_single_example函数对每个样本进行解析(tf.io.parse_example对批量样本进行解析)。 请注意,这里的 feature_description 是必需的,因为数据集使用计算图执行,并且需要这些描述来构建它们的形状和类型签名。
使用 tf.data.Dataset.map 方法可将函数应用于 Dataset 的每个元素。

显示图片

4.4 构建模型

模型由两层卷积和两层全连接层构成。

4.5 训练模型

训练模型使用@tf.function装饰器将普通Python函数转换成对应的TensorFlow计算图构建代码。使用tf.GradientTape实现自动求导,使用TensorFlow2 autograph机制,该机制可以把动态图转换成静态图加速。

运行结果:
Epoch 1, Acc 50.98039627075195, Test Acc 57.5
Epoch 2, Acc 68.13725280761719, Test Acc 52.499996185302734
Epoch 3, Acc 80.39215850830078, Test Acc 47.5
Epoch 4, Acc 91.66667175292969, Test Acc 57.5
Epoch 5, Acc 93.13725280761719, Test Acc 57.5
Epoch 6, Acc 97.54901885986328, Test Acc 52.499996185302734
Epoch 7, Acc 100.0, Test Acc 52.499996185302734
Epoch 8, Acc 100.0, Test Acc 50.0
Epoch 9, Acc 100.0, Test Acc 50.0
Epoch 10, Acc 100.0, Test Acc 52.499996185302734
有兴趣的读者可以从以下几方面提升性能
5.利用数据增强方法提升性能
6.利用现代经典网络提升性能
7.利用数据迁移方法提升性能
8.结合Transformer技术提升性能