Skip to content

Latest commit

 

History

History
700 lines (452 loc) · 32 KB

File metadata and controls

700 lines (452 loc) · 32 KB

十一、策略梯度和优化

在最后三章中,我们学习了各种深度强化学习算法,例如深度 Q 网络DQN),深度循环 Q 网络(DRQN)和异步优势演员评论家A3C)网络。 在所有算法中,我们的目标是找到正确的策略,以便我们能够最大化回报。 我们使用 Q 函数来找到最佳策略,因为 Q 函数告诉我们哪个动作是在某种状态下执行的最佳动作。 您认为我们不使用 Q 函数就能直接找到最优策略吗? 是。 我们可以。 在策略梯度方法中,我们无需使用 Q 函数就可以找到最优策略。

在本章中,我们将详细了解策略梯度。 我们还将研究不同类型的策略梯度方法,例如深度确定性策略梯度,然后是最新的策略优化方法,例如信任区域策略优化和近端策略优化。

在本章中,您将学习以下内容:

  • 策略梯度
  • 使用策略梯度的 Lunar Lander
  • 深度确定性策略梯度
  • 使用深度确定性策略梯度DDPG)摆动
  • 信任区域策略优化
  • 近端策略优化

策略梯度

策略梯度是强化学习RL)的惊人算法之一,在该算法中,我们直接优化由某些参数θ设置的策略。 到目前为止,我们已经使用 Q 函数来找到最佳策略。 现在,我们将了解如何找到没有 Q 函数的最优策略。 首先,让我们将策略函数定义为π(a | s),即在状态为s的情况下采取a动作的概率。 我们通过参数θ将策略参数化为π(a | s; θ),这使我们能够确定状态下的最佳操作。

策略梯度方法具有多个优点,它可以处理连续动作空间,在该连续动作空间中,我们具有无限数量的动作和状态。 假设我们正在制造自动驾驶汽车。 驾驶汽车时应避免撞到其他车辆。 当汽车撞到车辆时,我们得到负奖励,而当汽车没有撞到其他车辆时,我们得到正奖励。 我们以仅获得积极奖励的方式更新模型参数,以使我们的汽车不会撞到任何其他车辆。 这是策略梯度的基本思想:我们以最大化报酬的方式更新模型参数。 让我们详细看一下。

我们使用神经网络来找到最佳策略,我们将此网络称为策略网络。 策略网络的输入将是状态,而输出将是该状态中每个操作的概率。 一旦有了这个概率,就可以从该分布中采样一个动作,并在状态下执行该动作。 但是我们采样的动作可能不是在该状态下执行的正确动作。 很好-我们执行动作并存储奖励。 同样,我们通过从分布中采样一个动作来在每种状态下执行动作,并存储奖励。 现在,这成为我们的训练数据。 我们执行梯度下降并以这样的方式更新梯度:在状态下产生高奖励的动作将具有较高的概率,而在状态下产生低奖励的动作将具有较低的概率。 什么是损失函数? 在这里,我们使用 softmax 交叉熵损失,然后将损失乘以奖励值。

使用策略梯度的 Lunar Lander

假设我们的经纪人正在驾驶航天器,而我们的经纪人的目标是正确着陆在着陆垫上。 如果我们的智能体(着陆器)从着陆点着陆,则它会失去奖励,并且如果智能体崩溃或休息,剧集将终止。 在环境中可用的四个离散动作是“不执行任何操作”,“点火向左的引擎”,“点火主引擎”和“点火向右的引擎”。

现在,我们将看到如何训练我们的智能体以策略梯度正确降落在降落区。 本节中使用的代码属于 Gabriel

首先,我们导入必要的库:

import tensorflow as tf
import numpy as np
from tensorflow.python.framework import ops
import gym
import numpy as np
import time

然后,我们定义PolicyGradient类,该类实现了策略梯度算法。 让我们分解类并分别查看每个函数。 您可以将整个程序看作 Jupyter 笔记本

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函数,该函数存储转换,即stateactionreward。 我们可以使用以下信息来训练网络:

    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

    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})
        # 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函数:

    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函数,该函数将导致折扣和标准化奖励:

    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

现在我们实际执行学习:

    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,
        })
        # reset the episodic data
        self.episode_observations, self.episode_actions, self.episode_rewards = [], [], []

        return discounted_episode_rewards_norm

您可以看到如下输出:

深度确定性策略梯度

