我想基于时间步长而非周期来调整学习率,这与大多数调度器不同。我有一个模型如下:
import tensorflow as tffrom tensorflow.keras.models import Modelfrom tensorflow.keras import layersfrom tensorflow.keras.optimizers import Adamclass DQNagent: def __init__(self, state_size, action_size): self.state_size = state_size self.action_size = action_size self.model = self.build_model() # 原模型 self.target_model = self.build_model() # 目标模型 self.lr = 1e-2 def build_model(self): x_in = layers.Input(shape=(self.step_size, self.state_size)) x_out = layers.Dense(20, activation='relu')(x_in) output = layers.Dense(self.action_size, activation='linear')(x_out) self.learning_rate = CustomSchedule() opt = tf.keras.optimizers.Adam(self.learning_rate) model = Model(inputs=x_in, outputs=output_y, name="DQN") model.compile(loss=['mse'], optimizer=opt) return model
我想创建一个类似这样的调度器:
class CustomSchedule: def __init__(self, lr=1e-2): super(CustomSchedule, self).__init__() self.lr = lr self.t = 0 def __call__(self): self.t +=1 if self.t % 100 ==0: self.lr /= 10 return self.lr
我的主要代码没有声明所有内容,大致如下:
dqn = DQNagent(state_size, action_size)for step in range(1000): states_all = np.array([[[0, 0, 1],[1,0,1], [0, -1, 1], [1,-1,1]]]) Q_values = dqn.model.predict(state_all)[0] # 训练 batch = memory.sample(batch_size) batch_states = utils.get_states_user(batch) # 假设我使用此方法生成了状态 Q_states = dqn.model.predict(batch_states) # 假设我已经抽样了批次状态 dqn.model.fit(batch_states, Q_states, verbose =0)
我想调整我的学习率,使得当step%100==0
时,学习率减少为learning_rate/10
。看起来对于我创建的CustomSchedule
类,我需要重新编译model
,这似乎不太有效,因为需要保存和加载权重。有什么其他方法可以实现吗?
编辑:
我根据@FedericoMalerba的回答编辑了我的代码
创建了一个decay_func
如下:
def decay_func(step, lr): return lr/10**(step/100)
然后我对我的DQNAgent
类做了以下更改:
class DQNAgent(): def __init__(self, state_size, action_size): self.lr = 1e-2 self.t_step = tf.Variable(0, trainable=False, name='Step', dtype=tf.int64) self.decaying_lr = partial(decay_func, step=self.step, lr=self.lr) def __call__(self): self.step.assign_add(1) return self.step
并在我的主要代码中每一步调用dqn()
。可调用的decaying_lr
在build_model()
中传递给优化器,如opt = tf.keras.optimizers.Adam(self.decaying_lr)
回答:
一般来说,可以通过创建一个不带参数的可调用对象(函数)并将其传递给在DQNagent.build_model()
中定义的Adam优化器来处理这个问题。具体步骤如下:
- 创建你的epsilon衰减函数:
def decay_func(step_tensor, **other_arguments_your_func_needs): # 函数体。必须使用输入参数step tensor来确定要返回的学习率 return learning_rate
- 创建你的步长张量(必须是张量!!!):
step = tf.Variable(0, trainable=False, name='Step', dtype=tf.int64)
- 创建要传递给优化器的可调用对象:
from functools import partialdecaying_learning_rate = partial(decay_func, step_tensor=step, **other_arguments_your_func_needs)
- 在创建优化器时传递可调用对象:
opt = tf.keras.optimizers.Adam(decaying_learning_rate)
- 在训练循环中通过增加你的步长张量来迭代:
step.assing_add(1)
你实际上是在创建一个不带参数的可调用对象decaying_learning_rate
,因为所有参数都通过functools.partial
调用传递给了它。tensorflow优化器会发现学习率不是一个数字,而是一个可调用对象,并像这样调用它:
this_step_learning_rate = decaying_learning_rate()
由于张量是运行时共享的对象,当你使用step.assing_add(1)
增加你的步长计数器时,这个新的步长将在优化器的下一次调用中用于计算你的decay_func
中的新学习率。尽管你没有明确传递新的和更新的张量,但这仍然会发生。真是神奇!
顺便说一下,这正是指数衰减所做的。我在这里展示的只是一种通用的方法来定义你自己的decay_func
,并让它像TF预实现的指数衰减一样工作。