text.html 13.1 KB
Newer Older
ToTensor's avatar
ToTensor 已提交
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
 
<p class="content">现在开始用Q-Learning算法解决冰湖挑战问题。前面说过,冰湖挑战是一个4×4的迷宫,共有16个状态(0~15)。在每个状态中,有上、下、左、右4种选择(也就是智能体的4种可能的移动方向,其中左、下、右、上分别对应0、1、2、3)。因此Q-table就形成了一个16×4的矩阵。</p> 
<h3 class="thirdTitle" id="bw284"><a >11.4.1 环境的初始化</a></h3> 
<p class="content">下面就导入Gym库:</p> 
<div class="content_106"> 
 <p class="content_105">import gym # 导入Gym库</p> 
 <p class="content_105">import numpy as np # 导入Num Py库</p> 
</div> 
<p class="content">初始化冰湖挑战的环境:</p> 
<div class="content_106"> 
 <p class="content_105">env = gym.make('Frozen Lake-v0', is_slippery=False) # 生成冰湖挑战的环境</p> 
 <p class="content_105">env.reset() # 初始化冰湖挑战的环境</p> 
 <p class="content_105">print("状态数:", env.observation_space.n)</p> 
 <p class="content_105">print("动作数:", env.action_space.n)</p> 
 <p class="content_105">状态数:16</p> 
 <p class="content_105">动作数:4</p> 
</div> 
<p class="content">随机走20步:</p> 
<div class="content_106"> 
 <p class="content_105">for _ in range(20): # 随机走20步</p> 
 <p class="content_121">env.render() # 生成环境</p> 
 <p class="content_121">env.step(env.action_space.sample()) # 随机乱走</p> 
 <p class="content_105">env.close() # 关闭冰湖挑战的环境</p> 
</div> 
<p class="content">这一盘试玩中,到了第7步之后,就掉进了冰窟窿,游戏结束,如下所示:</p> 
<div class="bodyPic_104"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0335-0434.jpg"> 
</div> 
<p class="content">这个问题,我们唯一的目标,就是对该环境创建一个有用的Q-Table,以作为未来冰湖挑战者的行动指南和策略地图。</p> 
<p class="content">首先,来初始化Q-Table:</p> 
<div class="content_106"> 
 <p class="content_105"># 初始化Q-Table</p> 
 <p class="content_105">Q = np.zeros([env.observation_space.n, env.action_space.n])</p> 
 <p class="content_105">print(Q)</p> 
</div> 
<p class="content">输出结果如下:</p> 
<div class="content_126"> 
 <p class="content_109">[[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]</p> 
 <p class="content_109">[0.0.0.0.]]</p> 
</div> 
<p class="content">此时Q-Table是16×4的全0值矩阵。</p> 
<h3 class="thirdTitle" id="bw285"><a >11.4.2 Q-Learning算法的实现</a></h3> 
<p class="content">下面来实现Q-Learning算法:</p> 
<div class="content_106"> 
 <p class="content_105"># 初始化参数</p> 
 <p class="content_105">alpha = 0.6 # 学习速率</p> 
 <p class="content_105">gamma = 0.75 # 奖励折扣</p> 
 <p class="content_105">episodes = 500 # 游戏盘数</p> 
 <p class="content_105">r_history = [] # 奖励值的历史信息</p> 
 <p class="content_105">j_history = [] # 步数的历史信息</p> 
 <p class="content_105">for i in range(episodes):</p> 
 <p class="content_121">s = env.reset() # 重置环境</p> 
 <p class="content_121">r All = 0</p> 
 <p class="content_121">d = False</p> 
 <p class="content_121">j = 0</p> 
 <p class="content_121">#Q-Learning算法的实现</p> 
 <p class="content_121">while j < 99:</p> 
 <p class="content_111">j+=1</p> 
 <p class="content_111"># 通过Q-Table选择下一个动作, 但是增加随机噪声, 该噪声随着盘数的增加而减小</p> 
 <p class="content_111"># 所增加的随机噪声其实就是ε-Greedy策略的实现, 通过它在探索和利用之间平衡</p> 
 <p class="content_111">a = np.argmax(Q[s, :] +</p> 
 <p class="content_119">np.random.randn(1, env.action_space.n)*(1./(i+1)))</p> 
 <p class="content_111"># 智能体执行动作, 并从环境中得到新的状态和奖励</p> 
 <p class="content_111">s1, r, d, _ = env.step(a)</p> 
 <p class="content_111"># 通过贪心策略更新Q-Table, 选择新状态中的最大Q值</p> 
 <p class="content_111">Q[s, a] = Q[s, a] + alpha*(r + gamma*np.max(Q[s1, :]) - Q[s, a])</p> 
 <p class="content_111">r All += r</p> 
 <p class="content_111">s = s1</p> 
 <p class="content_111">if d == True:</p> 
 <p class="content_125">break</p> 
 <p class="content_121">j_history.append(j)</p> 
 <p class="content_121">r_history.append(r All)</p> 
 <p class="content_105">print(Q)</p> 
