【飞桨开发者说】吉祥:黑龙江哈尔滨人,毕业于桂林理工大学,检测技术与自动化装置专业,百度深度学习工程师集训营最佳学员,百度强化学习7日学员php
2019年Dota 2比赛中,OpenAI Five完胜世界冠队伍OG。python
Bill Gates在Twitter中提到:“AI机器人在Dota 2中打败人类,是AI发展的一个里程碑事件”。git
不管AlphaGo击败围棋世界冠军仍是OpenAI Five完胜世界冠队伍OG,都让大众感觉到了AI的魅力,促进了AI蓬勃发展。github
那么OpenAI Five应用了什么技术,实如今Dota中完胜世界冠军的呢?那就是Proximal Policy Optimization(PPO)算法,本文重点介绍基于飞桨PARL,完成PPO算法实践的过程。算法
飞桨PARL开源项目地址:网络
https://github.com/PaddlePaddle/PARL架构
PPO算法将游戏中全部的状态做为数据输入,经过数据分析计算,由PPO优化的智能体Agent作出相应的动做,智能体Agent的思考过程以下图所示。app
在介绍PPO算法以前,咱们先了解深度强化学习中基于策略梯度的Policy Gradient算法(如下简称PG)。框架
PG算法介绍dom
PG算法的基本组成以下图所示:
Actor:由智能体Agent产生的执行动做;
Env:智能体Agent的执行环境;
Reward Function:奖励计算方式,执行动做的评价指标。
智能体Agent在Env中不断学习,根据环境的状态State(也能够为观察到的observation,下文统一用State)来执行动做Action,过程当中会根据反馈的Reward来选择效果更好的动做,实现逻辑以下图所示:
采用神经网络拟合策略函数,经过计算策略梯度来优化策略网络;
经过环境产生的状态State矩阵或者向量Vector做为神经网络的输入,经过神经网络获得每一个执行动做的几率,选择几率最大的执行动做。
计算原理以下图所示:
在环境Env中获得状态State,Agent经过State得出使得Reward最大的执行动做Actor;
Actor在环境中又获得新的State(next state) ;
重复以上动做,直到Reward Fuction不成立或者达到此次循环终止条件。此时能够计算出一个轨迹的发生几率(一个episode的发生几率)。
在优化神经网络时,除了须要向网络输入数据外,还要给网络一个指望的输出(label)。
优化的目标是策略的指望回报,即全部轨迹的回报与对应轨迹发生几率的加权和。当N足够大时,经过采样N个轨迹求平均的方式近似表达,简单理解就是所得Reward的平均值。每次都让网络来拟合以前Reward的平均值,一般优化策略越好,Reward会越高,不过也不是绝对的。
计算策略梯度公式以下图所示:
首先用函数求导的特色进行转化;而后用N次采样的平均值来近似指望;最后将展开,将与无关的项去掉,即获得最终的结果。
关于PG的经验之谈
PG是基于策略梯度求解RL的方法,按照几率分布随机选择动做,计算某个状态下全部策略的分布几率,相似于经典分类问题中,给每一个类别预测一个几率,好的PG会给优良的策略分配较高的几率。
但它同时也存在一个缺点:优化是在一个完整的episode结束后,sample每一步的均值。只要最后结果很好,全部的动做所有看成好的动做来学习,所以在训练时极可能出现不稳定的状况,须要大量的数据集,保证足够多的sample次数。
Tip 1:增长基线,使得Reward有正有负。
Tip 2:分配适当的权重,选择在这个动做发生后的Reward总和做为动做的权重。
Tip 3:增长折扣因子。
PPO算法介绍
先了解强化学习经常使用的两种训练方式:On-policy和Off-policy。
On-policy:训练的agent一边互动一边学习(互动的agent就是训练的agent);
Off-policy:训练的agent一边看一边学习(互动的agent不是训练的agent)。
举例来讲,古代杰出帝王都须要深刻了解民间百姓的真实生活,这两种策略就相似帝王们获取信息的途径。皇帝能够选择微服出巡(On-policy),虽然眼见为实,但毕竟皇帝本人分身乏术,掌握状况不全;也能够选择派不一样的官员去了解状况,再向皇帝汇报(Off-policy)。
在网络训练过程当中,经过采样更新权重很是耗时,每更新一个参数就须要采样一次,所以咱们须要把On-policy训练方式转化成Off-policy。
从中采样出来的数据的指望值,能够替换为用采样出来的数据再乘以重要性权重。
值得注意的是,须要分布与分布差别很小才能采用这样的方式,如图所示。若当分布与分布相差较大时,须要足够多采样次数才能使他们的指望相近。
的指望和的指望最后相差的就是一个的系数(重要性权重),若是≈,那么他们的指望就相同(这里省略推导过程)。
PPO解决了On-policy转Off-policy时分布与分布相差较大的问题。经过KL散度来计算分布与分布差别,并将KL加入PPO模型的似然函数,并采用合理适配β来惩罚KL。KL过大咱们就增长β,KL小于必定值咱们就减少β,PPO算法过程以下图所示:
说明:在几率论或信息论中,一般使用KL散度( Kullback–Leibler divergence),又称相对熵(relative entropy),描述两个几率分布和差别。
Talk is cheap. Show me the code.
基于飞桨PARL实践PPO算法
下面咱们基于飞桨PARL框架,动手实践PPO算法,介绍实现CarPole和四轴飞行器悬浮任务的操做过程。
项目可在百度AI Studio平台上运行:
https://aistudio.baidu.com/aistudio/projectdetail/632270
基于飞桨PARL,使用PPO解决连续控制版本的CartPole问题,给小车一个力(连续量),使得车上的摆杆倒立起来。
1. 构建CartPole任务的Model、Algorithm和Agent。
构建PPO Model,须要声明Policy,定义Value的Model结构:
class PolicyModel(parl.Model): def __init__(self, obs_dim, act_dim, init_logvar): self.obs_dim = obs_dim self.act_dim = act_dim hid1_size = obs_dim * 10 hid3_size = act_dim * 10 hid2_size = int(np.sqrt(hid1_size * hid3_size)) self.lr = 9e-4 / np.sqrt(hid2_size) self.fc1 = layers.fc(size=hid1_size, act='tanh') self.fc2 = layers.fc(size=hid2_size, act='tanh') self.fc3 = layers.fc(size=hid3_size, act='tanh') self.fc4 = layers.fc(size=act_dim, act='tanh') self.logvars = layers.create_parameter( shape=[act_dim], dtype='float32', default_initializer=fluid.initializer.ConstantInitializer( init_logvar)) def policy(self, obs): #策略 hid1 = self.fc1(obs) hid2 = self.fc2(hid1) hid3 = self.fc3(hid2) means = self.fc4(hid3) logvars = self.logvars() return means, logvars def sample(self, obs): #采样 means, logvars = self.policy(obs) sampled_act = means + ( layers.exp(logvars / 2.0) * # stddev layers.gaussian_random(shape=(self.act_dim, ), dtype='float32')) return sampled_act class ValueModel(parl.Model): def __init__(self, obs_dim, act_dim): super(ValueModel, self).__init__() hid1_size = obs_dim * 10 hid3_size = 5 hid2_size = int(np.sqrt(hid1_size * hid3_size)) self.lr = 1e-2 / np.sqrt(hid2_size) self.fc1 = layers.fc(size=hid1_size, act='tanh') self.fc2 = layers.fc(size=hid2_size, act='tanh') self.fc3 = layers.fc(size=hid3_size, act='tanh') self.fc4 = layers.fc(size=1) def value(self, obs): hid1 = self.fc1(obs) hid2 = self.fc2(hid1) hid3 = self.fc3(hid2) V = self.fc4(hid3) V = layers.squeeze(V, axes=[]) return V
构建Agent:
class PPOAgent(parl.Agent): def __init__(self,algorithm,obs_dim,act_dim,kl_targ,loss_type,beta=1.0,epsilon=0.2,policy_learn_times=20,value_learn_times=10,value_batch_size=256): 参数初始化(略) def build_program(self): #在静态图下构建program,定义图的输入和输出 self.policy_predict_program = fluid.Program() self.policy_sample_program = fluid.Program() self.policy_learn_program = fluid.Program() self.value_predict_program = fluid.Program() self.value_learn_program = fluid.Program() def policy_sample(self, obs): #经过网络推理获得sample return sampled_act def policy_predict(self, obs): #经过网络推理获得predict return means def value_predict(self, obs): #经过网络推理获得value_predict return value #用ppo算法更新policy def policy_learn(self, obs, actions, advantages): self.alg.sync_old_policy() all_loss, all_kl = [], [] for _ in range(self.policy_learn_times): loss, kl = self._batch_policy_learn(obs, actions, advantages) all_loss.append(loss) all_kl.append(kl) if self.loss_type == 'KLPEN': # Adative KL penalty coefficient if kl > self.kl_targ * 2: self.beta = 1.5 * self.beta elif kl < self.kl_targ / 2: self.beta = self.beta / 1.5 return np.mean(all_loss), np.mean(all_kl) #用ppo算法更新value def value_learn(self, obs, value): data_size = obs.shape[0] if self.value_learn_buffer is None: obs_train, value_train = obs, value else: obs_train = np.concatenate([obs, self.value_learn_buffer[0]]) value_train = np.concatenate([value, self.value_learn_buffer[1]]) self.value_learn_buffer = (obs, value) all_loss = [] for _ in range(self.value_learn_times): random_ids = np.arange(obs_train.shape[0]) np.random.shuffle(random_ids) shuffle_obs_train = obs_train[random_ids] shuffle_value_train = value_train[random_ids] start = 0 while start < data_size: end = start + self.value_batch_size value_loss = self._batch_value_learn( shuffle_obs_train[start:end, :], shuffle_value_train[start:end]) all_loss.append(value_loss) start += self.value_batch_size return np.mean(all_loss)
构建PPO Algorithm,此段代码无需运行,便于你们理解PPO整个过程:
class PPO(Algorithm): def __init__(self,model,act_dim=None,policy_lr=None,value_lr=None,epsilon=0.2): 模型初始化略() def _calc_logprob(self, actions, means, logvars): exp_item = layers.elementwise_div( layers.square(actions - means), layers.exp(logvars), axis=1) exp_item = -0.5 * layers.reduce_sum(exp_item, dim=1) vars_item = -0.5 * layers.reduce_sum(logvars) logprob = exp_item + vars_item return logprob #计算KL def _calc_kl(self, means, logvars, old_means, old_logvars): log_det_cov_old = layers.reduce_sum(old_logvars) log_det_cov_new = layers.reduce_sum(logvars) tr_old_new = layers.reduce_sum(layers.exp(old_logvars - logvars)) kl = 0.5 * (layers.reduce_sum( layers.square(means - old_means) / layers.exp(logvars), dim=1) + ( log_det_cov_new - log_det_cov_old) + tr_old_new - self.act_dim) return kl def policy_learn(self, obs, actions, advantages, beta=None): old_means, old_logvars = self.old_policy_model.policy(obs) old_means.stop_gradient = True old_logvars.stop_gradient = True old_logprob = self._calc_logprob(actions, old_means, old_logvars) means, logvars = self.model.policy(obs) logprob = self._calc_logprob(actions, means, logvars) kl = self._calc_kl(means, logvars, old_means, old_logvars) kl = layers.reduce_mean(kl)
2. 模型训练
def main(): env = ContinuousCartPoleEnv() obs_dim = env.observation_space.shape[0] act_dim = env.action_space.shape[0] obs_dim += 1 # add 1 to obs dim for time step feature 应该是为了方便引入衰减因子 scaler = Scaler(obs_dim) model = PPOModel(obs_dim, act_dim) alg = parl.algorithms.PPO( model, act_dim=act_dim, policy_lr=model.policy_lr, value_lr=model.value_lr) agent = PPOAgent( alg, obs_dim, act_dim, kl_targ, loss_type=loss_type) # 运行几个episode来初始化 scaler collect_trajectories(env, agent, scaler, episodes=5) test_flag = 0 total_steps = 0 while total_steps < train_total_steps: trajectories = collect_trajectories( env, agent, scaler, episodes=episodes_per_batch) total_steps += sum([t['obs'].shape[0] for t in trajectories]) total_train_rewards = sum([np.sum(t['rewards']) for t in trajectories]) #产生训练数据 train_obs, train_actions, train_advantages, train_discount_sum_rewards = build_train_data( trajectories, agent) #计算policy_loss, kl policy_loss, kl = agent.policy_learn(train_obs, train_actions, train_advantages) value_loss = agent.value_learn(train_obs, train_discount_sum_rewards) if total_steps // test_every_steps >= test_flag: while total_steps // test_every_steps >= test_flag: test_flag += 1 eval_reward = run_evaluate_episode(env, agent, scaler) [07-23 12:00:44 MainThread @<ipython-input-7-710321de7941>:188] Steps 1001984, Evaluate reward: 23285.0
3. 实践效果
在CartPole环境下, Evaluate reward一直在上涨,达到了23285.0(训练过多可能会出现不稳定的状况)。
完整项目地址:
https://aistudio.baidu.com/aistudio/projectdetail/632270
实践二:四轴飞行器悬浮任务。
基于飞桨PARL,使用PPO实现四轴飞行器悬浮任务。您只须要获取以下文件,并在终端安装parl和rlschool后,运行python train.py,便可查看实践效果。
mujoco_model.py
mujoco_agent.py
scaler.py
train.py
.py文件获取路径:
https://aistudio.baidu.com/aistudio/projectdetail/632270
实践效果
在rlschool环境下 四轴飞行器悬浮任务 Evaluate reward一直在上涨,达到了7107。
完整项目地址:
https://aistudio.baidu.com/aistudio/projectdetail/632270
文章小结
在文章开篇和你们一块儿学习了PG(Policy Gradient)算法是如何解决连续动做空间上求解RL, PG在更新策略时的优缺点及相应的避“坑儿”技巧,如:增长基线、合理适配action的权重、增长代价因子。
接下来介绍了强化学习经常使用的两种模型训练方式:On-policy与Off-policy,并引出PPO算法。
最后基于飞桨PARL框架,动手实践了PPO算法,完成了CarPole和四轴飞行器悬浮两个任务。
飞桨PaddlePaddle课程连接
百度架构师手把手教深度学习:
https://aistudio.baidu.com/aistudio/course/introduce/888
强化学习7日打卡营:
https://aistudio.baidu.com/aistudio/course/introduce/1335
如在使用过程当中有问题,可加入飞桨官方QQ群进行交流:1108045677。
若是您想详细了解更多飞桨的相关内容,请参阅如下文档。
官网地址:
https://www.paddlepaddle.org.cn
飞桨PARL开源项目地址:
https://github.com/PaddlePaddle/PARL
飞桨开源框架项目地址:
GitHub:
https://github.com/PaddlePaddle/Paddle
Gitee:
https://gitee.com/paddlepaddle/Paddle
END