对tensorflow.data API的温和介绍

目录

概述

使用NumPy数组和生成器函数训练Keras模型

使用tf.data创建数据集

从生成器函数创建数据集

具有预取功能的数据集

延伸阅读

总结


构建和训练Keras深度学习模型时,可以通过多种不同的方式提供训练数据。将数据呈现为NumPy数组或TensorFlow张量是很常见的。另一种方法是创建一个Python生成器函数,让训练循环从中读取数据。提供数据的另一种方法是使用数据集tf.data

在本教程中,您将了解如何将tf.data数据集用于Keras模型。完成本教程后,您将学习:

  • 如何创建和使用tf.data数据集
  • 与生成器功能相比,这样做的好处

让我们开始吧。

tensorflow.data API
的温和介绍 摄影:Monika MG。保留部分权利。

概述

本文分为四个部分;它们是:

  • 使用NumPy数组和生成器函数训练Keras模型
  • 使用tf.data创建数据集
  • 从生成器函数创建数据集
  • 带预取的数据

使用NumPy数组和生成器函数训练Keras模型

在了解tf.data API的工作原理之前,让我们回顾一下通常如何训练Keras模型。

首先,您需要一个数据集。一个例子是Keras API附带的时尚MNIST数据集。该数据集包含60000个训练样本和10000个灰度为28×28像素的测试样本,相应的分类标签使用09的整数进行编码。

数据集是一个NumPy数组。然后,您可以构建用于分类的Keras模型,并使用模型的fit()函数提供NumPy数组作为数据。

完整代码如下:

import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.datasets.fashion_mnist import load_data
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential

(train_image, train_label), (test_image, test_label) = load_data()
print(train_image.shape)
print(train_label.shape)
print(test_image.shape)
print(test_label.shape)

model = Sequential([
    Flatten(input_shape=(28,28)),
    Dense(100, activation="relu"),
    Dense(100, activation="relu"),
    Dense(10, activation="sigmoid")
])
model.compile(optimizer="adam",
              loss="sparse_categorical_crossentropy", 
              metrics="sparse_categorical_accuracy")
history = model.fit(train_image, train_label,
                    batch_size=32, epochs=50,
                    validation_data=(test_image, test_label), verbose=0)

print(model.evaluate(test_image, test_label))

plt.plot(history.history['val_sparse_categorical_accuracy'])
plt.show()

运行此代码将打印出以下内容:

(60000, 28, 28)
(60000,)
(10000, 28, 28)
(10000,)
313/313 [==============================] - 0s 392us/step - loss: 0.5114 - sparse_categorical_accuracy: 0.8446
[0.5113903284072876, 0.8446000218391418]

此外,在训练模型的50epoch上创建以下验证精度图:

训练同一网络的另一种方法是提供来自Python生成器函数而不是NumPy数组的数据。生成器函数是具有yield语句的函数,用于在函数与数据使用者并行运行时发出数据。时尚MNIST数据集的生成器可以按如下方式创建:

def batch_generator(image, label, batchsize):
    N = len(image)
    i = 0
    while True:
        yield image[i:i+batchsize], label[i:i+batchsize]
        i = i + batchsize
        if i + batchsize > N:
            i = 0

这个函数应该用语法batch_generator(train_image, train_label, 32)调用。它将无限期地批量扫描输入数组。一旦到达数组的末尾,它将从头开始重新启动。

使用生成器训练Keras模型类似于使用fit()函数:

history = model.fit(batch_generator(train_image, train_label, 32),
                    steps_per_epoch=len(train_image)//32,
                    epochs=50, validation_data=(test_image, test_label), verbose=0)

您只需要提供生成器,而不是提供数据和标签,因为它会同时提供两者。当数据显示为NumPy数组时,您可以通过查看数组的长度来判断有多少个样本。当整个数据集使用一次时,Keras可以完成一个纪元。但是,您的生成器函数将无限期地发出批处理,因此您需要在纪元结束时告诉它,使用steps_per_epoch参数到fit()函数。

在上面的代码中,验证数据以NumPy数组的形式提供,但您可以使用生成器并指定validation_steps参数。

以下是使用生成器函数的完整代码,其中输出与前面的示例相同:

import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.datasets.fashion_mnist import load_data
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential

(train_image, train_label), (test_image, test_label) = load_data()
print(train_image.shape)
print(train_label.shape)
print(test_image.shape)
print(test_label.shape)

model = Sequential([
    Flatten(input_shape=(28,28)),
    Dense(100, activation="relu"),
    Dense(100, activation="relu"),
    Dense(10, activation="sigmoid")
])

