Как установить адаптивную скорость обучения для GradientDescentOptimizer?

104

Я использую TensorFlow для обучения нейронной сети. Вот как я инициализирую GradientDescentOptimizer:

init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)

mse        = tf.reduce_mean(tf.square(out - out_))
train_step = tf.train.GradientDescentOptimizer(0.3).minimize(mse)

Дело в том, что я не знаю, как установить правило обновления для скорости обучения или значение спада для этого.

Как я могу использовать здесь адаптивную скорость обучения?

отображаемое имя
источник
3
Это хорошая привычка инициализировать все переменные после того, как вы укажете свой оптимизатор, потому что некоторые оптимизаторы, такие как AdamOptimizer, используют свои собственные переменные, которые также необходимо инициализировать. В противном случае вы можете получить следующее сообщение об ошибке:FailedPreconditionError (see above for traceback): Attempting to use uninitialized value beta2_power
JYun
Я получаю эту вышеупомянутую ошибку, когда пытаюсь установить новую скорость обучения в Tensorflow с помощью tf.train.GradientDescentOptimizer(new_lr).minimize(loss). Похоже, что установка новой скорости обучения требует инициализации модели с уже обученными переменными. Но не могу понять, как это сделать.
Siladittya

Ответы:

193

Прежде всего, tf.train.GradientDescentOptimizerон предназначен для использования постоянной скорости обучения для всех переменных на всех этапах. TensorFlow также предоставляет готовые адаптивные оптимизаторы, в том числе tf.train.AdagradOptimizerи tf.train.AdamOptimizer, и их можно использовать в качестве замены.

Однако, если вы хотите контролировать скорость обучения с помощью ванильного градиентного спуска, вы можете воспользоваться тем фактом, что learning_rateаргументом tf.train.GradientDescentOptimizerконструктора может быть Tensorобъект. Это позволяет вам вычислять разные значения скорости обучения на каждом этапе, например:

learning_rate = tf.placeholder(tf.float32, shape=[])
# ...
train_step = tf.train.GradientDescentOptimizer(
    learning_rate=learning_rate).minimize(mse)

sess = tf.Session()

# Feed different values for learning rate to each training step.
sess.run(train_step, feed_dict={learning_rate: 0.1})
sess.run(train_step, feed_dict={learning_rate: 0.1})
sess.run(train_step, feed_dict={learning_rate: 0.01})
sess.run(train_step, feed_dict={learning_rate: 0.01})

В качестве альтернативы вы можете создать скаляр tf.Variable, содержащий скорость обучения, и назначать его каждый раз, когда вы хотите изменить скорость обучения.

мрри
источник
Отличный ответ. Можно ли использовать ту же технику для обрезки градиента? tf.clip_by_normне принимает тензор для нормы клипа, так как насчет того [(tf.minimum(gv[0], ct), gv[1]) for gv in optimizer.compute_gradients(cost, vars)], чтобы сделать , где?ct = tf.placeholder('float32', shape=[])
richizy
Это должно сработать, да. (Хотя, глядя на tf.clip_by_norm, единственное, что мешает ему принимать тензор в качестве входных данных, так это constant_op.constant(1.0 / clip_norm). Замена этого выражения на math_ops.inv(clip_norm)заставит его работать с вводом-заполнителем (или любым другим тензором).)
mrry
@mrry Я сделал, как вы сказали, и некоторые из них скорость обучения намного медленнее. Это ожидается, пожалуйста?
tnq177
89

Tensorflow обеспечивает опы автоматически применять экспоненциальный распад с тензором скорости обучения: tf.train.exponential_decay. В качестве примера использования см. Эту строку в примере сверточной модели MNIST . Затем используйте предложение @ mrry выше, чтобы передать эту переменную в качестве параметра learning_rate выбранному оптимизатору.

Ключевой отрывок, на который стоит обратить внимание:

# Optimizer: set up a variable that's incremented once per batch and
# controls the learning rate decay.
batch = tf.Variable(0)

learning_rate = tf.train.exponential_decay(
  0.01,                # Base learning rate.
  batch * BATCH_SIZE,  # Current index into the dataset.
  train_size,          # Decay step.
  0.95,                # Decay rate.
  staircase=True)
# Use simple momentum for the optimization.
optimizer = tf.train.MomentumOptimizer(learning_rate,
                                     0.9).minimize(loss,
                                                   global_step=batch)

Обратите внимание на global_step=batchпараметр, который нужно минимизировать. Это говорит оптимизатору услужливо увеличивать параметр «партии» для вас каждый раз, когда он тренируется.

dga
источник
3
Как правило, переменная , которую называют batchназывается , global_stepи есть несколько функций , удобства, один для его создания tf.train.create_global_step()(который просто создает целое tf.Variableи добавляет его к tf.GraphKeys.GLOBAL_STEPколлекции) и tf.train.get_global_step().
Ленар Хойт
86

Алгоритм градиентного спуска использует постоянную скорость обучения, которую вы можете обеспечить во время инициализации . Вы можете пройти различные уровни обучения так, как показал Mrry.

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

