神经网络变得轻松(第三十八部分):凭借分歧进行自我监督探索·进阶篇
🧠

神经网络变得轻松(第三十八部分):凭借分歧进行自我监督探索·进阶篇

(2/3)·当外部奖励稀少滞后,靠多个动态模型的分歧生成内在激励,代理者才肯踏进未探索区域

含代码示例 第 2/3 篇
很多人在静默行情模拟里直接套用游戏界的探索算法,结果预测模型被环境随机性带偏,策略迟迟不收敛。把稀有奖励当唯一信号,代理者只会原地打转。分歧自监督的思路是先让模型自己吵起来,再顺着吵架的地方去探。

反向传播里的向量化梯度累加

这段内核在做一件事:把上层传回来的梯度按输出节点分组,用 float4 向量一次算 4 个,再点积进 sum。循环以 k += 4 步进,switch(outputs - k) 专门处理末尾不足 4 个的残差,避免越界读 matrix_g 和 matrix_w。 权重矩阵在内存里按 (inputs + 1) 为单节点跨度排布,所以取第 k 个输出对第 i 个输入的权重要写 shift_w + k * (inputs + 1) + i。dot(grad, weight) 直接累加到 sum,比逐标量写四次快得多,在 MT5 的 OpenCL 内核里这个写法能明显压低回传耗时。 sum 算完先 isnan 兜底置 0,再按激活函数分流。case 0 是 tanh 类:先把 out 夹到 [-1,1],梯度乘 (1 - out^2) 且下限 1e-4;case 1 是 sigmoid 类:夹到 [0,1],梯度乘 out*(1-out) 同样封底 1e-4。外汇与贵金属行情跳空多,NaN 兜底不是可选项,实盘跑神经网络指标时漏掉这行可能让整层梯度蒸发。 把下面这段直接丢进你的内核做对照,重点看 switch 残差分支和 clamp 下限,这两处是多数自行移植版容易写错的地方。

MQL5 / C++
  class="type">float out = matrix_o[shift_in + i];
  float4 grad, weight;
  for(class="type">int k = class="num">0; k < outputs; k += class="num">4)
    {
      class="kw">switch(outputs - k)
        {
         case class="num">1:
            weight = (float4)(matrix_w[shift_w + k * (inputs + class="num">1) + i], class="num">0, class="num">0, class="num">0);
            grad = (float4)(matrix_g[shift_out + k], class="num">0, class="num">0, class="num">0);
            class="kw">break;
         case class="num">2:
            grad = (float4)(matrix_g[shift_out + k], matrix_g[shift_out + k + class="num">1], class="num">0, class="num">0);
            weight = (float4)(matrix_w[shift_w + k * (inputs + class="num">1) + i], matrix_w[shift_w + (k + class="num">1) * (inputs + class="num">1) + i], class="num">0, class="num">0);
            class="kw">break;
         case class="num">3:
            grad = (float4)(matrix_g[shift_out + k], matrix_g[shift_out + k + class="num">1], matrix_g[shift_out + k + class="num">2], class="num">0);
            weight = (float4)(matrix_w[shift_w + k * (inputs + class="num">1) + i], matrix_w[shift_w + (k + class="num">1) * (inputs + class="num">1) + i],
matrix_w[shift_w + (k + class="num">2) * (inputs + class="num">1) + i], class="num">0);
            class="kw">break;
         class="kw">default:
            grad = (float4)(matrix_g[shift_out + k], matrix_g[shift_out + k + class="num">1], matrix_g[shift_out + k + class="num">2],
matrix_g[shift_out + k + class="num">3]);
            weight = (float4)(matrix_w[shift_w + k * (inputs + class="num">1) + i], matrix_w[shift_w + (k + class="num">1) * (inputs + class="num">1) + i],
matrix_w[shift_w + (k + class="num">2) * (inputs + class="num">1) + i], matrix_w[shift_w + (k + class="num">3) * (inputs + class="num">1) + i]);
            class="kw">break;
        }
       sum += dot(grad, weight);
     }
   if(isnan(sum))
      sum = class="num">0;
   class="kw">switch(activation)
     {
      case class="num">0:
         out = clamp(out, -class="num">1.0f, class="num">1.0f);
         sum = clamp(sum + out, -class="num">1.0f, class="num">1.0f) - out;
         sum = sum * max(class="num">1 - pow(out, class="num">2), class="num">1.0e-4f);
         class="kw">break;
      case class="num">1:
         out = clamp(out, class="num">0.0f, class="num">1.0f);
         sum = clamp(sum + out, class="num">0.0f, class="num">1.0f) - out;
         sum = sum * max(out * (class="num">1 - out), class="num">1.0e-4f);
         class="kw">break;
      case class="num">2:
         if(out < class="num">0)

「多模型神经元的初始化与内存布局」

在 MT5 的 OpenCL 神经网络框架里,CNeuronMultiModel 通过继承 CNeuronBaseOCL 实现多模型并行推理。类内用 iModels 与 iUpdateModel 两个整型成员记录模型数量和当前更新目标,虚函数 feedForward、updateInputWeights 留给子类或运行时多态去落地。 Init 方法是硬关卡:先判 open_cl 指针有效性,再要求 numNeurons>0 且 models>0,否则直接返回 false。内部强制把 optimization 设为 ADAM,iBatch 写死为 1,这意味着该层默认按单样本、Adam 优化器跑多模型前向。 Output 缓冲区按 numNeurons * models 大小初始化为 0.0,并交由 OpenCL 创建显存对象;Gradient 缓冲区走同样的保护性 new + 判空流程。你在 EA 里调 models 参数时,显存占用会线性放大 models 倍,贵金属或外汇高频训练前先算清这张卡是否扛得住。 下方代码展示了类声明与 Init 前半段,注意 default 分支在 switch 里只 break,说明未匹配的模式不做任何累计算子处理。

MQL5 / C++
    sum *= class="num">0.01f;
    class="kw">break;
    class="kw">default:
      class="kw">break;
   }
  matrix_ig[shift_in + i] = sum;
 }
class CNeuronMultiModel : class="kw">public CNeuronBaseOCL
  {
 class="kw">protected:
   class="type">int                iModels;
   class="type">int                iUpdateModel;
   class=class="str">"cmt">//---
   class="kw">virtual class="type">bool      feedForward(CNeuronBaseOCL *NeuronOCL);
   class="kw">virtual class="type">bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL);
 class="kw">public:
                     CNeuronMultiModel(class="type">void){};
                    ~CNeuronMultiModel(class="type">void){};
   class="kw">virtual class="type">bool      Init(class="type">uint numInputs, class="type">uint myIndex, COpenCLMy *open_cl, class="type">uint numNeurons,
ENUM_OPTIMIZATION optimization_type, class="type">int models);
   class="kw">virtual class="type">void      SetActivationFunction(ENUM_ACTIVATION value) { activation = value;         }
   class=class="str">"cmt">//---
   class="kw">virtual class="type">bool      calcHiddenGradients(CNeuronBaseOCL *NeuronOCL);
   class=class="str">"cmt">//---
   class="kw">virtual class="type">bool      Save(class="type">int const file_handle);
   class="kw">virtual class="type">bool      Load(class="type">int const file_handle);
   class=class="str">"cmt">//---
   class="kw">virtual class="type">int       Type(class="type">void)      const                         {  class="kw">return defNeuronMultiModels; }
   };
class="type">bool CNeuronMultiModel::Init(class="type">uint numInputs, class="type">uint myIndex, COpenCLMy *open_cl, class="type">uint numNeurons,
ENUM_OPTIMIZATION optimization_type, class="type">int models)
  {
  if(CheckPointer(open_cl) == POINTER_INVALID || numNeurons <= class="num">0  || models <= class="num">0)
     class="kw">return false;
  OpenCL = open_cl;
  optimization = ADAM;
  iBatch = class="num">1;
  iModels = models;
class=class="str">"cmt">//---
  if(CheckPointer(Output) == POINTER_INVALID)
    {
     Output = new CBufferFloat();
     if(CheckPointer(Output) == POINTER_INVALID)
        class="kw">return false;
    }
  if(!Output.BufferInit(numNeurons * models, class="num">0.0))
     class="kw">return false;
  if(!Output.BufferCreate(OpenCL))
     class="kw">return false;
class=class="str">"cmt">//---
  if(CheckPointer(Gradient) == POINTER_INVALID)
    {
     Gradient = new CBufferFloat();
     if(CheckPointer(Gradient) == POINTER_INVALID)
        class="kw">return false;
    }

◍ 多模型神经元的权重初始化与前向传导

在 MT5 的 OpenCL 神经网络封装里,CNeuronMultiModel 的初始化先把梯度缓冲按 (神经元数+1)*模型数 的规模铺开,再为权重申请一块 (输入数+1)*神经元数*模型数 的浮点缓冲。若以 10 输入、8 神经元、4 模型 为例,count = (10+1)*8*4 = 352,权重数组一次 Reserve 到位。 权重填充用 1/sqrt(输入数+1) 做缩放系数 k,循环里写成 (2*随机值*k - k)*乘数,相当于在 [-k, k] 区间均匀撒点后再乘用户乘数。这种 Xavier 风格初始化在输入维度偏高时倾向让前向信号方差更稳定,外汇小时线特征维度动辄 20+,不调 k 直接训可能梯度爆炸。 前向 feedForward 没走逐样本循环,而是用二维 global_work_size:第 0 维是 输出总数/模型数,第 1 维是 模型数,一次 GPU 派发把所有子模型并行算完。SetArgumentBuffer 绑错权重索引会直接 printf 报错并返回 false,调试时盯紧 def_k_ff_matrix_w 这个参数槽。

MQL5 / C++
  if(!Gradient.BufferInit((numNeurons + class="num">1)*models, class="num">0.0))
        class="kw">return false;
  if(!Gradient.BufferCreate(OpenCL))
        class="kw">return false;
class=class="str">"cmt">//---
  if(CheckPointer(Weights) == POINTER_INVALID)
     {
       Weights = new CBufferFloat();
       if(CheckPointer(Weights) == POINTER_INVALID)
          class="kw">return false;
     }
  class="type">int count = (class="type">int)((numInputs + class="num">1) * numNeurons * models);
  if(!Weights.Reserve(count))
        class="kw">return false;
  class="type">float k = (class="type">float)(class="num">1 / sqrt(numInputs + class="num">1));
  for(class="type">int i = class="num">0; i < count; i++)
     {
       if(!Weights.Add((class="num">2 * GenerateWeight()*k - k)*WeightsMultiplier))
          class="kw">return false;
     }
  if(!Weights.BufferCreate(OpenCL))
        class="kw">return false;
class=class="str">"cmt">//---
  if(CheckPointer(DeltaWeights) != POINTER_INVALID)
       class="kw">delete DeltaWeights;
class=class="str">"cmt">//---
  if(CheckPointer(FirstMomentum) == POINTER_INVALID)
     {
       FirstMomentum = new CBufferFloat();
       if(CheckPointer(FirstMomentum) == POINTER_INVALID)
          class="kw">return false;
     }
  if(!FirstMomentum.BufferInit(count, class="num">0))
        class="kw">return false;
  if(!FirstMomentum.BufferCreate(OpenCL))
        class="kw">return false;
class=class="str">"cmt">//---
  if(CheckPointer(SecondMomentum) == POINTER_INVALID)
     {
       SecondMomentum = new CBufferFloat();
       if(CheckPointer(SecondMomentum) == POINTER_INVALID)
          class="kw">return false;
     }
  if(!SecondMomentum.BufferInit(count, class="num">0))
        class="kw">return false;
  if(!SecondMomentum.BufferCreate(OpenCL))
        class="kw">return false;
class=class="str">"cmt">//---
  class="kw">return true;
  }
class="type">bool CNeuronMultiModel::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
  if(CheckPointer(OpenCL) == POINTER_INVALID || CheckPointer(NeuronOCL) == POINTER_INVALID)
        class="kw">return false;
  class="type">uint global_work_offset[class="num">2] = {class="num">0, class="num">0};
  class="type">uint global_work_size[class="num">2];
  global_work_size[class="num">0] = Output.Total() / iModels;
  global_work_size[class="num">1] = iModels;
  if(!OpenCL.SetArgumentBuffer(def_k_FFMultiModels, def_k_ff_matrix_w, getWeightsIndex()))
     {
       printf("Error of set parameter kernel FeedForward: %d; line %d", GetLastError(), __LINE__);

多模型神经元的核参数绑定与梯度前置

这段 CNeuronMultiModel 的片段只做一件事:把多模型前馈核(FeedForward)和隐藏层梯度核(calcHiddenGradients)的缓冲区参数逐一对齐到 OpenCL 设备端。任何一次 SetArgumentBuffer / SetArgument 失败都会立刻 printf 报错并返回 false,调用方必须检查返回值,否则后续 Execute 跑的是脏数据。 前馈核里 def_k_ff_matrix_i、def_k_ff_matrix_o 分别绑的是上层神经元输出缓冲与本层输出缓冲;def_k_ff_inputs 用 NeuronOCL.Neurons()/iModels 算出单模型输入维度,def_k_ff_activation 把激活函数类型以 int 传入。最后 OpenCL.Execute 用二维全局尺寸 [NeuronOCL.Neurons()/iModels, iModels] 启动核,work_offset 恒为 {0,0}。 calcHiddenGradients 里 global_work_size[0] 同样取 NeuronOCL.Neurons()/iModels,[1] 取 iModels,说明梯度计算也是按模型并行切分的。权重索引 getWeightsIndex() 与梯度索引 getGradientIndex() 分别绑到 def_k_chg_matrix_w / def_k_chg_matrix_g,上层输出与梯度缓冲则来自 NeuronOCL.getOutputIndex() 和 getGradientIndex()。 在 MT5 里验证时,若日志频繁出现 'Error of set parameter kernel FeedForward: %d; line %d' 且行号落在这段,优先查 iModels 是否整除 NeuronOCL.Neurons(),除不尽会导致核维度越界。外汇与贵金属行情下用此类 GPU 推理模块,延迟抖动可能放大滑点风险,参数需先在模拟盘跑稳。

MQL5 / C++
  class="kw">return false;
   }
  if(!OpenCL.SetArgumentBuffer(def_k_FFMultiModels, def_k_ff_matrix_i, NeuronOCL.getOutputIndex()))
   {
     printf("Error of set parameter kernel FeedForward: %d; line %d", GetLastError(), __LINE__);
     class="kw">return false;
   }
  if(!OpenCL.SetArgumentBuffer(def_k_FFMultiModels, def_k_ff_matrix_o, Output.GetIndex()))
   {
     printf("Error of set parameter kernel FeedForward: %d; line %d", GetLastError(), __LINE__);
     class="kw">return false;
   }
  if(!OpenCL.SetArgument(def_k_FFMultiModels, def_k_ff_inputs, NeuronOCL.Neurons() / iModels))
   {
     printf("Error of set parameter kernel FeedForward: %d; line %d", GetLastError(), __LINE__);
     class="kw">return false;
   }
  if(!OpenCL.SetArgument(def_k_FFMultiModels, def_k_ff_activation, (class="type">int)activation))
   {
     printf("Error of set parameter kernel FeedForward: %d; line %d", GetLastError(), __LINE__);
     class="kw">return false;
   }
  if(!OpenCL.Execute(def_k_FFMultiModels, class="num">2, global_work_offset, global_work_size))
   {
     printf("Error of execution kernel FeedForward: %d", GetLastError());
     class="kw">return false;
   }
class=class="str">"cmt">//---
   class="kw">return true;
  }
class="type">bool CNeuronMultiModel::calcHiddenGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(CheckPointer(OpenCL) == POINTER_INVALID || CheckPointer(NeuronOCL) == POINTER_INVALID)
      class="kw">return false;
   class="type">uint global_work_offset[class="num">2] = {class="num">0, class="num">0};
   class="type">uint global_work_size[class="num">2];
   global_work_size[class="num">0] = NeuronOCL.Neurons() / iModels;
   global_work_size[class="num">1] = iModels;
   if(!OpenCL.SetArgumentBuffer(def_k_HGMultiModels, def_k_chg_matrix_w, getWeightsIndex()))
    {
      printf("Error of set parameter kernel calcHiddenGradients: %d; line %d", GetLastError(), __LINE__);
      class="kw">return false;
    }
   if(!OpenCL.SetArgumentBuffer(def_k_HGMultiModels, def_k_chg_matrix_g, getGradientIndex()))
    {
      printf("Error of set parameter kernel calcHiddenGradients: %d; line %d", GetLastError(), __LINE__);
      class="kw">return false;
    }
   if(!OpenCL.SetArgumentBuffer(def_k_HGMultiModels, def_k_chg_matrix_o, NeuronOCL.getOutputIndex()))
    {
      printf("Error of set parameter kernel calcHiddenGradients: %d; line %d", GetLastError(), __LINE__);
      class="kw">return false;
    }
   if(!OpenCL.SetArgumentBuffer(def_k_HGMultiModels, def_k_chg_matrix_ig, NeuronOCL.getGradientIndex()))
    {
      printf("Error of set parameter kernel calcHiddenGradients: %d; line %d", GetLastError(), __LINE__);
      class="kw">return false;

「多模型核的参数装配与分歧探索类骨架」

这段片段接着多模型神经元的前向与梯度核,把 OpenCL 参数槽逐个绑到具体变量上。SetArgument 调用里,def_k_chg_outputs 被设成 Neurons()/iModels,意味着隐藏层梯度核按模型数做输出切分;def_k_chg_activation 直接取 NeuronOCL 的激活函数标识,def_k_chg_model 则用 MathRand()/32767.0 随机落到 [0, iModels-1] 的某个模型下标,单次只更新一个子模型权重。 核执行走 OpenCL.Execute(def_k_HGMultiModels, 2, global_work_offset, global_work_size),二维全局工组尺寸由调用方给定;任一处 SetArgument 或 Execute 失败就 printf 报错并返回 false,错误行号靠 __LINE__ 内建宏定位。 存档侧 CNeuronMultiModel::Save 先调基类 CNeuronBaseOCL::Save,再 FileWriteInteger 写 iModels,返回值 ≤0 即判失败。宏定义给出核索引:前馈 46、隐藏梯度 47、权重更新 48,随机选模型的下标槽分别是 def_k_chg_model=6 与 def_k_uwa_model=9。 后面冒出的 CEVD 类(继承 CNet)是「Exploration via Disagreement」的载体,成员含 iMinBufferSize、iStateEmbedingLayer、dPrevBalance 及两个布尔开关 bUseTargetNet / bTrainMode,从命名看倾向用多模型预测分歧做探索,而非直接拿期望收益驱动。外汇与贵金属行情下接这类 GPU 推理模块,延迟与过拟合风险都偏高,参数请在 MT5 策略测试器里逐项核对。

MQL5 / C++
  }
  if(!OpenCL.SetArgument(def_k_HGMultiModels, def_k_chg_outputs, Neurons() / iModels))
    {
      printf("Error of set parameter kernel calcHiddenGradients: %d; line %d", GetLastError(), __LINE__);
      class="kw">return false;
    }
  if(!OpenCL.SetArgument(def_k_HGMultiModels, def_k_chg_activation, NeuronOCL.Activation()))
    {
      printf("Error of set parameter kernel calcHiddenGradients: %d; line %d", GetLastError(), __LINE__);
      class="kw">return false;
    }
  iUpdateModel = (class="type">int)MathRound(MathRand() / class="num">32767.0 * (iModels - class="num">1));
  if(!OpenCL.SetArgument(def_k_HGMultiModels, def_k_chg_model, iUpdateModel))
    {
      printf("Error of set parameter kernel calcHiddenGradients: %d; line %d", GetLastError(), __LINE__);
      class="kw">return false;
    }
  if(!OpenCL.Execute(def_k_HGMultiModels, class="num">2, global_work_offset, global_work_size))
    {
      printf("Error of execution kernel CalcHiddenGradient: %d", GetLastError());
      class="kw">return false;
    }
class=class="str">"cmt">//---
  class="kw">return true;
  }
class="type">bool CNeuronMultiModel::Save(const class="type">int file_handle)
  {
  if(!CNeuronBaseOCL::Save(file_handle))
    class="kw">return false;
  if(FileWriteInteger(file_handle, iModels) <= class="num">0)
    class="kw">return false;
class=class="str">"cmt">//---
  class="kw">return true;
  }
class="macro">#define def_k_FFMultiModels          class="num">46 class=class="str">"cmt">///< Index of the kernel of the multi-models neuron to calculate feed forward
class="macro">#define def_k_HGMultiModels          class="num">47 class=class="str">"cmt">///< Index of the kernel of the multi-models neuron to calculate hiden gradient
class="macro">#define def_k_chg_model              class="num">6  class=class="str">"cmt">///< Number of model to calculate
class="macro">#define def_k_UWMultiModels          class="num">48 class=class="str">"cmt">///< Index of the kernel of the multi-models neuron to update weights
class="macro">#define def_k_uwa_model              class="num">9  class=class="str">"cmt">///< Number of model to update
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Exploration via Disagreement                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CEVD : class="kw">protected CNet
  {
class="kw">protected:
  class="type">uint            iMinBufferSize;
  class="type">uint            iStateEmbedingLayer;
  class="type">class="kw">double          dPrevBalance;
  class="type">bool            bUseTargetNet;
  class="type">bool            bTrainMode;
  class=class="str">"cmt">//---

◍ EVD 类的接口与推理入口

在 MT5 里落地这套嵌入式值迭代网络,核心是先看清 CEVD 类对外暴露了哪些能力。它内部挂了三张网:cTargetNet 负责稳定目标值、cReplay 做经验回放缓冲、cForwardNet 处理前向状态映射,三者由同一个类统一调度。 从声明看,feedForward 的默认窗口是 1、温度参数 tem 默认开 true,backProp 的折扣因子写死 0.999f——这意味着你不动参数的话,Agent 偏向几乎无折扣的长期回报,外汇与贵金属这种高波动品种上容易对噪声过拟合,调参时建议先压到 0.95 附近观察。 feedForward 的实现先走 AddInputData 把输入压进缓冲,再调 CNet::feedForward。训练模式下它会取 iStateEmbedingLayer 那层的输出当 state,取不到就直接返回 -1,所以 SetStateEmbedingLayer 必须在跑之前设对层数,否则整条链路拿不到状态表征。 Save/Load 有两套重载,一套合存一套分存 dqn 与 forward 网络;实盘重启想热加载策略,用分存接口更方便单独替换前向网。开 MT5 把这段类头抄进自定义 EA 的 include,先只改 iStateEmbedingLayer 和 discount 两个量做离线回测。

MQL5 / C++
  CNet                cTargetNet;
  CReplayBuffer       cReplay;
  CNet                cForwardNet;
  class="kw">virtual class="type">bool        AddInputData(CArrayFloat *inputVals);
class="kw">public:
                      CEVD();
                      CEVD(CArrayObj *Description, CArrayObj *Forward);
  class="type">bool                Create(CArrayObj *Description, CArrayObj *Forward);
                      ~CEVD();
  class="type">int                 feedForward(CArrayFloat *inputVals, class="type">int window = class="num">1, class="type">bool tem = true);
  class="type">bool                backProp(class="type">int batch, class="type">float discount = class="num">0.999f);
  class="type">int                 getAction(class="type">int state_size = class="num">0);
  class="type">float               getRecentAverageError() { class="kw">return recentAverageError; }
  class="type">bool                Save(class="type">class="kw">string file_name, class="type">bool common = true);
  class="type">bool                Save(class="type">class="kw">string dqn, class="type">class="kw">string forward, class="type">bool common = true);
  class="kw">virtual class="type">bool        Load(class="type">class="kw">string file_name, class="type">bool common = true);
  class="type">bool                Load(class="type">class="kw">string dqn, class="type">class="kw">string forward, class="type">uint state_layer, class="type">bool common = true);
  class=class="str">"cmt">//---
  class="kw">virtual class="type">int         Type(class="type">void)  const   { class="kw">return defEVD;  }
  class="kw">virtual class="type">bool        TrainMode(class="type">bool flag) { bTrainMode = flag; class="kw">return (CNet::TrainMode(flag) && cForwardNet.TrainMode(flag));}
  class="kw">virtual class="type">bool        GetLayerOutput(class="type">uint layer, CBufferFloat *&result)
   { class="kw">return         CNet::GetLayerOutput(layer, result); }
  class=class="str">"cmt">//---
  class="kw">virtual class="type">void        SetStateEmbedingLayer(class="type">uint layer) { iStateEmbedingLayer = layer; }
  class="kw">virtual class="type">void        SetBufferSize(class="type">uint min, class="type">uint max);
};
class="type">int CEVD::feedForward(CArrayFloat *inputVals, class="type">int window = class="num">1, class="type">bool tem = true)
  {
  if(!AddInputData(inputVals))
    class="kw">return -class="num">1;
class=class="str">"cmt">//---
  if(!CNet::feedForward(inputVals, window, tem))
    class="kw">return -class="num">1;
  class="type">int action = -class="num">1;
  if(bTrainMode)
   {
    CBufferFloat *state;
    class=class="str">"cmt">//if(!GetLayerOutput(class="num">1, state))
    class=class="str">"cmt">//   class="kw">return -class="num">1;
    if(!GetLayerOutput(iStateEmbedingLayer, state))
      class="kw">return -class="num">1;
把多模型分歧监控交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同预测模型在状态空间里的方差热力,你只管判断哪片分歧区值得下注。

常见问题

外在奖励往往稀少且滞后,代理者难以建立因果,加上报价噪声大,预测模型易过拟合到随机扰动,探索效率很低,需引入内在激励。
原文以若干前向动态模型预测结果之间的不一致或方差作为分歧度量,代理者偏好选择分歧最大的动作,迫使融汇收敛到更准的均值。
小布已内置多模型预测方差的观测面板,可对接你的 MT5 品种页,省去自己搭融汇与采样管线的重复劳动,你专注调参和决策。
作者实验覆盖机器人控制与雅达利等图像任务,说明在高维数据里最大化模型不确定性同样可行,只是训练成本更高。
实验显示其在收敛速度和最终策略质量上更优,且不需显式建模环境随机分布,靠融汇自身方差就能应对交互噪声。