编写代理及创建游戏AI

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

Unity机器学习社区挑战赛

也在如火如荼的进行中。

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

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

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

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

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

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

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

TemplateAcademy组件脚本可从Github下载:

https://github.com/Unity-Technologies/ml-agents/blob/master/unity-environment/Assets/ML-Agents/Template/Scripts/TemplateAcademy.cs

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

NumberAcademy游戏对象

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

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

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

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

NumberBrain游戏对象

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

Brain脚本组件

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

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方法。代码如下:

public override List CollectState()

{

List state = new List();

state.Add(currentNumber);

state.Add(targetNumber);

return state;

}

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

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

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

public override void AgentReset()

{

targetNumber = UnityEngine.Random.RandomRange(-1f, 1f);

sphere.position = new Vector3(targetNumber * 5, 0, 0);

currentNumber = 0f;

}

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

代码如下:

public override void AgentStep(float[] action)

{

if (text != null)

text.text = string.Format("C: / T: []", 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)

{

reward = -1f;

done = true;

return;

}

float difference = Mathf.Abs(targetNumber - currentNumber);

if (difference

{

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变量作为计数器,用于调试,这样我们就能知道成功的次数了。

下面是完整的脚本代码:

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 CollectState()

{

List state = new List();

state.Add(currentNumber);

state.Add(targetNumber);

return state;

}

public override void AgentStep(float[] action)

{

if (text != null)

text.text = string.Format("C: / T: []", 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)

{

reward = -1f;

done = true;

return;

}

float difference = Mathf.Abs(targetNumber - currentNumber);

if (difference

{

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部分。

NumberDemoAgent游戏对象

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

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

在运行模式下测试

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

训练

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

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

Anaconda / Jupyter

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

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

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

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

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

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

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

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

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

结语

这个示例相当简单,只是用来让你理解这个系统的运作方式。我们很期待能看到这个项目你能够继续开发,并构建成更大更有趣的项目,用来控制游戏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-Technologies/ml-agents/blob/master/docs/best-practices-ppo.md

2017年最佳30个机器学习开源项目

https://medium.mybridge.co/30-amazing-machine-learning-projects-for-the-past-year-v-2018-b853b8621ac7

Unity机器学习系列文章

官方活动2018首场Unity大师课程-上海站 即将开场(名额有限!)

时间:2018年1月14日

地址:https://www.bagevent.com/event/1031633Neon全球挑战赛

时间:截至到2018年1月16日

地址:https://connect.unity.com/challenges/neonUnity机器学习社区挑战赛 !

地址:https://connect.unity.com/challenges/ml-agents-1

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180108A0B46800?refer=cp_1026

同媒体快讯

相关快讯

扫码关注云+社区