神经网络变得简单(第 71 部分):目标条件预测编码(GCPC)·进阶篇
🧠

神经网络变得简单(第 71 部分):目标条件预测编码(GCPC)·进阶篇

(2/3)·行为克隆只管模仿,轨迹怎么压缩才不丢信息?这篇拆开 GCPC 的预训练与政策学习双阶段

含代码示例偏理论 第 2/3 篇

不少团队直接把离线轨迹喂给决策转换器,却从没追问过:那些次优轨迹里哪些片段真值得被编码。忽略轨迹表示学习目标与政策学习目标的差异,模型很容易在长周期任务里失焦。

◍ 编码器与解码器的层栈拼装

这段逻辑在 CNetDescription 的构建函数里,负责把编码器(encoder)和解码器(decoder)的每一层描述对象 CLayerDescription 逐个 new 出来并挂到容器上。任何一层 Add 失败就 delete 描述符并返回 false,只有全部通过才在末尾 return true。 编码器从第 11 层开始:第 11 层是 MLMHAttentionOCL 类型,节点数取上一层的两倍(prev_count * 2),滑动步长 step=4、输出窗口 window_out=16、子层 layers=4,优化器用 ADAM。第 12 层退回到 BaseOCL,count 直接等于 EmbeddingSize,激活函数为 None。第 13 层是 SoftMaxOCL,count 沿用 prev_count,step=1,同样无激活。 解码器先 Clear 再搭:输入层是 BaseOCL,count=EmbeddingSize;layer 1 把 (HistoryBars + PrecoderBars) * EmbeddingSize 个节点压成一层,激活用 LReLU。layer 2 又是 MLMHAttentionOCL,把节点数除回 EmbeddingSize,window=EmbeddingSize、step=4、window_out=16、layers=2。layer 3 换成 MultiModels,count=3、window=prev_wout、step=prev_count、无激活。 在 MT5 里跑这套网络描述时,若你改了 EmbeddingSize 或 HistoryBars,解码器 layer 1 的节点数会线性放大,可能明显拖慢 OpenCL 前向计算。建议先打印各层 descr.count 确认维度链没断。

MQL5 / C++
  }
class=class="str">"cmt">//--- layer class="num">11
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronMLMHAttentionOCL;
   descr.count = prev_count * class="num">2;
   descr.window = prev_wout;
   descr.step = class="num">4;
   descr.window_out = class="num">16;
   descr.layers = class="num">4;
   descr.optimization = ADAM;
   if(!encoder.Add(descr))
      {
      class="kw">delete descr;
      class="kw">return false;
      }
class=class="str">"cmt">//--- layer class="num">12
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   prev_count = descr.count = EmbeddingSize;
   descr.optimization = ADAM;
   descr.activation = None;
   if(!encoder.Add(descr))
      {
      class="kw">delete descr;
      class="kw">return false;
      }
class=class="str">"cmt">//--- layer class="num">13
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronSoftMaxOCL;
   descr.count = prev_count;
   descr.step = class="num">1;
   descr.optimization = ADAM;
   descr.activation = None;
   if(!encoder.Add(descr))
      {
      class="kw">delete descr;
      class="kw">return false;
      }
class=class="str">"cmt">//--- Decoder
   decoder.Clear();
class=class="str">"cmt">//--- Input layer
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   descr.count = EmbeddingSize;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!decoder.Add(descr))
      {
      class="kw">delete descr;
      class="kw">return false;
      }
class=class="str">"cmt">//--- layer class="num">1
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   prev_count = descr.count = (HistoryBars + PrecoderBars) * EmbeddingSize;
   descr.activation = LReLU;
   descr.optimization = ADAM;
   if(!decoder.Add(descr))
      {
      class="kw">delete descr;
      class="kw">return false;
      }
class=class="str">"cmt">//--- layer class="num">2
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronMLMHAttentionOCL;
   prev_count = descr.count = prev_count / EmbeddingSize;
   prev_wout = descr.window = EmbeddingSize;
   descr.step = class="num">4;
   descr.window_out = class="num">16;
   descr.layers = class="num">2;
   descr.optimization = ADAM;
   if(!decoder.Add(descr))
      {
      class="kw">delete descr;
      class="kw">return false;
      }
