Загрузка графического процессора Tensorflow составляет всего 60% (GTX 1070)

#performance #machine-learning #neural-network #tensorflow #nvidia

#Производительность #машинное обучение #нейронная сеть #tensorflow #nvidia

Вопрос:

Я тренирую модель CNN с помощью tensorflow. Я достигаю загрузки графического процессора только на 60% ( — 2-3%) без больших падений.

 Sun Oct 23 11:34:26 2016       
 ----------------------------------------------------------------------------- 
| NVIDIA-SMI 367.57                 Driver Version: 367.57                    |
|------------------------------- ---------------------- ---------------------- 
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|=============================== ====================== ======================|
|   0  GeForce GTX 1070    Off  | 0000:01:00.0     Off |                  N/A |
|  1%   53C    P2    90W / 170W |   7823MiB /  8113MiB |     60%      Default |
 ------------------------------- ---------------------- ---------------------- 

 ----------------------------------------------------------------------------- 
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      3644    C   /usr/bin/python2.7                            7821MiB |
 ----------------------------------------------------------------------------- 
  

Поскольку это карта Pascal, я использую CUDA 8 с cudnn 5.1.5
Загрузка процессора составляет около 50% (равномерно распределено по 8 потокам. i7 4770k), поэтому производительность процессора не должна быть узким местом.

Я использую двоичный формат файла Tensorflow с и читаю с tf.TFRecordReader()

Я создаю пакеты изображений, подобные этому:

 #Uses tf.TFRecordReader() to read single Example
label, image = read_and_decode_single_example(filename_queue=filename_queue) 
image = tf.image.decode_jpeg(image.values[0], channels=3)
jpeg = tf.cast(image, tf.float32) / 255.
jpeg.set_shape([66,200,3])
images_batch, labels_batch = tf.train.shuffle_batch(
    [jpeg, label], batch_size= FLAGS.batch_size,
    num_threads=8,
    capacity=2000, #tried bigger values here, does not change the performance
    min_after_dequeue=1000) #here too
  

Вот мой цикл обучения:

 sess = tf.Session()

sess.run(init)
tf.train.start_queue_runners(sess=sess)
for step in xrange(FLAGS.max_steps):
    labels, images = sess.run([labels_batch, images_batch])
    feed_dict = {images_placeholder: images, labels_placeholder: labels}
    _, loss_value = sess.run([train_op, loss],
                                 feed_dict=feed_dict)
  

У меня нет большого опыта работы с tensorflow, и я не знаю, где может быть узкое место. Если вам нужны дополнительные фрагменты кода, которые помогут выявить проблему, я их предоставлю.

ОБНОВЛЕНИЕ: результаты теста пропускной способности

 ==5172== NVPROF is profiling process 5172, command: ./bandwidthtest

Device: GeForce GTX 1070
Transfer size (MB): 3960

Pageable transfers
  Host to Device bandwidth (GB/s): 7.066359
  Device to Host bandwidth (GB/s): 6.850315

Pinned transfers
  Host to Device bandwidth (GB/s): 12.038037
  Device to Host bandwidth (GB/s): 12.683915

==5172== Profiling application: ./bandwidthtest
==5172== Profiling result:
Time(%)      Time     Calls       Avg       Min       Max  Name
 50.03%  933.34ms         2  466.67ms  327.33ms  606.01ms  [CUDA memcpy DtoH]
 49.97%  932.32ms         2  466.16ms  344.89ms  587.42ms  [CUDA memcpy HtoD]

==5172== API calls:
Time(%)      Time     Calls       Avg       Min       Max  Name
 46.60%  1.86597s         4  466.49ms  327.36ms  606.15ms  cudaMemcpy
 35.43%  1.41863s         2  709.31ms  632.94ms  785.69ms  cudaMallocHost
 17.89%  716.33ms         2  358.17ms  346.14ms  370.19ms  cudaFreeHost
  0.04%  1.5572ms         1  1.5572ms  1.5572ms  1.5572ms  cudaMalloc
  0.02%  708.41us         1  708.41us  708.41us  708.41us  cudaFree
  0.01%  203.58us         1  203.58us  203.58us  203.58us  cudaGetDeviceProperties
  0.00%  187.55us         1  187.55us  187.55us  187.55us  cuDeviceTotalMem
  0.00%  162.41us        91  1.7840us     105ns  61.874us  cuDeviceGetAttribute
  0.00%  79.979us         4  19.994us  1.9580us  73.537us  cudaEventSynchronize
  0.00%  77.074us         8  9.6340us  1.5860us  28.925us  cudaEventRecord
  0.00%  19.282us         1  19.282us  19.282us  19.282us  cuDeviceGetName
  0.00%  17.891us         4  4.4720us     629ns  8.6080us  cudaEventDestroy
  0.00%  16.348us         4  4.0870us     818ns  8.8600us  cudaEventCreate
  0.00%  7.3070us         4  1.8260us  1.7040us  2.0680us  cudaEventElapsedTime
  0.00%  1.6670us         3     555ns     128ns  1.2720us  cuDeviceGetCount
  0.00%     813ns         3     271ns     142ns     439ns  cuDeviceGet
  

Комментарии:

1. Во-первых, насколько велика ваша партия ? И насколько велика ваша модель? Не могли бы вы попробовать создать больший CNN (и поместить больше данных в один пакет), чтобы увидеть, не связана ли проблема с синхронизацией между CPU и GPU?

2. @lejlot он уже в значительной степени использует память графического процессора

3. @FranckDernoncourt Tensorflow всегда занимает как можно больше памяти графического процессора, если вы ее не настраиваете.

4. @lejlot В моем пакете 32 изображения размером 200x66x3, это 5 Мб, если я не перепутал вычисления! Однако я также пробовал с 128 пакетами, и это ничего не изменило!! Размер модели довольно сложно рассчитать… Я получил 5 сверточных слоев и 3 полностью подключенных..

5. @andre_bauer Спасибо, я не знал об этом.

Ответ №1:

Получив еще немного опыта работы с tensorflow, я понял, что использование графического процессора сильно зависит от размера сети, пакета и предварительной обработки. Использование более крупной сети с большим количеством уровней conv (например, в стиле Resnet) увеличивает использование графического процессора, поскольку требуется больше вычислений и меньше накладных расходов (по отношению к вычислениям) за счет передачи данных и т.д.

Комментарии:

1. Это точно. Эксперименты с сетевыми уровнями, типом, размером пакета и даже точностью с плавающей точкой приведут к различным показателям использования графического процессора.

Ответ №2:

Одним из потенциальных узких мест является использование шины PCI Express между центральным процессором и графическим процессором при загрузке изображений на графический процессор. Вы можете использовать некоторые инструменты для ее измерения.

Еще одним потенциальным узким местом является дисковый ввод-вывод, я не вижу в вашем коде ничего, что могло бы вызвать это, но всегда полезно следить за этим.

Комментарии:

1. Каков наилучший способ проверить наличие узких мест ввода-вывода на диске? Я использую SSD, а данные представляют собой двоичный файл. Если это узкое место, я думаю, что нет способа улучшить :/

2. @andre_bauer Я использую iotop в Linux.

3. У меня есть 1,7 ГБ (на данный момент) обучающих данных, а диск полностью простаивает, потому что я думаю, что все находится в оперативной памяти (32 ГБ, так что есть место для большего количества обучающих данных!)

4. Я обновил свой вопрос и добавил результаты теста пропускной способности. Я думаю, они выглядят нормально, верно?

5. @andre_bauer да. Жаль, что у нас нет программы для мониторинга использования шины PCI Express в Linux.