TensorFlow 数据集
wangzf / 2022-07-05
目录
TensorFlow Dataset
TensorFlow Datasets 库
- 库安装:
$ pip install tensorflow
$ pip install tensorflow-datasets
- 库导入:
# tf.data, tf.data.Dataset, tf.data.Iterator
import tensorflow as tf
# tf.keras.datasets.<dataset_name>.load_data
from tensorflow.keras import datasets
# tfds.load
import tensorflow_datasets as tfds
TensorFlow Dataset API
tf.data
tf.data.Dataset
tf.data.Dataset.from_tensor_slices
tensorflow_datasets
tensorflow_datasets.load(data, split, shuffle_files, as_supervised)
tf.keras.datasets
tf.keras.datasets.mnist.load_data()
tf.data
TensorFlow 提供了 tf.data
模块, 它包括了一套灵活的数据集构建 API,
能够帮助快速、高效地构建数据输入的管道, 尤其适用于数据量巨大的情景
tf.data
API 在 TensorFlow 中引入了两个新的抽象类:
tf.data.Dataset
tf.data.Dataset
提供了对数据集的高层封装。tf.data.Dataset
由一系列可迭代访问的元素(element)组成, 其中每个元素包含一个或多个Tensor
对象tf.data.Dataset
可以通过两种方式来创建数据集:- 创建来源: 通过一个或多个
tf.Tensor
对象构建数据集tf.data.Dataset.from_tensors()
tf.data.Dataset.from_tensor_slices()
- 应用转换: 通过一个或多个
tf.data.Dataset
对象构建数据集tf.data.Dataset.map()
tf.data.Dataset.batch()
- 创建来源: 通过一个或多个
tf.data.Iterator
tf.data.Iterator
- 提供了从数据集中提取元素的主要方法
tf.data.Iterator.get_next()
- 返回的操作会在执行时生成
Dataset
的下一个元素, 并且此操作通常当输入管道和模型之间的接口
- 返回的操作会在执行时生成
tf.data.Iterator.initializer
- 使用不同的数据集重新初始化和参数化迭代器
tensorflow_datasets
TensorFlow Datasets(tensorflow_datasets
) 是可用于 TensorFlow
或其他 Python 机器学习框架(例如 Jax) 的一系列数据集。
所有数据集都作为 tf.data.Dataset
提供, 实现易用且高性能的输入管道
tf.keras.datasets
- TODO
TensorFlow Dataset 建立
tf.data.Dataset.from_tensor_slices
建立 tf.data.Dataset
的最基本的方法是使用 tf.data.Dataset.from_tensor_slices()
- 适用于数据量较小(能够将数据全部装进内存)的情况
- 如果数据集中的所有元素通过张量的第 0 维拼接成一个大的张量
import tensorflow as tf
import numpy as np
X = tf.constant([2013, 2014, 2015, 2016, 2017])
Y = tf.constant([12000, 14000, 15000, 16500, 17500])
dataset = tf.data.Dataset.from_tensor_slices((X, Y))
for x, y in dataset:
print(x.numpy(), y.numpy())
tf.data.Dataset.from_tensor_slices 和 tf.keras.datasets.mnist.load_data
import tensorflow as tf
import matplotlib.pyplot as plt
(train, train_label), (test, test_label) = tf.keras.datasets.mnist.load_data()
train = np.expand_dim(
train.astype(np.float32) / 255,
axis = -1
)
mnist_dataset = tf.data.Dataset.from_tensor_slices(
(train, train_label)
)
for image, label in mnist_dataset.take(1):
plt.title(label.numpy())
plt.imshow(image.numpy())
plt.show()
tensorflow_datasets
TensorFlow Datasets 提供了一系列可以和 TensorFlow 配合使用的数据集,
它负责下载和准备数据, 以及构建 tf.data.Dataset
。
每一个数据集(dataset) 都实现了抽象基类 tfds.core.DatasetBuilder 来构建
import tensorflow_datasets as tfds
# 构建 tf.data.Dataset
dataset1 = tfds.load(
"mnist",
split = "train",
shuffle_files = True
)
dataset2 = tfds.load(
"mnist",
split = tfds.Split.TRAIN,
as_supervised = True
)
# 构建输入数据 Pipeline
dataset1 = dataset1 \
.shuffle(1024) \
.batch(32) \
.prefetch(tf.data.experimential.AUTOTUNE)
for example in dataset1.take(1):
image, label = example["image"], example["label"]
TFRecord
对于特别巨大而无法完整载入内存的数据集, 可以先将数据集处理为 TFRecord
格式,
然后使用 tf.data.TFRecordDataset()
进行载入
TensorFlow 内置 Dataset
TensorFlow Datasets 提供了一系列可以和 TensorFlow 配合使用的数据集,
它负责下载和准备数据, 以及构建 tf.data.Dataset
每一个数据集(dataset) 都实现了抽象基类 tfds.core.DatasetBuilder
来构建
官方文档
- https://github.com/tensorflow/datasets
- https://www.tensorflow.org/datasets/overview
- https://www.tensorflow.org/datasets/catalog/overview#all_datasets
- https://www.tensorflow.org/datasets/api_docs/python/tfds
- https://blog.tensorflow.org/2019/02/introducing-tensorflow-datasets.html?hl=zh-CN
查看可用的数据集
import tensorflow as tf
import tensorflow_datasets as tfds
# 所有可用的数据集
print(tfds.list_builders())
['abstract_reasoning', 'aflw2k3d', 'amazon_us_reviews',
'bair_robot_pushing_small', 'bigearthnet', 'binarized_mnist', 'binary_alpha_digits',
'caltech101', 'caltech_birds2010', 'caltech_birds2011', 'cats_vs_dogs', 'celeb_a', 'celeb_a_hq', 'chexpert', 'cifar10', 'cifar100', 'cifar10_corrupted', 'clevr', 'cnn_dailymail', 'coco', 'coco2014', 'coil100', 'colorectal_histology', 'colorectal_histology_large', 'curated_breast_imaging_ddsm', 'cycle_gan',
'deep_weeds', 'definite_pronoun_resolution', 'diabetic_retinopathy_detection', 'downsampled_imagenet', 'dsprites', 'dtd', 'dummy_dataset_shared_generator', 'dummy_mnist',
'emnist', 'eurosat',
'fashion_mnist', 'flores', 'food101',
'gap', 'glue', 'groove',
'higgs', 'horses_or_humans',
'image_label_folder', 'imagenet2012', 'imagenet2012_corrupted', 'imdb_reviews', 'iris', 'kitti',
'kmnist',
'lfw', 'lm1b', 'lsun',
'mnist', 'mnist_corrupted', 'moving_mnist', 'multi_nli',
'nsynth',
'omniglot', 'open_images_v4', 'oxford_flowers102', 'oxford_iiit_pet',
'para_crawl', 'patch_camelyon', 'pet_finder', 'quickdraw_bitmap',
'resisc45', 'rock_paper_scissors', 'rock_you',
'scene_parse150', 'shapes3d', 'smallnorb', 'snli', 'so2sat', 'squad', 'stanford_dogs', 'stanford_online_products', 'starcraft_video', 'sun397', 'super_glue', 'svhn_cropped',
'ted_hrlr_translate', 'ted_multi_translate', 'tf_flowers', 'titanic', 'trivia_qa',
'uc_merced', 'ucf101',
'visual_domain_decathlon', 'voc2007',
'wikipedia', 'wmt14_translate', 'wmt15_translate', 'wmt16_translate', 'wmt17_translate', 'wmt18_translate', 'wmt19_translate', 'wmt_t2t_translate', 'wmt_translate',
'xnli']
内置数据集分类
- Audio
- groove
- nsynth
- Image
- abstract_reasoning
- aflw2k3d
- bigearthnet
- binarized_mnist
- binaryalphadigits
- caltech101
- caltech_birds2010
- caltech_birds2011
- catsvsdogs
- celeb_a
- celebahq
- cifar10
- cifar100
- cifar10_corrupted
- clevr
- coco
- coco2014
- coil100
- colorectal_histology
- colorectalhistologylarge
- curatedbreastimaging_ddsm
- cycle_gan
- deep_weeds
- diabeticretinopathydetection
- downsampled_imagenet
- dsprites
- dtd
- emnist
- eurosat
- fashion_mnist
- food101
- horsesorhumans
- imagelabelfolder
- imagenet2012
- imagenet2012_corrupted
- kitti
- kmnist
- lfw
- lsun
- mnist
- mnist_corrupted
- omniglot
- openimagesv4
- oxford_flowers102
- oxfordiiitpet
- patch_camelyon
- pet_finder
- quickdraw_bitmap
- resisc45
- rockpaperscissors
- scene_parse150
- shapes3d
- smallnorb
- so2sat
- stanford_dogs
- stanfordonlineproducts
- sun397
- svhn_cropped
- tf_flowers
- uc_merced
- visualdomaindecathlon
- voc2007
- Structured
- amazonusreviews
- higgs
- iris
- rock_you
- titanic
- Text
- cnn_dailymail
- definitepronounresolution
- gap
- glue
- imdb_reviews
- lm1b
- multi_nli
- snli
- squad
- super_glue
- trivia_qa
- wikipedia
- xnli
- Translate
- flores
- para_crawl
- tedhrlrtranslate
- tedmultitranslate
- wmt14_translate
- wmt15_translate
- wmt16_translate
- wmt17_translate
- wmt18_translate
- wmt19_translate
- wmtt2ttranslate
- Video
- bairrobotpushing_small
- moving_mnist
- starcraft_video
- ucf101
构建并加载内置数据集
tfds.load
是构建并加载tf.data.Dataset
最简单的方式tf.data.Dataset
是构建输入管道的标准 TensorFlow 接口- 加载数据集时, 默认使用规范的版本, 但是可以指定要使用的数据集的主版本, 并在结果中表明使用了哪个版本的数据集
示例 1:
mnist_train = tfds.load(
"mnist",
split = "train",
download = False,
data_dir = "~/.tensorflow_datasets/"
)
assert isinstance(mnist_train, tf.data.Dataset)
print(mnist_train)
示例 2: 版本控制
mnist_train = tfds.load(
"mnist:1.*.*",
split = "train",
download = False,
data_dir = "~/.tensorflow_datasets/"
)
assert isinstance(mnist_train, tf.data.Dataset)
print(mnist_train)
内置数据集特征字典
所有 tensorflow_datasets
数据集都包含将特征名称映射到 Tensor 值的特征字典。
典型的数据集将具有 2 个键:
"image"
"label"
示例:
mnist_train = tfds.load(
"mnist",
split = "train",
download = False,
data_dir = "~/.tensorflow_datasets/"
)
for mnist_example in mnist_train.take(1):
image, label = mnist_example["image"], mnist_example["label"]
plt.imshow(
image.numpy()[:, :, 0].astype(np.float32),
cma = plt.get_cmap("gray")
)
print("Label: %d" % label.numpy())
plt.show()
DatasetBuilder
tensorflow_datasets.load
实际上是一个基于 DatasetBuilder
的简单方便的包装器
示例:
mnist_builder = tfds.builder("mnist")
mnsit_builder.download_and_prepare()
mnist_train = mnist_builder.as_dataset(split = "train")
mnist_train
内置数据集输入管道
一旦有了 tf.data.Dataset
对象, 就可以使用 tf.data
接口定义适合模型训练的输入管道的其余部分
示例:
mnist_train = mnist_train
.repeat() \
.shuffle(1024) \
.batch(32)
# prefetch 将使输入管道可以在模型训练时一步获取批处理
mnist_train = mnist_train \
.repeat() \
.shuffle(1024) \
.batch(32) \
.prefetch(tf.data.experimental.AUTOTUNE)
内置数据集信息
示例:
# method 1
mnist_builder = tfds.builder("mnist")
info = mnist_builder.info
print(info)
print(info.features)
print(info.features["label"].num_classes)
print(info.features["label"].names)
# method 2
mnist_test, info = tfds.load(
"mnist",
split = "test",
with_info = True
)
print(info)
内置数据集可视化
示例:
fig = tfds.show_examples(info, mnist_test)
TensorFlow Dataset 预处理
数据集预处理 API 介绍
tf.data.Dataset
tf.data.Dataset
类提供了多种数据集预处理方法:
tf.data.Dataset.map(f)
:- 对数据集中的每个元素应用函数
f
, 得到一个新的数据集 - 结合
tf.io
对文件进行读写和解码 - 结合
tf.image
进行图像处理
- 对数据集中的每个元素应用函数
tf.data.Dataset.shuffle(buffer_size)
:- 将数据集打乱
- 设定一个固定大小的缓冲区(buffer), 取出前
buffer_size
个元素放入, 并从缓冲区中随机采样, 采样后的数据用后续数据替换
tf.data.Dataset.batch(batch_size)
:- 将数据集分成批次
- 对每
batch_size
个元素, 使用tf.stack()
在第 0 维合并, 成为一个元素
tf.data.Dataset.repeat()
:- 重复数据集的元素
tf.data.Dataset.reduce()
:- 与 Map 相对的聚合操作
tf.data.Dataset.take()
:- 截取数据集中的前若干个元素
tf.data.Dataset.prefetch()
:- 并行化策略提高训练流程效率
- 获取与使用
tf.data.Dataset
数据集元素
tf.data.Dataset
是一个 Python 的可迭代对象
Sequence Preprocessing
TimeseriesGenerator
pad_sequences
skipgrams
makesamplingtable
Text Preprocessing
Tokenizer
hashing_trick
- 将文本转换为固定大小的散列空间中的索引序列
one_hot
- One-hot将文本编码为大小为 n 的单词索引列表
texttoword_sequence
- 将文本转换为单词(或标记)序列
Image Preprocessing
- class
ImageDataGenerator
- method
.apply_transform()
.fit()
.flow()
- 采用数据和标签数组, 生成批量增强数据
.flowfromdataframe()
- 获取数据帧和目录路径, 并生成批量的扩充/规范化数据
.flowfromdirectory()
- 获取目录的路径并生成批量的增强数据
.getrandomtransform()
- 为转换生成随机参数
.random_transform()
- 随机转换
.standardize()
- 标准化
数据集处理示例
tf.data.Dataset.map()
使用 tf.data.Dataset.map()
将所有图片旋转 90 度
import tensorflow as tf
# data preprocessing function
def rot90(image, label):
image = tf.image.rot90(image)
return image, label
# data
mnist_dataset = tf.keras.datasets.mnist.load_data()
# data preprocessing
mnist_dataset = mnist_dataset.map(rot90)
# data visual
for image, label in mnist_dataset:
plt.title(label.numpy())
plt.imshow(image.numpy()[:, :, 0])
plt.show()
tf.data.Dataset.batch()
使用 tf.data.Dataset.batch()
将数据集划分为批次, 每个批次的大小为 4
import tensorflow as tf
# data
mnist_dataset = tf.keras.datasets.mnist.load_data()
# data preprocessing
mnist_dataset = mnist_dataset.batch(4)
# data visual
# image: [4, 28, 28, 1], labels: [4]
for images, labels in mnist_dataset:
fig, axs = plt.subplots(1, 4)
for i in range(4):
axs[i].set_title(label.numpy()[i])
axs[i].imshow(images.numpy()[i, :, :, 0])
plt.show()
tf.data.Dataset.shuffle()
使用 tf.data.Dataset.shuffle()
将数据打散后再设置批次, 缓存大小设置为 10000
一般而言, 若数据集的顺序分布较为随机, 则缓冲区的大小可较小, 否则需要设置较大的缓冲区
import tensorflow as tf
# data
mnist_dataset = tf.keras.datasets.mnist.load_data()
# data preprocessing
mnist_dataset = mnist_dataset.shuffle(buffer_size = 10000).batch(4)
# data visual
# image: [4, 28, 28, 1], labels: [4]
for i in range(2):
for images, labels in mnist_dataset:
fig, axs = plt.subplots(1, 4)
for i in range(4):
axs[i].set_title(label.numpy()[i])
axs[i].imshow(images.numpy()[i, :, :, 0])
plt.show()
tf.data.Dataset.prefetch()
使用 tf.data.Dataset.prefetch()
并行化策略提高训练流程效率
- 常规的训练流程
- 当训练模型时, 希望充分利用计算资源, 减少 CPU/GPU 的空载时间, 然而, 有时数据集的准备处理非常耗时, 使得在每进行一次训练前都需要花费大量的时间准备带训练的数据, GPU 只能空载等待数据, 造成了计算资源的浪费
- 使用
tf.data.Dataset.prefetch()
方法进行数据预加载后的训练流程tf.data.Dataset.prefetch()
可以让数据集对象Dataset
在训练时预先取出若干个元素, 使得在 GPU 训练的同时 CPU 可以准备数据, 从而提升训练流程的效率
import tensorflow as tf
# data preprocessing function
def rot90(image, label):
image = tf.image.rot90(image)
return image, label
# data
mnist_dataset = tf.keras.datasets.mnist.load_data()
# data preprocessing
# 开启数据预加载功能
mnist_dataset = mnist_dataset.prefetch(
buffer_size = tf.data.experimental.AUTOTUNE
)
# 利用多 GPU 资源, 并行化地对数据进行变换
mnist_dataset = mnist_dataset.map(
map_func = rot90,
num_parallel_calls = 2
)
mnist_dataset = mnist_dataset.map(
map_func = rot90,
num_parallel_calls = tf.data.experimental.AUTOTUNE
)
tf.data.Dataset for and iter
获取与使用 tf.data.Dataset
数据集元素
- 构建好数据并预处理后, 需要从中迭代获取数据用于训练
dataset = tf.data.Dataset.from_tensor_slices((A, B, C, ...))
for a, b, c ... in dataset:
pass
dataset = tf.data.Dataset.from_tensor_slices((A, B, C, ...))
it = iter(dataset)
a_0, b_0, c_0, ... = next(it)
a_1, b_1, c_1, ... = next(it)
图像
tf.keras.preprocessing.imgae.ImageDataGenerator
通过实时数据增强生成批量张量图像数据
- API
tf.keras.preprocessing.image.ImageDataGenerator(
featurewise_center = False, # 将数据的特征均值设定为 0
samplewise_center = False, # 将数据的样本均值设定为 0
featurewise_std_normalization = False, # 是否将特征除以特征的标准差进行归一化
samplewise_std_normalization = False, # 是否将样本除以样本的标准差进行归一化
zca_whitening = False, # 是否进行 ZCA 白化
zca_epsilon = 1e-06, # 进行 ZCA 白化的 epsilon参数
rotation_range = 0, # 随机旋转的角度范围
width_shift_range = 0.0, # 宽度调整的范围
height_shift_range = 0.0, # 高度调整的范围
brightness_range = None, # 亮度范围
shear_range = 0.0, # 剪切范围
zoom_range = 0.0, # 缩放范围
channel_shift_range = 0.0, # 通道调整范围
fill_mode = 'nearest', # 填充边界之外点的方式:
cval = 0.0,
horizontal_flip = False, # 水平翻转
vertical_flip = False, # 垂直翻转
rescale = None,
preprocessing_function = None,
data_format = None,
validation_split = 0.0,
dtype = None,
)
- 示例
from keras.datasets import cifar10
from keras import utils
from keras.preprocessing.image import ImageDataGenerator
# model training parameters
num_classes = 10
data_augmentation = True
batch_size = 32
epochs = 20
# data
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train.astype("float32")
x_test = x_test.astype("float32")
x_train /= 255
x_test /= 255
y_train = utils.to_categorical(y_train, num_classes = num_classes)
y_test = utils.to_categorical(y_test, num_classes = num_classes)
# model training
if not data_augmentation:
print("Not using data augmentation.")
model.fit(
x_train, y_train,
batch_size = batch_size,
epochs = epochs,
validation_data = (x_test, y_test),
shuffle = True
)
else:
print("Using real-time data augmentation.")
# This will do preprocessing and realtime data augmentation:
datagen = ImageDataGenerator(
featurewise_center = False,
samplewise_center = False,
featurewise_std_normalization = False,
samplewise_std_normalization = False,
zca_whitening = False,
zca_epsilon = 1e-6,
rotation_range = 0,
width_shift_range = 0.1,
height_shift_range = 0.1,
shear_range = 0.,
zoom_range = 0.,
channel_shift_range = 0,
fill_mode = "nearest",
cval = 0.,
horizontal_flip = True,
vertical_flip = False,
rescale = None,
preprocessing_function = None,
data_format = None,
validation_split = 0.0
)
datagen.fit(x_train)
model.fit_generator(datagen.flow(
x_train,
y_train,
batch_size = batch_size,
epochs = epochs,
validation_data = (x_test, y_test),
workers = 4
))
from keras.datasets import cifar10
from keras import utils
# data
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train.astype("float32")
x_test = x_test.astype("float32")
x_train /= 255
x_test /= 255
y_train = utils.to_categorical(y_train, num_classes = num_classes)
y_test = utils.to_categorical(y_test, num_classes = num_classes)
# model training parameters
batch_size = 32
epochs = 20
num_classes = 10
data_augmentation = True
# model training
datagen = ImageDataGenerator(featurewise_center = True,
featurewise_std_normalization = True,
rotation_range = 20,
width_shift_range = 0.2,
height_shift_range = 0.2,
horizontal_flip = True)
for e in range(epochs):
print("Epoch", e)
batches = 0
for x_batch, y_batch in datagen.flow(x_train, y_train, batch_size = batch_size):
model.fit(x_batchd, y_batch)
batches += 1
if batches >= len(x_train) / 32:
break
train_datagen = ImageDataGenerator(rescale = 1.0 / 255,
shear_range = 0.2,
zoom_range = 0.2,
horizontal_flip = True)
test_datagen = ImageDataGenerator(rescale = 1.0 / 255)
train_generator = train_datagen \
.flow_from_directory("data/train",
target_size = (150, 150),
batch_size = 32,
class_mode = "binary")
validation_generator = test_datagen \
.flow_from_directory("data/validation",
target_size = (150, 150),
batch_size = 32,
class_mode = "binary")
model.fit_generator(train_generator,
steps_per_epoch = 2000,
epochs = 50,
validation_data = validation_generator,
validation_steps = 800)
# we create two instances with the same arguments
data_gen_args = dict(featurewise_center=True,
featurewise_std_normalization=True,
rotation_range=90,
width_shift_range=0.1,
height_shift_range=0.1,
zoom_range=0.2)
image_datagen = ImageDataGenerator(**data_gen_args)
mask_datagen = ImageDataGenerator(**data_gen_args)
# Provide the same seed and keyword arguments to the fit and flow methods
seed = 1
image_datagen.fit(images, augment=True, seed=seed)
mask_datagen.fit(masks, augment=True, seed=seed)
image_generator = image_datagen.flow_from_directory(
'data/images',
class_mode=None,
seed=seed)
mask_generator = mask_datagen.flow_from_directory(
'data/masks',
class_mode=None,
seed=seed)
# combine generators into one which yields image and masks
train_generator = zip(image_generator, mask_generator)
model.fit_generator(
train_generator,
steps_per_epoch=2000,
epochs=50)
文本
tf.data.TextLineDataset
通常被用来以文本文件构建数据集(原文件中的一行为一个样本)。
这适用于大多数的基于行的文本数据(例如, 诗歌或错误日志)
- 删除文档的页眉、页脚、行号、章节标题
import os
import tensorflow as tf
import tensorflow_datasets as tfds
DIRECTORY_URL = "https://storage.googleapis.com/download.tensorflow.org/data/illiad/"
FILE_NAMES = ["cowper.txt", "derby.txt", "butler.txt"]
for name in FILE_NAMES:
text_dir = tf.keras.utils.get_file(name, origin = DIRECTORY_URL + name)
def labeler(example, index):
return example, tf.cast(index, tf.int64)
parent_dir = os.path.dirname(text_dir)
labeled_data_sets = []
for i, file_name in enumerate(FILE_NAMES):
lines_dataset = tf.data.TextLineDataset(os.path.join(parent_dir, file_name))
labeled_dataset = lines_dataset.map(lambda ex: labeler(ex, i))
labeled_data_sets.append(labeled_dataset)
BUFFER_SIZE = 50000
BATCH_SIZE = 64
TAKE_SIZE = 5000
all_labeled_data = labeled_data_sets[0]
for labeled_dataset in labeled_data_sets[1:]:
all_labeled_data = all_labeled_data.concatenate(labeled_dataset)
all_labeled_data = all_labeled_data.shuffle(BUFFER_SIZE, reshuffle_each_iteration = False)
for ex in all_labeled_data.take(5):
print(ex)
Unicode
TF.Text
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.text import hashing_trick
from keras.preprocessing.text import one_hot
from keras.preprocessing.text import text_to_word_sequence
数据管道构建
可以从 Numpy array、Pandas DataFrame、Python generator、 CSV 文件、文本文件、文件路径、TFRecord 文件等方式构建数据管道。
其中:
- 通过 Numpy array、Pandas DataFrame、文件路径构建数据管道是最常用的方法
- 通过 TFRecord 文件方式构建数据管道较为复杂,
需要对样本构建
tf.Example
后压缩成字符串写到 TFRecord 文件, 读取后再解析成tf.Example
- TFRecord 文件的优点是压缩后的文件较小,便于网络传播,加载速度较快
Numpy array 构建数据管道
import numpy as np
from sklearn import datasets
import tensorflow as tf
iris = datasets.load_iris()
iris_dataset = tf.data.Dataset.from_tensor_slices(
(iris["data"], iris["target"])
)
for features, label in iris_dataset.take(5):
print([features, label])
[
<tf.Tensor: shape=(4,), dtype=float64, numpy=array([5.1, 3.5, 1.4, 0.2])>,
<tf.Tensor: shape=(), dtype=int64, numpy=0>
]
[
<tf.Tensor: shape=(4,), dtype=float64, numpy=array([4.9, 3. , 1.4, 0.2])>,
<tf.Tensor: shape=(), dtype=int64, numpy=0>
]
[
<tf.Tensor: shape=(4,), dtype=float64, numpy=array([4.7, 3.2, 1.3, 0.2])>,
<tf.Tensor: shape=(), dtype=int64, numpy=0>
]
[
<tf.Tensor: shape=(4,), dtype=float64, numpy=array([4.6, 3.1, 1.5, 0.2])>,
<tf.Tensor: shape=(), dtype=int64, numpy=0>
]
[
<tf.Tensor: shape=(4,), dtype=float64, numpy=array([5. , 3.6, 1.4, 0.2])>,
<tf.Tensor: shape=(), dtype=int64, numpy=0>
]
Pandas DataFrame 构建数据管道
import pandas as pd
from sklearn import datasets
import tensorflow as tf
iris = datasets.load_iris()
iris_df = pd.DataFrame(
iris["data"],
columns = iris.feature_names,
)
iris_datasets = tf.data.Dataset.from_tensor_slices(
(iris_df.to_dict("list"), iris["target"])
)
for features, label in iris_dataset.take(3):
print([features, label])
[
{
'sepal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=5.1>,
'sepal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=3.5>,
'petal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=1.4>,
'petal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=0.2>
},
<tf.Tensor: shape=(), dtype=int64, numpy=0>
]
[
{
'sepal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=4.9>,
'sepal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=3.0>,
'petal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=1.4>,
'petal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=0.2>
},
<tf.Tensor: shape=(), dtype=int64, numpy=0>
]
[
{
'sepal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=4.7>,
'sepal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=3.2>,
'petal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=1.3>,
'petal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=0.2>
},
<tf.Tensor: shape=(), dtype=int64, numpy=0>
]
Python Generator 构建数据管道
CSV 构建数据管道
文本文件构建数据管道
dataset = tf.data.TextLineDataset(
filenames = [
"./data/titanic/train.csv",
"./data/titanic/test.csv",
],
).skip(1) # 略去第一行 header
for line in dataset.take(5):
print(lien)
tf.Tensor(b'493,0,1,"Molson, Mr. Harry Markland",male,55.0,0,0,113787,30.5,C30,S', shape=(), dtype=string)
tf.Tensor(b'53,1,1,"Harper, Mrs. Henry Sleeper (Myna Haxtun)",female,49.0,1,0,PC 17572,76.7292,D33,C', shape=(), dtype=string)
tf.Tensor(b'388,1,2,"Buss, Miss. Kate",female,36.0,0,0,27849,13.0,,S', shape=(), dtype=string)
tf.Tensor(b'192,0,2,"Carbines, Mr. William",male,19.0,0,0,28424,13.0,,S', shape=(), dtype=string)
tf.Tensor(b'687,0,3,"Panula, Mr. Jaako Arnold",male,14.0,4,1,3101295,39.6875,,S', shape=(), dtype=string)
文件路径构建数据管道
TFRecord 文件构建数据管道
TFRecord 数据文件介绍
TFRecord 是 TensorFlow 中的数据集存储格式。当将数据集整理成 TFRecord 格式后, TensorFlow 就可以高效地读取和处理这些数据集了。从而帮助更高效地进行大规模模型训练
TFRecord 可以理解为一系列序列化的 tf.train.Example
元素所组成的列表文件,
而每一个 tf.train.Example
又由若干个 tf.train.Feature
的字典组成
# dataset.tfrecords
[
{ # example 1 (tf.train.Example)
'feature_1': tf.train.Feature,
...
'feature_k': tf.train.Feature,
},
...
{ # example N (tf.train.Example)
'feature_1': tf.train.Feature,
...
'feature_k': tf.train.Feature,
},
]
TFRecord 文件保存
TFRecord 文件保存步骤
为了将形式各样的数据集整理为 TFRecord 格式, 可以对数据集中的每个元素进行以下步骤:
- 读取该数据元素到内存
- 将该元素转换为
tf.train.Example
对象- 每个
tf.train.Example
对象由若干个tf.train.Feature
的字典组成, 因此需要先建立 Feature 的子典
- 每个
- 将
tf.train.Example
对象序列化为字符串, 并通过一个预先定义的tf.io.TFRecordWriter
写入TFRecord
文件
TFRecord 文件保存示例
import tensorflow as tf
import os
# root
root_dir = "/Users/zfwang/project/machinelearning/deeplearning"
# project
project_path = os.path.join(root_dir, "deeplearning/src/tensorflow_src")
# model save
models_path = os.path.join(project_path, "save")
# data
cats_and_dogs_dir = os.path.join(root_dir, "datasets/cats_vs_dogs")
data_dir = os.path.join(root_dir, "datasets/cats_vs_dogs/cats_and_dogs_small")
# train data
train_dir = os.path.join(data_dir, "train")
train_cats_dir = os.path.join(train_dir, "cat")
train_dogs_dir = os.path.join(train_dir, "dog")
# tfrecord
tfrecord_file = os.path.join(cats_and_dogs_dir, "train.tfrecord")
# 训练数据
train_cat_filenames = [os.path.join(train_cats_dir, filename) for filename in os.listdir(train_cats_dir)]
train_dog_filenames = [os.path.join(train_dogs_dir, filename) for filename in os.listdir(train_dogs_dir)]
train_filenames = train_cat_filenames + train_dog_filenames
train_labels = [0] * len(train_cat_filenames) + [1] * len(train_dog_filenames)
# 迭代读取每张图片, 建立 tf.train.Feature 字典和 tf.train.Example 对象, 序列化并写入 TFRecord
with tf.io.TFRecordWriter(tfrecord_file) as writer:
for filename, label in zip(train_filenames, train_labels):
# 读取数据集图片到内存, image 为一个 Byte 类型的字符串
image = open(filename, "rb").read()
# 建立 tf.train.Feature 字典
feature = {
# 图片是一个 Byte 对象
"image": tf.train.Feature(bytes_list = tf.train.BytesList(value = [image])),
"label": tf.train.Feature(int64_list = tf.train.Int64List(value = [label]))
}
# 通过字典建立 Example
example = tf.train.Example(features = tf.train.Features(feature = feature))
# 将 Example 序列化并写入 TFRecord 文件
writer.write(example.SerializeToString())
TFRecord 文件读取
TFRecord 数据文件读取步骤
- 通过
tf.data.TFRecordDataset
读入原始的 TFRecord 文件, 获得一个tf.data.Dataset
数据集对象 此时文件中的tf.train.Example
对象尚未被反序列化 - 通过
tf.data.Dataset.map
方法, 对该数据集对象中的每个序列化的tf.train.Example
字符串 执行tf.io.parse_single_example
函数, 从而实现反序列化
TFRecord 数据文件读取示例
import tensorflow as tf
import os
import matplotlib.pyplot as plt
# root
root_dir = "/Users/zfwang/project/machinelearning/deeplearning"
# data
cats_and_dogs_dir = os.path.join(root_dir, "datasets/cats_vs_dogs")
# tfrecord
tfrecord_file = os.path.join(cats_and_dogs_dir, "train.tfrecord")
def _parse_example(example_string):
"""
将 TFRecord 文件中的每一个序列化的 tf.train.Example 解码
"""
# 定义 Feature 结构, 告诉解码器每个 Feature 的类型是什么
feature_description = {
"image": tf.io.FixedLenFeature([], tf.string),
"label": tf.io.FixedLenFeature([], tf.int64)
}
feature_dict = tf.io.parse_single_example(example_string, feature_description)
# 解码 JPEG 图片
feature_dict["image"] = tf.io.decode_jpeg(feature_dict["image"])
return feature_dict["image"], feature_dict["label"]
# 读取 TFRecord 文件
raw_dataset = tf.data.TFRecordDataset(tfrecord_file)
dataset = raw_dataset.map(_parse_example)
for image, label in dataset:
plt.title("cat" if label == 0 else "dog")
plt.imshow(image.numpy())
plt.show()
tf.io 的其他格式
tf.TensorArray
tf.TensorArray 介绍
在部分网络结构中, 尤其是涉及时间序列的结构中, 可能需要将一系列张量以数组的方式依次存放起来, 以供进一步处理
- 在即时执行模式下, 可以直接使用一个 Python 列表存放数组
- 如果需要基于计算图的特性, 例如使用
@tf.function
加速模型运行或者使用SaveModel
导出模型, 就无法使用 Python 列表了
TensorFlow 提供了 tf.TensorArray
(TensorFlow 动态数组) 支持计算图特性的 TensorFlow 动态数组
- 声明方式如下:
arr = tf.TensorArray(dtype, size, dynamic_size = False)
:- 声明一个大小为
size
, 类型为dtype
的TensorArray arr
- 如果将
dynamic_size
参数设置为 True, 则该数组会自动增长空间
- 声明一个大小为
- 读取和写入的方法如下:
write(index, value)
: 将 value 写入数组的第 index 个位置read(index)
: 读取数组的第 index 个值stack()
unstack()
import tensorflow as tf
@tf.function
def array_write_and_read():
arr = tf.TensorArray(dtype = tf.float32, size = 3)
arr = arr.write(0, tf.constant(0.0))
arr = arr.write(1, tf.constant(1.0))
arr = arr.write(2, tf.constant(2.0))
arr_0 = arr.read(0)
arr_1 = arr.read(1)
arr_2 = arr.read(2)
return arr_0, arr_1, arr_2
a, b, c = array_write_and_read()
print(a, b, c)
tf.TensorArray write
由于需要支持计算图, tf.TensorArray
的 write()
是不可以忽略左值的,
也就是说, 在图执行模式下, 必须按照以下的形式写入数组, 才可以正常生成一个计算图操作,
并将该操作返回给 arr
arr.write(index, value)
- 不可以写成
arr.write(index, value)
提升管道性能
训练深度学习模型常常会非常耗时,模型训练的耗时主要来自两个部分
- 数据准备
- 数据准备的过程的耗时则可以通过构建高效的数据管道进行提升
- 参数迭代
- 参数迭代过程的耗时通常依赖于 GPU 来提升
以下是一些构建高效数据管道的建议:
- 使用
prefetch
方法让数据准备和参数迭代两个过程相互并行 - 使用
interleave
方法可以让数据读取过程多进程执行 - 使用
map
时设置num_parallel_calls
让数据转换过程多进程执行 - 使用
cache
方法让数据在第一个epoch
后缓存到内存中,仅限于数据集不大的情形 - 使用
map
转换时,先batch
,然后采用向量化的转换方法对每个 batch 进行转换
prefetch
interleave
map num_parallel_calls
cache
map batch
特征列
特征列通常用于对结构化数据实施特征工程时使用,图像或者文本数据一般不会用到特征列