数据科学和机器学习(第 21 部分):解锁神经网络,优化算法揭秘(基础篇)
📘

数据科学和机器学习(第 21 部分):解锁神经网络,优化算法揭秘(基础篇)

第 1/3 篇

◍ 从特征工程到架构设计的抽象跃迁

做神经网络不是把线性的手工特征堆上去就完事。想让模型在 MT5 里真正跑出信号,你得把精力从「挑指标、造特征」挪到「设计网络结构、损失函数、优化制程」这三件事上。 这意味着原来的苦力活并没消失,只是被抬到了更高的抽象层。原文作者提到,以前花在特征设计上的所有努力,现在转移到了架构设计、损失函数设计和优化制程设计——手工劳动被提升到一个更高的抽象层次。 对外汇与贵金属交易者而言,这种转移直接抬高了门槛:你不再只需要会写几个 iMA、iRSI,还得懂怎么在 MQL5 里搭前向传播与反向传播逻辑。这类策略回测周期长、过拟合概率高,属高风险尝试,建议先用历史数据小样本验证再上实盘。

「为什么交易者该懂一点优化算法」

AI 在交易圈的热度不是空谈。MetaTrader 5 引入矩阵、向量和 ONNX 支持后,社区里直接跑复杂神经网络模型已经没有门槛,不需要你啃完线性代数才能上手。 但真正稀缺的是机器学习的底层基本面。知道优化器为什么这么跑,你才不会在调参时瞎蒙,面对不同品种和周期也能自己换路子。 今天这一节只聊优化算法:它们彼此怎么较劲,你什么场景下该选哪一个,才能让神经网络的性能和准确度更稳。 这些知识不挑语言。哪怕你之后用 Python 的 Scikit-Learn、Tensorflow 或 Pytorch 搭模型,优化器的逻辑是通用的,在 MT5 里想明白的事一样用得上。

优化器怎么动神经网络的权重

优化器的本职是在训练过程中调整神经网络的可学习参数,目标是把损失函数压到更低,从而让模型在外汇或贵金属价格序列上的拟合更稳。它每轮迭代都会比较真实值与网络预测值的差,再沿误差梯度反方向去改权重,改多少、往哪改由优化器决定。 具体到更新节奏,原文里区分了三种基础算法:随机梯度下降(SGD)每次拿单样本算梯度,批量梯度下降(BGD)用全样本集,小批量梯度下降则折中按一小批数据来。对 MT5 上跑的行情模型来说,BGD 在tick数据多时算力吃紧,SGD 抖动大,小批量更常被采用。 外汇与贵金属杠杆高、滑点突发的特性,意味着哪怕优化器把回测误差降到很低,实盘仍可能因流动性断裂而偏离。开 MT5 用之前文章搭好的网络接个 SGD,对比损失曲线就能直观看到优化器在起作用。

◍ SGD 怎么逐个样本啃权重

随机梯度下降是神经网络训练里最基础的优化器,核心思路是拿损失函数衡量预测值和真实标签的差距,再反推去改网络的权重和偏置。它和批量梯度下降、小批量梯度下降干的是同一件事——迭代、反向传播、更新参数,区别只在「每次拿多少数据来更新」。SGD 每次只丢一个样本进网络,算完梯度立刻改参数。 这种单样本更新的做法对大型数据集更省算力,遇到非凸损失函数时,因为更新带噪声,反而容易从局部最小值里跳出来,倾向找到更接近全局最小的解。但代价是训练轨迹会呈现之字折线,且对学习率极度敏感:调高了可能越过最优点,调低了收敛回合数会被拖长。 下面这段 MT5 里的反向传播骨架,把 SGD 的「逐样本」逻辑写得很直白:外层 epoch 控制总轮数,中间 iter 循环遍历每一行数据(即每个样本),内层再从最后一层往前逐层算梯度并更新。开 MT5 把这段塞进你自己的 CRegressorNets 类,把 optimizer_weights 换成 SGD 实现,就能直接跑通单样本更新。 别把学习率当常量 实盘接 SGD 训模型时,学习率别写死。EURUSD 这类高波动品种的特征分布会漂移,固定低学习率可能让模型在样本后期收敛过慢;可先设 0.01 试跑,观察损失曲线的折线幅度再手动减半或加倍。外汇与贵金属杠杆高,模型信号仅作概率参考,实盘需严控仓位。

MQL5 / C++
class="type">void CRegressorNets::backpropagation(const matrix& x, const vector &y)
 {

  for (class="type">class="kw">ulong epoch=class="num">0; epoch<epochs && !IsStopped(); epoch++)
    {

      for (class="type">class="kw">ulong iter=class="num">0; iter<rows; iter++) class=class="str">"cmt">//iterate through all data points
        {
          for (class="type">int layer=(class="type">int)mlp.hidden_layers-class="num">1; layer>=class="num">0 && !IsStopped(); layer--) class=class="str">"cmt">//Loop through the network backward from last to first layer
            {
              class=class="str">"cmt">// find partial derivatives of each layer WRT the loss function dW and dB


              class=class="str">"cmt">//--- Weights updates

              optimizer_weights.update(W, dW);
              optimizer_bias.update(B, dB);

              this.W_tensor.Add(W, layer);
              this.B_tensor.Add(B, layer);
            }
    }
  }
 }

