概述提高 Q-函数学习稳定性的基本要素之一是利用经验回放缓冲区。增加缓冲区可以收集更多样化的环境交互样本。这令赫兹量化交易软件的模型能够更好地研究和重现环境的 Q-函数。该技术广泛用于各种强化学习算法,包括扮演者-评论者系列算法。但硬币也有另一面。在学习过程中,扮演者的动作与存储在经验回放缓冲区中的样本差异增加。更新模型参数的迭代次数越多,这种差异就越大。这会导致训练扮演者政策的效率下降。2021 年 10 月,文章《依据乐观情绪探索政策外强化学习及分布校正》中提出了一种可能的解决方案。该方法的作者建议将分布校正估算(DICE)方法适配到软性扮演者-评论者算法。同时,方法作者还注意到了另一个细微差别。在训练政策时,软性扮演者-评论者方法使用了最小动作评估。这种方式的实际使用表明了一种悲观的倾向,即对环境的研究不足,以及动作的定向同质化。为了将这种影响最小化,本文的作者提议额外训练一个扮演者乐观情绪研究模型。这反过来又进一步增加了扮演者乐观情绪模型与环境之间的交互样本,以及已训练目标模型的动作分布之间的差距。然而,结合使用分布校正估算和扮演者乐观情绪模型的研究,可以提升目标模型的训练结果。
7 m5 l2 f0 F) C4 G/ Y图片上传失败重试
& u6 x3 R3 V6 W) m; S1. 乐观情绪研究有关乐观情绪的环境研究的第一个思路是在《与乐观情绪扮演者-评评论者一起进行更好的探索》一文(2019 年 10 月)中讲述的。其作者注意到,扮演者的贪婪式更新与评论者的悲观评估相结合,导致规避了代理者不知道的动作。这种现象被称为“悲观的探索不足”。此外,大多数算法都没有被告知研究方向。随机抽取的动作同样可能位于当前平均值的反侧,赫兹量化交易软件通常需要在确定区域的动作,远超其它区域。为了校正这些现象,提出了乐观情绪扮演者-评论者(OAC)算法,其近似状态-动作值函数的置信度下限和上限。这令乐观情绪理论可用于按上限进行不确定性定向研究。同时,下限有助于避免高估动作。方法作者提取并发展了乐观情绪扮演者-评论者的思路。与软性扮演者-评价者一样,我们将训练 2 个评论者模型。但同时,我们还将训练 2 个扮演者模型:πе 和目标 πт 研究。πе 政策是为了学习 QUB Q-函数值的最大近似上限。同时, πт 是训练期间 QLB Q-函数的最大近似下限。OAC 表明,与软性扮演者-评论者相比,涉及 πе 的研究可以更有效地使用抽样。为了获得 QUB Q-函数的近似上限,首先计算两个评论者的评分均值和方差:
C) V( B" E- s: S0 B, v图片上传失败重试
( x( b! t" A# V7 L0 H3 ~图片上传失败重试
7 l/ Y2 D- ?+ s z% F' h$ C5 ?接下来,我们定义 QUB 的方程:1 F% |7 B; G; k2 p% p( }
图片上传失败重试) K u" L9 U& g5 G
其中 βUB ∈ R,并管制乐观情绪水平。注意,QLB Q-函数的先前近似下限可以表示为6 Y3 a% q- y: I2 h0 ^: S5 Y0 h
图片上传失败重试, U0 q9 _8 u/ z% `4 }8 k
在悲观水平 βLB = 1 QLB 等于评论者的最低评分值。乐观情绪扮演者-评论者在 πе 和 πт 之间应用了一个最大 KL 离散度约束,这令赫兹量化交易软件能够获得 πе的接近解,及稳定的训练。同时,这限制了πе 执行更翔实动作的潜力,故可校正评论者的错误评分。这种限制不允许 πе 产生差距太大的动作,这是比之基于评论者的最低评分,按 πт 政策进行保守训练产生的动作。在 SAC+DICE 算法中,增加了分布校正,剔除了使用 KL 约束,从而解锁了乐观情绪政策的所有探索可能性。在这种情况下,在训练政策时显式校正有偏差的梯度估值,可维持训练的稳定性。而正在训练的扮演者行为政策 πт 是防止高估 Q-函数,使用 QLB 的近似下限作为评论者,如同软性扮演者-评论者方法。不过,已加上用 dπт(s,a)/dD(s,a) 比率对抽样分布进行调整。我们得到以下训练目标:! u- f: i# P D( N
图片上传失败重试
9 S: z4 E0 U9 L$ k# X$ s4 s其中 dπт(s,a) 表示当前政策的状态-动作分布,而 dD(s,a) 定义来自经验回放缓冲区的状态-动作分布。 这种训练目标的梯度提供了政策梯度的无背离估值,这与以前的扮演者-评论者学习算法不同,后者在训练目标政策时使用了背离的估值。πе 研究政策应从乐观情绪相对于 Q-函数估值的偏差入手,以便获得有效校正虚假估值的经验。因此,该方法的作者建议使用类似于乐观情绪扮演者-评论者(QUB)的近似上限作为目标函数中的评论者。πе 政策和更好的 Q-函数估值最终目标是促进更准确地估算 πт 目标政策的梯度。因此,πе 损失函数的抽样分布应与 πт 行为政策一致。有因于此,方法作者提议使用与扮演者目标政策的损失函数相同的校正系数。
6 D2 S1 c% ~- K1 ~/ E图片上传失败重试3 P5 D2 K2 X7 z" c G( L
至于评论者,则保留了之前讨论过的软性扮演者-评论者方法。目标模型的 Q-函数的下限用于训练它们。然而,有众多研究都证明了采用相同的样本来训练扮演者和评论者的效率。因此,在评论者损失函数中还添加了分布校正因子。7 V7 a2 L0 u3 n: Y
图片上传失败重试- f0 i. o( u( Q" z( z- o J8 d
正如您所见,从上述所有内容中分布校正系数引发了最多的问题。我们来详细研究一下。& C: _. V1 t1 `2 ]; e$ f$ @
2. 分布校正分布校正估算(DICE)算法家族旨在解决政策外估算(OPE)校正问题。这些方法允许我们训练政策值的估算器,即基于 D 静态重试缓冲区的单步常规化预期奖励。DICE 接收一个无背离估算器,用来估算分布校正系数。
9 v% O4 E6 O2 A! ~1 y6 C4 g图片上传失败重试
$ c! y3 m4 ~% S. h# i5 F$ U为了估算分布校正系数,方法作者采用了 DICE 优化结构,该结构可以表述为具有各种正则化的最小/最大线性分布程序。直接把 DICE 算法应用于政策外强化学习设置会带来重大的优化挑战。免估算训练假定固定目标政策,及具有足够状态-动作空间覆盖的静态回放缓冲区,而在 RL 中,目标政策和经验回放缓冲区在训练期间会发生变化。因此,SAC+DICE 方法的作者进行了一些修改,以便克服这些难点。赫兹量化交易软件现在不会扎进数学领域,也不会详述这些修改。您可以在原文中找到它们。我仅介绍由拟议的修改而获得的损失函数。
. c+ Y# P' s( H$ V图片上传失败重试
0 B, }; B3 l" v6 Q/ n, G4 c图片上传失败重试' t+ W" ~( C& G" U; @1 w ]
图片上传失败重试/ C1 ]7 I4 W' n6 M
此处 ζ(s,a) 和 v(s,a) 是神经网络模型,而 λ 是可调的拉格朗日(Lagrange)系数。ζ(s,a) 近似于分布校正因子。v(s,a) 是一类评论者。为了稳定训练,赫兹量化交易软件将用到 v 目标模型,并对其参数进行软更新,类似于评论者。为了优化所有参数,作者提议使用 Adam 方法。以上所有内容都普及到单个 SAC+DICE 算法之中。与传统的政策外强化学习算法一样,我们遵循 πе 乐观情绪探索政策,按顺序执行与环境的交互,并将数据保存到经验回放缓冲区。在每个训练步骤中,所研究的算法首先更新模型,并依据上述损失函数使用 SGD 更新 DICE 参数 (v, ζ, λ)。然后,我们据更新的模型计算 ζ 分布的校正比率。然后,使用 ζ,我们训练 RL 来更新 πт, πе,Q1 和 Q2。在每个训练步骤结束时,Q1, Q2 和 v 目标模型都会软性更新。
7 L0 D3 L# r4 g; ^( \7 ^3. 以 MQL5 实现在阅读理论部分时,您也许已经注意到已训练模型和参数的数量是如何急剧增长的。事实上,已训练模型数量已从 3 个增长到 6 个。它们的互动变得更加复杂。同时,我们期望收到一个扮演者行为政策模型。为了向用户隐藏所有例行工作,我们将稍微改变我们的方式,将整个训练包装在一个单独的类 CNet_SAC_DICE 之中。我们的新类将是 CNet 神经网络模型基类的继任者。在类主体中,我们将声明 5 个可训练模型,和 3 个目标模型。此处,我们还将声明一些内部变量。我们将在实现过程中看到它们的功能。class CNet_SAC_DICE : protected CNet {protected: CNet cActorExploer; CNet cCritic1; CNet cCritic2; CNet cTargetCritic1; CNet cTargetCritic2; CNet cZeta; CNet cNu; CNet cTargetNu; float fLambda; float fLambda_m; float fLambda_v; int iLatentLayer; //--- float fLoss1; float fLoss2; float fZeta; //--- vector<float> GetLogProbability(CBufferFloat *Actions);public: //--- CNet_SAC_DICE(void); ~CNet_SAC_DICE(void) {} //--- bool Create(CArrayObj *actor, CArrayObj *critic, CArrayObj *zeta, CArrayObj *nu, int latent_layer = -1); //--- virtual bool Study(CArrayFloat *State, CArrayFloat *SecondInput, CBufferFloat *Actions, vector<float> &ActionsLogProbab, CBufferFloat *NextState, CBufferFloat *NextSecondInput, float reward, float discount, float tau); virtual void GetLoss(float &loss1, float &loss2) { loss1 = fLoss1; loss2 = fLoss2; } //--- virtual bool Save(string file_name, bool common = true); bool Load(string file_name, bool common = true); };请注意,我们最初提到了 6 个可训练模型,但仅声明了 5 个。在公布的模型中,没有扮演者的目标政策。然而,整个训练的目标恰恰是为了获得它。如前所述,我们的新类是基准神经网络类的继任者。这意味着它本身就是一个学习模型。因此,将使用父类来训练基准的扮演者政策。此外,正在创建的新 CNet_SAC_DICE 类仅用于模型训练。在操作期间,创建其它模型的对象是没有意义的,且是不必要的资源消耗。因此,我们计划在操作期间使用基准模型对象。出于上述原因,新类没有向前或向后验算方法。所有功能都将在 Study 方法中实现。当然,有一些方法可以进行“保存”和“加载”文件操控。但首事首做。在类构造函数中,我们用初始值初始化内部变量。所有内部对象都声明为静态的,且不参与初始化。相应地,我们不需要在析构函数中清理内存,这样就允许我们将析构函数留空。CNet_SAC_DICE::CNet_SAC_DICE(void) : fLambda(1.0e-5f), fLambda_m(0), fLambda_v(0), fLoss1(0), fLoss2(0), fZeta(0) { }模型的完全初始化是在 Create 方法中执行的。在方法参数中,我们将传递所有用到的模型架构描述的动态数组,和扮演者隐含层的 ID,以及环境分析状态的压缩表示。在方法主体中,我们将首先创建扮演者模型。乐观情绪模型是在 cActorExploer 对象中创建的。目标模型是使用已继承的工具在我们的类主文中创建的。bool CNet_SAC_DICE::Create(CArrayObj *actor, CArrayObj *critic, CArrayObj *zeta, CArrayObj *nu, int latent_layer) { ResetLastError();//--- if(!cActorExploer.Create(actor) || !CNet::Create(actor)) { PrintFormat("Error of create Actor: %d", GetLastError()); return false; }//--- if(!opencl) { Print("Don't opened OpenCL context"); return false; }我们立即检查创建的 OpenCL 关联环境指针。接着,我们为两个评论者创建可训练模型。 if(!cCritic1.Create(critic) || !cCritic2.Create(critic)) { PrintFormat("Error of create Critic: %d", GetLastError()); return false; }它们后随的模块是 DICE 对象和目标模型。 if(!cZeta.Create(zeta) || !cNu.Create(nu)) { PrintFormat("Error of create function nets: %d", GetLastError()); return false; }//--- if(!cTargetCritic1.Create(critic) || !cTargetCritic2.Create(critic) || !cTargetNu.Create(nu)) { PrintFormat("Error of create target models: %d", GetLastError()); return false; }成功创建所有模型后,我们会将它们传递到单个 OpenCL 关联环境。 cActorExploer.SetOpenCL(opencl); cCritic1.SetOpenCL(opencl); cCritic2.SetOpenCL(opencl); cZeta.SetOpenCL(opencl); cNu.SetOpenCL(opencl); cTargetCritic1.SetOpenCL(opencl); cTargetCritic2.SetOpenCL(opencl); cTargetNu.SetOpenCL(opencl);并将模型参数复制到它们的目标副本。此外,我们不应忘记在每一步控制操作的执行。 if(!cTargetCritic1.WeightsUpdate(GetPointer(cCritic1), 1.0) || !cTargetCritic2.WeightsUpdate(GetPointer(cCritic2), 1.0) || !cTargetNu.WeightsUpdate(GetPointer(cNu), 1.0)) { PrintFormat("Error of update target models: %d", GetLastError()); return false; }成功创建所有必要的对象后,我们将数据传输到内部变量,并终止该方法。 fLambda = 1.0e-5f; fLambda_m = 0; fLambda_v = 0; fZeta = 0; iLatentLayer = latent_layer;//--- return true; }类的内部对象初始化之后,我们转入 CNet_SAC_DICE::Study 模型训练方法的工作。在该类的参数中,我们收到训练模型的一个步骤所需的所有信息。以下是环境的当前和未来状态。在本例中,每个状态在两个数据缓冲区中定义:历史数据和平衡状态。在此,您还将看到动作缓冲区和奖励变量。还有折扣率和目标模型的软更新变量。对于首次,我们添加了原始政策概率的对数向量(用于收集样本)。 |