CS285_Lecture05_Policy_Gradient

本节课的目标是搞明白Policy gradient一类的REINFORCEMENT方法,并且理解背后的局限性,然后知道为什么textbook很容易讲清楚,但是在实践中不行的原因。之后从Causality和Baseline两种方法来降低On- policy PG的方差。并给出结合IS(important sampling)的Off policy的梯度方法。最后利用代码实现RL里面的hello world—CartPole-v0来实现。

01 什么是Policy gradients

1.1 PG的梯度函数的下载

回顾DRL的目标,我们需要的是增加累积奖励的期望:

(目标函数)max\ E_{\tau-p_\theta(\tau)}[\Sigma_tr(s_t,a_t)]

根据(st,at)(s_t,a_t) 对的马尔可夫性质,(在有限的情况下finite horizon case)下简化成为

max Σt=1TE(st,at)pθ(st,at)[r(st,at)]max\ \Sigma_{t=1}^TE_{(s_t,a_t)-p_\theta(s_t,a_t)}[r(s_t,a_t)]

因此我们可以根据πθ\pi_\theta采样N个τ\tau,然后求得期望为

(采样后的目标函数)J(\theta)=\frac{1}{N}\Sigma_i\Sigma_tr(s_{i,t},a_{i,t})

这个式子的重要意义在于它直接给我们反应当前policy的优劣,也指明了我们需要前进的方向,就是最大化这个期望,虽然有的时候这个式子并不是容易计算。但是请把哪些问题先放放,直观的最大化这个问题,也就是用梯度提升的方法:

J(\theta)=\int p_\theta(\tau)r(\tau)d\tau(上式简写)

那么对参数求导的结果就是;同时由于我们无法直接取值函数,我们通常需要转换为期望的方式来利用sampling—expectation的方式来求期望

(目标函数梯度)\nabla_\theta J(\theta)=\int \nabla_\theta p_\theta(\tau)*r(\tau)d\tau)\\=\int p_\theta(\tau)*\nabla_\theta logp_\theta(\tau)*r(\tau)d\tau\\=E_{\tau-p_\theta(\tau)}[\nabla_\theta logp_\theta(\tau)*r(\tau)]

我们对上面的目标函数更进一步的探讨;首先疑问就是什么是pθ(τ)p_\theta(\tau),在MDP的state- observation-action- state转移中我们可以认识到

pθ(τ)=p(s1)πθ(atst)p(st+1st,at)p_\theta(\tau)=p(s_1)\prod \pi_\theta(a_t|s_t)p(s_{t+1}|s_t,a_t)

所以可以很轻松的替换掉上述

logpθ(τ)=logp(s1)+Σlogπθ(atst)+logp(st+1st,at)logp_\theta(\tau)=logp(s_1)+\Sigma log\pi_\theta(a_t|s_t)+logp(s_{t+1}|s_t,a_t)

假设我们求导的化,那么目标函数梯度就会变成

θJ(θ)=Eτpθ(τ)[(Σt=1Tθlogπθ(atst))(Σt=1Tr(st,at))]\nabla_\theta J(\theta)=E_{\tau-p_\theta(\tau)}[(\Sigma_{t=1}^T \nabla_\theta log \pi_\theta(a_t|s_t))(\Sigma_{t=1}^T r(s_t,a_t))]

Stop to think : just like before we evaluated our reinforcement learning objective by generating samples by actually running our policy in the world to get an estimate of an exception

这样情况下我们就可以得到采样的后的梯度计算:

θJ(θ)=1NΣi=1N[Σt=1Tθlogπθ(ai,tsi,t)][Σt=1Tr(si,t,ai,t)]\nabla_\theta J(\theta)=\frac{1}{N}\Sigma_{i=1}^N[\Sigma_{t=1}^T \nabla_\theta log \pi_\theta(a_{i,t}|s_{i,t})][\Sigma_{t=1}^T r(s_{i,t},a_{i,t})]

虽然我们成功得到了,我们如何从采样结果中得到如何计算梯度;但是我们需要对分析它具体的含义。

1.2 理解PG含义

