ML-Agents官方文档中首先介绍了该项目Demo Project的上手方法,随后才涉及到如何建立全新的机器学习环境。本专栏则将先行介绍如何从零开始搭建机器学习环境。
1.1 新建Unity 3D工程,名称随意。
1.2 菜单栏 Edit>Project Settings>Package Manager选项,勾选“Enable Preview Packages”选项,这将允许我们忽略Unity Editor版本所造成的限制,能够将最新版本的ML-Agents资源包导入到项目中,以便与Github项目资源或其他开源工程保持一致。
1.3 举个反例,当Unity Editor版本为2020.3.30时,若使用系统适配的ML-Agents资源包版本,则会出现部分命名空间未被定义的情况,说白了就是更新不及时,外面Demo已经在使用的功能旧版的包里没有。
1.4 菜单栏 Window>Package Manager,打开包管理器,搜索ML-Agents,在结果中选取最新版本的资源包并导入到本地工程中。
1.5 在Unity项目目录中新建文件夹,命名为“config”,用于存放训练配置文件。
1.6 在Unity项目中或硬盘其他位置新建文件夹,命名随意,用于安装虚拟环境所需包。需注意的是,各项配置完成后该文件夹将占用较大空间,若置于Unity工程中较方便,同时也将导致项目体积过大,可自行权衡。
2.1 搭建机器学习需要几十种插件,为避免项目逻辑混乱或影响到本地其他项目,应新建与本地环境完全隔绝的专用学习环境;此外,在专属环境中可以对不同版本、不同插件的兼容性进行较为便捷的验证。网上其他教程往往推荐使用conda对虚拟环境进行管理,若是只拿来跑ML-Agents,考虑到虚拟环境的可操作性(比如更换python解释器脚本)及稳定性(包链接被ban),更推荐通过CMD来管理虚拟环境。
2.2 在本地环境中安装Python解释器(别忘了设置系统变量和环境变量),推荐3.6及3.7版本(亲测3.7.0版本没有问题,所有工具采用默认版本即可,流程能跑得通),Python版本过高(如3.8以上)可能会出现与虚拟环境中所使用的插件无法兼容的问题。
2.3 在本地环境中安装pip,具体方法可参考pypi官网或其他论坛。
2.4 若Python及pip都安装成功,可在CMD中输入下列命令进行校验,若出现下列结果则证明安装无误。
pip -V
Python
2.5 跳转到1.6中新建的文件夹,在此目录中打开CMD。
2.6 当然,也可以自行扩展一下鼠标右键菜单(Win11更新后更难用了),添加一个“CMD Here”选项。新建一个文本文件,复制以下代码进去,然后将文件后缀名改为(.reg),退出点运行一下即可。对于经常用Git或者CMD的朋友们来说还是蛮有用的。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell\cmdhere]
@="Cmd&Here"
[HKEY_CLASSES_ROOT\*\shell\cmdhere\command]
@="\"C:\\Windows\\System32\\cmd.exe\" "
[HKEY_CLASSES_ROOT\Folder\shell\cmdhere]
@="Cmd&Here"
[HKEY_CLASSES_ROOT\Folder\shell\cmdhere\command]
@="\"C:\\Windows\\System32\\cmd.exe\""
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\background\shell\cmd_here]
@="在此处打开命令行"
"Icon"="cmd.exe"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\background\shell\cmd_here\command]
@="\"C:\\Windows\\System32\\cmd.exe\""
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\cmdPrompt]
@="在此处打开命令行"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\cmdPrompt\command]
@="\"C:\\Windows\\System32\\cmd.exe\" \"cd %1\""
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shell\cmd_here]
@="在此处打开命令行"
"Icon"="cmd.exe"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shell\cmd_here\command]
@="\"C:\\Windows\\System32\\cmd.exe\""
2.7 无论如何,在虚拟环境对应的目录中打开CMD,依次输入如下代码。
md python-envs %建立虚拟环境目录,名字可以自行决定
python -m venv python-envs\sample-env %建立虚拟环境,名字可以自行决定
pip install --upgrade pip %检查pip是否为最新版本
2.8 此时我们已经建立了虚拟环境,为将其激活,我们应运行虚拟环境目录/Scripts中的activate.bat文件,CMD命令如下所示(不嫌麻烦可以鼠标点)。后续每次运行ML-Agents,都需要事前激活虚拟环境。
cd /d J:\Virtual Environment\python-envs\sample-env\Scripts
%定位到虚拟环境所在文件夹,若有多个环境存在,不要搞错环境名及环境目录
activate.bat
%激活虚拟环境
cd /d J:\Unity Projects\Homemade-ML-Agent
%切换到Unity工程所在目录
2.9 仅仅有虚拟环境还不行,我们还需要配置ML-Agents所需的各类工具,如机器学习常用到的Pytorch、Numpy等等。激活虚拟环境后,在CMD中输入如下代码进行安装。由于链接可能被ban,所以在下载时应开启全局加速保证稳定性,再或是使用国内既有的开源镜像网站,如清华镜像、阿里镜像、豆瓣镜像等。工具包较大,安装时需要耐心等待。
pip3 install torch~=1.7.1 -f https://download.pytorch.org/whl/torch_stable.html
2.10 Windows系统下(默认大家都是了)运行ML-Agents还需要安装Microsoft's Visual C++ Redistributable库(Latest supported Visual C++ Redistributable downloads | Microsoft Learn),对应自己系统的版本进行选择即可。
2.11 最后一步,我们需要在虚拟环境中安装ML-Agents工具包,在CMD中输入如下代码进行安装。另注:2.9、2.10、2.11这三步都需要启动pip工具,都走到这一步了默认已经顺利安装上了。
python -m pip install mlagents==0.28.0
最后如果所有工具都安装成功,可在虚拟环境中输入如下代码核验所安装的工具列表,如图所示。
pip list
激活虚拟环境后,打开Unity工程,建立训练环境
3.1 场景中建立空物体,Transform(1,1,1),Position(0,0,0),重命名为“TrainingArea”。
3.2 场景中建立Plane,设为TrainingArea的子物体,Transform(1,1,1),Position(0,0,0),重命名为“Floor”。
3.3 新建Cube,设为TrainingArea的子物体,Transform(1,1,1),Position(-3,0.5,-3),重命名为“Target”。
3.4 新建Sphere,设为TrainingArea的子物体,,Transform(1,1,1),Position(3,0.5,3),重命名为“RollerAgent”。
3.5 既然都是研究行人了,往场景中放点火很合理吧,Unity Asset Store中搜索Procedural fire可以发现相关资源,导入到项目中;该资源效果还说得过去,兼容性较好,最重要的是不要钱。
3.6 将Procedural fire包导入到工程中,随便拖一个预制体放到场景中,设为TrainingArea的子物体,Transform(1,1,1),Position(0,0.5,0)。各类火焰效果预制体层级比较复杂(涉及到粒子系统、Shader等组件),如果要改大小或者位置需要调整的参数较多,所以不如直接指代一个同位置的透明物体(关闭Mesh Renderer组件)作为火焰目标,预制体仅作为呈现效果。下图中绿色线框所代表的Sphere才是训练环境中与智能体进行交互的“火焰”,需注意的是,严谨起见这个Sphere最好也设为TrainingArea的子物体。
4.1 之前已经提到过,场景中的智能体叫RollerAgent,选中3.4中已经建立的Sphere,为其挂上一个新脚本,也命名为“RollerAgent”。
4.2 引入需要用到的命名空间,声明Agent类,声明Rigidbody。
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
using System.Collections.Generic;
using UnityEngine;
public class RollerAgent : Agent
{
Rigidbody rBody;
void Start() //保留Start方法,弃用Update方法
{
rBody = GetComponent();//声明刚体,并将其指定为RollerAgent物体下的刚体
}
public Transform Target; //定义目标对象
public Transform Fire; //定义火焰对象
}
4.3 定义训练场景开始时所需进行的初始化操作,将智能体位置归零并将目标点移动至新的随机位置。
public override void OnEpisodeBegin() //训练场景初始化,每次训练执行一次
{
// 如果智能体跌落,重置其位置
if (this.transform.localPosition.y < 0)
{
this.rBody.angularVelocity = Vector3.zero;
this.rBody.velocity = Vector3.zero;
this.transform.localPosition = new Vector3(3.0f, 0.5f, 3.0f);
}
// 将目标放置到随机生成的新位置
Target.localPosition = new Vector3(Random.value * 8 - 4,
0.5f,
Random.value * 8 - 4);
}
4.4 调用Sensors,为智能体赋予观察周边环境的能力。
public override void CollectObservations(VectorSensor sensor)
{
// 观察目标和智能体位置
sensor.AddObservation(Target.localPosition);
sensor.AddObservation(this.transform.localPosition);
// 观察智能体(其实就是刚体)的运动速度
sensor.AddObservation(rBody.velocity.x);
sensor.AddObservation(rBody.velocity.z);
}
4.5 定义作用力的大小,在场景启动时,为Sphere智能体配置行为模式,设置惩罚及奖励机制。
public float forceMultiplier = 10; //由于智能体有Rigidbody组件,因此力的大小直接决定运动参数
public override void OnActionReceived(ActionBuffers actionBuffers)
//调用该方法需要使用MLAgents.Actuators命名空间,官方教程里没有引用
{
// Actions, size = 2
Vector3 controlSignal = Vector3.zero;
controlSignal.x = actionBuffers.ContinuousActions[0];
controlSignal.z = actionBuffers.ContinuousActions[1];
rBody.AddForce(controlSignal * forceMultiplier);
//向刚体施加f = 10
// 观察到目标点的距离,观察到火焰的距离
float distanceToTarget = Vector3.Distance(this.transform.localPosition, Target.localPosition);
float distanceToFire = Vector3.Distance(this.transform.localPosition, Fire.localPosition);
// 若到达目标点,则奖励并结束场景
if (distanceToTarget < 1.42f)
{
SetReward(1.0f);
EndEpisode();
}
// 若从TrainingArea中摔落,则结束场景
else if (this.transform.localPosition.y < 0)
{
EndEpisode();
}
//若距离火焰太近,则惩罚并结束场景
else if (distanceToFire < 1.0f) //退出事件,离火太近
{
//SetReward(-1.0f);
//根据需要时启用,在当前情景设置下开不开差别不大
EndEpisode();
}
}
}
4.6 扩展:为智能体class添加计算时间成本的方法
DateTime startTime;
DateTime CurrentTime;
在每次训练场景开始的时候为startTime赋值。
public override void OnEpisodeBegin()
{
startTime = DateTime.Now;
}
public override void OnActionReceived(ActionBuffers actionBuffers)
{
CurrentTime = DateTime.Now;
float TimeSpan = GetSubSeconds(startTime, CurrentTime);
}
public void CalculateTimeCost(float CostedTime)
{
float TimeLength = CostedTime;
float Punishment = -0.001f * TimeLength;
SetReward(Punishment);
}
public int GetSubSeconds(DateTime startTimer, DateTime endTimer)
{
TimeSpan startSpan = new TimeSpan(startTimer.Ticks);
TimeSpan nowSpan = new TimeSpan(endTimer.Ticks);
TimeSpan subTimer = nowSpan.Subtract(startSpan).Duration();
//返回间隔秒数(不算差的分钟和小时等,仅返回秒与秒之间的差)
//我们的学习场景肯定是用不上了
//return subTimer.Seconds;
//返回相差时长(算上分、时的差值,返回相差的总秒数)
return (int)subTimer.TotalSeconds;
}
4.7 为场景中智能体配置目标对象及火焰对象,即将Target物体及FireTarget物体(用于代表火焰的圆球)拖拽至Inspector面板中的对应位置。
4.8 在为智能体添加各种组件及脚本,随后通过Inspector面板中配置智能体参数。新添加的脚本应包括Decision Requester以及Behavior Parameters,设置完成的Inspector界面应当如下图所示。
4.9 通过yaml文件对学习训练过程进行配置。打开1.5中已经在工程目录下建立的config文件夹,在其中新建文本文件,添加以下内容后将文件后缀名改为.yaml。
behaviors:
RollerBall:
trainer_type: ppo
hyperparameters:
batch_size: 10
buffer_size: 100
learning_rate: 3.0e-4
beta: 5.0e-4
epsilon: 0.2
lambd: 0.99
num_epoch: 3
learning_rate_schedule: linear
beta_schedule: constant
epsilon_schedule: linear
network_settings:
normalize: false
hidden_units: 128
num_layers: 2
reward_signals:
extrinsic:
gamma: 0.99
strength: 1.0
max_steps: 500000
time_horizon: 64
summary_freq: 10000
至此,我们已经完成了所有前期准备工作,打开Unity工程,激活虚拟环境,输入如下代码即可激活ML-Agents组件。在看到Unity标志后,在Editor中运行场景便可开始训练智能体。
mlagents-learn config/rollerball_config.yaml --run-id=RollerBall --force
%如果不是首次运行且需要覆盖之前运行结果,则应启用--force参数,反之则不需要
%yaml文件路径必须与与之前所配置的参数文件对应上
在训练结束或中途推出后,可通过如下代码查看训练数据。
tensorboard --logdir results
一般来说,训练数据的本地端口不变,可在浏览器中打开如下链接查看训练数据。
http://localhost:6006/
需注意的是,训练会产生大量垃圾文件,何况每次训练并不能保证达到理想的效果,因此需要不定期清理ML-Agents工具产生的缓存,地址如下所示。
C:\Users\"用户名"\AppData\Local\pip