</div> 
<p class="content">其中两个最重要的,就是智能体动作的选择和Q-Table的更新这两段代码。</p> 
<p class="content">■智能体动作的选择,采用的是<span class="italic">ε</span>-Greedy策略,力图在探索和利用之间平衡。<span class="italic">ε</span>值在此处实际上是一个随机值。</p> 
<p class="content">■Q-Table的更新,则采用贪心策略,总是选择长期奖励最大<span class="italic">Q</span>值来更新Q-Table。</p> 
<p class="content">200盘游戏过后,Q-Table如下:</p> 
<div class="content_113"> 
 <p class="content_109">[[0.00896807 0.23730469 0.     0.0110088 ]</p> 
 <p class="content_110">[0.00697516 0.     0.     0.    ]</p> 
 <p class="content_110">[0.    0.     0.     0.    ]</p> 
 <p class="content_110">[0.     0.     0.     0.    ]</p> 
 <p class="content_110">[0.03321506  0.31640625  0.     0.00498226 ]</p> 
 <p class="content_110">[0.     0.     0.     0.    ]</p> 
 <p class="content_110">[0.     0.     0.     0.    ]</p> 
 <p class="content_110">[0.     0.     0.     0.    ]</p> 
 <p class="content_110">[0.     0.     0.421875  0.    ]</p> 
 <p class="content_110">[0.     0.     0.5625   0.    ]</p> 
 <p class="content_110">[0.     0.75    0.     0.    ]</p> 
 <p class="content_110">[0.     0.     0.     0.    ]</p> 
 <p class="content_110">[0.     0.     0.     0.    ]</p> 
 <p class="content_110">[0.     0.     0.     0.    ]</p> 
 <p class="content_110">[0.     0.     1.     0.    ]</p> 
 <p class="content_110">[0.     0.     0.     0.    ]]</p> 
</div> 
<p class="content">Q-Table里面的内容,就是强化学习学到的经验,也就是后续盘中智能体的行动指南。可以看出,越是接近冰湖挑战终点的状态,<span class="italic">Q</span>值越大。这是因为<span class="italic">γ</span>值的存在(<span class="italic">γ</span>=0.75),<span class="italic">Q</span>值逆向传播的过程呈现出逐步的衰减。</p> 
<p class="content">还可以绘制出游戏的奖惩值随迭代次数而变化的曲线,以及每盘游戏的步数:</p> 
<div class="content_165"> 
 <p class="content_105">import matplotlib.pyplot as plt # 导入Matplotlib库</p> 
 <p class="content_105">plt.figure(figsize=(16, 5))</p> 
 <p class="content_105">plt.subplot(1, 2, 1)</p> 
 <p class="content_105">plt.plot(r_history)</p> 
 <p class="content_105">plt.subplot(1, 2, 2)</p> 
 <p class="content_105">plt.plot(j_history)</p> 
</div> 
<p class="content">绘制出的曲线如下图所示。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0337-0435.jpg"> 
 <p class="imgtitle">Q-Learing算法中Q-Table的收敛很快</p> 