def batch_generator(image, label, batchsize):
    N = len(image)
    i = 0
    while True:
        yield image[i:i+batchsize], label[i:i+batchsize]
        i = i + batchsize
        if i + batchsize > N:
            i = 0

model.compile(optimizer="adam",
              loss="sparse_categorical_crossentropy",
              metrics="sparse_categorical_accuracy")
history = model.fit(batch_generator(train_image, train_label, 32),
                    steps_per_epoch=len(train_image)//32,
                    epochs=50, validation_data=(test_image, test_label), verbose=0)
print(model.evaluate(test_image, test_label))

plt.plot(history.history['val_sparse_categorical_accuracy'])
plt.show()

使用tf.data创建数据集

假设您已经加载了时尚MNIST数据,则可以将其转换为tf.data数据集,如下所示:

...
dataset = tf.data.Dataset.from_tensor_slices((train_image, train_label))
print(dataset.element_spec)

这将打印数据集的规范,如下所示:

(TensorSpec(shape=(28, 28), dtype=tf.uint8, name=None),
 TensorSpec(shape=(), dtype=tf.uint8, name=None))

您可以看到数据是一个元组(因为元组作为参数传递给from_tensor_slices()函数),而第一个元素在形状(28,28)中,而第二个元素是标量。这两个元素都存储为8位无符号整数。

如果在创建数据集时未将数据显示为两个NumPy数组的元组,也可以稍后执行此操作。以下内容将创建相同的数据集,但首先为图像数据和标签分别创建数据集,然后再合并它们:

...
train_image_data = tf.data.Dataset.from_tensor_slices(train_image)
train_label_data = tf.data.Dataset.from_tensor_slices(train_label)
dataset = tf.data.Dataset.zip((train_image_data, train_label_data))
print(dataset.element_spec)

这将打印相同的规格:

(TensorSpec(shape=(28, 28), dtype=tf.uint8, name=None),
 TensorSpec(shape=(), dtype=tf.uint8, name=None))

数据集中的zip()函数类似于Python中的zip()函数,因为它将多个数据集中的数据逐个匹配到一个元组中。

使用tf.data数据集的一个好处是处理数据的灵活性。下面是有关如何使用数据集训练Keras模型的完整代码,其中批大小设置为数据集:

import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.datasets.fashion_mnist import load_data
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential

(train_image, train_label), (test_image, test_label) = load_data()
dataset = tf.data.Dataset.from_tensor_slices((train_image, train_label))

model = Sequential([
    Flatten(input_shape=(28,28)),
    Dense(100, activation="relu"),
    Dense(100, activation="relu"),
    Dense(10, activation="sigmoid")
])

history = model.fit(dataset.batch(32),
                    epochs=50,
                    validation_data=(test_image, test_label),
                    verbose=0)

print(model.evaluate(test_image, test_label))

plt.plot(history.history['val_sparse_categorical_accuracy'])
plt.show()

这是使用数据集的最简单用例。如果深入研究,您会发现数据集只是一个迭代器。因此,您可以使用以下内容打印出数据集中的每个示例:

for image, label in dataset:
    print(image)  # array of shape (28,28) in tf.Tensor
    print(label)  # integer label in tf.Tensor

数据集内置了许多函数。之前使用的batch()就是其中之一。如果从数据集创建批处理并打印它们,则具有以下各项:

for image, label in dataset.batch(32):
    print(image)  # array of shape (32,28,28) in tf.Tensor
    print(label)  # array of shape (32,) in tf.Tensor

在这里,批次中的每个项目都不是样品,而是一批样品。您还具有诸如map()filter()reduce()序列转换,或另一个数据集组合的concatendate()interleave()函数。还有repeat(),take(),take_while(),skip()并且像我们熟悉的Python itertools模块中的对应物。可以在API文档中找到函数的完整列表。

从生成器函数创建数据集

到目前为止,您已经了解了在训练Keras模型时如何使用数据集代替NumPy数组。实际上,也可以从生成器函数中创建数据集。但是,您现在不再使用生成批处理的生成器函数,如您在上面的一个示例中看到的那样而是创建一个一次生成一个样本的生成器函数。以下是函数:

import numpy as np
import tensorflow as tf

def shuffle_generator(image, label, seed):
    idx = np.arange(len(image))
    np.random.default_rng(seed).shuffle(idx)
    for i in idx:
        yield image[i], label[i]

dataset = tf.data.Dataset.from_generator(
    shuffle_generator,
    args=[train_image, train_label, 42],
    output_signature=(
        tf.TensorSpec(shape=(28,28), dtype=tf.uint8),
        tf.TensorSpec(shape=(), dtype=tf.uint8)))
