模仿学习–行为克隆

1.模仿学习

模仿学习(imitation learning)不是强化学习,而是强化学习的一种替代品。模仿学习与强化学习有相同的目的:两者的目的都是学习策略网络,从而控制智能体。模仿学习与强化学习有不同的原理:模仿学习向人类专家学习,目标是让策略网络做出的决策与人类专家相同;而强化学习利用环境反馈的奖励改进策略,目标是让累计奖励(即回报)最大化。

虽然强化学习不需要有监督学习中的标签数据,但它十分依赖奖励函数的设置。有时在奖励函数上做一些微小的改动,训练出来的策略就会有天差地别。在很多现实场景中,奖励函数并未给定,或者奖励信号极其稀疏,此时随机设计奖励函数将无法保证强化学习训练出来的策略满足实际需要。例如,对于无人驾驶车辆智能体的规控,其观测是当前的环境感知恢复的 3D 局部环境,动作是车辆接下来数秒的具体路径规划,那么奖励是什么?如果只是规定正常行驶而不发生碰撞的奖励为+1,发生碰撞为-100,那么智能体学习的结果则很可能是找个地方停滞不前。具体能帮助无人驾驶小车规控的奖励函数往往需要专家的精心设计和调试。

假设存在一个专家智能体,其策略可以看成最优策略,我们就可以直接模仿这个专家在环境中交互的状态动作数据来训练一个策略,并且不需要用到环境提供的奖励信号。模仿学习(imitation learning)研究的便是这一类问题,在模仿学习的框架下,专家能够提供一系列状态动作对,表示专家在环境下做出了的动作,而模仿者的任务则是利用这些专家数据进行训练,无须奖励信号就可以达到一个接近专家的策略。

目前常见的模仿学习的方法基本上可以分为 3 类:

  1. 行为克隆(behavior cloning,BC)
  2. 逆强化学习(inverse reinforcement learning,IRL)
  3. 生成式对抗模仿学习(generative adversarial imitation learning,GAIL)

其中逆向强化学习(inverse reinforcement learning,缩写 IRL)非常有名,但由于其计算复杂度较高,在今天已经不常用了。

2.行为克隆

2.1 简介

  • 行为克隆(behavior cloning,BC)是最简单的模仿学习。行为克隆的目的是模仿人的动作,学出一个策略网络。虽然行为克隆的目的与强化学习中的策略学习类似,但是行为克隆的本质是监督学习(分类或者回归),而不是强化学习。行为克隆通过模仿人类专家的动作来学习策略,而强化学习则是从奖励中学习策略。

  • 行为克隆需要一个事先准备好的数据集,由(状态,动作)这样的二元组构成,记作:
    B = { ( s 1 , a 1 ) ⋯ ( s n , a n ) } B=\left\{ \left( s_1,a_1 \right) \cdots \left( s_n,a_n \right) \right\} B={(s1,a1)(sn,an)}
    其中 s j s_j sj 是一个状态,而对应的 a j a_j aj是人类专家基于状态 s j s_j sj做出的动作。可以把 s j s_j sj s j s_j sj分别视作监督学习中的输入和标签。

  • 行为克隆的学习目标为:
    θ ∗ = a r g min ⁡ θ E ( s , a )   B [ L ( π θ ( s ) , a ) ] \theta *=\underset{\theta}{arg\min}E_{\left( s,a \right) ~B}\left[ L\left( \pi _{\theta}\left( s \right) ,a \right) \right] θ=θargminE(s,a) B[L(πθ(s),a)]
    其中,是 B B B专家的数据集,是 L L L对应监督学习框架下的损失函数。若动作是离散的,该损失函数可以是最大似然估计得到的。若动作是连续的,该损失函数可以是均方误差函数。

2.2 应用

