Как передать список градиентов или пар (града, имени переменной) в мою модель - PullRequest
0 голосов
/ 24 апреля 2018

Это связано с предыдущим вопросом: Как разделить одну партию на несколько вызовов для экономии памяти , а также Как подготовить большую модель с относительно большим размером пакета на одной GPU с использованием Tensorflow? ; но все же я не мог найти точный ответ. Например, ответ на другой связанный вопрос tenensflow - запустить оптимизатор op для большой партии у меня не работает (кстати, он не был принят, и там больше нет комментариев).

Я хочу попробовать смоделировать пакет большего размера, но используя только один графический процессор. Итак, мне нужно вычислить градиенты для каждого меньшего пакета, агрегировать / усреднить их по нескольким таким меньшим пакетам и только затем применять.

(По сути, это похоже на синхронизированную распределенную SGD, но на одном устройстве / графическом процессоре, выполняемую последовательно. Конечно, преимущество ускорения распределенной SGD потеряно, но сам больший размер пакета может обеспечить конвергенцию с большей точностью и большим шагом. , как указано в нескольких недавних статьях.)

Чтобы поддерживать низкую потребность в памяти, я должен сделать стандартную SGD с небольшими пакетами, обновлять градиенты после каждой итерации и затем вызывать optimizer.apply_gradients() (где optimizer - один из реализованных оптимизаторов).

Итак, все выглядит просто, но когда я приступаю к его реализации, это на самом деле не так тривиально.

Например, я хотел бы использовать один Graph, вычислять градиенты для каждой итерации, а затем, когда обрабатывается несколько пакетов, суммировать градиенты и передавать их в мою модель. Но сам список не может быть передан в параметр feed_dict sess.run. Кроме того, передача градиентов напрямую не работает, я получаю TypeError: unhashable type: 'numpy.ndarray' (я думаю, причина в том, что я не могу передать numpy.ndarray, только переменную tenorflow). Я мог бы определить местозаполнитель для градиентов, но для этого мне сначала нужно будет построить модель (чтобы указать обучаемые переменные и т. Д.).

В общем, скажите, пожалуйста, есть более простой способ реализовать это.

Ответы [ 2 ]

0 голосов
/ 24 апреля 2018

Вам нужно будет указать градиенты как значения, которые передаются в apply_gradients. Это могут быть заполнители, но, вероятно, проще использовать обычную комбинацию compute_gradients / apply_gradients:

# Some loss measure
loss = ...
optimizer = ...
gradients = optimizer.compute_gradients(loss)
# gradients is a list of pairs
_, gradient_tensors = zip(*gradients)
# Apply gradients as usual
train_op = optimizer.apply_gradients(gradients)

# On training
# Compute some gradients
gradient_values = session.run(gradient_tensors, feed_dict={...})
# gradient_values is a sequence of numpy arrays with gradients

# After averaging multiple evaluations of gradient_values apply them
session.run(train_op, feed_dict=dict(zip(gradient_tensors, gradient_values_average)))

Если вы хотите также вычислить средние значения градиентов в TensorFlow, для этого требуется немного дополнительного кода специально для этого, возможно, что-то вроде этого:

# Some loss measure
loss = ...
optimizer = ...
gradients = optimizer.compute_gradients(loss)
# gradients is a list of pairs
_, gradient_tensors = zip(*gradients)
# Apply gradients as usual
train_op = optimizer.apply_gradients(gradients)

# Additional operations for gradient averaging
gradient_placeholders = [tf.placeholder(t.dtype, (None,) + t.shape)
                         for t in gradient_tensors]
gradient_averages = [tf.reduce_mean(p, axis=0) for p in gradient_placeholders]

# On training
gradient_values = None
# Compute some gradients
for ...:  # Repeat for each small batch
    gradient_values_current = session.run(gradient_tensors, feed_dict={...})
    if gradient_values is None:
        gradient_values = [[g] for g in gradient_values_current]
    else:
        for g_list, g in zip(gradient_values, gradient_values_current):
            g_list.append(g)
# Stack gradients
gradient_values = [np.stack(g_list) for g_list in gradient_values)
# Compute averages
gradient_values_average = session.run(
    gradient_averages, feed_dict=dict(zip(gradient_placeholders, gradient_values)))