class=class="str">"cmt">//--- layer class="num">3
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronMultiModels;
   descr.count = class="num">3;
   descr.window = prev_wout;
   descr.step = prev_count;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!decoder.Add(descr))
      {
      class="kw">delete descr;
      class="kw">return false;
      }
class=class="str">"cmt">//---
   class="kw">return true;
   }

「给强化学习网络搭描述层」

在 MT5 里用面向对象方式堆策略网络时,先要把 actor、goal、encoder 三组层描述容器准备好。函数入口处若传入空指针就当场 new 一个 CArrayObj,任一分配失败直接 return false,避免后面挂空引用把 EA 拖崩。 状态编码器(encoder)第一层用 defNeuronBaseOCL 类型,节点数等于 EmbeddingSize,优化器选 ADAM、激活函数 None;第二层切到 defNeuronConcatenate,count 设 LatentCount,window 接上一层节点数、step 取 AccountDescr,激活走 SIGMOID。这两层构成了把账户状态压成隐变量的前端。 actor 侧稍重一些:输入层同样是 defNeuronBaseOCL + LatentCount,随后 concatenate 层 step 换成 NRewards、激活改 LReLU;再叠两层 base 层,倒数第二层 SIGMOID,末层节点数直接拉到 2 * NActions 且不带激活——输出成对参数供后续采样动作。外汇与贵金属波动剧烈,这类网络只是概率建模,实盘前务必在策略测试器跑多周期回测。 逐行看这段构建代码,能发现所有 Add 失败分支都先 delete descr 再返 false,防止描述对象泄漏。复制进你的 .mqh 头文件,把 EmbeddingSize / LatentCount / NActions 换成自己的宏,就能在终端里编译验证结构是否撑得住。

MQL5 / C++
class="type">bool CreateDescriptions(CArrayObj *actor, CArrayObj *goal, CArrayObj *encoder)
  {
class=class="str">"cmt">//---
   CLayerDescription *descr;
class=class="str">"cmt">//---
   if(!actor)
     {
      actor = new CArrayObj();
      if(!actor)
         class="kw">return false;
     }
   if(!goal)
     {
      goal = new CArrayObj();
      if(!goal)
         class="kw">return false;
     }
   if(!encoder)
     {
      encoder = new CArrayObj();
      if(!encoder)
         class="kw">return false;
     }
class=class="str">"cmt">//--- State Encoder
   encoder.Clear();
class=class="str">"cmt">//--- Input layer
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   class="type">int prev_count = descr.count = EmbeddingSize;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">1
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronConcatenate;
   descr.count = LatentCount;
   descr.window = prev_count;
   descr.step = AccountDescr;
   descr.optimization = ADAM;
   descr.activation = SIGMOID;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- Actor
   actor.Clear();
class=class="str">"cmt">//--- Input layer
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   prev_count = descr.count = LatentCount;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">1
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronConcatenate;
   descr.count = LatentCount;
   descr.window = prev_count;
   descr.step = NRewards;
   descr.optimization = ADAM;
   descr.activation = LReLU;
   if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">2
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   descr.count = LatentCount;
   descr.activation = SIGMOID;
   descr.optimization = ADAM;
   if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">3
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   descr.count = class="num">2 * NActions;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!actor.Add(descr))
     {
      class="kw">delete descr;

Actor 与 Goal 网络的层定义和初始化加载

这段逻辑在构建强化学习智能体的两个子网络:Actor 负责输出动作,Goal 网络评估状态价值与目标偏差。第四层 Actor 用了 defNeuronVAEOCL 类型,节点数绑定 NActions,优化器统一走 ADAM;若 Add 失败立即 delete 描述符并返回 false,避免内存泄漏。 Goal 网络则从输入层开始:首层 defNeuronBaseOCL,节点数等于 EmbeddingSize,激活函数为 None;随后两层隐层均为 LatentCount 个节点、LReLU 激活。最后输出层用 defNeuronFQF 类型,节点数 NRewards,window_out 显式设为 32,这是分位数回归输出窗口,影响奖励分布的分位粒度。 网络对象在类域声明为 Encoder、StateEncoder、Actor、Goal 四个 CNet 实例。OnInit 里先尝试从 FileName+"Enc.nnw" 加载已训练模型,三个 temp 浮点作占位参数;若加载失败则现场 new 两个 CArrayObj 描述符容器,调用 CreateTrajNetDescriptions 重建结构,任一环节失败返回 INIT_FAILED,EA 不会启动。 外汇与贵金属市场高杠杆、高波动,此类模型在实盘仅具概率优势,请先在 MT5 策略测试器用历史数据验证层参数与加载逻辑。

MQL5 / C++
      class="kw">return false;
    }
class=class="str">"cmt">//--- layer class="num">4
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronVAEOCL;
   descr.count = NActions;
   descr.optimization = ADAM;
   if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- Goal
   goal.Clear();
class=class="str">"cmt">//--- Input layer
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   prev_count = descr.count = EmbeddingSize;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!goal.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">1
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   descr.count = LatentCount;
   descr.activation = LReLU;
   descr.optimization = ADAM;
   if(!goal.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">3
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   descr.count = LatentCount;
   descr.activation = LReLU;
   descr.optimization = ADAM;
   if(!goal.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">4
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronFQF;
   descr.count = NRewards;
   descr.window_out = class="num">32;
   descr.optimization = ADAM;
   descr.activation = None;
   if(!goal.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//---
   class="kw">return true;
   }
CNet          Encoder;
CNet          StateEncoder;
CNet          Actor;
CNet          Goal;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---
........
........
class=class="str">"cmt">//--- load models
   class="type">float temp;
   if(!Encoder.Load(FileName + "Enc.nnw", temp, temp, temp, dtStudied, true))
     {
      CArrayObj *encoder = new CArrayObj();
      CArrayObj *decoder = new CArrayObj();
      if(!CreateTrajNetDescriptions(encoder, decoder))
        {
         class="kw">delete encoder;
         class="kw">delete decoder;
         class="kw">return INIT_FAILED;
        }

◍ EA初始化时神经网络模型的加载与维度校验

在 MT5 智能交易系统的 OnInit 阶段,若本地已存在训练好的网络文件(后缀 StEnc.nnw、Goal.nnw、Act.nnw),会直接 Load 载入;任一文件载入失败则进入重建分支,用 CreateDescriptions 生成 actor / goal / encoder 的三套网络描述对象并 Create 构建,任何一步返回 false 都 delete 掉相关指针并以 INIT_FAILED 退出。 载入或构建完成后,代码会把 Actor 的 OpenCL 上下文同步给 StateEncoder、Encoder、Goal,并关闭 Encoder 的训练模式(TrainMode(false))。随后立刻做维度对账:Actor 输出节点数必须等于 NActions,Encoder 输出必须等于 EmbeddingSize,二者不符就 PrintFormat 报错并 INIT_FAILED。 Encoder 第 0 层输出维度还要等于 HistoryBars * BarDescr,例如 HistoryBars=30、BarDescr=5 时期望 150 维,错位会触发 'Input size of Encoder doesn't match state description' 并终止初始化。最后把账户余额与净值读入 PrevBalance / PrevEquity 作为回合基线,返回 INIT_SUCCEEDED 才算过闸。 外汇与贵金属市场杠杆高、滑点随机,这类依赖神经网络的 EA 在模型维度校验未过闸时绝不会下单,但过闸后实盘仍可能因分布偏移而表现漂移,需先在策略测试器用历史数据验证网络文件与参数匹配。

MQL5 / C++
if(!Encoder.Create(encoder))
  {
   class="kw">delete encoder;
   class="kw">delete decoder;
   class="kw">return INIT_FAILED;
  }
 class="kw">delete encoder;
 class="kw">delete decoder;
 class=class="str">"cmt">//---
  }
 if(!StateEncoder.Load(FileName + "StEnc.nnw", temp, temp, temp, dtStudied, true) ||
   !Goal.Load(FileName + "Goal.nnw", temp, temp, temp, dtStudied, true) ||
   !Actor.Load(FileName + "Act.nnw", temp, temp, temp, dtStudied, true))
  {
   CArrayObj *actor = new CArrayObj();
   CArrayObj *goal = new CArrayObj();
   CArrayObj *encoder = new CArrayObj();
   if(!CreateDescriptions(actor, goal, encoder))
     {
      class="kw">delete actor;
      class="kw">delete goal;
      class="kw">delete encoder;
      class="kw">return INIT_FAILED;
     }
   if(!Actor.Create(actor) || !StateEncoder.Create(encoder) || !Goal.Create(goal))
     {
      class="kw">delete actor;
      class="kw">delete goal;
      class="kw">delete encoder;
      class="kw">return INIT_FAILED;
     }
   class="kw">delete actor;
   class="kw">delete goal;
   class="kw">delete encoder;
   class=class="str">"cmt">//---
  }
 StateEncoder.SetOpenCL(Actor.GetOpenCL());
 Encoder.SetOpenCL(Actor.GetOpenCL());
 Goal.SetOpenCL(Actor.GetOpenCL());
 Encoder.TrainMode(false);
 Actor.getResults(Result);
 if(Result.Total() != NActions)
   {
    PrintFormat("The scope of the actor does not match the actions count(%d <> %d)", NActions, Result.Total());
    class="kw">return INIT_FAILED;
   }
 Encoder.getResults(Result);
 if(Result.Total() != EmbeddingSize)
   {
    PrintFormat("The scope of the Encoder does not match the embedding size(%d <> %d)", EmbeddingSize,
Result.Total());
    class="kw">return INIT_FAILED;
   }
class=class="str">"cmt">//---
 Encoder.GetLayerOutput(class="num">0, Result);
 if(Result.Total() != (HistoryBars * BarDescr))
   {
    PrintFormat("Input size of Encoder doesn&class="macro">#x27;t match state description(%d <> %d)", Result.Total(),
(HistoryBars * BarDescr));
    class="kw">return INIT_FAILED;
   }
class=class="str">"cmt">//---
 PrevBalance = AccountInfoDouble(ACCOUNT_BALANCE);
 PrevEquity = AccountInfoDouble(ACCOUNT_EQUITY);
class=class="str">"cmt">//---
 class="kw">return(INIT_SUCCEEDED);
 }

「编码器前向传播与模型懒加载」

这段逻辑把轨迹自编码器的推理链路直接铺在 EA 初始化与逐帧计算里。先看前向部分:先把缓冲状态 bState 喂给 Encoder 做一层前向(feedForward 第三个参数 false 表示不训练),再把 Encoder 输出转交 StateEncoder 得到账户状态向量 bAccount,随后 Goal 网络以 NULL 为输出缓冲只算表征,最后 Actor 基于 StateEncoder 出动作。任意一步返回 false 就直接 return,避免脏状态往下传。 初始化函数 OnInit 里先 ResetLastError,再 LoadTotalBase 装载学习数据;若失败会打印错误码并回 INIT_FAILED。模型文件默认从 FileName+"Enc.nnw" 与 "Dec.nnw" 读取,读取时传入 dtStudied 与 true 标记。 若本地没有现成神经网络权重,代码会走新建分支:建两个 CArrayObj 描述编码器与解码器结构,调 CreateTrajNetDescriptions 填充层定义,再分别 Encoder.Create / Decoder.Create。注意 Iterations 输入参数写死 1e4(即 10000 次),这是训练或回放轨迹的生成上限,开 MT5 改这个值能直接拉长轨迹缓冲。 OpenCL 上下文在模型创建或加载后由 Encoder.GetOpenCL() 统一取出,后续若接小布盯盘的 GPU 批处理脚本,可复用同一设备句柄省去重复初始化。外汇与贵金属行情下用这类自编码推理,信号延迟和过拟合风险都偏高,参数务必在模拟盘先验证。

MQL5 / C++
  class=class="str">"cmt">//---
   if(!Encoder.feedForward((CBufferFloat*)GetPointer(bState), class="num">1, false, (CNet*)GetPointer(Encoder)) ||
       !StateEncoder.feedForward((CNet *)GetPointer(Encoder), -class="num">1, (CBufferFloat *)GetPointer(bAccount)) ||
       !Goal.feedForward((CNet *)GetPointer(Encoder), -class="num">1, (CBufferFloat *)NULL) ||
       !Actor.feedForward((CNet *)GetPointer(StateEncoder), -class="num">1, (CNet *)GetPointer(Goal)))
      class="kw">return;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Input parameters                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">int                Iterations     = class="num">1e4;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
STrajectory        Buffer[];
CNet               Encoder;
CNet               Decoder;
CBufferFloat       LastEncoder;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   ResetLastError();
   if(!LoadTotalBase())
     {
      PrintFormat("Error of load study data: %d", GetLastError());
      class="kw">return INIT_FAILED;
     }
class=class="str">"cmt">//--- load models
   class="type">float temp;
   if(!Encoder.Load(FileName + "Enc.nnw", temp, temp, temp, dtStudied, true) ||
      !Decoder.Load(FileName + "Dec.nnw", temp, temp, temp, dtStudied, true))
     {
      Print("Init new models");
      CArrayObj *encoder = new CArrayObj();
      CArrayObj *decoder = new CArrayObj();
      if(!CreateTrajNetDescriptions(encoder, decoder))
        {
         class="kw">delete encoder;
         class="kw">delete decoder;
         class="kw">return INIT_FAILED;
        }
      if(!Encoder.Create(encoder) || !Decoder.Create(decoder))
        {
         class="kw">delete encoder;
         class="kw">delete decoder;
         class="kw">return INIT_FAILED;
        }
      class="kw">delete encoder;
      class="kw">delete decoder;
      class=class="str">"cmt">//---
     }
   OpenCL = Encoder.GetOpenCL();

初始化校验与训练循环的真实落点

在 MT5 里跑自编码器类策略,初始化阶段最容易栽在维度不匹配上。下面这段校验逻辑强制要求 Encoder 输出维度等于 EmbeddingSize,且第一层输出必须等于 HistoryBars * BarDescr;Decoder 输入也必须等于 EmbeddingSize,否则直接 INIT_FAILED。 [CODE] Decoder.SetOpenCL(OpenCL); Encoder.getResults(Result); if(Result.Total() != EmbeddingSize) { PrintFormat("The scope of the Encoder does not match the embedding size count (%d <> %d)", EmbeddingSize, Result.Total()); return INIT_FAILED; } //--- Encoder.GetLayerOutput(0, Result); if(Result.Total() != (HistoryBars * BarDescr)) { PrintFormat("Input size of Encoder doesn't match state description (%d <> %d)", Result.Total(), (HistoryBars * BarDescr)); return INIT_FAILED; } //--- Decoder.GetLayerOutput(0, Result); if(Result.Total() != EmbeddingSize) { PrintFormat("Input size of Decoder doesn't match Encoder output (%d <> %d)", Result.Total(), EmbeddingSize); return INIT_FAILED; }

if(!LastEncoder.BufferInit(EmbeddingSize,0)
!Gradient.BufferInit(EmbeddingSize,0)
!LastEncoder.BufferCreate(OpenCL)

!Gradient.BufferCreate(OpenCL)) { PrintFormat("Error of create buffers: %d", GetLastError()); return INIT_FAILED; } if(!EventChartCustom(ChartID(), 1, 0, 0, "Init")) { PrintFormat("Error of create study event: %d", GetLastError()); return INIT_FAILED; } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+

//Train function

//+------------------------------------------------------------------+ void Train(void) { //--- vector<float> probability = GetProbTrajectories(Buffer, 0.9); vector<float> result, target; matrix<float> targets; STD = vector<float>::Zeros((HistoryBars + PrecoderBars) * 3); int std_count = 0; uint ticks = GetTickCount(); for(int iter = 0; (iter < Iterations && !IsStopped()); iter ++) { int tr = SampleTrajectory(probability); int batch = GPTBars + 50; int state = (int)((MathRand() * MathRand() / MathPow(32767, 2)) * (Buffer[tr].Total - 3 - PrecoderBars - batch)); if(state <= 0) { iter--; continue; } Encoder.Clear(); Decoder.Clear(); LastEncoder.BufferInit(EmbeddingSize,0); [/CODE] 代码逐行拆解:Decoder.SetOpenCL 把计算丢给 OpenCL 设备;Encoder.getResults 取回结构输出,随后三个 if 分别卡死嵌入维数、状态描述长度、解码器输入维数。LastEncoder 与 Gradient 的 BufferInit/BufferCreate 失败会打印 GetLastError 并退出。EventChartCustom 发自定义事件 "Init" 通知图表端。 Train 函数里 probability 用 0.9 温度采样轨迹;batch 固定为 GPTBars+50;state 用 MathRand 平方归一化做偏置抽样,若算出小于等于 0 就重试本次迭代。外汇与贵金属行情高波动,这类模型维度错配会直接让 EA 加载失败,开 MT5 把 EmbeddingSize 和 HistoryBars*BarDescr 打印出来对一遍再编译。

MQL5 / C++
Decoder.SetOpenCL(OpenCL);
Encoder.getResults(Result);
if(Result.Total() != EmbeddingSize)
  {
  PrintFormat("The scope of the Encoder does not match the embedding size count(%d <> %d)", EmbeddingSize, Result.Total());
  class="kw">return INIT_FAILED;
  }
class=class="str">"cmt">//---
Encoder.GetLayerOutput(class="num">0, Result);
if(Result.Total() != (HistoryBars * BarDescr))
  {
  PrintFormat("Input size of Encoder doesn&class="macro">#x27;t match state description(%d <> %d)", Result.Total(), (HistoryBars * BarDescr));
  class="kw">return INIT_FAILED;
  }
class=class="str">"cmt">//---
Decoder.GetLayerOutput(class="num">0, Result);
if(Result.Total() != EmbeddingSize)
  {
  PrintFormat("Input size of Decoder doesn&class="macro">#x27;t match Encoder output(%d <> %d)", Result.Total(), EmbeddingSize);
  class="kw">return INIT_FAILED;
  }
if(!LastEncoder.BufferInit(EmbeddingSize,class="num">0) ||
   !Gradient.BufferInit(EmbeddingSize,class="num">0) ||
   !LastEncoder.BufferCreate(OpenCL) ||
   !Gradient.BufferCreate(OpenCL))
  {
  PrintFormat("Error of create buffers: %d", GetLastError());
  class="kw">return INIT_FAILED;
  }
if(!EventChartCustom(ChartID(), class="num">1, class="num">0, class="num">0, "Init"))
  {
  PrintFormat("Error of create study event: %d", GetLastError());
  class="kw">return INIT_FAILED;
  }
class=class="str">"cmt">//---
class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Train function                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Train(class="type">void)
  {
class=class="str">"cmt">//---
  vector<class="type">float> probability = GetProbTrajectories(Buffer, class="num">0.9);
  vector<class="type">float> result, target;
  matrix<class="type">float> targets;
  STD = vector<class="type">float>::Zeros((HistoryBars + PrecoderBars) * class="num">3);
  class="type">int std_count = class="num">0;
  class="type">uint ticks = GetTickCount();
  for(class="type">int iter = class="num">0; (iter < Iterations && !IsStopped()); iter ++)
    {
    class="type">int tr = SampleTrajectory(probability);
    class="type">int batch = GPTBars + class="num">50;
    class="type">int state = (class="type">int)((MathRand() * MathRand() / MathPow(class="num">32767, class="num">2)) * (Buffer[tr].Total - class="num">3 - PrecoderBars - batch));
    if(state <= class="num">0)
      {
      iter--;
      class="kw">continue;
      }
    Encoder.Clear();
    Decoder.Clear();
    LastEncoder.BufferInit(EmbeddingSize,class="num">0);
把轨迹压缩交给小布盯盘跑
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到预训练表示与政策输出的分离监控,你专注调参决策。

常见问题

决策转换器多用统一目标做序列建模,GCPC 明确拆分两阶段:先用序列模型把轨迹压成紧凑表示,再用 MLP 学行为政策,表示学习目标与政策学习目标不同。
次优轨迹常含可复用的子轨迹技能,GCPC 通过目标条件预测把过去与预测状态编码,模型倾向提取有用片段而非整条模仿。
可以,小布盯盘内置的表示监控能对照压缩前后状态重建误差,帮你在 MT5 品种页快速判断预训练是否丢掉了关键价位结构。
因状态预测以设定目标为条件,提供决定性指导,潜在表示同时覆盖过去与预测,缓解长周期信用分配模糊。
实验结论指向两者兼有的目标条件预测最有效,单纯编码历史或未来动态都不如联合预测在基准中竞争力强。