数据科学与机器学习(第 06 部分):梯度下降·综合运用
📉

数据科学与机器学习(第 06 部分):梯度下降·综合运用

(3/3)·从成本函数到逻辑回归,用梯度下降跑通一套可复用的模型训练闭环

新手友好 第 3/3 篇
很多交易者一听到梯度下降就跳去调库,却没弄清学习曲率设多大才不跳过极小值。先把成本函数想成模型犯错的账单,优化就是逼着账单数字往下走,这一步没通后面全是黑箱。

梯度下降在 EURUSD M1 上的数值爆炸

在 EURUSD 的 M1 周期上跑了一轮最朴素的梯度下降拟合,日志里打印的迭代轨迹很说明问题:从第 8 步到第 11 步,截距项 b0 从约 3.29e10 一路翻转到 -8.46e12,斜率项 B1 从 2.22e11 膨胀到 -5.70e13,每一步量级都在翻好几番。 成本函数的梯度 cost_B0、cost_B1 同样失控,第 11 步时 cost_B1 已经到 6.60e14 这个离谱规模。这说明学习率或者特征缩放没处理好的话,裸跑梯度下降在高频外汇数据上会直接数值发散。 外汇与贵金属属高风险品种,这种未收敛的回归系数没有任何实盘信号意义,只能当作算法稳定性的反面教材。开 MT5 把这段日志对应的 EA 跑一遍,把学习率调小三个数量级,看第 50 步是否还能保持有限值。

MQL5 / C++
MP        class="num">0     class="num">17:class="num">28:class="num">14.819    gradient-descent test(EURUSD,M1)      class="num">8 b0 = class="num">32871653967.62362671 cost_B0 = -class="num">380393068240.57440186 B1 = class="num">221513298448.70788574 cost_B1 = -class="num">2563367875191.31982422
MM        class="num">0     class="num">17:class="num">28:class="num">14.819    gradient-descent test(EURUSD,M1)      class="num">9 b0 = -class="num">209097460110.12799072 cost_B0 = class="num">2419691140777.51611328 B1 = -class="num">1409052343513.33935547 cost_B1 = class="num">16305656419620.47265625
HD        class="num">0     class="num">17:class="num">28:class="num">14.819    gradient-descent test(EURUSD,M1)      class="num">10 b0 = class="num">1330075004152.67309570 cost_B0 = -class="num">15391724642628.00976562 B1 = class="num">8963022367351.18359375 cost_B1 = -class="num">103720747108645.23437500
DP        class="num">0     class="num">17:class="num">28:class="num">14.819    gradient-descent test(EURUSD,M1)      class="num">11 b0 = -class="num">8460645083849.12207031 cost_B0 = class="num">97907200880017.93750000 B1 = -class="num">57014041694401.67187500 cost_B1 = class="num">659770640617528.50000000

◍ 把序列压进 0~1 的 MinMax 套路

做梯度下降前,先把特征塞进 [0,1] 区间最省事:用 (x-min)/(max-min) 逐点缩放,量纲差异就被抹平了。 下面这段 MQL5 把缩放和标准差核算封装成类方法,跑完直接 printf 出均值和 std,方便你肉眼判断分布是否被压扁。 实盘日志里能看到:X 缩放后 Mean=0.44823、Std=0.29683,Y 缩放后 Mean=0.45207、Std=0.31838;Std 没到 0.5 说明原序列偏态不明显,归一化后梯度更新会稳一些。外汇和贵金属波动高危,拿这组函数套行情前先想清楚样本窗口长度。 别把正态当圣经:MinMax 只管边界,不保证中间均匀,Std 仅作参考而非分布合格的证明。

MQL5 / C++
class="type">void CGradientDescent::MinMaxScaler(class="type">class="kw">double &Array[])
{
   class="type">class="kw">double mean = Mean(Array);
   class="type">class="kw">double max,min;
   class="type">class="kw">double Norm[];
   
   ArrayResize(Norm,ArraySize(Array));
   
   max = Array[ArrayMaximum(Array)];   min = Array[ArrayMinimum(Array)];
   
    for (class="type">int i=class="num">0; i<ArraySize(Array); i++)
        Norm[i] = (Array[i] - min) / (max - min);
   
    printf("Scaled data Mean = %.5f Std = %.5f",Mean(Norm),std(Norm));
   
    ArrayFree(Array);
    ArrayCopy(Array,Norm);
}
class="type">class="kw">double CGradientDescent::std(class="type">class="kw">double &data[])
{
   class="type">class="kw">double mean = Mean(data);
   class="type">class="kw">double sum = class="num">0;
   
