(八)强化学习——策略梯度,REINFORCEMENT算法,Actor-Critic

本文介绍了强化学习中的策略梯度方法,通过策略网络近似策略函数,与基于价值的学习相对,适用于动作连续的场景。此外,还讨论了Actor-Critic算法,其中策略网络作为演员,价值网络作为评委,用于连续动作的控制任务。
摘要由CSDN通过智能技术生成

1.前言
        我们上一节分享了DQN算法以及它的一些改进算法七)强化学习——DQN,DDQN,Dueling DQN,Rainbow DQN。其实最主要的核心就是我们用了一个神经网络Q(s,a,θ)来近似最优动作价值函数Q*(s,a)。一旦神经网络训练完成,我们只要输入一个状态,神经网络就能准确的输出在这个状态下各个动作的价值,我们就能选出Q值最大的那个动作执行,也就是完成了一次任务序列的一次最优决策。一个已经训练好了的Q网络其实就是一个特定的策略,只不过这里的策略是用Q值指导动作的选择,那我们能不能直接训练从状态到动作的网络。答案是肯定的。我们在(三)强化学习——策略函数π and 价值函数、贝尔曼方程中提到过策略函数π(a|s),表示在状态s下,通过策略π,直接选出动作a。我们用一个神经网络来近似它。
在这里插入图片描述

图 1 :用一个神经网络近似π函数

这个网络是不是很眼熟,和近似状态-动作Q函数的神经网络很像,只是输出不一样,这里的输出是每个动作对应的概率,而不是每个动作对应的Q值。p1+p2+…+pn=1。我们可以看见这其实是两种不同的学习方式。我们把前者称为基于价值的学习,后者称为基于策略的学习。基于价值的学习更适合于动作空间较小且离散的场景,如游戏等。基于策略的学习更适用于动作集合规模庞大、动作连续的场景(如机器人控制领域)。
2.策略梯度
        我们来回顾一下Qπ,这个其实就是动作价值函数,使用π策略,对当前状态st,执行动作a的一个好坏评价。从公式可以看出,它是通过未来奖励的期望回报高低来评价好坏的。

在这里插入图片描述

图2:Qπ

图3是Vπ(st)状态函数,这个是用来评判状态的好坏的。从公式可以看出它的计算方式是,在策略π下平均了经过状态St所有的期望值。
在这里插入图片描述

图3:Vπ

        仅从公式来看,我们很难深刻理解他们两者的关系。接下里我将尽可能的讲解。如图4所示,是3条完整的幕,幕就是智能体与环境的交互产生的状态、动作、奖励序列。在真正的强化学习任务中远比图中的要复杂。我们以状态st为例,状态st是3个幕的交点,就是在状态st这个时刻,有a1,a2,a3这3个动作可以选择。Qπ(st,a1)表示的就是在当前状态下执行动作a1的价值,就是幕1中st到终止状态的奖励的期望值。同理Qπ(st,a2),Qπ(st,a3)也是这样。Vπ(st)就是Qπ(st,a1),Qπ(st,a2),Qπ(st,a3)这3个对于动作a的一个期望。假设,这个π策略此时执行a1的概率是1/6,a2是1/6,a3是2/3。则Vπ(st)=1/6Qπ(st,a1)+1/6Qπ(st,a2)+2/3*Qπ(st,a3)。

在这里插入图片描述

图4:序列轨迹图

仔细一看,这个概率不就是策略网络的输出嘛,这样动作价值函数,状态价值函数,策略函数的关系我们就知道了。

在这里插入图片描述

大写的A表示随机变量。我们训练策略网络的目的是希望Vπ(s)越大越好,故对Vπ(s;θ)是做梯度上升。
在这里插入图片描述
对于离散的动作,我们可以这样表示,而对于连续的动作,我们就要用另一种梯度表示形式:
在这里插入图片描述
从上一个公式,到这一个公式的推导,我就不啰嗦了,感兴趣的可以去看bilibili北大王树森的深度强化学习教程,对这一块有推导。
        Vπ(st)评价的是状态st时的状态好坏,而我们希望的是所有状态更好。那么对于所有的状态S,状态价值Vπ(S)的均值应当很大。因此我们定义目标函数:
