前语

在 REINFORCE 算法中,每次需求根据一个战略收集一条完好的轨道,并核算这条轨道上的回报。这种采样办法的方差比较大,学习功率也比较低。咱们能够借鉴时序差分学习的思维,运用动态规划办法来进步采样功率,即从状况ss开端的总回报能够经过当前动作的即时奖赏r(s,a,s′)r(s,a,s’)和下一个状况s′s’的值函数来近似估量。

为了解决 High Variance 和 High bias 之间的对立,能够把它们结合在一起,运用value based 和 policy based 两类办法各自的优势,还顺带把它们的短板都补上了,所以就有了集大成的 Actor-Critic 类办法。 Actor-Critic类算法是一种结合战略梯度时序差分学习的强化学习办法,其间,Actor是指战略函数 (a∣s)\pi _{\theta }(a|s),即学习一个战略以得到尽可能高的回报。Critic是指价值函数V(s)V_{\pi}(s),对当前战略的值函数进行估量,即评估Actor的好坏。借助于价值函数,Actor-Critic算法能够进行单步参数更新,不需求比及回合结束才进行更新。所以咱们需求两个网络,一个担任生成战略的 Actor 和一个担任评价战略的 Critic。这就有点类似一个艺人在扮演,而一起一个评论家在随时纠正他的表现,而且两者都还在不断更新,这种互补式的练习办法会比独自的战略网络或许值函数网络更有用。

Actor-Critic是调集和ploicy gradient和Q-learning的办法。

回忆policy gradient和Q-learning办法

policy gradient:

深度强化学习系列: Actor-Critic(AC)算法原理及代码详解

能够看出policy gradient是基于回合更新的,因而咱们需求需求很大的耐性和环境产生交互样本,进行回合练习,而且因为每个回合是不稳定的,因而咱们需求的大量的样本。

Q-learning

深度强化学习系列: Actor-Critic(AC)算法原理及代码详解

Q-learning学习的是每个动作的价值,要求动作有必要是离散的。

AC(Actor-critic)算法原理

Actor-Critic 算法是一个既基于战略也基于价值的办法。在上一节咱们提到,在Reinfoce算法中能够用状况价值函数作为基准函数来下降梯度估量的方差。Actor-Critic 算法也沿用了相同的主意,一起学习举动者(Actor)函数(也便是智能体的战略函数(⋅∣s)\pi (\cdot |s))和批评者(Critic)函数(也便是状况价值函数V(s)V^{\pi }(s) )。此外,Actor-Critic算法还沿用了自举法(Bootstrapping)的思维来估量Q值函数。Reinfoce中的差错项∑t=i∞t−iRt−b(St′){\textstyle \sum_{t=i}^{\infty }}\gamma ^{t-i}R_{t}-b(S_{t’} )被时间差分差错替代了,即 Ri+V(Si+1)−V(Si)R_{i}+\gamma V^{\pi }(S_{i+1})-V^{\pi }(S_{i})

下面咱们具体看一下AC算法的数学推导:

  • 咱们这儿选用 L 步的时间差分差错,并经过最小化该差错的平方来学习批评者函数V(s)V_{\psi }^{\pi _{\theta } }(s),即
⟵−▽JV()\psi \longleftarrow \psi -\eta _{\psi} \bigtriangledown J_{V_{\psi }^{\pi _{\theta } }(\psi )}
  • 式子中\psi表明学习批评者函数的参数,\eta_{\psi }是学习步长,而且
JV()=12(∑t=ii+L−1t−iRt+LV(S′)−V(Si))2J_{V_{\psi }^{\pi _{\theta } }} (\psi )=\frac{1}{2}(\sum_{t=i}^{i+L-1}\gamma ^{t-i}R_{t}+\gamma^{L}V_{\psi }^{\pi _{\theta } }(S’)-V_{\psi }^{\pi _{\theta }}(S_{i} ) )^{2}
  • S′S’是智能体在 \pi _{\theta } 下 L 步之后到达的状况,所以
▽JV()=(V(Si)−∑t=ii+L−1t−iRt−LV(S′))▽V(Si)\bigtriangledown J_{V_{\psi }^{\pi _{\theta } }} (\psi )=(V_{\psi }^{\pi _{\theta }}(S_{i} )-\sum_{t=i}^{i+L-1}\gamma ^{t-i}R_{t}-\gamma^{L}V_{\psi }^{\pi _{\theta } }(S’))\bigtriangledown V_{\psi }^{\pi _{\theta }}(S_{i} )
  • 类似地,举动者函数(⋅∣s)\pi _{\theta }(\cdot |s)决定每个状况 s 上所采纳的动作或许动作空间上的一个概率散布。咱们选用和初版战略梯度类似的办法来学习这个战略函数。
=+▽J()\theta =\theta +\eta _{\theta }\bigtriangledown J_{\pi _{\theta } }(\theta )
  • 这儿\theta表明举动者函数的参数,\eta_{\psi } 是学习步长,而且
▽J()=E,[∑i=0∞∇log(Ai∣Si)(∑t=ii+L−1t−iRt+LV(S′)−V(Si))]\bigtriangledown J (\theta )=E_{\tau ,\theta }[\sum_{i=0}^{\infty }\nabla log\pi _{\theta }(A_{i}|S_{i} ) (\sum_{t=i}^{i+L-1}\gamma ^{t-i}R_{t}+\gamma^{L}V_{\psi }^{\pi _{\theta } }(S’)-V_{\psi }^{\pi _{\theta }}(S_{i} ) )]

