add some code
This commit is contained in:
@@ -22,13 +22,11 @@
|
||||
|
||||

|
||||
|
||||
我们对比下强化学习和监督学习。首先强化学习输入的序列的数据并不是像 supervised learning 里面这些样本都是独立的。另外一点是 learner 并没有被告诉你正确的每一步,正确的行为应该是什么。这个 learner 不得不自己去发现哪些行为是可以使得它最后得到这个奖励的啊,通过不停的去尝试发现最有利的 action。
|
||||
我们对比下强化学习和监督学习。首先强化学习输入的序列的数据并不是像 supervised learning 里面这些样本都是独立的。另外一点是 learner 并没有被告诉你正确的每一步,正确的行为应该是什么。这个 learner 不得不自己去发现哪些行为是可以使得它最后得到这个奖励的啊,通过不停的去尝试发现最有利的 action。
|
||||
|
||||
这里还有一点是 agent 获得自己能力的过程中,其实是通过不断地试错,就这里 trial-and-error exploration,exploration 和 exploitation 是强化学习里面非常核心的一个问题。Exploitation意思是说你会去尝试一些新的行为,让这些新的行为有可能会使你得到更高的这个奖励,也有可能使你一无所有。Exploitation 说的是你就是就采取你已知道可以获得最大行为的过程,那你就重复执行这个 action 就可以了。因为你已经知道可以获得一定的奖励,所以这就需要一个权衡,这也是在这个监督学习里面没有的情况。
|
||||
|
||||
在强化学习过程中,没有这个非常强的 supervisor,这里只有一个`奖励信号(reward signal)`,就是这个环境会在很久以后告诉你之前你采取的行为到底是不是有效的。Agent 在这个强化学习里面学习的话就非常困难,因为你并没有得到即时反馈,当你采取一个行为过后,如果是监督学习,你就立刻可以获得一个指引,就说你现在做出了一个错误的决定,那么正确的决定应该是谁。而在强化学习里面,环境可能会告诉你这个行为是错误,但是它并没有告诉你正确的行为是什么。而且更困难的是,他可能是在一两分钟过后告诉你错误,它在告诉你之前的行为到底行不行。所以这也是强化学习和监督学习不同的地方。
|
||||
|
||||
|
||||
在强化学习过程中,没有这个非常强的 supervisor,只有一个`奖励信号(reward signal)`,就是这个环境会在很久以后告诉你之前你采取的行为到底是不是有效的。Agent 在这个强化学习里面学习的话就非常困难,因为你并没有得到即时反馈,当你采取一个行为过后,如果是监督学习,你就立刻可以获得一个指引,就说你现在做出了一个错误的决定,那么正确的决定应该是谁。而在强化学习里面,环境可能会告诉你这个行为是错误,但是它并没有告诉你正确的行为是什么。而且更困难的是,他可能是在一两分钟过后告诉你错误,它在告诉你之前的行为到底行不行。所以这也是强化学习和监督学习不同的地方。
|
||||
|
||||
通过跟监督学习比较,我们可以总结出这个强化学习的一些特征。
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
当然在输出每一个动作之前,其实你都是可以选择不同的动作。比如说在 $t$ 时刻,我选择跑路的时候,熊已经追上来了,如果说 $t$ 时刻,我没有选择装死,而我是选择跑路的话,这个时候熊已经追上了,那这个时候,其实我有两种情况转移到不同的状态去,就我有一定的概率可以逃跑成功,也有很大的概率我会逃跑失败。那我们就用状态转移概率 $p\left[s_{t+1}, r_{t} \mid s_{t}, a_{t}\right]$ 来表述说在 $s_t$ 的状态选择了 $a_t$ 的动作的时候,转移到 $s_{t+1}$ ,而且拿到 $r_t$ 的概率是多少。
|
||||
|
||||
这样子的一个状态转移概率是具有`马尔科夫性质`的(系统下一时刻的状态仅由当前时刻的状态决定,不依赖于以往任何状态)。因为这个状态转移概率,它是下一时刻的状态是取决于当前的状态,它和之前的 $s_{t-1}$ 和 $s_{t-2}$ 都没有什么关系。然后再加上说这个过程也取决于智能体跟环境交互的这个$a_t$ ,所以有一个决策的一个过程在里面。我们就称这样的一个过程为`马尔可夫决策过程(MDP)`。
|
||||
这样子的一个状态转移概率是具有`马尔科夫性质(Markov Property)`的(系统下一时刻的状态仅由当前时刻的状态决定,不依赖于以往任何状态)。因为这个状态转移概率,它是下一时刻的状态是取决于当前的状态,它和之前的 $s_{t-1}$ 和 $s_{t-2}$ 都没有什么关系。然后再加上说这个过程也取决于智能体跟环境交互的这个$a_t$ ,所以有一个决策的一个过程在里面。我们就称这样的一个过程为`马尔可夫决策过程(MDP)`。
|
||||
|
||||
|
||||
MDP 就是序列决策这样一个经典的表达方式。MDP 也是强化学习里面一个非常基本的学习框架。状态、动作、奖励和状态转移概率(S,A,P,R),这四个合集就构成了强化学习 MDP 的四元组,后面也可能会再加个衰减因子构成五元组。
|
||||
|
||||
@@ -33,8 +34,6 @@ MDP 就是序列决策这样一个经典的表达方式。MDP 也是强化学习
|
||||
|
||||
强化学习要像人类一样去学习了,人类学习的话就是一条路一条路的去尝试一下,先走一条路,我看看结果到底是什么。多试几次,只要能活命的,我们其实可以慢慢的了解哪个状态会更好。我们用价值函数 $V(s)$ 来代表这个状态是好的还是坏的。然后用这个 Q 函数来判断说在什么状态下做什么动作能够拿到最大奖励,我们用 Q 函数来表示这个状态-动作值。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
接下来就会介绍 Q 函数。在经过多次尝试和那个熊打交道之后,人类就可以对熊的不同的状态去做出判断,我们可以用状态动作价值的来表达说在某个状态下,为什么动作 1 会比动作 2 好。因为动作 1 的价值比动作 2 要高。这个价值就叫 Q 函数。如果说这个 Q 表格是一张已经训练好的表格的话,那这一张表格就像是我们的一本生活手册。我们就知道在熊发怒的时候,装死的价值会高一点。在熊离开的时候,我们可能偷偷逃跑的会比较容易获救。这张表格里面 Q 函数的物理意义就是我选择了这个动作之后我最后面能不能成功,就是我需要去计算我在这个状态下,我选择了这个动作,后续能够一共拿到多少总收益。如果我可以预估未来的总收益的大小,我们当然知道在当前的这个状态下选择哪个动作,价值更高。我选择某个动作是因为我未来一共可以拿到的那个价值会更高一点。所以强化学习它的目标导向性很强,环境给了这个 reward 是一个非常重要的反馈,它就是根据环境的 reward 的反馈来去做选择。
|
||||
@@ -52,7 +51,7 @@ MDP 就是序列决策这样一个经典的表达方式。MDP 也是强化学习
|
||||

