作者归档:feiguyun


使用数据增强来提升模型的性能

1、导入模型

2、定义加载函数

3、定义批量加载函数

4、加载数据

把数据转换为dataset格式

5、定义数据增强方法

6、构建模型

生成实例

查看模型详细结构

7、编译模型

8、训练模型

为便于比较,这里先不使用数据增强方法

9、查看运行结果

运行结果

10、使用数据增强方法

11、查看使用数据增强的运行结果

运行结果

12、结果分析

从不使用数据增强与使用数据增强方法的结果可以看出,使用数据增强方法后,模型性能有提升(未使用数据增强的验证精度为71%,使用数据增强方法后,验证精度提升到74%),而且模型的泛化能力也有提高(使用数据增强方法后,训练与验证精度曲线靠得较近)。


这里介绍几种TensorFlow2提升模型性能的几种有效方法,如数据增强、使用经典网络、使用迁移学习、使用新架构等。

一、数据增强

数据增强是提升模型性能的常用方法之一,尤其当训练数据集较小时,通过数据增强方法,效果更加明确。通过数据增强不但可以增加数据量、丰富数据的多样性,从而有效提升模型的泛化能力。
TensorFlow2提供了多种数据增强方法,常用的几种方法为:
 有监督的数据增强:
1、使用tf.image的预处理
该方法一般基于dataset,然后把数据增强方法嵌入到map中
2、使用tf.keras.preprocessing.image.ImageDataGenerator
利用实时数据增强技术生成批量的张量图像数据
使用tf.keras.preprocessing.image.ImageDataGenerator,获取数据源的方法主要有:
(1).flow(x, y)
(2).flow_from_directory(directory)
3、使用tf.keras.layers.experimental.preprocessing
更多信息可参考官网:https://keras.io/guides/preprocessing_layers/
 无监督的数据增强
通过模型学习数据的分布,随机生成与训练数据集分布一致的图片,代表方法,GAN。
GAN模型包含两个网络,一个是生成网络(G),一个是判别网络(D),基本原理如下:
(1)G是一个生成图片的网络,它接收随机的噪声z,通过噪声生成图片,记做G(z)。
(2)D是一个判别网络,判别一张图片是不是“真实的”,即是真实的图片,还是由G生成的图片。其架构图如下:

下载所有实例使用数据(提取码为:fg29)
具体实例

二、使用现代经典模型

现代经典模型主要有: VGG、GoogLeNet、Inception、Xception、ResNet、MobileNet、DenseNet、NASNet等,各种网咯结构可参考:
https://blog.csdn.net/Forrest97/article/details/105630719

具体实例(待续)

三、利用迁移方法

Tensorflow.keras.application下载已经训练好的模型,包括如下预训练模型:
VGG、GoogLeNet、Inception、Xception、ResNet、MobileNet、DenseNet、NASNet等

具体实例(待续)

四、使用新架构如Transformer等

Transformer)模型在NLP领域取得了SOTA成绩,目前人们正把Transformer引用到CV领域,在图像识别、图像分类等领域取得不俗的表现。在目标检测(如DETR)、图像分类(如ViT)、图像分割(如SETR)都取得不错的效果。这里我们重点介绍ViT(Vision Transformer)。如何把Transformer引入CV领域?需要对CV中的图像做哪些处理?
NLP处理的语言数据是序列化的,而CV中处理的图像数据是三维的(height、width和channels)。所以需要通过某种方法将图像这种三维数据转化为序列化的数据。
Vision Transformer将CV和NLP领域知识结合起来,对原始图片进行分块,然后展平成序列,输入进原始Transformer模型的编码器Encoder部分,最后接入一个全连接层对图片进行分类。具体步骤为:
(1)原始图片(H,W,C)进行分块(patches)
进行一个类似卷积操作,把原始图片进行分块,分块数=H*W/P*P(P为块的大小)。
(2)展平每块(Flatten the patches)
把每块展平为一维向量,大小为:P*P*C(对应实例中的patch_dims)
(3)把展平后的块映射为更低维的向量
通过一个全连接层,把展平后块映射为一个更低维的向量(对应实例中的patch embedding),其大小为D(对应实例中的projection_dim),这个维度在各层保存不变,主要为便于使用残差连接。
(4)在patch embedding基础上添加一个类标签
类似BERT的[class] token,在patch embedding的序列之前添加一个可学习的embedding向量xclass
(5)加上位置嵌入(Add positional embeddings)
低维的向量+位置嵌入,
(6)把(5)的结果作为标准transformer encoder的输入
(7)在一个大数据集上训练模型
(8)在下游数据集中,进行微调。
ViT架构图如下:


具体实例

具体实例

五、练习

1、利用ResNet经典模型提升性能
2、使用keras的数据增强方法提升性能
3、viT中Transformer的输入数据的形状(shape)与哪些因素有关?包括哪些数据?如何生成Transformer的输入数据?

1.导入CIFAR-10数据集

CIFAR-10是由 Hinton 的学生 Alex Krizhevsky 和 Ilya Sutskever 整理的一个用于识别普适物体的小型数据集。一共包含 10 个类别的 RGB 彩色图片:飞机( a叩lane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。图片的尺寸为 32×32,3个通道 ,数据集中一共有 50000 张训练圄片和 10000 张测试图片。 CIFAR-10数据集有3个版本,这里使用python版本。

1.1 导入需要的库

1.2 定义批量导入数据的函数

1.3 定义加载数据函数

1.4 加载数据

运行结果
loading data\cifar-10-batches-py\data_batch_1
loading data\cifar-10-batches-py\data_batch_2
loading data\cifar-10-batches-py\data_batch_3
loading data\cifar-10-batches-py\data_batch_4
loading data\cifar-10-batches-py\data_batch_5
finished loadding CIFAR-10 data

1.5 可视化加载数据

运行结果

2 .数据预处理并设置超参数

3.使用tf.data构建数据管道

4.构建模型

使用子类方法自定义模型结构的一般步骤:

5.训练模型

(1)实例化模型
model = MyCNN()
(2)查看模型的详细结构
model.model01().summary()
Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 32, 32, 3)] 0
_________________________________________________________________
conv2d (Conv2D) (None, 32, 32, 32) 896
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 16, 16, 32) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 16, 16, 64) 18496
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 8, 8, 64) 0
_________________________________________________________________
reshape (Reshape) (None, 4096) 0
_________________________________________________________________
dense (Dense) (None, 256) 1048832
_________________________________________________________________
dense_1 (Dense) (None, 10) 2570
_________________________________________________________________
tf_op_layer_Softmax (TensorF [(None, 10)] 0
=================================================================
Total params: 1,070,794
Trainable params: 1,070,794
Non-trainable params: 0
(3)编译及训练模型
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])
epochs = 10
batch_size = 64
learning_rate = 0.0002

train_history = model.fit(x_train, y_train,
validation_split=0.2,
epochs=epochs,
#steps_per_epoch=100,
batch_size=batch_size,
verbose=1)
运行结果
Train on 40000 samples, validate on 10000 samples
Epoch 1/10
40000/40000 [==============================] - 5s 113us/sample - loss: 1.4482 - accuracy: 0.4811 - val_loss: 1.1808 - val_accuracy: 0.5889
Epoch 2/10
40000/40000 [==============================] - 2s 53us/sample - loss: 1.0530 - accuracy: 0.6291 - val_loss: 1.0052 - val_accuracy: 0.6466
Epoch 3/10
40000/40000 [==============================] - 2s 51us/sample - loss: 0.9031 - accuracy: 0.6814 - val_loss: 0.9358 - val_accuracy: 0.6751
Epoch 4/10
40000/40000 [==============================] - 2s 50us/sample - loss: 0.7926 - accuracy: 0.7207 - val_loss: 0.8919 - val_accuracy: 0.6909
Epoch 5/10
40000/40000 [==============================] - 2s 53us/sample - loss: 0.6966 - accuracy: 0.7573 - val_loss: 0.8932 - val_accuracy: 0.6904
Epoch 6/10
40000/40000 [==============================] - 2s 50us/sample - loss: 0.6029 - accuracy: 0.7889 - val_loss: 0.8699 - val_accuracy: 0.7036
Epoch 7/10
40000/40000 [==============================] - 2s 51us/sample - loss: 0.5131 - accuracy: 0.8210 - val_loss: 0.8832 - val_accuracy: 0.7092
Epoch 8/10
40000/40000 [==============================] - 2s 52us/sample - loss: 0.4263 - accuracy: 0.8533 - val_loss: 0.9517 - val_accuracy: 0.7028
Epoch 9/10
40000/40000 [==============================] - 2s 53us/sample - loss: 0.3407 - accuracy: 0.8815 - val_loss: 0.9970 - val_accuracy: 0.7065
Epoch 10/10
40000/40000 [==============================] - 2s 52us/sample - loss: 0.2693 - accuracy: 0.9078 - val_loss: 1.0540 - val_accuracy: 0.7090