在第 8 章,“深度 Q 网络和 Atari 游戏”中,我们研究了 DQN 的工作原理,并应用了 DQN 玩 Atari 游戏。 但是,在那些离散的环境中,我们只有一组有限的动作。 想象一个连续的环境空间,例如训练机器人走路; 在那些环境中,应用 Q 学习是不可行的,因为要找到一个贪婪的策略将需要在每一步进行很多优化。 即使我们使连续的环境离散,我们也可能会失去重要的函数并最终获得大量的动作空间。 当我们拥有巨大的行动空间时,很难实现融合。

因此,我们使用称为演员评论家的新架构,该架构具有两个网络:演员和评论家。 演员评论家架构结合了策略梯度和状态操作值函数。 演员网络的作用是通过调整参数θ来确定状态中的最佳动作,而评论家的作用是评估演员产生的动作。 评论家通过计算时间差异误差来评估演员的行动。 也就是说,我们在演员网络上执行策略梯度以选择操作, 评论家网络使用 TD 误差评估由演员网络产生的操作。 下图显示了演员评论家架构:

与 DQN 相似,这里我们使用经验缓冲区,通过采样少量的经验来训练演员和评论家网络。 我们还使用单独的目标演员和评论家网络来计算损失。

例如,在乒乓游戏中,我们将具有不同比例的不同特征,例如位置,速度等。 因此,我们以所有特征都处于相同比例的方式来缩放特征。 我们使用一种称为批归一化的方法来缩放特征。 它将所有特征归一化以具有单位均值和方差。 我们如何探索新的行动? 在连续环境中,将有n个动作。 为了探索新动作,我们在演员网络产生的动作中添加了一些噪声N。 我们使用称为 Ornstein-Uhlenbeck 随机过程的过程来生成此噪声。

现在,我们将详细介绍 DDPG 算法。

假设我们有两个网络:演员网络和评论家网络。 我们用输入为状态的μ(s; θ^μ)表示演员网络 ,并以θ^μ作为演员网络权重的结果进行操作。 我们将评论家网络表示为Q(s, a; θ^Q),它将输入作为状态和动作并返回Q值,其中θ^Q是评论家网络权重。

同样,我们将演员网络和评论家网络的目标网络分别定义为μ(s; θ^μ')Q(s, a; θ^Q'),其中θ^μ'θ^Q'是目标演员和评论家网络的权重。

我们使用策略梯度更新演员网络权重,并使用根据 TD 误差计算得出的梯度更新评论家网络权重。

首先,我们通过将探索噪声N添加到演员网络产生的动作(例如μ(s; θ^μ) + N)来选择动作。 我们在s状态下执行此操作,获得r奖励,然后移至新状态s'。 我们将此转移信息存储在经验回放缓冲区中。

经过一些迭代后,我们从回放缓冲区采样转移并训练网络,然后计算目标Q值:

我们将 TD 误差计算为:

其中M是来自回放缓冲区的用于训练的样本数。 我们使用根据此损失L计算出的梯度来更新评论家网络的权重。

同样,我们使用策略梯度更新策略网络权重。 然后,我们在目标网络中更新 Actor 和评论家网络的权重。 我们会缓慢更新目标网络的权重,从而提高稳定性。 它称为软替换:

摆动摆锤

我们有一个从随机位置开始的摆锤,我们的探员的目标是向上摆动摆锤使其保持直立。 我们将在这里看到如何使用 DDPG。 wshuail 提供了本节中使用的代码。

首先,让我们导入必要的库:

import tensorflow as tf
import numpy as np
import gym

接下来,我们按如下方式定义超参数:

# 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 算法。 我们分解类以查看每个函数。 首先,我们初始化所有内容:

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)
        # 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 随机过程生成噪声:

  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函数,在该函数中进行实际训练。 在这里,我们从经验缓冲区中选择一批statesactionsrewards和下一个状态。 我们以此来训练演员和评论家网络:

    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函数,该函数将所有信息存储在缓冲区中并执行学习:

    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函数来构建演员网络:

    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函数:

    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环境:

env = gym.make("Pendulum-v0")
env = env.unwrapped
env.seed(1)

我们得到状态数:

no_of_states = env.observation_space.shape[0]

我们得到的动作数:

no_of_actions = env.action_space.shape[0]

此外,该动作的上限:

a_bound = env.action_space.high

现在,我们为DDPG类创建一个对象:

ddpg = DDPG(no_of_actions, no_of_states, a_bound)

我们初始化列表以存储总奖励:

total_reward = []

设置剧集数:

no_of_episodes = 300

现在,让我们开始训练:

# 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

您将看到如下输出:

