可随意转载。Update2022.02.20

前言

基于信息论的算法是另外一个大类强化学习算法,它们属于off-policy和随机策略。我们常见的PPO算法属于on-policy算法,采样效率不高,在一些实际场景中不适用。DDPG属于off-policy和确定策略(deterministic policy)算法,采样效率高,但是确定策略只需要寻求其中一个解,可能陷入局部最优。来自伯克利大学的算法Soft Actor-Critic(SAC)属于这一类基于信息论的算法(off-policy + 随机policy)。官方算法论文有三篇:

2017年:Reinforcement Learning with Deep Energy-Based Policies (Soft Q-Learning)

2018年:Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning with a Stochastic Actor

2019年:Soft Actor-Critic Algorithms and Applications

飞桨目前的实现是基于第二篇论文(2018年)

一、SAC算法原理

DQN算法的目标函数:

最大化奖励最大的状态-动作序列。

SAC算法的目标函数:

最大化奖励以及信息熵的状态-动作序列。

为什么除了奖励最大还要加上信息熵最大呢。因为信息论原理可以证明,信息熵越大,系统越混乱;缩小到RL领域,信息熵越大,动作越随机。SAC算法的基本思想是:最大化信息熵等同于尽可能的探索动作空间,获取最多的探索路径(trajectory)。

对比DDPG的deterministic policy,SAC的思路优点是什么呢?

  1. 随机policy可以学到更多路径,SAC算法可以作为其他高阶算法(HRL)的基础。
  2. 更强的探索能力。
  3. 更健壮。

其实在A3C/A2C算法中,已经加入了entropy。例如:飞桨PARL库代码(a2c.py)中:

        # 分类分布
        policy_distribution = Categorical(logits)
        policy_entropy = policy_distribution.entropy()
        entropy = paddle.sum(policy_entropy)

        total_loss = (
            pi_loss + vf_loss * self.vf_loss_coeff + entropy * entropy_coeff)

Soft Q-Learning

面对多模的(multimodal)的Q function,传统的RL只能收敛到一个选择(左图),而更优的办法是右图,让policy也直接拟合Q的分布。见下图:

作者在2017年论文中推导了从Soft Value Function (V) 到Energy-Based Models获得了等式Soft Q,推导过程请看知乎大神的blog

二、飞桨的SAC库

2.1 机器人控制场景(Mujoco环境)

1. 下载模拟环境

可以直接使用pip上的Mujoco环境。因为它已经开源了。另外本文使用的是v3版本的环境 — Humanoid-v3。

2. 启动训练

# --env 是环境参数,表示启动哪一个训练环境
# --alpha 表示最大化熵和最大化奖励占学习的比例
python train.py --env Humanoid-v3 --alpha 0.2

3. main函数

    env = gym.make(args.env)
    # 这个参数是SAC算法特定的
    env.seed(args.seed)
    # 用到了PARL封装的连续动作类
    env = ActionMappingWrapper(env)
    # 状态空间和动作空间
    obs_dim = env.observation_space.shape[0]
    action_dim = env.action_space.shape[0]

    # 传统三件套model algorith agent
    model = MujocoModel(obs_dim, action_dim)
    algorithm = SAC(
        model,
        gamma=GAMMA,
        tau=TAU,
        alpha=args.alpha,
        actor_lr=ACTOR_LR,
        critic_lr=CRITIC_LR)
    agent = MujocoAgent(algorithm)
    # Q-Learning传统的rpm
    rpm = ReplayMemory(
        max_size=MEMORY_SIZE, obs_dim=obs_dim, act_dim=action_dim)

从状态和动作空间维度看,这个例子是一个非常简单的RL场景。

4. 训练函数run_train_episode

def run_train_episode(agent, env, rpm):
    while not done:
        # 选择动作
            action = agent.sample(obs)
        # step函数
        next_obs, reward, done, _ = env.step(action)
        # 写ReplyMemory
        rpm.append(obs, action, reward, next_obs, terminal)
        ...
        # 预填充一些再开始学习
        if rpm.size() >= WARMUP_STEPS:
            batch_obs, batch_action, batch_reward, batch_next_obs, batch_terminal = rpm.sample_batch(
                BATCH_SIZE)
            # 学习,注意这里的learn没有返回值
            agent.learn(batch_obs, batch_action, batch_reward, batch_next_obs,
                        batch_terminal)
    return episode_reward, episode_steps

5. 网络模型mujoco_model.py

5.1 Actor_Model

动作网络是最简单的两层线性+Relu网络。

class Actor(parl.Model):
    def __init__(self, obs_dim, action_dim):
        super(Actor, self).__init__()

        self.l1 = nn.Linear(obs_dim, 256)
        self.l2 = nn.Linear(256, 256)
        self.mean_linear = nn.Linear(256, action_dim)
        self.std_linear = nn.Linear(256, action_dim)

    def forward(self, obs):
        x = F.relu(self.l1(obs))
        x = F.relu(self.l2(x))

        act_mean = self.mean_linear(x)
        act_std = self.std_linear(x)
        act_log_std = paddle.clip(act_std, min=LOG_SIG_MIN, max=LOG_SIG_MAX)
        return act_mean, act_log_std