6.可视化运行结果

7.测试模型

运行结果
10000/10000 - 1s - loss: 1.1505 - accuracy: 0.6936
test_loss: 1.1505295364379884
test_acc: 0.6936
metrics_names: ['loss', 'accuracy']

8.保存恢复整个模型

(1)保存模型参数及网络结构等
可以使用两种格式将整个模型保存到磁盘:TensorFlow SavedModel 格式和较早的 Keras H5 格式。 tensorflow官方推荐使用 SavedModel 格式。它是使用 model.save() 时的默认格式,这种保存方式适合Sequential, Functional Model, or Model subclass。

(2)恢复模型

(3)检查恢复模型的结构

运行结果
Model: "my_cnn_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_2 (Conv2D) multiple 896
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 multiple 0
_________________________________________________________________
conv2d_3 (Conv2D) multiple 18496
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 multiple 0
_________________________________________________________________
reshape_1 (Reshape) multiple 0
_________________________________________________________________
dense_2 (Dense) multiple 1048832
_________________________________________________________________
dense_3 (Dense) multiple 2570
=================================================================
Total params: 1,070,794
Trainable params: 1,070,794
Non-trainable params: 0
_______________________________
与原模型结构完全一致!

(4)基于恢复模型进行测试

运行结果
10000/10000 - 1s - loss: 1.1488 - accuracy: 0.6936
test_loss: 1.1488204129219055
test_acc: 0.6936
metrics_names: ['loss', 'accuracy']
模型精度与原模型完全一致!


有三种计算图的构建方式:静态计算图,动态计算图,以及Autograph。 TensorFlow 2.0主要使用的是动态计算图和Autograph。 动态计算图易于调试,编码效率较高,但执行效率偏低。 静态计算图执行效率很高,但较难调试。 而Autograph机制可以将动态图转换成静态计算图,兼收执行效率和编码效率之利。 当然Autograph机制能够转换的代码并不是没有任何约束的,有一些编码规范需要遵循,否则可能会转换失败或者不符合预期。 我们将着重介绍Autograph的编码规范和Autograph转换成静态图的原理。
Autograph编码规范:
• 1.被@tf.function修饰的函数应尽可能使用TensorFlow中的函数而不是Python中的其他函数。例如使用tf.print而不是print,使用tf.range而不是range,使用tf.constant(True)而不是True.
• 2.避免在@tf.function修饰的函数内部定义tf.Variable.
• 3.被@tf.function修饰的函数不可修改该函数外部的Python列表或字典等数据结构变量。

1.导入CIFAR-10数据集