我们可以在 TensorBoard 中看到计算图:

信任区域策略优化

在了解信任区域策略优化TRPO)之前,我们需要了解受约束的策略优化。 我们知道,在 RL 智能体中,通过反复试验来学习,以使报酬最大化。 为了找到最佳策略,我们的智能体将探索所有不同的行动,并选择能获得良好回报的行动。 在探索不同的动作时,我们的智能体很有可能也会探索不良的动作。 但是最大的挑战是,当我们允许智能体在现实世界中学习以及奖励函数设计不当时。 例如,考虑一个学习走路而没有遇到任何障碍的智能体。 如果智能体被任何障碍物击中,它将获得负奖励;而如果没有被任何障碍物击中,则将获得正奖励。 为了找出最佳策略,智能体会探索不同的操作。 智能体还采取行动,例如撞到障碍物以检查它是否给出了良好的回报。 但这对我们的经纪人来说并不安全; 当智能体在现实环境中学习时,这尤其不安全。 因此,我们介绍了基于约束的学习。 我们设置一个阈值,如果碰到障碍物的可能性小于该阈值,则我们认为我们的智能体是安全的,否则我们认为我们的智能体是不安全的。 添加了约束以确保我们的智能体位于安全区域内。

在 TRPO 中,我们迭代地改进了该策略,并施加了一个约束,以使旧策略和新策略之间的 Kullback-LeiblerKL)差异要小于某个常数。 。 该约束称为信任区域约束。

那么什么是 KL 散度? KL 散度告诉我们两个概率分布如何彼此不同。 由于我们的策略是针对行动的概率分布,因此 KL 差异告诉我们新策略与旧策略有多远。 为什么我们必须使旧策略和新策略之间的距离保持小于恒定值δ? 因为我们不希望我们的新策略与旧策略脱节。 因此,我们施加了约束以使新策略接近旧策略。 同样,为什么我们必须保持旧策略呢? 当新策略与旧策略相距甚远时,它将影响智能体的学习表现,并导致完全不同的学习行为。 简而言之,在 TRPO 中,我们朝着改善策略的方向迈出了一步,即使报酬最大化,但我们还应确保满足信任区域约束。 它使用共轭梯度下降优化网络参数θ,同时满足约束条件。 该算法保证了单调策略的改进,并且在各种连续环境中也取得了出色的效果。

现在,我们将了解 TRPO 的数学原理。 如果您对数学不感兴趣,可以跳过本节。

准备一些很棒的数学。

让我们指定预期的总折扣奖励η(π),如下所示:

现在让我们将新策略视为π'; 就相对于旧策略π的优势而言,可以将其定义为策略π'的预期回报,如下所示:

好吧,为什么我们要利用旧策略的优势? 因为我们正在衡量新策略π'相对于旧策略π的平均效果有多好。 我们可以用状态之和而不是时间步来重写前面的方程,如下所示:

是折扣的访问频率,即:

