Заполнители Tensorflow против констант Tensorflow против массивов Numpy - PullRequest
4 голосов
/ 18 мая 2019

Я пытаюсь запустить прямой проход по сверточной нейронной сети, имеющей сверточный уровень, за которым следует слой объединения и, наконец, уровень активации выпрямленной линейной единицы (ReLU). Подробности о входных данных и фильтрах сверточного слоя следующие:

  • X: 4-мерные входные данные, имеющие форму [N, H, W, C], где N = 60000 - размер пакета, H = 32 - высота входного изображения, W = 32 - ширина входного изображения, и C = 1 - количество каналов во входном изображении.
  • W: 4-мерный сверточный фильтр, имеющий форму [F, F, C, Cout], где F = 3 - высота и ширина фильтра, C = 1 - количество каналов во входном изображении, а Cout = 6 - это количество каналов в выходном изображении.

Для этого есть три подхода.

Подход 1: Без использования tf.constant() или tf.placeholder()

import numpy as np
import tensorflow as tf

X = np.random.random([60000, 32, 32, 1])
W = np.random.random([3, 3, 1, 6])

C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
A = tf.nn.relu(P)

with tf.Session() as sess:
  result = sess.run(A)       # Takes 14.98 seconds

Подход 2: Использование tf.constant()

import numpy as np
import tensorflow as tf

X = tf.constant(np.random.random([60000, 32, 32, 1]), dtype=tf.float64)
W = tf.constant(np.random.random([3, 3, 1, 6]), dtype=tf.float64)

C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
A = tf.nn.relu(P)

with tf.Session() as sess:
  result = sess.run(A)       # Takes 14.73 seconds

Подход 3: Использование tf.placeholder()

import numpy as np
import tensorflow as tf 

x = np.random.random([60000, 32, 32, 1])
w = np.random.random([3, 3, 1, 6])

X = tf.placeholder(dtype=tf.float64, shape=[None, 32, 32, 1])
W = tf.placeholder(dtype=tf.float64, shape=[3, 3, 1, 6])

C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
A = tf.nn.relu(P)

with tf.Session() as sess:
  result = sess.run(A, feed_dict={X:x, W:w})       # Takes 3.21 seconds

Подход 3 (с использованием tf.placeholder()) работает почти в 4-5 раз быстрее, чем Подход 1 и Подход 2. Все эти эксперименты проводились на графическом процессоре NVIDIA GeForce GTX 1080.

Вопрос в том, почему мы получаем почти 4-5-кратное ускорение, просто используя tf.placeholder() в подходе 3 по сравнению с подходом 1 и подходом 2? В своей базовой реализации, что делает tf.placeholder(), что позволяет ему иметь такую ​​хорошую производительность?

Ответы [ 2 ]

2 голосов
/ 18 мая 2019

Приветствую @ y.selivonchyk за бесценные эксперименты, однако я чувствую, что ответ не уточняет , почему эти результаты появляются.

Я полагаю, что это не так много о«placeholder» означает «хороший», но скорее о двух других методах, которые являются плохой идеей.

Я бы предположил, что 1) и 2) на самом деле одинаковы, а 1) преобразовывает массив в константу вКапюшон - по крайней мере, это объясняет идентичное поведение.

Причина 1) и 2) занимает так много времени, что constant s встраиваются явно в вычислительный граф.Поскольку они довольно большие тензоры, это объясняет, почему построение графика занимает так много времени.Однако после построения графика последующие запуски выполняются быстрее, потому что там все «содержится».Как правило, вы должны стараться избегать включения больших частей данных в сам график - в идеале это должен быть просто набор инструкций для вычислений (т. Е. Операции Tensorflow).

С 3), граф гораздо быстреестроить, потому что мы не встраиваем в него огромный массив, а просто символический заполнитель.Однако выполнение выполняется медленнее, чем 1) и 2), поскольку значение необходимо вводить каждый раз в заполнитель (что также означает, что данные должны передаваться в графический процессор в случае, если вы работаете на одном из них).

2 голосов
/ 18 мая 2019

Я получил 12 с, 12 с и 1 с соответственно.Но.

Ваш метод не учитывает время настройки: построение графиков, распределение памяти, оптимизация графиков и т. Д. Я взял на себя смелость немного продвинуть ваши эксперименты.А именно, я делаю 10 вызовов метода session.run () для каждого метода и измеряю не только общее время, но и время для каждого отдельного вызова.Ниже приведены результаты этих экспериментов.Интересной частью является время выполнения первого вызова.

%%time
import numpy as np
import tensorflow as tf
​
X = np.random.random([60000, 32, 32, 1])
W = np.random.random([3, 3, 1, 6])
​
C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
A = tf.nn.relu(P)
​
with tf.Session() as sess:
    for i in range(10):
        ts = time.time()
        result = sess.run(A)  
        te = time.time()
        print('%2.2f sec' % (te-ts))



10.44 sec
0.24 sec
0.23 sec
0.23 sec
0.23 sec
0.24 sec
0.23 sec
0.23 sec
0.24 sec
0.23 sec
CPU times: user 17 s, sys: 7.56 s, total: 24.5 s
Wall time: 13.8 s

2:

%%time
import numpy as np
import tensorflow as tf
​
X = tf.constant(np.random.random([60000, 32, 32, 1]), dtype=tf.float64)
W = tf.constant(np.random.random([3, 3, 1, 6]), dtype=tf.float64)
​
C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
A = tf.nn.relu(P)
​
with tf.Session() as sess:
    for i in range(10):
        ts = time.time()
        result = sess.run(A) 
        te = time.time()
        print('%2.2f sec' % (te-ts))



10.53 sec
0.23 sec
0.23 sec
0.24 sec
0.23 sec
0.23 sec
0.23 sec
0.23 sec
0.23 sec
0.26 sec
CPU times: user 17 s, sys: 7.77 s, total: 24.8 s
Wall time: 14.1 s

3:

    %%time
    import numpy as np
    import tensorflow as tf 
    ​
    x = np.random.random([60000, 32, 32, 1])
    w = np.random.random([3, 3, 1, 6])
    ​
    X = tf.placeholder(dtype=tf.float64, shape=[None, 32, 32, 1])
    W = tf.placeholder(dtype=tf.float64, shape=[3, 3, 1, 6])
    ​
    C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
    P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
    A = tf.nn.relu(P)
    ​
    with tf.Session() as sess:
        for i in range(10):
            ts = time.time()
            result = sess.run(A, feed_dict={X:x, W:w})  
            te = time.time()
            print('%2.2f sec' % (te-ts))

0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
CPU times: user 2.81 s, sys: 2.31 s, total: 5.12 s
Wall time: 5.02 s

Как видите, дляПервые 2 метода Первый вызов sess.run действительно занимает некоторое время (10 секунд), в то время как метод 3 всегда занимает 45 секунд.Но второй и последующие прогоны первых двух проходят в два раза быстрее с .23 сек.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...