多层感知机与反向传播算法·进阶篇
📘

多层感知机与反向传播算法·进阶篇

第 2/3 篇

感知机权重与预测函数的裸实现

下面这段 MQL5 片段给出了一个极简感知机的静态数据集与推理入口。数据集每行是 {x1, x2, label} 结构,前两组标签为 0,后五组标签为 1,分界线并不线性可分,肉眼可见 8.675 那行 x2 为负却仍标 1。 权重数组写死为 {-0.1, 0.20653640140000007, -0.23418117710000003},偏置占第 0 位。predict() 用 z = w0 + Σ w_i*x_i 做线性组合,再丢进 activation() 阶跃函数:z≥0 返回 1,否则 0。 主循环里对 7 行样本逐一打印 Expected 与 Predicted,开 MT5 把代码补完直接跑,能立刻看到哪几行被错分。外汇与贵金属行情用这类硬阈值模型极易过拟合,实盘前务必做样本外验证。 别把阶跃当万能 activation() 返回非 0 即 1,梯度恒为 0,没法直接反向传播;真要在线学习得换 sigmoid 或 ReLU 再接 SGD。

MQL5 / C++
class="type">class="kw">double weights[] = {-class="num">0.1, class="num">0.20653640140000007, -class="num">0.23418117710000003};
for(class="type">int row=class="num">0; row<ArrayRange(dataset, class="num">0); row++)
  {
  class="type">class="kw">double predict = predict(dataset, weights, row);
  printf("Expected=%.1f, Predicted=%.1f", dataset[row][nINPUT-class="num">1], predict);
  }
}
class=class="str">"cmt">// Make a prediction with weights
class="kw">template <class="kw">typename Array>
class="type">class="kw">double predict(class="kw">const Array &X[][nINPUT], class="kw">const Array &weights[], class="kw">const class="type">int row=class="num">0)
  {
  class="type">class="kw">double z = weights[class="num">0];
  for(class="type">int i=class="num">0; i<ArrayRange(X, class="num">1)-class="num">1; i++)
    {
    z+=weights[i+class="num">1]*X[row][i];
    }
  class="kw">return activation(z);
  }
class="type">class="kw">double activation(class="kw">const class="type">class="kw">double activation)
  {
  class="kw">return activation>=class="num">0.0?class="num">1.0:class="num">0.0;
  }
