如何按照全局步骤在 Keras 中实现指数衰减学习率
How to implement exponentially decay learning rate in Keras by following the global steps
看下面的例子
# 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
的每次迭代中。因此学习率只是常数(1.0,根据 step_decay
)。事实上,我不是直接设置 epoch
>1,而是像示例中那样做外层循环,而在每个循环的内部,我只是 运行 1 个纪元。 (当我实施深度强化学习而不是监督学习时就是这种情况)。
我的问题是如何在我的示例中设置指数衰减学习率以及如何在 ep
.
的每次迭代中获得学习率
您实际上可以将 两个参数 传递给 LearningRateScheduler
。
根据Keras documentation,调度器是
a function that takes an epoch index as input (integer, indexed from
0) and current learning rate and returns a new learning rate as output
(float).
所以,基本上,只需将 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()
混合使用,只需提供一个不同的 epochs
参数在你的 fit()
函数中。在这种情况下,您有以下选择:
首先,keras
通过预定义的优化器提供衰减功能本身。例如在你的情况下 Adam()
actual code is:
lr = lr * (1. / (1. + self.decay * K.cast(self.iterations, K.dtype(self.decay))))
这也不完全是指数级的,它与 tensorflow's one 在某种程度上有所不同。此外,它仅在 decay > 0.0
时使用,因为它很明显。
要遵循指数衰减的张量流约定,您应该实现:
decayed_learning_rate = learning_rate * ^ (global_step / decay_steps)
根据您的需要,您可以选择实现 Callback
subclass 并在其中定义一个函数(请参阅下面的第 3 个项目符号)或使用 LearningRateScheduler
即 actually exactly this with some checking:一个Callback
子class,它在每个epoch结束时更新学习率。
- 如果你想更好地处理你的学习率策略(例如每批次),你必须实施你的 subclass 因为据我所知没有实施 subclass为了这个任务。好的部分是它超级简单:
创建子class
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
最后,在class里面添加实际的函数:
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
真正酷的部分是,如果您希望上面的子 class 更新每个纪元,您可以使用 on_epoch_begin(self, epoch, logs=None)
,它很好地将纪元作为其签名的参数。这种情况甚至更容易,因为您可以完全跳过全局步骤(除非您想要一种更奇特的方式来应用衰减,否则现在不需要跟踪它)并在其位置使用 epoch
。
看下面的例子
# 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
的每次迭代中。因此学习率只是常数(1.0,根据 step_decay
)。事实上,我不是直接设置 epoch
>1,而是像示例中那样做外层循环,而在每个循环的内部,我只是 运行 1 个纪元。 (当我实施深度强化学习而不是监督学习时就是这种情况)。
我的问题是如何在我的示例中设置指数衰减学习率以及如何在 ep
.
您实际上可以将 两个参数 传递给 LearningRateScheduler
。
根据Keras documentation,调度器是
a function that takes an epoch index as input (integer, indexed from 0) and current learning rate and returns a new learning rate as output (float).
所以,基本上,只需将 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()
混合使用,只需提供一个不同的 epochs
参数在你的 fit()
函数中。在这种情况下,您有以下选择:
首先,
keras
通过预定义的优化器提供衰减功能本身。例如在你的情况下Adam()
actual code is:lr = lr * (1. / (1. + self.decay * K.cast(self.iterations, K.dtype(self.decay))))
这也不完全是指数级的,它与 tensorflow's one 在某种程度上有所不同。此外,它仅在 decay > 0.0
时使用,因为它很明显。
要遵循指数衰减的张量流约定,您应该实现:
decayed_learning_rate = learning_rate * ^ (global_step / decay_steps)
根据您的需要,您可以选择实现 Callback
subclass 并在其中定义一个函数(请参阅下面的第 3 个项目符号)或使用 LearningRateScheduler
即 actually exactly this with some checking:一个Callback
子class,它在每个epoch结束时更新学习率。
- 如果你想更好地处理你的学习率策略(例如每批次),你必须实施你的 subclass 因为据我所知没有实施 subclass为了这个任务。好的部分是它超级简单:
创建子class
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
最后,在class里面添加实际的函数:
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
真正酷的部分是,如果您希望上面的子 class 更新每个纪元,您可以使用 on_epoch_begin(self, epoch, logs=None)
,它很好地将纪元作为其签名的参数。这种情况甚至更容易,因为您可以完全跳过全局步骤(除非您想要一种更奇特的方式来应用衰减,否则现在不需要跟踪它)并在其位置使用 epoch
。