CPU方面的性能瓶颈

nin*_*nja 6 keras tensorflow

我正在使用语义分割架构进行研究。我需要加快培训速度,但不知道在哪里进一步寻找。

一般信息

  • 形状的图像(512,512,3)
  • 提供4个GPU GeForce GTX 1080 11 GB GPU内存
  • 1个可用的CPU Intel(R)Xeon(R)CPU E5-2637 v4#3.50GHz
  • 足够的内存
  • 我用Keras
  • 我使用轻量数据预处理(主要是裁剪,没有太多的数据扩充)

我曾尝试过有关数据加载的不同方法,但每一次瓶颈似乎都是CPU而不是GPU。我跑nvidia-smihtop看到利用。

到目前为止我尝试过的是:

  • Keras +定制DataGenerator带有8个工作人员和1个GPU model.fit_generator(generator=training_generator,use_multiprocessing=True, workers=8)

  • Keras + tf.data.dataset包含从原始图像加载的数据 model.fit(training_dataset.make_one_shot_iterator(),...)

    我尝试了两种预取方式:
    dataset = dataset.prefetch(tf.contrib.data.AUTOTUNE)
    dataset = dataset.apply(tf.contrib.data.prefetch_to_device('/gpu:0'))

  • Keras + tf.data.dataset,数据是从tf.Records
    => 加载的。

发现

  • 使用多个GPU(使用Keras相当容易)会减慢训练速度,因为开销计算会占用CPU。
  • 令人惊讶的是,普通的DataGenerator方法(没有tf.data.dataset)是目前最快的方法。
  • 每种方法都可以在很短的时间内使GPU利用率提高100%。但有时也为0%。

现在,我的处理链看起来像这样:

磁盘上的数据-> CPU将数据加载到RAM中-> CPU执行数据预处理-> CPU将数据移至GPU-> GPU执行训练步骤

因此,加快培训速度的唯一方法是预先进行所有预处理,然后将文件保存到磁盘上(数据扩充将非常庞大)。然后使用tf.Records有效地加载文件。

您还有其他想法如何提高培训速度?

更新资料

我已经用两种模型测试了管道。

简单模型

在此处输入图片说明

复杂模型

在此处输入图片说明

绩效结果

我训练了3个时期的2个模型,每个时期140个步骤(批量大小= 3)。这是结果。

  1. 原始图像数据=> Keras.DataGenerator
    简单模型:126s
    复杂模型:154s

  2. 原始图像数据=> tf.data.datasets
    简单模型:208s
    复杂模型:215s

数据生成器

辅助功能

def load_image(self,path):
    image = cv2.cvtColor(cv2.imread(path,-1), cv2.COLOR_BGR2RGB)
    return image
Run Code Online (Sandbox Code Playgroud)

主要部分

#Collect a batch of images on the CPU step by step (probably the bottlebeck of the whole computation)
for i in range(len(image_filenames_tmp)):
    #print(image_filenames_tmp[i])
    #print(label_filenames_tmp[i])
    input_image = self.load_image(image_filenames_tmp[i])[: self.shape[0], : self.shape[1]]
    output_image = self.load_image(label_filenames_tmp[i])[: self.shape[0], : self.shape[1]]

    # Prep the data. Make sure the labels are in one-hot format
    input_image = np.float32(input_image) / 255.0
    output_image = np.float32(self.one_hot_it(label=output_image, label_values=label_values))

    input_image_batch.append(np.expand_dims(input_image, axis=0))
    output_image_batch.append(np.expand_dims(output_image, axis=0))

    input_image_batch = np.squeeze(np.stack(input_image_batch, axis=1))
    output_image_batch = np.squeeze(np.stack(output_image_batch, axis=1))            


return input_image_batch, output_image_batch
Run Code Online (Sandbox Code Playgroud)

tf.data.dataset

辅助功能

def preprocess_fn(train_image_filename, train_label_filename):
'''A transformation function to preprocess raw data
into trainable input. '''
     x = tf.image.decode_png(tf.read_file(train_image_filename))
     x = tf.image.convert_image_dtype(x,tf.float32,saturate=False,name=None)

     x = tf.image.resize_image_with_crop_or_pad(x,512,512)

     y = tf.image.decode_png(tf.read_file(train_label_filename))
     y = tf.image.resize_image_with_crop_or_pad(y,512,512)

     class_names, label_values = get_label_info(csv_path)

     semantic_map = []
     for colour in label_values:
         class_map = tf.reduce_all(tf.equal(y, colour), axis=-1)
         semantic_map.append(class_map)
         semantic_map = tf.stack(semantic_map, axis=-1)
         # NOTE cast to tf.float32 because most neural networks operate in float32.
      semantic_map = tf.cast(semantic_map, tf.float32)       

      return x, semantic_map
Run Code Online (Sandbox Code Playgroud)

主要部分

dataset = tf.data.Dataset.from_tensor_slices((train_image_filenames, train_label_filenames))

dataset = dataset.apply(tf.contrib.data.map_and_batch(
            preprocess_fn, batch_size,
            num_parallel_batches=4,  # cpu cores
            drop_remainder=True if is_training    
dataset = dataset.repeat()
dataset = dataset.prefetch(tf.contrib.data.AUTOTUNE) # automatically picks best buffer_size
Run Code Online (Sandbox Code Playgroud)

Jod*_*odo 2

您对处理链的看法是正确的。

根据我的经验,可以大幅提高性能的是并行化数据加载(例如,如果来自远程数据库)以及数据预处理。

这样,您可以在训练时继续处理下一批的数据,理想情况下,一旦 GPU 上的最后一个训练步骤完成,下一批的处理数据就准备好了。

如果与非常快的训练步骤相比,您有非常非常繁重的预处理,这可能不会大幅提高性能。那么我想说你最好的选择是将预处理也转移到 GPU,例如使用 CUDA。

编辑:如果这没有帮助,我建议进行更深入的分析。如果它确实是某个处理部分,请考虑如何加快速度,或者可能是一些简单的问题,其中使用列表而不是 numpy 进行数组操作。最后,您唯一的选择是保存预处理的数据,而不是在运行时计算。另一种解决方案可能是在第一次处理后缓存它(取决于您有多少内存)。