# 政策梯度和优化
在最后三章中,我们学习了各种深度强化学习算法,例如**深度 Q 网络**(**DQN**),**深度递归 Q 网络**([HTG6 DRQN )和**异步优势演员评论家**(**A3C**)网络。 在所有算法中,我们的目标是找到正确的策略,以便我们能够最大化回报。 我们使用 Q 函数来找到最佳策略,因为 Q 函数告诉我们哪个动作是在某种状态下执行的最佳动作。 您认为我们不使用 Q 函数就能直接找到最优策略吗? 是。 我们可以。 在策略梯度方法中,我们无需使用 Q 函数就可以找到最优策略。
在本章中,我们将详细了解政策梯度。 我们还将研究不同类型的策略梯度方法,例如深度确定性策略梯度,然后是最新的策略优化方法,例如信任区域策略优化和近端策略优化。
在本章中,您将学习以下内容:
* 政策梯度
* 使用政策梯度的月球着陆器
* 深度确定性政策梯度
* 使用`d`**深度确定性策略梯度**(**DDPG**)摆动
* 信任区域政策优化
* 近端政策优化
# 政策梯度
策略梯度是**强化学习**(**RL**)的惊人算法之一,在该算法中,我们直接优化由某些参数![](img/00304.jpeg)设置的策略。 到目前为止,我们已经使用 Q 函数来找到最佳策略。 现在,我们将了解如何找到没有 Q 函数的最优策略。 首先,让我们将策略功能定义为![](img/00305.jpeg),即在状态为*的情况下*采取*和*动作的概率。 我们通过参数![](img/00306.jpeg)将策略参数化为![](img/00307.jpeg),这使我们能够确定状态下的最佳操作。
策略梯度方法具有多个优点,它可以处理连续动作空间,在该连续动作空间中,我们具有无限数量的动作和状态。 假设我们正在制造自动驾驶汽车。 驾驶汽车时应避免撞到其他车辆。 当汽车撞到车辆时,我们得到负奖励,而当汽车没有撞到其他车辆时,我们得到正奖励。 我们以仅获得积极奖励的方式更新模型参数,以使我们的汽车不会撞到任何其他车辆。 这是策略梯度的基本思想:我们以最大化报酬的方式更新模型参数。 让我们详细看一下。
我们使用神经网络来找到最佳策略,我们将此网络称为策略网络。 策略网络的输入将是状态,而输出将是该状态中每个操作的概率。 一旦有了这个概率,就可以从该分布中采样一个动作,并在状态下执行该动作。 但是我们采样的动作可能不是在该状态下执行的正确动作。 很好-我们执行动作并存储奖励。 同样,我们通过从分布中采样一个动作来在每种状态下执行动作,并存储奖励。 现在,这成为我们的训练数据。 我们执行梯度下降并以这样的方式更新梯度:在状态下产生高奖励的动作将具有较高的概率,而在状态下产生低奖励的动作将具有较低的概率。 什么是损失函数? 在这里,我们使用 softmax 交叉熵损失,然后将损失乘以奖励值。
# 使用策略渐变的 Lunar Lander
假设我们的经纪人正在驾驶航天器,而我们的经纪人的目标是正确着陆在着陆垫上。 如果我们的特工(着陆器)从着陆点着陆,则它会失去奖励,并且如果特工崩溃或休息,情节将终止。 在环境中可用的四个离散动作是“不执行任何操作”,“向左射击引擎”,“向主机射击”和“向右引擎”。
现在,我们将看到如何训练我们的代理商以策略梯度正确降落在降落区。 本节中使用的代码属于 [Gabriel](https://github.com/gabrielgarza/openai-gym-policy-gradient):
![](img/00308.jpeg)
首先,我们导入必要的库:
```py
import tensorflow as tf
import numpy as np
from tensorflow.python.framework import ops
import gym
import numpy as np
import time
```
然后,我们定义`PolicyGradient`类,该类实现了策略梯度算法。 让我们分解类并分别查看每个函数。 [您可以将整个程序看作 Jupyter 笔记本](https://github.com/sudharsan13296/Hands-On-Reinforcement-Learning-With-Python/blob/master/11.%20Policy%20Gradients%20and%20Optimization/11.2%20Lunar%20Lander%20Using%20Policy%20Gradients.ipynb):
```py
class PolicyGradient:
# first we define the __init__ method where we initialize all variables
def __init__(self, n_x,n_y,learning_rate=0.01, reward_decay=0.95):
# number of states in the environment
self.n_x = n_x
# number of actions in the environment
self.n_y = n_y
# learning rate of the network
self.lr = learning_rate
# discount factor
self.gamma = reward_decay
# initialize the lists for storing observations,
# actions and rewards
self.episode_observations, self.episode_actions, self.episode_rewards = [], [], []
# we define a function called build_network for
# building the neural network
self.build_network()
# stores the cost i.e loss
self.cost_history = []
# initialize tensorflow session
self.sess = tf.Session()
self.sess.run(tf.global_variables_initializer())
```
接下来,我们定义一个`store_transition`函数,该函数存储转换,即`state`,`action`和`reward`。 我们可以使用以下信息来训练网络:
```py
def store_transition(self, s, a, r):
self.episode_observations.append(s)
self.episode_rewards.append(r)
# store actions as list of arrays
action = np.zeros(self.n_y)
action[a] = 1
self.episode_actions.append(action)
```
给定`state`,我们定义`choose_action`函数来选择`action`:
```py
def choose_action(self, observation):
# reshape observation to (num_features, 1)
observation = observation[:, np.newaxis]
# run forward propagation to get softmax probabilities
prob_weights = self.sess.run(self.outputs_softmax, feed_dict = {self.X: observation})
```
```py
# select action using a biased sample this will return
# the index of the action we have sampled
action = np.random.choice(range(len(prob_weights.ravel())), p=prob_weights.ravel())
return action
```
我们定义用于构建神经网络的`build_network`函数:
```py
def build_network(self):
# placeholders for input x, and output y
self.X = tf.placeholder(tf.float32, shape=(self.n_x, None), name="X")
self.Y = tf.placeholder(tf.float32, shape=(self.n_y, None), name="Y")
# placeholder for reward
self.discounted_episode_rewards_norm = tf.placeholder(tf.float32, [None, ], name="actions_value")
# we build 3 layer neural network with 2 hidden layers and
# 1 output layer
# number of neurons in the hidden layer
units_layer_1 = 10
units_layer_2 = 10
# number of neurons in the output layer
units_output_layer = self.n_y
# now let us initialize weights and bias value using
# tensorflow's tf.contrib.layers.xavier_initializer
W1 = tf.get_variable("W1", [units_layer_1, self.n_x], initializer = tf.contrib.layers.xavier_initializer(seed=1))
b1 = tf.get_variable("b1", [units_layer_1, 1], initializer = tf.contrib.layers.xavier_initializer(seed=1))
W2 = tf.get_variable("W2", [units_layer_2, units_layer_1], initializer = tf.contrib.layers.xavier_initializer(seed=1))
b2 = tf.get_variable("b2", [units_layer_2, 1], initializer = tf.contrib.layers.xavier_initializer(seed=1))
W3 = tf.get_variable("W3", [self.n_y, units_layer_2], initializer = tf.contrib.layers.xavier_initializer(seed=1))
b3 = tf.get_variable("b3", [self.n_y, 1], initializer = tf.contrib.layers.xavier_initializer(seed=1))
# and then, we perform forward propagation
Z1 = tf.add(tf.matmul(W1,self.X), b1)
A1 = tf.nn.relu(Z1)
Z2 = tf.add(tf.matmul(W2, A1), b2)
A2 = tf.nn.relu(Z2)
Z3 = tf.add(tf.matmul(W3, A2), b3)
A3 = tf.nn.softmax(Z3)
# as we require, probabilities, we apply softmax activation
# function in the output layer,
logits = tf.transpose(Z3)
labels = tf.transpose(self.Y)
self.outputs_softmax = tf.nn.softmax(logits, name='A3')
# next we define our loss function as cross entropy loss
neg_log_prob = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=labels)
# reward guided loss
loss = tf.reduce_mean(neg_log_prob * self.discounted_episode_rewards_norm)
# we use adam optimizer for minimizing the loss
self.train_op = tf.train.AdamOptimizer(self.lr).minimize(loss)
```
接下来,我们定义`discount_and_norm_rewards`函数,该函数将导致折扣和标准化奖励:
```py
def discount_and_norm_rewards(self):
discounted_episode_rewards = np.zeros_like(self.episode_rewards)
cumulative = 0
for t in reversed(range(len(self.episode_rewards))):
cumulative = cumulative * self.gamma + self.episode_rewards[t]
discounted_episode_rewards[t] = cumulative
discounted_episode_rewards -= np.mean(discounted_episode_rewards)
discounted_episode_rewards /= np.std(discounted_episode_rewards)
return discounted_episode_rewards
```
现在我们实际执行学习:
```py
def learn(self):
# discount and normalize episodic reward
discounted_episode_rewards_norm = self.discount_and_norm_rewards()
# train the network
self.sess.run(self.train_op, feed_dict={
self.X: np.vstack(self.episode_observations).T,
self.Y: np.vstack(np.array(self.episode_actions)).T,
self.discounted_episode_rewards_norm: discounted_episode_rewards_norm,
})
```
```py
# reset the episodic data
self.episode_observations, self.episode_actions, self.episode_rewards = [], [], []
return discounted_episode_rewards_norm
```
您可以看到如下输出:
![](img/00309.jpeg)
# 深度确定性政策梯度
在第 8 章,*具有深度 Q 网络的 Atari 游戏*,中,我们研究了 DQN 的工作原理,并应用了 DQN 玩 Atari 游戏。 但是,在那些离散的环境中,我们只有一组有限的动作。 想象一个连续的环境空间,例如训练机器人走路; 在那些环境中,应用 Q 学习是不可行的,因为要找到一个贪婪的策略将需要在每一步进行很多优化。 即使我们使连续的环境离散,我们也可能会失去重要的功能并最终获得大量的动作空间。 当我们拥有巨大的行动空间时,很难实现融合。
因此,我们使用称为 Actor Critic 的新架构,该架构具有两个网络-Actor 和 Critic。 Actor Critic 体系结构结合了策略梯度和状态操作值功能。 **Actor** 网络的作用是通过调整参数![](img/00310.jpeg)来确定**状态**中的最佳动作,而 **Critic** 的作用是评估 **Actor** 产生的动作。 **评论家**通过计算时间差异误差来评估演员的行动。 也就是说,我们在 **Actor** 网络上执行策略渐变以选择操作, **Critic** 网络评估由 **Actor** 产生的操作 ]网络使用 TD 错误。 下图显示了 Actor Critic 体系结构:
![](img/00311.gif)
与 DQN 相似,这里我们使用体验缓冲区,通过采样少量的经验来训练 Actor 和 Critic 网络。 我们还使用单独的目标 Actor 和 Critic 网络来计算损失。
例如,在 Pong 游戏中,我们将具有不同比例的不同特征,例如位置,速度等。 因此,我们以所有要素都处于相同比例的方式来缩放要素。 我们使用一种称为批归一化的方法来缩放要素。 它将所有特征归一化以具有单位均值和方差。 我们如何探索新的行动? 在连续环境中,将有`n`个动作。 为了探索新动作,我们在 Actor 网络产生的动作中添加了一些噪声`N`。 我们使用称为 Ornstein-Uhlenbeck 随机过程的过程来生成此噪声。
现在,我们将详细介绍 DDPG 算法。
假设我们有两个网络:Actor 网络和 Critic 网络。 我们用输入为状态的![](img/00312.jpeg)表示 Actor 网络
,并以![](img/00313.jpeg)作为 Actor 网络权重的结果进行操作。 我们将 Critic 网络表示为![](img/00314.jpeg),它将输入作为状态和动作并返回`Q`值,其中![](img/00315.jpeg)是 Critic 网络权重。
同样,我们将 Actor 网络和 Critic 网络的目标网络分别定义为![](img/00316.jpeg)和![](img/00317.jpeg),其中![](img/00318.jpeg)和![](img/00319.jpeg)是目标 Actor 和 Critic 网络的权重。
我们使用策略梯度更新 Actor 网络权重,并使用根据 TD 误差计算得出的梯度更新 Critic 网络权重。
首先,我们通过将探索噪声`N`添加到 Actor 网络产生的动作(例如![](img/00320.jpeg))来选择动作。 我们在`s`状态下执行此操作,获得`r`奖励,然后移至新状态 *s'*。 我们将此过渡信息存储在体验重播缓冲区中。
经过一些迭代后,我们从重播缓冲区采样过渡并训练网络,然后计算目标`Q`值![](img/00321.jpeg)。 我们将 TD 误差计算为:
![](img/00322.jpeg)
其中`M`是来自重播缓冲区的用于训练的样本数。 我们使用根据此损耗`L`计算出的梯度来更新评论网络的权重。
同样,我们使用策略梯度更新策略网络权重。 然后,我们在目标网络中更新 Actor 和 Critic 网络的权重。 我们会缓慢更新目标网络的权重,从而提高稳定性。 它称为软替换:
![](img/00323.jpeg)
# 摆动钟摆
我们有一个从随机位置开始的摆锤,我们的探员的目标是向上摆动摆锤使其保持直立。 我们将在这里看到如何使用 DDPG。 [wshuail](https://github.com/wangshuailong/reinforcement_learning_with_Tensorflow/tree/master/DDPG) 提供了本节中使用的代码。
首先,让我们导入必要的库:
```py
import tensorflow as tf
import numpy as np
import gym
```
接下来,我们按如下方式定义超参数:
```py
# number of steps in each episode
epsiode_steps = 500
# learning rate for actor
lr_a = 0.001
# learning rate for critic
lr_c = 0.002
# discount factor
gamma = 0.9
# soft replacement
alpha = 0.01
# replay buffer size
memory = 10000
# batch size for training
batch_size = 32
render = False
```
我们将在`DDPG`类中实现 DDPG 算法。 我们分解类以查看每个函数。 首先,我们初始化所有内容:
```py
class DDPG(object):
def __init__(self, no_of_actions, no_of_states, a_bound,):
# initialize the memory with shape as no of actions, no of states and our defined memory size
self.memory = np.zeros((memory, no_of_states * 2 + no_of_actions + 1), dtype=np.float32)
# initialize pointer to point to our experience buffer
self.pointer = 0
# initialize tensorflow session
self.sess = tf.Session()
# initialize the variance for OU process for exploring policies
self.noise_variance = 3.0
self.no_of_actions, self.no_of_states, self.a_bound = no_of_actions, no_of_states, a_bound,
# placeholder for current state, next state and rewards
self.state = tf.placeholder(tf.float32, [None, no_of_states], 's')
self.next_state = tf.placeholder(tf.float32, [None, no_of_states], 's_')
self.reward = tf.placeholder(tf.float32, [None, 1], 'r')
# build the actor network which has separate eval(primary)
# and target network
with tf.variable_scope('Actor'):
self.a = self.build_actor_network(self.state, scope='eval', trainable=True)
a_ = self.build_actor_network(self.next_state, scope='target', trainable=False)
# build the critic network which has separate eval(primary)
# and target network
with tf.variable_scope('Critic'):
q = self.build_crtic_network(self.state, self.a, scope='eval', trainable=True)
q_ = self.build_crtic_network(self.next_state, a_, scope='target', trainable=False)
# initialize the network parameters
self.ae_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='Actor/eval')
self.at_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='Actor/target')
self.ce_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='Critic/eval')
self.ct_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='Critic/target')
# update target value
self.soft_replace = [[tf.assign(at, (1-alpha)*at+alpha*ae), tf.assign(ct, (1-alpha)*ct+alpha*ce)]
for at, ae, ct, ce in zip(self.at_params, self.ae_params, self.ct_params, self.ce_params)]
# compute target Q value, we know that Q(s,a) = reward + gamma *
Q'(s',a')
q_target = self.reward + gamma * q_
# compute TD error i.e actual - predicted values
td_error = tf.losses.mean_squared_error(labels=(self.reward + gamma * q_), predictions=q)
# train the critic network with adam optimizer
self.ctrain = tf.train.AdamOptimizer(lr_c).minimize(td_error, name="adam-ink", var_list = self.ce_params)
# compute the loss in actor network
a_loss = - tf.reduce_mean(q)
# train the actor network with adam optimizer for
# minimizing the loss
self.atrain = tf.train.AdamOptimizer(lr_a).minimize(a_loss, var_list=self.ae_params)
```
```py
# initialize summary writer to visualize our network in tensorboard
tf.summary.FileWriter("logs", self.sess.graph)
# initialize all variables
self.sess.run(tf.global_variables_initializer())
```
我们如何在 DDPG 中选择一个动作? 我们通过向动作空间添加噪音来选择动作。 我们使用 Ornstein-Uhlenbeck 随机过程生成噪声:
```py
def choose_action(self, s):
a = self.sess.run(self.a, {self.state: s[np.newaxis, :]})[0]
a = np.clip(np.random.normal(a, self.noise_variance), -2, 2)
return a
```
然后,我们定义`learn`函数,在该函数中进行实际训练。 在这里,我们从经验缓冲区中选择一批`states`,`actions`,`rewards`和下一个状态。 我们以此来训练演员和评论家网络:
```py
def learn(self):
# soft target replacement
self.sess.run(self.soft_replace)
indices = np.random.choice(memory, size=batch_size)
batch_transition = self.memory[indices, :]
batch_states = batch_transition[:, :self.no_of_states]
batch_actions = batch_transition[:, self.no_of_states: self.no_of_states + self.no_of_actions]
batch_rewards = batch_transition[:, -self.no_of_states - 1: -self.no_of_states]
batch_next_state = batch_transition[:, -self.no_of_states:]
self.sess.run(self.atrain, {self.state: batch_states})
self.sess.run(self.ctrain, {self.state: batch_states, self.a: batch_actions, self.reward: batch_rewards, self.next_state: batch_next_state})
```
我们定义了一个`store_transition`函数,该函数将所有信息存储在缓冲区中并执行学习:
```py
def store_transition(self, s, a, r, s_):
trans = np.hstack((s,a,[r],s_))
index = self.pointer % memory
self.memory[index, :] = trans
self.pointer += 1
if self.pointer > memory:
self.noise_variance *= 0.99995
self.learn()
```
我们定义了`build_actor_network`函数来构建 Actor 网络:
```py
def build_actor_network(self, s, scope, trainable):
# Actor DPG
with tf.variable_scope(scope):
l1 = tf.layers.dense(s, 30, activation = tf.nn.tanh, name = 'l1', trainable = trainable)
a = tf.layers.dense(l1, self.no_of_actions, activation = tf.nn.tanh, name = 'a', trainable = trainable)
return tf.multiply(a, self.a_bound, name = "scaled_a")
```
我们定义`build_ crtic_network`函数:
```py
def build_crtic_network(self, s, a, scope, trainable):
# Critic Q-leaning
with tf.variable_scope(scope):
n_l1 = 30
w1_s = tf.get_variable('w1_s', [self.no_of_states, n_l1], trainable = trainable)
w1_a = tf.get_variable('w1_a', [self.no_of_actions, n_l1], trainable = trainable)
b1 = tf.get_variable('b1', [1, n_l1], trainable = trainable)
net = tf.nn.tanh( tf.matmul(s, w1_s) + tf.matmul(a, w1_a) + b1 )
q = tf.layers.dense(net, 1, trainable = trainable)
return q
```
现在,我们使用`make`函数初始化`gym`环境:
```py
env = gym.make("Pendulum-v0")
env = env.unwrapped
env.seed(1)
```
我们得到状态数:
```py
no_of_states = env.observation_space.shape[0]
```
我们得到的动作数:
```py
no_of_actions = env.action_space.shape[0]
```
此外,该动作的上限:
```py
a_bound = env.action_space.high
```
现在,我们为`DDPG`类创建一个对象:
```py
ddpg = DDPG(no_of_actions, no_of_states, a_bound)
```
我们初始化列表以存储总奖励:
```py
total_reward = []
```
设置剧集数:
```py
no_of_episodes = 300
```
现在,让我们开始训练:
```py
# for each episodes
for i in range(no_of_episodes):
# initialize the environment
s = env.reset()
# episodic reward
ep_reward = 0
for j in range(epsiode_steps):
env.render()
# select action by adding noise through OU process
a = ddpg.choose_action(s)
# perform the action and move to the next state s
s_, r, done, info = env.step(a)
# store the the transition to our experience buffer
# sample some minibatch of experience and train the network
ddpg.store_transition(s, a, r, s_)
# update current state as next state
s = s_
# add episodic rewards
ep_reward += r
if j == epsiode_steps-1:
# store the total rewards
total_reward.append(ep_reward)
# print rewards obtained per each episode
print('Episode:', i, ' Reward: %i' % int(ep_reward))
break
```
您将看到如下输出:
![](img/00324.jpeg)
我们可以在 TensorBoard 中看到计算图:
![](img/00325.jpeg)
# 信任区域政策优化
在了解**信任区域策略优化**(**TRPO**)之前,我们需要了解受约束的策略优化。 我们知道,在 RL 代理商中,通过反复试验来学习,以使报酬最大化。 为了找到最佳政策,我们的代理商将探索所有不同的行动,并选择能获得良好回报的行动。 在探索不同的动作时,我们的代理商很有可能也会探索不良的动作。 但是最大的挑战是,当我们允许代理商在现实世界中学习以及奖励功能设计不当时。 例如,考虑一个学习走路而没有遇到任何障碍的特工。 如果代理商被任何障碍物击中,它将获得负奖励;而如果没有被任何障碍物击中,则将获得正奖励。 为了找出最佳策略,代理会探索不同的操作。 代理还采取行动,例如撞到障碍物以检查它是否给出了良好的回报。 但这对我们的经纪人来说并不安全; 当代理在现实环境中学习时,这尤其不安全。 因此,我们介绍了基于约束的学习。 我们设置一个阈值,如果碰到障碍物的可能性小于该阈值,则我们认为我们的代理人是安全的,否则我们认为我们的代理人是不安全的。 添加了约束以确保我们的代理位于安全区域内。
在 TRPO 中,我们迭代地改进了该策略,并施加了一个约束,以使旧策略和新策略之间的 **Kullback-Leibler**(**KL**)差异要小于某个常数。 ![](img/00326.jpeg)。 该约束称为信任区域约束。
那么什么是 KL 分歧? KL 散度告诉我们两个概率分布如何彼此不同。 由于我们的政策是针对行动的概率分布,因此 KL 差异告诉我们新政策与旧政策有多远。 为什么我们必须使旧策略和新策略之间的距离保持小于恒定值![](img/00327.jpeg)? 因为我们不希望我们的新政策与旧政策脱节。 因此,我们施加了约束以使新策略接近旧策略。 同样,为什么我们必须保持旧政策呢? 当新政策与旧政策相距甚远时,它将影响代理的学习绩效,并导致完全不同的学习行为。 简而言之,在 TRPO 中,我们朝着改善政策的方向迈出了一步,即使报酬最大化,但我们还应确保满足信任区域约束。 [它使用共轭梯度下降](http://www.idi.ntnu.no/~elster/tdt24/tdt24-f09/cg.pdf)优化网络参数![](img/00328.jpeg),同时满足约束条件。 该算法保证了单调策略的改进,并且在各种连续环境中也取得了出色的效果。
现在,我们将了解 TRPO 的数学原理。 如果您对数学不感兴趣,可以跳过本节。
准备一些很棒的数学。
让我们指定预期的总折扣奖励![](img/00329.jpeg),如下所示:
![](img/00330.jpeg)
现在让我们将新策略视为![](img/00331.jpeg); 就相对于旧政策![](img/00333.jpeg)的优势而言,可以将其定义为政策![](img/00332.jpeg)的预期回报,如下所示:
![](img/00334.jpeg)
好吧,为什么我们要利用旧政策的优势? 因为我们正在衡量新策略![](img/00335.jpeg)相对于旧策略![](img/00336.jpeg)的平均效果有多好。 我们可以用状态之和而不是时间步来重写前面的方程,如下所示:
![](img/00337.jpeg)
![](img/00338.jpeg)是折扣的访问频率,即:
![](img/00339.jpeg)
如果您看到前面的方程![](img/00340.jpeg),则![](img/00341.jpeg)与![](img/00342.jpeg)
之间存在复杂的依存关系,因此很难对方程进行优化。 因此,我们将局部近似![](img/00343.jpeg)引入![](img/00344.jpeg),如下所示:
![](img/00345.jpeg)
![](img/00346.jpeg)使用访问频率![](img/00347.jpeg)而不是![](img/00348.jpeg),也就是说,由于策略的变化,我们忽略了状态访问频率的变化。 简而言之,我们假设新旧政策的州访问频率均相同。 当我们计算![](img/00349.jpeg)的梯度时,相对于某些参数![](img/00351.jpeg)而言,这也会提高![](img/00350.jpeg),我们不确定要采取多少步骤。
Kakade 和 Langford 提出了一种新的策略更新方法,称为保守策略迭代,如下所示:
![](img/00352.jpeg) ----(1)
![](img/00353.jpeg)是新政策。 ![](img/00354.jpeg)是旧政策。
![](img/00355.jpeg),即![](img/00356.jpeg),是最大化![](img/00357.jpeg)的策略。
Kakade 和 Langford 从(1)得出以下方程式:
![](img/00358.jpeg) ----(2)
`C`是惩罚系数,等于![](img/00359.jpeg),![](img/00360.jpeg)表示旧政策与新政策之间的 KL 分歧。
如果我们仔细观察前面的方程式(2),我们会注意到,只要右侧最大化,我们的预期长期回报![](img/00361.jpeg)就会单调增加。
让我们将此右侧术语定义为![](img/00362.jpeg),如下所示:
![](img/00363.jpeg) ----(3)
将公式(3)代入(2),我们得到:
![](img/00364.jpeg) ----(4)
由于我们知道两个相同策略之间的 KL 差异为`0`,我们可以这样写:
![](img/00365.jpeg) ----(5)
结合方程式(4)和(5),我们可以写:
![](img/00366.jpeg)
在前面的等式中,我们可以理解,最大化 *M i* 可以保证我们期望收益的最大化。 因此,现在我们的目标是最大化 *M i* ,从而最大化我们的预期回报。 由于我们使用参数化策略,因此在上一个公式中将![](img/00367.jpeg)替换为![](img/00368.jpeg),然后使用![](img/00369.jpeg)表示我们要改进的策略,如下所示:
![](img/00370.jpeg)
但是,在前面的公式中具有惩罚系数`C`将导致步长非常小,从而减慢了更新速度。 因此,我们对 KL 分歧的旧策略和新策略施加了约束,即信任区域约束,这将有助于我们找到最佳步长:
![](img/00371.jpeg)
现在,问题是在状态空间的每个点上都施加了 KL 散度,当我们拥有高维状态空间时,解决它确实是不可行的。 因此,我们使用启发式近似,其平均 KL 散度为:
![](img/00372.jpeg)
因此,现在,我们可以将平均 KL 散度约束重写为先前的目标函数,如下所示:
![](img/00373.jpeg)
扩展`L`的值,我们得到以下信息:
![](img/00374.jpeg)
在前面的公式中,我们将状态总和![](img/00375.jpeg)替换为期望![](img/00376.jpeg),并且将重要性总和估计值替换为行动总和,如下所示:
![](img/00377.jpeg)
然后,我们将优势目标值![](img/00378.jpeg)替换为 Q 值![](img/00379.jpeg)。
因此,我们最终的目标函数将变为:
![](img/00380.jpeg)
优化前面提到的具有约束的目标函数称为约束优化。 我们的约束是使旧策略和新策略之间的平均 KL 差异小于![](img/00381.jpeg)。我们使用共轭梯度下降来优化先前的功能。
# 近端政策优化
现在,我们将看另一种策略优化算法,称为**近端策略优化**(**PPO**)。 它是对 TRPO 的改进,由于其性能,已成为解决许多复杂 RL 问题的默认 RL 算法。 它是 OpenAI 的研究人员为克服 TRPO 的缺点而提出的。 回忆一下 TRPO 的替代目标函数。 这是一个约束优化问题,我们在其中施加了一个约束-新旧政策之间的平均 KL 差异应小于![](img/00382.jpeg)。 但是 TRPO 的问题在于,它需要大量计算能力才能计算共轭梯度以执行约束优化。
因此,PPO 通过将约束更改为惩罚项来修改 TRPO 的目标函数,因此我们不想执行共轭梯度。 现在,让我们看看 PPO 的工作原理。 我们将![](img/00383.jpeg)定义为新旧政策之间的概率比。 因此,我们可以将目标函数编写为:
![](img/00384.jpeg)
*L CPI* 表示保守策略迭代。 但是最大化`L`会导致无限制地进行大量策略更新。 因此,我们通过添加惩罚项来重新定义我们的目标函数,该惩罚项会惩罚较大的政策更新。 现在目标函数变为:
![](img/00385.jpeg)
我们刚刚在实际方程式中添加了一个新术语![](img/00386.jpeg)。 这是什么意思? 实际上,它会在间隔![](img/00388.jpeg)之间裁剪![](img/00387.jpeg)的值,也就是说,如果![](img/00389.jpeg)的值导致目标函数增加,则在间隔之间大量裁剪的值会降低其效果。
基于两种情况,我们将概率比限制在![](img/00390.jpeg)或![](img/00391.jpeg):
* **案例 1** :![](img/00392.jpeg)
当优势为正时,这意味着相对于所有其他操作的平均值,应优先选择相应的操作。 我们将为该操作增加![](img/00393.jpeg)的值,因此将有更大的机会被选择。 当我们执行![](img/00394.jpeg)的限幅值时,不会超过![](img/00395.jpeg):
![](img/00396.gif)
* **案例 2** :![](img/00397.jpeg)
当优势的值为负时,这意味着该动作没有意义,因此不应采用。 因此,在这种情况下,我们将减小该操作的![](img/00398.jpeg)值,以使其被选择的机会较小。 类似地,当我们执行裁剪时,![](img/00399.jpeg)的值将不会减小到小于![](img/00400.jpeg):
![](img/00401.gif)
当我们使用神经网络架构时,我们必须定义损失函数,其中包括目标函数的值函数误差。 就像在 A3C 中一样,我们还将添加熵损失以确保足够的探索。 因此,我们最终的目标函数变为:
![](img/00402.jpeg)
*c 1* 和 *c 2* 是系数,![](img/00403.jpeg)是实际值和目标值函数之间的平方误差损失,即 是![](img/00404.jpeg)和`S`是熵加。
# 概要
我们从策略梯度方法开始,该方法无需 Q 函数即可直接优化策略。 我们通过解决 Lunar Lander 游戏了解了策略梯度,并研究了 DDPG,它具有策略梯度和 Q 函数的优点。
然后,我们研究了诸如 TRPO 之类的策略优化算法,该算法通过对新旧策略之间的 KL 差异实施不大于![](img/00405.jpeg)的限制来确保单调策略的改进。
我们还研究了近端策略优化,该优化通过惩罚大型策略更新将约束变为惩罚。 在下一章[,第 12 章](12.html#65D4E0-3c5bb317ad314d43ac43a332c0db6f00), *Capstone 项目–使用 DQN* 进行赛车,我们将了解如何构建能够赢得赛车游戏的代理。
# 问题
问题列表如下:
1. 什么是政策梯度?
2. 政策梯度为何有效?
3. DDPG 中的 Actor Critic 网络有什么用途?
4. 约束优化问题是什么?
5. 什么是信任区域?
6. PPO 如何克服 TRPO 的缺点?
# 进一步阅读
您可以进一步参考以下论文:
* [**DDPG 论文**](https://github.com/wangshuailong/reinforcement_learning_with_Tensorflow/tree/master/DDPG)
* [**TRPO 论文**](https://arxiv.org/pdf/1502.05477.pdf)
* [**PPO 论文**](https://arxiv.org/pdf/1707.06347.pdf)