ML-Agents(九)Wall Jump

本文深入解析Curriculum Learning在ML-Agents中的应用,通过WallJump示例,展示如何通过逐步增加任务难度提升智能体的学习效率。

一、前言

这次我们来看一下 Wall Jump 示例,这个例子又和我们之前学习的示例不同,它引用了 Curriculum Learning (课程学习)的学习方法,简单来讲就是使用授课学习的方式来训练神经网络,学习的样本从易到难,模拟人类学系的过程。先来看看本示例的最终效果:
由图中可以看到本示例的效果,小蓝需要越过蓝色的墙体到达绿色的目标地点,此外还可以留意到,蓝色的墙体高度是随机变化的:当蓝色的墙体较高时,小蓝推动大白块当梯子才能越过墙体;当蓝色墙体高度较低时,小蓝则可以直接跳跃过去。
因此,在本示例中,小蓝拥有两个训练好的训练模型—— SmallWallJump 和 BigWallJump ,分别对应矮墙(无墙)和高墙情况下的行动。
下面我们先来学习一下官方对于Curriculum Learning的相关文档。

二、课程训练(Curriculum Learning)

这一节内容主要是翻译官方文档 Training with Curriculum Learning
首先文档介绍了课程学习是ML-Agents的一项功能,它允许在训练的过程中更改环境的属性来帮助学习。

一个教学示例

先想象一个任务,agent需要越过一堵墙到达目标位置(其实就是Jump Wall)。一开始训练agent来完成该任务其实是一个随机策略。因此直接训练的话,开始的策略将使agent在循环中运行,并且可能永远,或者很少正确地越过墙体到达目标以获得奖励( 意思就是一开始要是训练难度过大,agent可能很难理解自己要达成的目标 )。如果我们从一个更简单的任务开始,例如让agent朝着一个无障碍的目标前进,那么agent则会很容易地学会完成任务。在此基础上,我们再通过增加墙体的大小来慢慢增加任务的难度,直到agent可以完成最初几乎不可能完成的任务(就是小蓝通过大白块间接越过高墙)。下图展示了任务由易到难的过程:

具体实现

在一个训练环境中,具有相同 Behavior Name 的每一组Agent具有相同的课程。这些课程被称为"metacurriculum"(元课程)。元课程允许不同组的agent在同一环境中学习不同的课程。
指定课程
为了定义课程,第一步是确定环境的哪些参数会变化。在Wall Jump示例环境中,墙的高度则是这个变量。我们将墙的高度定义为 Academy.Instance.EnvironmentParameters 中可以访问的 Environment Parameters 参数,并通过这样做使得Python API来对其调整。我们将创建一个YAML配置文件来描述课程过程,而不是通过手动来调整课程。通过该配置文件,我们可以指定墙在训练的某个阶段开始改变高度,既可以通过训练总步数的百分比来设置,也可以通过agent获得的平均奖励来设置(Wall Jump中用的是第一种)。下面来看一会下Wall Jump环境课程的示例配置。
    