    for (class="type">int i=class="num">0; i<ArraySize(data); i++)
        sum += MathPow(data[i] - mean,class="num">2);
   
    class="kw">return(MathSqrt(sum/ArraySize(data)));  
}
class="type">void OnStart()
  {
class=class="str">"cmt">//---
    class="type">class="kw">string filename = "Salary_Data.csv";
    class="type">class="kw">double XMatrix[];
    class="type">class="kw">double YMatrix[];
    
    
    grad = new CGradientDescent(class="num">1, class="num">0.01,class="num">1000);
    
    grad.ReadCsvCol(filename,class="num">1,XMatrix);
    grad.ReadCsvCol(filename,class="num">2,YMatrix);
    
    grad.MinMaxScaler(XMatrix);
    grad.MinMaxScaler(YMatrix);
    
    ArrayPrint("Normalized X",XMatrix);
    ArrayPrint("Normalized Y",YMatrix);
    
    
    grad.GradientDescentFunction(XMatrix,YMatrix,MSE);
    
    class="kw">delete (grad);
  }

「梯度下降在 EURUSD M1 上的归一化输出」

下面这段 MT5 专家日志来自一次梯度下降测试,品种为 EURUSD、周期 M1,时间戳 18:50:53.387。ER 行给出 15 个归一化后的预测值,从 0.4043 单调爬升到 1.0000,说明模型在该样本窗口内对价格的拟合呈单向递增倾向。 NQ 行标注 Normalized Y,代表真实值做了同样归一化;IF 行是输入特征 [0] 的 15 个量,范围 0.0000~0.3155,波动杂乱无规律。IS 行是另一组输入缩放值,末端 1.0000 与 0.9939 贴近上限。 把 ER 和 IS 对照看:当 IS 从 0.3568 走到 1.0000 时,ER 同步从 0.4043 到 1.0000,两者相关系数可能偏高。但 IF 这路特征明显没被模型当成主驱动,外汇与贵金属这类高杠杆品种用梯度下降拟合微周期,过拟合概率不低,开 MT5 把这段日志复现一遍最实在。

MQL5 / C++
ER       class="num">0    class="num">18:class="num">50:class="num">53.387   gradient-descent test(EURUSD,M1)      [class="num">15] class="num">0.4043 class="num">0.4255 class="num">0.4468 class="num">0.5106 class="num">0.5213 class="num">0.6064 class="num">0.6383 class="num">0.7234 class="num">0.7553 class="num">0.8085 class="num">0.8404 class="num">0.8936 class="num">0.9043 class="num">0.9787 class="num">1.0000
NQ       class="num">0    class="num">18:class="num">50:class="num">53.387   gradient-descent test(EURUSD,M1)      Normalized Y
IF       class="num">0    class="num">18:class="num">50:class="num">53.387   gradient-descent test(EURUSD,M1)      [ class="num">0] class="num">0.0190 class="num">0.1001 class="num">0.0000 class="num">0.0684 class="num">0.0255 class="num">0.2234 class="num">0.2648 class="num">0.1974 class="num">0.3155 class="num">0.2298 class="num">0.3011 class="num">0.2134 class="num">0.2271 class="num">0.2286 class="num">0.2762
IS       class="num">0    class="num">18:class="num">50:class="num">53.387   gradient-descent test(EURUSD,M1)      [class="num">15] class="num">0.3568 class="num">0.3343 class="num">0.5358 class="num">0.5154 class="num">0.6639 class="num">0.6379 class="num">0.7151 class="num">0.7509 class="num">0.8987 class="num">0.8469 class="num">0.8015 class="num">0.9360 class="num">0.8848 class="num">1.0000 class="num">0.9939

逻辑模型的梯度怎么从交叉熵里抠出来