|
||||
|
||||
|
||||
举个具体的例子来看看这些计算出来的是什么效果。这是一个悬崖问题。这个问题是需要智能体从这个出发点 S 出发,然后到达目的地 G,同时避免掉进悬崖(cliff),掉进悬崖的话就会有负一百分的惩罚,而且不会结束游戏,它会被直接拖回那个起点,游戏继续。为了到达目的地的话,我们可以沿着蓝线和红线走。
|
||||
举个具体的例子来看看这些计算出来的是什么效果。这是一个悬崖问题。这个问题是需要智能体从出发点 S 出发,然后到达目的地 G,同时避免掉进悬崖(cliff),掉进悬崖的话就会有负一百分的惩罚,但游戏不会结束,它会被直接拖回起点,游戏继续。为了到达目的地的话,我们可以沿着蓝线和红线走。
|
||||
|
||||

|
||||
|
||||
@@ -60,9 +59,9 @@ MDP 就是序列决策这样一个经典的表达方式。MDP 也是强化学习
|
||||
|
||||
如果 $\gamma = 0$,然后用这个公式去计算的话,它相当于考虑的就是一个单步的收益。我们可以认为它是一个目光短浅的一个计算的方法。
|
||||
|
||||
但 $\gamma = 1$ 的话,那就等于是说把后续所有的收益可能都全部加起来。在这里悬崖问题,你每走一步都会拿到一个 -1 分的 reward。只有到了终点之后,它才会停止。如果说 $\gamma =1 $的话,我们用这个公式去计算,就这里是 -1。然后这里的话,未来的总收益就是 $-1+-1=-2$ ,这样子去计算。
|
||||
但 $\gamma = 1$ 的话,那就等于是说把后续所有的收益可能都全部加起来。在这里悬崖问题,你每走一步都会拿到一个 -1 分的 reward。只有到了终点之后,它才会停止。如果说 $\gamma =1 $的话,我们用这个公式去计算,就这里是 -1。然后这里的话,未来的总收益就是 $-1+-1=-2$ 。
|
||||
|
||||
如果说折中一下,让 $\gamma = 0.6$ 话,那就是目光没有放得那么的长远,计算出来是这个样子的。
|
||||
如果让 $\gamma = 0.6$ 的话,就是目光没有放得那么的长远,计算出来是这个样子的。
|
||||
|
||||
|
||||
利用 $G_{t}=R_{t+1}+\gamma G_{t+1}$ 这个公式从后往前推。
|
||||
@@ -81,7 +80,7 @@ $$
|
||||
这里的计算是我们选择了一条路,走完这条路径上每一个状态动作的价值,我们可以看一下右下角这个图,如果说我走的不是这条路,我走的是这一条路,那我算出来那个状态动作价值的 Q 值可能是这样。那我们就知道,当小乌龟在 -12 这个点的时候,往右边走是 -11,往上走是 -15。它自然就知道往右走的价值更大,小乌龟就会往右走
|
||||
|
||||

|
||||
最后我们要求解的就是类似于这样子的一张 Q表格。就是它的行数是所有的状态数量,一般可以用坐标来表示表示格子的状态,也可以用 1、2、3、4、5、6、7 来表示不同的位置。那一共四列的话就代表说是上下左右四个动作。最开始这张 Q 表格会全部初始化为零,然后在 agent 不断地去和环境交互得到不同的轨迹,当交互的次数足够多的时候,我们就可以估算出每一个状态下,每个行动的平均总收益去更新这个 Q 表格。怎么去更新 Q 表格就是我们接下来要引入的强化学习的强化概念。
|
||||
最后我们要求解的就是类似于这样子的一张 Q 表格。就是它的行数是所有的状态数量,一般可以用坐标来表示表示格子的状态,也可以用 1、2、3、4、5、6、7 来表示不同的位置。Q 表格一共四列的话就代表说是上下左右四个动作。最开始这张 Q 表格会全部初始化为零,然后在 agent 不断地去和环境交互得到不同的轨迹,当交互的次数足够多的时候,我们就可以估算出每一个状态下,每个行动的平均总收益去更新这个 Q 表格。怎么去更新 Q 表格就是我们接下来要引入的强化学习的强化概念。
|
||||
|
||||
强化概念的就是我们可以用下一个状态的价值来更新当前状态的价值。其实就是强化学习里面有一个bootstrap(自助)的概念。在强化学习里面,你可以每走一步更新一下 Q 表格,然后用下一个状态的 Q 值来更新这个状态的 Q 值。
|
||||
|
||||
@@ -91,9 +90,7 @@ $$
|
||||
|
||||