# After averaging multiple gradients apply them
session.run(train_op, feed_dict=dict(zip(gradient_tensors, gradient_values_average)))
0 голосов
/ 24 апреля 2018

Нет более простого способа, чем то, что вам уже сказали. Поначалу этот способ может показаться сложным, но на самом деле он действительно прост. Вам просто нужно использовать API низкого уровня, чтобы вручную рассчитать градиенты для каждого пакета, усреднить по ним и затем вручную передать усредненные градиенты оптимизатору, чтобы применить их.

Я постараюсь предоставить несколько урезанных кодов, как это сделать. Я буду использовать точки в качестве заполнителей для реального кода, который будет зависеть от проблемы. То, что вы обычно делаете, будет примерно таким:

import tensorflow as tf
[...]
input = tf.placeholder(...)
[...]
loss = ...
[...]
# initialize the optimizer
optimizer = tf.train.AdamOptimizer(LEARNING_RATE)
# define operation to apply the gradients
minimize = optimizer.minimize(loss)
[...]
if __name__ == '__main__':
    session = tf.Session(config=CONFIG)
    session.run(tf.global_variables_initializer())
    for step in range(1, MAX_STEPS + 1):
        data = ...
        loss = session.run([minimize, loss],
                           feed_dict={input: data})[1]

То, что вы хотите сделать вместо этого сейчас, для усреднения по нескольким пакетам в память консерванта будет выглядеть так:

import tensorflow as tf
[...]
input = tf.placeholder(...)
[...]
loss = ...
[...]
# initialize the optimizer
optimizer = tf.train.AdamOptimizer(LEARNING_RATE)

# grab all trainable variables
trainable_variables = tf.trainable_variables()

# define variables to save the gradients in each batch
accumulated_gradients = [tf.Variable(tf.zeros_like(tv.initialized_value()),
                                     trainable=False) for tv in
                         trainable_variables]

# define operation to reset the accumulated gradients to zero
reset_gradients = [gradient.assign(tf.zeros_like(gradient)) for gradient in
                   accumulated_gradients]

# compute the gradients
gradients = optimizer.compute_gradients(loss, trainable_variables)

# Note: Gradients is a list of tuples containing the gradient and the
# corresponding variable so gradient[0] is the actual gradient. Also divide
# the gradients by BATCHES_PER_STEP so the learning rate still refers to
# steps not batches.

# define operation to evaluate a batch and accumulate the gradients
evaluate_batch = [
    accumulated_gradient.assign_add(gradient[0]/BATCHES_PER_STEP)
    for accumulated_gradient, gradient in zip(accumulated_gradients,
                                              gradients)]

# define operation to apply the gradients
apply_gradients = optimizer.apply_gradients([
    (accumulated_gradient, gradient[1]) for accumulated_gradient, gradient
    in zip(accumulated_gradients, gradients)])

# define variable and operations to track the average batch loss
average_loss = tf.Variable(0., trainable=False)
update_loss = average_loss.assign_add(loss/BATCHES_PER_STEP)
reset_loss = average_loss.assign(0.)
[...]
if __name__ == '__main__':
    session = tf.Session(config=CONFIG)
    session.run(tf.global_variables_initializer())

    data = [batch_data[i] for i in range(BATCHES_PER_STEP)]
    for batch_data in data:
        session.run([evaluate_batch, update_loss],
                    feed_dict={input: batch_data})

    # apply accumulated gradients
    session.run(apply_gradients)

    # get loss
    loss = session.run(average_loss)

    # reset variables for next step
    session.run([reset_gradients, reset_loss])

Это должно быть работоспособно, если вы заполните пробелы. Однако, возможно, я допустил ошибку, урезав ее и вставив сюда. Для работающего примера вы можете взглянуть на проект Я сейчас работаю над собой.

Я также хочу пояснить, что это не то же самое, что оценка потерь для всех пакетных данных одновременно, поскольку вы усредняете по градиентам. Это особенно важно, когда ваша потеря плохо работает с низкой статистикой. Возьмем, например, квадрат хи гистограмм, вычисление средних градиентов для квадрата хи гистограмм с низким количеством бинов не будет столь же хорошим, как вычисление градиента только для одной гистограммы, когда все бины заполнены одновременно.

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