使用 tf.data.Dataset.from_tensor_slices 创建一个数据集:
dataset = tf.data.Dataset.from_tensor_slices(tf.range(10))
print(dataset, tf.range(10))
for item in dataset:
print(item)
结果如下:
该函数创建一个给定张量的切片(沿第一个维度)。给定的张量沿它们的第一维进行切片。此操作保留输入张量的结构,删除每个张量的第一个维度并将其用作数据集维度。所有输入张量的第一维必须具有相同的大小。
from_tensor_slices(
tensors
)
dataset = dataset.repeat(3).batch(7, drop_remainder=True)
for item in dataset:
print("repeat(3).batch: ", item)
dataset = dataset.map(lambda x: x * 2)
for item in dataset:
print("map: ", item)
dataset = dataset.apply(tf.data.experimental.unbatch())
for item in dataset:
print("apply: ", item)
dataset = dataset.filter(lambda x: x < 10)
for item in dataset:
print("filter: ", item)
for item in dataset.take(3):
print("take: ", item)
可以对数据集进行转换,每个方法都会返回一个新的数据集而不是对原有数据集改动。所以需要新的变量来保存新数据集。Dateset 的方法如下
重复数据集 count 次。
repeat(
count=None
)
参数 | 注释 |
---|---|
count | 一个 tf.int64,表示数据集应该重复的次数。默认行为(如果 count 是None 或-1 )是无限期重复数据集。 |
将此数据集的连续元素组合成批。
batch(
batch_size, drop_remainder=False, num_parallel_calls=None, deterministic=None
)
参数 | 注释 |
---|---|
batch_size | 在单个批次中组合的此数据集的连续元素的数量 |
drop_remainder | 表示在最后一批少于元素的情况下是否应该删除它 ; |
其它 | tf.data.Dataset | TensorFlow Core v2.6.0 |
映射数据集的元素。
map(
map_func, num_parallel_calls=None, deterministic=None
)
参数 | 注释 |
---|---|
map_func | 映射函数 |
其它 | tf.data.Dataset | TensorFlow Core v2.6.0 |
转换整个数据集
apply(
transformation_func
)
参数 | 注释 |
---|---|
transformation_func | 接受一个 Dataset 参数并返回一个 Dataset 的函数 |
示例如下:
dataset = tf.data.Dataset.range(100)
def dataset_fn(ds):
return ds.filter(lambda x: x < 5)
dataset = dataset.apply(dataset_fn)
print(list(dataset.as_numpy_iterator()))
过滤数据集。
filter(
predicate
)
参数 | 注释 |
---|---|
predicate | 将数据集元素映射到布尔值的函数 |
当训练数据集独立且均匀分布时,梯度下降效果最佳,可以通过 shuffle 方法对实例进行乱序,该函数会创建一个新的数据集,首先将源数据集的第一个元素填充到缓冲区中,并用源数据集中的新元素替换它,需要指定缓冲区的大小,并且可以提供随机种子。
dataset = tf.data.Dataset.range(10).repeat(3)
dataset = dataset.shuffle(buffer_size=5, seed=42).batch(7)
for item in dataset:
print("shuffle:", item)
如果在经过乱序的数据集上调用 repeat 函数,默认情况下会在每次迭代生成一个新次序;如果希望每次迭代重用相同的顺序,可以设置 reshuffle_each_iteration = False 。
但是对于不适合内存的大型数据集,缓冲区会很小,上述方法的作用很小。可以将源数据拆分成多个文件,在训练过程中以随机顺序读取它们,由于同一文件中的实例接近,可以同时读取多个文件并交错读取记录。
# 设 train_filepaths 为文件匹配集合
filepath_datasets = tf.data.Dataset.list_files(train_filepaths, seed=42)
接下来可以调用 interleave 方法一次读取多个文件:
dataset = filepath_datasets.interleave(lambda filepath: tf.data.TextLineDataset(filepath).skip(1), cycle_length=5)
默认情况下,interleave 不使用并行,它顺序的从每个文件中一次读取一行,若希望并行读取,可以将 num_parallel_calls 参数设置为所需的线程数。
shuffle(
buffer_size, seed=None, reshuffle_each_iteration=None
)
参数 | 注释 |
---|---|
buffer_size | 新数据集的取样数量 |
其它 | tf.data.Dataset | TensorFlow Core v2.6.0 |
list_files(
file_pattern, shuffle=None, seed=None
)
参数 | 注释 |
---|---|
file_pattern | 表示将匹配的文件名全局模式。 |
shuffle | 文件是否被打乱 |
seed | 随机种子 |
interleave(
map_func, cycle_length=None, block_length=None, num_parallel_calls=None,
deterministic=None
)
参数 | 注释 |
---|---|
map_func | 映射数据集的函数 |
cycle_length | 同时输入的数量 |
其它 | tf.data.Dataset | TensorFlow Core v2.6.0 |
加载文本文件,并创建一个数据集,其中文件的每一行成为数据集的元素。
tf.data.TextLineDataset(
filenames, compression_type=None, buffer_size=None, num_parallel_reads=None
)
参数 | 注释 |
---|---|
filenames | 一个或多个文件名。 |
其它 | tf.data.TextLineDataset | TensorFlow Core v2.6.0 |
创建从该数据集预取元素的数据集。这允许在处理当前元素时准备后面的元素。这通常会提高延迟和吞吐量,但代价是使用额外的内存来存储预取的元素。
prefetch(
buffer_size
)
TFRecord 格式是 Tensorflow 的首选格式,用于存储大量数据并有效读取数据,它只包含大小不同的二进制记录序列(一个长度、一个用于检查长度是否损坏的 CRC 校验、实际数据和最后一个 CRC 校验和)。使用方式如下:
with tf.io.TFRecordWriter('tfr.tfrecord') as f:
f.write(b"the first")
f.write(b"the second")
filepaths = ["tfr.tfrecord"]
dataset = tf.data.TFRecordDataset(filepaths)
for item in dataset:
print(item)
tf.io.TFRecordWriter(
path, options=None
)
tf.io.TFRecordWriter | TensorFlow Core v2.6.0
tf.data.TFRecordDataset(
filenames, compression_type=None, buffer_size=None, num_parallel_reads=None
)
参数 | 注释 |
---|---|
filenames | 一个或多个文件名 |
num_parallel_reads | 表示要并行读取的文件数。如果大于1,则并行读取的文件记录以交错顺序输出。 |
其它 | tf.data.TFRecordDataset | TensorFlow Core v2.6.0 |
有时候压缩文件会很有用,可以通过设置 options 参数来创建压缩的 TFRecord 文件:
options = tf.io.TFRecordOptions(compression_type="GZIP")
with tf.io.TFRecordWriter('tfr2.tfrecord', options=options) as f:
f.write(b"the first")
f.write(b"the second")
用于操作 TFRecord 文件的选项。
tf.io.TFRecordOptions(
compression_type=None, flush_mode=None, input_buffer_size=None,
output_buffer_size=None, window_bits=None, compression_level=None,
compression_method=None, mem_level=None, compression_strategy=None
)
参数 | 注释 |
---|---|
compression_type | "GZIP" , "ZLIB" , 或"" (无压缩) |
其它 | tf.io.TFRecordOptions | TensorFlow Core v2.6.0 |
Tensorflow 文件通常使用 Example protobuf ,这表示一个数据集的实例,可以通过 tf.train.Example 来创建一个实例。
person_example = tf.train.Example(
features=tf.train.Features(
feature={
"name": tf.train.Feature(bytes_list=tf.train.BytesList(value=[b"Alice"])),
"id": tf.train.Feature(int64_list=tf.train.Int64List(value=[123])),
"emails": tf.train.Feature(tf.train.Feature(bytes_list=tf.train.BytesList(value=[b"[email protected]", b"[email protected]"])))
}
)
)
然后可以调用 SerializeToString 方法对其序列化并将记录写入 TFRecord 文件中。
with tf.io.TFRecordWriter('tfr3.tfrecord') as f:
f.write(person_example.SerializeToString())
可以创建一个转换脚本,从当前格式如 CSV 中读取记录,为每个记录创建一个 Example protobuf 并序列化,然后保存的 TFRecord 文件中,最好处理过程乱序。
要加载序列化的 Example protobuf ,需要使用 tf.io.parse_single_example 来解析每个 Example ,它需要两个参数,一个是包含序列化数据的字符串标量张量,另一个是关于特征的描述,对上节的 Example 的特征描述如下:
feature_description = {
"name": tf.io.FixedLenFeature([], tf.string, default_value=""),
"id": tf.io.FixedLenFeature([], tf.int64, default_value=0),
"emails": tf.io.VarLenFeature(tf.string)
}
然后遍历 TFRecord Dataset 并解析序列化的 Example protobuf :
for example in tf.data.TFRecordDataset(['tfr3.tfrecord']):
print(tf.io.parse_single_example(example, feature_description))
固定长度特征被解析为规则张量,可变长度则解析为稀疏张量,可以通过 tf.sparse.to_dense 来将稀疏张量转换为密集张量。
可以在 BytesList 中包含任何需要的二进制数据,如对一个 JPEG 格式的图像采用 tf.io.encode_jgp 编码,并将编码得到的二进制数据放入 ByteList 中,在解析后调用 tf.io.decode_jpeg 来获取原始图像 (或者通过 tf.io.decode_image 来解码任何图像)。也可以通过 tf.io.serialize_tensor() 来序列化张量并存储在 ByteList 中,然后调用 tf.io.parse_tensor 来解析。
除此之外可以通过使用 tf.io.parse_example 一个批次地解析。
用于解析固定长度输入特征的配置
tf.io.FixedLenFeature(
shape, dtype, default_value=None
)
shape
:输入数据的形状。dtype
: 输入的数据类型。default_value
:如果示例缺少此功能时使用的值。它必须与dtype
指定的 和兼容shape
tf.io.FixedLenFeature | TensorFlow Core v2.6.0
用于解析可变长度输入特征的配置
tf.io.VarLenFeature(
dtype
)
tf.io.VarLenFeature | TensorFlow Core v2.6.0
解析单个Example
proto
tf.io.parse_single_example(
serialized, features, example_names=None, name=None
)
tf.io.parse_single_example | TensorFlow Core v2.6.0
将Example
protos解析dict
为张量。
tf.io.parse_example(
serialized, features, example_names=None, name=None
)
tf.io.parse_example | TensorFlow Core v2.6.0
tf.io.parse_single_sequence_example(
serialized, context_features=None, sequence_features=None, example_name=None,
name=None
)
tf.io.parse_single_sequence_example | TensorFlow Core v2.6.0
tf.io.parse_sequence_example(
serialized, context_features=None, sequence_features=None, example_names=None,
name=None
)
tf.io.parse_sequence_example | TensorFlow Core v2.6.0
为神经网络准备的数据需要全转化为数值特征(归一化等),对于分类特征或文本特征,需要将其转化为数字,其中一种方法就是在模型中包含预处理层。可以使用 Lambda 层实现标准下,代码参考如下:
model = keras.Sequential([
keras.layers.Lambda(lambda inputs: (inputs - means) / (std + eps))
])
但更倾向一个自包含自定义层,首先需要创建一个继承 keas.layers.Layer 类:
class Standardization(keras.layers.Layer):
def adapt(self, data):
self.means_ = np.mean(data, axis=0, keepdims=True)
self.stds_ = np.std(data, axis=0, keepdims=True)
def call(self, inputs):
return (inputs - self.means) / (self.stds_ + keras.backend.epsilon())
首先创建一个该类的对象,并通过调用 adapt 方法来创建均值和方差,然后可以通过 model.add 方法将该层添加到模型中。现在可以使用 tf.keras.layers.Normalization 代替上述过程。
将任意表达式包装为Layer
对象
tf.keras.layers.Lambda(
function, output_shape=None, mask=None, arguments=None, **kwargs
)
参数 | 注释 |
---|---|
function | 函数表达式 |
其它 | tf.keras.layers.Lambda | TensorFlow Core v2.6.0 |
tf.keras.layers.Normalization(
axis=-1, mean=None, variance=None, **kwargs
)
tf.keras.layers.Normalization | TensorFlow Core v2.6.0
vocab = ['1H OCEAN', 'INLAND', 'NEAR OCEAN', 'NEAR BAY', 'ISLAND']
indices = tf.range(len(vocab), dtype=tf.int64)
table_init = tf.lookup.KeyValueTensorInitializer(vocab, indices)
table = tf.lookup.StaticVocabularyTable(table_init, 2)
上述代码中首先定义词汇表,然后创建带有相应索引的张量,为每个查找表创建一个初始化程序并将类别列表及索引传递给它,最后创建查找表,为其提供了初始化程序并制定了词汇表外桶的数量。如果查找词汇表中不存在的类别则查找表将计算该类的哈希并将这个未知类别分配给 oov 桶中的一个,计算出的索引从已知类别开始。
然后就可以根据查找表将一小批分类特征编码为独热向量。
categories = tf.constant(['NEAR BAY', 'DESERT', 'INLAND', 'INLAND'])
cat_indices = table.lookup(categories)
cat_one_hot = tf.one_hot(cat_indices, depth=len(vocab) + 2)
如果想将上述程序封装到一个类中,也需要一个 adapt 方法提取样本类别,并调用 call 方法建立映射。现在可以通过 tf.keras.layers.TextVectorization 实现。
tf.keras.layers.TextVectorization(
max_tokens=None, standardize='lower_and_strip_punctuation',
split='whitespace', ngrams=None, output_mode='int',
output_sequence_length=None, pad_to_max_tokens=False, vocabulary=None, **kwargs
)
tf.keras.layers.TextVectorization | TensorFlow Core v2.6.0