如何用unity3D对游戏运行性能进行优化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用unity3D对游戏运行性能进行优化相关的知识,希望对你有一定的参考价值。
大家在玩游戏的时候可能经常会遇到卡顿,延迟,死机,不流畅等等问题,那么这些问题是怎么引起的呢?如何去尽量的减少这些情况的发生呢?这些问题对于游戏开发者来说是必须要面对的问题, 也是必须要解决的问题。
上面我们例举在游戏运行的过程中可能会遇到的一些问题, 每种问题引起的原因有很多多,但是我们可以从大方向对整体游戏进行优化,使游戏整体性能更优,从而减少这些情况的发生。对于性能优化我们大体可以从四个大方向去优化,即:CPU,GPU, 内存以及网络和IO,下面给大家一一讲解:
CPU优化,在游戏中CPU主要分担着运算的责任,因此像短时间大量的计算从而导致画面不流畅,电量消耗大,发热严重等情况都可能是因为CPU导致的。针对这些情况我们就需要对CPU优化,那么CPU的优化说白了就是对运算的优化,大家应该尽量减少大量运算或者短时间的大量运算,对此大家可以从四方面着手。一是将计算分散到多个逻辑中,减少短时间的大量运算。二是将可以缓存的数据尽量缓存起来,从而避免那些重复的计算。三是减少CPU对资源的申请、销毁与调配。四是使用合理的算法和数据结构,这个也是CPU优化中最重要的。
GPU优化,GPU的职责就是负责游戏中所有的图像、特效的渲染。GPU的消耗过高会导致游戏画面卡顿、画质降低、手机发热等情况,严重影响游戏体验。对于游戏来讲这是致命的。关于CPU优化大家可以从以下几方面入手:
1、资源优化,比如合理规划图集,指定合理的粒子效果,约定模型的三角面数
2、简化着色器,使用多级纹理与材质贴图技术相结合
3、使用LOD技术、遮挡剔除等技术,减少GPU绘制的数量
4、针对不同的系统平台使用对应的压缩格式。
5、优化显存带宽
游戏渲染可以说是游戏的心脏,所以GPU的优化显得尤为重要,需要开发者格外的重视
内存优化,内存的功能我就不多介绍了, 相信大家都了解。由于内存不足所导致的问题有闪退,卡死等。对于内存的优化,一是降低资源的大小,比如剔除不需要的资源、对资源进行压缩等;二是及时动态的加载和卸载资源,这样可以大大的减少瞬时内存的压力,减少因内存浪费而给游戏带来不必要的消耗。三是降低资源的质量,这是一种有损的优化,不到最后一般不用,当然我们也可以根据不同的设备使用不同质量的资源,将损失降到最低。
网络和IO优化, 他们主要负责资源的加载, 可能是网络的或者本地的。网络不好,或者资源加载时间过长会让大大降低用户体验。因此在CPU、GPU、内存优化后我们同时也不能忽略网络与IO优化,对于网络与IO的优化,大家可以从以下几方面入手:
1、限制短时间内的发包率
2、合理优化包大小,减少包的冗余数据,降低网络请求次数
3、对回包进行分帧处理,及时响应
4、使用独立线程、协程等手段优化资源加载。
参考技术A 一、遇到麻烦时要调用“垃圾回收器”(Garbage Collector,无用单元收集程序,以下简称GC)由于具有C/C++游戏编程背景,我们并不习惯无用单元收集程序的特定行为。确保自动清理你不用的内存,这种做法在刚开始时很好,但很快你就公发现自己的分析器经常显示CPU负荷过大,原因是垃圾回收器正在收集垃圾内存。这对移动设备来说尤其是个大问题。要跟进内存分配,并尽量避免它们成为优先数,以下是我们应该采取的主要操作:
1.移除代码中的任何字符串连接,因为这会给GC留下大量垃圾。
2.用简单的“for”循环代替“foreach”循环。由于某些原因,每个“foreach”循环的每次迭代会生成24字节的垃圾内存。一个简单的循环迭代10次就可以留下240字节的垃圾内存。
3.更改我们检查游戏对象标签的方法。用“if (go.CompareTag (“Enemy”)”来代替“if (go.tag == “Enemy”)” 。在一个内部循环调用对象分配的标签属性以及拷贝额外内存,这是一个非常糟糕的做法。
4.对象库很棒,我们为所有动态游戏对象制作和使用库,这样在游戏运行时间内不会动态分配任何东西,不需要的时候所有东西反向循环到库中。
5.不使用LINQ命令,因为它们一般会分配中间缓器,而这很容易生成垃圾内存。
二、谨慎处理高级脚本和本地引擎C++代码之间的通信开销。
所有使用Unity3D编写的游戏玩法代码都是脚本代码,在我们的项目中是使用Mono执行时间处理的C#代码。任何与引擎数据的通信需求都要有一个进入高级脚本语言的本地引擎代码的调用。这当然会产生它自己的开销,而尽量减少游戏代码中的这些调用则要排在第二位。
1.在这一情景中四处移动对象要求来自脚本代码的调用进入引擎代码,这样我们就会在游戏玩法代码的一个帧中缓存某一对象的转换需求,并一次仅向引擎发送一个请求,以便减少调用开销。这种模式也适用于其他相似的地方,而不仅局限于移动和旋转对象。
2.将引用本地缓存到元件中会减少每次在一个游戏对象中使用 “GetComponent” 获取一个元件引用的需求,这是调用本地引擎代码的另一个例子。
三、物理效果
1.将物理模拟时间步设置到最小化状态。在我们的项目中就不可以将让它低于16毫秒。
2.减少角色控制器移动命令的调用。移动角色控制器会同步发生,每次调用都会耗损极大的性能。我们的做法是缓存每帧的移动请求,并且仅运用一次。
3.修改代码以免依赖“ControllerColliderHit” 回调函数。这证明这些回调函数处理得并不十分迅速。
4.面对性能更弱的设备,要用skinned mesh代替physics cloth。cloth参数在运行表现中发挥重要作用,如果你肯花些时间找到美学与运行表现之间的平衡点,就可以获得理想的结果。
5.在物理模拟过程中不要使用ragdolls,只有在必要时才让它生效。
6.要谨慎评估触发器的“onInside”回调函数,在我们的项目中,我们尽量在不依赖它们的情况下模拟逻辑。
7.使用层次而不是标签。我们可以轻松为对象分配层次和标签,并查询特定对象,但是涉及碰撞逻辑时,层次至少在运行表现上会更有明显优势。更快的物理计算和更少的无用分配内存是使用层次的基本原因。
8.千万不要使用Mesh对撞机。
9.最小化碰撞检测请求(例如ray casts和sphere checks),尽量从每次检查中获得更多信息。
四、让AI代码更迅速
我们使用AI敌人来阻拦忍者英雄,并同其过招。以下是与AI性能问题有关的一些建议:
1.AI逻辑(例如能见度检查等)会生成大量物理查询。可以让AI更新循环设置低于图像更新循环,以减少CPU负荷。
五、最佳性能表现根本就不是来自代码!
没有发生什么情况的时候,就说明性能良好。这是我们关闭一切不必要之物的基本原则。我们的项目是一个侧边横向卷轴动作游戏,所以如果不具有可视性时,就可以关闭许多动态关卡物体。
1.使用细节层次的定制关卡将远处的敌人AI关闭。
2.移动平台和障碍,当它们远去时其物理碰撞机也会关闭。
3.Unity内置的“动画挑选”系统可以用来关闭未被渲染对象的动画。
4.所有关卡内的粒子系统也可以使用同样的禁用机制。 参考技术B MHOL的怪都很有挑战性很好玩的,想要断尾晶蝎的尾巴可没有那么简单,顺序一般是这样的,破某一爪的水晶,然后打出肉质。再破另一爪的水晶和肉质,接着是头部破位,最后才能完成断尾工作
看DeepMind如何用Reinforcement learning玩游戏
原文地址:http://www.infoq.com/cn/articles/atari-reinforcement-learning
原文作者:作者简介
尹绪森,Intel实习生,熟悉并热爱机器学习相关内容,对自然语言处理、推荐系统等有所涉猎。目前致力于机器学习算法并行、凸优化层面的算法优化问题,以及大数据平台性能调优。对Spark、Mahout、GraphLab等开源项目有所尝试和理解,并希望从优化层向下,系统层向上对并行算法及平台做出贡献。
引子
说到机器学习最酷的分支,非Deep learning和Reinforcement learning莫属(以下分别简称DL和RL)。这两者不仅在实际应用中表现的很酷,在机器学习理论中也有不俗的表现。DeepMind工作人员合两者之精髓,在Stella模拟机上让机器自己玩了7个Atari 2600的游戏,结果是玩的冲出美洲,走向世界,超越了物种的局限。不仅战胜了其他机器人,甚至在其中3个游戏中超越了人类游戏专家。噢,忘记说了,Atari 2600是80年代风靡美国的游戏机,当然你现在肯定不会喜欢了。长成什么样子?玩玩当下最火的flappy bird吧!
闲话少叙,来看看准备工作吧。首先是一台Atari 2600,估计是研发人员从爹妈的废物处理箱中翻箱倒柜的找出来的。等会,都生锈了是怎么回事儿?电池也装不上的说!淡定……由Stella倾情打造了模拟机,甚至还有为学术界专门贡献的Arcade Learning Environment,妈妈再也不用担心我的科研了。输入信息就是模拟器当前画面,输出为可供选择的摇杆和按钮“A-B-B-A-上-上-下-下”,学术点说就是当前状态下合法的操作集合。目的呢,当然是赢得游戏,分数多多益善。
然后就是玩游戏了。作为很酷很酷的科学家,肯定不会亲手玩游戏咯,当然一方面也是怕老板发现。不过,想要机器玩游戏,先得想清楚人类是怎么玩游戏的:
- 首先,游戏开始,停留在初始时刻。然后,游戏场景开始变换,玩家眼睛捕捉到画面的变化,将视觉信号传递回脑皮层进行处理。
- 之后,脑皮层将视觉信号转换为游戏的语义信息,通过经验指导,将语义信息与应该进行的操作做映射,之后是将映射后得到的操作信号传递到身体,如手指动作。操作结束后,游戏场景进入下一帧,玩家得到一定的回报,如越过关隘,或者吃到金币。如此循环,直到游戏结束。
仔细想想这个过程,发生在游戏内部的那些事情是玩家所不用考虑的,玩家能够覆盖的只是上述游戏循环的右半段。即输入视觉信号,输出手指动作。而手指动作到下一帧场景,以及玩家得到回报是游戏内部的过程。
既然了解了人类玩家的操作过程,并分解出实际需要玩家的部分内容,下一步就是让机器替代人类玩家了。为了区分,通常称机器玩家为agent。与人类玩家的操作类似,agent需要负责:
- 由上一帧回报信号学习到玩游戏的知识,即经验(什么场景下需要什么操作)
- 视觉信号的处理与理解(降维,高层特征抽取)
- 根据经验以及高层的视觉特征,选择合理的经验(动作)
- 动作反馈到游戏,即玩家手动的部分
所以说,游戏都是越玩越好的,人类玩家如此,agent亦如此。既然已经刻画出来操作步骤,随着DL和RL的发展,实操也不是什么难题嘛。下面,首先看看RL是如何促进agent的学习。之后会讲到DL是如何合理的安插到RL的学习框架中,并如何起到作用的。然后,会强调一下这两者在游戏agent操作中的难点,以及如何解决实际问题。最后,来看看agent游戏玩的到底如何。总结涉及对RL的升华。
Reinforcement Learning
RL其实就是一个连续决策的过程。传统的机器学习中的supervised learning就是给定一些标注数据,这些标注作为supervisor,学习一个好的函数,来对未知数据作出很好的决策。但是有时候你不知道标注是什么,即一开始不知道什么是“好”的结果,所以RL不是给定标注,而是给一个回报函数,这个回报函数决定当前状态得到什么样的结果(“好”还是“坏”)。 其数学本质是一个马尔科夫决策过程。最终的目的是决策过程中整体的回报函数期望最优。
来看看一些关键元素:
- 状态集合S:S是一个状态集合,其中每一个元素都代表一个状态。在游戏的场景中,状态S就是某一时刻采集到的视觉信号。
- 动作集合A:A中包含所有合法操作。如flappy bird中点击一下屏幕,temple run中的上下左右等指动。
- 状态转移概率P:P是一个概率的集合,其中每一项都表示着一个跳转的概率。例如,在当前状态s下,进行操作a转移到下一个状态的概率。
- 回报函数R:R是一个映射,跟状态转移概率P有点联系,R说明的是,在当前状态s下,选择操作a,将会得到怎样的回报。需要注意的是,这里的回报不一定是即时回报,如棋牌游戏中,棋子移动一次可能会立刻吃掉对方的棋子,也可能在好多步之后才产生作用。
回报函数有一些小小的tricky。
首先,RL的过程是一种随机过程,意即整个决策的过程都是有概率特性的,每一步的选择都不是确定的,而是在一个概率分布中采样出来的结果。因此,整个回报函数是一种沿时间轴进行的时序/路径积分。依据贝叶斯定理,开局时刻不确定性是最大的,开局基本靠猜,或者一些现有的先验知识。随着游戏的不断进行接近终点,局势会逐渐晴朗,预测的准确性也会增高。深蓝对战国际象棋大师卡斯帕罗夫的时候,开局就是一些经典的开局场景,中局不断预测,多考虑战略优势,局势逐渐明朗,因此这时候一般会出现未结束就认输的情况。终局通常就是一些战术上的考量,如何更快的将军等。类似地,在RL中,回报函数的时序/路径积分中,每一步的回报都会乘上一个decay量,即回报随着游戏的进行逐渐衰减。此举也有另一些意味:如何最快的找到好的结果,例如在无人直升机中,花费最小的时间找到最优的控制策略,剩下的就是微调。
接下来,当这一切都确定了,剩下的事情就是寻找一种最优策略(policy)。所谓策略,就是状态到动作的映射。我们的目的是,找到一种最优策略,使得遵循这种策略进行的决策过程,得到的全局回报最大。所以,RL的本质就是在这些信号下找到这个最佳策略。
众所周知动态规划,其中一条理论基石就来自Bellman公式。Bellman公式告诉我们,在一种序列求解的过程中,如果一个解的路径是最优路径,那么其中的每个分片都是当前的最佳路径,即子问题的最优解合起来就是全局最优解。回报函数的最大化就服从Bellman公式,这是非常棒的性质,表示着我们可以不断迭代求解问题。旅行商问题就不服从Bellman公式,因此它是NP-hard问题。
于是,RL的学习分为两个方面,两方面相互交织,最终得到结果。这是一种典型的Expectation-Maximization算法的过程。EM算法在机器学习中是相当经典的算法,大量的机器学习优化都使用这个方法。
如下图所示的一种EM算法求解RL的示例:
该示例代码取自Spark Summit 2013,由Adobe的Nedim Lipka介绍了RL在市场策略(网页个性化展示)上的应用。这里抛开具体的应用语义,以及分布式算法,来简单分析RL优化过程中EM算法的一般过程。
这里,是一个函数,这个函数以当前状态s为参数,返回一个动作a,这个动作是一个概率分布,代表着在当前状态s下,转移到任意另外一个状态的概率是多少。假设我们有三个状态,那么这个动作分布可能是这个样子的:
当前状态 |
1 |
2 |
3 |
1 |
0.3 |
0.1 |
0.6 |
另外,是一个价值函数,即我们从s这个状态出发,直到无穷大的遍历,能获得的最大回报的期望。价值函数其实就是在策略为,初始状态为s情况下的回报函数。另外,是一个即时(immediate)回报函数,即从状态s出发,经过a这个动作的作用,走到这个状态获得的回报是多少。例如用户在某个页面上浏览,点了一个广告,到了广告商的页面,广告商付给该网站1块钱。
价值函数,其中表示当前动作下面的转移概率,表示当前动作下的即时回报函数,是从s转移到之后,所能得到最大的期望价值。
这个函数优化有个问题,那就是和都是未知的,而这两个量是相互纠缠的,计算需要最大化,而计算需要对最好的进行积分。所以这是个典型的Expectation-Maximization算法。代码中第一部分就是EM算法中的Expectation,第二部分就是EM算法的Maximization部分。
那么为什么第一部分会有迭代呢?那是因为大家记得随机游走,都不是游走一次就能结束的。整个转移链想达到稳定状态,需要多次迭代才可以。这就类似于Gibbs sampling 算法,必须多次迭代才能收敛。这里也是,计算Expectation需要让整体的网络达到稳定状态。其中符号delta代表着前后两次迭代差距是否足够小,因此判断是否收敛。
(数据的结构,数据图的网络相依,类似与随机游走)
总结一下,说白了,RL就是一个supervised random walk(可以参考斯坦福大学Jure Leskovec教授的论文Supervised Random Walk)。传统的random walk是按照固定的转移概率随便(采样)游走,RL就是在随机游走的每一步,都选择一个能使回报函数最大化的方向走,即选择一个当前状态下最好的action。而RL游走的这个网络,是由状态S为点集,动作A为边集,状态转移概率P为边权重的有向无环图(DAG)。状态转移概率P不是不变的,而是随着agent在这个网络中的步进,不断变的更加正确,符合现实世界的分布。这个DAG,就是一种混沌的网络状态。
澄清一些概念,Reward 是一次action得到的payoff,Return是一序列reward的函数,如discounting sum。上述两个是目标,而下面的value function是要学习的函数。Value function是状态的函数,或者是“状态-动作”这个序对的函数。来预测在给定状态(或者给定“状态-动作”)下agent能表现多好。有多好,表明的是在这点的expected reward,即在这点所能看到的未来最大期望收益。Approximator,关键是泛化能力,在有限的状态-动作子集上获得的经验,如何扩展到全部的状态和动作上?使用动态规划这种“查找表”的方式,是有局限的,而且这个局限不仅仅是内存上的(硬件上的)。Off-policy是指不需要一个policy查找表之类的,而是直接求最大化reward的那个action。
Deep Learning in RL
Deep在何处?换句话说,因为DL参与的RL与传统的RL有何不同,从而要引入DL?我们在前面介绍RL的过程中,处理的是状态。而实际上,很多时候状态是连续的、复杂的、高维的。不像之前介绍中说的4个状态就可以了。实际上,假设我们有128*128的画面,那么状态的数目是指数级增长的,即有2^(128*128)中可能存在的状态,这个数字是1.19e+4932,这可是个天文数字!游戏画面连续存在,就算按照每秒30帧来算,一局游戏玩下来,啥都不用干了。处理数据的速度根本跟不上游戏画面变化的速度,更不用说那些高清的游戏。实际上,DeepMind现在也就能玩玩Atari这种爸爸辈的游戏吧。
无奈,因此求助于DL。注意,在此之前有很多人工特征处理,但很明显,一旦引入了人类的活动,就无法做成一种集成性的系统了,只能成为实验室的二维画面玩具。人类为什么玩游戏玩的好呢?因为人脑非常善于处理高维数据,并飞快的从中抽取模式。现在由DL来替代这块短板。
DL现在有两种经典形式,由Hinton、LeCun和Yoshida等人(原谅我不能一一列举大牛们)逐步完善。DL作为机器学习界的明星方法,早已耳熟能详。但是兹事体大,还是稍微提一下两种经典形式吧。首先说明的是,两种形式在深层架构上很类似,但是在每层的处理上有所不用。依据多种神经网络之不同,DL分类如下:
- 第一个差别就是单层网络的不同,分为Auto-encoder和Restricted Boltzmann Machine;
- 第二就是深层架构之不同,如何安排深层架构,是直接堆叠,还是通过卷积神经网络?
- 第三就是最高两层分类/识别层的不同安排,不同的高两层安排代表了不同的学习形式,是生成模型,还是判别模型?
- 第四是不同的激活函数选择,常见的是sigmoid函数,但也有通过Rectified Linear Unit增强学习能力的,甚至还有convex函数的选择,如DSN。
所谓Q-learning
初始化的时候需要设置DL与RL的起始参数,例如episode(其表述一种天然存在分割的序列,如玩游戏,总会遇到终局。一个episode就是这样一个天然的分割。)设置为零,初始化策略,以及初始化空的replay memory。
之后就是在一个个episode中进行探索。简单来讲,就是累计4帧游戏画面,经过些许预处理(裁剪、白化)之后,算作当前状态。之后根据现有的策略,选择一个最大化全局回报动作。在ALE模拟器中执行这个动作,收获下面4帧画面,以及此次回报。并将本次探索的结果存入replay memory。
接下来就是进行新的策略(模型)学习。首先从replay memory中采样几组探索结果,分别根据一阶的Bellman公式求解理论回报值,最为标注信息。之后使用标注信息来优化CNN,通过SGD进行优化。
要明确的是,不同的episode之间有哪些变量是共用的呢?有哪些是新eposide中置零,重新开始的呢?很显然,function approximator,即我们的神经网络是维持不变的,因为CNN在这里出现的本意就是随着样本数目、迭代数目不断增加,优化的越来越好。剩下的,replay memory也是不变的,因为replay memory算是一个资源池,也就是传统意义上的数据。数据收集越来越多,但是不会丢弃。至于其他的,像学到的policy,以及reward等都是要重新开始的。
以上介绍的过程就是Q-learning的一般过程。通常来说,Q-learning是model-free的,什么意思呢?就是说使用Q-learning的RL过程在计算value function(即Q-function)的时候,不需要和环境进行交互。而上文中提到的动态规划方法,是需要跟环境交互才能计算最优回报的。通过使用一个称作Q-function的函数,可以完全避免计算最优回报的时候和环境交互。这个Q-function通常又被称作function approximator.
细数挑战
很多问题都是看起来简单,实操过程中困难重重,因此,做任何事情都要“in the wild”,否则只是在外围打转,没有深度,因此词句缺乏力量,从而写不出有力的篇章。(作者躺枪)
首先是如何将整个过程构成闭环,在实时的游戏中进行持续学习和决策。可以肯定的是,一般情况下,游戏进行画面计算的时间是相当短的,然而DL编码出特征,并用RL找出策略这个过程要长的多。因此,游戏运行的每一帧都要停下来看看agent算完了没有。如果这是一个流处理系统,那么整套系统的性能就被压死在这里。在实验环境中,我们当然可以容忍agent慢慢玩,但是这样是无法与人类玩家力拼的。DeepMind的科学家们也没给出太好的解决方案,只是设置了一个k值,意即每出k帧动画才判决一次。细想一下会出很多问题,如agent在这k帧就不幸挂掉了,负分滚粗。这点还是期待更佳性能,或者更轻量的解决方案。正所谓,性能性需求不如功能性需求优先,但是,当性能性需求在这种情况下变成了一种功能性需求,那就必须解决了。
相比于有监督学习,RL的另一大挑战是没有大量标注数据。首先要澄清一点,就是DL在前面的pre-train的过程中不需要标注数据,不代表整个DL过程中不需要标注数据。恰恰相反的是,只要有充分的标注数据,DL是可以抛开前面的pre-train而直接计算的。RL每一次计算的时候是不知道一个具体的label来表明对错的,只能得到一个叫做标量回报的信号,这个信号通常都是稀疏的,有噪声的,尤其重要的一点,是有延迟的。延迟,表明的是当前动作和回报之间的延迟,游戏得分可能依赖于之前所有的状态和动作,而一个动作所得到的反馈很可能到数千步之后才能展现出来(如围棋,这也是战略性游戏和战术性游戏的差别)。可以在本文游戏结果一节中看到,对于战略性游戏,agent表现还是非常差的。
还有一个问题是机器学习算法都是有数据分布独立性的假设的,IID是一个很重要的性质,如果数据之间是有关联的,那么计算出来的模型就是有偏向的。但是RL中的数据通常是一个前后严重相依的序列。并且随着policy的学习,数据分布倾向于不同,严重影响回归器的使用。可想而知,当前情况下的状态会影响下一次的动作选择,而下一次动作选择的不同会影响下一帧画面,下一帧画面又会影响下下次动作的选择。犹如一个长长的链条,让状态和动作纠缠不清。怎么破IID的问题?DeepMind学习Long-Ji Lin 93年用来控制机器人运动的大作,通过使用replay memory,存储过去一段时间内的“状态-动作-新状态-回报”序列,并进行随机采样以打破依赖,以及用过去的动作做平滑。
历史局限性也严重制约这agent的能力,局限性嘛,就是眼光看不到未来,正如当年葡王拒绝了当地人哥伦布的远航,而西班牙女王伊莎贝拉则是拿出自己的首饰珠宝让哥伦布出海。这里的历时局限性是指在当前阶段只能看到游戏的一部分画面,无法掌控全局。从而产生一个更严重的问题,就是富者更富的马太效应难以调和,agent选择的动作会偏向一定的画面,而这种画面会使得agent在这个偏向上持续增强。例如,当前时刻最大化回报的操作是向左移动,因此agent选择向左移动,所以左侧的画面会被更多的看到,左侧画面占据大量的训练样本席位,从而控制进一步的学习。这种情况下,强烈的正反馈的循环会让agent迅速陷入局部最优值,甚至直接发散开。(John和Benjamin在97年的automatic control上对此有所论述。)通过replay memory会让更多的历史样本参与训练,从而冲淡马太效应带来的影响。
最后是Bellman公式的局限性。根据前文叙述的RL用法,我们可以很happy的看到求解未来的回报是一个可以动态规划的过程,因此Bellman公式大杀四方,可以快速得到最大未来回报的结果。可惜的是,这种计算看似很好的解决问题,实则不然。这种情况下预测只针对当前最优路径这一单条路径的情况进行计算,不具备泛化能力。比如对当前数据做个分类器,可以轻轻松松达到100%的正确性,但是这个100%的分类器用在其他数据上甚至不如随机分类的结果。这种情况的解决办法是,使用一个自定义的function来模拟这个最大回报。这里的函数就可以任意选择了,例如有些人选用简单的线型函数,有些人则选用更加复杂的函数,如这里使用的卷积神经网络。之前的做法是,给我当前的策略、 状态,以及动作的选择作为输入,通过动态规划计算出未来的回报。现在则是给定这些输入,直接输送到神经网络中计算出未来的回报。
致命一击
游戏准备
DeepMind工作人员最终用这个DRL玩了7个Atari游戏,分别是激光骑士(Beam Rider),打砖块(Breakout),摩托大战(Enduro),乓(Pong),波特Q精灵(Q*bert),深海游弋(Seaquest),太空侵略者(Space Invaders)。玩这些游戏的过程中呢,用的网络深层架构、学习算法,甚至是超参设置都是完全一样的,这充分说明了该方法的有效性,以及泛化能力。(当然,也说明了DeepMind的小伙伴们懒得去调一手好参。)当然,有一点肯定是把不同的游戏修改了的,那就是得分。不同的游戏得分、算分的情况很不相同,导致处理起来很麻烦。因此,玩游戏的过程中,每得到一个正分就加一,得到一个负分(滚粗)就给个减一。通过这种做法让不同的游戏都融合在一个框架内,不会因为奇怪的得分、给分方法导致出现计算上的困难。
注意我们的Arcade Learning Environment模拟器,跟agent配合起来会有一些问题,因为ALE把游戏画面一帧一帧计算出来很快,超过了agent的计算判决时间,所以导致游戏玩起来一卡一卡的(这点不像棋牌类游戏,可以给出思考时间),因为设置ALE出k帧才让agent判决一次,这样才能保证玩起来不是那么的卡。在本组实验中,k通常设置为4。
传统的有监督学习过程中,评测是简单确定的,给定了测试集,就可以对现有模型给出一个评价。然而,RL的评测是很困难的。最自然的评测莫过于计算游戏的结果,或者几次游戏结果的均值,甚至是训练过程中周期性的分数统计。但是,这种做法会有很大的噪声,因为策略上权重的微小扰动可能造成策略扫过的状态大不相同(回顾一下,状态来自游戏画面,不同的动作选择会导致下一帧画面的变化,这个效应累计起来变化是巨大的。)。因此,DeepMind选择了更加稳定的评价策略,即直接使用动作的价值函数,累加每一步操作agent可以得到的折扣回报。
实际操练
首先一些预处理是必不可少的,虽然论文本身标榜基本无预处理。但是显然,DeepMind的玩家们更倾向于直接使用现成的Deep Neural Network(Hinton 2012年做ImageNet分类用到的卷积神经网络,并使用了GPU加速),而不是自己从头开始。正所谓“做像罗马人做的事一样的事情”,为了直接使用“罗马人”开发的DL,首先做的是降维处理,将RGB三色图变换成灰度图,其次是做了一些裁剪,将原图像由210×160采样成110×84的图像,并最终裁剪成84×84的图像。最终是每4帧图像合在一起当作一次训练的样本。
网络架构方面,输入是84×84×4的像素,第一层神经元是16个8×8的过滤器,第二层是32个4×4的过滤器,最后一层是与256个rectifier单元的全连接,输出层是与单一输出与下层的全连接的线性函数。DeepMind称这种与RL结合使用的卷积神经网络为Deep Q-Network.
对照最左侧的Q-value评价曲线,与右侧“深海巡弋”相对照。点A时刻有一个敌军出现在屏幕最左侧,此时Q-value升高,B点时刻升高到峰值,因为我们发射的鱼雷就要击中敌军。击溃敌军潜艇之后,Q-value降低。说明DeepMind的DRL是可以感知图片语义的。
最终的评测对象中包含了Sarsa算法,Contingency算法,本算法,以及人类专家。前两个算法都使用了人工合成的features。人类玩家的结果是玩每个游戏两小时之后取得所有成成绩的中位数。最终对比结果显示,首先是本算法远胜于所有人工合成features的方法,其次是本方法还在打砖块、摩托大战和乓上得分超过人类玩家,在激光骑士上能跟人类玩家比部落下风。但是本算法在波特Q精灵、深海游弋和太空侵略者三个游戏上还离人类专家相去甚远。因为这三个游戏比另外的游戏需要更多的深思熟虑,即策略链条上的每一次抉择都可能会对长时间后的结果造成影响,而前三个游戏前后之间关联度小,前面操作造成的影响不易传播到后面的策略中,因此效果会更好。
飞翔吧,小鸟!
由DRL看世界
“看看你自己的生活,你的职业选择、你与配偶的邂逅、你被迫离开故土、你面临的背叛、你突然的致富或潦倒,这些事有多少是按照计划发生的?”正如塔勒布在《黑天鹅》中提到的,世界是随机的。纳特?西尔弗也保持这种观点:预测一直都不是简单的问题。复杂动力系统的预测困难来自三个方面,一是微观结构的易变性,稀疏性导致缺少显著的统计特征;二是复杂动力系统的混沌性,简单的微扰会带来巨大的变化;三是人类行为的因变性,导致数据分布改变影响预测模型。而不同的目的导向也导致了不同的不同的预测结果。除了天气预报,鲜见较准确的预测系统。
只不过此随机并非完全随机的,而是某种程度上可预测的随机。因为依据状态的不同,动作的选择并不是一个均匀分布。所谓一花一世界,一叶一菩提,RL正如现实世界的一个缩影。正是由于RL和DL对世界和人类高度的拟真性,笔者才感觉这俩是机器学习中最有趣的部分。苏格拉底说“认识你自己”,尼采也有言“离每个人最远的,就是他自己”,RL和DL像两位不懈的巨人,在人类认识自我,认识环境的道路上渐行渐远。
笔者一直对随机过程保持敬畏之心。当然原因之一也是笔者曾差点“随机过程随机过”,但是,抛开那些“只是更善于阐述而已,甚至只是更善于用复杂的数学模型把你弄晕而已”的故弄玄虚,随机过程支撑整个世界,贝叶斯点睛你的生活。
结构之美
一篇DRL引出了三种结构,这些结构都是美不胜收的。分别是“模型的结构”“数据的结构”以及“模型和数据的结构”。要注意的是,这里都只是画出了结构的一部分,还有其他大块的部分没体现在图中。
(模型的结构,图为DL中的受限波尔特兹曼自动机)
(模型和数据的结构,Gibbs sampling的网络相依,节点为隐含变量和观测变量)
参考文献
- Playing Atari with Deep Reinforcement Learning
- Residual Algorithms: Reinforcement Learning with Function Approximation
- Bayesian Learning of Recursively Factored Environments
- The Arcade Learning Environment: An Evaluation Platform for General Agents
- CS229 Lecture notes: Reinforcement Learning and Control
- Rectified Linear Units Improve Restricted Boltzmann Machines
- An Analysis of Temporal-Difference Learning with Function Approximation
- Deep Auto-Encoder Neural Networks in Reinforcement Learning
- On optimization methods for deep learning
- Technical Note: Q-Learning
- Towards Distributed Reinforcement Learning for Digital Marketing with Spark
以上是关于如何用unity3D对游戏运行性能进行优化的主要内容,如果未能解决你的问题,请参考以下文章