量化交易软件:神经网络变得轻松

本文详细解析了软性扮演者-评价者算法,一种基于SAC的强化学习方法,强调了随机扮演者策略在探索和应对环境不确定性的重要性。通过熵正则化和MQL5实现,文章介绍了算法的训练过程,包括初始化、模型更新和目标模型的使用策略。
摘要由CSDN通过智能技术生成

1. 软性扮演者-评价者算法

在研究 SAC 算法时,赫兹量化软件应该能立即注意到它不是 TD3 方法的直系后代(反之亦然)。但它们有一些相似之处。特别是:

  • 它们都是异政策算法

  • 它们都利用 DDPG 方法

  • 它们都用到 2 个评论者。

添加图片注释,不超过 140 字(可选)

但不像之前讨论的两种方法,SAC 使用随机扮演者政策。这允许算法探索不同的策略,并找到最优解,同时考虑到扮演者动作的最大可变性。

说到环境的随机性,赫兹量化软件明白在 S 状态下,当执行 A 动作时,我们在 [Rmin, Rmax] 内获得 R 奖励,概率为 Psa。

软性扮演者-评价者用到的扮演者具有随机政策。这意味着处于 S 状态的扮演者能够以一定的 Pa' 概率从整个动作空间中选择 A' 动作。换言之,在每个特定状态下,扮演者的政策允许赫兹量化软件不一定选择特定的最优动作,而是任何可能的行动(但具有一定程度的概率)。在训练过程中,扮演者学习获得最大奖励的概率分布。

随机扮演者政策的这一属性令我们能够探索不同的策略,并发现在运用判定性策略时可能隐藏的最优解。此外,随机扮演者政策还考虑到环境中的不确定性。在出现噪声或随机因素的情况下,该种类政策可能更具弹性和适应性,因为它们可以生成各种动作,以便有效地与环境交互。

不过,训练扮演者的随机政策也会对训练进行调整。经典强化学习旨在最大化预期回报。在训练过程中,对于每个 S 动作,赫兹量化软件选择 A* 动作,其最有可能给我们带来更大的盈利能力。这种判定性方式建立了明确的关系 St → At → St+1 ⇒ R,并且没有给随机动作留下余地。为了训练随机政略,软性扮演者-评论者算法的作者在奖励函数当中引入了熵正则化。

在这种情况下,entropy (H) 是衡量政策不确定性或多样性的指标。ɑ>0 参数是一个温度系数,令我赫兹量化软件们能够在所研究环境和动作模型之间取得平衡。

如您所知,熵是随机变量不确定性的度量,且由方程判定

注意,我们谈论的是 [0, 1] 范围内选择动作的概率的对数。在这个可接受值的间隔内,熵函数的图形正在降低,且位于正值区域。因此,选择动作的概率越低,奖励就越高,并且鼓励模型探索环境。

如您所见,有关于此,对 ɑ 超参数的选择提出了相当高的要求。目前,有多种选项可用于实现 SAC 算法。传统的固定参数方法就是其中之一。往往是,我们可以找到参数逐渐减少的实现。很容易就能看出,当 ɑ=0 时,我们得出了判定性强化学习。此外,在训练过程中,模型本身还有多种方式可以优化 ɑ 参数。

我们转入训练评论者。与 TD3 类似,SAC 并行训练 2 个评论者模型,并采用 MSE 作为损失函数。对于未来状态的预测值,从两个评论者目标模型中取较小值。但这里有两个关键区别。

第一个是上面讨论的奖励函数。赫兹量化软件对于当前和后续状态都使用熵正则化,并针对系统下一个状态的成本,参考应用一个折扣因子。

第二个区别是扮演者。SAC 不使用目标扮演者模型。若要选择当前和后续状态下的动作,会用到一个经过训练的扮演者模型。因此,我们强调,实现未来回报是利用现行政策来达成的。此外,使用单个扮演者模型可降低内存和计算资源的成本。

为了训练扮演者政策,赫兹量化软件采用 DDPG 方式。我们通过反向传播贯穿评论者模型的预测行动成本的误差梯度,来获得动作误差梯度。但不像 TD3(其中我们只用了 Critic 1 模型),SAC 的作者建议取用估算行动成本较低的模型。

此处还有一件事。在训练期间,我们更改政策,这会导致在系统特定状态下扮演者的行为发生变化。此外,随机扮演者政策的使用也有助于扮演者动作的多样性。同时,我们依据来自经验回放缓冲区的数据来训练模型,并为其它代理者动作提供奖励。在这种情况下,我们以理论假设为指导,即在训练扮演者的过程中,我们朝着最大化预测奖励的方向前进。这意味着,在任何 S 状态下,采用 π 新政策的动作成本都不低于 π 旧政策的动作成本。

这是一个非常主观的假设,但它与我们的模型训练范式完全一致。为了不累积可能的误差,我建议在训练期间更频繁地更新经验回放缓冲区,同时考虑更新扮演者政策。

使用类似于 TD3 的 τ 因子平滑目标模型的更新。

这与 TD3 方法还有一点区别。软性扮演者-评论者算法在扮演者训练和更新目标模型时不能使用延迟。在此,所有模型都会在每个训练步骤中更新。

我们总结一下软性扮演者-评论者算法:

  • 在奖励函数中引入熵正则化。

  • 在训练开始时,扮演者和 2 个评论者模型以随机参数进行初始化。

  • 作为与环境交互的结果,将填充经验回放缓冲区。我们保持环境状态、动作、后续状态和奖励不变。

  • 填满经验回放缓冲区后,我们训练模型

  • 我们从经验回放缓冲区中随机提取一组数据

  • 判定未来状态的动作,同时考虑扮演者的当前政策

  • 使用至少 2 个目标评论者模型的当前政策来判定未来状态的预测值

  • 更新评论者模型

  • 更新动作政策

  • 更新目标模型。