我们可以看到损失函数内部主要有两部分组成,第一部分是有梯度的东西,第二部份是奖励的求和;那么对于两项分别分析,可以发现第一部分梯度与简单的MLE类似,也就是直接的梯度下降,但是由于有奖励的加权,导致我们的结果是:

good stuff is made more likely;bad stuff is made less likely(增加好的可能性,降低坏的可能性),也标记为“trial and error”

对于中间的一项,θlogπθ(ai,tsi,t)\nabla_\theta log \pi_\theta(a_{i,t}|s_{i,t}),假设我们用一个神经网络来进行拟合,假如采用Gaussian policies;也就是我们的模型假设是

logπθ(atst)=N(model(st);Σ)log\pi_\theta(a_t|s_t)=N(model(s_t);\Sigma)

那么我们的损失函数,根据MLE自然而然就可以得到(参考linear regression)

θJ(θ)=12Σ1(model(st)at)dfdθ\nabla_\theta J(\theta)=-\frac{1}{2}\Sigma^{-1}(model(s_t)-a_t)\frac{df}{d\theta}

这样我们就得到我们的REINFORCEMENT algorithm;这里缩写是针对:REward Increments=nonnegative FactorOffset Reinforcement Characteristic+Eligibility

image-20221026121451902

但是PG的问题在于:

  1. 我们是否可以在partial observation,也就是o不等于s情况下使用?
  2. 具有很大的方差,对于不同的reward取值结果大不相同。
    1. example:假设N=3;其中总的奖励分为与(一负,两正),那么更新后的策略会偏向于右边,假如是加上constant(三正),那么更新的策略会比偏向中间。这种不确定性导致最终结果的方差偏大

image-20221026121503952

0x02 如何降低方差一:Causality

在未来发生的事情并不会对现在的事情造成影响(policy at time b cannot affect reward at time a when a<b),因此在t之前的梯度不会对后面造成影响

所以我们的梯度需要:

θJ(θ)=1NΣi=1N[Σt=1Tθlogπθ(ai,tsi,t)][Σi=1Tr(si,t,ai,t)]\nabla_\theta J(\theta)=\frac{1}{N}\Sigma_{i=1}^N[\Sigma_{t=1}^T \nabla_\theta log \pi_\theta(a_{i,t}|s_{i,t})][\Sigma_{i=1}^T r(s_{i,t},a_{i,t})]

变成;注意后面计算奖励的下标!有效的证明过程可能需要在paper中才能看到

