Pytorch: ¿Cómo crear una regla de actualización que no provenga de derivados?

Quiero implementar el siguiente algoritmo, tomado de este libro, sección 13.6 :

introduzca la descripción de la imagen aquí

No entiendo cómo implementar la regla de actualización en pytorch (la regla para w es bastante similar a la de theta).

Por lo que sé, la antorcha requiere una pérdida por pérdida. loss.backwward() .

Esta forma no parece aplicar para el algoritmo entre comillas.

Todavía estoy seguro de que hay una forma correcta de implementar dichas reglas de actualización en pytorch.

Apreciaría enormemente un fragmento de código de cómo deberían actualizarse los pesos w, dado que V (s, w) es la salida de la neural network, parametrizada por w.


EDITAR: Chris Holland sugirió una manera de implementar, y lo implementé. No converge en Cartpole, y me pregunto si hice algo mal.

La crítica converge en la solución de la función gamma*f(n)=f(n)-1 que es la sum de la serie gamma+gamma^2+...+gamma^inf significado, gamma = 1 divergen gamma = 0.99 converge en 100, gamma = 0.5 converge en 2 y así sucesivamente. Independientemente del actor o la política.

El código:

 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] 

y

 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() 

EDIT 2: la solución de Chris Holland funciona. El problema se originó a partir de un error en mi código que causó la línea

 if s_t1 is not None: expected_reward_from_t1 = self.critic_nn(s_t1) 

para que siempre se llame, por lo tanto, expected_reward_from_t1 nunca fue cero, y por lo tanto no se especificó una condición de parada para la recursión de la ecuación de botones.

Sin una ingeniería de recompensa, gamma=1 , lambda=0.6 , y una única capa oculta de tamaño 128 para el actor y el crítico, esto convergió en una política óptima bastante estable en 500 episodios.

Incluso más rápido con gamma=0.99 , como muestra la gráfica (la recompensa por episodio con el mejor descuento es de aproximadamente 86.6).

Gracias

MUCHAS gracias a @Chris Holland, quien “lo probó”

Voy a intentarlo.

.backward() no necesita una función de pérdida, solo necesita una salida escalar diferenciable. Se aproxima un gradiente con respecto a los parámetros del modelo. Veamos el primer caso: la actualización de la función de valor.

Tenemos un gradiente que aparece para v, podemos aproximar este gradiente por

 v = model(s) v.backward() 

Esto nos da un gradiente de v que tiene la dimensión de los parámetros de su modelo. Suponiendo que ya calculamos las otras actualizaciones de parámetros, podemos calcular la actualización del optimizador real:

 for i, p in enumerate(model.parameters()): z_theta[i][:] = gamma * lamda * z_theta[i] + l * p.grad p.grad[:] = alpha * delta * z_theta[i] 

Luego podemos usar opt.step() para actualizar los parámetros del modelo con el gradiente ajustado.