在训练过程中,验证数据集是否每一次都会被初始化?

问题描述 投票:1回答:1

设置。

  • U-Net网络被训练成处理小的补丁(例如64x64像素)。
  • 使用Tensorflow Dataset API为网络提供训练数据集和验证数据集。
  • 小补丁是通过对大得多的图像进行采样(随机)产生的。
  • 图像补丁的采样发生在训练过程中(训练和验证图像补丁在飞行中被裁剪)。
  • Tensorflow 2.1 (急切执行模式)

训练和验证数据集都是一样的。

dataset = tf.data.Dataset.from_tensor_slices((large_images, large_targets))
dataset = dataset.shuffle(buffer_size=num_large_samples)
dataset = dataset.map(get_patches_from_large_images, num_parallel_calls=num_parallel_calls)
dataset = dataset.unbatch()
dataset = dataset.shuffle(buffer_size=num_small_patches)
dataset = dataset.batch(patches_batch_size)
dataset = dataset.prefetch(1)
dataset = dataset.repeat()

训练和验证数据集都是一样的 get_patches_from_large_images 从一个大图像中提取预定义数量的小斑点,使用的是 tf.image.random_crop. 有两个嵌套循环 forwhile. 外环 for 负责生成预定义数量的小补丁和 while 是用来检查随机生成的补丁是否使用了 tf.image.random_crop 符合一些预定义的标准(例如,只包含背景的补丁应该被丢弃)。内循环 while 如果不能在一些预定义的迭代次数中生成一个合适的补丁,就会放弃,这样我们就不会陷入这个循环。这种方法是基于所提出的解决方案 此处.

for i in range(number_of_patches_from_one_large_image):
    num_tries = 0
    patches = []
    while num_tries < max_num_tries_befor_giving_up:
          patch = tf.image.random_crop(large_input_and_target_image,[patch_size, patch_size, 2])
          if patch_meets_some_criterions:
             break
          num_tries = num_tries + 1
   patches.append(patch)