线性回归里那套梯度下降流程在逻辑模型上基本能复用,差别只在成本函数换了形态。分类任务里我们盯的是二元交叉熵(BCE,也叫对数损失),它对权重求偏导后,梯度表达式会收敛成「(真实值-预测值)×自变量」的累加结构,截距项则省掉自变量乘子。 下面这段 MQL5 把 BCE 梯度直接写成类方法。注意它从矩阵第 2 列取自变量 x,按 Slope / Intercept 分支累计误差,最后乘 -1/m 返回平均梯度: double CGradientDescent::Bce(double Bo,double B1,Beta wrt) { double sum_sqr=0; double m = ArraySize(Y); double x[]; MatrixColumn(m_XMatrix,x,2); if (wrt == Slope) for (int i=0; i<ArraySize(Y); i++) { double Yp = Sigmoid(Bo+B1*x[i]); sum_sqr += (Y[i] - Yp) * x[i]; } if (wrt == Intercept) for (int i=0; i<ArraySize(Y); i++) { double Yp = Sigmoid(Bo+B1*x[i]); sum_sqr += (Y[i] - Yp); } return((-1/m)*sum_sqr); } 逐行拆解:Bo 是截距、B1 是斜率、wrt 指定求哪个参数的梯度;m 用 ArraySize(Y) 拿到样本数;MatrixColumn 把特征矩阵第 2 列拽进 x 数组。Slope 分支里每个样本算 Sigmoid(Bo+B1*x[i]) 得到预测概率 Yp,误差乘 x[i] 累加;Intercept 分支只累加误差本身。末尾 -(1/m) 把总和变成均值梯度,符号与上升方向相反,供下降更新用。 实测用泰坦尼克号的 Pclass(乘客舱位)预测 Survived,前 4 轮日志显示:b0 从 -0.0116 走到 -0.0325,B1 从 -0.0406 走到 -0.1263,对应的 cost_B1 从 0.4057 降到 0.2367。分类数据没做标准化,但梯度依然在收敛,说明逻辑回归对轻度量纲差异不像线性回归那么敏感。 开 MT5 把 titanic.csv 的 3 列读进 X、2 列读进 Y,直接调 GradientDescentFunction(XMatrix,YMatrix,BCE),就能复现上面这组递减曲线。外汇贵金属行情若套类似二分类思路,记得标的波动带杠杆,过拟合倾向会放大实盘风险。

MQL5 / C++
class="type">class="kw">double CGradientDescent::Bce(class="type">class="kw">double Bo,class="type">class="kw">double B1,Beta wrt)
{
  class="type">class="kw">double sum_sqr=class="num">0;
  class="type">class="kw">double m = ArraySize(Y);
  class="type">class="kw">double x[];
  MatrixColumn(m_XMatrix,x,class="num">2);
  if (wrt == Slope)
    for (class="type">int i=class="num">0; i<ArraySize(Y); i++)
    {
      class="type">class="kw">double Yp = Sigmoid(Bo+B1*x[i]);
      sum_sqr += (Y[i] - Yp) * x[i];
    }
  if (wrt == Intercept)
    for (class="type">int i=class="num">0; i<ArraySize(Y); i++)
     {
       class="type">class="kw">double Yp = Sigmoid(Bo+B1*x[i]);
       sum_sqr += (Y[i] - Yp);
     }
  class="kw">return((-class="num">1/m)*sum_sqr);
}

◍ 梯度下降在 EURUSD M1 上的收敛轨迹

把线性回归的梯度下降直接跑在 EURUSD 的 M1 周期上,前几步的权重还飘在负值区:第 4 次迭代 b0 = -0.03609603、B1 = -0.14619252,到 第 10 次迭代 b0 仍为负(-0.03882570)、B1 降到 -0.21692190,cost_B1 从 0.199 收敛到 0.078。 真正稳定发生在第 6935 次附近。日志显示 b0 跳到 1.44678930、B1 定格在 -0.85010666,cost_B0 与 cost_B1 都已压到 0.00000001 级别,此后 6936–6938 次迭代数值不再跳动。 这说明在 M1 这种噪声极高的品种周期上,梯度下降能收敛,但迭代量可能到数千次才企稳。外汇与贵金属属高风险品种,此类拟合结果仅反映历史样本,实盘泛化能力需自行在 MT5 中用不同品种和周期复跑验证。

「梯度下降在 EURUSD M1 上收敛到局部极小」

上面这段 MT5 专家日志来自 EURUSD 的 M1 周期,跑的是一次梯度下降拟合测试。从迭代 6939 到 6945,截距 b0 稳定在 1.44678931,斜率 B1 停在 -0.85010666,对应的 cost_B0 与 cost_B1 都已逼近 0(末段 cost_B0 为 -0.00000000)。

日志最后一行由 NG 线程打出:Iterations Local Minima are,紧接着 MJ 线程输出 B0(Intercept) = 1.44679B1(Coefficient) = -0.85011。这说明算法判定已落入局部极小,而非全局最优——在外汇高频数据上,梯度下降很容易卡在这类平坦区。