|
||||
|
||||
巴普洛夫效应揭示的是中性刺激(铃声)跟无条件刺激(食物)紧紧挨着反复出现的时候,条件刺激也可以引起无条件刺激引起的唾液分泌,然后形成这个条件刺激。这种中性刺激跟无条件刺激在时间上面的结合,我们就称之为强化。 强化的次数越多,那条件反射就会越巩固,那就是小狗原本不觉得铃声有价值的,经过强化之后,小狗就会慢慢地意识到铃声也是有价值的,它可能带来带来食物。更重要是一种条件反射巩固之后,我们再用另外一种新的刺激和条件反射去结合,还可以形成第二级条件反射,同样还可以形成第三级条件反射。在人的身上是可以建立多级的条件反射的。举个例子,比如说一般我们遇到熊都是这样一个顺序,看到树上有熊瓜,然后看到熊之后,突然熊发怒,扑过来了。经历这个过程之后,我们可能最开始看到熊才会瑟瑟发抖,后面就是看到树上有熊爪就已经有害怕的感觉了。也就说在不断的重复试验之后,下一个状态的价值,它是可以不断地去强化影响上一个状态的价值的。
|
||||
|
||||
|
||||
巴普洛夫效应揭示的是中性刺激(铃声)跟无条件刺激(食物)紧紧挨着反复出现的时候,条件刺激也可以引起无条件刺激引起的唾液分泌,然后形成这个条件刺激。这种中性刺激跟无条件刺激在时间上面的结合,我们就称之为强化。 强化的次数越多,条件反射就会越巩固。小狗原本不觉得铃声有价值的,经过强化之后,小狗就会慢慢地意识到铃声也是有价值的,它可能带来带来食物。更重要是一种条件反射巩固之后,我们再用另外一种新的刺激和条件反射去结合,还可以形成第二级条件反射,同样还可以形成第三级条件反射。在人的身上是可以建立多级的条件反射的。举个例子,比如说一般我们遇到熊都是这样一个顺序,看到树上有熊瓜,然后看到熊之后,突然熊发怒,扑过来了。经历这个过程之后,我们可能最开始看到熊才会瑟瑟发抖,后面就是看到树上有熊爪就已经有害怕的感觉了。也就说在不断的重复试验之后,下一个状态的价值,它是可以不断地去强化影响上一个状态的价值的。
|
||||
|
||||

