强化学习笔记(6)-执行者/评论者方法

以下为阅读《强化学习:原理与python实现》这本书第八章的学习笔记。

本章介绍带自益的策略梯度算法,这类算法将策略梯度和自益结合起来:一方面,用一个含参函数近似价值函数,利用这个近似值来估计回报值;另一方面,利用估计得到的回报值估计策略梯度,进而更新策略参数。这两方面常常被称为评论者(critic)和执行者(actor)。所以带自益的策略梯度算法称为执行者/评论者算法。

执行者/评论者算法用含参函数h(s,a;\theta)表示偏好,用其softmax运算的结果\pi(a|s;\theta)来近似最优策略。在更新参数\theta时,也是根据策略梯度定理,取E[\psi_{t}\triangledown ln\pi(A_{t}|S_{t};\theta)]为梯度方向迭代更新。\psi_{t}可以是以下几种形式:

(动作价值)\psi_{t}=\gamma ^{t}q_{\pi}(S_{t},A_{t})

(优势函数)\psi_{t}=\gamma ^{t}[q_{\pi}(S_{t},A_{t})-v_{\pi}(S_{t}))]

(时序差分)\psi_{t}=\gamma ^{t}[R_{t}+\gamma v_{\pi}(S_{t+1})-v_{\pi}(S_{t})]

我们可以用含参函数v(s;w) (s\in S)q(s,a;w) (s\in S, a\in A(s))来近似v_{\pi}, q_{\pi}

动作价值同策执行者/评论者算法

1.(初始化)\theta \leftarrow任意值,w\leftarrow任意值

2.(带自益的策略更新)对每个回合执行以下操作:

2.1(初始化累计折扣)I\leftarrow 1

2.2(决定初始动作)用\pi(\cdot |S;\theta)得到动作A

2.3 如果回合未结束,执行以下操作:

2.3.1(采样)根据状态S和动作A得到采样R和下一状态S'

