利用TF dataset改善模型训练效率的最佳实践

Posted MrCharles

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用TF dataset改善模型训练效率的最佳实践相关的知识,希望对你有一定的参考价值。

不好的实践

已经提前把数据全部保存为tfrecord, 以便提升模型训练的时候的效率,数据集大小大概为4G左右。使用如下数据集构建流程:

def load_tfrecord_dataset(tfrecord_name, batch_size, shuffle=True,
                          buffer_size=1024):
    """load dataset from tfrecord"""
    raw_dataset = tf.data.TFRecordDataset(tfrecord_name)#.cache()
    raw_dataset = raw_dataset.repeat()
    if shuffle:
        raw_dataset = raw_dataset.shuffle(
            buffer_size=buffer_size)
    dataset = raw_dataset.map(
        _parse_tfrecord(),
        num_parallel_calls=tf.data.experimental.AUTOTUNE)
    dataset = dataset.batch(batch_size).map( lambda x, y: randomcrop(x, y),  num_parallel_calls=tf.data.experimental.AUTOTUNE).prefetch(FLAGS.prefetch)
    # dataset = dataset.prefetch(
    #     buffer_size=tf.data.experimental.AUTOTUNE)
    return dataset

很显然,这样是不好的操作,只能达到70左右的利用率:

一个epoch需要34S:

好的实践

修改为如下流程,既可以提升很好:

def load_tfrecord_dataset_good(tfrecord_name, batch_size, shuffle=True,
                          buffer_size=1024):
    """load dataset from tfrecord"""
    raw_dataset = tf.data.TFRecordDataset(tfrecord_name)
    dataset = raw_dataset.map(_parse_tfrecord(),num_parallel_calls=tf.data.experimental.AUTOTUNE).cache().repeat().shuffle(buffer_size=buffer_size)
    dataset = dataset.batch(batch_size).map( lambda x, y: randomcrop(x, y),  num_parallel_calls=tf.data.experimental.AUTOTUNE)
    dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
    return dataset

可以提升到86左右了:

一个epoch大概27S:

可是还是没有跑满GPU

更好的做法

注意到我们随机裁剪是在batch之后,可能会受限制与batch的操作,导致来不及处理,我们可以让他提前裁剪,然后再去取batch,这样可以加快,让CPU一直去裁剪:

def load_tfrecord_dataset_better(tfrecord_name, batch_size, shuffle=True,
                          buffer_size=1024):
    """load dataset from tfrecord"""
    raw_dataset = tf.data.TFRecordDataset(tfrecord_name)
    dataset = raw_dataset.map(_parse_tfrecord(),num_parallel_calls=tf.data.experimental.AUTOTUNE).cache().repeat().shuffle(buffer_size=buffer_size)
    dataset = dataset.map( lambda x, y: randomcrop_single(x, y),  num_parallel_calls=tf.data.experimental.AUTOTUNE).batch(batch_size)
    dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
    return dataset

现在可以运行到97%了:

一个epoch可以24S了:

内存不够,不用cache

这种情况会慢一点,GPU利用可以达到90%左右:

但是不占内存,一个epoch大概32S:

batch, shuffle, repeat这三者的顺序

batch, shuffle, repeat这三者的顺序不同,结果也会不一样。具体可以运行下面代码并调整三者顺序来验证

    tf.compat.v1.enable_eager_execution()
    dataset = tf.data.Dataset.from_tensor_slices(list(range(0, 10)))
    print('init ds:',list(dataset.as_numpy_iterator()))
    dataset = dataset.batch(3, drop_remainder=False)
    print('batched ds:',list(dataset.as_numpy_iterator()))
    dataset = dataset.repeat(2)
    print('batched + repeated ds:',list(dataset.as_numpy_iterator()))
    dataset = dataset.shuffle(4)
    print('batched + repeated + shuffle ds:',list(dataset.as_numpy_iterator()))

得到:

init ds: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
batched ds: [array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8]), array([9])]
batched + repeated ds: [array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8]), array([9]), array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8]), array([9])]
batched + repeated + shuffle ds: [array([6, 7, 8]), array([9]), array([0, 1, 2]), array([6, 7, 8]), array([0, 1, 2]), array([9]), array([3, 4, 5]), array([3, 4, 5])]

以上是关于利用TF dataset改善模型训练效率的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

利用TF dataset改善模型训练效率的最佳实践

利用TF dataset改善模型训练效率的最佳实践

TensorFlow2 动手训练模型和部署服务

TensorFlow2 动手训练模型和部署服务

如何将 tf.data.Dataset 与 kedro 一起使用?

使用tf.train时,使用tf.dataset的Keras model.fit()会失败