BigWallJump : measure : progress thresholds : [ 0.1 , 0.3 , 0.5 ] min_lesson_length : 100 signal_smoothing : true parameters : big_wall_min_height : [ 0.0 , 4.0 , 6.0 , 8.0 ] big_wall_max_height : [ 4.0 , 7.0 , 8.0 , 8.0 ] SmallWallJump : measure : progress thresholds : [ 0.1 , 0.3 , 0.5 ] min_lesson_length : 100 signal_smoothing : true parameters : small_wall_height : [ 1.5 , 2.0 , 2.5 , 4.0 ]
在配置的顶层是 Behavior Name ,即对应于agent的行为名称(在Unity中的设置)。每种行为的课程都有以下参数:
  • measure :衡量学习进度和课程进度的方法。
  • reward :使用奖励来衡量。
  • progress :使用steps/max_steps比例来衡量。
  • thresholds (float array):配合 measure 使用,应当改变课程的阶段。
  • 简单解释一下以上两个属性,以Wall Jump为例,其measure属性为 progress ,对应thresholds为 [0.1,0.3,0.5] ,其含义是:
  • 一开始训练时,墙的高度变化范围是0-4(参考下面 parameters 参数);当steps/max_steps=0.1(当前训练步数/总训练步数=0.1)时,改变一次墙的高度范围(对应下面参数为4.0-7.0);当steps/max_steps=0.3时,在改变一次墙的高度范围(6.0-8.0);当steps/max_steps=0.5时,墙的高度固定为8.0。
  • min_lesson_length (int):在课程改变之前,应该完成的episodes最小数量。如果 measure 设置为 reward ,则将使用最后 min_lesson_length episodes的平均奖励来确定课程是否应该改变。必须是非负数。
  • 重要 :与 thresholds 比较的平均奖励不同于控制台(Console)中记录的平均奖励。例如,如果 min_lesson_length 为100,那么在最近100的episodes的平均累积奖励超过当前 thresholds 设置的值后,课程将改变。记录到控制台的平均奖励是由配置文件中 summary_freq 参数决定的。
  • signal_smoothing (true/false):是否通过以前的值来衡量当前的进度。
  • 如果设置为 true ,则权重将由老的0.25变为新的0.75。
  • parameters (dictionary: key(string),value(float array)):对应于要控制的环境参数。每个数组的长度应该大于thresholds的数目。具体的意思在上面也有解释。
一旦我们定义好课程配置,我们就必须使用定义的环境参数,并通过agent的 OnEpisodeBegin() 函数来修改环境。具体我们在后面的章节介绍Wall Jump的Agent脚本时再来看。
开始训练
至此,我们指定好了我们的课程配置文件,然后通过ml-agents命令台中使用 -curriculum 命令字来指定我们的配置文件,PPO将使用课程学习进行训练。例如下面要通过课程学习训练Wall Jump,在控制台中可以输入:
mlagents-learn config/trainer_config.yaml --curriculum=config/curricula/wall_jump.yaml --run-id=wall-jump-curriculum
主要是留意--curriculum命令字的使用。
Note:如果要恢复使用课程的训练,在mlagents-learn时使用--lesson标志来输入最后课程的编号。
至此,我们大概了解了在ML-Agents中课程训练的配置文件,下面我们开始正式学习Wall Jump示例。

三、环境与训练参数

  • 设定:一个平台环境中,agent可以跳过一堵墙。
  • 目标:Agent必须使用方块越过墙体到达目的地。
  • Agents:环境中包含一个链接到两个不同模型的agent。Agent的策略链接改变取决于墙的高度(即墙低的时候<4时,采用SmallWallJump训练,墙高度>4时,采用BigWallJump训练)。策略的改变在WallJumpAgent脚本中实现,下面看代码会有介绍。
  • Agent奖励设定:
  • 每一步-0.0005。
  • 如果agent到达目的地,则+1.0。
  • 如果agent从平台上掉落,则-1.0。
  • 行为参数:
  • 矢量观测空间:74个变量,对应于14条射线(ray casts),每条射线检测四个物体。再加agent的世界坐标以及agent是否已接地。
  • 矢量动作空间:离散(Discrete),4个分支,分别是
  • 前后移动:前移、后移、No Action
  • 旋转:左旋转、右旋转、No Action
  • 左右移动:左平移、右平移、No Action
  • 跳跃:跳跃、NoAction
  • 视觉观察值:无
  • 可变参数:4个
  • 基准平均奖励(Big && Small Wall):0.8

四、场景基本构成

