Estoy tratando de recrear un ejemplo muy simple de Policy Gradient, desde su blog de origen Andrej Karpathy . En ese artículo, encontrará ejemplos con CartPole y Policy Gradient con una lista de peso y activación de Softmax. Aquí está mi ejemplo recreado y muy simple del gradiente de políticas de CartPole, que funciona perfectamente .
import gym
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
import copy
NUM_EPISODES = 4000
LEARNING_RATE = 0.000025
GAMMA = 0.99
# noinspection PyMethodMayBeStatic
class Agent:
def __init__(self):
self.poly = PolynomialFeatures(1)
self.w = np.random.rand(5, 2)
def policy(self, state):
z = state.dot(self.w)
exp = np.exp(z)
return exp/np.sum(exp)
def __softmax_grad(self, softmax):
s = softmax.reshape(-1,1)
return np.diagflat(s) - np.dot(s, s.T)
def grad(self, probs, action, state):
dsoftmax = self.__softmax_grad(probs)[action,:]
dlog = dsoftmax / probs[0,action]
grad = state.T.dot(dlog[None,:])
return grad
def update_with(self, grads, rewards):
for i in range(len(grads)):
# Loop through everything that happend in the episode
# and update towards the log policy gradient times **FUTURE** reward
total_grad_effect = 0
for t, r in enumerate(rewards[i:]):
total_grad_effect += r * (GAMMA ** r)
self.w += LEARNING_RATE * grads[i] * total_grad_effect
print("Grads update: " + str(np.sum(grads[i])))
def main(argv):
env = gym.make('CartPole-v0')
np.random.seed(1)
agent = Agent()
complete_scores = []
for e in range(NUM_EPISODES):
state = env.reset()[None, :]
state = agent.poly.fit_transform(state)
rewards = []
grads = []
score = 0
while True:
probs = agent.policy(state)
action_space = env.action_space.n
action = np.random.choice(action_space, p=probs[0])
next_state, reward, done,_ = env.step(action)
next_state = next_state[None,:]
next_state = agent.poly.fit_transform(next_state.reshape(1, 4))
grad = agent.grad(probs, action, state)
grads.append(grad)
rewards.append(reward)
score += reward
state = next_state
if done:
break
agent.update_with(grads, rewards)
complete_scores.append(score)
env.close()
plt.plot(np.arange(NUM_EPISODES),
complete_scores)
plt.savefig('image1.png')
if __name__ == '__main__':
main(None)
.
.
Pregunta
Estoy tratando de hacer, casi el mismo ejemplo pero con la activación Sigmoid (solo por simplicidad). Eso es todo lo que necesito hacer. Cambie la activación en el modelo de softmax
a sigmoid
. Lo que debería funcionar con seguridad (según la explicación a continuación). Pero mi modelo Policy Gradient no aprende nada y se mantiene al azar. ¿Cualquier sugerencia?
import gym
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
NUM_EPISODES = 4000
LEARNING_RATE = 0.000025
GAMMA = 0.99
# noinspection PyMethodMayBeStatic
class Agent:
def __init__(self):
self.poly = PolynomialFeatures(1)
self.w = np.random.rand(5, 1) - 0.5
# Our policy that maps state to action parameterized by w
# noinspection PyShadowingNames
def policy(self, state):
z = np.sum(state.dot(self.w))
return self.sigmoid(z)
def sigmoid(self, x):
s = 1 / (1 + np.exp(-x))
return s
def sigmoid_grad(self, sig_x):
return sig_x * (1 - sig_x)
def grad(self, probs, action, state):
dsoftmax = self.sigmoid_grad(probs)
dlog = dsoftmax / probs
grad = state.T.dot(dlog)
grad = grad.reshape(5, 1)
return grad
def update_with(self, grads, rewards):
if len(grads) < 50:
return
for i in range(len(grads)):
# Loop through everything that happened in the episode
# and update towards the log policy gradient times **FUTURE** reward
total_grad_effect = 0
for t, r in enumerate(rewards[i:]):
total_grad_effect += r * (GAMMA ** r)
self.w += LEARNING_RATE * grads[i] * total_grad_effect
def main(argv):
env = gym.make('CartPole-v0')
np.random.seed(1)
agent = Agent()
complete_scores = []
for e in range(NUM_EPISODES):
state = env.reset()[None, :]
state = agent.poly.fit_transform(state)
rewards = []
grads = []
score = 0
while True:
probs = agent.policy(state)
action_space = env.action_space.n
action = np.random.choice(action_space, p=[1 - probs, probs])
next_state, reward, done, _ = env.step(action)
next_state = next_state[None, :]
next_state = agent.poly.fit_transform(next_state.reshape(1, 4))
grad = agent.grad(probs, action, state)
grads.append(grad)
rewards.append(reward)
score += reward
state = next_state
if done:
break
agent.update_with(grads, rewards)
complete_scores.append(score)
env.close()
plt.plot(np.arange(NUM_EPISODES),
complete_scores)
plt.savefig('image1.png')
if __name__ == '__main__':
main(None)
Trazar todo el aprendizaje se mantiene al azar. Nada ayuda con el ajuste de hiperparámetros. Debajo de la imagen de muestra.
referencias :
1) Aprendizaje de refuerzo profundo: Pong de píxeles
2) Una introducción a los gradientes de políticas con Cartpole y Doom
3) Derivar gradientes de políticas e implementar REINFORCE
4) Truco de aprendizaje automático del día (5): Log Derivative Trick 12
ACTUALIZAR
Parece que la respuesta a continuación podría hacer algo de trabajo desde el gráfico. Pero no es la probabilidad de registro y ni siquiera el gradiente de la política. Y cambia todo el propósito de la Política de gradiente de RL. Por favor, consulte las referencias anteriores. Siguiendo la imagen, la siguiente declaración.
Necesito tomar un gradiente de la función de registro de mi política (que es simplemente pesos y sigmoid
activación).
softmax
asignmoid
. Eso es solo una cosa que necesito hacer en el ejemplo anterior.[0, 1]
que puede interpretarse como probabilidad de acción positiva (por ejemplo, gire a la derecha en CartPole). Entonces la probabilidad de acción negativa (girar a la izquierda) es1 - sigmoid
. La suma de estas probabilidades es 1. Sí, este es un entorno de tarjeta de poste estándar.Respuestas:
El problema es con el
grad
método.En el código original se utilizó Softmax junto con la función de pérdida CrossEntropy. Cuando cambia la activación a Sigmoid, la función de pérdida adecuada se convierte en Binary CrossEntropy. Ahora, el propósito del
grad
método es calcular el gradiente de la función de pérdida wrt. pesas Ahorrando los detalles, el gradiente adecuado viene dado por(probs - action) * state
la terminología de su programa. Lo último es agregar un signo menos: queremos maximizar lo negativo de la función de pérdida.grad
Método apropiado así:Otro cambio que es posible que desee agregar es aumentar la tasa de aprendizaje.
LEARNING_RATE = 0.0001
yNUM_EPISODES = 5000
producirá la siguiente trama:La convergencia será mucho más rápida si los pesos se inicializan utilizando distribución gaussiana con media cero y varianza pequeña:
ACTUALIZAR
Código completo agregado para reproducir los resultados:
fuente
sigmoid
. Pero su gradiente en respuesta no debería tener nada que ver con mi gradiente. ¿Derecha?(action - probs) * sigmoid_grad(probs)
, pero omitísigmoid_grad
debido al problema de desaparición del gradiente sigmoide.action = 1
, queremosprobs
estar más cerca1
, aumentando los pesos (gradiente positivo). Siaction=0
, queremosprobs
estar más cerca de0
, por lo tanto, disminuir los pesos (gradiente negativo).(action - probs)
es solo otra forma de cambiar los mismos wights.