CIFAR-10是由 Hinton 的学生 Alex Krizhevsky 和 Ilya Sutskever 整理的一个用于识别普适物体的小型数据集。一共包含 10 个类别的 RGB 彩色图片:飞机( a叩lane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。图片的尺寸为 32×32,3个通道 ,数据集中一共有 50000 张训练圄片和 10000 张测试图片。 CIFAR-10数据集有3个版本,这里使用python版本。

1.1 导入需要的库

1.2 定义批量导入数据的函数

1.3 定义加载数据函数

1.4 加载数据

运行结果
loading data\cifar-10-batches-py\data_batch_1
loading data\cifar-10-batches-py\data_batch_2
loading data\cifar-10-batches-py\data_batch_3
loading data\cifar-10-batches-py\data_batch_4
loading data\cifar-10-batches-py\data_batch_5
finished loadding CIFAR-10 data

1.5 可视化加载数据

运行结果

2 .数据预处理并设置超参数

3.使用tf,data构建数据管道

4.定义卷积层及池化层

5.构建模型

6.定义训练模型函数

自定训练过程:
(1)打开一个遍历各epoch的for循环
(2)对于每个epoch,打开一个分批遍历数据集的 for 循环
(3)对于每个批次,打开一个 GradientTape() 作用域
(4)在此作用域内,调用模型(前向传递)并计算损失
(5)在作用域之外,检索模型权重相对于损失的梯度
(6)根据梯度使用优化器来更新模型的权重
(7)评估模型指标

train_model(model,train_data,training_steps,display_step)
运行结果
step =1000,loss = 1.3011,accuracy =0.5781 ,times=0.0000
step =2000,loss = 1.2720,accuracy =0.6094 ,times=0.0000
step =3000,loss = 1.2153,accuracy =0.5469 ,times=0.0000
step =4000,loss = 0.8636,accuracy =0.7500 ,times=0.0000
step =5000,loss = 0.7936,accuracy =0.7500 ,times=0.0000
step =6000,loss = 0.9527,accuracy =0.6875 ,times=0.0156
step =7000,loss = 0.9352,accuracy =0.7344 ,times=0.0000
step =8000,loss = 0.6892,accuracy =0.7969 ,times=0.0000
step =9000,loss = 0.7949,accuracy =0.7031 ,times=0.0000
step =10000,loss = 0.4768,accuracy =0.8438 ,times=0.0000
step =11000,loss = 0.7983,accuracy =0.7188 ,times=0.0000
step =12000,loss = 0.5601,accuracy =0.8281 ,times=0.0000
step =13000,loss = 0.7934,accuracy =0.7031 ,times=0.0000
step =14000,loss = 0.6450,accuracy =0.8438 ,times=0.0000
step =15000,loss = 0.5681,accuracy =0.7656 ,times=0.0000
step =16000,loss = 0.5413,accuracy =0.8125 ,times=0.0000
step =17000,loss = 0.3914,accuracy =0.8438 ,times=0.0000
step =18000,loss = 0.3687,accuracy =0.8906 ,times=0.0000
step =19000,loss = 0.4534,accuracy =0.8750 ,times=0.0000
step =20000,loss = 0.3855,accuracy =0.8438 ,times=0.0000

从运行结果来看,运行时间快了很多!

7.可视化运行结果

8.测试模型

运行结果
Test accuracy:0.720653
性能也有一定提升!

1.导入CIFAR-10数据集

CIFAR-10是由 Hinton 的学生 Alex Krizhevsky 和 Ilya Sutskever 整理的一个用于识别普适物体的小型数据集。一共包含 10 个类别的 RGB 彩色图片:飞机( a叩lane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。图片的尺寸为 32×32,3个通道 ,数据集中一共有 50000 张训练圄片和 10000 张测试图片。 CIFAR-10数据集有3个版本,这里使用python版本。

1.1 导入需要的库

1.2 定义批量导入数据的函数

1.3 定义加载数据函数

1.4 加载数据

运行结果
loading data\cifar-10-batches-py\data_batch_1
loading data\cifar-10-batches-py\data_batch_2
loading data\cifar-10-batches-py\data_batch_3
loading data\cifar-10-batches-py\data_batch_4
loading data\cifar-10-batches-py\data_batch_5
finished loadding CIFAR-10 data

1.5 可视化加载数据

运行结果

2 .数据预处理并设置超参数

3.使用tf.data构建数据管道

4.定义卷积层及池化层

5.构建模型

6.定义训练模型函数

自定义训练过程:
(1)打开一个遍历各epoch的for循环
(2)对于每个epoch,打开一个分批遍历数据集的 for 循环
(3)对于每个批次,打开一个 GradientTape() 作用域
(4)在此作用域内,调用模型(前向传递)并计算损失
(5)在作用域之外,检索模型权重相对于损失的梯度
(6)根据梯度使用优化器来更新模型的权重
(7)评估模型指标

train_model(model,train_data,training_steps,display_step)
运行结果
step =1000,loss = 1.2807,accuracy =0.5781 ,times=0.0080
step =2000,loss = 1.1832,accuracy =0.6562 ,times=0.0080
step =3000,loss = 0.9727,accuracy =0.6562 ,times=0.0080
step =4000,loss = 1.0398,accuracy =0.6406 ,times=0.0050
step =5000,loss = 0.8615,accuracy =0.6406 ,times=0.0156
step =6000,loss = 0.7207,accuracy =0.7188 ,times=0.0000
step =7000,loss = 1.0945,accuracy =0.5938 ,times=0.0090
step =8000,loss = 0.7337,accuracy =0.7656 ,times=0.0080
step =9000,loss = 0.5792,accuracy =0.7812 ,times=0.0080
step =10000,loss = 0.7154,accuracy =0.7500 ,times=0.0080
step =11000,loss = 0.6398,accuracy =0.8125 ,times=0.0156
step =12000,loss = 0.6413,accuracy =0.7500 ,times=0.0080
step =13000,loss = 0.5555,accuracy =0.7812 ,times=0.0156
step =14000,loss = 0.6729,accuracy =0.8281 ,times=0.0156
step =15000,loss = 0.5163,accuracy =0.7500 ,times=0.0156
step =16000,loss = 0.5521,accuracy =0.7969 ,times=0.0000
step =17000,loss = 0.4475,accuracy =0.8594 ,times=0.0156
step =18000,loss = 0.3158,accuracy =0.8594 ,times=0.0080
step =19000,loss = 0.3829,accuracy =0.9062 ,times=0.0090
step =20000,loss = 0.2731,accuracy =0.9062 ,times=0.0080

7.可视化运行结果

8.测试型

运行结果
Test accuracy:0.712640

1.导入CIFAR-10数据集

CIFAR-10是由 Hinton 的学生 Alex Krizhevsky 和 Ilya Sutskever 整理的一个用于识别普适物体的小型数据集。一共包含 10 个类别的 RGB 彩色图片:飞机( a叩lane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。图片的尺寸为 32×32,3个通道 ,数据集中一共有 50000 张训练圄片和 10000 张测试图片。 CIFAR-10数据集有3个版本,这里使用python版本。

1.1 导入需要的库

1.2 定义批量导入数据的函数

1.3 定义加载数据函数

1.4 加载数据

运行结果
loading data\cifar-10-batches-py\data_batch_1
loading data\cifar-10-batches-py\data_batch_2
loading data\cifar-10-batches-py\data_batch_3
loading data\cifar-10-batches-py\data_batch_4
loading data\cifar-10-batches-py\data_batch_5
finished loadding CIFAR-10 data

1.5 可视化加载数据

运行结果

2 .数据预处理并设置超参数

3.使用tf,data构建数据管道

4.定义卷积层及池化层

5.定义并初始化权重参数

6.构建模型

7.定义损失函数、评估函数

8.定义梯度计算函数

自定义梯度计算过程:
(1)打开一个 GradientTape() 作用域
(2)在此作用域内,调用模型(正向传播)并计算损失
(3)在作用域之外,检索模型权重相对于损失的梯度
(4)根据梯度使用优化器来更新模型的权重
(5)利用优化器进行反向传播(更新梯度)

9.训练模型

10.可视化运行结果

运行结果

11.测试模型

运行结果
Test accuracy:0.704327

一、纯手工

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