print(dataset.element_spec)

此函数通过随机播放索引向量来随机化输入数组。然后它一次生成一个样本。与前面的示例不同,此生成器将在数组中的样本耗尽时结束。

您可以使用从from_generator()函数创建数据集。您需要提供生成器函数的名称(而不是实例化的生成器)以及数据集的输出签名。这是必需的,因为tf.data.Dataset API无法在使用生成器之前推断数据集规范。

运行上面的代码将打印与以前相同的规范:

(TensorSpec(shape=(28, 28), dtype=tf.uint8, name=None),
 TensorSpec(shape=(), dtype=tf.uint8, name=None))

此类数据集在功能上等效于您之前创建的数据集。因此,您可以像以前一样使用它进行培训。以下是完整的代码:

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets.fashion_mnist import load_data
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential

(train_image, train_label), (test_image, test_label) = load_data()

def shuffle_generator(image, label, seed):
    idx = np.arange(len(image))
    np.random.default_rng(seed).shuffle(idx)
    for i in idx:
        yield image[i], label[i]

dataset = tf.data.Dataset.from_generator(
    shuffle_generator,
    args=[train_image, train_label, 42],
    output_signature=(
        tf.TensorSpec(shape=(28,28), dtype=tf.uint8),
        tf.TensorSpec(shape=(), dtype=tf.uint8)))

model = Sequential([
    Flatten(input_shape=(28,28)),
    Dense(100, activation="relu"),
    Dense(100, activation="relu"),
    Dense(10, activation="sigmoid")
])

history = model.fit(dataset.batch(32),
                    epochs=50,
                    validation_data=(test_image, test_label),
                    verbose=0)

print(model.evaluate(test_image, test_label))

plt.plot(history.history['val_sparse_categorical_accuracy'])
plt.show()

具有预取功能的数据集

使用数据集的真正好处是使用prefetch()

使用NumPy数组进行训练可能是性能最好的。但是,这意味着您需要将所有数据加载到内存中。使用生成器函数进行训练允许您一次准备一个批次,例如,可以按需从磁盘加载数据。但是,使用生成器函数来训练Keras模型意味着训练循环或生成器函数随时运行。要让生成器函数和Keras的训练循环并行运行并不容易。

数据集是允许生成器和训练循环并行运行的API。如果你有一个计算成本很高的生成器(例如,实时进行图像增广),你可以从这样的生成器函数创建一个数据集,然后使用prefetch()使用它,如下所示:

...
history = model.fit(dataset.batch(32).prefetch(3),
                    epochs=50,
                    validation_data=(test_image, test_label),
                    verbose=0)

Prefetch()的数字参数是缓冲区的大小。在这里,要求数据集在内存中保留三个批次,以便训练循环使用。每当使用批处理时,数据集API都会恢复生成器函数,以便在后台异步重新填充缓冲区。因此,您可以允许生成器函数内的训练循环和数据准备算法并行运行。

值得一提的是,在上一节中,你为数据集API创建了一个随机生成器。事实上,数据集API也有一个Shuffle()函数来做同样的事情,但你可能不想使用它,除非数据集足够小以适合内存。

shuffle()函数与prefetch()相同,采用缓冲区大小参数。Shuffle算法将用数据集填充缓冲区,并从中随机绘制一个元素。使用的元素将替换为数据集中的下一个元素。因此,您需要与数据集本身一样大的缓冲区来进行真正的随机洗牌。以下代码片段演示了此限制:

import tensorflow as tf
import numpy as np

n_dataset = tf.data.Dataset.from_tensor_slices(np.arange(10000))
for n in n_dataset.shuffle(10).take(20):
    print(n.numpy())

上面的输出如下所示:

9
6
2
7
5
1
4
14
11
17
19
18
3
16
15
22
10
23
21
13

在这里,您可以看到数字在其附近被打乱,并且您永远不会从其输出中看到大量数字。

延伸阅读

有关tf.data数据集的更多信息可以从其API文档中找到:

总结

在这篇文章中,您已经了解了如何使用tf.data数据集以及如何将其用于训练Keras模型。

具体而言,您了解到:

  • 如何使用来自NumPy数组、生成器和数据集的数据训练模型
  • 如何使用NumPy数组或生成器函数创建数据集
  • 如何将预取与数据集一起使用,使生成器和训练循环并行运行

https://machinelearningmastery.com/a-gentle-introduction-to-tensorflow-data-api/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值