在训练数据量比较大的时候,BC能够很快地学习到一个不错的策略。例如,围棋人工智能AlphaGo就是首先在16万盘棋局的3000万次落子数据中学习人类选手是如何下棋的,仅仅凭这个行为克隆方法,AlphaGo的棋力就已经超过了很多业余围棋爱好者。由于BC的实现十分简单,因此在很多实际场景下它都可以作为策略预训练的方法。BC能使得策略无须在较差时仍然低效地通过和环境交互来探索较好的动作,而是通过模仿专家智能体的行为数据来快速达到较高水平,为接下来的强化学习创造一个高起点。

2.3 局限

BC也存在很大的局限性,该局限在数据量比较小的时候犹为明显。具体来说,由于通过BC学习得到的策略只是拿小部分专家数据进行训练,因此BC只能在专家数据的状态分布下预测得比较准。然而,强化学习面对的是一个序贯决策问题,通过BC学习得到的策略在和环境交互过程中不可能完全学成最优,只要存在一点偏差,就有可能导致下一个遇到的状态是在专家数据中没有见过的。此时,由于没有在此状态(或者比较相近的状态)下训练过,策略可能就会随机选择一个动作,这会导致下一个状态进一步偏离专家策略遇到的的数据分布。最终,该策略在真实环境下不能得到比较好的效果,这被称为行为克隆的复合误差(compounding error)问题。

3. 代码实践

3.1 PPO算法生成专家数据

行为克隆需要专家数据,我们使用基于CartPole-v0环境训练好的ppo算法网络生成一些专家数据。详细的训练过程可参考:【强化学习】PPO算法

3.1.1 加载环境
import paddle
import paddle.nn.functional as F
import paddle.nn as nn
import gym
import matplotlib.pyplot as plt
from matplotlib import animation
from tqdm import tqdm
import numpy as np
import random
3.1.2 定义策略网络

我们并不需要价值网络,仅使用策略网络即可。

class PolicyNet(paddle.nn.Layer):
    def __init__(self, state_dim, hidden_dim, action_dim):
        super(PolicyNet, self).__init__()
        self.fc1 = paddle.nn.Linear(state_dim, hidden_dim)
        self.fc2 = paddle.nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return F.softmax(self.fc2(x))
3.1.3 定义专家数据生成函数

使用策略网络进行预测,选取可能性最大的动作。

def sample_expert_data(n_episode,env,model):
    states = []
    actions = []
    for episode in range(n_episode):
        state = env.reset()
        done = False
        while not done:
            state=paddle.to_tensor(state,dtype='float32')
            action = model(state)
            action=np.argmax(action.numpy())
            states.append(state)
            actions.append(action)
            next_state, reward, done, _ = env.step(action)
            state = next_state
    
    return np.array(states), np.array(actions)
3.1.4 定义环境,获取数据
n_episode = 1 # 该参数控制使用几个episode的专家数据
env_name='CartPole-v0'
env=gym.make(env_name)
state_dim=env.observation_space.shape[0]
action_dim=env.action_space.n
hidden_dim=128

actor=PolicyNet(state_dim,hidden_dim,action_dim)
layer_state_dict = paddle.load("net.pdparams")
actor.set_state_dict(layer_state_dict)
expert_s, expert_a = sample_expert_data(n_episode,env,actor)
print("专家数据量:",expert_s.shape[0])
专家数据量: 200

3.2 行为克隆

在 BC 中,我们将专家数据中的中的视为标签,BC 则转化成监督学习中经典的分类问题,采用最大似然估计的训练方法可得到分类结果。

3.2.1 定义行为克隆类

CartPole-v0环境是离散的动作,使用最大似然估计计算loss

