构建人工智能模型基础:TFDS和Keras的完美搭配
上一篇:《数据工程师,转型人工智能岗位的理想时空通道》
序言:本节将带您深入探索 TensorFlow 提供的关键工具和方法,涵盖数据集管理和神经网络模型的构建与训练。在现代人工智能框架中,TensorFlow 的数据集接口 (TensorFlow Datasets, 简称 TFDS) 与 Keras 模型库为深度学习任务提供了极大的便利。本章将具体展示如何使用 TFDS 和 Keras 配合构建神经网络架构,以实现高效的数据处理和模型训练。通过本节的实践操作,您将掌握从数据加载、预处理到模型搭建的核心流程,为进一步的人工智能模型研发奠定坚实的基础。
使用TFDS与Keras模型
在上一节中,我们学到了如何使用TensorFlow和Keras创建一个简单的计算机视觉模型,使用Keras内置的数据集(包括Fashion MNIST),代码如下所示:
mnist = tf.keras.datasets.fashion_mnist
(training_images, training_labels), (test_images, test_labels) = mnist.load_data()
使用TFDS时,代码非常相似,但有一些小的变化。Keras的数据集直接给我们返回了可以在model.fit中原生使用的ndarray类型,但使用TFDS时,我们需要进行一些转换:
(training_images, training_labels), (test_images, test_labels) = tfds.as_numpy(tfds.load('fashion_mnist', split=['train', 'test'], batch_size=-1, as_supervised=True))
在这里,我们使用了tfds.load,将fashion_mnist作为所需的数据集传递给它。我们知道这个数据集有训练集和测试集的划分,所以在数组中传入这些划分项将返回包含图像和标签的适配器数组。使用tfds.as_numpy在调用tfds.load时会将数据返回为Numpy数组。指定batch_size=-1会让我们获取所有数据,而as_supervised=True则确保返回的格式为(输入,标签)元组。
完成这些操作后,我们基本上获得了与Keras数据集相同的数据格式,但有一个区别——在TFDS中,数据的形状是(28, 28, 1),而在Keras数据集中是(28, 28)。
这意味着代码需要做一些小的改动来指定输入数据的形状为(28, 28, 1),而不是(28, 28):
import tensorflow as tf
import tensorflow_datasets as tfds
(training_images, training_labels), (test_images, test_labels) = tfds.as_numpy(tfds.load('fashion_mnist', split=['train', 'test'], batch_size=-1, as_supervised=True))
training_images = training_images / 255.0
test_images = test_images / 255.0
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28, 1)),
tf.keras.layers.Dense(128, activation=tf.nn.relu),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(training_images, training_labels, epochs=5)
对于更复杂的示例,可以参考第3章中使用的“马或人”数据集,这个数据集在TFDS中也可以使用。以下是使用它来训练模型的完整代码:
import tensorflow as tf
import tensorflow_datasets as tfds
data = tfds.load('horses_or_humans', split='train', as_supervised=True)
train_batches = data.shuffle(100).batch(10)
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(300, 300, 3)),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
model.compile(optimizer='Adam', loss='binary_crossentropy', metrics=['accuracy'])
history = model.fit(train_batches, epochs=10)
正如你所见,这相当直接:只需调用 tfds.load,传入你想要的分割(在本例中是训练集),然后在模型中使用它。数据被批处理并打乱顺序,以便更有效地进行训练。
“Horses or Humans”数据集被划分为训练集和测试集,因此如果你想在训练时验证模型,可以通过TFDS加载单独的验证集,方法如下:
val_data = tfds.load('horses_or_humans', split='test', as_supervised=True)
你需要像处理训练集一样批处理它。例如:
validation_batches = val_data.batch(32)
然后在训练时,指定这些批次作为验证数据。你还需要显式设置每个周期要使用的验证步数,否则TensorFlow会报错。如果不确定,设置为1即可,如下:
history = model.fit(train_batches, epochs=10, validation_data=validation_batches, validation_steps=1)
加载特定版本
所有在TFDS中存储的数据集都使用MAJOR.MINOR.PATCH编号系统。其保证如下:如果仅PATCH更新,则调用返回的数据相同,但底层组织可能发生变化。这种变化对开发者应是无感知的。如果MINOR更新,则数据保持不变,但可能会在每条记录中增加新的特性(非破坏性更改)。此外,对特定切片(参见第74页的“使用自定义切片”)的数据不会重新排序。如果MAJOR更新,则记录格式及其位置可能会发生变化,因此特定切片可能会返回不同的值。
在检查数据集时,你会看到何时有不同版本可用——例如,cnn_dailymail数据集就是这样。如果你不想要默认版本(本文撰写时是3.0.0),而是希望使用早期版本,例如1.0.0,可以按以下方式加载:
data, info = tfds.load("cnn_dailymail:1.0.0", with_info=True)
请注意,如果你在Colab上使用TFDS,建议检查Colab使用的TFDS版本。本文撰写时,Colab预装的TFDS是2.0版,但其中存在一些加载数据集的bug(包括cnn_dailymail数据集),这些问题在TFDS 2.1及之后的版本中已修复,因此建议使用这些版本,或者至少在Colab中安装它们,而不是依赖内置的默认版本。
使用映射函数进行数据增强
在前面的章节中,你见到了使用ImageDataGenerator为模型提供训练数据时的一些有用增强工具。你可能想知道如何在使用TFDS时实现同样的功能,因为这时你不是从子目录流式读取图像。实现此功能的最佳方法(或任何其他形式的转换)是对数据适配器使用映射函数。让我们看看如何实现这一点。
之前,我们对Horses or Humans数据集的处理只是从TFDS中加载数据并为其创建批次,如下所示:
data = tfds.load('horses_or_humans', split='train', as_supervised=True)
train_batches = data.shuffle(100).batch(10)
要对数据进行变换并将其映射到数据集,你可以创建一个映射函数。这只是标准的Python代码。例如,假设你创建了一个名为augmentimages的函数,并让它进行一些图像增强,如下所示:
def augmentimages(image, label):
image = tf.cast(image, tf.float32)
image = (image/255)
image = tf.image.random_flip_left_right(image)
return image, label
然后你可以将其映射到数据上,创建一个名为train的新数据集:
train = data.map(augmentimages)
之后,在创建批次时,使用train而不是data,如下:
train_batches = train.shuffle(100).batch(32)
在augmentimages函数中,你可以看到使用tf.image.random_flip_left_right(image)对图像进行左右随机翻转。tf.image库中有很多可用于增强的函数;详细内容请参阅文档。
使用TensorFlow Addons
TensorFlow Addons库包含更多可用函数。ImageDataGenerator增强中的一些功能(如旋转)仅在此库中可用,因此建议查看它。
使用TensorFlow Addons非常简单——只需安装库即可:
pip install tensorflow-addons
安装完成后,可以将Addons混入到你的映射函数中。以下是将旋转Addons用于前面映射函数的示例:
import tensorflow_addons as tfa
def augmentimages(image, label):
image = tf.cast(image, tf.float32)
image = (image/255)
image = tf.image.random_flip_left_right(image)
image = tfa.image.rotate(image, 40, interpolation='NEAREST')
return image, label
使用自定义分割
到目前为止,你一直使用的是预先分割为训练集和测试集的数据集。例如,Fashion MNIST有60,000和10,000条记录,分别用于训练和测试。但如果你不想使用这些分割呢?如果你想根据自己的需求分割数据呢?TFDS的一个强大之处就在于——它提供了一个API,允许你精细地控制数据的分割方式。
实际上你已经见过这种方式了,例如像这样加载数据时:
data = tfds.load('cats_vs_dogs', split='train', as_supervised=True)
注意split参数是一个字符串,这里你请求了train分割,它恰好是整个数据集。如果你熟悉Python的切片符号,也可以使用它。这种符号可以总结为在方括号内定义你想要的切片,如下所示:[
例如,如果你希望train的前10,000条记录作为训练数据,可以省略
data = tfds.load('cats_vs_dogs', split='train[:10000]', as_supervised=True)
你还可以使用%来指定分割。例如,如果你希望前20%的记录用于训练,可以像这样使用:20%:
data = tfds.load('cats_vs_dogs', split='train[:20%]', as_supervised=True)
你甚至可以更进一步,组合多个分割。也就是说,如果你希望训练数据是前1000条记录和最后1000条记录的组合,可以这样做(-1000:表示“最后1000条记录”,“:1000”表示“前1000条记录”):
data = tfds.load('cats_vs_dogs', split='train[-1000:]+train[:1000]', as_supervised=True)
Dogs vs. Cats数据集没有固定的训练、测试和验证分割,但使用TFDS,创建自定义分割非常简单。假设你希望分割为80%、10%、10%。可以这样创建三个数据集:
train_data = tfds.load('cats_vs_dogs', split='train[:80%]', as_supervised=True)
validation_data = tfds.load('cats_vs_dogs', split='train[80%:90%]', as_supervised=True)
test_data = tfds.load('cats_vs_dogs', split='train[-10%:]', as_supervised=True)
一旦你有了它们,就可以像使用任何命名分割一样使用它们。
需要注意的是,由于返回的数据集无法被探测其长度,因此通常很难确认你是否正确地分割了原始数据集。要查看你在某个分割中的记录数量,你必须遍历整个数据集并一条条计数。以下是对你刚创建的训练集进行计数的代码:
train_length = [i for i, _ in enumerate(train_data)][-1] + 1
print(train_length)
这可能是一个较慢的过程,因此请确保仅在调试时使用它。
本节总结: 本章介绍了如何使用 TensorFlow Datasets(TFDS)和 Keras 搭建神经网络模型,并探索了数据增强、分割和预处理等关键操作。通过将 TFDS 数据集与 Keras 模型结合,您学会了如何高效加载和转换数据,为神经网络模型的构建和训练做好准备。此外,我们还介绍了如何灵活地自定义数据分割和应用数据增强,为模型提供更加多样化的训练数据。掌握了这些技能后,您将能够更自如地应用 TensorFlow 和 Keras 进行各种深度学习项目的开发,为复杂模型的搭建奠定坚实的基础。下一节我们将会为大家介绍从TFDS中下载出来的数据集是以什么形式保存下来的—TFRecord!