5.2 Critic_Model

策略网络是双Q网络,三层线性+两层Relu

class Critic(parl.Model):
    def __init__(self, obs_dim, action_dim):
        super(Critic, self).__init__()

        # Q1 network
        self.l1 = nn.Linear(obs_dim + action_dim, 256)
        self.l2 = nn.Linear(256, 256)
        self.l3 = nn.Linear(256, 1)

        # Q2 network
        self.l4 = nn.Linear(obs_dim + action_dim, 256)
        self.l5 = nn.Linear(256, 256)
        self.l6 = nn.Linear(256, 1)

    def forward(self, obs, action):
        x = paddle.concat([obs, action], 1)

        # Q1
        q1 = F.relu(self.l1(x))
        q1 = F.relu(self.l2(q1))
        q1 = self.l3(q1)

        # Q2
        q2 = F.relu(self.l4(x))
        q2 = F.relu(self.l5(q2))
        q2 = self.l6(q2)
        return q1, q2

2.2 模拟汽车驾驶场景(CARLA环境)

1.CARLA环境下载

2.创建解压缩目录(因为下载的文件是散开的)

mkdir CARLA_0.9.6

tar -zxf CARLA_0.9.6.tar.gz -C CARLA_0.9.6

3.编辑~/.bashrc,增加下面这一行:

export PYTHONPATH="${YOUR_FLODER}/CARLA_0.9.6/PythonAPI/carla/dist/carla-0.9.6-py3.5-linux-x86_64.egg:$PYTHONPATH"

4.克隆飞桨PARL帮我们对接好的python操作CARLA游戏项目源码并安装:

$ git clone https://github.com/ShuaibinLi/gym_carla.git
$ cd gym_carla
$ pip install -r requirements.txt
$ pip install -e .

5.检查是否安装成功:

pip list | grep gym_carla

如果报remote的类找不到gym_carla, 请执行pip install -e . 然后再重新启动XPARL。

6. 分别开启多个游戏进程

编写shell脚本开启多游戏进程:

DISPLAY= ./CarlaUE4.sh -opengl -carla-port=2021 &
sleep 10
...

7. 启动XPARL做并行训练

8.进入PARL项目的examples/CARLA_SAC目录并执行:

开启了10个无窗口游戏环境:8个做训练用,1个做评估用,1个做测试用。如下图:

主要代码逻辑如下:


def main():
    # 连接XPARL集群
    parl.connect('localhost:8080')
    ...
    # 创建多个游戏环境
    env_list = ParallelEnv(EnvConfig['env_name'], train_envs_params)
    ...
    # 三件套
    model = CarlaModel(obs_dim, action_dim)
    algorithm = SAC(
        model,
        gamma=GAMMA,
        tau=TAU,
        alpha=ALPHA,
        actor_lr=ACTOR_LR,
        critic_lr=CRITIC_LR)
    agent = CarlaAgent(algorithm)

    # rpm,参考Q-Learning
    rpm = ReplayMemory(
        max_size=MEMORY_SIZE, obs_dim=obs_dim, act_dim=action_dim)

    
    # 重置一下环境
    obs_list = env_list.reset()
    # 开启循环训练 
    while total_steps < args.train_total_steps:
        action_list = [agent.sample(obs) for obs in obs_list]
        next_obs_list, reward_list, done_list, info_list = env_list.step(action_list)
        ...
            # 循环写入rpm
            if not info_list[i]['timeout']:
                rpm.append(obs_list[i], action_list[i], reward_list[i],next_obs_list[i], done_list[i])
        ...
        # 暖机后开始学习
        batch_obs, batch_action, batch_reward, batch_next_obs, batch_terminal = rpm.sample_batch(BATCH_SIZE)
        agent.learn(batch_obs, batch_action, batch_reward, batch_next_obs,batch_terminal)
        ...
        # 保存agent,这一步是SAC算法特别的地方
        if total_steps > int(1e5) and total_steps > last_save_steps + int(1e4):
            agent.save('./model/step_{}_model.ckpt'.format(total_steps))
            last_save_steps = total_steps

训练结果

评估结果

进入CARLA主目录,执行下面命令,开启游戏窗口,评估模型:

$ ./CarlaUE4.sh -windowed -carla-port=2039

运行PARL/examples/CARLA_SAC/evaluate.py, 效果如下

注意Ubuntu录制的mp4因为格式细节问题,在Windows和Mac系统打不开,需要使用以下命令转换:

ffmpeg -y -i input_file.mp4 -c:v libx264 -c:a aac -strict experimental -tune fastdecode -pix_fmt yuv420p -b:a 192k -ar 48000 output_file.mp4