以 MQL5 实现强化分类任务的融汇方法·进阶篇
🧩

以 MQL5 实现强化分类任务的融汇方法·进阶篇

(2/3)·组件分类器只吐离散决策时,数字组合为何失灵,以及排位输出怎么救场

实战向 第 2/3 篇
很多交易者把回归用的数值平均直接套到分类器上,结果被离散输出和置信噪声搞崩。把不同周期模型的信号强行加权,短期机会常被长期趋势掩掉。先转排位再融汇,是更稳的工程解法。

「用均匀噪声扰动输出并做归一化选优」

这段逻辑在模型推理后做一层轻量随机扰动,目的是避免每次都卡在同一个输出索引上。核心做法是遍历输出数组 m_output,对每个元素加 0.999 倍的均匀随机数,再记录扰动后最大的那个索引。 循环里 temp = m_output[i] + 0.999 * CAlglib::HQRndUniformR(state) 这行决定了噪声强度,系数 0.999 意味着扰动幅度接近原输出量级,而非微小抖动。若 i==0 或 temp 超过当前 best,就更新 best 与 ibest,同时把原值累加进 sum。 循环结束后,当 sum>0.0 时对 m_output 整体除以 sum 做归一化,最后返回 ibest。外汇与贵金属市场高风险,该扰动可能让选出的信号在样本外更分散,但绝不保证盈利。 把系数从 0.999 调到 0.1 或 2.0,在 MT5 里跑同一段,能直接看出输出分布是被压窄还是被拉散。

MQL5 / C++
for(class="type">ulong i=class="num">0 ; i<m_output.Size() ; i++)
    {
      temp = m_output[i] + class="num">0.999 * CAlglib::HQRndUniformR(state);
      if((i == class="num">0)  || (temp > best))
        {
         best = temp ;
         ibest = i ;
        }
      sum += m_output[i] ;
    }
   if(sum>class="num">0.0)
      m_output/=sum;
   class="kw">return ibest;
   }

把组件模型输出当数字因子来均化