场景中包含24个训练单元,训练单元之间都相隔较远距离,如下图:
  • PlayerCam
  • PlayerCam 是我们一开始游戏的相机,对应于第一个训练单元:
  • OverviewCam
  • OverviewCam 是一个鸟瞰相机,注意它是在 Display 2 中,具体效果如下:
  • 这个相机目前没发现什么特殊的用途,感觉应该是想把所有的训练单元都纳入,于是我自己调整了一下,就变成如下效果:
  • 这个视图还是有一点好处,就是之后我们在训练的时候,因为这个示例也和上一次讲的PushBlock示例一样,当小蓝完成任务地面就变绿闪一下;当小蓝任务失败或者掉落平台,地面就变红闪一下。用这种鸟瞰视图就很容易看出来你训练的效果怎么样,例如一开始应该是红色闪的多,越到后面就是绿色闪的多,由此来看训练效果。
  • WallJumpSettings
  • WallJumpSettings 物体依旧是设置了一些全局变量,主要有小蓝的速度、小蓝跳跃的高度等。
  • WallJumpArea
  • WallJumpArea 是一个基本的训练单元,主要有以下物体:
  • 这里面的构成和PushBlock里的很相似,没什么太难的点。主要说一下 SpawnVolume ,这个对象在运行的时候就令它SetActive(false)了。那为什么还需要这个对象呢?其实是在Agent的脚本里,利用了该对象的Bounds,即控制大白块的位置是随机产生在该区域里的。
  • 其他的物体没什么好说的,下面我们直接进入代码环节。

五、代码分析

其余代码都比较简单,我们主要来研究一下 WallJumpAgent.cs 脚本。