class="kw">template <class="kw">typename Array>
class="type">void train_weights(Array &weights[], class="kw">const Array &X[][nINPUT], class="type">class="kw">double l_rate=class="num">0.1, class="type">int n_epoch=class="num">5)
  {
  ArrayResize(weights, ArrayRange(X, class="num">1));
  for(class="type">int i=class="num">0; i<ArrayRange(X, class="num">1); i++)
    {

◍ 在 MT5 里跑通感知机权重迭代

下面这段 MQL5 脚本用固定随机种子复现了一个二分类感知机的在线学习过程。数据集含 10 行样本,前两列是特征 X1、X2,第三列是标签 Y(0 或 1),典型如区分两类价格形态簇。 训练核心是一个双层循环:外层按 n_epoch 轮迭代,内层遍历每一行样本,用当前 weights 做 predict 得到 y,再以「标签 - 预测」为 error,对偏置 weights[0] 与各特征权重做梯度更新。每轮结束用 printf 打出累计平方误差,方便你直接看误差是否随 epoch 下降。 随机种子设为 42(random.seed(42)),意味着同环境每次重跑权重初值一致,你能稳定对比调 l_rate 的效果。外汇与贵金属行情受突发事件扰动大,这类线性边界模型只适合做辅助过滤,实盘误判概率不低,务必轻仓验证。 把代码贴进 MT5 脚本的 OnStart,改 n_epoch 或 l_rate 各跑一次,观察终端里 error 曲线的收敛速度差异,比看任何文字说明都直观。

MQL5 / C++
class="macro">#define nINPUT class="num">3
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
class=class="str">"cmt">//---
   random.seed(class="num">42);
   class="type">class="kw">double dataset[][nINPUT] = {       class=class="str">"cmt">//X1      //X2   //Y  
                                 {class="num">2.7810836,class="num">2.550537003,class="num">0},
                                 {class="num">1.465489372,class="num">2.362125076,class="num">0},
                                 {class="num">3.396561688,class="num">4.400293529,class="num">0},
                                 {class="num">1.38807019,class="num">1.850220317,class="num">0},
                                 {class="num">3.06407232,class="num">3.005305973,class="num">0},
                                 {class="num">7.627531214,class="num">2.759262235,class="num">1},
                                 {class="num">5.332441248,class="num">2.088626775,class="num">1},
                                 {class="num">6.922596716,class="num">1.77106367,class="num">1},
                                 {class="num">8.675418651,-class="num">0.242068655,class="num">1},
                                 {class="num">7.673756466,class="num">3.508563011,class="num">1}};

「感知机权重训练与预测的函数实现」

这段 MT5 代码把感知机最核心的两件事拆开了:用随机梯度下降训权重,再用训好的权重做二分类预测。激活函数走的是硬阈值路线,z≥0 输出 1.0,否则 0.0,适合处理已经二值化的行情标签(如涨/跌)。 train_weights 默认学习率 0.1、迭代 5 轮(n_epoch=5)。每轮先算全样本平方误差和,再逐行更新偏置 weights[0] 与各特征权重,更新量是 l_rate * error * X[row][i]。在 MT5 里把 nINPUT 和 dataset 准备好后直接调,终端会按轮打印 >epoch=0, lrate=0.100, error=... 这样的过程日志。 predict 是纯前向:z 从偏置起算,累加 weights[i+1]*X[row][i] 后过 activation。主流程里用 ArrayPrint(weights,20) 打出 20 位精度权重,再逐行 printf 出 Expected 与 Predicted 对比——你复制进 EA 的 OnStart 跑一遍,就能看到权重收敛到什么数值、预测偏离多大。外汇与贵金属杠杆高,这套只作特征工程验证,实盘信号须自行评估概率与风险。

MQL5 / C++
class="type">class="kw">double weights[];
  train_weights(weights, dataset);
  ArrayPrint(weights, class="num">20);
  for(class="type">int row=class="num">0; row<ArrayRange(dataset, class="num">0); row++)
    {
      class="type">class="kw">double predict = predict(dataset, weights, row);
      printf("Expected=%.1f, Predicted=%.1f", dataset[row][nINPUT-class="num">1], predict);
    }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// Make a prediction with weights
class="kw">template <class="kw">typename Array>
class="type">class="kw">double predict(class="kw">const Array &X[][nINPUT], class="kw">const Array &weights[], class="kw">const class="type">int row=class="num">0)
  {
   class="type">class="kw">double z = weights[class="num">0];
   for(class="type">int i=class="num">0; i<ArrayRange(X, class="num">1)-class="num">1; i++)
     {
       z+=weights[i+class="num">1]*X[row][i];
     }
   class="kw">return activation(z);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                    Transfer neuron activation                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double activation(class="kw">const class="type">class="kw">double activation) class=class="str">"cmt">//#
  {
   class="kw">return activation>=class="num">0.0?class="num">1.0:class="num">0.0;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Estimate Perceptron weights using stochastic gradient descent   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">template <class="kw">typename Array>
class="type">void train_weights(Array &weights[], class="kw">const Array &X[][nINPUT], class="type">class="kw">double l_rate=class="num">0.1, class="type">int n_epoch=class="num">5)
  {
   ArrayResize(weights, ArrayRange(X, class="num">1));
   ArrayInitialize(weights, class="num">0);
    
   for(class="type">int epoch=class="num">0; epoch<n_epoch; epoch++)
     {
      class="type">class="kw">double sum_error = class="num">0.0;
      for(class="type">int row=class="num">0; row<ArrayRange(X, class="num">0); row++)
        {
         class="type">class="kw">double y = predict(X, weights, row);
         class="type">class="kw">double error = X[row][nINPUT-class="num">1] - y;
         sum_error += pow(error, class="num">2);
         weights[class="num">0] = weights[class="num">0] + l_rate * error;
         for(class="type">int i=class="num">0; i<ArrayRange(X, class="num">1)-class="num">1; i++)
           {
            weights[i+class="num">1] = weights[i+class="num">1] + l_rate * error * X[row][i];
           }
        }
      printf(">epoch=%d, lrate=%.3f, error=%.3f",epoch, l_rate, sum_error);
     }
  }

把单神经元叠成多层感知机

单个神经元只能拟合一条线性边界,实战里价格序列的非线性耦合它根本吃不下。把神经元按层堆叠,前一层输出直接喂给下一层输入,就构成了多层感知机(MLP)。 输入向量 X 先进初始层,该层输出作为下一层输入,逐层传递直到末层给出结果。层数加深后,网络有能力逼近越来越复杂的映射关系——这也是为什么 MT5 里的神经网络信号常做成 3 层以上而非单层。 外汇与贵金属波动受多维因子纠缠,MLP 拟合的是概率性关联而非因果,实盘使用前务必在策略测试器用历史数据做样本外验证。

◍ 给 MLP 喂带标签的数据

网络上线前得先训练,不然就是一堆随机权重。监督训练的思路很直接:你手里有输入和已知正确的输出,让模型在两者间建立映射,这和教小孩对照卡片认字是一个道理。 具体到任务类型,监督学习拆成两类。回归盯连续值,比如用房屋面积推房价,面积到价格是一条连续曲线;分类盯离散标签,把同样的房子按「是否贵过某条线」切成两类,输出就不再是数字而是类别。 做 MT5 上的行情模型时,你如果用 MLP 预测下一根收盘价,那是回归;若只判「涨还是跌」两态,就是二分类。外汇与贵金属波动剧烈、杠杆风险高,同一套网络在回测里拟合得好,实盘也可能因样本外漂移而失效,标签口径先定清再谈训练。

常见问题

权重是输入特征的线性系数,预测函数就是权重点积输入再加偏置后过激活;先用单神经元裸写跑通再谈复杂结构。
打印每轮训练后的权重向量和损失值,看数值是否随样本输入持续变动,不变说明学习率或数据标签有问题。
可以,小布能读取你的训练输出并标注权重收敛异常、激活饱和等风险,你只管调结构和数据。
不行,全零初始化会让对称神经元更新一致学不到特征,需用小的随机值打破对称。
标签维度或编码不对会导致损失算错、权重朝错误方向迭代,训练前先校验标签与输出层节点数一致。