跳转到内容

使用连续动作

让我们从之前的教程项目开始,为其添加连续动作。您可以使用初始项目跟随操作,或者如果您愿意,可以查看完整项目

离散动作 vs. 连续动作

在之前的指南中,我们使用了离散动作 - 我们的智能体必须在有限选项集(0 或 1)之间选择以匹配模式。在实际场景中,我们可能会接收大量传感器数据和视觉输入来决定按下哪个按钮。

然而,在许多现实应用中,这并不总是可能的。对于控制以下类型的事物:

  • 车辆的转向角度
  • 机械臂的关节扭矩
  • 发动机的功率水平

我们的智能体需要输出连续动作——精确的浮点值,而非分类选择。

向环境添加连续动作

让我们修改环境,同时包含离散和连续动作。我们将保留原始的模式匹配任务,但添加第二个模式,我们期望 AI 输出这个新值的平方根。

注意我们只改变了期望值——智能体需要仅通过奖励信号的引导,通过试错来弄清楚我们想要什么!

首先,在 PatternMatchingEnvironment.cs 中添加新字段以跟踪第二个模式和连续动作:

PatternMatchingEnvironment.cs
private int pattern = 0;
private int pattern2 = 0;
private int aiChoice = 0;
private float aicontinuousChoice = 0f;
private bool roundFinished = false;

接下来,添加第二个观察方法和我们的连续动作方法:

PatternMatchingEnvironment.cs
[RLMatrixObservation]
public float SeePattern() => pattern;
[RLMatrixObservation]
public float SeePattern2() => pattern2;
[RLMatrixActionContinuous]
public void MakeChoiceContinuous(float input)
{
aicontinuousChoice = input;
}

现在,让我们创建奖励函数:

PatternMatchingEnvironment.cs
[RLMatrixReward]
public float GiveReward() => aiChoice == pattern ? 1.0f : -1.0f;
// 当 AI 的连续输出接近第二个模式的平方根时,添加 +2 奖励
[RLMatrixReward]
public float ExtraRewards() => Math.Abs(aicontinuousChoice - Math.Sqrt(pattern2)) < 0.1f ? 2f : 0.0f;

最后,我们需要更新 StartNewRound 方法以生成两种模式:

PatternMatchingEnvironment.cs
[RLMatrixReset]
public void StartNewRound()
{
pattern = Random.Shared.Next(2);
pattern2 = Random.Shared.Next(10);
aiChoice = 0;
roundFinished = false;
}

注意我们为 pattern2 使用了 0-9 的范围,这给智能体提供了一个更有趣的挑战:预测不同的平方根值。

修复编译错误

当您尝试构建解决方案时,您会遇到一系列错误。这实际上很有帮助——RLMatrix 使用强类型来防止运行时错误,并引导您朝着连续动作的正确实现方向前进。

错误 1:环境类型不匹配

Argument 1: cannot convert from 'PatternMatchingExample.PatternMatchingEnvironment' to 'RLMatrix.IEnvironmentAsync<float[]>'

这是因为 RLMatrix 为连续和离散环境提供了不同的接口,以确保类型安全。让我们在 Program.cs 中更新代码:

Program.cs - Environment Type
var env = new List<IEnvironmentAsync<float[]>> {
var env = new List<IContinuousEnvironmentAsync<float[]>> {
environment,
//new PatternMatchingEnvironment().RLInit() //you can add more than one to train in parallel
};

错误 2:智能体类型不匹配

进行此更改后,我们会遇到第二个错误:

Argument 2: cannot convert from 'System.Collections.Generic.List<RLMatrix.IContinuousEnvironmentAsync<float[]>>' to 'System.Collections.Generic.IEnumerable<RLMatrix.IEnvironmentAsync<float[]>>'

这是因为我们试图将离散智能体与连续环境一起使用。我们需要更改智能体类型:

Program.cs - Agent Type
var agent = new LocalDiscreteRolloutAgent<float[]>(learningSetup, env);
var agent = new LocalContinuousRolloutAgent<float[]>(learningSetup, env);

错误 3:算法选项不匹配

这导致我们的第三个错误:

Argument 1: cannot convert from 'RLMatrix.DQNAgentOptions' to 'RLMatrix.PPOAgentOptions'

这个最终错误表明 DQN 与连续动作不兼容。我们需要切换到 PPO(近端策略优化),它可以处理离散和连续动作空间:

Program.cs - Algorithm Options
var learningSetup = new DQNAgentOptions(
batchSize: 32,
memorySize: 1000,
gamma: 0.99f,
epsStart: 1f,
epsEnd: 0.05f,
epsDecay: 150f
);
var learningSetup = new PPOAgentOptions(
batchSize: 128,
memorySize: 1000,
gamma: 0.99f,
width: 128,
lr: 1E-03f
);

我们的首次训练运行

现在让我们运行训练,看看会发生什么:

Training Output
Step 800/1000 - Last 50 steps accuracy: 42.0%
Press Enter to continue...
Step 850/1000 - Last 50 steps accuracy: 38.0%
Press Enter to continue...
Step 900/1000 - Last 50 steps accuracy: 40.0%
Press Enter to continue...
Step 950/1000 - Last 50 steps accuracy: 38.0%
Press Enter to continue...
Step 1000/1000 - Last 50 steps accuracy: 37.0%
Press Enter to continue...

惊讶吧!AI 几乎没有学到任何东西。准确率没有超过 50%,如果查看仪表板,我们会发现它定期获得离散动作(匹配模式)的 +1 奖励,但很少获得连续动作(预测 √pattern2)的 +2 奖励。

为什么会这样?

问问自己:为什么 AI 学习离散动作比连续动作容易得多?

您的第一直觉可能是学习率(lr)——也许太低了?让我们尝试将其更改为 1E-02f 并再次运行训练…

有帮助吗?可能没有。事实上,您可能会注意到,虽然智能体学习离散动作更快,但它几乎不探索连续动作空间,随着训练的进行,准确率甚至变得更糟。

那么,究竟发生了什么?

添加指导信号

让我们尝试通过提供更有帮助的奖励信号来解决这个问题。我们将添加一个随着智能体接近正确平方根而增加的奖励,而不是仅奖励精确匹配:

PatternMatchingEnvironment.cs
[RLMatrixReward]
public float ExtraSupportingReward() => 0.5f / (1 + Math.Abs(aicontinuousChoice - (float)Math.Sqrt(pattern2)));
//别忘了将 lr 改回 1E-03f!

这个奖励函数创建了一个梯度——随着智能体接近正确值,信号会变得更强。即使它不完全正确,它也会得到关于是变”热”还是变”冷”的反馈。

让我们再次运行训练,看看会发生什么:

Training Output
Step 850/1000 - Last 50 steps accuracy: 35.0%
Press Enter to continue...
Step 900/1000 - Last 50 steps accuracy: 40.0%
Press Enter to continue...
Step 950/1000 - Last 50 steps accuracy: 47.0%
Press Enter to continue...
Step 1000/1000 - Last 50 steps accuracy: 36.0%
Press Enter to continue...

我们看到一些小的改进,但仍然不太理想。仪表板可能显示学习正在进行的迹象,但显然,对于这个更复杂的任务,我们需要更多的训练时间。

延长训练时间

对于连续动作预测等更复杂的挑战,我们通常需要更多的训练步骤。让我们修改程序,训练 10,000 步而不是 1,000 步:

Program.cs
for (int i = 0; i < 10000; i++)
{
await agent.Step();
if ((i + 1) % 500 == 0)
{
Console.WriteLine($"Step {i + 1}/10000 - Last 500 steps accuracy: {environment.RecentAccuracy:F1}%");
environment.ResetStats();
Console.WriteLine("\nPress Enter to continue...");
Console.ReadLine();
}
}

实验:学习率的影响

在观察更长的训练进度时,尝试使用不同的学习率进行实验。如果进一步降低会发生什么?如果显著提高呢?

在我的实验中,设置非常高的学习率会导致模型陷入只收集离散动作的 +1 奖励,同时完全无法充分探索连续空间。

关键要点

通过这个练习,我们学到了几个重要的教训:

  1. 连续动作本质上比离散动作更难学习,这是由于稀疏奖励问题。如果可能的话,将您的动作空间离散化!

  2. 奖励工程对连续控制问题至关重要。提供关于”变热”的信号将一个不可能的学习任务转变为可处理的任务。

  3. 复杂任务需要更多训练时间。随着我们向动作空间添加维度,我们需要相应地扩展训练持续时间。

  4. 算法选择至关重要。DQN 根本无法处理连续动作,而 PPO 可以处理离散、连续或混合动作空间。

  5. 学习率调整是微妙的,特别是使用 PPO 时。更高并不总是更好,有时对探索甚至可能更糟。

这些原则在您应对更复杂的 RLMatrix 强化学习挑战时将为您提供很好的帮助。

测试您的理解

理解连续动作

下一步

现在您已经了解了连续动作空间的挑战以及如何应对这些挑战,您已准备好尝试一个具有更复杂观察的经典强化学习问题。