自2017年9月Unity推出了机器学习代理工具, 机器学习的开发者持续增长并在社区中获得认可。Unity机器学习代理项目入选mybridge评选2017年最佳30个机器学习开源项目,并在Github中获得1710星。Unity机器学习社区挑战赛也在如火如荼的进行中。

今天的文章中,我们将会学习如何设置一个基本代理,它能利用强化机器学习达到随机选择的数字目标。我们将使用到最新的Unity机器学习代理工具创建并训练代理,完成指定任务,并探讨将这个简单的代理优化为真正的游戏AI。

设置Unity机器学习代理工具和TensorFlow

开始之前,我们需要设置好机器学习代理工具。设置请参考以下文章:

配置Unity机器学习代理工具和TensorFlow环境

Mac下配置Unity机器学习代理工具

说明:本文的代码和运行环境基于Windows 10环境。

机器学习代理(ML-Agents)场景设置

当我们完成设置后,打开Unity项目,创建新场景。

首先新建一个游戏对象,命名为“NumberAcademy“。添加“TemplateAcademy”组件到“NumberAcademy“游戏对象中。

TemplateAcademy组件脚本可从Github下载:

https://github.com/Unity-Technol ... /TemplateAcademy.cs

场景设置过程并不需要这个组件做太多事,所以我们只用模板中的基本内容即可。

101403gk41tm10u0slca1s.jpg
NumberAcademy游戏对象

在这个组件下,创建子游戏对象,命名为“NumberBrain“。

添加一个Brain组件。将状态数量(State Size)和动作数量(Action Size)的值设为2。将动作空间类型(Action Space Type)设为离散(Discrete)。

在这项目中,我们将会用到两个独立的动作,也就是“上”和“下”。之所以使用离散类型,是因为这些动作会以整数值形式呈现。

将状态空间类型(State Space Type)设为连续(Continuous)。我们将跟踪两个浮点数,所以使用连续类型。

101445r7dq7qrfr7ngrbps.jpg
NumberBrain游戏对象

将大脑类型(Brain Type)设为玩家(Player),并加入两个动作。选择任意两个键位,在此我们选择的是A和B,并分别赋值为0和1。绑定0的键位将减小当前值,而绑定1的键位将增大它。

101506q6n4eqsi4enceibn.jpg
Brain脚本组件

现在我们来创建新脚本,命名为NumberDemoAgent.cs,将其基本类设置为Agent,替换原有的“: MonoBehaviour with : Agent”。代码如下:

[C#] 纯文本查看 复制代码public class NumberDemoAgent : Agent { [SerializeField] private float currentNumber; [SerializeField] private float targetNumber; [SerializeField] private Text text; [SerializeField] private Transform cube; [SerializeField] private Transform sphere; int solved;

currentNumber和targetNumber字段在这段代码里最重要。的代码都是用作调试和可视化。我们的代理将挑选一个随机的targetNumber,试着通过使用增长和减小指令,将currentNumber的值调整到目标值。

下面将重写CollectState方法。代码如下:

[C#] 纯文本查看 复制代码public override List<float> CollectState() { List<float> state = new List<float>(); state.Add(currentNumber); state.Add(targetNumber); return state; }

在上面这段段代码里,我们会返回两个浮点数给currentNumber和targetNumber,作为代理的状态。

注意:这里是如何匹配二个状态变量的,而且它们都是浮点数,这就是为什么我们要将状态空间类型设为连续而不是分离。

为了训练我们的代理,需要选取随机目标数,所以要重写AgentReset()方法。代码如下:

[C#] 纯文本查看 复制代码public override void AgentReset() { targetNumber = UnityEngine.Random.RandomRange(-1f, 1f); sphere.position = new Vector3(targetNumber * 5, 0, 0); currentNumber = 0f; }

最后也是最重要的一部分是AgentReset()方法。这是我们的输入动作,处理任务,也就是回应动作,如果代理回报(reward)结果则为成功。

代码如下:

[C#] 纯文本查看 复制代码public override void AgentStep(float[] action) { if (text != null) text.text = string.Format(C:{0} / T:{1} [{2}], currentNumber, targetNumber, solved); switch ((int)action[0]) { case 0: currentNumber -= 0.01f; break; case 1: currentNumber += 0.01f; break; default: return; } cube.position = new Vector3(currentNumber * 5f, 0f, 0f); if (currentNumber < -1.2f currentNumber > 1.2f) { reward = -1f; done = true; return; } float difference = Mathf.Abs(targetNumber - currentNumber); if (difference <= 0.01f) { solved++; reward = 1; done = true; return; } }

首先我们会看到文字的变化。这只是用于调试或可视化,它让我们能看到当前值,还有目标值,以及我们所需要解决问题(即达到目标值)所测试的数值次数。

下一步是切换查看动作和完成任务的地方。在示例中,脚本要么通过减小当前值回应动作0,要么增大当前值回应动作1。除此之外,输入的值都无效,但若是脚本得到了值,脚本会忽略这个值并返回。

现在我们基于currentNumber移动立方体,这个值会用作x轴偏移值。该立方体在此用于可视化,对实际的逻辑或训练过程没有任何影响。

然后检查currentNumber对大小限制的反应。因为我们选取的随机数是在-1和1之间的,如果随机数为-1.2或是1.2,将视作失败,因为数值超过了极限。本示例中,我们给代理回报-1,表示失败,然后给done变量赋值为true,以让代理能重置并再次尝试。

最后,我们会检查currentNumber和目标值的差值是否小于0.01。若小于,则认为是匹配值,将回报(reward)值设为1.0,表示成功,给done赋值为true表示完成。我们还会再次期间使用solved变量作为计数器,用于调试,这样我们就能知道成功的次数了。

下面是完整的脚本代码:

[C#] 纯文本查看 复制代码using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class NumberDemoAgent : Agent { [SerializeField] private float currentNumber; [SerializeField] private float targetNumber; [SerializeField] private Text text; [SerializeField] private Transform cube; [SerializeField] private Transform sphere; int solved; public override List<float> CollectState() { List<float> state = new List<float>(); state.Add(currentNumber); state.Add(targetNumber); return state; } public override void AgentStep(float[] action) { if (text != null) text.text = string.Format(C:{0} / T:{1} [{2}], currentNumber, targetNumber, solved); switch ((int)action[0]) { case 0: currentNumber -= 0.01f; break; case 1: currentNumber += 0.01f; break; default: return; } cube.position = new Vector3(currentNumber * 5f, 0f, 0f); if (currentNumber < -1.2f currentNumber > 1.2f) { reward = -1f; done = true; return; } float difference = Mathf.Abs(targetNumber - currentNumber); if (difference <= 0.01f) { solved++; reward = 1; done = true; return; } } public override void AgentReset() { targetNumber = UnityEngine.Random.RandomRange(-1f, 1f); sphere.position = new Vector3(targetNumber * 5, 0, 0); currentNumber = 0f; } }

设置代理

现在脚本完成了,我们需要新建游戏对象,命名为“NumberDemoAgent”。

将刚刚写好的NumberDemoAgent.cs脚本附加到该游戏对象上,并将NumberBrain添加到该组件的Brain部分。

101840v2pgf94fg5nbnnl5.jpg
NumberDemoAgent游戏对象

下一步新建文本(Text)对象,将其放到明显的位置,理想位置是在屏幕的中央,字号要大。将该文本对象附加到NumberDemoAgent上。

新建立方体(Cube)对象,将它们也添加到NumberDemoAgent上,这样我们可以看到过程的变化,比阅读一个个数值更轻松。

在运行模式下测试

点击play,就能用设置好的键位A和B,左右移动立方体。当我们将方块向球体移动时,它将增大solved计数器,并重置。如果朝错误的方向移动的太远,它也会重置(请记住1.2的范围值限制)。

训练

当它在运行模式下成功运行后,选择brain并把Brain Type设为外部(External)。保存场景,并构建一个可执行文件,文件中只包含了一个启用调试模式的场景。

101933lo7ty71ftydyof03.jpg

关于输出文件夹,选择python文件夹作为存放这个机器学习代理项目的文件夹。例如,我的文件夹的地址是这个:C:\ml-agents\python。别忘了该可执行文件的名字,因为后续我们就需要它了。

Anaconda / Jupyter

启动Anaconda命令指示符。将目录定位到刚刚的构建目录,也就是那个python文件夹下,命令示例:cd c:\ml-agents\python

输入命令“jupyter notebook”(提示:这里可能要打两次回车),自动打开浏览器界面。会得到这样一个网页界面:

102018aqpsajsxjjpm2oxp.jpg

提示:这里要修改高亮部分。对于env_name变量,要输入可执行文件的名字。对于buffer_size和batch_size,你可以使用截图里的值。要注意,这里面的当前值部分只在测试的时候会看到。

当编辑好Hyperparameters部分后,按照步骤运行。从第一步和第二步开始。在第三步时,你会看到一个打开的游戏窗口。第一次打开时,你也许会得到窗口许可对话框,记得要选择允许。

在进行到第四步时,先注意得到的结果,第一次运行时也许会需要几分钟。

102115m55qbihiudsslwfu.jpg

当它保存多次后,点击stop按钮。然后继续第五步,并运行。这将导出你的训练数据到一个与可执行文件同名的.bytes文件中,导出位置在上述python目录的子目录下。示例:python/models/ppo。

102135ayh550600rotafjr.jpg

复制.bytes文件,(再次提醒,它的名字会和你的可执行文件名字一样)将这个粘贴到Unity项目里的某个位置。

选择Brain脚本组件,将Brain Type设为Internal。把.byetes文件添加到Graph Model字段上。

102204axqpbb06bxtq0bwt.jpg

保存并点击运行,就这样我们就完成创建自定义代理。

结语

这个示例相当简单,只是用来让你理解这个系统的运作方式。我们很期待能看到这个项目你能够继续开发,并构建成更大更有趣的项目,用来控制游戏AI,从而制作出有意思的游戏机制和AI。更多精彩的机器学习文章欢迎访问 Unity官方社区(Unitychina.cn)! 更多Unity机器学习社区挑战赛的信息请访问Unity Connect!

参考信息

Unity ML Agents GitHub
https://github.com/Unity-Technologies/ml-agents

HyperParameters Doc
https://github.com/Unity-Technol ... st-practices-ppo.md

2017年最佳30个机器学习开源项目
https://medium.mybridge.co/30-am ... v-2018-b853b8621ac7 Unity, 机器学习锐亚教育

锐亚教育 锐亚科技 unity unity教程