量化软件——赫兹MT5神经网络变得简单(第 58 部分)决策转换器DT

序列基于马尔可夫(Markov)过程的原理。假设起点是环境的当前状态。摆脱这种状态只有一种最优方法,它不依赖以前的路径。

我想讲述另一种替代方式,它是由谷歌团队在文章《决策转换器:通过序列建模进行强化学习》(2021 年 6 月 2 日)中提出。这项工作的主要亮点是将强化学习问题投影到条件化动作序列的建模,条件化则依据所需奖励的自回归模型。


1. 决策转换器方法特点
决策转换器是一种架构,它改变了我们看待强化学习的方式。与选择智能体动作的经典方法对比,决策制定序列问题的研究是在语言建模的框架内。

该方法的作者建议依据先前执行的动作和访问状态的上下文构建智能体的动作轨迹,就像语言模型依据普通文本的上下文构建句子(单词序列)一样。以这种方式处置问题,允许使用各种语言模型工具,只需进行最少的修改,包括 GPT(生成式预训练转换器)。

可能值得从构造智能体轨迹的原则开始。在这种情况下,我们正在专门谈论构建轨迹,而不是一系列动作。

选择轨迹表示时的需求之一是使用转换器的能力,这将允许人们在源数据中提取重要形态。除了对环境条件的描述外,还有就是智能体执行的动作和奖励。方法作者在此提供了一种相当有趣的方式来建模奖励。我们希望模型基于未来期望的奖励来生成动作,而非过去的奖励。毕竟,我们的愿望是达成一些目标。作者没有直接提供奖励,取而代之提供了一个“在途回报(Return-To-Go)”量级模型。这类似于直至剧集结束时的累积奖励。不过,我们表示的是期望的结果,而非实际的结果。

在测试已训练模型时,我们可以指定所需的奖励(例如,1 代表成功,或 0 代表失败),以及环境的初始状态,作为触发生成的信息。执行为当前状态生成的动作之后,我们依据环境中接收到的数额降低目标奖励,并重复该过程,直到获得所需的总奖励,或世代完成。

请注意,如果您采用这种方式,并在达到所需的总奖励水平后继续,则也许会将负值传递给“在途回报”。这也许会导致损失。

为了让智能体制定决策,我们将最后 K 时间步骤作为源数据传递给决策转换器。总共有 3*K 个令牌。每种模态一个:在途回报、状态、和导致该状态的动作。为了获得令牌的向量表示,该方法的作者对每种模态都使用经过训练且完全连接神经层,其会将源数据投射到向量表示的维度之中。之后对图层进行常规化。在分析复杂(复合)环境状态的情况下,可以使用卷积编码器,替代完全连接神经层。

此外,对于每个时间步骤,都会训练时间戳的向量表示,并将其添加到每个令牌之中。这种方法与转换器中的标准定位向量表示不同,因为一个时间步骤对应若干个令牌(在给定的示例中,有三个令牌)。然后使用 GPT 模型处理令牌,其用自回归建模预测未来的动作令牌。在研究监督训练方法时,我们在《A take on GPT》一文中更多地讨论了 GPT 模型的架构。

也许看似很奇怪,但模型训练过程是使用监督学习方法构建的。首先,我们安排与环境的交互,并对一组随机轨迹进行采样。我们已经多次如此做了。之后运作离线训练。我们从收集的轨迹集中选择 K 长度的迷你包。对应于 st 输入令牌的预测头学习预测 at 动作 — 即可对离散动作使用交叉熵损失函数,亦可对连续动作使用均方误差。每个时间步骤的损失求均值。

然而,在实验期间,该方法的作者并未发现预测后续状态或奖励可以提高模型的效率。

2. 利用 MQL5 实现
在简要介绍了决策转换器方法的理论层面之后,我们转入利用 MQL5 实现它。我们要面对的第一件事是实现源数据实体的嵌入问题。在解决监督学习方法中的类似问题时,我们曾用过卷积层,其步长等于原始数据的窗口。但在这种情况下,有两个难题在等着我们:

环境状态描述向量的大小与动作空间向量不同。奖励向量具有第三个大小。
所有实体都包含来自不同分布渠道的源数据。需要不同的嵌入矩阵才能在单个空间中将它们转换为可比较的形式。
我们将环境状态分为两个内容和大小完全不同的区块:价格走势的历史数据,和账户当前状态的描述。这增加了另一种模态供分析。在新的实验中,也许会出现可供分析的额外数据。显然,以这种条件,我们不能使用卷积层,我们需要另一个通用解决方案,能够在大小为 [n1, n2, n3,...,nN] 的向量里嵌入 N 个模态。如上所述,方法作者对每种模态都使用了经过训练的完全连接层。这种方式非常普遍,但在我们的案例中,它意味要放弃若干种模态的并行处理。

在这种情况下,在我看来,最优的解决方案是以神经嵌入层 CNeuronEmbeddingOCL 的形式创建一个新对象。这是允许我们正确构建流程的唯一方法。不过,在创建新类的对象和功能之前,我们仍然需要决定其架构的一些特性。

在前向验算的每次迭代中,我们计划传输五个源数据向量:

__kernel void Embedding(__global float *inputs,
                        __global float *outputs,
                        __global float *weights,
                        __global int   *windows,
                        __global float *std,
                        const int stack_size
                       )
  {
   const int window_out = get_global_size(0);
   const int pos = get_local_id(0);
   const int emb = get_global_id(1);
   const int emb_total = get_global_size(1);
   const int shift_out = emb * window_out + pos;
   const int step = emb_total * window_out;
   const uint ls = min((uint)get_local_size(0), (uint)LOCAL_ARRAY_SIZE);
在内核主体中,我们辨别两个维度的流,并在数据缓冲区中定义偏移常量。然后,我们在结果缓冲区中顺移先前获得的嵌入。请注意,每个线程中只传输一个嵌入位置。这允许在并行线程中安排数据复制。

   for(int i=stack_size-1;i>0;i--)
      outputs[i*step+shift_out]=outputs[(i-1)*step+shift_out];
下一步是检测正在分析的模态于源数据缓冲区中的偏移量。为此,我们数一数所分析数据之前源数据缓冲区中的模态元素总数。

   int shift_in = 0;
   for(int i = 0; i < emb; i++)
      shift_in += windows[i];
此处,我们判定权重矩阵缓冲区中的偏移量,同时考虑贝叶斯元素。

   const int shift_weights = (shift_in + emb) * window_out;
我们将当前模态的源数据窗口的大小保存到局部变量之中,并为操控局部数组定义常量。

   const int window_in = windows[emb];
   const int local_pos = (pos >= ls ? pos % (ls - 1) : pos);
   const int local_orders = (window_out + ls - 1) / ls;
   const int local_order = pos / ls;
创建一个局部数组,并用零值填充它。此处,我们将为局部线程同步设置一个栅栏。

   __local float temp[LOCAL_ARRAY_SIZE];
   if(local_order == 0)
      temp[local_pos] = 0;
   barrier(CLK_LOCAL_MEM_FENCE);
至此,可以认为准备工作已经完毕,我们直接处置嵌入操作。首先,我们将所分析模态的输入数据向量乘以相应的权重比向量。以这种方式,我们就可以得到我们需要的嵌入元素。

   float value = weights[shift_weights + window_in];
   for(int i = 0; i < window_in; i++)
      value += inputs[shift_in + i] * weights[shift_weights + i];
在这种情况下,我们不使用激活函数,因为我们需要在所需的子空间中获取序列的每个元素的投影。不过,我们知道这种方式并不能保证不同源数据嵌入的可比性。因此,下一步是在单一模态的嵌入中对数据进行规范化。因此,我们将所有嵌入的数据降至零到单位方差间的均值。我来提醒您常规化方程。

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值