1 Introducción al gradiente de políticas
1.1 Los métodos de aprendizaje por refuerzo basados en políticas y basados en valores son diferentes
El aprendizaje por refuerzo es un mecanismo para aprender el comportamiento correcto a través de recompensas y castigos. Hay muchos miembros diferentes en la familia, con recompensas y castigos de aprendizaje, y eligen comportamientos en función de lo que cree que son valores altos, como Q-Learning, Deep-Q-network, y también hay un método para generar comportamientos directamente sin análisis de recompensas y castigos Este es el gradiente de política del que vamos a hablar hoy agrega una red neuronal para generar acciones predichas. En comparación con los métodos basados en valores, la mayor ventaja de la salida directa de acciones de Policy Gradient es que puede seleccionar acciones en un intervalo continuo, mientras que los métodos basados en valores, como Q-Learning, si calcula el valor de un número infinito de acciones, así que elige comportarse, lo cual es demasiado para él.
1.2 Actualización del algoritmo
Ciertamente es conveniente tener una red neuronal, pero ¿cómo revertimos la transferencia de errores de la red neuronal? ¿Cuál es el error de Policy Gradient? La respuesta es ningún error. Pero en realidad está realizando una especie de transmisión hacia atrás. El propósito de este paso atrás es hacer que el comportamiento seleccionado tenga más probabilidades de ocurrir la próxima vez. Pero, ¿cómo determinamos si este comportamiento debería aumentar la probabilidad de ser seleccionado? En este momento, las recompensas y los castigos pueden ser útiles en este momento.
1.3 Pasos de actualización específicos
Ahora demuestre nuevamente, la información observada es analizada por la red neuronal, y se selecciona el comportamiento de la izquierda, y realizamos directamente la transmisión inversa, de modo que aumenta la posibilidad de ser seleccionado la próxima vez, pero la información de recompensa y castigo dice nos dice que este comportamiento no es correcto.De acuerdo, entonces el aumento de nuestras posibilidades de acción se reduce en consecuencia. De esta forma, las recompensas pueden usarse para controlar el paso hacia atrás de nuestra red neuronal. Tomemos otro ejemplo, si la información de observación esta vez hace que la red neuronal elija el comportamiento de la derecha, el comportamiento de la derecha se transmitirá a la inversa, de modo que el comportamiento de la derecha se seleccionará un poco más la próxima vez. esta vez, la información de recompensa y castigo también vendrá, dinos que este es un buen comportamiento, luego aumentaremos nuestros esfuerzos en este pase inverso, para que sea seleccionado más violentamente la próxima vez. Esta es la idea central de Policy Gradient.
2 Actualización del algoritmo de gradiente de políticas
La dirección de descarga gratuita de todos los códigos es la siguiente:
https://download.csdn.net/download/shoppingend/85194070
2.1 Puntos principales
Policy Gradient es una gran familia en RL, no es como el método Value-based (Q-Learning, Sarsa), pero también recibe información del entorno (observación), la diferencia es que no da salida al valor de la acción, pero la acción específica de That, por lo que Policy Gradient salta la etapa de valor. Y una de las mayores ventajas de Policy Gradient es que la salida de la acción puede ser un valor continuo. El método basado en valores que mencionamos antes genera valores discontinuos, y luego se selecciona la acción con el valor más grande. El gradiente de política puede seleccionar una acción en una distribución continua.
2.2 Algoritmo
El algoritmo de gradiente de política más simple que presentamos es una actualización basada en la ronda completa de datos, también llamada método REINFORCE. Este método es el método más básico de Policy Gradient. Con esta base, haremos otros más avanzados.
Δ(log(Política(s,a))*V representa el grado de sorpresa de la acción seleccionada a en el estado s, si la probabilidad de Política(s,a) es menor, logaritmo inverso(Política(s,a)) (Es decir, -log§) es mayor. Si obtiene una R grande cuando Policy(s,a) es pequeña, es decir, una V grande, entonces -Δ(log(Policy(s,a))* La V es más grande, lo que significa más sorprendido (Elegí una acción que no se selecciona a menudo, pero descubrí que puede obtener una buena recompensa, por lo que esta vez tengo que hacer una modificación importante en mis parámetros). Este es el significado físico de el asombro se ha ido.
2.3 Formación de código de algoritmo
Primero defina el bucle para la actualización principal:
import gym
from RL_brain import PolicyGradient
import matplotlib.pyplot as plt
RENDER = False # 在屏幕上显示模拟窗口会拖慢运行速度, 我们等计算机学得差不多了再显示模拟
DISPLAY_REWARD_THRESHOLD = 400 # 当 回合总 reward 大于 400 时显示模拟窗口
env = gym.make('CartPole-v0') # CartPole 这个模拟
env = env.unwrapped # 取消限制
env.seed(1) # 普通的 Policy gradient 方法, 使得回合的 variance 比较大, 所以我们选了一个好点的随机种子
print(env.action_space) # 显示可用 action
print(env.observation_space) # 显示可用 state 的 observation
print(env.observation_space.high) # 显示 observation 最高值
print(env.observation_space.low) # 显示 observation 最低值
# 定义
RL = PolicyGradient(
n_actions=env.action_space.n,
n_features=env.observation_space.shape[0],
learning_rate=0.02,
reward_decay=0.99, # gamma
# output_graph=True, # 输出 tensorboard 文件
)
bucle principal:
for i_episode in range(3000):
observation = env.reset()
while True:
if RENDER: env.render()
action = RL.choose_action(observation)
observation_, reward, done, info = env.step(action)
RL.store_transition(observation, action, reward) # 存储这一回合的 transition
if done:
ep_rs_sum = sum(RL.ep_rs)
if 'running_reward' not in globals():
running_reward = ep_rs_sum
else:
running_reward = running_reward * 0.99 + ep_rs_sum * 0.01
if running_reward > DISPLAY_REWARD_THRESHOLD: RENDER = True # 判断是否显示模拟
print("episode:", i_episode, " reward:", int(running_reward))
vt = RL.learn() # 学习, 输出 vt, 我们下节课讲这个 vt 的作用
if i_episode == 0:
plt.plot(vt) # plot 这个回合的 vt
plt.xlabel('episode steps')
plt.ylabel('normalized state-action value')
plt.show()
break
observation = observation_
3 Decisión de pensamiento de gradiente de política
3.1 Estructura del código principal
Usando el algoritmo de gradiente de política básico, se ve muy similar al algoritmo anterior basado en valores.
class PolicyGradient:
# 初始化 (有改变)
def __init__(self, n_actions, n_features, learning_rate=0.01, reward_decay=0.95, output_graph=False):
# 建立 policy gradient 神经网络 (有改变)
def _build_net(self):
# 选行为 (有改变)
def choose_action(self, observation):
# 存储回合 transition (有改变)
def store_transition(self, s, a, r):
# 学习更新参数 (有改变)
def learn(self, s, a, r, s_):
# 衰减回合的 reward (新内容)
def _discount_and_norm_rewards(self):
inicialización:
class PolicyGradient:
def __init__(self, n_actions, n_features, learning_rate=0.01, reward_decay=0.95, output_graph=False):
self.n_actions = n_actions
self.n_features = n_features
self.lr = learning_rate # 学习率
self.gamma = reward_decay # reward 递减率
self.ep_obs, self.ep_as, self.ep_rs = [], [], [] # 这是我们存储 回合信息的 list
self._build_net() # 建立 policy 神经网络
self.sess = tf.Session()
if output_graph: # 是否输出 tensorboard 文件
# $ tensorboard --logdir=logs
# http://0.0.0.0:6006/
# tf.train.SummaryWriter soon be deprecated, use following
tf.summary.FileWriter("logs/", self.sess.graph)
self.sess.run(tf.global_variables_initializer())
3.2 Establecer una red neuronal de políticas
La red neuronal que vamos a construir esta vez es así:
debido a que esto es aprendizaje por refuerzo, no hay una etiqueta y en el aprendizaje supervisado en la red neuronal. En su lugar, elegimos la acción.
class PolicyGradient:
def __init__(self, n_actions, n_features, learning_rate=0.01, reward_decay=0.95, output_graph=False):
...
def _build_net(self):
with tf.name_scope('inputs'):
self.tf_obs = tf.placeholder(tf.float32, [None, self.n_features], name="observations") # 接收 observation
self.tf_acts = tf.placeholder(tf.int32, [None, ], name="actions_num") # 接收我们在这个回合中选过的 actions
self.tf_vt = tf.placeholder(tf.float32, [None, ], name="actions_value") # 接收每个 state-action 所对应的 value (通过 reward 计算)
# fc1
layer = tf.layers.dense(
inputs=self.tf_obs,
units=10, # 输出个数
activation=tf.nn.tanh, # 激励函数
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
bias_initializer=tf.constant_initializer(0.1),
name='fc1'
)
# fc2
all_act = tf.layers.dense(
inputs=layer,
units=self.n_actions, # 输出个数
activation=None, # 之后再加 Softmax
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
bias_initializer=tf.constant_initializer(0.1),
name='fc2'
)
self.all_act_prob = tf.nn.softmax(all_act, name='act_prob') # 激励函数 softmax 出概率
with tf.name_scope('loss'):
# 最大化 总体 reward (log_p * R) 就是在最小化 -(log_p * R), 而 tf 的功能里只有最小化 loss
neg_log_prob = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act, labels=self.tf_acts) # 所选 action 的概率 -log 值
# 下面的方式是一样的:
# neg_log_prob = tf.reduce_sum(-tf.log(self.all_act_prob)*tf.one_hot(self.tf_acts, self.n_actions), axis=1)
loss = tf.reduce_mean(neg_log_prob * self.tf_vt) # (vt = 本reward + 衰减的未来reward) 引导参数的梯度下降
with tf.name_scope('train'):
self.train_op = tf.train.AdamOptimizer(self.lr).minimize(loss)
¿Por qué usar loss=-log(prob)*Vt como loss. En pocas palabras, arriba se mencionan dos tipos de líneas para calcular neg_log_prob.Estos dos tipos de líneas son exactamente iguales, excepto que la segunda es la expansión de la primera. Si observa detenidamente la primera fila, se trata de entropía cruzada en el problema de clasificación de redes neuronales. Los parámetros de la red neuronal se mejoran de acuerdo con esta verdad básica usando softmax. Obviamente, esto no es muy diferente de un problema de clasificación.Podemos entender este neg_log_prob como un error de clasificación de entropía cruzada. La etiqueta en el problema de clasificación es la y correspondiente a la x real, y en nuestro gradiente de política, x es el estado e y es el número de acción que realiza de acuerdo con esta x. Entonces también se puede entender que la acción que hace según x es siempre correcta (la acción que sale es siempre la etiqueta correcta, y siempre modificará sus parámetros según esta etiqueta correcta. Pero no es así, su las acciones no son necesariamente todas Es la etiqueta correcta, que es la diferencia entre Gradiente de política y clasificación
Para garantizar que esta acción sea la etiqueta correcta, nuestra pérdida se multiplica por Vt en la línea de entropía cruzada original, y Vt es se usa para decir a la cruz ¿Es el gradiente calculado por -entropía un gradiente confiable? Si Vt es pequeño o negativo, significa que el descenso del gradiente está en la dirección incorrecta, y debemos actualizar los parámetros en otra dirección. Si este Vt es positivo , o si es muy grande, Vt elogiará el gradiente de entropía cruzada, y el gradiente caerá en esta dirección. Hay una imagen a continuación, que es exactamente la idea explicada. Y por qué es loss=-log(prob
) Vt en lugar de loss= -prob Vt. La razón es que el prob aquí proviene de softmax, y el cálculo de todos los gradientes de parámetros en la red neuronal usa entropía cruzada, y luego multiplica este gradiente por Vt para controlar la dirección y la intensidad de descenso de gradiente.
3.3 Comportamiento de selección
Este comportamiento ya no se selecciona por valor Q, sino por probabilidad. Incluso sin epsilon-greedy, tiene un cierto grado de aleatoriedad.
class PolicyGradient:
def __init__(self, n_actions, n_features, learning_rate=0.01, reward_decay=0.95, output_graph=False):
...
def _build_net(self):
...
def choose_action(self, observation):
prob_weights = self.sess.run(self.all_act_prob, feed_dict={
self.tf_obs: observation[np.newaxis, :]}) # 所有 action 的概率
action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel()) # 根据概率来选 action
return action
3.4 Ronda de almacenamiento
Esta parte es para agregar la observación, la acción y la recompensa de este paso a la lista. Debido a que la lista debe borrarse después del final de esta ronda, y luego se almacenarán los datos para la próxima ronda, por lo que borraremos la lista en learn()
class PolicyGradient:
def __init__(self, n_actions, n_features, learning_rate=0.01, reward_decay=0.95, output_graph=False):
...
def _build_net(self):
...
def choose_action(self, observation):
...
def store_transition(self, s, a, r):
self.ep_obs.append(s)
self.ep_as.append(a)
self.ep_rs.append(r)
3.5 Aprendizaje
El método learn() en esta sección es muy simple. En primer lugar, tenemos que manipular todos los recursos de esta ronda para que sea más adecuado para el aprendizaje. La primera es usar γ para atenuar las recompensas futuras a medida que pasa el tiempo, y luego reducir la varianza de la ronda del gradiente de política hasta cierto punto.
class PolicyGradient:
def __init__(self, n_actions, n_features, learning_rate=0.01, reward_decay=0.95, output_graph=False):
...
def _build_net(self):
...
def choose_action(self, observation):
...
def store_transition(self, s, a, r):
...
def learn(self):
# 衰减, 并标准化这回合的 reward
discounted_ep_rs_norm = self._discount_and_norm_rewards() # 功能再面
# train on episode
self.sess.run(self.train_op, feed_dict={
self.tf_obs: np.vstack(self.ep_obs), # shape=[None, n_obs]
self.tf_acts: np.array(self.ep_as), # shape=[None, ]
self.tf_vt: discounted_ep_rs_norm, # shape=[None, ]
})
self.ep_obs, self.ep_as, self.ep_rs = [], [], [] # 清空回合 data
return discounted_ep_rs_norm # 返回这一回合的 state-action value
Mira de nuevo discounted_ep_rs_norm
vt = RL.learn() # 学习, 输出 vt, 我们下节课讲这个 vt 的作用
if i_episode == 0:
plt.plot(vt) # plot 这个回合的 vt
plt.xlabel('episode steps')
plt.ylabel('normalized state-action value')
plt.show()
Finalmente, cómo usar el algoritmo para realizar la atenuación de recompensas futuras
class PolicyGradient:
def __init__(self, n_actions, n_features, learning_rate=0.01, reward_decay=0.95, output_graph=False):
...
def _build_net(self):
...
def choose_action(self, observation):
...
def store_transition(self, s, a, r):
...
def learn(self):
...
def _discount_and_norm_rewards(self):
# discount episode rewards
discounted_ep_rs = np.zeros_like(self.ep_rs)
running_add = 0
for t in reversed(range(0, len(self.ep_rs))):
running_add = running_add * self.gamma + self.ep_rs[t]
discounted_ep_rs[t] = running_add
# normalize episode rewards
discounted_ep_rs -= np.mean(discounted_ep_rs)
discounted_ep_rs /= np.std(discounted_ep_rs)
return discounted_ep_rs
Fuente del artículo: Aprendizaje por refuerzo de Mofan https://mofanpy.com/tutorials/machine-learning/reinforcement-learning/