「BGD 为什么在 MT5 里基本没人用」

批量梯度下降(BGD)和随机梯度下降(SGD)的核心差别在于:每次迭代它都用整个数据集来算梯度,而不是抽一条样本就更新。 理论上,当损失函数满足平滑且凸这两个条件时,BGD 能稳定收敛到全局最小值;这一点在教科书里很漂亮,但在实盘特征工程里极少成立。 真正劝退交易者的是算力账:假设你用 MT5 跑 5 年 EURUSD 的 M1 收盘价序列(约 130 万根 K 线)做一层线性回归特征的梯度更新,BGD 每迭代一次就要把全量数据过一遍,十几次迭代就能让脚本卡到没法盯盘。 原文作者没在现有神经网络里实装 BGD,只说可以像小批量梯度下降那样轻松补上——如果你要在 MT5 里验证,直接把小批量的 batch size 设成样本总数即可,但别对回测速度有期待。外汇与贵金属杠杆高、滑点乱,这类实验仅作算法理解,实盘风险极大。

小批量梯度下降的折衷与回测表现

小批量梯度下降站在 SGD 与 BGD 中间:每次迭代只取训练集的一小块(小批量)去更新参数。它比 SGD 更新更稳,又比 BGD 能扛更大的数据集,代价是批量规模要手动调,且内存与计算开销随批量增大而上升。 实测一组对照:学习率都设 0.0001,BGD 批量规模 = 16。SGD 约第 10 回合逼近局部最小值,BGD 到第 20 回合才接近;训练与验证准确率上 SGD 约 60%,BGD 训练 15%、验证 13%。这不代表 BGD 更差,可能只是学习率或批量规模没调到该数据集的甜点。 绘图时损失值常套一层 log10,纯粹是为把早期巨大的损失压进图表视野,真实数值在智能选项卡里看,别被曲线平滑度骗了。BGD 曲线平滑是因为总损失是各独立批量损失的均值,训练过程倾向稳定。 把 SGD 优化器想成木匠,反向传播里用的 SGD 或小-BGD 算法就是他工具箱里那把专削神经网络梯度的凿子。下面这段 MT5 里的反向传播骨架,能直接看清小批量是怎么切进去的。 void CRegressorNets::backpropagation(...) 是入口,batch_size 默认 0 即退化为纯 SGD;一旦传入大于 0 的数就激活小批量循环。 内层 for(uint batch...) 按 batch_size 从 x、y 里切片 batch_x、batch_y,再对每行样本算预测与真值、求 dW/dB,最后用 optimizer_weights.update(W,dW) 和 optimizer_bias.update(B,dB) 落权重。 外汇与贵金属模型训练属高风险实验,回测准确率不等于实盘胜率,参数请先在策略测试器小样本验证。

MQL5 / C++
class="type">void CRegressorNets::backpropagation(const matrix& x, const vector &y, OptimizerSGD *sgd, const class="type">uint epochs, class="type">uint batch_size=class="num">0, class="type">bool show_batch_progress=class="kw">false)
{
class=class="str">"cmt">//....
	for (class="type">class="kw">ulong epoch=class="num">0; epoch<epochs && !IsStopped(); epoch++)
		{
					
				for (class="type">uint batch=class="num">0, batch_start=class="num">0, batch_end=batch_size; batch<num_batches; batch++, batch_start+=batch_size, batch_end=(batch_start+batch_size-class="num">1))
				 {
					matrix batch_x = MatrixExtend::Get(x, batch_start, batch_end-class="num">1);
					vector batch_y = MatrixExtend::Get(y, batch_start, batch_end-class="num">1);
					
					rows = batch_x.Rows();								
					
						for (class="type">class="kw">ulong iter=class="num">0; iter<rows ; iter++) class=class="str">"cmt">//replace to rows 
						 {
							pred_v[class="num">0] = predict(batch_x.Row(iter));
							actual_v[class="num">0] = y[iter];
							
							class=class="str">"cmt">// Find derivatives WRT weights dW and bias dB				
							class=class="str">"cmt">//....									
							
							class=class="str">"cmt">//--- Updating the weights using a given optimizer
							
							 optimizer_weights.update(W, dW);
							 optimizer_bias.update(B, dB);
							
							 this.Weights_tensor.Add(W, layer);

常见问题

建议先理解从特征到架构的抽象跃迁,再碰网络设计,否则容易堆层数却不知为何。先用简单模型验证特征有效,再考虑神经网络。
优化算法决定权重怎么更新,直接影响模型收敛和过拟合概率。懂基础能帮你判断策略回测曲线异常是数据问题还是训练问题。
小布可以替你梳理优化器输出的权重变化与回测表现,标注可能的过拟合区间。把重复诊断交给小布,你专注调参决策。
SGD 每样本更新噪声大、收敛慢但可能逃出局部最优。实盘上信号抖动更频繁,需配合止损和仓位控制应对高风险。
小批量在收敛稳定性和速度间折衷,回测上方差通常小于 SGD、慢于 BGD。具体表现随品种和窗口变化,建议自己跑一遍验证。