点击上方关注,All in AI中国
作者:Mike Shi
今天我想展示如何通过50行Python编码训练一台机器来平衡一根长杆!我们将使用标准的OpenAI Gym作为测试环境,并创建我们的代理,采用numpy。
推车平衡长杆(cartpole)问题就是我们必须左右推动推车以平衡车上的长杆。这类似于在我们的指尖上保持铅笔垂直平衡,这非常具有挑战性!
在我们开始之前,你可以在这里查看repl.it上的最终演示。
强化学习(RL)速成课程
如果这是你第一次接触机器学习或强化学习,我将在这里介绍一些基础知识,这样你就可以了解我们将在这里使用的术语。如果这是第一次,可以继续向下进行,并制定我们的策略!
强化学习
强化学习(RL)是研究训练代理(算法/机器)来执行某些任务/动作而不明确告诉它如何做到这一点的研究领域。可以把它想象成一名婴儿,以随机的方式移动双腿。如果婴儿直立,我们会给他一个糖果/奖励。同样,代理的目标是在其生命周期内最大化总奖励,我们将决定与想要完成的任务相一致的奖励。对于直立的例子,直立时奖励1,否则为0。
强化学习(RL)代理的一个知名的例子是AlphaGo,其中AlphaGo这个代理已经学会了如何玩围棋游戏,并最大化其奖励(赢得游戏)。在本教程中,我们将创建一个代理,通过向左或向右推动推车,可以解决平衡推车上的长杆问题。
状态
如果需要复习,可以了解一下乒乓球比赛
一种状态就是游戏此刻的样子。我们通常处理游戏的数字表示。在乒乓球比赛中,它可能是每个球拍的垂直位置和乒乓球的x和y坐标。在推车长杆平衡的情况下,我们的状态由4个数字组成:推车的位置、推车的速度、工杆的位置(作为角度)和长杆的角速度。这4个数字作为数组(或向量)提供,这个很重要。理解状态是一个数字数组意味着我们可以对它进行一些数学运算,根据状态决定采取什么行动。
策略
策略是一种可以处理游戏状态的函数(例如推车和杆的位置)。并输出代理应该在该位置采取的动作(例如,向左推动推车)。在代理采取我们选择的操作后,游戏将使用下一个状态进行更新,我们将再次将其纳入策略以做出决策。这种情况一直持续到游戏以某种方式结束。这个策略非常重要,是我们正在寻找的代理背后的决策能力。
点积
两个数组(向量)之间的点积简单地将第一个数组的每个元素乘以第二个数组的对应元素,并将它们全部加在一起。假设我们想找到数组A和B的点积,它只是A [0] * B [0] + A [1] * B [1] ......我们将使用这个操作来增加状态(这是一个数组)乘以另一个数组(这将是我们的策略)。我们将在下一节中看到这一点。
开发策略
为了解决我们的推车长杆平衡游戏问题,我们希望通过采用机器学习策略赢得比赛或最大化我们的奖励。
对于我们要开发的代理,我们将策略表示为一个由4个数字组成的数组,这代表状态的每个组成部分的"重要性"(购物车位置、杆的位置等)。然后将策略数组与状态点乘以输出单个数字。根据数字是正数还是负数,我们将向左或向右推动推车。
如果这听起来有点抽象,那么让我们选择一个具体的例子,看看会发生什么。
假设推车在游戏中居中并且静止,并且杆向右倾斜并且也向右倾斜。它看起来像这样:
关联状态可能如下所示:
然后状态数组将是[0,0,0.2,0.05]。
现在直观地说,我们要把推车推向右边,使长杆垂直。而从一次训练中得到了一个很好的策略,其策略数据如下:[-0.116,0.332,0.207 0.352]。让我们快速完成数学运算,看看这个策略将作为此状态的一个动作输出。
在这里,我们将点状态数组[0,0,0.2,0.05]和策略数组(粘贴在上面)。如果数字是正数,我们将推车推向右边,如果数字是负数,我们向左推。
其结果是积极的,这意味着策略也会在这种情况下推动推车向右推进,这正是我们希望它表现的方式。
现在这一切都很好,我们所需要的只有4个神奇的数字,就像上面提到的那样有助于解决这个问题。现在,我们如何获得这些数字?如果我们只是随机挑选它们怎么办?它的效果如何?让我们找出并开始挖掘代码!
开始编辑!
让我们在repl.it上打开一个Python实例。 Repl.it允许你快速启动大量不同编程环境的云计算实例,并在可在任何地方访问的强大云计算IDE中编辑代码!
安装软件包
我们首先安装这个项目所需的两个软件包:numpy帮助进行数值计算,OpenAI Gym作为代理的模拟器。
只需在编辑器左侧的包搜索工具中输入gym和numpy,然后单击加号按钮即可安装包。
打下基础
让我们首先将我们刚刚安装的两个依赖项导入到main.py脚本中并设置一个新的gym环境:
接下来,我们将定义一个名为"play"的函数,该函数将被赋予一个环境和一个策略数组,并将在环境中播放策略数组并返回得分,以及每个时间步的游戏快照(观察)。我们将使用分数来告诉我们策略的效果,以及我们查看策略在游戏中表现的快照。这样我们就可以测试不同的策略,看看他们在游戏中的表现如何!
让我们从功能定义开始,然后将游戏重置为开始状态。
接下来,我们将初始化一些变量以跟踪游戏是否已经结束,策略的总得分以及游戏中每个步骤的快照(观察)。
现在我们只是玩游戏的部分花很多时间,直到gym环境告诉我们游戏已经完成。
上面的大部分代码主要是在玩游戏并记录结果。我们的策略的实际代码就是这两行:
我们在这里所做的只是策略数组和状态(观察)数组之间的点积运算,就像我们之前在具体例子中所示的那样。然后我们要么选择1或0(左或右)的动作,取决于结果是正还是负。
到目前为止,我们的main.py应该如下所示:
现在我们要开始玩一些游戏,并找到我们的最优策略!
玩第一场比赛
既然我们有一个游戏函数,并告诉我们的策略有多好,我们将要开始制定一些策略,看看它们的表现如何。
如果我们只是尝试插入一些随机策略呢?我们能走多远?让我们使用numpy生成我们的策略,这是一个4元素数组或4×1矩阵。它会选择0到1之间的4个数字作为我们的策略。
有了这个策略,以及我们上面创建的环境,我们可以将它们插入游戏并获得分数。
只需点击运行即可运行我们的脚本。它应该输出我们的策略得分。
这个游戏的最大得分是500,很可能是你的策略没有那么好。如果你这么做了,恭喜!今天一定是你的幸运日!只是看到一个数字并不是很有意义,如果我们可以想象代理如何玩游戏,那就太好了,下一步我们就会设置它!
观察代理
要观察我们的代理,我们将使用Flask设置轻量级服务器,以便我们可以在浏览器中查看代理的性能。Flask是一个轻量级的Python 服务器背后的细节训练代理并不重要。
我们首先想要将Flask安装为Python包,就像我们在前面几节中安装gym和numpy一样。
接下来,在脚本的底部,我们将创建一个Flask服务器。它将在数据端点上显示游戏的每个帧的记录。它将在/data端点上显示游戏的每个帧的记录,并在/上托管UI。
另外,我们需要添加两个文件。一个是项目的空白Python文件。这是repl.it如何检测repl是处于eval模式还是项目模式的技术性。只需使用新文件按钮添加空白Python脚本即可。
之后我们还想创建一个将承载渲染UI的index.html。我不会在此处深入了解详细信息,只需将此index.html上传到你的repl.it项目即可。
你现在应该有一个如下所示的项目目录:
现在有了这两个新文件,当我们运行repl时,它现在也应该回放我们的策略。有了这个,让我们试着找到一个最优的策略!
策略检索
在我们的第一次通过中,我们只是随机选择了一个策略,但是如果我们选择了一些策略,并且只保留那个做得最好的策略呢?
让我们回到发布策略的部分,而不是仅生成一个,让我们编写一个循环来生成一些循环,并跟踪每个策略的执行情况,并仅保存最佳策略。
我们将首先创建一个名为max的元组,它将存储我们迄今为止看到的最佳策略的得分,观察和策略数组。
接下来,我们将生成并评估10个策略,并在最大值中保存最佳策略。
我们还必须告诉我们的/data端点返回最佳策略的重放。
这个终点:
应改为:
你的main.py应该现在看起来像这样:
如果我们现在运行repl,我们应该获得500的最大分数,如果没有,请再次尝试运行repl!我们还可以完美地观察杆的策略平衡!哇,这很简单!
并不是那么快
或许它不是。我们在第一部分中以某种方式作弊。首先,我们只是在0到1的范围内随机创建了策略数组。这恰好可行,但是如果我们翻转大于运算符,我们将看到代理将会遭遇失败。如果你尝试改变:action = 1 if outcome > 0 else 0 to action = 1 if outcome < 0 else 0.
这似乎并不是非常强大,因为如果我们恰好选择少于而不是大于这个数,我们永远找不到一个可以解决游戏的策略。为了缓解这种情况,我们实际上应该生成带有负数的策略。这将使得找到一个更好的策略变得更加困难(因为许多负面策略并不好),但我们不再通过将我们的特定算法与特定游戏相匹配来"欺骗"。如果我们试图在OpenAI gym的其他环境中这样做,其算法肯定会失败。
要做到这一点而不是使用policy = np.random.rand(1,4),我们将改为policy = np.random.rand(1,4)- 0.5。这样我们策略中的每个数字都在-0.5到0.5之间,而不是0到1。但是因为这更难,我们还想搜索更多策略。在上面的for循环中,不是迭代10个策略,而是通过在for _ in range(100):中更改_的读取代码来尝试100个策略:在此你可以首先尝试遍历10个策略,看看现在使用负数获得好的策略有多难。
现在我们的main.py应该如下所示:
如果你现在运行repl,无论我们使用的是否大于或小于,我们仍然可以为游戏找到一个好的策略。
并不是那么快(第二部分)
但是等等,还有更多!即使我们的策略可以在一次运行中达到最高分500,但每次都能做到吗?当我们生成100个策略,并选择在单一运行中表现最佳的策略时,该策略可能只是非常幸运,而且它可能是一个非常糟糕的策略,也许恰好有一个非常好的策略运行。这是因为游戏本身具有随机性因素(起始位置每次都不同),因此策略可能只适用于一个起始位置,而不是其他起始位置。
因此,为了解决这个问题,我们想要评估策略在多次试验中的表现。现在,让我们采取我们之前发现的最佳策略,看看它在100次试验中的表现如何。
在这里,我们正在播放最佳政策(最大指数2)100次,并且每次都记录得分。然后我们使用numpy计算平均分数并将其打印到终端。没有严格的已发布的"已解决"定义,但它应该只有少数几个点。你可能会注意到最好的策略实际上可能是低于平均水平。不过,我会把解决方案留给你来决定!
done = True
恭喜!我们已经成功创建了一个可以非常有效地解决推车长杆平平衡问题的人工智能问题。现在有很大的改进空间可以成为后续系列文章的一部分。我们可以更多地研究一些事情:
•寻找"真正的"最优策略(将在100个单独的游戏中表现良好)
•优化搜索次数以找到最优策略(("样本效率")
•正确搜索策略,而不是试图随机选择它们。
•解决其他环境。
如果你有兴趣使用预训练模型和开箱即用的工作代码为下一个项目尝试更多机器学习,请查看ModelDepot!
/