训练模型的过程是迭代的,并重复进行,直至得到所需的结果,或达到评论者损失函数图形上的最小极值。

2. 利用 MQL5 实现

在软性扮演者-评论者算法理论概述之后,我们转入利用 MQL5 实现它。我们面临的第一件事是检测特定动作的概率。实际上,对于扮演者政策的表格化实现来说,这是一个非常简单的问题。但在使用神经网络时这会造成困难。毕竟,我们不保留有关环境条件和所采取动作的统计数据。它被“硬连线”到我们模型的可定制参数之中。有关于此,我想起了分布式 Q-训练。您也许还记得,我们谈到过有关预期回报概率分布的研究。分布式 Q-学习允许我们获得给定数量的固定区间奖励值的概率分布。完全参数化的 Q-函数(FQF)模型允许我们研究区间值及其概率。

2.1创建一个新的神经层类

继承自 CNeuronFQF 类,我们将创建一个新的神经层类来实现所提出的 CNeuronSoftActorCritic 算法。新类的方法集非常标准,但它也有自己的特性。

特别是,在我们的实现中,我们决定使用自定义熵正则化参数。为此目的,添加了 cAlphas 神经层。该实现使用 CNeuronConcatenate 类型的层。为了决定比率的大小,我们将用到当前状态的嵌入,以及在输出中用到分位数分布。

此外,我们还添加了一个单独的缓冲区来记录熵值,稍后我们将在奖励函数中用到它。

添加的两个对象都声明为静态,这允许我们将类构造函数和析构函数留空。

 
 

class CNeuronSoftActorCritic : public CNeuronFQF { protected: CNeuronConcatenate cAlphas; CBufferFloat cLogProbs; virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronSoftActorCritic(void) {}; ~CNeuronSoftActorCritic(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint actions, uint quantiles, uint numInputs, ENUM_OPTIMIZATION optimization_type, uint batch); virtual bool calcAlphaGradients(CNeuronBaseOCL *NeuronOCL); virtual bool GetAlphaLogProbs(vector<float> &log_probs) { return (cLogProbs.GetData(log_probs) > 0); } virtual bool CalcLogProbs(CBufferFloat *buffer); //--- virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual int Type(void) override const { return defNeuronSoftActorCritic; } virtual void SetOpenCL(COpenCLMy *obj); };

首先,我们将查看类的 Init 初始化方法。方法参数完全重复父类相似方法的参数。我们立即在方法主体中调用父类方法。我们经常使用这种技术,因为所有必要的控制都已在父类中实现了。所有继承对象的初始化也一并执行。一次检查父类方法的结果取代了所提及操作的完全控制。我们所要做的就是初始化添加的对象。

首先,我们初始化 ɑ 比率计算层。如上所述,我们将向该模型的输入提交当前状态的嵌入,其大小将等于前一个神经层的大小。此外,我们将在当前层的输出中添加一个分位数分布,该分位数分布将包含在内部层 cQuantile2 当中(其在父类中声明和初始化)。在 cAlphas 层的输出端,我们将获得每个单独动作的温度系数。相应地,层的大小将等于动作的数量。

系数应为非负数。为了满足这个需求,我们将该层的激活函数定义为 Sigmoid。

在方法结束时,我们用零值初始化熵缓冲区。它的大小也等于动作的数量。马上在当前 OpenCL 关联环境中创建缓冲区。

 
 

bool CNeuronSoftActorCritic::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint actions, uint quantiles, uint numInputs, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronFQF::Init(numOutputs, myIndex, open_cl, actions, quantiles, numInputs, optimization_type, batch)) return false; //--- if(!cAlphas.Init(0, 0, OpenCL, actions, numInputs, cQuantile2.Neurons(), optimization_type, batch)) return false; cAlphas.SetActivationFunction(SIGMOID); //--- if(!cLogProbs.BufferInit(actions, 0) || !cLogProbs.BufferCreate(OpenCL)) return false; //--- return true; }

接下来,我们转入实现前向验算。在此,我们从父类借用训练分位数和概率分布的过程,不做任何修改。但我们必须加入一些过程,安排检测温度系数,以及计算熵值。甚至,虽然温度的计算涉及调用经由 cAlphas 层的直接验算,但检测熵值应从 “0” 开始实现。

我们必须计算扮演者每个动作的熵。在这个阶段,我们期望此处的动作不会太多。由于所有源数据都在 OpenCL 关联环境存储器当中,故逻辑上应将我们的操作转移到该环境。首先,我们将创建 OpenCL 内核程序 SAC_AlphaLogProbs,来实现此功能。

在内核参数中,我们将传递 5 个数据缓冲区和 2 个常量:

  • outputs — 结果缓冲区包含每个动作的分位数值的概率加权总和

  • quantiles — 平均分位数值(cQuantile2 内层结果缓冲区)

  • probs — 概率张量(cSoftMax 内层结果缓冲区)

  • alphas — 温度系数的向量

  • log_probs — 熵值的向量(在本例中,记录结果的缓冲区)

  • count_quants — 每个动作的分位数

  • activation — 激活函数类型。

CNeuronFQF 类在输出端不使用激活函数。我甚至要说这与类背后的观点相矛盾。毕竟,在模型训练过程中,预期奖励的分位数平均值的分布是由实际奖励本身来界定的。在我们的例子中,在自层输出端,我们期望从连续分布取得扮演者动作的确定值。由于各种技术或其它境况,代理者允许的动作界域也许会受到限制。激活函数允许我们这样做。但对于我们,在判定实际动作的概率后,获得所应用激活函数的实际概率估算非常重要。因此,我们将其实现添加到此内核之中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值