如果您看到前面的方程η(π'),则ρ[π'](s)π' 之间存在复杂的依存关系,因此很难对方程进行优化。 因此,我们将局部近似L[π](π')引入η(π'),如下所示:

使用访问频率ρ[π]而不是ρ[π'],也就是说,由于策略的变化,我们忽略了状态访问频率的变化。 简而言之,我们假设新旧策略的状态访问频率均相同。 当我们计算L[π]的梯度时,相对于某些参数θ而言,这也会提高η,我们不确定要采取多少步骤。

Kakade 和 Langford 提出了一种新的策略更新方法,称为保守策略迭代,如下所示:

----(1)

是新策略。 π_old是旧策略。

,即π',是最大化L[π_old]的策略。

Kakade 和 Langford 从(1)得出以下方程式:

----(2)

C是惩罚系数,等于4εγ / (1 - α)^2D_max[KL]表示旧策略与新策略之间的 KL 散度。

如果我们仔细观察前面的方程式(2),我们会注意到,只要右侧最大化,我们的预期长期回报η就会单调增加。

让我们将此右侧项定义为M[i](π),如下所示:

----(3)

将公式(3)代入(2),我们得到:

----(4)

由于我们知道两个相同策略之间的 KL 差异为0,我们可以这样写:

----(5)

结合方程式(4)(5),我们可以写:

在前面的等式中,我们可以理解,最大化M[i]可以保证我们期望收益的最大化。 因此,现在我们的目标是最大化M[i],从而最大化我们的预期回报。 由于我们使用参数化策略,因此在上一个公式中将π替换为θ,然后使用θ_old表示我们要改进的策略,如下所示:

但是,在前面的公式中具有惩罚系数C将导致步长非常小,从而减慢了更新速度。 因此,我们对 KL 散度的旧策略和新策略施加了约束,即信任区域约束,这将有助于我们找到最佳步长:

现在,问题是在状态空间的每个点上都施加了 KL 散度,当我们拥有高维状态空间时,解决它确实是不可行的。 因此,我们使用启发式近似,其平均 KL 散度为:

因此,现在,我们可以将平均 KL 散度约束重写为先前的目标函数,如下所示:

扩展L的值,我们得到以下信息:

在前面的公式中,我们将状态总和Σ[s] ρ θ_old替换为期望E[s ~ ρ θ_old],并且将重要性总和估计值替换为行动总和,如下所示:

然后,我们将优势目标值A[θ_old]替换为 Q 值Q[θ_old]

因此,我们最终的目标函数将变为:

优化前面提到的具有约束的目标函数称为约束优化。 我们的约束是使旧策略和新策略之间的平均 KL 差异小于δ。我们使用共轭梯度下降来优化先前的函数。

近端策略优化

现在,我们将看另一种策略优化算法,称为近端策略优化PPO)。 它是对 TRPO 的改进,由于其表现,已成为解决许多复杂 RL 问题的默认 RL 算法。 它是 OpenAI 的研究人员为克服 TRPO 的缺点而提出的。 回忆一下 TRPO 的替代目标函数。 这是一个约束优化问题,我们在其中施加了一个约束-新旧策略之间的平均 KL 差异应小于δ。 但是 TRPO 的问题在于,它需要大量计算能力才能计算共轭梯度以执行约束优化。

因此,PPO 通过将约束更改为惩罚项来修改 TRPO 的目标函数,因此我们不想执行共轭梯度。 现在,让我们看看 PPO 的工作原理。 我们将r[t](θ)定义为新旧策略之间的概率比。 因此,我们可以将目标函数编写为:

L_CPI表示保守策略迭代。 但是最大化L会导致无限制地进行大量策略更新。 因此,我们通过添加惩罚项来重新定义我们的目标函数,该惩罚项会惩罚较大的策略更新。 现在目标函数变为:

我们刚刚在实际方程式中添加了一个新项:

这是什么意思? 实际上,它会在间隔[1 - ε, 1 + ε]之间裁剪r[t](θ)的值,也就是说,如果r[t](θ)的值导致目标函数增加,则在间隔之间大量裁剪的值会降低其效果。

基于两种情况,我们将概率比限制在1 - εε

  • 案例 1A_hat[t] > 0

当优势为正时,这意味着相对于所有其他操作的平均值,应优先选择相应的操作。 我们将为该操作增加r[t](θ)的值,因此将有更大的机会被选择。 当我们执行r[t](θ)的限幅值时,不会超过1 + ε

  • 案例 2A_hat[t]

当优势的值为负时,这意味着该动作没有意义,因此不应采用。 因此,在这种情况下,我们将减小该操作的r[t](θ)值,以使其被选择的机会较小。 类似地,当我们执行裁剪时,r[t](θ)的值将不会减小到小于1 - ε

当我们使用神经网络架构时,我们必须定义损失函数,其中包括目标函数的值函数误差。 就像在 A3C 中一样,我们还将添加熵损失以确保足够的探索。 因此,我们最终的目标函数变为:

c[1]c[2]是系数,L[t]^(VP)是实际值和目标值函数之间的平方误差损失,即:

S是熵增。

总结

我们从策略梯度方法开始,该方法无需 Q 函数即可直接优化策略。 我们通过解决 Lunar Lander 游戏了解了策略梯度,并研究了 DDPG,它具有策略梯度和 Q 函数的优点。

然后,我们研究了诸如 TRPO 之类的策略优化算法,该算法通过对新旧策略之间的 KL 差异实现不大于δ的限制来确保单调策略的改进。

我们还研究了近端策略优化,该优化通过惩罚大型策略更新将约束变为惩罚。 在下一章第 12 章, “Capstone 项目 – 使用 DQN 进行赛车”,我们将了解如何构建能够赢得赛车游戏的智能体。

问题

问题列表如下:

  1. 什么是策略梯度?
  2. 策略梯度为何有效?
  3. DDPG 中的演员评论家网络有什么用途?
  4. 约束优化问题是什么?
  5. 什么是信任区域?
  6. PPO 如何克服 TRPO 的缺点?

进一步阅读

您可以进一步参考以下论文: