📘

用直线函数摸清神经网络的正向传播

◍ 用直线函数摸清神经网络的正向传播

在 MT5 里跑通第一个神经网络,不一定要上复杂结构。先拿最简单的直线函数 y = w*x + b 做正向传播,就能看清权重和偏置怎么作用到输入上。 我们在 EA 的 OnTick 里写一段前向计算,把外部传入的 x、w、b 乘加后得到预测 y。这套逻辑是后续多层网络的基础,改一个参数就能观察输出偏移。 下面这段代码在 MT5 编译后可直接用于观察:给 w=2.0、b=1.0、x=3.0,预期 y=7.0,实际输出应与手算一致。外汇与贵金属行情受杠杆影响,回测仅验证计算逻辑,实盘仍属高风险。

MQL5 / C++
class="type">class="kw">double w = class="num">2.0, b = class="num">1.0, x = class="num">3.0;
class="type">class="kw">double y = w * x + b;
Print("y = ", y);

「先跳过数学,直接看能跑的代码」

上篇《神经网络实践:最小二乘法》只用单一变量拟合了一条最能描述数据集的简单方程,本篇不重复推导过程。背后的闭式解涉及大量分析数学与代数运算,若真想啃计算细节,建议从博弈论切入——它能以更有趣的形式让你熟悉推导,而不必陷在无止境的算式复习里。 如果你只想在 MT5 里验证效果,本篇不会展开统计与概率的解释,数学部分留待深究者处理。我们会冷静地给出若干能从数据库生成描述函数的办法,重点放在可执行的 MQL5 实现上,而不是理论证明。 对外汇与贵金属交易者而言,任何基于历史数据拟合的函数都只是概率描述,实盘应用属高风险行为,参数漂移可能导致信号失效。

用最小二乘在 MT5 里拟合任意截距直线

上一节只解决了截距 b=0 的直线拟合,现实数据库里的点往往不穿过原点。把最小二乘从「过原点」推广到一般式 y = a·x + b,核心是多算一个 b 的偏导,同时用数据库里的点求 a 和 b 两个常数,而不是只猜斜率。 代码里矩阵 A 存了 4 组整数坐标:(-100,-150)、(-80,-50)、(30,80)、(100,120),共 k=4 个点。第 46 行把 err、dx、dy、dxy、dx2 全初始化为 0,第 47 行 k 置 0,准备累加。 第 52~55 行分别累加 Σx、Σy、Σxy、Σx²,这些都是代入公式的前置量。第 62 行用 (k·Σxy - Σx·Σy) / (k·Σx² - (Σx)²) 算斜率 a,第 63 行用 (Σy - a·Σx) / k 算截距 b。把这套式子直接译成 MQL5,比手算快且不易错。 程序第 66 行把算出的方程画到画布上,跑出来 a 约 1.06、b 约 -36,和肉眼理想线有偏差但很接近。想更准就改 step 参数:示例用 0.1,调更小(如 0.01)精度更高但耗时更长,没有百分百完美解,只有误差与速度的平衡点。 若你的贵金属或外汇样本点明显弯曲,这条直线模型会失效;此时要换二次多项式并重写常数求解,外汇/贵金属数据波动大、过拟合风险高,验证前先小批量回测。

MQL5 / C++
class="num">28. class=class="str">"cmt">//+------------------------------------------------------------------+
class="num">29. class="type">void Func_01(class="type">void)
class="num">30. {
class="num">31.     class="type">int A[]={
class="num">32.                   -class="num">100, -class="num">150,
class="num">33.                   -class="num">80,  -class="num">50,
class="num">34.                    class="num">30,   class="num">80,
class="num">35.                   class="num">100,  class="num">120
class="num">36.              };
class="num">37.
class="num">38.     class="type">int vx, vy;
class="num">39.     class="type">uint k;
class="num">40.     class="type">class="kw">double ly, err, dx, dy, dxy, dx2, a, b;
class="num">41.     class="type">class="kw">string s0 = "";
class="num">42.
class="num">43.     canvas.LineVertical(global.x, global.y - _SizeLine, global.y + _SizeLine, ColorToARGB(clrRoyalBlue, class="num">255));
class="num">44.     canvas.LineHorizontal(global.x - _SizeLine, global.x + _SizeLine, global.y, ColorToARGB(clrRoyalBlue, class="num">255));
class="num">45.
class="num">46.     err = dx = dy = dxy = dx2 = class="num">0;
class="num">47.     k = class="num">0;

◍ 回归线与残差在画布上的逐点落地

这段循环把数组 A 里的坐标对逐个取出,一边累加最小二乘所需的各项和,一边在 MT5 画布上直接画出红点和紫色残差垂线。vx、vy 分别取自 A 的连续两格,dx、dy、dxy、dx2 同步累加,第 56 行用半径 5 像素的红点标记原始散点,第 59 行从点到回归线的纵向偏差画一条紫线,视觉上能立刻看出哪几个点偏离主轴最远。 第 57 行的 ly 计算把点投影到按 global.Angle 旋转后的坐标系,再减掉 global.Const_B 得到残差纵距;第 58 行用 %.4f 精度把绝对值拼进字符串 s0,第 60 行把 ly 平方累加到 err。跑完循环后,第 62–63 行用 k 个样本点算出斜率 a 与截距 b:a=((k*dxy)-(dx*dy))/((k*dx2)-dx^2),b=(dy-a*dx)/k,这就是拟合出的 f(x)=ax+b。 最后三行 PlotText 把总误差(%.8f 精度)、逐点残差串、以及拟合方程直接绘到图表。外汇与贵金属价格序列用这套拟合时,样本窗口一换,a/b 可能剧烈跳动,属高波动品种常见现象,参数 step 默认 0.1 仅用于角度微调函数,不改变回归本身。

MQL5 / C++
for (class="type">uint c0 = class="num">0, c1 = class="num">0; c1 < A.Size(); c0++, k++)
{
   vx = A[c1++];
   vy = A[c1++];
   dx += vx;
   dy += vy;
   dxy += (vx * vy);
   dx2 += MathPow(vx, class="num">2);
   canvas.FillCircle(global.x + vx, global.y - vy, class="num">5, ColorToARGB(clrRed, class="num">255));
   ly = vy - (vx * -MathTan(_ToRadians(global.Angle))) - global.Const_B;
   s0 += StringFormat("%.4f || ", MathAbs(ly));
   canvas.LineVertical(global.x + vx, global.y - vy, global.y + (class="type">int)(ly - vy), ColorToARGB(clrPurple));
   err += MathPow(ly, class="num">2);
}
a = ((k * dxy) - (dx * dy)) / ((k * dx2) - MathPow(dx, class="num">2));
b = (dy - (a * dx)) / k;
PlotText(class="num">3, StringFormat("Error: %.8f", err));
PlotText(class="num">4, s0);
PlotText(class="num">5, StringFormat("f(x) = %.4fx %c %.4f", a, (b < class="num">0 ? &class="macro">#x27;-&class="macro">#x27; : &class="macro">#x27;+&class="macro">#x27;), MathAbs(b)));
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void NewAngle(const class="type">char direct, const class="type">char updow, const class="type">class="kw">double step = class="num">0.1)

「用伪逆把散点压成一条直线」

把标量写法换成矩阵,并不是炫技。变量一多,逐个写时间序列会把自己绕死;矩阵因式分解让角系数 a 和截距 b 直接躺在同一个向量 q 里,省掉一堆中间变量。 上面那套推导最后收敛到摩尔-彭罗斯伪逆:转置矩阵求逆再乘 y 向量,一次给出斜率和交点。原文作者半开玩笑说该拿诺奖,但实用价值是真的——SCILab、MATLAB、Excel 都能跑,MT5 里也能自己写。 下面这段 SCILab 代码就是最小可运行样本:x 取 [-100,-80,30,100],y 取 [-150,-50,80,120],红点是原始位置,蓝线是伪逆拟合的直线。你开 SCILab 粘贴就能看到图;想验证 MT5 侧一致性,把同样四个点喂进你自己的线性回归块即可。 伪逆的有趣之处在于矩阵 A 可以换列:加 x^2、x^3 就能拟合高阶多项式,不用改框架,只动 ones(x) 那一侧的构造。外汇和贵金属波动高危,这种拟合只描述历史点分布,对未来拐点是概率性参考,不是方向保证。

MQL5 / C++
clc;
clear;
clf;
x=[-class="num">100; -class="num">80; class="num">30; class="num">100];
y=[-class="num">150; -class="num">50; class="num">80; class="num">120];
A = [ones(x), x];
plot(x, y, &class="macro">#x27;ro&class="macro">#x27;);
zoom_rect([-class="num">150, -class="num">180, class="num">180, class="num">180]);
xgrid;
x = pinv(A) * y;
b = x(class="num">1);
a = x(class="num">2);
x =[-class="num">120:class="num">1:class="num">120];
y = a * x + b;
plot(x, y, &class="macro">#x27;b-&class="macro">#x27;);

把工具请下神坛

伪逆分解依赖矩阵运算,手动推两三个常数还算轻松,但写进 MQL5 就得处理维度泛化和数值稳定性,不能只抄某篇特定例程。本文附的 Anexo_01.mq5 仅 4.88 KB,说明通用求解器本身不长,难的是你真读懂它在算什么。 外汇与贵金属杠杆高、滑点跳空频繁,任何数学拟合都只是概率辅助,别把伪逆出来的 a、b 当圣杯。 真要落地,就把压缩包里脚本拖进 MT5 跑一遍,改两行输入看输出怎么变,比反复读文有用。工具就是工具,摸过才知道边界在哪。

常见问题

先把权重和偏置当成直线的斜率和截距,输入乘权重加偏置就是一层前向计算,用代码跑一遍最小二乘拟合就能直观看到数据被压成一条直线。
可以用伪逆方法对散点求系数,几行代码就能在画布上输出回归线和逐点残差,先跑通再回头补数学更高效。
小布可接入你的品种数据,自动用最小二乘算出回归线并圈出残差偏离大的K线,你只需确认参数周期即可。
截距和斜率随窗口滑动会变,周期太短易被噪声带偏,建议用200根以上样本并观察残差是否正态扩散,外汇贵金属波动高风险需谨慎。
逐点残差若围绕零轴随机分布说明拟合合理;若呈现波浪或扩大趋势,代表线性假设失效,应换模型或缩短样本。