在这里插入图片描述
图片来源:北大王树森
这个目标函数排除掉了状态S 的因素,只依赖于策略网络π的参数θ;策略越好,则J(θ) 越大。所以策略学习可以描述为这样一个优化问题:
在这里插入图片描述
我们希望通过对策略网络参数θ的更新,使得目标函数J(θ)越来越大,也就意味着策略网络越来越强。想要求解最大化问题,显然可以用梯度上升更新θ,使得J(θ)增大。设当前策略网络的参数为θnow。做梯度上升更新参数,得到新的参数θnew:
在这里插入图片描述把之前的Vπ(st)的推导写成所有状态的形式。策略梯度可以写成下面定理中的期望形式
在这里插入图片描述
解析求出这个期望是不可能的,因为我们并不知道状态S概率密度函数;即使我们知道S 的概率密度函数,能够通过连加或者定积分求出期望,我们也不愿意这样做,因为连加或者定积分的计算量非常大。我们用蒙特卡洛近似,可以用这种方法用来近似策略梯度中的期望。每次从环境中观测到一个状态s,它相当于随机变量S的观测值。然后再根据当前的策略网络(策略网络的参数必须是最新的)随机抽样得出一个动作,计算随机梯度:
在这里插入图片描述

3.REINFORCEMENT算法        从梯度策略的更新中,我们可以看见,还有一个Qπ(st,at)的值是不确定的。这里有两种办法得到Qπ(st,at)。第一种对于一个完整的幕,及从开始状态到终态的状态、动作、序列我们都知道。在这里插入图片描述

根据定义,我们可以直接计算出每个时刻的Qπ(at,st)。这种算法就是REINFORCEMENT算法。训练步骤如下:
在这里插入图片描述
在这里插入图片描述

这种算法的更新需要序列是完整的。第二种方法就是,我们用一个神经网络来近似Qπ(at,st),用TD的方法来更新,这种算法就是Actor-crictic结构的算法。
4.actor-critic
        Actor-Critic算法,从名字翻译就是“演员-评委”方法。策略网络π(a|s;θ)相当于演员。价值网络相当于评委。如下图所示,图片来源北大王树森。
在这里插入图片描述
训练流程如下
在这里插入图片描述

