百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

一文上手最新Tensorflow2.0系列“tf.data”API 使用

toyiye 2024-06-21 12:21 9 浏览 0 评论

除了GPU和TPU等硬件加速设备以外,一个高效的数据输入管道也可以很大程度的提升模型性能,减少模型训练所需要的时间。数据输入管道本质是一个ELT(Extract、Transform和Load)过程:

  • Extract:从硬盘中读取数据(可以是本地的也可以是云端的)。
  • Transform:数据的预处理(例如数据清洗、格式转换等)。
  • Load:将处理好的数据加载到计算设备(例如CPU、GPU以及TPU等)。

数据输入管道一般使用CPU来执行ELT过程,GPU等其他硬件加速设备则负责模型的训练,ELT过程和模型的训练并行执行,从而提高模型训练的效率。另外ELT过程的各个步骤也都可以进行相应的优化,例如并行的读取数据以及并行的处理数据等。在TensorFlow中我们可以使用“tf.data”API来构建这样的数据输入管道。

我们首先下载实验中需要用的图像数据集(下载地址“https://storage.googleapis.com/download.tensorflow.org/example_images/”,百度网盘地址“https://pan.baidu.com/s/16fvNOBvKyGVa8yCB5mDUOQ”)。

该数据集是一个花朵图片的数据集,将下载下来的数据解压后如图所示,除了一个License文件以外主要是五个分别存放着对应类别花朵图片的文件夹。其中“daisy(雏菊)”文件夹中有633张图片,“dandelion(蒲公英)”文件夹中有898张图片,“roses(玫瑰)”文件夹中有641张图片,“sunflowers(向日葵)”文件夹中有699张图片,“tulips(郁金香)”文件夹中有799张图片。

解压后的数据集


接下来我们开始实现代码,首先我们导入需要使用的包:

import tensorflow as tf
import pathlib

pathlib提供了一组用于处理文件系统路径的类。导入需要的包后,可以先检查一下TensorFlow的版本:

print(tf.__version__)

首先获取所有图片样本文件的路径:

# 获取当前路径
data_root = pathlib.Path.cwd()
# 获取指定目录下的文件路径(返回是一个列表,每一个元素是一个PosixPath对象)
all_image_paths = list(data_root.glob('*/*/*'))
print(type(all_image_paths[0]))
# 将PosixPath对象转为字符串
all_image_paths = [str(path) for path in all_image_paths]
print(all_image_paths[0])
print(data_root)

输出结果如图所示:

件路径输出结果

接下来我们需要统计图片的类别,并给每一个类别分配一个类标:

# 获取图片类别的名称,即存放样本图片的五个文件夹的名称
label_names = sorted(item.name for item in data_root.glob('*/*/') if item.is_dir())
# 将类别名称转为数值型的类标
label_to_index = dict((name, index) for index, name in enumerate(label_names))
# 获取所有图片的类标
all_image_labels = [label_to_index[pathlib.Path(path).parent.name]
 
print(label_to_index)
print("First 10 labels indices: ", all_image_labels[:2])
print("First 10 labels indices: ", all_image_paths[:2])

输出结果如图所示,daisy(雏菊)、dandelion(蒲公英)、roses(玫瑰)、sunflowers(向日葵)、tulips(郁金香)的类标分别为0、1、2、3、4、5。

图片类标的输出结果


处理完类标之后,我们接下来需要对图片本身做一些处理,这里我们定义一个函数用来加载和预处理图片数据:

def load_and_preprocess_image(path):
 # 读取图片
 image = tf.io.read_file(path)
 # 将jpeg格式的图片解码,得到一个张量(三维的矩阵)
 image = tf.image.decode_jpeg(image, channels=3)
 # 由于数据集中每张图片的大小不一样,统一调整为192*192
 image = tf.image.resize(image, [192, 192])
 # 对每个像素点的RGB值做归一化处理
 image /= 255.0
 
 return image

完成对类标和图像数据的预处理之后,我们使用“tf.data.Dataset”来构建和管理数据集:

# 构建图片路径的“dataset”
path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)
# 使用AUTOTUNE自动调节管道参数
AUTOTUNE = tf.data.experimental.AUTOTUNE
# 构建图片数据的“dataset”
image_ds = path_ds.map(load_and_preprocess_image,
num_parallel_calls=AUTOTUNE)
# 构建类标数据的“dataset”
label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels, tf.int64))
# 将图片和类标压缩为(图片,类标)对
image_label_ds = tf.data.Dataset.zip((image_ds, label_ds))
 
print(image_ds)
print(label_ds)
print(image_label_ds)

输出结果:

构建的“dataset”


在代码中,我们使用了“from_tensor_slices”方法使用张量的切片元素构建“dataset”,“tf.data.Dataset”类还提供了“from_tensor”直接使用单个张量来构建“dataset”,以及可以使用生成器生成的元素来构建“dataset”的“from_generator”方法。