θJ(θ)=1NΣi=1N[Σt=1Tθlogπθ(ai,tsi,t)][Σt=tTr(si,t,ai,t)]\nabla_\theta J(\theta)=\frac{1}{N}\Sigma_{i=1}^N[\Sigma_{t=1}^T \nabla_\theta log \pi_\theta(a_{i,t}|s_{i,t})][\Sigma_{t'=t}^T r(s_{i,t'},a_{i,t'})]

通常的我们会将后面的记为:reward to go:

(reward\ to\ go) \hat Q_{i,t}=\Sigma_{t'=t}^Tr(s_{i,t'},a_{i,t'})

0x03 如何降低发差二:Baselines

首先直觉的感受不同reward的影响,还是之前的例子,如果reward=(-1,2,2)和(1,4,4)或者是(10000,10005,10005)这样计算的结果通常是不一样的。我们需要在其中减去一个baseline,相当于unbiased in expectation,很直观的我们是减去奖励的平均值,注意!!平均值不一定是最好的

\nabla_\theta J(\theta)=\int p_\theta(\tau)*\nabla_\theta logp_\theta(\tau)*(r(\tau)-b)d\tau\\

可以简单的证明减去一个baseline不会对梯度造成影响,但是会对梯度变化程度造成影响

E[θlogpθ(τ)b]=bθpθ(τ)dτ=bθ1=0E[\nabla_\theta logp_\theta(\tau)b]=b\nabla_\theta \int p_\theta(\tau)d \tau=b\nabla_\theta*1=0

除去平均值,我们会采取什么baseline来计算?我们的目标是降低模型的方差,那么最好的baseline就是让模型方差最低的,根据方差计算

Var[X]=E[X2]E[X]2Var[X]=E[X^2]-E[X]^2

那么

Var=E[(θlogpθ(τ)(r(τ)b))2]E2[θlogpθ(τ)(r(τ)b)]Var=E[(\nabla_\theta logp_\theta(\tau)*(r(\tau)-b))^2]-E^2[\nabla_\theta logp_\theta(\tau)*(r(\tau)-b)]

直接对方差Var的bias求导

dVardb=2E[θlogpθ(τ)2r(τ)]+2bE[θlogpθ(τ)2]\frac{dVar}{db}=-2E[\nabla_\theta logp_\theta(\tau)^2*r(\tau)]+2bE[\nabla_\theta logp_\theta(\tau)^2]

这样就可以得到最好的b值,但是通常在实践中很难计算,所以还君之好用

b=E[θlogpθ(τ)2r(τ)]E[θlogpθ(τ)2]b=\frac{E[\nabla_\theta logp_\theta(\tau)^2*r(\tau)]}{E[\nabla_\theta logp_\theta(\tau)^2]}

0x04 Off-policy Policy gradient

我们在之前的讨论中得到最终的梯度理论公式和采样公式,但是我们仔细思考这个过程,我们需要第i次采样得到完整τi\tau_i之后才能利用梯度提升更新policy,这样对于每次policy只是简单的更新一次梯度;这样的结果就是采样效率非常低。

image-20221026121521658

可以简单的引入 important sampling;来尝试解决这个问题,其理论过程是这样的

Exp(x)[f(x)]=p(x)f(x)dx=Exq(x)[p(x)q(x)f(x)]E_{x-p(x)}[f(x)]=\int p(x)f(x)dx=E_{x-q(x)}[\frac{p(x)}{q(x)}f(x)]

这样的好处是我们可以使用智能体旧策略pθ(τ)p_\theta(\tau) 来更新策略梯度的值,得到pθ(τ)p_{\theta'}(\tau),也就是目标函数会变成

J(θ)=Eτpθ(τ)[pθ(τ)pθ(τ)(r(τ)b)]J(\theta')=E_{\tau-p_\theta(\tau)}[\frac{p_{\theta'}(\tau)}{p_\theta(\tau)}(r(\tau)-b)]

之间的比值可以展开得到

pθ(τ)pθ(τ)=p(s1)πθ(atst)p(st+1st,at)p(s1)πθ(atst)p(st+1st,at)=πθ(atst)πθ(atst)\frac{p_{\theta'}(\tau)}{p_\theta(\tau)}=\frac{p(s_1)\prod\pi_\theta(a_t|s_t)p(s_{t+1}|s_t,a_t)}{p(s_1)\prod \pi_{\theta'}(a_t|s_t)p(s_{t+1}|s_t,a_t)}=\frac{\prod \pi_\theta(a_t|s_t)}{\prod\pi_{\theta'}(a_t|s_t)}

因此可以得到 Important sampling下的目标函数的期望

θJ(θ)=Eτpθ(τ)[pθ(τ)pθ(τ)θlogπθ(τ)(r(τ)b)]\nabla_{\theta'} J(\theta')=E_{\tau-p_\theta(\tau)}[\frac{p_{\theta'}(\tau)}{p_\theta(\tau)}\nabla_{\theta'}log\pi_{\theta'}(\tau)(r(\tau)-b)]

θJ(θ)=Eτpθ(τ)[πθ(atst)πθ(atst)θlogπθ(τ)(r(τ)b)]\nabla_{\theta'} J(\theta')=E_{\tau-p_\theta(\tau)}[\frac{\prod \pi_{\theta'}|(a_t|s_t)}{\prod\pi_{\theta}(a_t|s_t)}\nabla_{\theta'}log\pi_{\theta'}(\tau)(r(\tau)-b)]

继续我们之前的causality的说明,所以采样情况下上面展开得到

θJ(θ)=Eτpθ(τ)[Σt=1Tθlogπθ(atst)(t=1Tπθ(atst)πθ(atst))(Σt=tT(r(st,atb)(Σt=ttt=1Tπθ(atst)πθ(atst)))]\nabla_{\theta'} J(\theta')=E_{\tau-p_\theta(\tau)}[\Sigma_{t=1}^T \nabla_{\theta'}log\pi_{\theta'}(a_t|s_t)(\prod *{t'=1}^T\frac{\pi*{\theta'}(a_t'|s_t')}{\pi_{\theta}(a_t'|s_t')})(\Sigma_{t'=t}^T(r(s_{t'},a_{t'}-b)(\Sigma_{t''=t}^{t'}\prod *{t'=1}^T\frac{\pi*{\theta'}(a_t''|s_t'')}{\pi_{\theta}(a_t''|s_t'')}))]

通常reward to go里的分布会删掉,因此不影响结果(paper中有)

(Σt=ttt=1Tπθ(atst)πθ(atst))(\Sigma_{t''=t}^{t'}\prod *{t'=1}^T\frac{\pi*{\theta'}(a_t''|s_t'')}{\pi_{\theta}(a_t''|s_t'')})

同时可以改变策略的比值,这个在之后课程中会讲到,这里采用first- order approximation,

t=1Tπθ(si,t)πθ(si,t)πθ(atst)πθ(atst)=t=1Tπθ(at,st)πθ(at,st)\prod *{t'=1}^T\frac{\pi*{\theta'}(s_{i,t})}{\pi_\theta(s_{i,t})}\frac{\pi_{\theta'}(a_t'|s_t')}{\pi_{\theta}(a_t'|s_t')}=\prod *{t'=1}^T\frac{\pi*{\theta'}(a_{t'},s_t')}{\pi_{\theta}(a_t',s_t')}

因此得到Off-Policy Policy gradient with important sampling的目标函数梯度:

θJ(θ)=1NΣi=1N[Σt=1Tθlogπθ(atst)(t=1Tπθ(at,st)πθ(at,st))Q^i,t]\nabla_{\theta'} J(\theta')=\frac{1}{N}\Sigma_{i=1}^N[\Sigma_{t=1}^T \nabla_{\theta'}log\pi_{\theta'}(a_t|s_t)(\prod *{t'=1}^T\frac{\pi*{\theta'}(a_{t'},s_t')}{\pi_{\theta}(a_t',s_t')}) \hat Q_{i,t}]

作为对比之前的on- policy gradient是:

θJ(θ)=1NΣi=1N[Σt=1Tθlogπθ(ai,tsi,t)]Q^i,t\nabla_\theta J(\theta)=\frac{1}{N}\Sigma_{i=1}^N[\Sigma_{t=1}^T \nabla_\theta log \pi_\theta(a_{i,t}|s_{i,t})]\hat Q_{i,t}

0x05 代码实现

参考《动手学深度学习》

首先几个有趣的发现

  1. 在CartPole-v0成功的不一定能在MountainCar-v0中实现,这个与奖励函数的设置关系很大(小车没有上山之前都是-1,被打击到了)
  2. 不同的超参数对最终结果影响很大
  3. 推荐使用GYM 0.21版本;最新版本总会出问题

5.1 环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## 环境说明:Cartpole-v0 
我们采用的测试环境是 CartPole-v0,其状态空间相对简单,只有 4 个变量,因此网络结构
的设计也相对简单:采用一层 128 个神经元的全连接并以 ReLU 作为激活函数。当遇到更复
杂的诸如以图像作为输入的环境时,我们可以考虑采用深度卷积神经网络。
# 引入一些包
from pyvirtualdisplay import Display
display = Display(visible=0, size=(1400, 900))
display.start()

import matplotlib.pyplot as plt
from IPython import display
import time
from IPython import display
from PIL import Image
import gym
import torch
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm

5.2 定义Policy net

输入是state,输出是action。随便一个浅层MLP就够了

1
2
3
4
5
6
7
8
9
10
11
12
class PolicyNet(torch.nn.Module):
def __init__(self,state_dim,hidden_dim,action_dim):
super(PolicyNet,self).__init__()
self.fc1=torch.nn.Linear(state_dim,hidden_dim)
self.fc2=torch.nn.Linear(hidden_dim,action_dim)
for m in self.modules():
if isinstance(m, torch.nn.Linear):
torch.nn.init.normal_(m.weight, std=0.01)

def forward(self,x):
x=F.relu(self.fc1(x))
return F.softmax(self.fc2(x),dim=1)

5.3 定义算法

三步,首先sample generation、然后policy evaluate,之后是improve policy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from traitlets import observe

class REINFORCE:
def __init__(self,state_dim,hidden_dim,action_dim,learning_rate,gamma,device):
self.policy_net=PolicyNet(state_dim,hidden_dim,action_dim).to(device)
self.optimizer=torch.optim.Adam(self.policy_net.parameters(),lr=learning_rate)
self.gamma=gamma
self.device=device

def take_action(self,state):
# 根据动作概率分布随机采样
state=torch.tensor([state],dtype=torch.float).to(self.device)
probs=self.policy_net(state)
action_dist=torch.distributions.Categorical(probs)
action=action_dist.sample()
return action.item()

def update(self,transition_dict):
reward_list=transition_dict['rewards']
state_list=transition_dict['states']
action_list=transition_dict['actions']

G=0
self.optimizer.zero_grad()
for i in reversed(range(len(reward_list))):
# 从最后一步开始
state=torch.tensor([state_list[i]],dtype=torch.float).to(self.device)
reward=reward_list[i]
action=torch.tensor([action_list[i]]).view(-1,1).to(self.device)
log_prob=torch.log(self.policy_net(state).gather(1,action))
G=self.gamma*G+reward
loss=-log_prob*G
loss.backward()
self.optimizer.step()

5.3 开始实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
learning_rate=0.001
num_episodes=1000
hidden_dim=128
gamma=0.98
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')
env_name='CartPole-v0'
# env_name='MountainCar-v0'
# env_name='LunarLander-v2'
## 观察环境

env=gym.make(env_name)
env.seed(0)
print('state:{}'.format(env.observation_space.dtype))
print('action:{}'.format(env.action_space))
print('state shape:{}~{}'.format(env.observation_space.low,env.observation_space.high))
torch.manual_seed(0)
state_dim=env.observation_space.shape[0]
action_dim=env.action_space.n
agent=REINFORCE(state_dim,hidden_dim,action_dim,learning_rate,gamma,device)
return_list = []
for i in range(10):
with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:
for i_episode in range(int(num_episodes / 10)):
episode_return = 0
transition_dict = {
'states': [],
'actions': [],
'next_states': [],
'rewards': [],
'dones': []
}

state = env.reset()
# 清除当前 Cell 的输出
done = False
while not done:
action = agent.take_action(state)
next_state, reward, done, _ = env.step(action)
transition_dict['states'].append(state)
transition_dict['actions'].append(action)
transition_dict['next_states'].append(next_state)
# special for Mountain
# reward=(next_state[0]+1)**5
# if abs(next_state[1])>0.04:
# reward+=1
transition_dict['rewards'].append(reward)
transition_dict['dones'].append(done)
state = next_state
episode_return += reward
return_list.append(episode_return)
agent.update(transition_dict)
if (i_episode + 1) % 10 == 0:
pbar.set_postfix({
'episode':
'%d' % (num_episodes / 10 * i + i_episode + 1),
'return':
'%.3f' % np.mean(return_list[-10:])
})
pbar.update(1)

Untitled

假如设置三层网络结果有点奇怪,但是policy是好的

两层结果

三层神经网络,但是policy好的

5.4 观察

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
done = False
state = env.reset()
flag=1
# 防止刷新过快
while flag<100:
action = agent.take_action(state)
next_state, reward, done, _ = env.step(action)
state=next_state
# 清除当前 Cell 的输出
display.clear_output(wait=True)
# 渲染画面,得到画面的像素数组
rgb_array = env.render(mode='rgb_array')
# 使用像素数组生成图片
img = Image.fromarray(rgb_array)
# 当前 Cell 中展示图片
display.display(img)
time.sleep(1/24)
flag+=1

第一个是2层,第二是3层

视频可以看https://www.notion.so/clouddirty/Lecture-05-Policy-Gradients-9384f9b7ad1f49d5aa326f66b97f05eb#3d9149bbd9b642ce926f0df2f0bf62f0


CS285_Lecture05_Policy_Gradient
https://blog.tjdata.site/posts/ed4cf7b5.html
作者
chenxia
发布于
2022年10月23日
许可协议