Agent初始化

    
using System . Collections ; using UnityEngine ; using MLAgents ; using Barracuda ; using MLAgents . Sensors ; public class WallJumpAgent : Agent { //该值范围为[0,5],控制墙体高度以及设置不同的Brain int m_Configuration ; //当墙体高度为0时,采用此Brain public NNModel noWallBrain ; //当墙体高度为1时,采用此Brain(实际上与NoWallBrain是一样的) public NNModel smallWallBrain ; //当墙体高度大于1时,采用此Brain public NNModel bigWallBrain ; public GameObject ground ; //地面,变换地面材质用 public GameObject spawnArea ; //大白块随机生成区域 Bounds m_SpawnAreaBounds ; //区域的Bounds public GameObject goal ; //目标区域 public GameObject shortBlock ; //大白块 public GameObject wall ; //墙体 Rigidbody m_ShortBlockRb ; //大白块的刚体 Rigidbody m_AgentRb ; //小蓝的刚体 Material m_GroundMaterial ; Renderer m_GroundRenderer ; WallJumpSettings m_WallJumpSettings ; //小蓝的速度、跳跃高度等设置 public float jumpingTime ; //跳起空中时间 public float fallingForce ; //小蓝在空中时下降时所受向下的力 //判断小蓝是否在落在地上、墙上或大白块上 public Collider [ ] hitGroundColliders = new Collider [ 3 ] ; Vector3 m_JumpTargetPos ; //跳跃目标位置 Vector3 m_JumpStartingPos ; //起跳位置 /// <summary> /// 初始化Agent /// </summary> public override void InitializeAgent ( ) { m_WallJumpSettings = FindObjectOfType < WallJumpSettings > ( ) ; //获取全局设定 m_Configuration = Random . Range ( 0 , 5 ) ; //随机产生墙面高度 m_AgentRb = GetComponent < Rigidbody > ( ) ; //获得小蓝的刚体 m_ShortBlockRb = shortBlock . GetComponent < Rigidbody > ( ) ; //获得大白的刚体 m_SpawnAreaBounds = spawnArea . GetComponent < Collider > ( ) . bounds ; //获得大白随机产生的区域范围 m_GroundRenderer = ground . GetComponent < Renderer > ( ) ; m_GroundMaterial = m_GroundRenderer . material ; //获得地面的材质,以备后面改变地面材质 spawnArea . SetActive ( false ) ; } }
初始化内容都比较简单,注意第一个变量 m_Configuration ,该变量只标识了墙的高度应该为多少,但并不是指定墙的高度是几,例如m_Configuration=1时,实际墙的高度会是4而不是1,该值的使用一会儿在 ConfigureAgent(int config) 方法中讲解。下面我们来看Agent收集的环境观测值。

环境观测值收集

在第三章环境训练参数中,我们知道了本示例小蓝除了采用了14条射线来收集射线监测数据外,还需要采集自己的世界坐标以及是否接触地面的信息,这两种信息在 CollectObservations(Vector sensor) 方法中进行收集。
    
/// <summary> /// 收集环境中其他数据 /// </summary> /// <param name="sensor"></param> public override void CollectObservations ( VectorSensor sensor ) { var agentPos = m_AgentRb . position - ground . transform . position ; //小蓝相对于地面中心的位置,除以20是为了让其位置x、y、z值归一化 sensor . AddObservation ( agentPos / 20f ) ; //判断小蓝是否落地 sensor . AddObservation ( DoGroundCheck ( true ) ? 1 : 0 ) ; } /// <summary> /// 检测是否落地 /// </summary> /// <param name="smallCheck">墙的高度是否是低(<=4)</param> /// <returns>true为落地,否则为false</returns> public bool DoGroundCheck ( bool smallCheck ) { if ( ! smallCheck ) { //4<墙高度<=8 hitGroundColliders = new Collider [ 3 ] ; var o = gameObject ; //无GC的相交盒检测,可采集与相交盒碰撞的碰撞体Collider[] //此处赋值给hitGroundColliders Physics . OverlapBoxNonAlloc ( o . transform . position + new Vector3 ( 0 , - 0.05f , 0 ) , new Vector3 ( 0.95f / 2f , 0.5f , 0.95f / 2f ) , hitGroundColliders , o . transform . rotation ) ; var grounded = false ; foreach ( var col in hitGroundColliders ) { //遍历与碰撞盒产生碰撞的物体 if ( col != null && col . transform != transform && ( col . CompareTag ( "walkableSurface" ) || col . CompareTag ( "block" ) || col . CompareTag ( "wall" ) ) ) { //若碰撞的物体为地面、大白块或墙体,则判断小蓝已落地 grounded = true ; //then we're grounded break ; } } return grounded ; } else { //0<=墙高度<=4 RaycastHit hit ; //若墙的高度较低,则只需要向下发出长度为1的射线来检测小蓝是否落地 Physics . Raycast ( transform . position + new Vector3 ( 0 , - 0.05f , 0 ) , - Vector3 . up , out hit , 1f ) ; if ( hit . collider != null && ( hit . collider . CompareTag ( "walkableSurface" ) || hit . collider . CompareTag ( "block" ) || hit . collider . CompareTag ( "wall" ) ) && hit . normal . y > 0.95f ) { return true ; } return false ; } }
此处代码中,需要注意 DoGroundCheck(bool smallCheck) 这个方法,该方法除了在收集观测值时使用,还在其他3处地方分别使用。
首先该方法是为了检测小蓝是否落地,是则返回true,否则返回false。然后其分别处理了墙面高和低的两种情况:若墙面较高,则采用无GC的相交盒来检测小蓝的碰撞状态;若墙面较低,则直接向下发射射线来检测小蓝是否落地。
Physics.OverlapBoxNonAlloc() 则是相交盒检测方法,在上一篇“ML-Agents(八)PushBlock”中介绍了 Physics.CheckBox() ,该方法与CheckBox()不同的是,CheckBox只会返回bool来判断是否产生碰撞,而OverlapBox()不仅可以返回bool来判断是否产生碰撞,而且可以将产生碰撞的Colliders获取到。
如下图,我将小蓝材质改透明,其中白色的方块则是代码中Physics.OverlapBoxNonAlloc()方法产生的相交盒。Physics.OverlapXXXNonAlloc()对应无GC的方式,这里XXX也可以是Sphere和Capsule。

Agent动作反馈

    
/// <summary> /// Agent动作 /// </summary> /// <param name="vectorAction"></param> public override void AgentAction ( float [ ] vectorAction ) { MoveAgent ( vectorAction ) ; //小蓝移动 if ( ( ! Physics . Raycast ( m_AgentRb . position , Vector3 . down , 20 ) ) || ( ! Physics . Raycast ( m_ShortBlockRb . position , Vector3 . down , 20 ) ) ) { //若小蓝落下平台或大白块落下平台 SetReward ( - 1f ) ; //惩罚1 Done ( ) ; //本次训练结束,并重置agent ResetBlock ( m_ShortBlockRb ) ; //重置大白块位置、速度等 //设置地面颜色为红色 StartCoroutine ( GoalScoredSwapGroundMaterial ( m_WallJumpSettings . failMaterial , .5f ) ) ; } } /// <summary> /// Agent移动 /// </summary> /// <param name="act"></param> public void MoveAgent ( float [ ] act ) { AddReward ( - 0.0005f ) ; //每一步-0.0005 //判断小蓝是否在地面上,若在地面上则移动速度相应要快一些,在空中的话移动速度要减半 var smallGrounded = DoGroundCheck ( true ) ; //墙低情况 var largeGrounded = DoGroundCheck ( false ) ; //墙高情况 var dirToGo = Vector3 . zero ; var rotateDir = Vector3 . zero ; var dirToGoForwardAction = ( int ) act [ 0 ] ; //前后移动 var rotateDirAction = ( int ) act [ 1 ] ; //左右旋转 var dirToGoSideAction = ( int ) act [ 2 ] ; //左右移动 var jumpAction = ( int ) act [ 3 ] ; //跳跃 //前后移动 if ( dirToGoForwardAction == 1 ) dirToGo = ( largeGrounded ? 1f : 0.5f ) * 1f * transform . forward ; else if ( dirToGoForwardAction == 2 ) dirToGo = ( largeGrounded ? 1f : 0.5f ) * - 1f * transform . forward ; //左右旋转 if ( rotateDirAction == 1 ) rotateDir = transform . up * - 1f ; else if ( rotateDirAction == 2 ) rotateDir = transform . up * 1f ; //左右平移 if ( dirToGoSideAction == 1 ) dirToGo = ( largeGrounded ? 1f : 0.5f ) * - 0.6f * transform . right ; else if ( dirToGoSideAction == 2 ) dirToGo = ( largeGrounded ? 1f : 0.5f ) * 0.6f * transform . right ; //跳跃 if ( jumpAction == 1 ) if ( ( jumpingTime <= 0f ) && smallGrounded ) { //判断小蓝是否在地上且jumpingTime<=0,初始化起跳变量 Jump ( ) ; } transform . Rotate ( rotateDir , Time . fixedDeltaTime * 300f ) ; //旋转 m_AgentRb . AddForce ( dirToGo * m_WallJumpSettings . agentRunSpeed , ForceMode . VelocityChange ) ; //前后左右移动 if ( jumpingTime > 0f ) { //起跳条件满足 m_JumpTargetPos = new Vector3 ( m_AgentRb . position . x , m_JumpStartingPos . y + m_WallJumpSettings . agentJumpHeight , m_AgentRb . position . z ) + dirToGo ; //计算跳跃后控制位置 //使得小蓝跳到计算后的位置m_JumpTargetPos,并限制其速度 MoveTowards ( m_JumpTargetPos , m_AgentRb , m_WallJumpSettings . agentJumpVelocity , m_WallJumpSettings . agentJumpVelocityMaxChange ) ; } if ( ! ( jumpingTime > 0f ) && ! largeGrounded ) { //判断小蓝处于空中,给小蓝施加向下的力使其下落 m_AgentRb . AddForce ( Vector3 . down * fallingForce , ForceMode . Acceleration ) ; } jumpingTime -= Time . fixedDeltaTime ; } /// <summary> /// 重置大白块 /// </summary> /// <param name="blockRb"></param> void ResetBlock ( Rigidbody blockRb ) { //重置大白块的位置 blockRb . transform . position = GetRandomSpawnPos ( ) ; blockRb . velocity = Vector3 . zero ; //速度置零 blockRb . angularVelocity = Vector3 . zero ; //角速度置零 } /// <summary> /// 改变地面材质颜色 /// </summary> /// <returns></returns> /// <param name="mat">要换的材质</param> /// <param name="time">变换材质后变回原先材质的延时时间</param> IEnumerator GoalScoredSwapGroundMaterial ( Material mat , float time ) { m_GroundRenderer . material = mat ; yield return new WaitForSeconds ( time ) ; //等待2秒 m_GroundRenderer . material = m_GroundMaterial ; }
这里的代码虽长,但是都比较简单,属于一看就懂系列,但是有一个点可以注意一下,即此处小蓝起跳以及下落的代码处理过程,感觉和我见过处理跳跃的方式有一些不同。

Agent重置

    
/// <summary> /// Agent重置 /// </summary> public override void AgentReset ( ) { ResetBlock ( m_ShortBlockRb ) ; //重置大白块 //重置小蓝位置 transform . localPosition = new Vector3 ( 18 * ( Random . value - 0.5f ) , 1 , - 12 ) ; m_Configuration = Random . Range ( 0 , 5 ) ; //重置墙体高度以及选用的Brain m_AgentRb . velocity = default ( Vector3 ) ; //小蓝速度置零 } /// <summary> /// 检测小蓝是否到达目标区域 /// </summary> /// <param name="col"></param> void OnTriggerStay ( Collider col ) { if ( col . gameObject . CompareTag ( "goal" ) && DoGroundCheck ( true ) ) { //若小蓝到目标区域,且在地面上 SetReward ( 1f ) ; //奖励1 Done ( ) ; //结束此次训练 //使地面置为绿色 StartCoroutine ( GoalScoredSwapGroundMaterial ( m_WallJumpSettings . goalScoredMaterial , 2 ) ) ; } }
重置Agent的代码,一部分实际上是在Unity的方法 OnTriggerStay(Collider col) 中实现的,因为目标区域其实也是有碰撞体的,因此若小蓝Stay在目标区域,则会触发此函数。
到此为止,我们还没有看到此示例是如何使用两个Brain来回切换使用的,下面我们就来看一下这一部分代码是如何实现的。

其他

    
void FixedUpdate ( ) { if ( m_Configuration != - 1 ) { //设置agent的Brain ConfigureAgent ( m_Configuration ) ; //标志位置位 m_Configuration = - 1 ; } } /// <summary> /// 设置Agent的Brain,墙的高低来决定不同的Brain /// </summary> /// <param name="config"> /// 如果为0:No wall + noWallBrain /// 如果为1:Samll Wall + samllWallBrain /// 其他:Tall wall + bigWallBrain /// </param> void ConfigureAgent ( int config ) { var localScale = wall . transform . localScale ; //墙的比例大小 if ( config == 0 ) { //如果m_Configuration==0,墙高度为0 localScale = new Vector3 ( localScale . x , Academy . Instance . FloatProperties . GetPropertyWithDefault ( "no_wall_height" , 0 ) , localScale . z ) ; wall . transform . localScale = localScale ; //设置agent的Model GiveModel ( "SmallWallJump" , noWallBrain ) ; } else if ( config == 1 ) { //如果m_Configuration==1 localScale = new Vector3 ( localScale . x , Academy . Instance . FloatProperties . GetPropertyWithDefault ( "small_wall_height" , 4 ) , localScale . z ) ; wall . transform . localScale = localScale ; GiveModel ( "SmallWallJump" , smallWallBrain ) ; } else { //如果m_Configuration>1 //若开始训练时,此处的min和max值取决于课程配置值 var min = Academy . Instance . FloatProperties . GetPropertyWithDefault ( "big_wall_min_height" , 8 ) ; var max = Academy . Instance . FloatProperties . GetPropertyWithDefault ( "big_wall_max_height" , 8 ) ; var height = min + Random . value * ( max - min ) ; localScale = new Vector3 ( localScale . x , height , localScale . z ) ; wall . transform . localScale = localScale ; GiveModel ( "BigWallJump" , bigWallBrain ) ; } }
由以上代码可以看出,其实现是在FixedUpdate()中,实时判断m_Configuration的值来改变agent不同的Brain,m_Configuration会在训练一开始以及AgentReset的时候随机重置。
此外,之前已经讲过, Academy.Instance.FloatProperties.GetPropertyWithDefault(string key, float defaultValue) 这个方法的第二个参数defaultValue是默认值,若key没读取到,则采用输入的默认值。因此在该示例运行的时候,你会发现墙的高度只有0、4、8这是三个值,但是如果开始训练后,该方法中的key就会与之前课程配置文件中的值开始对应。
即在SmallWallJump的时候,墙的高度从1.5->2.0->2.5->4.0;在BigWallJump时,墙的高度则一开始会在0-4随机,然后在4-7随机,然后在6-8随机,最后高度固定在8。从此也可以看出课程训练从易到难的特点。

六、训练

训练配置参数

Wall Jump的课程训练配置已经在上面第二章讲解过了,下面我们来看一下Wall Jump的训练配置:
trainer_config.yaml
    
SmallWallJump : max_steps : 5e6 batch_size : 128 buffer_size : 2048 beta : 5.0e-3 hidden_units : 256 summary_freq : 20000 time_horizon : 128 num_layers : 2 normalize : false BigWallJump : max_steps : 2e7 batch_size : 128 buffer_size : 2048 beta : 5.0e-3 hidden_units : 256 summary_freq : 20000 time_horizon : 128 num_layers : 2 normalize : false
首先因为要训练两个Model,分别对应SmallWallJump和BigWallJump,因此在配置文件中对应的是两个部分。可以看出Small的 max_steps 比Big的更小,也好理解,简单的任务训练快,难的任务需要训练步数应该较多。
其他的属性在前几篇都已经分析过,所以不再赘述,这里有一个 normalize 属性,其作用是是否对输入的矢量观测值(vector observation)进行规范化处理。在之前的内容我们知道,规范化对于复杂的连续控制(continuous control)问题很有用,但对于较简单的离散控制(discrete control)可能反而有害。而且我们在代码中已经对小蓝位置做过规范化处理,因此这里使其变为false。

开始训练

cd到ml-agents目录,并输入一下命令:
mlagents-learn config/trainer_config.yaml --curriculum=config/curricula/wall_jump.yaml --run-id=walljump
开始训练,一开始会发现失败的情况更多,如下图:
上面红色闪烁则为失败的单元,同时我们观察Console会发现, SmallWallJump 和 BigWallJump 会随机穿插进行训练,如下图:
训练了一晚上,发现没训练成功。。。。。摔!
由图可以看到已经训练9个小时了,但平均收益还是-1.1左右,在Unity中也是失败的多,没有什么改观。
其实这期间我又做了许多次尝试,包括修改源码之类的,但是都没有训练成功。于是我最终从git上又拉取了 最新版的ml-agents(release_1) ,并重新新建了一个Anaconda训练环境(具体可参考 Unity ML-Agents v0.15.0(一)环境部署与试运行 ),然后开始训练,终于得到了比较好的训练效果:
这才是训练了40万步左右的结果,相比之前的训练效果好了不止N倍。其实我现在还没搞清楚为什么一开始的版本(0.15.0)训练失败。。。
除此之外,最新版release_1里的文档少了很多,包括课程训练的简介都没有了,大家要是想看英文原版,还是要选0.15.1之前版本的doc才能看到。
OK,这次训练结果就没有问题了,相应的Tensorboard如下:
图中橘黄色线的是 SmallWallJump 的训练数据,蓝色线是 BigWallJump 的训练数据,在Lesson图表有明显的阶梯状,代表各个课程的开始。
我们再以蓝色线BigWallJump训练数据为例,在 Cumulative Reward 图标里,可以看出每次课程难度的增加,会使得改变时的累计奖励骤减,但是慢慢会上升,最终的基准平均奖励也和官方的数据一致,大概在0.8左右。
我们把训练好的模型放到Unity中试验一下:
方便起见直接用一个来测试即可。
OK,发现训练的模型也没有问题。

七、总结

这个示例我们主要学习了如何使用Curriculum Learning进行训练,其中关于射线传感器的数据采集内容在 ML-Agents(八)PushBlock 已有讲述,不熟悉的亲可以回看。本篇文章就此结束,欢迎大家留言交流~
写文不易~因此做以下申明:
1.博客中标注原创的文章,版权归原作者 煦阳(本博博主) 所有;
2.未经原作者允许不得转载本文内容,否则将视为侵权;
3.转载或者引用本文内容请注明来源及原作者;
4.对于不遵守此声明或者其他违法使用本文内容者,本人依法保留追究权等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值