注:本文章为官方文档翻译,如有侵权行为请联系作者删除
Agent - Unity ML-Agents Toolkit–原文链接>
ML-Agents:智能体(一)
ML-Agents:智能体(二)
ML-Agents:智能体(三)
ML-Agents:智能体(四)
动作是Agent执行的来自 Policy 的指令。当 Academy 调用函数时,动作将作为参数 传递给 an IActionReceiver
( anAgent
或 an IActuator
)。支持两种类型的动作:连续和离散。ActionBuffers``IActionReciever.OnActionReceived()
策略和训练算法都不知道动作值本身的含义。训练算法只是尝试不同的动作列表值,并观测随着时间的推移和许多训练事件对累积奖励的影响。因此,为Agent定义动作的唯一地方是在函数中OnActionReceived()
。
例如,如果您设计了一个在二维空间中移动的Agent,则可以使用连续或离散动作。在连续情况下,您可以将动作大小设置为 2(每个维度一个),Agent的策略将输出一个具有两个浮点值的动作。在离散情况下,您将使用一个大小为 4 的分支(每个方向一个),策略将创建一个动作数组,其中包含一个值范围从零到三的元素。或者,您可以创建两个大小为 2 的分支(一个用于水平移动,一个用于垂直移动),策略将输出一个动作数组,其中包含两个值范围从零到一的元素。您也可以使用连续和离散动作的组合,例如,对水平移动使用一个连续动作,对垂直移动使用大小为 2 的离散分支。
Heuristic()
请注意,当您为Agent编写操作程序时,使用Agent的方法测试操作逻辑通常会很有帮助,这可以让您将键盘命令映射到操作。
当Agent的策略具有连续动作时, ActionBuffers.ContinuousActions
传递给AgentOnActionReceived()
函数的是一个长度等于Continuous Action Size
属性值的数组。数组中的各个值具有您赋予它们的任何含义。例如,如果您将数组中的元素指定为Agent的速度,则训练过程将学习通过此参数控制Agent的速度。
3DBall 示例使用具有两个控制值的连续动作。
这些控制值应用于立方体的旋转:
public override void OnActionReceived(ActionBuffers actionBuffers)
{
var actionZ = 2f * Mathf.Clamp(actionBuffers.ContinuousActions[0], -1f, 1f);
var actionX = 2f * Mathf.Clamp(actionBuffers.ContinuousActions[1], -1f, 1f);
gameObject.transform.Rotate(new Vector3(0, 0, 1), actionZ);
gameObject.transform.Rotate(new Vector3(1, 0, 0), actionX);
}
默认情况下,我们提供的 PPO 算法的输出会将值预限制 ActionBuffers.ContinuousActions
在 [-1, 1] 范围内。如果您计划在环境中使用第三方算法,最佳做法是手动限制这些值。如上所示,您可以在限制控制值后根据需要缩放控制值。
当 Agent 的 Policy 采用离散动作时, ActionBuffers.DiscreteActions
传递给 AgentOnActionReceived()
函数的 是一个长度为 的整数数组Discrete Branch Size
。定义离散动作时,Branches
是一个整数数组,每个值对应每个分支的可能性数量。
例如,如果我们想要一个可以在平面上移动和跳跃的Agent,我们可以定义两个分支(一个用于运动,一个用于跳跃),因为我们希望我们的Agent能够同时移动和跳跃。我们将第一个分支定义为有 5 种可能的动作(不移动、向左、向右、向后、向前),第二个分支定义为有 2 种可能的动作(不跳、跳跃)。该 OnActionReceived()
方法看起来像:
// Get the action index for movement
int movement = actionBuffers.DiscreteActions[0];
// Get the action index for jumping
int jump = actionBuffers.DiscreteActions[1];
// Look up the index in the movement action list:
if (movement == 1) { directionX = -1; }
if (movement == 2) { directionX = 1; }
if (movement == 3) { directionZ = -1; }
if (movement == 4) { directionZ = 1; }
// Look up the index in the jump action list:
if (jump == 1 && IsGrounded()) { directionY = 1; }
// Apply the action results to move the Agent
gameObject.GetComponent<Rigidbody>().AddForce(
new Vector3(
directionX * 40f, directionY * 300f, directionZ * 40f));
使用离散操作时,可以指定某些操作对于下一个决策而言是不可能的。当Agent由神经网络控制时,Agent将无法执行指定操作。请注意,当Agent由其启发式方法控制时,Agent仍将能够决定执行屏蔽操作。为了禁止某个操作,请重写虚拟方法Agent.WriteDiscreteActionMask()
,并调用 SetActionEnabled()
提供的IDiscreteActionMask
:
public override void WriteDiscreteActionMask(IDiscreteActionMask actionMask)
{
actionMask.SetActionEnabled(branch, actionIndex, isEnabled);
}
在哪里:
branch
是要允许或禁止操作的分支的索引(从 0 开始)actionIndex
是您想要允许或禁止的操作的索引。isEnabled
是一个布尔值,指示是否应该允许该操作。例如,如果你有一个有 2 个分支的Agent,并且在第一个分支(分支 0)上有 4 个可能的操作:“不做任何事”、“跳跃”、“射击” 和_“更换武器”。然后使用下面的代码,Agent将在下一个决策中“不做任何事”或“更换武器”_(因为动作索引 1 和 2 被屏蔽)
actionMask.SetActionEnabled(0, 1, false);
actionMask.SetActionEnabled(0, 2, false);
Note:
SetActionEnabled
如果要在多个分支上放置掩码,可以多次调用。Actuator API 允许用户将行为从 Agent 中抽象出来并放入组件中(类似于 ISensor API)。IActuator
接口和Agent
类都实现了IActionReceiver
接口,以便与当前的向后兼容Agent.OnActionReceived
。这意味着在您决定使用 API 之前,您无需更改代码IActuator
。
与界面一样ISensor
,该IActuator
界面适用于高级用户。
抽象类ActuatorComponent
用于IActuator
在运行时创建实际。它必须与 附加到GameObject
,Agent
或附加到子GameObject
。执行器及其所有数据结构在 期间初始化Agent.Initialize
。这样做是为了防止运行时出现意外分配。
IActuator
您可以在示例场景中找到实现的示例Basic
。 注意Behavior Parameters
:使用IActuator
和时,无需调整Agent中的操作 ActuatorComponents
。
在内部,Agent.OnActionReceived
使用IActuator
将动作发送给Agent,尽管这主要是从用户那里抽象出来的。
Discrete
和/或Continuous
动作。鉴于作者水平有限,本文可能存在不足之处,欢迎各位读者提出指导和建议,共同探讨、共同进步。