Что такое правильный способ для сравнения части тензора графа потока? - PullRequest
4 голосов
/ 13 апреля 2020

Я хочу сравнить некоторые части графика, здесь для простоты я использую conv_block, который просто conv3x3.

  1. Это нормально, что x_np используется в l oop то же самое или мне нужно каждый раз регенерировать его?
  2. Нужно ли выполнять прогрев для прогрева перед запуском фактического теста (кажется, это необходимо для теста на GPU)? как это сделать правильно? sess.run(tf.global_variables_initializer()) достаточно?
  3. Как правильно измерить время в python, т. е. более точный метод.
  4. Нужно ли сбросить системный кеш на linux перед запуском скрипта? (возможно, достаточно отключить np.random.seed)?

Пример кода:

import os
import time

import numpy as np
import tensorflow as tf

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '1'
tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR)

np.random.seed(2020)


def conv_block(x, kernel_size=3):
    # Define some part of graph here

    bs, h, w, c = x.shape
    in_channels = c
    out_channels = c

    with tf.variable_scope('var_scope'):
        w_0 = tf.get_variable('w_0', [kernel_size, kernel_size, in_channels, out_channels], initializer=tf.contrib.layers.xavier_initializer())
        x = tf.nn.conv2d(x, w_0, [1, 1, 1, 1], 'SAME')

    return x


def get_data_batch(spatial_size, n_channels):
    bs = 1
    h = spatial_size
    w = spatial_size
    c = n_channels

    x_np = np.random.rand(bs, h, w, c)
    x_np = x_np.astype(np.float32)
    #print('x_np.shape', x_np.shape)

    return x_np


def run_graph_part(f_name, spatial_size, n_channels, n_iter=100):
    print('=' * 60)
    print(f_name.__name__)

    tf.reset_default_graph()
    with tf.Session() as sess:
        x_tf = tf.placeholder(tf.float32, [1, spatial_size, spatial_size, n_channels], name='input')
        z_tf = f_name(x_tf)
        sess.run(tf.global_variables_initializer())

        x_np = get_data_batch(spatial_size, n_channels)
        start_time = time.time()
        for _ in range(n_iter):
            z_np = sess.run(fetches=[z_tf], feed_dict={x_tf: x_np})[0]
        avr_time = (time.time() - start_time) / n_iter
        print('z_np.shape', z_np.shape)
        print('avr_time', round(avr_time, 3))

        n_total_params = 0
        for v in tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope='var_scope'):
            n_total_params += np.prod(v.get_shape().as_list())
        print('Number of parameters:', format(n_total_params, ',d'))


if __name__ == '__main__':
    run_graph_part(conv_block, spatial_size=128, n_channels=32, n_iter=100)

1 Ответ

2 голосов
/ 25 апреля 2020

Ответ на ваш основной вопрос: «Как правильно оценивать часть диаграммы tenorflow?»:

Tensorflow включает в себя абстрактный класс, который предоставляет помощники для тестов тензорного потока: Benchmark .

Таким образом, объект Benchmark может быть создан и использован для выполнения теста на части графа тензорного потока. В приведенном ниже коде создается эталонный объект, а затем вызывается метод run_op_benchmark. run_op_benchmark передается сеанс, Тензор conv_block (в данном случае), feed_dict, число итераций записи, желаемое минимальное количество итераций, логический флаг, чтобы тест производительности не влиял также на вычисление использования памяти и удобное имя. Метод возвращает словарь, содержащий результаты теста:

benchmark = tf.test.Benchmark()
results = benchmark.run_op_benchmark(sess=sess, op_or_tensor=z_tf, 
                                     feed_dict={x_tf: x_np}, burn_iters=2, 
                                     min_iters=n_iter, 
                                     store_memory_usage=False, name='example')

Этот блок кода может быть вставлен в ваш код следующим образом для сравнения двух тестов:

import os
import time

import numpy as np
import tensorflow as tf

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '1'
tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR)

np.random.seed(2020)


def conv_block(x, kernel_size=3):
    # Define some part of graph here

    bs, h, w, c = x.shape
    in_channels = c
    out_channels = c

    with tf.compat.v1.variable_scope('var_scope'):
        w_0 = tf.get_variable('w_0', [kernel_size, kernel_size, in_channels, out_channels], initializer=tf.keras.initializers.glorot_normal())
        x = tf.nn.conv2d(x, w_0, [1, 1, 1, 1], 'SAME')

    return x


def get_data_batch(spatial_size, n_channels):
    bs = 1
    h = spatial_size
    w = spatial_size
    c = n_channels

    x_np = np.random.rand(bs, h, w, c)
    x_np = x_np.astype(np.float32)
    #print('x_np.shape', x_np.shape)

    return x_np


def run_graph_part(f_name, spatial_size, n_channels, n_iter=100):
    print('=' * 60)
    print(f_name.__name__)

    tf.reset_default_graph()
    with tf.Session() as sess:
        x_tf = tf.placeholder(tf.float32, [1, spatial_size, spatial_size, n_channels], name='input')
        z_tf = f_name(x_tf)
        sess.run(tf.global_variables_initializer())

        x_np = get_data_batch(spatial_size, n_channels)
        start_time = time.time()
        for _ in range(n_iter):
            z_np = sess.run(fetches=[z_tf], feed_dict={x_tf: x_np})[0]
        avr_time = (time.time() - start_time) / n_iter
        print('z_np.shape', z_np.shape)
        print('avr_time', round(avr_time, 3))

        n_total_params = 0
        for v in tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope='var_scope'):
            n_total_params += np.prod(v.get_shape().as_list())
        print('Number of parameters:', format(n_total_params, ',d'))

        # USING TENSORFLOW BENCHMARK
        benchmark = tf.test.Benchmark()
        results = benchmark.run_op_benchmark(sess=sess, op_or_tensor=z_tf, 
                                             feed_dict={x_tf: x_np}, burn_iters=2, min_iters=n_iter,
                                             store_memory_usage=False, name='example')

        return results


if __name__ == '__main__':
    results = run_graph_part(conv_block, spatial_size=128, n_channels=32, n_iter=100)

Эта реализация Класс бенчмаркинга в самой библиотеке tenorflow предоставляет подсказки относительно ответов на другие ваши вопросы. Поскольку реализация тензорного потока не требует использования нового feed_dict для каждой итерации теста, может показаться, что ответ на вопрос 1) «Хорошо, что x_np, используемый в l oop, такой же, или мне нужно восстановить его каждый раз? является то, что можно использовать один и тот же x_np каждый л oop. Что касается вопроса 2), похоже, что необходим некоторый «разогрев». Число итераций записи по умолчанию, предложенное реализацией библиотеки tenorflow, равно 2. Что касается вопроса 3), timeit является отличным инструментом для измерения времени выполнения небольших фрагментов кода. Однако сама библиотека tenorflow использует time.time() аналогично тому, что вы сделали: run_op_benchmark (источник) . Интересно, что реализация эталонного теста тензорного потока сообщает о медиане, а не о среднем времени выполнения операции (предположительно, чтобы сделать эталонный тест более устойчивым к выбросам).

...