实验。

  • 训练数据集和验证数据集喂养模型是相同的(5个大的输入-目标图像对),两个数据集从单个大图像中产生完全相同数量的小补丁。
  • 训练和验证的batch_size是相同的,等于50个图像补丁。
  • steps_per_epochvalidation_steps 等(20批

训练运行时 validation_freq=5

unet_model.fit(dataset_train, epochs=10, steps_per_epoch=20, validation_data = dataset_val, validation_steps=20, validation_freq=5)


Train for 20 steps, validate for 20 steps
Epoch 1/10
20/20 [==============================] - 44s 2s/step - loss: 0.6771 - accuracy: 0.9038
Epoch 2/10
20/20 [==============================] - 4s 176ms/step - loss: 0.4952 - accuracy: 0.9820
Epoch 3/10
20/20 [==============================] - 4s 196ms/step - loss: 0.0532 - accuracy: 0.9916
Epoch 4/10
20/20 [==============================] - 4s 194ms/step - loss: 0.0162 - accuracy: 0.9942
Epoch 5/10
20/20 [==============================] - 42s 2s/step - loss: 0.0108 - accuracy: 0.9966 - val_loss: 0.0081 - val_accuracy: 0.9975
Epoch 6/10
20/20 [==============================] - 1s 36ms/step - loss: 0.0074 - accuracy: 0.9978
Epoch 7/10
20/20 [==============================] - 4s 175ms/step - loss: 0.0053 - accuracy: 0.9985
Epoch 8/10
20/20 [==============================] - 3s 169ms/step - loss: 0.0034 - accuracy: 0.9992
Epoch 9/10
20/20 [==============================] - 3s 171ms/step - loss: 0.0023 - accuracy: 0.9995
Epoch 10/10
20/20 [==============================] - 43s 2s/step - loss: 0.0016 - accuracy: 0.9997 - val_loss: 0.0013 - val_accuracy: 0.9998

我们可以看到,第一个纪元和有验证的纪元(每5个纪元)比没有验证的纪元花费了更多的时间。同样的实验,但这次验证是在每个纪元运行,给我们以下结果。

history = unet_model.fit(dataset_train, epochs=10, steps_per_epoch=20, validation_data = dataset_val, validation_steps=20)
Train for 20 steps, validate for 20 steps
Epoch 1/10
20/20 [==============================] - 84s 4s/step - loss: 0.6775 - accuracy: 0.8971 - val_loss: 0.6552 - val_accuracy: 0.9542
Epoch 2/10
20/20 [==============================] - 41s 2s/step - loss: 0.5985 - accuracy: 0.9833 - val_loss: 0.4677 - val_accuracy: 0.9951
Epoch 3/10
20/20 [==============================] - 43s 2s/step - loss: 0.1884 - accuracy: 0.9950 - val_loss: 0.0173 - val_accuracy: 0.9948
Epoch 4/10
20/20 [==============================] - 44s 2s/step - loss: 0.0116 - accuracy: 0.9962 - val_loss: 0.0087 - val_accuracy: 0.9969
Epoch 5/10
20/20 [==============================] - 44s 2s/step - loss: 0.0062 - accuracy: 0.9979 - val_loss: 0.0051 - val_accuracy: 0.9983
Epoch 6/10
20/20 [==============================] - 45s 2s/step - loss: 0.0039 - accuracy: 0.9989 - val_loss: 0.0033 - val_accuracy: 0.9991
Epoch 7/10
20/20 [==============================] - 44s 2s/step - loss: 0.0025 - accuracy: 0.9994 - val_loss: 0.0023 - val_accuracy: 0.9995
Epoch 8/10
20/20 [==============================] - 44s 2s/step - loss: 0.0019 - accuracy: 0.9996 - val_loss: 0.0017 - val_accuracy: 0.9996
Epoch 9/10
20/20 [==============================] - 44s 2s/step - loss: 0.0014 - accuracy: 0.9997 - val_loss: 0.0013 - val_accuracy: 0.9997
Epoch 10/10
20/20 [==============================] - 45s 2s/step - loss: 0.0012 - accuracy: 0.9998 - val_loss: 0.0011 - val_accuracy: 0.9998

问题:在第一个例子中,我们可以看到训练数据集(dataset_train)的初始化创建花费了大约40s。然而,随后的epoch(没有验证)时间较短,需要约4s。尽管如此,对于具有验证步骤的epoch,持续时间再次延长到约40秒。验证数据集(dataset_val)与训练数据集(datasat_train)完全相同,因此其创建初始化的过程花费了约40s。然而,我感到惊讶的是,每一步验证都很费时间。我预计第一次验证需要40s,但接下来的验证应该需要4s左右。我以为验证数据集会像训练数据集一样,所以第一次取数据集会花很长时间,但后续应该会短很多。我说的对不对,还是我遗漏了什么?

更新一下。我检查了一下,从数据集创建迭代器需要大约40秒的时间。

dataset_val_it = iter(dataset_val) #40s

如果我们从内部看 fit 函数,我们将看到 data_handler 对象 一经创建 整个训练,它 返回数据迭代器 在训练过程的主循环中使用。迭代器是通过调用函数 enumerate_epochs. 当拟合函数想要执行验证过程时,它 调用评估函数. 每当 evaluate 函数被称为 它创建了新的data_handler对象. 然后... 它调用enumerate_epochs函数。 进而从数据集中创建迭代器。不幸的是,在复杂数据集的情况下,这个过程非常耗时。

tensorflow tensorflow2.0 tensorflow-datasets tf.keras
1个回答
0
投票

如果你想只是想要一个快速的解决方案来加快你的输入管道,你可以尝试一下 缓存验证数据集的元素。.

如果我们看一下fit函数里面,我们会发现data_handler对象在整个训练过程中被创建了一次,它返回的数据迭代器被用于训练过程的主循环中。迭代器是通过调用函数enumerate_epochs来创建的。当拟合函数要执行验证过程时,它调用evaluate函数。每当evaluation函数被调用时,它都会创建新的data_handler对象。然后调用enumerate_epochs函数,再从数据集中创建迭代器。不幸的是,在复杂数据集的情况下,这个过程很耗时。

我从来没有深入研究过这个问题。tf.data 代码,但你似乎提出了一个观点。我觉得可以为此在Github上开一个issue。

© www.soinside.com 2019 - 2024. All rights reserved.