5.总结
        强化学习的两种学习方式,一种是基于价值,一种就是基于策略,他们分别由deepmind和openAI引领,而后走向了归一。这一节我们介绍了基于策略的算法。下一节会分享AC结构的强化学习算法。

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于Tensorflow的《Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning with a Stochastic Actor》版本的SAC强化学习算法的Python代码: ```python import tensorflow as tf import numpy as np import gym # Create actor network class Actor(tf.keras.Model): def __init__(self, state_dim, action_dim, max_action): super(Actor, self).__init__() self.layer1 = tf.keras.layers.Dense(256, activation='relu') self.layer2 = tf.keras.layers.Dense(256, activation='relu') self.mu_layer = tf.keras.layers.Dense(action_dim, activation='tanh') self.sigma_layer = tf.keras.layers.Dense(action_dim, activation='softplus') self.max_action = max_action def call(self, state): x = self.layer1(state) x = self.layer2(x) mu = self.mu_layer(x) * self.max_action sigma = self.sigma_layer(x) + 1e-4 return mu, sigma # Create two critic networks class Critic(tf.keras.Model): def __init__(self, state_dim, action_dim): super(Critic, self).__init__() self.layer1 = tf.keras.layers.Dense(256, activation='relu') self.layer2 = tf.keras.layers.Dense(256, activation='relu') self.layer3 = tf.keras.layers.Dense(1, activation=None) def call(self, state, action): x = tf.concat([state, action], axis=1) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) return x # Create Soft Actor-Critic (SAC) Agent class SACAgent: def __init__(self, state_dim, action_dim, max_action): self.actor = Actor(state_dim, action_dim, max_action) self.critic1 = Critic(state_dim, action_dim) self.critic2 = Critic(state_dim, action_dim) self.target_critic1 = Critic(state_dim, action_dim) self.target_critic2 = Critic(state_dim, action_dim) self.max_action = max_action self.alpha = tf.Variable(0.1, dtype=tf.float32, name='alpha') self.gamma = 0.99 self.tau = 0.005 self.optimizer_actor = tf.keras.optimizers.Adam(learning_rate=3e-4) self.optimizer_critic1 = tf.keras.optimizers.Adam(learning_rate=3e-4) self.optimizer_critic2 = tf.keras.optimizers.Adam(learning_rate=3e-4) def get_action(self, state): state = np.expand_dims(state, axis=0) mu, sigma = self.actor(state) dist = tfp.distributions.Normal(mu, sigma) action = tf.squeeze(dist.sample(1), axis=0) return action.numpy() def update(self, replay_buffer, batch_size): states, actions, rewards, next_states, dones = replay_buffer.sample(batch_size) with tf.GradientTape(persistent=True) as tape: # Compute actor loss mu, sigma = self.actor(states) dist = tfp.distributions.Normal(mu, sigma) log_pi = dist.log_prob(actions) q1 = self.critic1(states, actions) q2 = self.critic2(states, actions) q_min = tf.minimum(q1, q2) alpha_loss = -tf.reduce_mean(self.alpha * (log_pi + self.target_entropy)) actor_loss = -tf.reduce_mean(tf.exp(self.alpha) * log_pi * q_min) # Compute critic loss next_mu, next_sigma = self.actor(next_states) next_dist = tfp.distributions.Normal(next_mu, next_sigma) next_actions = tf.clip_by_value(next_dist.sample(1), -self.max_action, self.max_action) target_q1 = self.target_critic1(next_states, next_actions) target_q2 = self.target_critic2(next_states, next_actions) target_q = tf.minimum(target_q1, target_q2) target_q = rewards + self.gamma * (1.0 - dones) * (target_q - tf.exp(self.alpha) * next_dist.entropy()) q1_loss = tf.reduce_mean(tf.square(q1 - target_q)) q2_loss = tf.reduce_mean(tf.square(q2 - target_q)) critic_loss = q1_loss + q2_loss + alpha_loss # Compute gradients and update weights actor_grads = tape.gradient(actor_loss, self.actor.trainable_variables) critic1_grads = tape.gradient(critic_loss, self.critic1.trainable_variables) critic2_grads = tape.gradient(critic_loss, self.critic2.trainable_variables) self.optimizer_actor.apply_gradients(zip(actor_grads, self.actor.trainable_variables)) self.optimizer_critic1.apply_gradients(zip(critic1_grads, self.critic1.trainable_variables)) self.optimizer_critic2.apply_gradients(zip(critic2_grads, self.critic2.trainable_variables)) # Update target networks for w, w_target in zip(self.critic1.weights, self.target_critic1.weights): w_target.assign(self.tau * w + (1 - self.tau) * w_target) for w, w_target in zip(self.critic2.weights, self.target_critic2.weights): w_target.assign(self.tau * w + (1 - self.tau) * w_target) # Update alpha alpha_grad = tape.gradient(alpha_loss, self.alpha) self.alpha.assign_add(1e-4 * alpha_grad) def save(self, filename): self.actor.save_weights(filename + '_actor') self.critic1.save_weights(filename + '_critic1') self.critic2.save_weights(filename + '_critic2') def load(self, filename): self.actor.load_weights(filename + '_actor') self.critic1.load_weights(filename + '_critic1') self.critic2.load_weights(filename + '_critic2') # Create replay buffer class ReplayBuffer: def __init__(self, max_size): self.max_size = max_size self.buffer = [] self.position = 0 def add(self, state, action, reward, next_state, done): if len(self.buffer) < self.max_size: self.buffer.append(None) self.buffer[self.position] = (state, action, reward, next_state, done) self.position = (self.position + 1) % self.max_size def sample(self, batch_size): indices = np.random.choice(len(self.buffer), batch_size, replace=False) states, actions, rewards, next_states, dones = [], [], [], [], [] for idx in indices: state, action, reward, next_state, done = self.buffer[idx] states.append(np.array(state, copy=False)) actions.append(np.array(action, copy=False)) rewards.append(reward) next_states.append(np.array(next_state, copy=False)) dones.append(done) return np.array(states), np.array(actions), np.array(rewards, dtype=np.float32), np.array(next_states), np.array(dones, dtype=np.uint8) # Create environment and agent env = gym.make('Pendulum-v0') state_dim = env.observation_space.shape[0] action_dim = env.action_space.shape[0] max_action = float(env.action_space.high[0]) agent = SACAgent(state_dim, action_dim, max_action) replay_buffer = ReplayBuffer(1000000) # Train agent max_episodes = 1000 max_steps = 500 batch_size = 256 update_interval = 1 target_entropy = -action_dim for episode in range(max_episodes): state = env.reset() total_reward = 0 for step in range(max_steps): action = agent.get_action(state) next_state, reward, done, _ = env.step(action) replay_buffer.add(state, action, reward, next_state, done) if len(replay_buffer.buffer) > batch_size: agent.update(replay_buffer, batch_size) state = next_state total_reward += reward if done: break print("Episode:", episode, "Total Reward:", total_reward) ``` 请注意,以上代码仅供参考,并且需要根据具体环境和参数进行调整和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值