使用连续动作
让我们从之前的教程项目开始,为其添加连续动作。您可以使用初始项目跟随操作,或者如果您愿意,可以查看完整项目。
离散动作 vs. 连续动作
在之前的指南中,我们使用了离散动作 - 我们的智能体必须在有限选项集(0 或 1)之间选择以匹配模式。在实际场景中,我们可能会接收大量传感器数据和视觉输入来决定按下哪个按钮。
然而,在许多现实应用中,这并不总是可能的。对于控制以下类型的事物:
- 车辆的转向角度
- 机械臂的关节扭矩
- 发动机的功率水平
我们的智能体需要输出连续动作——精确的浮点值,而非分类选择。
向环境添加连续动作
让我们修改环境,同时包含离散和连续动作。我们将保留原始的模式匹配任务,但添加第二个模式,我们期望 AI 输出这个新值的平方根。
注意我们只改变了期望值——智能体需要仅通过奖励信号的引导,通过试错来弄清楚我们想要什么!
首先,在 PatternMatchingEnvironment.cs 中添加新字段以跟踪第二个模式和连续动作:
private int pattern = 0;private int pattern2 = 0;private int aiChoice = 0;private float aicontinuousChoice = 0f;private bool roundFinished = false;接下来,添加第二个观察方法和我们的连续动作方法:
[RLMatrixObservation]public float SeePattern() => pattern;
[RLMatrixObservation]public float SeePattern2() => pattern2;
[RLMatrixActionContinuous]public void MakeChoiceContinuous(float input){ aicontinuousChoice = input;}现在,让我们创建奖励函数:
[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 方法以生成两种模式:
[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 中更新代码:
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[]>>'这是因为我们试图将离散智能体与连续环境一起使用。我们需要更改智能体类型:
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(近端策略优化),它可以处理离散和连续动作空间:
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);我们的首次训练运行
现在让我们运行训练,看看会发生什么:
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 并再次运行训练…
有帮助吗?可能没有。事实上,您可能会注意到,虽然智能体学习离散动作更快,但它几乎不探索连续动作空间,随着训练的进行,准确率甚至变得更糟。
那么,究竟发生了什么?
添加指导信号
让我们尝试通过提供更有帮助的奖励信号来解决这个问题。我们将添加一个随着智能体接近正确平方根而增加的奖励,而不是仅奖励精确匹配:
[RLMatrixReward]public float ExtraSupportingReward() => 0.5f / (1 + Math.Abs(aicontinuousChoice - (float)Math.Sqrt(pattern2)));
//别忘了将 lr 改回 1E-03f!这个奖励函数创建了一个梯度——随着智能体接近正确值,信号会变得更强。即使它不完全正确,它也会得到关于是变”热”还是变”冷”的反馈。
让我们再次运行训练,看看会发生什么:
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 步:
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 奖励,同时完全无法充分探索连续空间。
关键要点
通过这个练习,我们学到了几个重要的教训:
-
连续动作本质上比离散动作更难学习,这是由于稀疏奖励问题。如果可能的话,将您的动作空间离散化!
-
奖励工程对连续控制问题至关重要。提供关于”变热”的信号将一个不可能的学习任务转变为可处理的任务。
-
复杂任务需要更多训练时间。随着我们向动作空间添加维度,我们需要相应地扩展训练持续时间。
-
算法选择至关重要。DQN 根本无法处理连续动作,而 PPO 可以处理离散、连续或混合动作空间。
-
学习率调整是微妙的,特别是使用 PPO 时。更高并不总是更好,有时对探索甚至可能更糟。
这些原则在您应对更复杂的 RLMatrix 强化学习挑战时将为您提供很好的帮助。
测试您的理解
理解连续动作
下一步
现在您已经了解了连续动作空间的挑战以及如何应对这些挑战,您已准备好尝试一个具有更复杂观察的经典强化学习问题。