如何按照全局步骤在 Keras 中实现指数衰减学习率



请看下面的例子

# encoding: utf-8
import numpy as np
import pandas as pd
import random
import math
from keras import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import Adam, RMSprop
from keras.callbacks import LearningRateScheduler
X = [i*0.05 for i in range(100)]
def step_decay(epoch):
initial_lrate = 1.0
drop = 0.5
epochs_drop = 2.0
lrate = initial_lrate * math.pow(drop, 
math.floor((1+epoch)/epochs_drop))
return lrate
def build_model():
model = Sequential()
model.add(Dense(32, input_shape=(1,), activation='relu'))
model.add(Dense(1, activation='linear'))
adam = Adam(lr=0.5)
model.compile(loss='mse', optimizer=adam)
return model
model = build_model()
lrate = LearningRateScheduler(step_decay)
callback_list = [lrate]
for ep in range(20):
X_train = np.array(random.sample(X, 10))
y_train = np.sin(X_train)
X_train = np.reshape(X_train, (-1,1))
y_train = np.reshape(y_train, (-1,1))
model.fit(X_train, y_train, batch_size=2, callbacks=callback_list, 
epochs=1, verbose=2)

在这个例子中,LearningRateSchedule根本不改变学习率,因为在ep的每次迭代中,epoch=1.因此,学习率只是常量(根据step_decay为 1.0(。实际上,我不是直接设置epoch>1,而是必须按照示例中所示进行外部循环,并且每个循环内部,我只需运行1个epoch。(当我实现深度强化学习而不是监督学习时,就是这种情况(。

我的问题是如何在我的例子中设置指数衰减学习率以及如何在ep的每次迭代中获得学习率。

您实际上可以将两个参数传递给LearningRateScheduler。 根据 Keras 文档,调度程序是

将纪元索引作为输入的函数(整数,索引自 0( 和当前学习率,并返回新的学习率作为输出 (浮动(。

因此,基本上,只需将initial_lr替换为函数参数,如下所示:

def step_decay(epoch, lr):
# initial_lrate = 1.0 # no longer needed
drop = 0.5
epochs_drop = 2.0
lrate = lr * math.pow(drop,math.floor((1+epoch)/epochs_drop))
return lrate

你实现的实际函数不是指数衰减(正如你在标题中提到的(,而是一个阶梯函数

另外,你提到你的学习率在你的循环内没有改变。这是真的,因为您同时设置了model.fit(..., epochs=1,...)epochs_drop = 2.0。我不确定这是否是您想要的情况。您正在提供一个玩具示例,在这种情况下并不清楚。

我想添加更常见的情况,即您不会将 for 循环与fit()混合,而只是在fit()函数中提供不同的epochs参数。在这种情况下,您有以下选项:

  1. 首先,keras通过预定义的优化器本身提供了衰减功能。例如,在您的情况下Adam()实际代码是:

    lr = lr * (1./(1. + self.decay * K.cast(self.iterations, K.dtype(self.decay(

    (((

这也不是完全指数的,它与TensorFlow的有些不同。此外,它仅在decay > 0.0时使用,因为它很明显。

  1. 要遵循指数衰减的张量流约定,您应该实现:

    decayed_learning_rate = learning_rate * ^ (global_step/decay_steps(

根据您的需要,您可以选择实现一个Callback子类并在其中定义一个函数(请参阅下面的第三个项目符号(或使用LearningRateScheduler实际上正是经过一些检查:一个Callback子类,它在每个纪元结束时更新学习率。

  1. 如果你想更精细地处理你的学习率策略(例如每批(,你必须实现你的子类,因为据我所知,这个任务没有实现的子类。好的部分是它超级简单:

创建子类

class LearningRateExponentialDecay(Callback):

并添加__init__()函数,该函数将使用所有需要的参数初始化您的实例,并创建一个global_step变量来跟踪迭代(批处理(:

def __init__(self, init_learining_rate, decay_rate, decay_steps):
self.init_learining_rate = init_learining_rate
self.decay_rate = decay_rate
self.decay_steps = decay_steps
self.global_step = 0

最后,在类中添加实际函数:

def on_batch_begin(self, batch, logs=None):
actual_lr = float(K.get_value(self.model.optimizer.lr))
decayed_learning_rate = actual_lr * self.decay_rate ^ (self.global_step / self.decay_steps)
K.set_value(self.model.optimizer.lr, decayed_learning_rate)
self.global_step += 1

真正酷的部分是,如果您希望上面的子类更新您可以使用的每个纪元,on_epoch_begin(self, epoch, logs=None)它很好地将纪元作为其签名的参数。这种情况甚至更容易,因为您可以完全跳过全局步骤(现在无需跟踪它,除非您想要一种更花哨的方式来应用您的衰减(并在它的位置使用epoch

最新更新