|
||||
|
||||
@@ -194,7 +191,9 @@ $$
|
||||
& = \mathbb{E}[ r _ { t }|s_t,a_t]+ \gamma \mathbb{E}[G_{t+1}|s_t,a_t]
|
||||
\\ &= \mathbb { E } \left[ r _ { t } + \gamma Q ^ { \pi } \left( s _ { t + 1 } , a _ { t + 1 } \right) \mid s _ { t } , a _ { t } \right] \end{aligned}
|
||||
$$
|
||||
上式是 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})$ 有关,因此,状态-动作值函数的计算可以通过动态规划算法来实现。
|
||||
上式是 MDP 中 Q-function 的 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})$ 有关,因此,状态-动作值函数的计算可以通过动态规划算法来实现。
|
||||
|
||||
>Bellman Equation 就是当前状态与未来状态的迭代关系,表示当前状态的值函数可以通过下个状态的值函数来计算。Bellman Equation 因其提出者、动态规划创始人 Richard Bellman 而得名 ,也 叫作“动态规划方程”。
|
||||
|
||||
从另一方面考虑,在计算 $t$ 时刻的动作价值 $Q^{\pi}(s_t,a_t)$ 时,需要知道在 $t$、$t+1$、$t+2 \cdots \cdots$ 时刻的奖励,这样就不仅需要知道某一状态的所有可能出现的后续状态以及对应的奖励值,还要进行全宽度的回溯来更新状态的价值。这种方法无法在状态转移函数未知或者大规模问题中使用。因此,Q- learning 采用了浅层的时序差分采样学习,在计算累积奖励时,基于当前策略 $\pi$ 预测接下来发生的 $n$ 步动作($n$ 可以取 1 到 $+\infty$)并计算其奖励值。
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 575 KiB After Width: | Height: | Size: 683 KiB |
@@ -6,6 +6,8 @@
|
||||
|
||||
Q-learning 是 `value-based` 的方法。在 value based 的方法里面,我们 learn 的不是 policy,我们要 learn 的是一个 `critic`。Critic 并不直接采取行为,它想要做的事情是评价现在的行为有多好或是有多不好。假设有一个 actor $\pi$ ,critic 的工作就是来评价这个 actor 的 policy $\pi$ 好还是不好,即 `Policy Evaluation(策略评估)`。
|
||||
|
||||
> 注:李宏毅深度强化学习课程提到的 Q-learning,其实是 DQN。
|
||||
|
||||
举例来说,有一种 critic 叫做 `state value function`。State value function 的意思就是说,假设 actor 叫做 $\pi$,拿 $\pi$ 跟环境去做互动。假设 $\pi$ 看到了某一个state s,如果在玩 Atari 游戏的话,state s 是某一个画面,看到某一个画面的时候,接下来一直玩到游戏结束,累积的 reward 的期望值有多大。所以 $V^{\pi}$ 是一个function,这个 function input 一个 state,然后它会 output 一个 scalar。这个 scalar 代表说,$\pi$ 这个 actor 看到 state s 的时候,接下来预期到游戏结束的时候,它可以得到多大的 value。
|
||||
|
||||
举个例子,假设你是玩 space invader 的话,
|
||||
|
||||
75
docs/code/Q-learning/agent.py
Normal file
75
docs/code/Q-learning/agent.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class QLearningAgent(object):
|
||||
def __init__(self,
|
||||
obs_n,
|
||||
act_n,
|
||||
learning_rate=0.01,
|
||||
gamma=0.9,
|
||||
e_greed=0.1):
|
||||
self.act_n = act_n # 动作维度,有几个动作可选
|
||||
self.lr = learning_rate # 学习率
|
||||
self.gamma = gamma # reward的衰减率
|
||||
self.epsilon = e_greed # 按一定概率随机选动作
|
||||
self.Q = np.zeros((obs_n, act_n))
|
||||
|
||||
# 根据输入观察值,采样输出的动作值,带探索
|
||||
def sample(self, obs):
|
||||
if np.random.uniform(0, 1) < (1.0 - self.epsilon): #根据table的Q值选动作
|
||||
action = self.predict(obs)
|
||||
else:
|
||||
action = np.random.choice(self.act_n) #有一定概率随机探索选取一个动作
|
||||
return action
|
||||
|
||||
# 根据输入观察值,预测输出的动作值
|
||||
def predict(self, obs):
|
||||
Q_list = self.Q[obs, :]
|
||||
maxQ = np.max(Q_list)
|
||||
action_list = np.where(Q_list == maxQ)[0] # maxQ可能对应多个action
|
||||
action = np.random.choice(action_list)
|
||||
return action
|
||||
|
||||
# 学习方法,也就是更新Q-table的方法
|
||||
def learn(self, obs, action, reward, next_obs, done):
|
||||
""" off-policy
|
||||
obs: 交互前的obs, s_t
|
||||
action: 本次交互选择的action, a_t
|
||||
reward: 本次动作获得的奖励r
|
||||
next_obs: 本次交互后的obs, s_t+1
|
||||
done: episode是否结束
|
||||
"""
|
||||
predict_Q = self.Q[obs, action]
|
||||
if done:
|
||||
target_Q = reward # 没有下一个状态了
|
||||
else:
|
||||
target_Q = reward + self.gamma * np.max(
|
||||
self.Q[next_obs, :]) # Q-learning
|
||||
self.Q[obs, action] += self.lr * (target_Q - predict_Q) # 修正q
|
||||
|
||||
# 把 Q表格 的数据保存到文件中
|
||||
def save(self):
|
||||
npy_file = './q_table.npy'
|
||||
np.save(npy_file, self.Q)
|
||||
print(npy_file + ' saved.')
|
||||
|
||||
# 从文件中读取数据到 Q表格
|
||||
def restore(self, npy_file='./q_table.npy'):
|
||||
self.Q = np.load(npy_file)
|
||||
print(npy_file + ' loaded.')
|
||||
195
docs/code/Q-learning/gridworld.py
Normal file
195
docs/code/Q-learning/gridworld.py
Normal file
@@ -0,0 +1,195 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import gym
|
||||
import turtle
|
||||
import numpy as np
|
||||
|
||||
# turtle tutorial : https://docs.python.org/3.3/library/turtle.html
|
||||
|
||||
|
||||
def GridWorld(gridmap=None, is_slippery=False):
|
||||
if gridmap is None:
|
||||
gridmap = ['SFFF', 'FHFH', 'FFFH', 'HFFG']
|
||||
env = gym.make("FrozenLake-v0", desc=gridmap, is_slippery=False)
|
||||
env = FrozenLakeWapper(env)
|
||||
return env
|
||||
|
||||
|
||||
class FrozenLakeWapper(gym.Wrapper):
|
||||
def __init__(self, env):
|
||||
gym.Wrapper.__init__(self, env)
|
||||
self.max_y = env.desc.shape[0]
|
||||
self.max_x = env.desc.shape[1]
|
||||
self.t = None
|
||||
self.unit = 50
|
||||
|
||||
def draw_box(self, x, y, fillcolor='', line_color='gray'):
|
||||
self.t.up()
|
||||
self.t.goto(x * self.unit, y * self.unit)
|
||||
self.t.color(line_color)
|
||||
self.t.fillcolor(fillcolor)
|
||||
self.t.setheading(90)
|
||||
self.t.down()
|
||||
self.t.begin_fill()
|
||||
for _ in range(4):
|
||||
self.t.forward(self.unit)
|
||||
self.t.right(90)
|
||||
self.t.end_fill()
|
||||
|
||||
def move_player(self, x, y):
|
||||
self.t.up()
|
||||
self.t.setheading(90)
|
||||
self.t.fillcolor('red')
|
||||
self.t.goto((x + 0.5) * self.unit, (y + 0.5) * self.unit)
|
||||
|
||||
def render(self):
|
||||
if self.t == None:
|
||||
self.t = turtle.Turtle()
|
||||
self.wn = turtle.Screen()
|
||||
self.wn.setup(self.unit * self.max_x + 100,
|
||||
self.unit * self.max_y + 100)
|
||||
self.wn.setworldcoordinates(0, 0, self.unit * self.max_x,
|
||||
self.unit * self.max_y)
|
||||
self.t.shape('circle')
|
||||
self.t.width(2)
|
||||
self.t.speed(0)
|
||||
self.t.color('gray')
|
||||
for i in range(self.desc.shape[0]):
|
||||
for j in range(self.desc.shape[1]):
|
||||
x = j
|
||||
y = self.max_y - 1 - i
|
||||
if self.desc[i][j] == b'S': # Start
|
||||
self.draw_box(x, y, 'white')
|
||||
elif self.desc[i][j] == b'F': # Frozen ice
|
||||
self.draw_box(x, y, 'white')
|
||||
elif self.desc[i][j] == b'G': # Goal
|
||||
self.draw_box(x, y, 'yellow')
|
||||
elif self.desc[i][j] == b'H': # Hole
|
||||
self.draw_box(x, y, 'black')
|
||||
else:
|
||||
self.draw_box(x, y, 'white')
|
||||
self.t.shape('turtle')
|
||||
|
||||
x_pos = self.s % self.max_x
|
||||
y_pos = self.max_y - 1 - int(self.s / self.max_x)
|
||||
self.move_player(x_pos, y_pos)
|
||||
|
||||
|
||||
class CliffWalkingWapper(gym.Wrapper):
|
||||
def __init__(self, env):
|
||||
gym.Wrapper.__init__(self, env)
|
||||
self.t = None
|
||||
self.unit = 50
|
||||
self.max_x = 12
|
||||
self.max_y = 4
|
||||
|
||||
def draw_x_line(self, y, x0, x1, color='gray'):
|
||||
assert x1 > x0
|
||||
self.t.color(color)
|
||||
self.t.setheading(0)
|
||||
self.t.up()
|
||||
self.t.goto(x0, y)
|
||||
self.t.down()
|
||||
self.t.forward(x1 - x0)
|
||||
|
||||
def draw_y_line(self, x, y0, y1, color='gray'):
|
||||
assert y1 > y0
|
||||
self.t.color(color)
|
||||
self.t.setheading(90)
|
||||
self.t.up()
|
||||
self.t.goto(x, y0)
|
||||
self.t.down()
|
||||
self.t.forward(y1 - y0)
|
||||
|
||||
def draw_box(self, x, y, fillcolor='', line_color='gray'):
|
||||
self.t.up()
|
||||
self.t.goto(x * self.unit, y * self.unit)
|
||||
self.t.color(line_color)
|
||||
self.t.fillcolor(fillcolor)
|
||||
self.t.setheading(90)
|
||||
self.t.down()
|
||||
self.t.begin_fill()
|
||||
for i in range(4):
|
||||
self.t.forward(self.unit)
|
||||
self.t.right(90)
|
||||
self.t.end_fill()
|
||||
|
||||
def move_player(self, x, y):
|
||||
self.t.up()
|
||||
self.t.setheading(90)
|
||||
self.t.fillcolor('red')
|
||||
self.t.goto((x + 0.5) * self.unit, (y + 0.5) * self.unit)
|
||||
|
||||
def render(self):
|
||||
if self.t == None:
|
||||
self.t = turtle.Turtle()
|
||||
self.wn = turtle.Screen()
|
||||
self.wn.setup(self.unit * self.max_x + 100,
|
||||
self.unit * self.max_y + 100)
|
||||
self.wn.setworldcoordinates(0, 0, self.unit * self.max_x,
|
||||
self.unit * self.max_y)
|
||||
self.t.shape('circle')
|
||||
self.t.width(2)
|
||||
self.t.speed(0)
|
||||
self.t.color('gray')
|
||||
for _ in range(2):
|
||||
self.t.forward(self.max_x * self.unit)
|
||||
self.t.left(90)
|
||||
self.t.forward(self.max_y * self.unit)
|
||||
self.t.left(90)
|
||||
for i in range(1, self.max_y):
|
||||
self.draw_x_line(
|
||||
y=i * self.unit, x0=0, x1=self.max_x * self.unit)
|
||||
for i in range(1, self.max_x):
|
||||
self.draw_y_line(
|
||||
x=i * self.unit, y0=0, y1=self.max_y * self.unit)
|
||||
|
||||
for i in range(1, self.max_x - 1):
|
||||
self.draw_box(i, 0, 'black')
|
||||
self.draw_box(self.max_x - 1, 0, 'yellow')
|
||||
self.t.shape('turtle')
|
||||
|
||||
x_pos = self.s % self.max_x
|
||||
y_pos = self.max_y - 1 - int(self.s / self.max_x)
|
||||
self.move_player(x_pos, y_pos)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 环境1:FrozenLake, 可以配置冰面是否是滑的
|
||||
# 0 left, 1 down, 2 right, 3 up
|
||||
env = gym.make("FrozenLake-v0", is_slippery=False)
|
||||
env = FrozenLakeWapper(env)
|
||||
|
||||
# 环境2:CliffWalking, 悬崖环境
|
||||
# env = gym.make("CliffWalking-v0") # 0 up, 1 right, 2 down, 3 left
|
||||
# env = CliffWalkingWapper(env)
|
||||
|
||||
# 环境3:自定义格子世界,可以配置地图, S为出发点Start, F为平地Floor, H为洞Hole, G为出口目标Goal
|
||||
# gridmap = [
|
||||
# 'SFFF',
|
||||
# 'FHFF',
|
||||
# 'FFFF',
|
||||
# 'HFGF' ]
|
||||
# env = GridWorld(gridmap)
|
||||
|
||||
env.reset()
|
||||
for step in range(10):
|
||||
action = np.random.randint(0, 4)
|
||||
obs, reward, done, info = env.step(action)
|
||||
print('step {}: action {}, obs {}, reward {}, done {}, info {}'.format(\
|
||||
step, action, obs, reward, done, info))
|
||||
# env.render() # 渲染一帧图像
|
||||
90
docs/code/Q-learning/train.py
Normal file
90
docs/code/Q-learning/train.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import gym
|
||||
from gridworld import CliffWalkingWapper, FrozenLakeWapper
|
||||
from agent import QLearningAgent
|
||||
import time
|
||||
|
||||
|
||||
def run_episode(env, agent, render=False):
|
||||
total_steps = 0 # 记录每个episode走了多少step
|
||||
total_reward = 0
|
||||
|
||||
obs = env.reset() # 重置环境, 重新开一局(即开始新的一个episode)
|
||||
|
||||
while True:
|
||||
action = agent.sample(obs) # 根据算法选择一个动作
|
||||
next_obs, reward, done, _ = env.step(action) # 与环境进行一个交互
|
||||
# 训练 Q-learning算法
|
||||
agent.learn(obs, action, reward, next_obs, done) # 不需要下一步的action
|
||||
|
||||
obs = next_obs # 存储上一个观察值
|
||||
total_reward += reward
|
||||
total_steps += 1 # 计算step数
|
||||
if render:
|
||||
env.render() #渲染新的一帧图形
|
||||
if done:
|
||||
break
|
||||
return total_reward, total_steps
|
||||
|
||||
|
||||
def test_episode(env, agent):
|
||||
total_reward = 0
|
||||
obs = env.reset()
|
||||
while True:
|
||||
action = agent.predict(obs) # greedy
|
||||
next_obs, reward, done, _ = env.step(action)
|
||||
total_reward += reward
|
||||
obs = next_obs
|
||||
time.sleep(0.5)
|
||||
env.render()
|
||||
if done:
|
||||
print('test reward = %.1f' % (total_reward))
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
# env = gym.make("FrozenLake-v0", is_slippery=False) # 0 left, 1 down, 2 right, 3 up
|
||||
# env = FrozenLakeWapper(env)
|
||||
|
||||
env = gym.make("CliffWalking-v0") # 0 up, 1 right, 2 down, 3 left
|
||||
env = CliffWalkingWapper(env)
|
||||
|
||||
agent = QLearningAgent(
|
||||
obs_n=env.observation_space.n,
|
||||
act_n=env.action_space.n,
|
||||
learning_rate=0.1,
|
||||
gamma=0.9,
|
||||
e_greed=0.1)
|
||||
|
||||
is_render = False
|
||||
for episode in range(500):
|
||||
ep_reward, ep_steps = run_episode(env, agent, is_render)
|
||||
print('Episode %s: steps = %s , reward = %.1f' % (episode, ep_steps,
|
||||
ep_reward))
|
||||
|
||||
# 每隔20个episode渲染一下看看效果
|
||||
if episode % 20 == 0:
|
||||
is_render = True
|
||||
else:
|
||||
is_render = False
|
||||
# 训练结束,查看算法效果
|
||||
test_episode(env, agent)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
74
docs/code/Sarsa/agent.py
Normal file
74
docs/code/Sarsa/agent.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import numpy as np
|
||||
|
||||
# 根据Q表格选动作
|
||||
class SarsaAgent(object):
|
||||
def __init__(self,
|
||||
obs_n,
|
||||
act_n,
|
||||
learning_rate=0.01,
|
||||
gamma=0.9,
|
||||
e_greed=0.1):
|
||||
self.act_n = act_n # 动作维度,有几个动作可选
|
||||
self.lr = learning_rate # 学习率
|
||||
self.gamma = gamma # reward的衰减率
|
||||
self.epsilon = e_greed # 按一定概率随机选动作
|
||||
self.Q = np.zeros((obs_n, act_n)) # 初始化Q表格
|
||||
|
||||
# 根据输入观察值,采样输出的动作值,带探索(epsilon-greedy,训练时用这个方法)
|
||||
def sample(self, obs):
|
||||
if np.random.uniform(0, 1) < (1.0 - self.epsilon): #根据table的Q值选动作
|
||||
action = self.predict(obs)
|
||||
else:
|
||||
action = np.random.choice(self.act_n) #有一定概率随机探索选取一个动作
|
||||
return action
|
||||
|
||||
# 根据输入观察值,预测输出的动作值(已有里面挑最大,贪心的算法,只有利用,没有探索)
|
||||
def predict(self, obs):
|
||||
Q_list = self.Q[obs, :]
|
||||
maxQ = np.max(Q_list) # 找到最大Q对应的下标
|
||||
action_list = np.where(Q_list == maxQ)[0] # maxQ可能对应多个action
|
||||
action = np.random.choice(action_list) # 从这些action中随机挑一个action(可以打印出来看看)
|
||||
return action
|
||||
|
||||
# 学习方法,也就是更新Q-table的方法
|
||||
def learn(self, obs, action, reward, next_obs, next_action, done):
|
||||
""" on-policy
|
||||
obs: 交互前的obs, s_t
|
||||
action: 本次交互选择的action, a_t
|
||||
reward: 本次动作获得的奖励r
|
||||
next_obs: 本次交互后的obs, s_t+1
|
||||
next_action: 根据当前Q表格, 针对next_obs会选择的动作, a_t+1
|
||||
done: episode是否结束
|
||||
"""
|
||||
predict_Q = self.Q[obs, action]
|
||||
if done: # done为ture的话,代表这是episode最后一个状态
|
||||
target_Q = reward # 没有下一个状态了
|
||||
else:
|
||||
target_Q = reward + self.gamma * self.Q[next_obs,
|
||||
next_action] # Sarsa
|
||||
self.Q[obs, action] += self.lr * (target_Q - predict_Q) # 修正q
|
||||
|
||||
def save(self):
|
||||
npy_file = './q_table.npy'
|
||||
np.save(npy_file, self.Q)
|
||||
print(npy_file + ' saved.')
|
||||
|
||||
def restore(self, npy_file='./q_table.npy'):
|
||||
self.Q = np.load(npy_file)
|
||||
print(npy_file + ' loaded.')
|
||||
195
docs/code/Sarsa/gridworld.py
Normal file
195
docs/code/Sarsa/gridworld.py
Normal file
@@ -0,0 +1,195 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import gym
|
||||
import turtle
|
||||
import numpy as np
|
||||
|
||||
# turtle tutorial : https://docs.python.org/3.3/library/turtle.html
|
||||
|
||||
|
||||
def GridWorld(gridmap=None, is_slippery=False):
|
||||
if gridmap is None:
|
||||
gridmap = ['SFFF', 'FHFH', 'FFFH', 'HFFG']
|
||||
env = gym.make("FrozenLake-v0", desc=gridmap, is_slippery=False)
|
||||
env = FrozenLakeWapper(env)
|
||||
return env
|
||||
|
||||
|
||||
class FrozenLakeWapper(gym.Wrapper):
|
||||
def __init__(self, env):
|
||||
gym.Wrapper.__init__(self, env)
|
||||
self.max_y = env.desc.shape[0]
|
||||
self.max_x = env.desc.shape[1]
|
||||
self.t = None
|
||||
self.unit = 50
|
||||
|
||||
def draw_box(self, x, y, fillcolor='', line_color='gray'):
|
||||
self.t.up()
|
||||
self.t.goto(x * self.unit, y * self.unit)
|
||||
self.t.color(line_color)
|
||||
self.t.fillcolor(fillcolor)
|
||||
self.t.setheading(90)
|
||||
self.t.down()
|
||||
self.t.begin_fill()
|
||||
for _ in range(4):
|
||||
self.t.forward(self.unit)
|
||||
self.t.right(90)
|
||||
self.t.end_fill()
|
||||
|
||||
def move_player(self, x, y):
|
||||
self.t.up()
|
||||
self.t.setheading(90)
|
||||
self.t.fillcolor('red')
|
||||
self.t.goto((x + 0.5) * self.unit, (y + 0.5) * self.unit)
|
||||
|
||||
def render(self):
|
||||
if self.t == None:
|
||||
self.t = turtle.Turtle()
|
||||
self.wn = turtle.Screen()
|
||||
self.wn.setup(self.unit * self.max_x + 100,
|
||||
self.unit * self.max_y + 100)
|
||||
self.wn.setworldcoordinates(0, 0, self.unit * self.max_x,
|
||||
self.unit * self.max_y)
|
||||
self.t.shape('circle')
|
||||
self.t.width(2)
|
||||
self.t.speed(0)
|
||||
self.t.color('gray')
|
||||
for i in range(self.desc.shape[0]):
|
||||
for j in range(self.desc.shape[1]):
|
||||
x = j
|
||||
y = self.max_y - 1 - i
|
||||
if self.desc[i][j] == b'S': # Start
|
||||
self.draw_box(x, y, 'white')
|
||||
elif self.desc[i][j] == b'F': # Frozen ice
|
||||
self.draw_box(x, y, 'white')
|
||||
elif self.desc[i][j] == b'G': # Goal
|
||||
self.draw_box(x, y, 'yellow')
|
||||
elif self.desc[i][j] == b'H': # Hole
|
||||
self.draw_box(x, y, 'black')
|
||||
else:
|
||||
self.draw_box(x, y, 'white')
|
||||
self.t.shape('turtle')
|
||||
|
||||
x_pos = self.s % self.max_x
|
||||
y_pos = self.max_y - 1 - int(self.s / self.max_x)
|
||||
self.move_player(x_pos, y_pos)
|
||||
|
||||
|
||||
class CliffWalkingWapper(gym.Wrapper):
|
||||
def __init__(self, env):
|
||||
gym.Wrapper.__init__(self, env)
|
||||
self.t = None
|
||||
self.unit = 50
|
||||
self.max_x = 12
|
||||
self.max_y = 4
|
||||
|
||||
def draw_x_line(self, y, x0, x1, color='gray'):
|
||||
assert x1 > x0
|
||||
self.t.color(color)
|
||||
self.t.setheading(0)
|
||||
self.t.up()
|
||||
self.t.goto(x0, y)
|
||||
self.t.down()
|
||||
self.t.forward(x1 - x0)
|
||||
|
||||
def draw_y_line(self, x, y0, y1, color='gray'):
|
||||
assert y1 > y0
|
||||
self.t.color(color)
|
||||
self.t.setheading(90)
|
||||
self.t.up()
|
||||
self.t.goto(x, y0)
|
||||
self.t.down()
|
||||
self.t.forward(y1 - y0)
|
||||
|
||||
def draw_box(self, x, y, fillcolor='', line_color='gray'):
|
||||
self.t.up()
|
||||
self.t.goto(x * self.unit, y * self.unit)
|
||||
self.t.color(line_color)
|
||||
self.t.fillcolor(fillcolor)
|
||||
self.t.setheading(90)
|
||||
self.t.down()
|
||||
self.t.begin_fill()
|
||||
for i in range(4):
|
||||
self.t.forward(self.unit)
|
||||
self.t.right(90)
|
||||
self.t.end_fill()
|
||||
|
||||
def move_player(self, x, y):
|
||||
self.t.up()
|
||||
self.t.setheading(90)
|
||||
self.t.fillcolor('red')
|
||||
self.t.goto((x + 0.5) * self.unit, (y + 0.5) * self.unit)
|
||||
|
||||
def render(self):
|
||||
if self.t == None:
|
||||
self.t = turtle.Turtle()
|
||||
self.wn = turtle.Screen()
|
||||
self.wn.setup(self.unit * self.max_x + 100,
|
||||
self.unit * self.max_y + 100)
|
||||
self.wn.setworldcoordinates(0, 0, self.unit * self.max_x,
|
||||
self.unit * self.max_y)
|
||||
self.t.shape('circle')
|
||||
self.t.width(2)
|
||||
self.t.speed(0)
|
||||
self.t.color('gray')
|
||||
for _ in range(2):
|
||||
self.t.forward(self.max_x * self.unit)
|
||||
self.t.left(90)
|
||||
self.t.forward(self.max_y * self.unit)
|
||||
self.t.left(90)
|
||||
for i in range(1, self.max_y):
|
||||
self.draw_x_line(
|
||||
y=i * self.unit, x0=0, x1=self.max_x * self.unit)
|
||||
for i in range(1, self.max_x):
|
||||
self.draw_y_line(
|
||||
x=i * self.unit, y0=0, y1=self.max_y * self.unit)
|
||||
|
||||
for i in range(1, self.max_x - 1):
|
||||
self.draw_box(i, 0, 'black')
|
||||
self.draw_box(self.max_x - 1, 0, 'yellow')
|
||||
self.t.shape('turtle')
|
||||
|
||||
x_pos = self.s % self.max_x
|
||||
y_pos = self.max_y - 1 - int(self.s / self.max_x)
|
||||
self.move_player(x_pos, y_pos)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 环境1:FrozenLake, 可以配置冰面是否是滑的
|
||||
# 0 left, 1 down, 2 right, 3 up
|
||||
env = gym.make("FrozenLake-v0", is_slippery=False)
|
||||
env = FrozenLakeWapper(env)
|
||||
|
||||
# 环境2:CliffWalking, 悬崖环境
|
||||
# env = gym.make("CliffWalking-v0") # 0 up, 1 right, 2 down, 3 left
|
||||
# env = CliffWalkingWapper(env)
|
||||
|
||||
# 环境3:自定义格子世界,可以配置地图, S为出发点Start, F为平地Floor, H为洞Hole, G为出口目标Goal
|
||||
# gridmap = [
|
||||
# 'SFFF',
|
||||
# 'FHFF',
|
||||
# 'FFFF',
|
||||
# 'HFGF' ]
|
||||
# env = GridWorld(gridmap)
|
||||
|
||||
env.reset()
|
||||
for step in range(10):
|
||||
action = np.random.randint(0, 4)
|
||||
obs, reward, done, info = env.step(action)
|
||||
print('step {}: action {}, obs {}, reward {}, done {}, info {}'.format(\
|
||||
step, action, obs, reward, done, info))
|
||||
# env.render() # 渲染一帧图像
|
||||
92
docs/code/Sarsa/train.py
Normal file
92
docs/code/Sarsa/train.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import gym
|
||||
from gridworld import CliffWalkingWapper, FrozenLakeWapper
|
||||
from agent import SarsaAgent
|
||||
import time
|
||||
|
||||
|
||||
def run_episode(env, agent, render=False):
|
||||
total_steps = 0 # 记录每个episode走了多少step
|
||||
total_reward = 0
|
||||
|
||||
obs = env.reset() # 重置环境, 重新开一局(即开始新的一个episode)
|
||||
action = agent.sample(obs) # 根据算法选择一个动作
|
||||
|
||||
while True:
|
||||
next_obs, reward, done, _ = env.step(action) # 与环境进行一个交互
|
||||
next_action = agent.sample(next_obs) # 根据算法选择一个动作
|
||||
# 训练 Sarsa 算法
|
||||
agent.learn(obs, action, reward, next_obs, next_action, done)
|
||||
|
||||
action = next_action
|
||||
obs = next_obs # 存储上一个观察值
|
||||
total_reward += reward
|
||||
total_steps += 1 # 计算step数
|
||||
if render:
|
||||
env.render() #渲染新的一帧图形
|
||||
if done:
|
||||
break
|
||||
return total_reward, total_steps
|
||||
|
||||
|
||||
def test_episode(env, agent):
|
||||
total_reward = 0
|
||||
obs = env.reset()
|
||||
while True:
|
||||
action = agent.predict(obs) # greedy,只取最优的动作
|
||||
next_obs, reward, done, _ = env.step(action)
|
||||
total_reward += reward
|
||||
obs = next_obs
|
||||
time.sleep(0.5) # 每个step延迟0.5秒来看看效果
|
||||
env.render()
|
||||
if done:
|
||||
print('test reward = %.1f' % (total_reward))
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
# env = gym.make("FrozenLake-v0", is_slippery=False) # 0 left, 1 down, 2 right, 3 up
|
||||
# env = FrozenLakeWapper(env)
|
||||
|
||||
env = gym.make("CliffWalking-v0") # 0 up, 1 right, 2 down, 3 left
|
||||
env = CliffWalkingWapper(env) # 这行不加也可以,这个是为了显示效果更好一点
|
||||
|
||||
agent = SarsaAgent(
|
||||
obs_n=env.observation_space.n,
|
||||
act_n=env.action_space.n,
|
||||
learning_rate=0.1,
|
||||
gamma=0.9,
|
||||
e_greed=0.1)
|
||||
|
||||
is_render = False
|
||||
for episode in range(500):
|
||||
ep_reward, ep_steps = run_episode(env, agent, is_render)
|
||||
print('Episode %s: steps = %s , reward = %.1f' % (episode, ep_steps,
|
||||
ep_reward))
|
||||
|
||||
# 每隔20个episode渲染一下看看效果(每个episode都渲染的话,时间会比较长)
|
||||
if episode % 20 == 0:
|
||||
is_render = True
|
||||
else:
|
||||
is_render = False
|
||||
# 训练结束,查看算法效果
|
||||
test_episode(env, agent)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user