# 该函数是torch.gather()的paddle版本
# 函数功能:沿指定的轴(axis)收集值
def paddle_gather(x, axis, index):
    index_shape = index.shape
    index_flatten = index.flatten()
    if axis < 0:
        axis = len(x.shape) + axis
    nd_index = []
    for k in range(len(x.shape)):
        if k == axis:
            nd_index.append(index_flatten)
        else:
            reshape_shape = [1] * len(x.shape)
            reshape_shape[k] = x.shape[k]
            x_arange = paddle.arange(x.shape[k], dtype=index.dtype)
            x_arange = x_arange.reshape(reshape_shape)
            axis_index = paddle.expand(x_arange, index_shape).flatten()
            nd_index.append(axis_index)
    ind2 = paddle.transpose(paddle.stack(nd_index), [1, 0]).astype("int64")
    paddle_out = paddle.gather_nd(x, ind2).reshape(index_shape)
    return paddle_out
class BehaviorClone:
    def __init__(self, state_dim, hidden_dim, action_dim, lr):
        self.policy = PolicyNet(state_dim, hidden_dim, action_dim)
        self.optimizer = paddle.optimizer.Adam(parameters=self.policy.parameters(),learning_rate=lr)

    def learn(self, states, actions):
        states = paddle.to_tensor(states, dtype="float32")
        actions = paddle.to_tensor(actions).reshape([-1, 1]) 
        log_probs = paddle.log(paddle_gather(self.policy(states),1,actions)) 
        bc_loss = paddle.mean(-log_probs)  # 最大似然估计

        self.optimizer.clear_grad()
        bc_loss.backward()
        self.optimizer.step()

    def take_action(self, state):
        state = paddle.to_tensor([state], dtype="float32")
        probs = self.policy(state)
        action_dist = paddle.distribution.Categorical(probs)
        action = action_dist.sample([1])
        return action.numpy()[0][0]
3.2.2 定义测试类
def test_agent(agent, env, n_episode):
    return_list = []
    for episode in range(n_episode):
        episode_return = 0
        state = env.reset()
        done = False
        while not done:
            action = agent.take_action(state)
            next_state, reward, done, _ = env.step(action)
            state = next_state
            episode_return += reward
        return_list.append(episode_return)
    return np.mean(return_list)
3.2.3 行为克隆实践
lr = 1e-3
bc_agent = BehaviorClone(state_dim, hidden_dim, action_dim, lr)
n_iterations = 1000
batch_size = 32
test_returns = []

with tqdm(total=n_iterations, desc="进度条") as pbar:
    for i in range(n_iterations):
        sample_indices = np.random.randint(low=0,
                                           high=expert_s.shape[0],
                                           size=batch_size)
        bc_agent.learn(expert_s[sample_indices], expert_a[sample_indices])
        current_return = test_agent(bc_agent, env, 5)
        test_returns.append(current_return)
        if (i + 1) % 10 == 0:
            pbar.set_postfix({'return': '%.3f' % np.mean(test_returns[-10:])})
        pbar.update(1)
进度条: 100%|██████████| 1000/1000 [02:16<00:00,  7.33it/s, return=62.780]

█████████| 1000/1000 [02:16<00:00, 7.33it/s, return=62.780]

3.2.4 绘制行为克隆的奖励曲线
iteration_list = list(range(len(test_returns)))
plt.plot(iteration_list, test_returns)
plt.xlabel('Iterations')
plt.ylabel('Returns')
plt.title('BC on {}'.format(env_name))
plt.show()

在这里插入图片描述

4 总结

  • 本项目简单介绍了一下行为克隆的概念,并给出了简单是代码示例。
  • 本项目使用的专家是训练好的PPO算法网络中的策略网络,训练过程参见【强化学习】PPO算法
  • 本项目使用的环境是CartPole-v0
  • 行为克隆是监督学习。
  • 行为克隆可以作为强化学习的预训练策略。
  • 行为克隆存在复合误差问题。

声明:

① 本项目参考了一些网络资源,如有不妥,请及时联系,侵删。

② 本项目在整理中可能存在表述或者代码错误,如有发现,可留言,后续会修正。

③ 本人小白一只,欢迎大家一起交流有关强化学习的内容呀!!!

此文章为搬运
原项目链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