当每个组件模型吐出的数值既有意义、又能跨模型比较时,直接把这些量值合并,往往比多数投票或 Borda 计数更能榨干数据。后者丢掉了大部分可用信息,而均化思路是取每个类别在所有组件模型里输出的中位数——模型数量固定时,这在数学上等价于把输出全加起来。本质上,它是把每个分类模型当成数值预测器,用最朴素的 averaging 拼到一起,最后看哪类聚合输出最高就判给谁。 分类任务和数值预测在这里有个坑:数值模型通常共用训练目标,输出天然对齐;分类模型往往只有排位有意义,原始输出可能根本不可比。这种错位会让合并悄悄变成隐式加权均值,而不是真平均,个别模型可能对总和产生不成比例的影响,反过来伤了融汇效果。所以上 MT5 前,先验证各组件输出尺度是否一致,是保住元模型完整性的前提。 有人假设组件输出就是概率,于是引出乘积规则——把加法换成连乘。听着优雅,实则脆弱:只要有一个模型把某类概率严重低估到接近零,乘进去后这类就被不可逆地罚死了。这种对概率假设违反的极度敏感,让乘积规则在实盘里基本不实用,更适合当反面教材。 下面的 CAvgClass 就是均化规则的进化实现,结构镜像 CMajority。classify() 从所有组件收预测并累加,最终类取累积得分最高者。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| full resolution&class="macro">#x27; version of majority rule.                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class  CAvgClass
  {
class="kw">private:
   class="type">ulong               m_outputs;
   class="type">ulong               m_inputs;
   vector              m_output;
class="kw">public:
                     CAvgClass(class="type">void);
                    ~CAvgClass(class="type">void);
   class="type">ulong               classify(vector &inputs, IClassify* &models[]);
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| make classification with consensus model                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">ulong CAvgClass::classify(vector &inputs, IClassify* &models[])
  {
   m_output=vector::Zeros(models[class="num">0].getNumOutputs());
   vector model_classification;
   for(class="type">uint i =class="num">0 ; i<models.Size(); i++)
     {
      model_classification = models[i].classify(inputs);
      m_output+=model_classification;
     }
   class="type">class="kw">double sum = m_output.Sum();
   class="type">ulong min = m_output.ArgMax();
   m_output/=sum;
   class="kw">return min;
  }

◍ 用排位中值压住极端预测

多模型集成时,均值融汇会把异常预测直接拉偏,尤其外汇与贵金属这类高波动品种,单根极端 K 线就可能让某个子模型输出离群值,进而污染整体信号。中位数融汇的思路是:先对每个模型的输出独立排位,再取各类排位的中值,极端值即便存在也只影响排位边缘,不会主导结果。 这种基于排位的转换在保留类间相对次序的同时,削弱了对异常值的敏感度。实测中,当预测分布偏斜或不对称时,中值法比简单平均更稳,代价仅是信息利用率略有下降。 ensemble 里的 CMedian 类把这套逻辑封装好了,核心在 classify 方法:先把各模型输出按列塞进矩阵,再逐行取中值。下面代码可直接拷进 MT5 看结构。 别把正态当圣经 真实盘面里预测分布常带厚尾,中值融汇不是万灵药,但是当你的用例里『偶尔极端预测』比『每次都用满信息』更致命时,它往往更划算。外汇与贵金属交易杠杆高、风险大,任何集成方案都只是概率倾斜,不等于方向锁定。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  median of predications                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CMedian
  {
class="kw">private:
   class="type">ulong                m_outputs;
   class="type">ulong                m_inputs;
   vector               m_output;
   matrix               m_out;
class="kw">public:
                     CMedian(class="type">void);
                    ~CMedian(class="type">void);
   class="type">ulong                classify(vector &inputs, IClassify* &models[]);
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| constructor                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CMedian::CMedian(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| destructor                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CMedian::~CMedian(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| consensus classification                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">ulong CMedian::classify(vector &inputs,IClassify *&models[])
  {
  m_out = matrix::Zeros(models[class="num">0].getNumOutputs(),models.Size());
  vector model_classification;
  for(class="type">uint i = class="num">0;i<models.Size(); i++)
   {
     model_classification = models[i].classify(inputs);
     if(!m_out.Col(model_classification,i))
      {
       Print(__FUNCTION__, "  ", __LINE__, " failed row insertion ", GetLastError());
       class="kw">return ULONG_MAX;
      }
   }
  m_output = vector::Zeros(models[class="num">0].getNumOutputs());
  for(class="type">ulong i = class="num">0;i<m_output.Size(); i++)
   {
     vector row = m_out.Row(i);
     if(!row.Size())
      {
       Print(__FUNCTION__," ", __LINE__," empty vector ");

「中位数合成与概率归一的实现尾段」

这段收尾逻辑把每行排序后的中位数塞进 m_output 数组,再对全体输出求和并用 ArgMax 定位权重最大的列索引。若 sum 大于 0,则整体除以 sum 完成归一,使各列输出转化为相对概率分布,最后返回概率最高的索引。 外汇与贵金属市场的高风险在于:这种归一结果只反映历史样本的统计倾向,实盘里信号失效的概率并不低,任何一次调用都应先在 MT5 策略测试器里跑多品种回测。 直接把上面这段贴在自定义指标的最后,改一行 row.Size() 的入参就能验证不同窗口长度下中位数的稳定性。

MQL5 / C++
      class="kw">return ULONG_MAX;
      }
   qsortd(class="num">0,row.Size()-class="num">1,row);
   m_output[i] = row.Median();
   }
  class="type">class="kw">double sum = m_output.Sum();
  class="type">ulong mx = m_output.ArgMax();

  if(sum>class="num">0.0)
      m_output/=sum;
  class="kw">return mx;
  }

模型专精与排错:MaxMax 与 MaxMin 的取舍

组件模型融汇里,单个模型常被训练成只吃透某几类样本。在它擅长的领域,输出置信度会明显冲高;碰到专业外的类,往往吐出 0.4~0.6 这种不上不下的中间值,信息量很低。 MaxMax 规则按「所有模型中对每个类的最大输出」来定类,等于只听最自信的那一声。它适合模型高度专精、中等输出纯属噪音的架构,但二次输出本身带分析价值的场景会失效——这点容易被忽略。 反过来,有些模型专长是「排除」而非「认出」。某类样本进来时,融汇里至少有一个模型会对错误类给出极低分,等于帮你在矩阵里划掉干扰项。MaxMin 规则取每个类在所有模型里的最小输出做归属判断,正好利用这种异常排错能力。 落地前先摸清楚自己的融汇形态:用 MaxMax 就确认模型真有专业分工、中等输出确为噪音、且类别覆盖完整;用 MaxMin 则核对异常排错能否覆盖全部误分类可能,别留缝隙。 下方是 MaxMax 类的核心实现,classify 里先建零向量,再逐模型取各类最大输出,最后 ArgMax 出最优类、Sum 归一。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|Compute the maximum of the predictions                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CMaxMax
  {
class="kw">private:
   class="type">ulong                m_outputs;
   class="type">ulong                m_inputs;
   vector               m_output;
   matrix               m_out;
class="kw">public:
                     CMaxMax(class="type">void);
                    ~CMaxMax(class="type">void);
   class="type">ulong               classify(vector &inputs, IClassify* &models[]);
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|   constructor                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
CMaxMax::CMaxMax(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|   destructor                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
CMaxMax::~CMaxMax(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|   ensemble classification                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">ulong CMaxMax::classify(vector &inputs,IClassify *&models[])
  {
   class="type">class="kw">double sum;
   class="type">ulong ibest;
   m_output = vector::Zeros(models[class="num">0].getNumOutputs());
   for(class="type">uint i  = class="num">0; i<models.Size(); i++)
     {
       vector classification = models[i].classify(inputs);
       for(class="type">ulong j = class="num">0; j<classification.Size(); j++)
         {
           if(classification[j] > m_output[j])
             m_output[j] = classification[j];
         }
     }
   ibest  = m_output.ArgMax();
   sum  = m_output.Sum();
   if(sum>class="num">0.0)
     m_output/=sum;
   class="kw">return ibest;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Compute the minimum of the predictions                            |
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 集成分类里的极小投票实现

把多个分类模型的结果做集成,不一定非要取平均。CMaxMin 这个类走的是另一条路:对每一个类别,只保留所有模型给出的最小置信度,相当于一种保守的『极小投票』机制。 核心在 classify 方法里。它先拿第一个模型的分类向量初始化 m_output,之后遍历其余模型,逐元素取 min,所以 m_output[j] 最终等于所有模型在第 j 类上的最低打分。 得到合成向量后,用 ArgMax 挑出置信最低的类别里相对最高的那个作为 ibest 返回;若总和大于 0 再做一次归一化。这种写法在模型间分歧大时倾向压低整体置信,外汇与贵金属行情受突发事件扰动频繁,此类保守集成可能降低假信号概率,但同样会拖慢响应,实盘前请在 MT5 策略测试器用历史数据验证分歧样本下的表现。 下面的代码可直接拷进 MQ5 文件编译,注意 IClassify 是外部模型接口,需你自行实现 classify(vector&) 返回各类别打分向量。

MQL5 / C++
class CMaxMin
  {
class="kw">private:
   class="type">ulong                m_outputs;
   class="type">ulong                m_inputs;
   vector               m_output;
   matrix               m_out;
class="kw">public:
                     CMaxMin(class="type">void);
                    ~CMaxMin(class="type">void);
   class="type">ulong               classify(vector &inputs, IClassify* &models[]);
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  constructor                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CMaxMin::CMaxMin(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  destructor                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CMaxMin::~CMaxMin(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  ensemble classification                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">ulong CMaxMin::classify(vector &inputs,IClassify *&models[])
  {
   class="type">class="kw">double sum;
   class="type">ulong ibest;
   for(class="type">uint i  = class="num">0; i<models.Size(); i++)
     {
      vector classification = models[i].classify(inputs);
      if(i == class="num">0)
        m_output = classification;
      else
        {
         for(class="type">ulong j = class="num">0; j<classification.Size(); j++)
          if(classification[j] < m_output[j])
            m_output[j] = classification[j];
        }
     }
   ibest  = m_output.ArgMax();
   sum  = m_output.Sum();
   if(sum>class="num">0.0)
     m_output/=sum;
   class="kw">return ibest;
  }
把多模型排位交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同分类器的排位对比与融汇信号,你只管判断是否跟单。

常见问题

排位在类数多时保留次要选择信息,概率若被误用会放大风险;三类以内排位增益有限,多类场景排位更抗不确定。
长周期模型在短周期预测里引入噪声,平均会掩盖高流动性市场的短期见解,转排位可缓解归一化冲突。
只关心真实类是否在最小子集里用缩减;需要利用次要类置信做风控或加权就用完整排位意向。
可以,对应品种页已内置多分类器排位与融汇视图,省去自己写 CTrade 和信号合成的重复劳动。
MaxMax 偏信最强信号适合趋势明确,MaxMin 取最保守交叉确认适合贵金属高波动防假突破。