Использование stop_gradient с AdamOptimizer в TensorFlow

1

Я пытаюсь реализовать схему обучения/финализации, когда в каждой итерации backpropagation определенный набор параметров остается фиксированным. Я хочу иметь возможность изменять набор обновлений или фиксированных параметров с итерации на итерацию. Метод tf.stop_gradient, который, по-видимому, заставляет градиенты некоторых параметров оставаться нулевыми, очень полезен для этой цели и отлично работает с разными оптимизаторами, если набор обновлений или фиксированных параметров не изменяется от итераций к итерациям. Он также может обрабатывать различные установки обновления или фиксированные параметры, если он используется при стохастическом градиентном спуске. Моя проблема в том, что tf.stop_gradient не может обрабатывать такие случаи при использовании с оптимизатором Адама. Более конкретно, он сохраняет градиенты фиксированных параметров в ноль на выходе tf.compute_gradients, но при применении градиентов (tf.apply_gradients) значение фиксированных параметров изменяется. Я полагаю, это связано с тем, что шаг оптимизации в оптимизаторе Ада не равен нулю, даже если градиент равен нулю (на основе алгоритма 1 в бумаге Кингма и Ба). Существует ли дешевый способ замораживания переменной набора параметров в каждой итерации Адама без явного сохранения предыдущих значений итерации фиксированных параметров?

Подробнее:

Предположим, что у меня есть одноуровневая сеть с весовой матричной переменной W и матрицей-заполнителем бинарной маски MW которая определяет, какие элементы W должны обновляться на каждой итерации (значение 1 в). Вместо того, чтобы использовать W для записи отношения ввода/вывода этого слоя, я изменяю его как ниже

masked_W = MW*W + tf.stop_gradient(tf.abs(1-MW)*W)

для маскировки некоторых элементов W из ненулевых градиентов. Затем я использую masked_W для формирования вывода слоя, и, следовательно, потеря сети зависит от этой маскированной переменной. Дело в том, что MW меняется на каждой итерации. Пусть W - вектор из 4 элементов, инициализированных всем нулевым вектором. Вот что происходит:

opt=tf.AdamOptimizer(1e-5)
sess.run(tf.global_variables_initializer())
grads_vars=opt.compute_gradients(loss, W)

# initial value of W=[0,0,0,0]

# first iteration:
MW_val = [0,1,1,0]
feed_dict={MW:MW_val, x: batch_of_data, y_:batch_of_labels}
sess.run(opt.apply_gradients(grads_vars), feed_dict=feed_dict))
# gradient of  W=[0,xx,xx,0]
# new value of W=[0,a,b,0]

где xx - некоторые ненулевые значения градиента, а a и b - новые значения обновляющих элементов W На второй итерации мы изменим значение, присвоенное матричной матричной маске MW на [1,0,0,1], поэтому мы ожидаем иметь фиксированные значения для W[1] и W[2] и обновить значения для W[0] и W[3]. Но вот что происходит:

# second iteration
MW_val = [1,0,0,1]
feed_dict={MW:MW_val, x: batch_of_data, y_:batch_of_labels}
sess.run(opt.apply_gradients(grads_vars), feed_dict=feed_dict))
# gradient of  W=[xx,0,0,xx]
# new value of W=[c,aa,bb,d]

То есть, хотя градиенты W[1] и W[2] равны нулю, они получают новые значения (aa != a bb != b и bb != b). При изменении оптимизатора от Адама до SGD значения фиксированных параметров остаются такими же, как ожидалось.

Теги:
tensorflow
machine-learning
backpropagation

2 ответа

0
Лучший ответ

Я нашел решение своего вопроса и поделился им здесь, если другие найдут это полезным. После первой итерации моменты тех параметров, которые были обновлены на первой итерации, уже отличны от нуля. Поэтому, даже если на второй итерации положить их градиенты на ноль, они будут обновляться из-за их ненулевых тензоров импульса. Чтобы предотвратить обновление, tf.stop_gradient использовать только tf.stop_gradient, мы также должны удалить их импульс. В случае оптимизатора Адама это можно сделать с помощью метода get_slot оптимизатора: opt.get_slot(par, 'm') и opt.get_slot(par,'v'), где первые и последние предоставляют доступ к первому и вторых импульсов импульса параметра par, соответственно. В примере вопроса мы должны добавить следующие строки, чтобы заморозить W[1] и W[2] на второй итерации:

# momentums of W in the first iteration
m_vals = opt.get_slot(W, 'm')
v_vals = opt.get_slot(W, 'v')
# mask them before running the second iteration
masked_m_vals[[1,2]]=0
masked_v_vals[[1,2]]=0
sess.run(opt.get_slot(W, 'm').assign(masked_m_vals))
sess.run(opt.get_slot(W, 'v').assign(masked_v_vals))

Лучше сохранить замаскированные импульсы в примере выше m_vals[[1,2]] и v_vals[[1,2]], так что если на третьей итерации мы ослаблим фиксирующее ограничение W[1] и W[2], мы можем восстановить их импульсы до их первоначальных значений на первой итерации.

0

В качестве альтернативы вы можете передавать разные подмножества переменных apply_gradients, когда вы хотите обновлять разные подмножества переменных.

  • 0
    Спасибо за Ваш ответ. Проблема с этой идеей заключается в том, что когда вы используете opt.compute_gradients вы должны либо включать, либо исключать целую переменную, например, вы не можете написать opt.compute_gradients(loss, W[1:3]) потому что TF жалуется на неподдерживаемый тип ( который, я думаю, происходит от подачи полосатого ломтика). Есть ли какой-нибудь дешевый метод, который может разложить огромные тензоры параметров на определенные части (что-то вроде индексации тензора с произвольными наборами индексов) и иметь tf.apply_gradients для работы только с этими частями?
  • 0
    Нет, в тензорном потоке нет способа применять обновления только к подмножествам тензоров. Вместо этого вам нужно построить свою модель так, чтобы W (тензор, а не переменная) представлял собой конкатенацию двух переменных, которые затем можно обновлять независимо.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню