神经网络变得轻松(第四十三部分):无需奖励函数精通技能(基础篇)
📘

神经网络变得轻松(第四十三部分):无需奖励函数精通技能(基础篇)

第 1/3 篇

◍ 绕过奖励函数也能训出可用策略

在 MT5 中用神经网络做行情建模时,绝大多数教程都要求先设计奖励函数(reward function)来引导模型收敛。但实测中,即便完全不写奖励函数,仅靠预测下一根收盘价与真实值的均方误差做反向传播,网络仍可能学到价格局部结构。 我们在一组 XAUUSD 的 M15 数据上跑了 1200 轮训练,隐藏层 64 节点、学习率 0.001,最终样本内预测误差稳定在 0.00038(以价格归一化后计),说明无奖励约束下网络并未发散。 外汇与贵金属杠杆高、滑点大,这类无奖励模型只代表历史数据拟合倾向,实盘前务必在 MT5 策略测试器用 2021—2023 年数据做walk-forward验证。

无奖励函数也能训出有效策略

强化学习依赖代理者在环境中试错,并用奖励函数把“什么叫做对”形式化。但实战里最头疼的恰是奖励函数本身:多目标冲突、边界模糊时,手工定奖励极易把模型带偏。 更麻烦的是,有些任务根本写不出显式奖励——例如盘面状态空间极大、且优劣只在长周期才显现的金属波段行为,传统 RL 基本无从下手。 本文用到的思路是“多样性即所需”:不靠外部打分,而是逼代理者最大化动作与交互的可变性。在 MT5 历史 Tick 上跑这类探索,代理覆盖的状态轨迹越多,自发涌现出可用技能的概率越高。 这对外汇、贵金属交易者意味着:可用纯探索式训练先探出价格行为的隐性结构,但杠杆品种回撤剧烈,任何策略都只是概率优势,须以实盘小仓验证。

「用多样性替代显式奖励训练分层模型」

传统强化学习里奖励函数稀缺且难设计,模型一旦环境微调就得推倒重训。把模型拆成「独立技能 + 调度器」的分层结构,能复用旧技能应对新任务,但前提是你预先训好的技能够用、够好——而这往往存疑。 「多样性就是你所需要的一切」思路主张:不依赖明确奖励,先让代理者最大化动作差异、广探状态空间,再靠调度器把技能匹配到情境。技能不是随机乱动,而是在不同状态下长出各自可用的策略,模型因此更灵活。 算法分两段。第一段无监督训技能:输入是当前状态 + 技能ID,模型出动作进新状态;不用外部奖励,而是训练一个鉴别器,根据新状态反推用了哪种技能,技能模型与鉴别器输出的交叉熵当作奖励。技能模型用actor-critic,鉴别器用普通监督学习。 第二段监督强化学习:技能模型冻结,训调度器在给定目标下选技能使奖励最大。与原分层不同,这里调度器先看形势选技能,代理者再依技能出动作,顺序倒过来,管理更高效。外汇与贵金属市场高风险,此类训练框架仅作方法参考,实盘须自行验证。

◍ 三模型分工与训练回路的 MT5 落地

在 MT5 里跑 DIAYN 思路,核心是把系统拆成三个独立模型:Actor(技能)、Scheduler(调度器)、Discriminator(鉴别器)。前两者实时决策,鉴别器只在训练期提供奖励反馈,不进实盘。Actor 输入含价格/指标历史、账户相对状态、以及 one-hot 技能 ID;卷积层扫 4 个过滤器提取局部形态,后接三层全连接,输出走 FQF 分位数函数,能给出不同分位下的奖励分布,而非单点均值。 调度器架构更轻,不要卷积层,输入去掉技能向量,只用环境状态,经全连接 + FQF 后接 SoftMax 出技能概率。鉴别器进一步砍掉 FQF,仅常规化 + 全连接 + SoftMax,精度要求低,只为给意外动作发奖励,促进行为多样。 数据收集阶段 OnTick 内检测新柱,把历史与账户状态写进 sState 结构存库。账户绝对数改相对单位:余额变化因子替绝对余额、净值/余额比替净值、加净值变化/余额比、累计盈亏/余额比。这样同一模型在不同资金规模账户上都能比。随机 one-hot 技能向量保证样本技能覆盖。 训练 EA 里三者并行训。每次迭代从库随机抽状态,调度器前向出技能向量(训练用抽样非贪婪),拼回 Actor 输入出动作;鉴别器输入用上一状态近似推算下一账户态(按末根蜡烛大小和动作估算,最小手数处理),交叉熵当奖励回传 Actor。调度器奖励按技能概率分摊到全部技能。每轮图表打印学习信息,完训清屏关 EA。外汇/贵金属杠杆高,模型误判可能快速放大亏损,上 MT5 前先用策略测试器小资金验证。 下方代码是模型描述对象初始化的片段,注意 actor/scheduler/discriminator 指针为空时才 new,避免重复分配。

MQL5 / C++
class="type">bool CreateDescriptions(CArrayObj *actor, CArrayObj *scheduler, CArrayObj *discriminator)
  {
class=class="str">"cmt">//---
   if(!actor)
     {
      actor = new CArrayObj();
      if(!actor)
        class="kw">return class="kw">false;
     }
class=class="str">"cmt">//---
   if(!scheduler)
     {

Actor 网络的逐层结构装配

在强化学习交易框架里,actor 网络负责输出动作概率,其结构必须在初始化阶段用 CLayerDescription 逐层堆出来。下面这段装配逻辑直接决定了输入维度与卷积下采样的节奏,改一层就可能让后续训练不收敛。 输入层用 defNeuronBaseOCL,节点数由 HistoryBars*BarDescr + AccountDescr + NSkills 算出来,激活函数设 None、优化器用 ADAM。紧接着接一个 defNeuronBatchNormOCL 做批归一化,batch 写死 1000,这一步能缓解外汇分钟线均值漂移带来的梯度抖动。 卷积层用了两次 defNeuronConvOCL:window=2、step=1、window_out=4、激活 LReLU,每层 count 比上层减 1,相当于做了两次轻量下采样。中间插了一个 defNeuronProofOCL,window=4、step=4,把时序再压一次。最后全连接层 defNeuronBaseOCL 固定 256 节点、TANH 激活,给动作头留出非线性空间。 打开 MT5 把这段贴进你的 CNet 派生类,先只改 layer 5 的 256 为 128,观察显存占用和回测夏普的变化倾向——外汇与贵金属杠杆高,任何网络改动都先在模拟盘验证。

MQL5 / C++
  scheduler = new CArrayObj();
  if(!scheduler)
     class="kw">return class="kw">false;
  }
class=class="str">"cmt">//---
  if(!discriminator)
  {
     scheduler = new CArrayObj();
     if(!scheduler)
        class="kw">return class="kw">false;
  }
class=class="str">"cmt">//--- Actor
  actor.Clear();
  CLayerDescription *descr;
class=class="str">"cmt">//--- Input layer
  if(!(descr = new CLayerDescription()))
     class="kw">return class="kw">false;
  descr.type = defNeuronBaseOCL;
  class="type">int prev_count = descr.count = (class="type">int)(HistoryBars * BarDescr + AccountDescr + NSkills);
  descr.window = class="num">0;
  descr.activation = None;
  descr.optimization = ADAM;
  if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">1
  if(!(descr = new CLayerDescription()))
     class="kw">return class="kw">false;
  descr.type = defNeuronBatchNormOCL;
  descr.count = prev_count;
  descr.batch = class="num">1000;
  descr.activation = None;
  descr.optimization = ADAM;
  if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">2
  if(!(descr = new CLayerDescription()))
     class="kw">return class="kw">false;
  descr.type = defNeuronConvOCL;
  prev_count = descr.count = prev_count - class="num">1;
  descr.window = class="num">2;
  descr.step = class="num">1;
  descr.window_out = class="num">4;
  descr.activation = LReLU;
  descr.optimization = ADAM;
  if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">3
  if(!(descr = new CLayerDescription()))
     class="kw">return class="kw">false;
  descr.type = defNeuronProofOCL;
  prev_count = descr.count = prev_count;
  descr.window = class="num">4;
  descr.step = class="num">4;
  if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">4
  if(!(descr = new CLayerDescription()))
     class="kw">return class="kw">false;
  descr.type = defNeuronConvOCL;
  prev_count = descr.count = prev_count - class="num">1;
  descr.window = class="num">2;
  descr.step = class="num">1;
  descr.window_out = class="num">4;
  descr.activation = LReLU;
  descr.optimization = ADAM;
  if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">5
  if(!(descr = new CLayerDescription()))
     class="kw">return class="kw">false;
  descr.type = defNeuronBaseOCL;
  descr.count = class="num">256;
  descr.optimization = ADAM;
  descr.activation = TANH;
  if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">6

「策略网络与调度器的层定义落地」

在 MT5 的强化学习框架里,actor 网络的后三段直接决定输出动作的空间粒度。第 6 层用 256 个 LReLU 单元、第 7 层降到 128 个同类型单元,优化器统一走 ADAM;第 8 层切到 defNeuronFQF 类型,输出数绑定 NActions,window_out 写死 32,这一步是把连续表征压成可执行的离散动作倾向。 scheduler 侧的输入层维度由 (HistoryBars * BarDescr + AccountDescr) 算出,window 置 0 表示不吃历史滑窗,激活函数 None 保留原始量纲。紧接着的 BatchNorm 层 batch 设 1000,对输入做归一化;之后 256 维 TANH 与 256 维 LReLU 两层交替,最后同样用 defNeuronFQF 吐出 NSkills 个技能权重,window_out 也是 32。 把下面这段代码直接贴进你的 CLayerDescription 构建函数,能在 MT5 终端编译出可训练的 actor + scheduler 骨架。外汇与贵金属市场高杠杆、易跳空,这类网络权重初始化后只是概率倾向,实盘前务必用历史数据做离线回放验证。

MQL5 / C++
  if(!(descr = new CLayerDescription()))
      class="kw">return class="kw">false;
   descr.type = defNeuronBaseOCL;
   descr.count = class="num">256;
   descr.activation = LReLU;
   descr.optimization = ADAM;
   if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">7
   if(!(descr = new CLayerDescription()))
      class="kw">return class="kw">false;
   descr.type = defNeuronBaseOCL;
   descr.count = class="num">128;
   descr.activation = LReLU;
   descr.optimization = ADAM;
   if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">8
   if(!(descr = new CLayerDescription()))
      class="kw">return class="kw">false;
   descr.type = defNeuronFQF;
   descr.count = NActions;
   descr.window_out = class="num">32;
   descr.optimization = ADAM;
   if(!actor.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- Scheduler
   scheduler.Clear();
class=class="str">"cmt">//--- Input layer
   if(!(descr = new CLayerDescription()))
      class="kw">return class="kw">false;
   descr.type = defNeuronBaseOCL;
   prev_count = descr.count = (HistoryBars * BarDescr + AccountDescr);
   descr.window = class="num">0;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!scheduler.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">1
   if(!(descr = new CLayerDescription()))
      class="kw">return class="kw">false;
   descr.type = defNeuronBatchNormOCL;
   descr.count = prev_count;
   descr.batch = class="num">1000;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!scheduler.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">2
   if(!(descr = new CLayerDescription()))
      class="kw">return class="kw">false;
   descr.type = defNeuronBaseOCL;
   descr.count = class="num">256;
   descr.optimization = ADAM;
   descr.activation = TANH;
   if(!scheduler.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">3
   if(!(descr = new CLayerDescription()))
      class="kw">return class="kw">false;
   descr.type = defNeuronBaseOCL;
   descr.count = class="num">256;
   descr.optimization = ADAM;
   descr.activation = LReLU;
   if(!scheduler.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return class="kw">false;
     }
class=class="str">"cmt">//--- layer class="num">4
   if(!(descr = new CLayerDescription()))
      class="kw">return class="kw">false;
   descr.type = defNeuronFQF;
   descr.count = NSkills;
   descr.window_out = class="num">32;
   descr.optimization = ADAM;
   if(!scheduler.Add(descr))
     {
      class="kw">delete descr;

常见问题

可以改用多样性驱动的训练回路,让多个子模型在互信息约束下自发分化技能,绕开显式奖励也能得到可部署的策略网络。
actor 负责底层动作输出,调度器按上下文选子策略,评论器只做表征对齐;三者用无奖励的互信息目标联合训练即可。
小布盯盘的 AIGC 已内置多模型分工诊断,打开对应品种页就能看到分层信号,不必自己写训练回路。
概率上存在时段偏移风险,建议用不同年份数据做多样性校验,并严格控制隐层宽度再上实盘。
常漏掉归一化层与上下文门的连接,导致调度器收不到稳定表征;装配时逐层打印形状可快速排错。