</div> 
<p class="content">左图告诉我们的信息是,开始的时候,智能体只是盲目地走动,有时走运,可以得到1分,大部分是得到0分,直到Q-Table里面的知识比较丰富了,几乎每盘游戏都得到1分。而右图告诉我们的信息是,智能体没有知识的初期,游戏的步数摇摆不定,要么很快掉进冰窟窿,要么来回地乱走。但是到了大概50盘之后,Q-Table的知识积累完成了,这以后,智能体可以保证每次都得到奖励,而且按照6步的速度迅速地走到飞盘所在地,完成冰湖挑战。</p> 
<h3 class="thirdTitle" id="bw286"><a >11.4.3 Q-Table的更新过程</a></h3> 
<p class="content">下面详细地看一下Q-Table的更新过程。</p> 
<p class="content">在开始阶段,智能体将做出大量尝试(甚至可能是10次、100次的尝试),然而,因为没有任何方向感和Q-Table作为指引,智能体的知识空间如同一张白纸。这时智能体处于婴儿态,经常性地坠入冰窟窿,而且得不到系统的任何奖赏。因为在冰湖挑战的环境设定中,掉进冰窟窿只是结束游戏,并没有惩罚分数,因此初始很多盘的尝试并不能带来Q-Table的更新。</p> 
<p class="content">如果用env.render方法显示出所有的环境状态,可以看出智能体的探索过程。</p> 
<p class="content">第1盘:</p> 
<div class="bodyPic_104"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0338-0436.jpg"> 
</div> 
<p class="content">第2盘:</p> 
<div class="bodyPic_104"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0338-0437.jpg"> 
</div> 
<p class="content">第3盘:</p> 
<div class="bodyPic_104"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0338-0438.jpg"> 
</div> 
<p class="content">直到终于有一天,奇迹发生了,智能体达到了状态14(如第N盘的输出所示),并且随机地选择出了正确的动作,它终于可以得到它梦寐以求的奖励—就好像抓来抓去的婴儿终于抓到了一颗能吃的糖果。</p> 
<p class="content"><span class="italic">N</span>盘:</p> 
<div class="bodyPic_104"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0338-0439.jpg"> 
</div> 
<p class="content">第一个Q-Table状态的更新可以称之为奇点:</p> 
<div class="content_113"> 
 <p class="content_109">[[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.0.]</p> 
 <p class="content_110">[0.0.0.75 0.]</p> 
 <p class="content_110">[0.0.0.0.]]</p> 
</div> 
<p class="content">此时<span class="italic">Q</span>[14,2]被更新为0.75(加了<span class="italic">γ</span>折扣的奖励值)。以后,有了这个值做引导,Q-Table自身更新的能力被增强不少:</p> 
<p class="content_114">Q[s, a] = Q[s, a] + lr*(r + y*np.max(Q[s1, :]) - Q[s, a])</p> 
<p class="content">因为在Q-Table的更新策略中,任何奖励<span class="italic">r</span>或者<span class="italic">Q</span>[s1,:]的值都会带来之前状态<span class="italic">Q</span><span class="italic">s</span><span class="italic">a</span>]的更新。也就是说,当智能体未来有朝一日,从状态10或13踩进状态14,都会带来状态10或13中<span class="italic">Q</span>值的更新,智能体也能够根据Q-Table的指引从状态14走到终点。之后开始发生蝴蝶效应,第一个奖励值就像波浪一样,它荡起的涟漪越传越远,从后面的状态向前传递。</p> 
<p class="content_101"><img alt="" class="h-pic" src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0339-0440.jpg">咖哥发言</p> 
<p class="content">冰湖挑战的环境设定的一些细节如下。</p> 
<p class="content">第一,环境初始化时有一个is_slippery开关,默认是Ture值,这代表冰面很滑,此时智能体不能顺利地走到自己想去的方向。这种设定大大增加了随机性和游戏的难度。为了降低难度,我把这个开关,设为False,以演示Q-Table的正常更新过程。同学们可以尝试设置is_slippery=Ture,重新进行冰湖挑战。</p> 
<p class="content">第二,冰湖挑战设定所有的状态<span class="italic">Q</span>值的最大值为1。也就是说,当累积得到的奖励分大于1时,就不继续增加该状态<span class="italic">Q</span><span class="italic">s</span><span class="italic">a</span>]的<span class="italic">Q</span>值了。</p>