开 MT5 把这段测试脚本挂到 EURUSD M1,观察 Print 迭代号到 6945 附近是否也出现 cost 全零。若你换品种或周期,B1 符号和量级可能明显不同,EURUSD M1 这一拟合结果不代表其他货币对倾向。 外汇与贵金属杠杆高、滑点随机,这类数值实验仅用于理解优化器行为,实盘信号须另做样本外验证。

MQL5 / C++
PK      class="num">0    class="num">07:class="num">19:class="num">09.726   gradient-descent test(EURUSD,M1)    class="num">6939 b0 = class="num">1.44678931 cost_B0 = -class="num">0.00000001 B1 = -class="num">0.85010666 cost_B1 = class="num">0.00000000
RK      class="num">0    class="num">07:class="num">19:class="num">09.726   gradient-descent test(EURUSD,M1)    class="num">6940 b0 = class="num">1.44678931 cost_B0 = -class="num">0.00000001 B1 = -class="num">0.85010666 cost_B1 = class="num">0.00000000
MJ      class="num">0    class="num">07:class="num">19:class="num">09.726   gradient-descent test(EURUSD,M1)    class="num">6941 b0 = class="num">1.44678931 cost_B0 = -class="num">0.00000001 B1 = -class="num">0.85010666 cost_B1 = class="num">0.00000000
HI      class="num">0    class="num">07:class="num">19:class="num">09.726   gradient-descent test(EURUSD,M1)    class="num">6942 b0 = class="num">1.44678931 cost_B0 = -class="num">0.00000001 B1 = -class="num">0.85010666 cost_B1 = class="num">0.00000000
CH      class="num">0    class="num">07:class="num">19:class="num">09.726   gradient-descent test(EURUSD,M1)    class="num">6943 b0 = class="num">1.44678931 cost_B0 = -class="num">0.00000001 B1 = -class="num">0.85010666 cost_B1 = class="num">0.00000000
MH      class="num">0    class="num">07:class="num">19:class="num">09.727   gradient-descent test(EURUSD,M1)    class="num">6944 b0 = class="num">1.44678931 cost_B0 = -class="num">0.00000001 B1 = -class="num">0.85010666 cost_B1 = class="num">0.00000000
QG      class="num">0    class="num">07:class="num">19:class="num">09.727   gradient-descent test(EURUSD,M1)    class="num">6945 b0 = class="num">1.44678931 cost_B0 = -class="num">0.00000000 B1 = -class="num">0.85010666 cost_B1 = class="num">0.00000000
NG      class="num">0    class="num">07:class="num">19:class="num">09.727   gradient-descent test(EURUSD,M1)    class="num">6945 Iterations Local Minima are
MJ      class="num">0    class="num">07:class="num">19:class="num">09.727   gradient-descent test(EURUSD,M1)    B0(Intercept) = class="num">1.44679  ||  B1(Coefficient) = -class="num">0.85011

把这条线请下神坛

单自变量梯度下降跑通之后,多变量版本其实没那么玄——把标量求导换成向量和矩阵运算即可,MQL5 自 2023 版起内置的 matrix 标准库已经把乘法、转置、求逆封装到位,直接 #include <Matrix.mqh> 就能在 MT5 里验证多维下降轨迹。 前文 41.03 KB 的 Gradient_Descent.zip 里只给了单维示例,真要做多变量回归,自己补一个 3×3 系数矩阵、用 matrix::Mul 算梯度,学习率先锁 0.01 观察收敛步数,比盯视频直观。 外汇与贵金属杠杆高、滑点随机,回测收敛不等于实盘能复现,矩阵跑出来的权重只是概率倾向,别当成确定性信号。

把重复迭代交给小布
这些诊断与小布盯盘的 AIGC 已内置,打开对应品种页即可看到参数敏感度和训练曲线,你只需判断信号是否值得跟。

常见问题

梯度下降用于最小化成本函数找最优参数,梯度上升可用来最大化似然或收益类目标;外汇贵金属波动高,两种都可能因学习曲率不当而失效。
把特征缩到相近区间能避免某些维度梯度过大,令下降路径更平稳,概率上减少迭代次数,但不会改变局部极小本身的存在。
逻辑函数饱和区梯度趋近零,若初始点落进去更新极慢;倾向用较小学习曲率配合多次迭代来缓解这个现象。
小布内置的 AIGC 可展示品种页上的参数诊断与训练参考,把重复劳动交给小布,你专注决策,具体模型训练仍在本地或自有环境完成。
0.01 只是常见默认,贵金属跳空多时可能跳过极小,倾向先用网格搜参再固定,实盘前用历史分段验证。