在真实游戏中使用机器学习代理
Unity的技术经理Alessia Nigretti和Ciro Continisio都不是机器学习的专家,但他们一起开发了第一个基于Unity新机器学习代理系统制作的游戏演示项目。并在DevGAMM Minsk 2017游戏开发者大会上作了展示。今天的文章基于他们在大会上演讲的内容,向大家介绍在创作机器学习演示项目过程中所思所想。以帮助开发者进一步熟悉和了解Unity机器学习代理工具。
介绍
Unity核心价值观之一的游戏开发大众化,所以我们希望确保新的机器学习代理工具能够适用于想要深入其中学习与使用的每一个人。首先我们与Unity机器学习团队进行了合作,并制作了一些小型的演示项目,以了解它们的工作原理。我们碰到很多问题,我们认为这些问题是第一次接触机器学习代理的开发者也可能会碰到的。我们把它们记录了下来,尽可能的简单总结了所有内容。这样你在读这篇文章时,即使你不知道机器学习是什么或者它是如何工作的,你也仍然可以在Unity机器学习代理的世界中找到自己的方向!
我们的机器学习演示项目《Roguelike》 是一款2D动作游戏,玩家必须与凶恶狡猾的史莱姆战斗。这些由机器学习控制的敌人将会不断攻击我们的英雄,丝毫不留喘息的机会,而当它们感觉到有生命危险时则会四散而逃。
什么是机器学习?
让我们从介绍机器学习开始。机器学习是人工智能的一种应用,它为系统提供了一种不靠人工设计,而从数据中自主学习的能力。机器学习的工作原理是通过给系统提供可用于寻找模式的信息与观察,并对未来的结果进行预测。更笼统的说,系统需要习得期望的输入与输出映射关系。这样系统才可以选择接下来的最佳动作,以达到结果的最优。
实现的方法有多种,具体可以根据我们提供的观察结果来选择。在本文中环境中使用的强化学习(Reinforcement Learning)。强化学习的特点是:它不会告诉系统做什么,而只会指出什么是正确,什么是错误。这意味着,我们让系统执行随机行为,而我们根据这些行为结果判断对错,提供奖励或惩罚。最终系统将会明白,要获得奖励必须要执行什么样的操作。我们可以把这想象成训练小狗坐下:小狗不会明白我们的意图,但如果我们在它做对的行为时给予小奖励,它最终将会把动作和奖励联系到一起。
我们使用强化学习来训练一个神经网络(Neural Network),这是一个基于神经系统的计算机系统模型。神经网络由单元或“神经元”(Neuron)组成。神经元被分为多个层。与外部环境交互并收集所有信息的层是输入层。与之相反,输出层的所有神经元被用于在网络中储存某个特定输入的结果信息。在中间的是隐藏层,它们包含的神经元负责进行所有的计算。它们学习输入数据的复杂抽象表征,从而使它们的最终结果是“智能”的。大多数的层是“完全连接”的,也就是意味着该层里的神经元与前一层中的所有神经元都有连接。每个连接都由一个“权重”定义,这个数值用于强化或弱化神经元之间的连接。
在Unity中,我们的“代理”(执行学习的实体)使用强化学习模型。代理在一个环境中执行动作。动作会引发环境的改变,引起的改变将会连同一些奖励或惩罚反馈给代理。动作发生的地方我们称之为学习环境(Learning Environment),在实践中对应的就是一个常规的Unity场景。
在学习环境中有一个学院(Academy),这是一个脚本,包涵了定义训练的属性,例如:帧率、时间尺度等。学院将这些参数传递给大脑(Brain),即包含了训练模型的实体。最后代理连接到大脑,获得动作和反馈信息,以促进学习进程。为了执行训练,系统需要使用一个额外的模块,允许大脑使用TensorFlow机器学习库和外部的Python环境进行交流。一旦训练完成后,这个环境会将学习过程提炼成一个“模型(Model)”,这是一个二进制格式的文件。随后可以将模型重新导入Unity,作为一个已训练的大脑。
将机器学习应用于真实的游戏
在制作简单演示项目《3D平衡小球》和其它用几个小时完成的小项目之后,如下图所示。我们被这项技术的潜力所吸引。
我们发现了一个可用于2D Roguelike游戏的实际应用。在构建场景时,我们还使用了2D Tilemap和Cinemachine 2D等新功能。
机器学习演示项目《Roguelike》 的意图是创作一款简单的动作游戏,而游戏中所有的实体都是机器学习代理。这样,我们建立了一种同时可用于玩家和敌人之间的通用交互语言。游戏的目的非常简单:四处移动,并在遭遇中幸存下来。
设置训练
每一个良好的训练都是从有关训练算法的高级别头脑风暴会议开始的。这是在代理的类中运行的代码,确定输入对代理的影响以及产生的回报。我们先定义什么是游戏的基本动作,移动、攻击、治疗…它们之间是如何练习的,我们希望代理从中分别能学习到什么,我们要给予什么奖励或惩罚。
首先要做的第一个决定是对于状态(State)和动作(Action),应该使用离散(Discrete)数据还是连续(Continuous)数据。离散意味着状态和动作一次只能有一个值—它是真实环境的简化版本,因此代理可以更容易地将动作与奖励联系起来。在示例中,我们使用离散的操作,一共有6个值:0:保持静止; 1- 4:向一个方向移动; 5:攻击。连续意味着可以有多个状态或动作,且都是浮点值。但是,由于它们的可变性会使代理混淆,所以它们很难使用。在《Roguelike》项目中,我们使用连续状态来检测生命、目标、目标距离等。
奖励功能
我们提出的初始算法是,如果代理在不产生危险的前提下,能不断接近目标就给予奖励。同样,如果发生危险时,它与目标之间的距离增加(代理“逃跑”),就会得到奖励。此外,该算法还包括对代理的惩罚,以防止它在不允许的情况下进行攻击。
经过反复的测试,初始算法发生了很大的变化。我们认识到,要获得期望的行为,最好的方式就是遵循一个简单的模式:尝试,失败,从失败中学习,循环。就像我们希望机器从我们通过反复试验得到的观察中学习一样,我们也想从不断的试错观察中进行学习。
例如,我们注意到的一件事是,代理总能找到一种方法来取得奖励:在我们的案例中,代理开始来回移动,因为每当它再次向前移动时,它就会得到一些奖励。基本上,它找到了一种利用我们的算法获得最大奖励的方法!
从寻找合适解决方案的研究中,我们学到了许多不同的东西,并成功将它们应用于我们最终的项目。要谨记,在游戏中的任何时候都可以给予奖励,只要我们认为代理做“对”了。
训练场景
在设置好算法后,下一步就是设置训练场景。我们选择设置一个小场景作为训练环境,然后在另一个更大场景的游戏中使用代理,训练模型就是从此场景导出的。我们决定创建4个不同的房间,每个房间都有不同的长度和宽度参数,这是为了避免代理习惯于特定类型的房间,而能考虑所有可能的变量。
训练场景负责代理位置的配置,代理与大脑的连接,以及学院和参数的设置。从建立训练场景的过程中,我们认识到使用并行训练,而不是仅仅重复几次同样的情况,能使训练更加有效,因为代理可以获得更多的数据进行学习。要做到这一点,需要设置一些启发式代理来执行一些简单的操作,帮助代理学习,且不会导致他们做出错误的假设。例如,我们设置了一个简单的启发式(Heuristic)脚本,使代理进行随机攻击,为我们的训练代理提供一个示例,以了解当它们受到伤害时会发生什么。
一旦环境准备就绪,就可以开始训练了。如果我们计划进行一个很长的训练,最好检查你的算法逻辑有没有问题。这不是编译时的错误,或者与你是否是一个优秀的程序员无关—正如之前所提到的,代理将会找到一种方法来利用算法,所以要确保没有逻辑缺陷。要确保万无一失,可以启动一个1倍速度的训练,看看会发生什么。观察一下,看看你的代理是否像期望的那样进行动作。
训练
训练的准备工作就绪后,我们需要新建一个可以在Python中和TensorFlow环境进行交互的版本。
首先,我们将需要进行外部训练的大脑设置为“External”,然后生成游戏。完成后,我们打开Python环境,设置超参数(hyperparameter),启动训练。在机器学习的Wiki页面你可以找到有关如何生成训练专用版本以及如何选择超参数的信息。
我们在进行训练的同时,可以观察到代理所获得的平均奖励。这应该会缓慢增加,直到稳定。这会在代理停止学习时发生。当我们对模型满意后,就可以将它导回到Unity查看效果。
上图中的动画展示了1小时训练的成果:与我们的预期一致,史莱姆靠近另一个代理进行攻击,直到被还击并,损失大部分生命。此时它开始逃跑进行回血,然后重新返回,再次攻击对手。
测试
当我们把这个模型应用到《Roguelike》游戏,可以看到它的行为方式保持一致。史莱姆已经学会如何有智慧地行动,以及适应与训练环境不同的场景。
结语
本文旨在提供一些小型演示,说明你能用Unity的机器学习代理工具做些什么,而不必对机器学习有任何具体的技术知识。
如果你已经着手在游戏制作中实现机器学习,还有很多事情可以做。例如将训练和启发式结合,对于一些训练太复杂的行为用硬编码解决,而且使用机器学习AI能为游戏增加一丝真实性。
如果你想为训练增加更多策略,也可以通过一个封闭的Beta测试或内部试玩来整合真实玩家的游戏风格,策略和意图。有了这些数据,你就可以进一步优化代理的训练,以达到下一级别的AI复杂度。如果你喜欢大胆尝试,你可以试着建立自己的机器学习模型和算法,以获得更多的灵活性!
现在你已经学习了如何使用机器学习代理,赶快加入机器学习代理挑战赛!期待看到您的参赛作品!