我们还使用了“tf.data.Dataset”的“map”方法,该方法允许我们自己定义一个函数,将原数据集中的元素依次经过该函数处理,并将处理后的数据作为新的数据集,处理前和处理后的数据顺序不变。例如这里我们自己定义了一个“load_and_preprocess_image”函数,将“path_ds”中的图片路径转换成了经过预处理的图像数据,并保存在了“image_ds”中。

最后我们使用“tf.data.Dataset”的“zip”方法将图片数据和类标数据压缩成“(图片,类标)”对,其结构如图所示。我们可视化一下数据集中的部分数据:

import matplotlib.pyplot as plt
 
plt.figure(figsize=(8,8))
for n,image_label in enumerate(image_label_ds.take(4)):
 plt.subplot(2,2,n+1)
 plt.imshow(image_label[0])
 plt.grid(False)
 plt.xticks([])
 plt.yticks([])
 plt.xlabel(image_label[1])

结果如图所示:

数据集中部分数据的可视化


接下来我们用创建的dataset训练一个分类模型,为了简单,我们直接使用“tf.keras.applications”包中训练好的模型,并将其迁移到我们的花朵分类任务上来。这里我们使用“MobileNetV2”模型。

# 下载的模型在用户根目录下,具体位置:“~/.keras/models/
mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_192_no_top.h5”
mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192, 192, 3),
include_top=False)
# 禁止训练更新“MobileNetV2”模型的参数
mobile_net.trainable = False

当我们执行代码后,训练好的“MobileNetV2”模型会被下载到本地,该模型是在ImageNet数据集上训练的。因为我们是想把该训练好的模型迁移到我们的花朵分类问题中来,所以我们设置该模型的参数不可训练和更新。

接下来我们打乱一下数据集,以及定义好训练过程中每个“batch”的大小。

# 使用Dataset类的shuffle方法打乱数据集
image_count = len(all_image_paths)
ds = image_label_ds.shuffle(buffer_size=image_count)
# 让数据集重复多次
ds = ds.repeat()
# 设置每个batch的大小
BATCH_SIZE = 32
ds = ds.batch(BATCH_SIZE)
# 通过“prefetch”方法让模型的训练和每个batch数据集的加载并行
ds = ds.prefetch(buffer_size=AUTOTUNE)

在代码中,使用“tf.data.Dataset”类的“shuffle”方法将数据集进行打乱。“repeat”方法让数据集可以重复获取,通常情况下如果我们一个“epoch”只对完整的数据集训练一遍的话,可以不需要设置“repeat”。“repeat”方法可以设置参数,例如“ds.repeat(2)”是让数据集可以重复获取两遍,即一个训练回合(epoch)中我们可以使用两遍数据集,不加参数的话,则默认可以无限次重复获取数据集。

由于“MobileNetV2”模型接收的输入数据是归一化后范围在[-1,1]之间的数据,我们在代码中对数据进行了一次归一化处理后,其范围在[0,1]之间,因此我们需要将我们的数据映射到[-1,1]之间。

def change_range(image,label):
 return 2*image-1, label
# 使用“map”方法对dataset进行处理
keras_ds = ds.map(change_range)

接下来我们定义模型,由于预训练好的“MobileNetV2”返回的数据维度为“(32,6,6,1280)”,其中32是一个“batch”的大小,“6,6”代表输出的特征图的大小为“6X6”,1280代表该层使用了1280个卷积核。为了适应我们的分类任务,我们需要在“MobileNetV2”返回数据的基础上再增加两层网络层。

model = tf.keras.Sequential([
 mobile_net,
 tf.keras.layers.GlobalAveragePooling2D(),
 tf.keras.layers.Dense(len(label_names))])

全局平均池化(GAP,Global Average Pooling)将每一个特征图求平均,将该平均值作为该特征图池化后的结果,因此经过该操作后数据的维度变为了(32,1280)。由于我们的花朵分类任务是一个5分类的任务,因此我们再使用一个全连接(Dense),将维度变为(32,5)。

接着我们编译一下模型,同时指定使用的优化器和损失函数:

model.compile(optimizer=tf.keras.optimizers.Adam(),
 loss='sparse_categorical_crossentropy',
 metrics=["accuracy"])
model.summary()
“model.summary()”可以输出模型各层的参数概况,如图所示:

模型各层的参数概况


最后我们使用“model.fit”训练模型:

model.fit(ds, epochs=1, steps_per_epoch=10)

这里参数“epochs”指定需要训练的回合数,“steps_per_epoch”代表每个回合要取多少个“batch”数据,通常“steps_per_epoch”的大小等于我们数据集的大小除以“batch”的大小后上取整。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码