Поэтому я реализовал код, как вы предложили. Это не сходится на Cartpole, что является неожиданным. Хуже того, если я заставлю актера ничего не изучать, просто критик даже не сойдет с правильной функцией значения статической политики. Я вроде не в курсе, как это отладить ... Я добавил полный код как редактирование к исходному вопросу
у реализовать следующий алгоритм, взятый изэта книга, раздел 13.6:
Я не понимаю, как реализовать правило обновления в pytorch (правило для w довольно похоже на правило тета).
Насколько я знаю, факел требует потериloss.backwward()
.
Эта форма, по-видимому, не относится к указанному алгоритму.
Я все еще уверен, что есть правильный способ реализации таких правил обновления в pytorch.
Был бы очень признателен за фрагмент кода того, как должны обновляться веса w, учитывая, что V (s, w) является выходом нейронной сети, параметризованной w.
РЕДАКТИРОВАТЬ: Крис Холланд предложил способ реализации, и я реализовал его. Это не сходится на Cartpole, и мне интересно, если я сделал что-то не так.
Критик сходится к решению функцииgamma*f(n)=f(n)-1
которая оказывается суммой рядаgamma+gamma^2+...+gamma^inf
значение гамма = 1 расходится. гамма = 0,99 сходится на 100, гамма = 0,5 сходится на 2 и так далее. Независимо от актера или политики.
Код:
def _update_grads_with_eligibility(self, is_critic, delta, discount, ep_t):
gamma = self.args.gamma
if is_critic:
params = list(self.critic_nn.parameters())
lamb = self.critic_lambda
eligibilities = self.critic_eligibilities
else:
params = list(self.actor_nn.parameters())
lamb = self.actor_lambda
eligibilities = self.actor_eligibilities
is_episode_just_started = (ep_t == 0)
if is_episode_just_started:
eligibilities.clear()
for i, p in enumerate(params):
if not p.requires_grad:
continue
eligibilities.append(torch.zeros_like(p.grad, requires_grad=False))
# eligibility traces
for i, p in enumerate(params):
if not p.requires_grad:
continue
eligibilities[i][:] = (gamma * lamb * eligibilities[i]) + (discount * p.grad)
p.grad[:] = delta.squeeze() * eligibilities[i]
а также
expected_reward_from_t = self.critic_nn(s_t)
probs_t = self.actor_nn(s_t)
expected_reward_from_t1 = torch.tensor([[0]], dtype=torch.float)
if s_t1 is not None: # s_t is not a terminal state, s_t1 exists.
expected_reward_from_t1 = self.critic_nn(s_t1)
delta = r_t + gamma * expected_reward_from_t1.data - expected_reward_from_t.data
negative_expected_reward_from_t = -expected_reward_from_t
self.critic_optimizer.zero_grad()
negative_expected_reward_from_t.backward()
self._update_grads_with_eligibility(is_critic=True,
delta=delta,
discount=discount,
ep_t=ep_t)
self.critic_optimizer.step()
РЕДАКТИРОВАТЬ 2: Решение Криса Холланда работает. Проблема возникла из-за ошибки в моем коде, которая вызвала строку
if s_t1 is not None:
expected_reward_from_t1 = self.critic_nn(s_t1)
чтобы всегда звонить, таким образомexpected_reward_from_t1
никогда не был равен нулю, и, следовательно, для рекурсии уравнения Беллмана не было указано условие остановки.
Без поощрения инженеров,gamma=1
, lambda=0.6
и единый скрытый слой размером 128 для актера и критика, это сходилось на довольно стабильной оптимальной политике в течение 500 эпизодов.
Еще быстрее сgamma=0.99
, как показывает график (лучшая скидка за эпизод составляет около 86,6).
Большое спасибо @Chris Holland, который "дал это попробовать"