Вот краткое объяснение, основанное на моем понимании:

  • Импульс помогает SGD ориентироваться в соответствующих направлениях и смягчает колебания в несущественном. Он просто добавляет часть направления предыдущего шага к текущему шагу. Этим достигается увеличение скорости при правильном направлении и смягчение колебаний в неправильных направлениях. Эта доля обычно находится в диапазоне (0, 1). Также имеет смысл использовать адаптивный импульс. В начале обучения большой импульс будет только препятствовать вашему прогрессу, поэтому имеет смысл использовать что-то вроде 0,01, а когда все высокие градиенты исчезнут, вы можете использовать более крупный импульс. Есть одна проблема с импульсом: когда мы очень близки к цели, наш импульс в большинстве случаев очень высок, и он не знает, что он должен замедляться. Это может привести к пропуску или колебаниям вокруг минимумов.
  • Ускоренный градиент нестерова преодолевает эту проблему за счет раннего замедления. В импульсе мы сначала вычисляем градиент, а затем совершаем прыжок в этом направлении, усиленный тем импульсом, который у нас был ранее. NAG делает то же самое, но в другом порядке: сначала мы делаем большой скачок на основе нашей сохраненной информации, а затем вычисляем градиент и делаем небольшую поправку. Это, казалось бы, несущественное изменение дает значительное практическое ускорение.
  • AdaGrad или адаптивный градиент позволяет адаптировать скорость обучения в зависимости от параметров. Он выполняет большие обновления для нечастых параметров и меньшие обновления для частых. Из-за этого он хорошо подходит для разреженных данных (NLP или распознавание изображений). Еще одно преимущество состоит в том, что он в основном избавляет от необходимости настраивать скорость обучения. У каждого параметра своя скорость обучения, и из-за особенностей алгоритма скорость обучения монотонно уменьшается. Это вызывает самую большую проблему: в какой-то момент скорость обучения настолько мала, что система перестает обучаться
  • AdaDelta решает проблему монотонного уменьшения скорости обучения в AdaGrad. В AdaGrad скорость обучения рассчитывалась примерно как деленная на сумму квадратных корней. На каждом этапе вы добавляете к сумме еще один квадратный корень, что приводит к постоянному уменьшению знаменателя. В AdaDelta вместо суммирования всех прошлых квадратных корней используется скользящее окно, которое позволяет уменьшать сумму. RMSprop очень похож на AdaDelta
  • Адам или адаптивный импульс - алгоритм, похожий на AdaDelta. Но помимо хранения скорости обучения для каждого из параметров, он также сохраняет изменения импульса для каждого из них отдельно.

    Через несколько визуализаций : введите описание изображения здесь введите описание изображения здесь

Сальвадор Дали
источник
2
Для сравнения различных оптимизаторов в TensorFlow взгляните на следующий блокнот ipython: github.com/vsmolyakov/experiments_with_python/blob/master/chp03/… for
Вадим Смоляков
Более продвинутые оптимизаторы не следует брать «взамен», а вдобавок см. Stats.stackexchange.com/questions/200063/…
Дима Литуев
@DimaLituiev можно ли использовать два оптимизатора одновременно? Если нет, то вы используете optimizer1 вместо optimizer2.
Сальвадор Дали
1
Я не об этом говорю, и вопрос не в этом. Вы предлагаете использовать расширенные оптимизаторы вместо адаптивной скорости обучения. Я говорю, что вы бы предпочли использовать расширенные оптимизаторы в дополнение к адаптивной скорости обучения
Дима Литуев
7

Из официальных документов tensorflow

global_step = tf.Variable(0, trainable=False)
starter_learning_rate = 0.1
learning_rate = tf.train.exponential_decay(starter_learning_rate, global_step,
                                       100000, 0.96, staircase=True)

# Passing global_step to minimize() will increment it at each step.
learning_step = (
tf.train.GradientDescentOptimizer(learning_rate)
.minimize(...my loss..., global_step=global_step))
Пракаш Ванапалли
источник
0

Если вы хотите установить определенные скорости обучения для интервалов эпох, например 0 < a < b < c < .... Затем вы можете определить скорость обучения как условный тензор, зависящий от глобального шага, и передать его как обычно оптимизатору.

Вы можете добиться этого с помощью набора вложенных tf.condоператоров, но проще построить тензор рекурсивно:

def make_learning_rate_tensor(reduction_steps, learning_rates, global_step):
    assert len(reduction_steps) + 1 == len(learning_rates)
    if len(reduction_steps) == 1:
        return tf.cond(
            global_step < reduction_steps[0],
            lambda: learning_rates[0],
            lambda: learning_rates[1]
        )
    else:
        return tf.cond(
            global_step < reduction_steps[0],
            lambda: learning_rates[0],
            lambda: make_learning_rate_tensor(
                reduction_steps[1:],
                learning_rates[1:],
                global_step,)
            )

Затем, чтобы использовать его, вам нужно знать, сколько шагов обучения есть в одной эпохе, чтобы мы могли использовать глобальный шаг для переключения в нужный момент и, наконец, определить эпохи и скорость обучения, которые вы хотите. Поэтому, если мне нужны скорости обучения в [0.1, 0.01, 0.001, 0.0001]течение интервалов эпох [0, 19], [20, 59], [60, 99], [100, \infty]соответственно, я бы сделал:

global_step = tf.train.get_or_create_global_step()
learning_rates = [0.1, 0.01, 0.001, 0.0001]
steps_per_epoch = 225
epochs_to_switch_at = [20, 60, 100]
epochs_to_switch_at = [x*steps_per_epoch for x in epochs_to_switch_at ]
learning_rate = make_learning_rate_tensor(epochs_to_switch_at , learning_rates, global_step)
Бен
источник