注意到,咱们这儿别离用了\theta\psi来表明战略函数和价值函数的参数。在实践运用中,当咱们挑选用神经网络来表明这两个函数的时分,经常会让两个网络共享一些底层的网络层作为共同的状况表征(State Representation)。此外,AC 算法中的 L 值经常设为 1, 也便是 TD(0) 差错。

  • 值得注意的是,AC 算法也能够运用 Q 值函数作为其批评者。在这种情况下,优势函数能够用以下式子估量。
Q(s,a)−V(s)=Q(s,a)−∑a(a∣s)Q(s,a)Q(s,a)-V(s)=Q(s,a)-\sum_{a}^{}\pi (a|s)Q(s,a)
  • 用来学习 Q 值函数这个批评者的损失函数为
JQ=(Rt+Q(St+1,At+1)−Q(St,At))2J_{Q}=(R_{t}+\gamma Q(S_{t+1},A_{t+1})-Q(S_{t},A_{t}))^{2}

AC算法的代码详解

以AC: CartPole-V0为例

Actor-Critic 算法经过 TD 办法核算基准,能在每次和环境交互后立刻更新战略,和 MC 非常不同。

在 Actor-Critic 算法中,咱们树立了 2 个类:Actor 和 Critic,其结构如下所示。

class Actor(object):
    def __init__(self, state_dim, action_num, lr=0.001): # 类初始化。创立模型、优化器及其
        ...
    def learn(self, state, action, td_error): # 更新模型
        ...
    def get_action(self, state, greedy=False): # 经过概率散布或许贪心办法挑选动作
        ...
    def save(self): # 存储练习模型
        ...
    def load(self): # 载入练习模型
        ...
class Critic(object):
    def __init__(self, state_dim, lr=0.01): # 类初始化。创立模型、优化器及其所需变量
        ...
    def learn(self, state, reward, state_): # 更新模型
        ...
    def save(self): # 存储练习模型
        ...
    def load(self): # 载入练习模型
        ...

Actor 类的部分和战略梯度算法很像。仅有的区别是 learn() 函数运用了 TD 差错作为优势估量值进行更新,而不是运用扣头化奖赏。

def learn(self, state, action, td_error):
    with tf.GradientTape() as tape:
        _logits = self.model(np.array([state]))
        _exp_v = tl.rein.cross_entropy_reward_loss(logits=_logits, actions=[action],
                                                   rewards=td_error[0])
    grad = tape.gradient(_exp_v, self.model.trainable_weights)
    self.optimizer.apply_gradients(zip(grad, self.model.trainable_weights))
    return _exp_v

和 PG 算法不同,AC 算法有一个带有价值网络的批评者,它能估量每个状况的价值。所以它初始化函数十分清晰,只需求创立网络和优化器即可。

class Critic(object):
    def __init__(self, state_dim, lr=0.01):
        input_layer = tl.layers.Input([1, state_dim], name=’state’)
        layer = tl.layers.Dense(
            n_units=30, act=tf.nn.relu6, W_init=tf.random_uniform_initializer(0, 0.01),
            name=’hidden’
        )(input_layer)
        layer = tl.layers.Dense(n_units=1, act=None, name=’value’)(layer)
        self.model = tl.models.Model(inputs=input_layer, outputs=layer, name="Critic")
        self.model.train()
        self.optimizer = tf.optimizers.Adam(lr)

在初始化函数之后,咱们有了一个价值网络。下一步便是树立 learn() 函数。learn() 函数任务非常简略,经过公式 =R+V(s′)−V(s)\delta =R+\gamma V(s’)-V(s)核算 TD 差错\delta,之后将 TD 差错作为优势估量来核算损失。

def learn(self, state, reward, state_, done):
    d = 0 if done else 1
    v_ = self.model(np.array([state_]))
    with tf.GradientTape() as tape:
        v = self.model(np.array([state]))
        td_error = reward + d * LAM * v_ - v
        loss = tf.square(td_error)
    grad = tape.gradient(loss, self.model.trainable_weights)
    self.optimizer.apply_gradients(zip(grad, self.model.trainable_weights))
    return td_error

存储和载入函数与平常一样。咱们也能够将网络参数存储为.npz 格式的文件。

def save(self):
    if not os.path.exists(os.path.join(’model’, ’ac’)):
        os.makedirs(os.path.join(’model’, ’ac’))
        tl.files.save_npz(self.model.trainable_weights, name=os.path.join(’model’, ’ac’,’model_critic.npz’))
def load(self): 
    tl.files.load_and_assign_npz(name=os.path.join(’model’, ’ac’,
        ’model_critic.npz’), network=self.model)

练习循环的代码和之前的代码非常类似。仅有的不同是更新的机遇不同。运用 TD 差错的情况下,咱们能够在每步进行更新。

if args.train:
    all_episode_reward = []
    for episode in range(TRAIN_EPISODES):
        state = env.reset().astype(np.float32)
        step = 0 
        episode_reward = 0 
        while True:
            if RENDER: env.render()
            action = actor.get_action(state)
            state_new, reward, done, info = env.step(action)
            state_new = state_new.astype(np.float32)
            if done: reward = -20 
            episode_reward += reward
            td_error = critic.learn(state, reward, state_new, done)
            actor.learn(state, action, td_error)
            state = state_new
            step += 1
            if done or step >= MAX_STEPS:
                break