接着上篇文章讲一下Dataset的性能优化,训练深度学习模型常常会非常耗时。模型训练的耗时主要来自于两个部分,一部分来自数据准备,另一部分来自参数迭代。参数迭代过程的耗时通常依赖于GPU来提升, 而数据准备过程的耗时则可以通过构建高效的数据管道进行提升。
下边是一些构建高效数据管道的建议:
1.prefetch方法:
原始方法执行,可以看到执行训练步骤涉及:
• 打开文件(如果尚未打开)
• 从文件中获取数据条目,
• 使用数据进行训练。
prefetc h与训练步骤的预处理和模型执行重叠。当模型执行训练步骤时s,输入管道将读取步骤s+ 1的数据。这样做可以将步长时间减少到训练的最大值(而不是总和),并减少提取数据所需的时间。
"""
tf.data.Dataset.prefetch:它可用于将产生数据的时间与消耗数据的时间分开。特别是,map使用后台线程和内部缓冲区在请求输入之前,提前从输入数据集中预提取元素。注意:要预取的元素数量应等于(或可能大于)单个训练步骤消耗的ba tc h数量。可以手动调整此值,也可以将其设置为tf.data.experimental.AUTOTUNE ,
提示 tf.data运行时在运行时动态调整值的值。
"""
#定义解码函数
def _decode_and_resize(filename, label):
image_string = tf.io.read_file(filename) # 读取原始文件
image_decoded = tf.image.decode_jpeg(image_string) # 解码JPEG图片
image_resized = tf.image.resize(image_decoded, [256, 256]) / 255.0
return image_resized, label
batch_size = 32
train_dataset = tf.data.Dataset.from_tensor_slices((train_filenames, train_labels))
#计算时长函数
def benchmark(dataset, num_epochs=1):
start_time = time.perf_counter()
for epoch_num in range(num_epochs):
for sample in dataset:
# Performing a training step
time.sleep(0.01)
tf.print("Execution time:", time.perf_counter() - start_time)
benchmark(train_dataset.map(
map_func=_decode_and_resize,
num_parallel_calls=tf.data.experimental.AUTOTUNE),
num_epochs=1)
out:Execution time: 29.59943906086638
benchmark(
train_dataset.map(
map_func=_decode_and_resize,
num_parallel_calls=tf.data.experimental.AUTOTUNE)
.prefetch(tf.data.experimental.AUTOTUNE),
num_epochs=1
)
out:Execution time: 29.054936807737768
2.interleave 方法
tf.data.Dataset .interleave可以进行并行化数据加载,并交织其他数据集(例如数据文件读取器)的内容。可以通过cycle_length参数指定要重叠的数据集数量,而并行度则可以通过num_parallel_calls参数指定。使用num_parallel_calls可以并行加载多个数据集,从而减少了等待文件打开的时间。
"""
2.interleave的使用:
interleave(
map_func, cycle_length=AUTOTUNE, block_length=1, num_parallel_calls=None,
deterministic=None
)
interleave()是Dataset的类方法,所以interleave是作用在一个Dataset上的。
1.从Dataset中取出cycle_length个element,然后对这些element apply map_func, 得到cycle_length个新的Dataset对象。
2.从这些新生成的Dataset对象中取数据,每个Dataset对象一次取block_length个数据。
3.当新生成的某个Dataset的对象取尽时,从原Dataset中再取一个element,然后apply map_func,以此类推。
"""
filenames = ["./interleave_data/train.csv", "./interleave_data/eval.csv",
"./interleave_data/train.csv", "./interleave_data/eval.csv",]
dataset = tf.data.Dataset.from_tensor_slices(filenames)
def data_func(line):
line = tf.strings.split(line, sep = ",")
return line
dataset_1 = dataset.interleave(lambda x:
tf.data.TextLineDataset(x).skip(1).map(data_func),
cycle_length=4, block_length=16)
benchmark(dataset_1,
num_epochs=1)
out:Execution time: 25.970601931961596
dataset_2 = dataset.interleave(lambda x:
tf.data.TextLineDataset(x).skip(1).map(data_func),
num_parallel_calls=tf.data.experimental.AUTOTUNE,
cycle_length=4, block_length=16)
benchmark(dataset_2,
num_epochs=1)
out:Execution time: 25.826337882865033
3.使用map来多进程执行数据预处理
benchmark(
train_dataset.map(
map_func=_decode_and_resize,
num_parallel_calls=tf.data.experimental.AUTOTUNE)
)
out:Execution time: 29.13341431492455
benchmark(
train_dataset.map(
map_func=_decode_and_resize
)
)
out:Execution time: 28.908936553196412