fix some errors
This commit is contained in:
@@ -159,7 +159,7 @@ POMDP 可以用一个 7 元组描述:$(S,A,T,R,\Omega,O,\gamma)$,其中 $S$
|
||||
## Major Components of an RL Agent
|
||||
|
||||

|
||||
对于一个强化学习 agent,它有哪些组成成分,首先 agent 有一个这决策函数,policy function,这个函数是会被 agent 用来选取它下一步的动作。
|
||||
对于一个强化学习 agent,它有哪些组成成分,首先 agent 有一个 policy function,这个函数是会被 agent 用来选取它下一步的动作。
|
||||
|
||||
然后它也可能生成一个价值函数(value function)。这个价值函数被 agent 用来对现在当前状态进行估价,它就是说你进入现在这个状态,到底可以对你后面的收益带来多大的影响。当这个价值函数大的时候,说明你进入这个状态越有利。
|
||||
|
||||
@@ -357,41 +357,28 @@ $python
|
||||

|
||||
在OpenAI Gym 里面有很经典的控制类游戏,比如说 Acrobot,就是把这个两节铁杖,然后甩了立起来。还有 CartPole,通过控制一个平板,让这个木棍立起来。还有 MountainCar 的一个例子,就通过前后移动这个车,让它到达这个旗子的位置。大家可以去[这个链接](https://gym.openai.com/envs/#classic_control)看一看这些环境。在刚开始测试强化学习的时候,可以选择这些简单环境,因为这些环境可能是在一两分钟之内你就可以见到一个效果。
|
||||
|
||||
|
||||
|
||||
Gym 官方网站提供了一个简单的例子供我们了解接口(https://gym.openai.com/docs/)。通过这个例子,我们就能知道这个接口的使用方法。
|
||||
|
||||

|
||||
|
||||
这里我们看一下 CartPole 的这个环境。对于这个环境,有两个动作,Cart 往左移还是往右移。这里得到了观测:它这个车当前的位置,Cart 当前的往左往右移的速度,这个杆的这个角度以及它的杆的最高点的这个速度。
|
||||
|
||||
如果 observation 越详细的话,就可以更好地描述当前这个所有的状态。然后这里有 reward 定义的话,如果能多保留一步,然后你就会得到一个奖励,所以你尽可能多的时间存活来得到更多的奖励。一段游戏,它的终止条件就是说,你没有把这个杆平衡。当这个杆的角度大于某一个角度的时候或者这个车已经出到外面的时候,你就输了。所以这个 agent 的目的就是为了控制这个木棍,让它尽可能地保持平衡以及尽可能保持在这个环境的中央。
|
||||
|
||||
```python
|
||||
import gym
|
||||
env = gym.make('CartPole-v0’)
|
||||
env.reset()
|
||||
env.render() # display the rendered scene
|
||||
action = env.action_space.sample()
|
||||
observation, reward, done, info = env.step(action)
|
||||
```
|
||||
|
||||
如果我们玩这个环境的话,就就直接可以 import gym,调入 CartPole 这个环境。然后这里就可以通过这个采样,然后来执行这个环境。
|
||||
|
||||
```python
|
||||
import gym # 导入Gym的Python接口环境包
|
||||
env = gym.make('CartPole-v0') # 构建实验环境
|
||||
env.reset() # 重置一个 episode
|
||||
for _ in range(1000):
|
||||
env.render() # 用于显示渲染的场景
|
||||
action = env.action_space.sample()
|
||||
env.render() # 显示图形界面
|
||||
action = env.action_space.sample() # 从动作空间中随机选取一个动作
|
||||
env.step(action) # 用于提交动作,括号内是具体的动作
|
||||
env.close()
|
||||
env.close() # 关闭环境
|
||||
```
|
||||
|
||||
当你执行这段代码时,可能会很失望,因为机器人就像个醉汉,完全无视那根本该立起来的杆子,驾驶着小车朝某个方向一通跑,直到不见踪影。别着急,我们还没开始训练机器人呢。
|
||||
注意:如果绘制了实验的图形界面窗口,那么关闭该窗口的最佳方式是调用`env.close()`。试图直接关闭图形界面窗口可能会导致内存不能释放,甚至会导致死机。
|
||||
|
||||
Gym中的小游戏,大部分都可以用一个普通的实数或者向量来充当动作。打印 `env.action_space.sample()` 的返回值,能看到输出为1或者0。
|
||||
当你执行这段代码时,可能会很失望,因为机器人会完全无视那根本该立起来的杆子,驾驶着小车朝某个方向一通跑,直到不见踪影。别着急,我们还没开始训练机器人呢。
|
||||
|
||||
Gym 中的小游戏,大部分都可以用一个普通的实数或者向量来充当动作。打印 `env.action_space.sample()` 的返回值,能看到输出为1或者0。
|
||||
|
||||
`env.action_space.sample()`的含义是,在该游戏的所有动作空间里随机选择一个作为输出。在这个例子中,意思就是,动作只有两个,一个是 0,另一个是 1,一左一右。
|
||||
|
||||
@@ -405,11 +392,11 @@ Gym中的小游戏,大部分都可以用一个普通的实数或者向量来
|
||||
在每个训练中都要使用的返回值有observation、reward、done。但是,observation的结构会由于游戏的不同而发生变化。以CartPole-v0小游戏为例,我们修改下代码:
|
||||
|
||||
```python
|
||||
import gym # 导入Gym的Python接口环境包
|
||||
env = gym.make('CartPole-v0') # 构建实验环境
|
||||
env.reset() # 重置一个 episode
|
||||
import gym
|
||||
env = gym.make('CartPole-v0')
|
||||
env.reset()
|
||||
for _ in range(1000):
|
||||
env.render() # 用于显示渲染的场景
|
||||
env.render()
|
||||
action = env.action_space.sample()
|
||||
observation, reward, done, info = env.step(action)
|
||||
print(observation)
|
||||
@@ -429,11 +416,141 @@ env.close()
|
||||
|
||||
`env.step()`完成了一个完整的 $S \to A \to R \to S'$ 过程。我们只要不断观测这样的过程,并让机器在其中用相应的算法完成训练,就能得到一个高质量的强化学习模型。
|
||||
|
||||
想要查看当前 Gym 库已经注册了哪些环境,可以使用以下代码:
|
||||
|
||||
```python
|
||||
from gym import envs
|
||||
env_specs = envs.registry.all()
|
||||
envs_ids = [env_spec.id for env_spec in env_specs]
|
||||
print(envs_ids)
|
||||
```
|
||||
|
||||
每个环境都定义了自己的观测空间和动作空间。环境 env 的观测空间用`env.observation_space`表示,动作空间用 `env.action_space `表示。观测空间和动作空间既可以是离散空间(即取值是有限个离散的值),也可以是连续空间(即取值是连续的)。在Gym库中,离散空间一般用`gym.spaces.Discrete`类表示,连续空间用`gym.spaces.Box`类表示。
|
||||
|
||||
例如,环境`'MountainCar-v0'`的观测空间是`Box(2,)`,表示观测可以用 2 个float值表示;环境`'MountainCar-v0'`的动作空间是`Dicrete(3)`,表示动作取值自`{0,1,2}`。对于离散空间,`gym.spaces.Discrete`类实例的成员 n 表示有几个可能的取值;对于连续空间,`Box`类实例的成员 low 和 high 表示每个浮点数的取值范围。
|
||||
|
||||
### MountainCar-v0 Example
|
||||
|
||||
接下来,我们通过一个例子来学习如何与 Gym 库进行交互。我们选取的 `小车上山(MountainCar-v0)`。
|
||||
|
||||
首先我们来看看这个任务的观测空间和动作空间:
|
||||
|
||||
```python
|
||||
import gym
|
||||
env = gym.make('MountainCar-v0')
|
||||
print('观测空间 = {}'.format(env.observation_space))
|
||||
print('动作空间 = {}'.format(env.action_space))
|
||||
print('观测范围 = {} ~ {}'.format(env.observation_space.low,
|
||||
env.observation_space.high))
|
||||
print('动作数 = {}'.format(env.action_space.n))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
观测空间 = Box(2,)
|
||||
动作空间 = Discrete(3)
|
||||
观测范围 = [-1.2 -0.07] ~ [0.6 0.07]
|
||||
动作数 = 3
|
||||
```
|
||||
|
||||
由输出可知,观测空间是形状为 (2,) 的浮点型 np.array,动作空间是取 {0,1,2} 的 int 型数值。
|
||||
|
||||
接下来考虑智能体。智能体往往是我们自己实现的。我们可以实现一个智能体类:`BespokeAgent类`,代码如下所示:
|
||||
|
||||
```python
|
||||
class BespokeAgent:
|
||||
def __init__(self, env):
|
||||
pass
|
||||
|
||||
def decide(self, observation): # 决策
|
||||
position, velocity = observation
|
||||
lb = min(-0.09 * (position + 0.25) ** 2 + 0.03,
|
||||
0.3 * (position + 0.9) ** 4 - 0.008)
|
||||
ub = -0.07 * (position + 0.38) ** 2 + 0.07
|
||||
if lb < velocity < ub:
|
||||
action = 2
|
||||
else:
|
||||
action = 0
|
||||
return action # 返回动作
|
||||
|
||||
def learn(self, *args): # 学习
|
||||
pass
|
||||
|
||||
agent = BespokeAgent(env)
|
||||
```
|
||||
|
||||
智能体的 `decide()` 方法实现了决策功能,而 `learn()` 方法实现了学习功能。`BespokeAgent`类是一个比较简单的类,它只能根据给定的数学表达式进行决策,并且不能有效学习。所以它并不是一个真正意义上的强化学习智能体类。但是,用于演示智能体和环境的交互已经足够了。
|
||||
|
||||
接下来我们试图让智能体与环境交互,代码如下所示:
|
||||
|
||||
```python
|
||||
def play_montecarlo(env, agent, render=False, train=False):
|
||||
episode_reward = 0. # 记录回合总奖励,初始化为0
|
||||
observation = env.reset() # 重置游戏环境,开始新回合
|
||||
while True: # 不断循环,直到回合结束
|
||||
if render: # 判断是否显示
|
||||
env.render() # 显示图形界面,图形界面可以用 env.close() 语句关闭
|
||||
action = agent.decide(observation)
|
||||
next_observation, reward, done, _ = env.step(action) # 执行动作
|
||||
episode_reward += reward # 收集回合奖励
|
||||
if train: # 判断是否训练智能体
|
||||
agent.learn(observation, action, reward, done) # 学习
|
||||
if done: # 回合结束,跳出循环
|
||||
break
|
||||
observation = next_observation
|
||||
return episode_reward # 返回回合总奖励
|
||||
```
|
||||
|
||||
上面代码中的 `play_montecarlo` 函数可以让智能体和环境交互一个回合。这个函数有 4 个参数:
|
||||
|
||||
* `env` 是环境类
|
||||
* `agent` 是智能体类
|
||||
* `render`是 bool 类型变量,指示在运行过程中是否要图形化显示。如果函数参数 render为 True,那么在交互过程中会调用 `env.render()` 以显示图形化界面,而这个界面可以通过调用 `env.close()` 关闭。
|
||||
* `train`是 bool 类型的变量,指示在运行过程中是否训练智能体。在训练过程中应当设置为 True,以调用 `agent.learn()` 函数;在测试过程中应当设置为 False,使得智能体不变。
|
||||
|
||||
这个函数有一个返回值 `episode_reward`,是 float 类型的数值,表示智能体与环境交互一个回合的回合总奖励。
|
||||
|
||||
接下来,我们使用下列代码让智能体和环境交互一个回合,并在交互过程中图形化显示,可用 `env.close()` 语句关闭图形化界面。
|
||||
|
||||
```python
|
||||
env.seed(0) # 设置随机数种子,只是为了让结果可以精确复现,一般情况下可删去
|
||||
episode_reward = play_montecarlo(env, agent, render=True)
|
||||
print('回合奖励 = {}'.format(episode_reward))
|
||||
env.close() # 此语句可关闭图形界面
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
回合奖励 = -105.0
|
||||
```
|
||||
|
||||
为了系统评估智能体的性能,下列代码求出了连续交互 100 回合的平均回合奖励。
|
||||
|
||||
```python
|
||||
episode_rewards = [play_montecarlo(env, agent) for _ in range(100)]
|
||||
print('平均回合奖励 = {}'.format(np.mean(episode_rewards)))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
平均回合奖励 = -102.61
|
||||
```
|
||||
|
||||
小车上山环境有一个参考的回合奖励值 -110,如果当连续 100 个回合的平均回合奖励大于 -110,则认为这个任务被解决了。`BespokeAgent` 类对应的策略的平均回合奖励大概就在 -110 左右。
|
||||
|
||||
测试 agent 在 Gym 库中某个任务的性能时,学术界一般最关心 100 个回合的平均回合奖励。至于为什么是 100 个回合而不是其他回合数(比如 128 个回合),完全是习惯使然,没有什么特别的原因。对于有些环境,还会指定一个参考的回合奖励值,当连续 100 个回合的奖励大于指定的值时,就认为这个任务被解决了。但是,并不是所有的任务都指定了这样的值。对于没有指定值的任务,就无所谓任务被解决了或者没有被解决。
|
||||
|
||||
最后提一下,Gym 有对应的[官方文档](https://gym.openai.com/docs/),大家可以阅读文档来学习 Gym。
|
||||
|
||||
## References
|
||||
|
||||
* [百面深度学习](https://book.douban.com/subject/35043939/)
|
||||
* [强化学习:原理与Python实现](https://book.douban.com/subject/34478302/)
|
||||
* [白话强化学习与PyTorch](https://book.douban.com/subject/34809676/)
|
||||
* [OpenAI Spinning Up ](https://spinningup.openai.com/en/latest/algorithms/ddpg.html#)
|
||||
* [OpenAI Spinning Up ](https://spinningup.openai.com/en/latest/spinningup/rl_intro.html#)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
## Sarsa
|
||||
|
||||
### MDP
|
||||
|
||||

|
||||
|
||||
强化学习的三个重要的要素:状态动作和奖励。强化学习智能体跟环境是一步一步交互的,就是我先观察一下状态,然后再输入动作。再观察一下状态,再输出动作,拿到这些 reward 。它是一个跟时间相关的一个序列决策的问题。
|
||||
@@ -14,7 +16,7 @@
|
||||
|
||||
这样子的一个状态转移概率是具有`马尔科夫性质`的(系统下一时刻的状态仅由当前时刻的状态决定,不依赖于以往任何状态)。因为这个状态转移概率,它是下一时刻的状态是取决于当前的状态,它和之前的 $s_{t-1}$ 和 $s_{t-2}$ 都没有什么关系。然后再加上说这个过程也取决于智能体跟环境交互的这个$a_t$ ,所以有一个决策的一个过程在里面。我们就称这样的一个过程为`马尔可夫决策过程(MDP)`。
|
||||
|
||||
MDP 就是序列决策这样一个经典的表达方式。MDP 也是强化学习里面一个非常基本的学习框架。像之前的这四个状态、动作、奖励和状态转移概率,S,A,P,R,这四个合集就构成了强化学习 MDP 的四元组,那后面其实也可能会再加个衰减因子构成五元组。
|
||||
MDP 就是序列决策这样一个经典的表达方式。MDP 也是强化学习里面一个非常基本的学习框架。状态、动作、奖励和状态转移概率(S,A,P,R),这四个合集就构成了强化学习 MDP 的四元组,那后面其实也可能会再加个衰减因子构成五元组。
|
||||
|
||||
|
||||

|
||||
@@ -29,7 +31,7 @@ MDP 就是序列决策这样一个经典的表达方式。MDP 也是强化学习
|
||||

|
||||
因为现实世界中人类第一次遇到熊之前,我们根本不知道我们能不能跑得过熊。所以刚刚那个10%、90%的概率也就是虚构出来的概率,熊到底在什么时候会往什么方向去转变的话,我们经常是不知道的。我们是处在一个未知的环境里的,也就是这一系列的决策的 P 函数和 R 函数是未知的。这就是 model-based 跟 model-free 的一个最大的区别。强化学习就是可以用来解决用完全未知的和随机的环境。
|
||||
|
||||
强化学习要像人类一样去学习了,人类学习的话就是一条路一条路的去尝试一下,先走一条路,我看看结果到底是什么。多试几次,只要能活命的,我们其实可以慢慢的了解哪个状态会更好。我们用价值函数 $V(s)$ 来代表这个状态是好的还是坏的。然后用这个 Q 函数来判断说在什么状态下做什么动作能够拿到最大奖励,我们用Q函数来表示这个状态-动作值。
|
||||
强化学习要像人类一样去学习了,人类学习的话就是一条路一条路的去尝试一下,先走一条路,我看看结果到底是什么。多试几次,只要能活命的,我们其实可以慢慢的了解哪个状态会更好。我们用价值函数 $V(s)$ 来代表这个状态是好的还是坏的。然后用这个 Q 函数来判断说在什么状态下做什么动作能够拿到最大奖励,我们用 Q 函数来表示这个状态-动作值。
|
||||
|
||||
|
||||
|
||||
@@ -105,9 +107,11 @@ $$
|
||||
|
||||
玩起来是这样的,先初始化一下,然后开始时序差分的更新过程,训练的过程你会看到这个小黄球不断的在试错。但探索当中会先迅速地发现有 reward的地方。最开始的时候,只是这些有 reward 的格子 才有价值,当不断的重复走这些路线的时候,这些有价值的格子,它可以去慢慢的影响它附近的格子的价值。反复训练之后,有 reward 的这些格子周围的格子的状态就会慢慢的被强化,然后强化就是当它收敛到最后一个最优的状态了,就是把这些价值最终收敛到一个最优的情况之后,那个小黄球就会自动地知道,就是我一直往价值高的地方走,我就能够走到能够拿到 reward 的地方。
|
||||
|
||||
### Temporal Difference
|
||||
|
||||

|
||||
|
||||
这种强化方式其实在数学上面一行公式就表达出来了。我们也喊说这种更新的方式叫做时序差分的一个更新的方式。这个公式它想要表达就是我可以拿下一步的Q 值 $Q(S_{t+_1},A_{t+1})$ 来更新我这一步的 Q 值 $Q(S_t,A_t)$ 。
|
||||
这种强化方式其实在数学上面一行公式就表达出来了。这种更新的方式叫做`时序差分`的一个更新的方式。这个公式它想要表达就是我可以拿下一步的 Q 值 $Q(S_{t+_1},A_{t+1})$ 来更新我这一步的 Q 值 $Q(S_t,A_t)$ 。
|
||||
|
||||
为了理解这个公式,如图所示,我们先把这一块当作是一个目标值,就是 $Q(S_t,A_t)$ 想要去逼近的一个目标值。我们想要计算的就是这个 $Q(S_t,A_t)$ 。因为最开始Q值都是随机初始化,或者是初始化为零。它需要不断的去逼近它理想中真实的Q 值,我们就叫 target 。Target 就是未来收益的总和大概是有多少,而且是带衰减的那个。
|
||||
|
||||
@@ -121,7 +125,7 @@ $$
|
||||
|
||||
软更新的方式就是 $\alpha$ ,每次我只更新一点点。这个 $\alpha$ 有点类似于像学习率一样的东西。最终的话,Q 值都是可以慢慢地逼近到真实的 target 值的。这样我们的更新公式只需要用到当前时刻的 $S_{t},A_t$ ,然后还有拿到的 $R_{t+1}, S_{t+1},A_{t+1}$ 。
|
||||
|
||||
我们只需要这几个值,就是$(S_{t}, A_{t}, R_{t+1}, S_{t+1}, A_{t+1})$ ,这就是 Sarsa 算法。它的命名其实就是因为它用到的就是这几个值。因为它走了一步之后,它拿到了 $(S_{t}, A_{t}, R_{t+1}, S_{t+1}, A_{t+1})$ 之后,它就可以做一次这样子的更新。
|
||||
我们只需要 $(S_{t}, A_{t}, R_{t+1}, S_{t+1}, A_{t+1})$ 这几个值 ,这就是 Sarsa 算法。它的命名其实就是因为它用到的就是这几个值。因为它走了一步之后,它拿到了 $(S_{t}, A_{t}, R_{t+1}, S_{t+1}, A_{t+1})$ 之后,它就可以做一次这样子的更新。
|
||||
|
||||

|
||||
|
||||
@@ -170,7 +174,7 @@ Sarsa 实际上都是用自己的策略产生了 S,A,R,S',A' 这一条轨迹。
|
||||
$$
|
||||
\begin{aligned} Q ^ { \pi } \left( s _ { t } , a _ { t } \right) & = \mathbb { E } \left[ G _ { t } \mid s _ { t } , a _ { t } \right] \\ & = \mathbb { E } \left[ r _ { t } + \gamma r _ { t + 1 } + \gamma ^ { 2 } r _ { t + 2 } + \cdots \mid s _ { t } , a _ { t } \right] \\ & = \mathbb { E } \left[ r _ { t } + \gamma \left( r _ { t + 1 } + \gamma r _ { t + 2 } + \cdots \right) \mid s _ { t } , a _ { t } \right] \\ & = \mathbb { E } \left[ r _ { t } + \gamma Q ^ { \pi } \left( s _ { t + 1 } , a _ { t + 1 } \right) \mid s _ { t } , a _ { t } \right] \end{aligned}
|
||||
$$
|
||||
上式是马尔可夫决策过程中 Bellman 方程的基本形式。累积奖励 $G_t$ 的计算,不仅考虑当下 $t$ 时刻的动作 $a_t$ 的奖励 $r_t$,还会累积计算对之后決策带来的影响(公式中的 $\gamma$ 是后续奖励的衰减因子)。从上式可以看出,当前状态的动作价值 $Q^{\pi}(s_t,a_t)$ ,与当前动作的奖励 $r_t$ 以及下一状态的动作价值 $Q^{\pi}(s_{t+1},a_{t+1})$ 有关,因此,状态-动作值函数的计算可以通过动态规划算法来实现。
|
||||
上式是 MDP 中 Bellman 方程的基本形式。累积奖励 $G_t$ 的计算,不仅考虑当下 $t$ 时刻的动作 $a_t$ 的奖励 $r_t$,还会累积计算对之后決策带来的影响(公式中的 $\gamma$ 是后续奖励的衰减因子)。从上式可以看出,当前状态的动作价值 $Q^{\pi}(s_t,a_t)$ ,与当前动作的奖励 $r_t$ 以及下一状态的动作价值 $Q^{\pi}(s_{t+1},a_{t+1})$ 有关,因此,状态-动作值函数的计算可以通过动态规划算法来实现。
|
||||
|
||||
从另一方面考虑,在计算 $t$ 时刻的动作价值 $Q^{\pi}(s_t,a_t)$ 时,需要知道在 $t$、$t+1$、$t+2 \cdots \cdots$ 时刻的奖励,这样就不仅需要知道某一状态的所有可能出现的后续状态以及对应的奖励值,还要进行全宽度的回溯来更新状态的价值。这种方法无法在状态转移函数未知或者大规模问题中使用。因此,Q- learning 采用了浅层的时序差分采样学习,在计算累积奖励时,基于当前策略 $\pi$ 预测接下来发生的 $n$ 步动作($n$ 可以取 1 到 $+\infty$)并计算其奖励值。
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
我们把一开始的初始画面,写作 $s_1$, 把第一次执行的动作叫做 $a_1$,把第一次执行动作完以后得到的 reward 叫做 $r_1$。不同的书会有不同的定义,有人会觉得说这边应该要叫做 $r_2$,这个都可以,你自己看得懂就好。Actor 决定一个的行为以后, 就会看到一个新的游戏画面,这边是 $s_2$。然后把这个 $s_2$ 输入给 actor,这个 actor 决定要开火,然后它可能杀了一只怪,就得到五分。然后这个 process 就反复地持续下去,直到今天走到某一个 timestamp 执行某一个 action,得到 reward 之后, 这个 environment 决定这个游戏结束了。比如说,如果在这个游戏里面,你是控制绿色的船去杀怪,如果你被杀死的话,游戏就结束,或是你把所有的怪都清空,游戏就结束了。
|
||||
|
||||

|
||||
一场游戏叫做一个 `Episode`。把这个游戏里面,所有得到的 reward 都总合起来,就是 `Total reward`,我们称其为`Return(回报)`,用 R 来表示它。Actor 存在的目的就是想办法去 maximize 它可以得到的 reward。
|
||||
一场游戏叫做一个 `Episode(回合)` 或者 `Trial(试验)`。把这个游戏里面,所有得到的 reward 都总合起来,就是 `Total reward`,我们称其为`Return(回报)`,用 R 来表示它。Actor 要想办法去 maximize 它可以得到的 reward。
|
||||
|
||||

|
||||
首先,`environment` 是一个`function`,游戏的主机也可以把它看作是一个 function,虽然它不一定是 neural network,可能是 rule-based 的规则,但你可以把它看作是一个 function。这个 function,一开始就先吐出一个 state,也就是游戏的画面,接下来你的 actor 看到这个游戏画面 $s_1$ 以后,它吐出 $a_1$,然后 environment 把 $a_1$ 当作它的输入,然后它再吐出 $s_2$,吐出新的游戏画面。Actor 看到新的游戏画面,再采取新的行为 $a_2$,然后 environment 再看到 $a_2$,再吐出 $s_3$。这个 process 会一直持续下去,直到 environment 觉得说应该要停止为止。
|
||||
|
||||
@@ -15,17 +15,17 @@ Q-learning 是 `value-based` 的方法。在 value based 的方法里面,我
|
||||
|
||||
这边需要强调的一个点是说,当你在讲这一个 critic 的时候,critic 都是绑一个 actor 的,critic 没有办法去凭空去 evaluate 一个 state 的好坏,它所 evaluate 的东西是在给定某一个 state 的时候, 假设接下来互动的 actor 是 $\pi$,那我会得到多少 reward。因为就算是给同样的 state,你接下来的 $\pi$ 不一样,你得到的 reward 也是不一样的。举例来说,在左边那个case,虽然假设是一个正常的 $\pi$,它可以杀很多怪,那假设他是一个很弱的 $\pi$,它就站在原地不动,然后马上就被射死了,那你得到的 V 还是很小。所以 critic output 值有多大,其实是取决于两件事:state 和 actor。所以你的 critic 其实都要绑一个 actor,它是在衡量某一个 actor 的好坏,而不是 generally 衡量一个 state 的好坏。这边要强调一下,critic output 是跟 actor 有关的,state value 其实是 depend on 你的 actor。当你的 actor 变的时候,state value function 的output 其实也是会跟着改变的。
|
||||
|
||||
再来问题就是,怎么衡量这一个 state value function 呢?怎么衡量这一个$V^{\pi}(s)$ 呢?有两种不同的做法。
|
||||
### State Value Function Estimation
|
||||
|
||||

|
||||
|
||||
怎么 estimate 这些critic,那怎么 estimate $V^{\pi}(s)$ 呢。有两个方向,一个是用` Monte-Carlo(MC) based` 的方法。MC based 的方法就是让 actor 去跟环境做互动,你要看 actor 好不好, 你就让 actor 去跟环境做互动,给critic 看。然后,critic 就统计说,actor 如果看到 state $s_a$,接下来 accumulated reward 会有多大。如果它看到 state $s_b$,接下来accumulated reward 会有多大。但是实际上,你当然不可能把所有的state 通通都扫过。如果你是玩 Atari 游戏的话,你的 state 是 image ,你没有办法把所有的state 通通扫过。所以实际上我们的 $V^{\pi}(s)$ 是一个network。对一个network 来说,就算是 input state 是从来都没有看过的,它也可以想办法估测一个 value 的值。
|
||||
怎么衡量这个 state value function $V^{\pi}(s)$ 呢?有两种不同的做法。一个是用` Monte-Carlo(MC) based` 的方法。MC based 的方法就是让 actor 去跟环境做互动,你要看 actor 好不好, 你就让 actor 去跟环境做互动,给 critic 看。然后,critic 就统计说,actor 如果看到 state $s_a$,接下来 accumulated reward 会有多大。如果它看到 state $s_b$,接下来accumulated reward 会有多大。但是实际上,你不可能把所有的state 通通都扫过。如果你是玩 Atari 游戏的话,你的 state 是 image ,你没有办法把所有的state 通通扫过。所以实际上我们的 $V^{\pi}(s)$ 是一个 network。对一个 network 来说,就算是 input state 是从来都没有看过的,它也可以想办法估测一个 value 的值。
|
||||
|
||||
怎么训练这个 network 呢?因为如果在state $s_a$,接下来的 accumulated reward 就是 $G_a$。也就是说,对这个 value function 来说,如果 input 是 state $s_a$,正确的 output 应该是$G_a$。如果 input state $s_b$,正确的output 应该是value $G_b$。所以在 training 的时候, 它就是一个 `regression problem`。Network 的 output 就是一个 value,你希望在 input $s_a$ 的时候,output value 跟 $G_a$ 越近越好,input $s_b$ 的时候,output value 跟 $G_b$ 越近越好。接下来把 network train 下去,就结束了。这是第一个方法,MC based 的方法。
|
||||
|
||||

|
||||
|
||||
第二个方法是`Temporal-difference(时序差分)` 的方法, `即 TD based ` 的方法。在 MC based 的方法中,每次我们都要算 accumulated reward,也就是从某一个 state $s_a$ 一直玩到游戏结束的时候,得到的所有 reward 的总和。所以你要 apply MC based 的 approach,你必须至少把这个游戏玩到结束。但有些游戏非常的长,你要玩到游戏结束才能够 update network,你可能根本收集不到太多的资料,花的时间太长了。所以我们会采用 TD based 的方法。TD based 的方法不需要把游戏玩到底,只要在游戏的某一个情况,某一个 state $s_t$ 的时候,采取 action $a_t$ 得到 reward $r_t$ ,跳到 state $s_{t+1}$,就可以 apply TD 的方法。
|
||||
第二个方法是`Temporal-difference(时序差分)` 的方法, `即 TD based ` 的方法。在 MC based 的方法中,每次我们都要算 accumulated reward,也就是从某一个 state $s_a$ 一直玩到游戏结束的时候,得到的所有 reward 的总和。所以你要 apply MC based 的 approach,你必须至少把这个游戏玩到结束。但有些游戏非常的长,你要玩到游戏结束才能够 update network,花的时间太长了。因此我们会采用 TD based 的方法。TD based 的方法不需要把游戏玩到底,只要在游戏的某一个情况,某一个 state $s_t$ 的时候,采取 action $a_t$ 得到 reward $r_t$ ,跳到 state $s_{t+1}$,就可以 apply TD 的方法。
|
||||
|
||||
怎么 apply TD 的方法呢?这边是基于以下这个式子:
|
||||
$$
|
||||
@@ -46,7 +46,7 @@ $$
|
||||
Var 就是指 variance。
|
||||
通过这个式子,我们知道 $G_a$ 的 variance 相较于某一个 state 的 reward,它会是比较大的,$G_a$ 的variance 是比较大的。
|
||||
|
||||
现在,如果说用TD 的话呢?用 TD 的话,你是要去 minimize 这样的一个式子:
|
||||
如果用 TD 的话,你是要去 minimize 这样的一个式子:
|
||||
|
||||

|
||||
|
||||
@@ -56,7 +56,7 @@ Var 就是指 variance。
|
||||
上图是讲 TD 跟 MC 的差异。假设有某一个 critic,它去观察某一个 policy $\pi$ 跟环境互动的 8 个 episode 的结果。有一个actor $\pi$ 跟环境互动了8 次,得到了8 次玩游戏的结果。接下来这个 critic 去估测 state 的 value。
|
||||
|
||||
* 我们看看 $s_b$ 的 value 是多少。$s_b$ 这个state 在 8 场游戏里面都有经历过,其中有6 场得到 reward 1,有两场得到 reward 0,所以如果你是要算期望值的话,就看到 state $s_b$ 以后得到的 reward,一直到游戏结束的时候得到的 accumulated reward 期望值是 3/4。
|
||||
* 但 $s_a$ 期望的 reward 到底应该是多少呢?这边其实有两个可能的答案:一个是0,一个是3/4。为什么有两个可能的答案呢?这取决于你用MC 还是TD。用MC 跟用TD 算出来的结果是不一样的。
|
||||
* 但 $s_a$ 期望的 reward 到底应该是多少呢?这边其实有两个可能的答案:一个是 0,一个是 3/4。为什么有两个可能的答案呢?这取决于你用MC 还是TD。用 MC 跟用 TD 算出来的结果是不一样的。
|
||||
|
||||
假如你用 MC 的话,你会发现这个$s_a$ 就出现一次,看到$s_a$ 这个state,接下来 accumulated reward 就是 0。所以今天 $s_a$ expected reward 就是 0。
|
||||
|
||||
@@ -67,13 +67,13 @@ $$
|
||||
|
||||
因为我们在 state $s_a$ 得到 reward r=0 以后,跳到 state $s_b$。所以 state $s_b$ 的 reward 会等于 state $s_b$ 的 reward 加上在state $s_a$ 跳到 state $s_b$ 的时候可能得到的 reward r。而这个得到的 reward r 的值是 0,$s_b$ expected reward 是3/4,那$s_a$ 的reward 应该是3/4。
|
||||
|
||||
用 MC 跟 TD 估出来的结果,其实很有可能是不一样的。就算 critic observed 到一样的 training data,它最后估出来的结果。也不见得会是一样。那为什么会这样呢?你可能问说,那一个比较对呢?其实就都对。
|
||||
用 MC 跟 TD 估出来的结果,其实很有可能是不一样的。就算 critic 观察到一样的 training data,它最后估出来的结果。也不见得会是一样。那为什么会这样呢?你可能问说,那一个比较对呢?其实就都对。
|
||||
|
||||
因为在第一个 trajectory, $s_a$ 得到 reward 0 以后,再跳到 $s_b$ 也得到 reward 0。这边有两个可能。
|
||||
|
||||
* 一个可能是$s_a$,它就是一个带 sign 的 state,所以只要看到 $s_a$ 以后,$s_b$ 就会拿不到reward,有可能$s_a$ 其实影响了$s_b$。如果是用 MC 的算法的话,它会把 $s_a$ 影响 $s_b$ 这件事考虑进去。所以看到 $s_a$ 以后,接下来 $s_b$ 就得不到 reward,所以看到$s_a$ 以后,期望的reward 是 0。
|
||||
|
||||
* 另一个可能是,看到$s_a$ 以后, $s_b$ 的 reward 是0 这件事只是一个巧合,就并不是 $s_a$ 所造成,而是因为说 $s_b$ 有时候就是会得到 reward 0,这只是单纯运气的问题。其实平常 $s_b$ 会得到 reward 期望值是3/4,跟 $s_a$ 是完全没有关系的。所以假设 $s_a$ 之后会跳到 $s_b$,那其实得到的 reward 按照 TD 来算应该是3/4。
|
||||
* 另一个可能是,看到$s_a$ 以后, $s_b$ 的 reward 是0 这件事只是一个巧合,就并不是 $s_a$ 所造成,而是因为说 $s_b$ 有时候就是会得到 reward 0,这只是单纯运气的问题。其实平常 $s_b$ 会得到 reward 期望值是 3/4,跟 $s_a$ 是完全没有关系的。所以假设 $s_a$ 之后会跳到 $s_b$,那其实得到的 reward 按照 TD 来算应该是 3/4。
|
||||
|
||||
所以不同的方法考虑了不同的假设,运算结果不同。
|
||||
|
||||
@@ -295,9 +295,9 @@ $$
|
||||
|
||||
但是这样做有什么好处呢?这么做有两个好处。
|
||||
|
||||
* 第一个好处,其实在做 reinforcement learning 的时候, 往往最花时间的 step 是在跟环境做互动,train network 反而是比较快的。因为你用 GPU train 其实很快, 真正花时间的往往是在跟环境做互动。用 replay buffer 可以减少跟环境做互动的次数,因为在做 training 的时候,你的experience 不需要通通来自于某一个policy。一些过去的 policy 所得到的 experience 可以放在 buffer 里面被使用很多次,被反复的再利用,这样让你的 sample 到 experience 的利用是比较 efficient。
|
||||
* 第一个好处,其实在做 reinforcement learning 的时候, 往往最花时间的 step 是在跟环境做互动,train network 反而是比较快的。因为你用 GPU train 其实很快, 真正花时间的往往是在跟环境做互动。用 replay buffer 可以减少跟环境做互动的次数,因为在做 training 的时候,你的 experience 不需要通通来自于某一个policy。一些过去的 policy 所得到的 experience 可以放在 buffer 里面被使用很多次,被反复的再利用,这样让你的 sample 到 experience 的利用是比较 efficient。
|
||||
|
||||
* 第二个好处,在 train network 的时候,其实我们希望一个 batch 里面的 data 越 diverse 越好。如果你的 batch 里面的 data 都是同样性质的,你 train 下去是容易坏掉的。如果 batch 里面都是一样的data,你 train 的时候,performance 会比较差。我们希望 batch data 越 diverse 越好。那如果你今天,你的这个buffer 里面的那些experience 通通来自于不同的policy ,那你sample 到的一个batch 里面的data 会是比较diverse 。
|
||||
* 第二个好处,在 train network 的时候,其实我们希望一个 batch 里面的 data 越 diverse 越好。如果你的 batch 里面的 data 都是同样性质的,你 train 下去是容易坏掉的。如果 batch 里面都是一样的 data,你 train 的时候,performance 会比较差。我们希望 batch data 越 diverse 越好。那如果 buffer 里面的那些 experience 通通来自于不同的 policy ,那你 sample 到的一个 batch 里面的 data 会是比较 diverse 。
|
||||
|
||||
Q:我们明明是要观察 $\pi$ 的 value,里面混杂了一些不是 $\pi$ 的 experience ,这有没有关系?
|
||||
|
||||
|
||||
Reference in New Issue
Block a user