2.3.2(执行)用\pi(\cdot |S';\theta)得到动作A'

2.3.3(估计回报)U\leftarrow R+\gamma q(S',A';w)

2.3.4(策略改进)更新\theta以减小-Iq(S,A;w)ln\pi(A|S;\theta) 如\theta\leftarrow \theta+\alpha^{(\theta)}Iq(S,A;w)\triangledown ln\pi(A|S;\theta)

2.3.5(更新价值)更新w以减小[U-q(S,A;w)]^{2} 如w\leftarrow w+\alpha^{(w)}[U-q(S,A;w)]\triangledown q(S,A;w)

2.3.6(更新累计折扣)I\leftarrow \gamma I

2.3.7(更新状态)S\leftarrow S', A\leftarrow A'

优势执行者/评论者算法

在基本执行者/评论者算法中引入基线函数B(S_{t})=v(S_{t};w),就会得到\psi_{t}=\gamma^{t}[q(S_{t},A_{t};w)-v(S_{t};w)],其中,q(S_{t},A_{t};w)-v(S_{t};w)是优势函数的估计,从而得到了优势执行者/评论者算法。不过,为了避免需要搭建两个函数分别表示q(w)和v(w),我们可以采用U_{t}=R_{t}+\gamma v(S_{t+1};w)做目标,这样优势函数的估计就变为单步时序差分的形式R_{t}+\gamma v(S_{t+1};w)-v(S_{t};w),算法如下:

输入:环境(无数学描述)

输出:最优策略的估计\pi(\theta)

参数:优化器(隐含学习率\alpha^{(\theta)}, \alpha^{(w)}),折扣因子\gamma,控制回合数和回合内步数的参数

1.(初始化)\theta\leftarrow任意值,w\leftarrow任意值

2.(带自益的策略更新)对每个回合执行以下操作

2.1(初始化累积折扣)I\leftarrow 1

2.2(决定初始动作)用\pi(\cdot |S;\theta)得到动作A

2.3 如果回合未结束,执行以下操作:

2.3.1(采样)根据状态S和动作A得到采样R和下一状态S'

2.3.2(执行)用\pi(\cdot|S';\theta)得到动作A'

2.3.3(估计回报)U\leftarrow R+\gamma q(S',A';w)

2.3.4(策略改进)更新\theta以减小-I[U-v(S;w)]ln\pi(A|S;\theta),如\theta\leftarrow \theta+\alpha^{(\theta)}I[U-v(S;w)]\triangledown ln\pi(A|S;\theta)

2.3.5(更新价值)更新w以减小[U-v(S;w)]^{2},如w\leftarrow w+\alpha^{(w)}[U-v(S;w)]\triangledown v(S;w)

2.3.6(更新累积折扣)I\leftarrow \gamma I

2.3.7(更新状态)S\leftarrow S', A\leftarrow A'

其他算法

在书中还提到了其他算法,例如基于代理优势的同策算法,信任域算法,柔性执行者/评论者算法等,这些算法都比较复杂,在此略过,感兴趣的朋友可以详细阅读这本书。

双节倒立摆案例

以Gym库中的双节倒立摆(Acrobot-v1)作为案例。可以看到有两根杆子首尾相接,一端固定在原点,另一端在二维垂直面上活动。可以在两个杆子的连接处施加动作{0,1,2},每过一步,惩罚奖励值-1。当活动端超过一个固定高度时,或者回合达到500步,回合结束。我们希望回合数尽量少。

以下视频是以随机的动作执行的情况:

Acrobot_random

以下是优势执行者/评论者算法的代码,经过测试,优势执行者/评论者算法比简单的同策执行者/评论者算法性能更好,训练所花费的时间也更少。

import gym
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
import tensorflow.keras as keras
from keras.initializers import GlorotUniform
import tqdm
%matplotlib notebook
from IPython import display

env = gym.make("Acrobot-v1")

class Chart:
    def __init__(self):
        self.fig, self.ax = plt.subplots(1, 1)
    
    def plot(self, episode_rewards):
        self.ax.clear()
        self.ax.plot(episode_rewards)
        self.ax.set_xlabel('iteration')
        self.ax.set_ylabel('episode reward')
        self.fig.canvas.draw()

class AdvantageActorCriticAgent:
    def __init__(self, env, actor_kwargs, critic_kwargs, gamma=0.99):
        self.action_n = env.action_space.n
        self.gamma = gamma
        self.discount = 1.

        self.actor_net = self.build_network(output_size=self.action_n,
                output_activation=tf.nn.softmax,
                loss=tf.losses.categorical_crossentropy,
                **actor_kwargs)
        self.critic_net = self.build_network(output_size=1,
                **critic_kwargs)
        
    def build_network(self, hidden_sizes, output_size, input_size=None,
                activation=tf.nn.relu, output_activation=None,
                loss=tf.losses.mse, learning_rate=0.01):
        model = keras.Sequential()
        for idx, hidden_size in enumerate(hidden_sizes):
            kwargs = {}
            if idx == 0 and input_size is not None:
                kwargs['input_shape'] = (input_size,)
            model.add(keras.layers.Dense(units=hidden_size,
                    activation=activation, **kwargs))
        model.add(keras.layers.Dense(units=output_size,
                activation=output_activation))
        optimizer = tf.optimizers.Adam(learning_rate)
        model.compile(optimizer=optimizer, loss=loss)
        return model
    
    def decide(self, observation):
        probs = self.actor_net.predict(observation[np.newaxis])[0]
        action = np.random.choice(self.action_n, p=probs)
        return action
    
    def learn(self, observation, action, reward, next_observation, done):
        x = observation[np.newaxis]
        u = reward + (1. - done) * self.gamma * \
                self.critic_net.predict(next_observation[np.newaxis])
        td_error = u - self.critic_net.predict(x)
        
        # 训练执行者网络
        x_tensor = tf.convert_to_tensor(observation[np.newaxis],
                dtype=tf.float32)
        with tf.GradientTape() as tape:
            pi_tensor = self.actor_net(x_tensor)[0, action]
            logpi_tensor = tf.math.log(tf.clip_by_value(pi_tensor,
                    1e-6, 1.))
            loss_tensor = -self.discount * td_error * logpi_tensor
        grad_tensors = tape.gradient(loss_tensor, self.actor_net.variables)
        self.actor_net.optimizer.apply_gradients(zip(
                grad_tensors, self.actor_net.variables)) # 更新执行者网络
        
        # 训练评论者网络
        self.critic_net.fit(x, u, verbose=0) # 更新评论者网络
        
        if done:
            self.discount = 1. # 为下一回合初始化累积折扣
        else:
            self.discount *= self.gamma # 进一步累积折扣\

def play_qlearning(env, agent, train=False, render=False):
    episode_reward = 0
    observation = env.reset()
    step = 0
    while True:
        if render:
            env.render()
        action = agent.decide(observation)
        next_observation, reward, done, _ = env.step(action)
        episode_reward += reward
        if train:
            agent.learn(observation, action, reward, next_observation,
                    done)
        if done:
            break
        step += 1
        observation = next_observation
    return episode_reward

actor_kwargs = {'hidden_sizes' : [100,], 'learning_rate' : 0.0001}
critic_kwargs = {'hidden_sizes' : [100,], 'learning_rate' : 0.0002}
agent = AdvantageActorCriticAgent(env, actor_kwargs=actor_kwargs,
        critic_kwargs=critic_kwargs)

# 训练
episodes = 100
episode_rewards = []
chart = Chart()
for episode in tqdm.trange(episodes):
    episode_reward = play_qlearning(env, agent, train=True)
    episode_rewards.append(episode_reward)
    chart.plot(episode_rewards)

训练过程中的回合回报如下:

 训练完成后,我们可以测试一下,如以下视频:

Acrobot_train

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gzroy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值