English Русский Español Deutsch 日本語 Português
preview
您应当知道的 MQL5 向导技术(第 31 部分):选择损失函数

您应当知道的 MQL5 向导技术(第 31 部分):选择损失函数

MetaTrader 5交易系统 | 5 五月 2025, 10:14
76 0
Stephen Njuki
Stephen Njuki

概述

MQL5 向导可当作广泛思路的试验温床,正如我们在本系列中到目前为止所涵盖的那样。每次相隔一段时间,就会展示一个自定义信号,其具有多种实现方式。我们在 2 篇关于学习率、以及最后一篇关于批量归一化的文章中视察过该场景。正如所论,机器学习中的每一个层面都呈现出不止一个潜在的自定义信号。损失,也是凭借拥有多种格式,处于类似的状况。

将测试运行结果与其目标进行比较的方式并非只有单一方法。如果我们研究 MQL5 中 ENUM_LOSS_FUNCTION 枚举的可用项,它们共有 14 个,而该列表甚至并不详尽。这是否意味着它们每个都能提供独有的机器学习训练方式?大概不是,但重点是有差异,一些细微差别,这些差异往往意味着您需要根据正在训练的网络、或算法的性质仔细选择损失函数。

尽管除了损失函数之外,还可以考虑使用 ENUM_REGRESSION_METRIC,但这种与统计量关系更密切的指标并不合适,在于它的最佳用途是衡量机器学习算法训练后的性能。故此,特别是在最终输出具有多个维度的实例中,则该量度枚举将非常实用。不过,本文专注意向函数。

选择合适的损失量值至关重要,因为原则上,神经网络(本文中我们的机器学习算法)可能属于回归器相较分类器的类别,或者它们属于监督相较无监督的类型。此外,诸如强化学习的范式可能需要一种多面方式来运用,以及应用损失函数。

故此,损失函数能以各种方式应用,不仅因为有很多格式,而且还有各种各样的“问题”(神经网络类型)需要解决。在解决这些问题、或进行训练时,损失函数主要量化测试参数与其预期目标的偏差,这一过程也称为监督学习。

然而,即便配合损失函数,它们直观看似大家总是进行监督训练;无监督学习的理想损失函数的问题看似不对劲。尽管如此,即使在无监督设置中,诸如 Kohonen 映射或聚类,在衡量多维数据的间隙或距离时,也始终需要一个标准化的量值,而损失函数恰好填补了这一空白。


损失函数概览

故此,MQL5 提供了多达 14 种不同的损失函数量化方法,在考虑应用于案例之前,我们将触及所有。至于我们的目,损失用于表示损失梯度,其预期输出是向量,而非标量值。此外,实现这些公式的 MQL5 代码不会被分享,因为它们都是从内置向量函数运行的。下面给出了一个测试各种损失函数的简单脚本:

#property  script_show_inputs
input ENUM_LOSS_FUNCTION __loss = LOSS_HUBER;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
      vector _a = {1.0, 2.0, 4.0};
      vector _b = {4.0, 12.0, 36.0 };
      
      vector _loss = _a.LossGradient(_b,__loss); 
      
      printf(__FUNCSIG__+" for: "+EnumToString(__loss));
      Print(" loss gradient is: ",_loss);
      PrintFormat(" while loss is: %.5f. ",_a.Loss(_b,__loss));
  }
//+------------------------------------------------------------------+

首先是损失均方误差。这是一个广泛使用的损失函数,其目标是衡量预测值和目标值之间的平方差,从而为误差量加上一个数字。它确保误差始终为正(重点放在量级上),并且它极大地惩罚了由平方而导致的较大误差。解释也更能加无缝衔接,因为误差始终以目标变量的平方为单位。其公式呈现如下:

其中

  • n 是目标和比较变量的维度大小。典型情况下,这些变量采用矢量格式
  • i 是向量空间内的内部索引
  • y^ 是输出或预测向量
  • y 是目标向量

它的优点可能是对大误差敏感,并且由于其平滑梯度,因此适合搭配梯度下降优化方法。缺点是对异常值敏感。

接下来是损失-平均-绝对-误差函数。像是上面的 MSE,这是另一种常见的损失函数,与 MSE 一样,它专注于量级误差,且不考虑方向。与上面的 MSE 的不同之处在于,它不给大数值额外的权重,因为不涉及平方。由于未使用平方,因此误差单位与目标向量的误差单位匹配,因此解释比之 MSE 更直接。其公式类似于我们上面给出的公式如下:

其中

  • n、i、y 和 y^ 表示与上述 MSE 中的相同值

主要优点是它对异常值和大误差的敏感性较低,因为它不执行平方,并且在维护目标单位方面很简单,这有助于解释。然而,其梯度行为不如 MSE 平滑,因为在微分时,输出要么是 +1.0,要么是 -1.0 或零。这些数值之间的交替并不像使用 MSE 那样有助于训练过程顺利收敛,尤其是在回归环境中,这可能是一个问题。还有,将所有误差视同等价在某种程度上与收敛过程相悖。

这随后导致我们得出类别交叉熵。该量值,是在严格的多维空间中,预测概率分布和实际概率分布之间的差异。故此,尽管我们在 MQL5 损失函数算法中使用了 MAE 和 MSE 作为向量输出(因为个别差异并未汇总),但它们很容易当作标量,正如它们的公式所示。另一方面,类别交叉熵(CCE)始终输出多维输出。其公式给出如下:

其中

  • N 是类别的数量或数据点向量大小
  • y 是实际值或目标值
  • p 是预测值或网络输出值
  • 而 log 是自然对数

CCE 本质上是一个分类器,而非回归器,特别适用于在训练时使用多个类别对数据集进行分类的情况。这方面的主流应用是图形和图像处理,但当然,一如既往,这并不能阻止我们研究将其应用于交易者的途径。值得注意的是,尽管 CCE 特别适合与 Soft-Max 激活搭配使用,其在典型情况下还会输出一个数值向量,附带把它们的合计为一。这极大满足了寻找给定数据点的所分析类别的概率分布的意向。对数分量惩罚置信度,但不正确的预测比之较低置信度预测更严重,这主要是因为独热编码的举措。目标或真实值始终被归一化,如此仅为正确的类别给出完整的权重(其典型为 1.0),而其它所有都分配为零。CCE 在优化过程中提供平滑梯度,并且由于上述对误差预测的惩罚效应,它鼓励模型在预测当中充满信心。当训练在跨类别的不平衡种群规模中延续时,可以实现权重调整,以便平衡竞赛场所。然而,当类别过多时,存在过度拟合的危险,故在判定模型应评估的类别数量时需要采取预防措施。

接下来是二元交叉熵(BCE),其能被视为上述 CCE 的亲戚。它还按两类设置量化了预测和目标之间的间隙,不像 CCE,其更适配处理多个类别。其输出范围约束在 0.0 到 1.0,由以下公式指导:


其中

  • N 与上述其它损失函数一样,是样本大小
  • y 是预测值
  • p 是预测
  • 而 log 是自然对数

在 BCE 术语中,两个所参考类别往往称为正类别和负类别。故此,本质上,BCE 输出总是被理解为提供数据点处于正类别的概率,并且该值在 0.0 – 1.0 范围内,其相适的可配对激活函数是 hard-sigmoid 函数。内建在 MQL5 向量数据类型中的激活函数,会输出一个向量,其应包括正类别和负类别的 2 个概率。

Kullback Liebler 离散 是另一种有趣的损失函数算法,类似于我们上面看到的向量间隙方法。其公式由下式给出:

其中

  • P 是预测的概率
  • Q 是实际概率

其输出范围 0 表示没有发散,直至无穷。仅有这些正值清晰地示意预测与“真实”相距多远。如前面的公式所示,当目标以标量输出时,汇总才相关。MQL5 中内置的向量实现提供了一个向量输出,在执行反向传播时,它更适合,以及需要计算增量和最终梯度。“Kullback-Liebler 离散”建立在信息论的基础上,鉴于其灵巧性,它在强化学习、以及变分自动编码器中得到了一些应用。不过,它的缺点是不对称、对零值敏感,以及鉴于其输出的无约束性质,解释存在挑战。零值灵敏很重要,因为如果一个类别的概率为零,另一个类别会自动具有无穷值,但不对称性不仅妨碍了对给定概率的正确解释,还令转移学习更加困难。(给定 K 的 P 概率,与给定 P 的 K 概率不成反比)。正向和逆向概率之和不是预定义值。有时是无穷的,有时则不是。

这导致我们得到余弦相似性,与我们到目前为止看到的向量间隙量值不同,它参考了方向。其公式如下:

其中

  • A.B 是两个向量的点积
  • ||A|| 和 ||B|| 是它们的量级或范数

上面的公式是关于向量 A 的损失梯度。关于向量 B 是一个单独的逆公式,当汇总 A 到 B 的余弦时,结果不会是固定或任意的常数。为此,余弦相似性不是一个真正的量值,因为它不满足不等三角形。(A 至 B 的余弦相似性,加上 B 至 C 的余弦相似性,并不总是大于或等于 A 至 C 的余弦相似性)。它的优点是尺度不变性,因为它提供的值与问题中的向量量级无关,即重要的是方向,而不是量级。此外,比之其它方法,它的计算密集度更低,这是处理深度网络、或变换器、或两者兼有时的关键参考因素!已发现它对高维数据(如大型语言模型的文本嵌入)具有很高的适用性,其中推断的方向(意即?)是比每个向量值的单独量级更相关的指标。其缺点是,它并不适合所有任务,尤其是在训练时向量量级很重要的情况下。此外,如果其中一个向量的范数为零(其中所有值都为零),则余弦相似度将是未定的。最后,已经提到过,由于不满足不等式三角规则,故不能作为衡量值。其至关重要的示例可能有点古怪,但它们包括:几何深度学习、基于图的神经网络、和孪生网络的对比损失。在上述每种情况中,量级比方向更重要。不过,在 MQL5 中应用余弦相似度时,重点是要注意使用和返回的是余弦接近度,因为这与机器学习更相关。它是角度余弦的距离等效物,且它是通过从 1 中减去余弦相似度得到的。

泊松(Poisson)损失梯度函数适用于对可数或离散数据的建模。这就像上面提到的损失函数,通过向量数据类型的内置函数实现。其公式由下式给出:

其中

  • y 是目标向量值(索引 i 处)
  • y^ 是预测值

梯度值以向量格式返回,因为这更好地服务于反向传播过程。它们也是原始泊松函数标量返回公式的一阶导数,即:

其中

  • 表示很多梯度公式

在这种实例中,交易者可以投喂给神经网络的离散数据类型可能包括价格柱线蜡烛类型,或者之前的柱线是看涨、看跌、亦或持平。它的用例涵盖了计算数据的场景,故举例,可以训练一个自近期历史取各种蜡烛价格柱线形态的神经网络,从而返回标准样本(例如 10 根未来价格柱线)中预期的特定蜡烛类型的数量。它的系数很容易解释,因为它们是对数率比,并且它与泊松回归较好对齐,这意味着可以使用泊松回归轻松完成训练后的分析。它还确保计数预测始终为正(非负)。事实并非如此,故好的特征包括方差假设,其中始终假设均值和方差具有相同或几乎相似的数值。如果显然不是这种情况,那么损失函数就不会表现良好。它对异常值很敏感,尤其是那些在输入数据中较高计数的异常值,使用自然对数潜在会出现 NaN 或无效结果。此外,它的应用仅限于正值数据,这意味着需要持续负面预测的情况下,诸如在预测价格变化时,没人会用它。

Huber 梯度损失函数为我们总结了 MQL5 向量数据类型内提供的样本。还有其它类别我们尚未研究过,例如:双曲余弦的对数、分类铰链、平方铰链、铰链、均方对数误差、和平均绝对百分比误差。神经网络是回归器还是分类器,这些并不重要,其构成了我们关注的一部分,因此我们忽略了它们。然而,Huber 损失由以下公式给出:

其中

  • y^ 是预测值
  • y 是目标
  • Delta 是一个损失输入值,在该处,关系从线性变为二次

就像原始 Huber-损失,梯度可以按两种方式之一计算,具体取决于真实值或目标值与预测值的比较情况。它部分是线性函数,部分是二次函数,是目标值和预测值之间差值的映射,作为输入参数的调整。它在稳健回归中占主导地位,因为它结合了 MAE 和 MSE 的优点,对异常值不太敏感,同时对于小误差比 MAE 更稳定。由于完全可微分,这对于梯度下降十分理想,且幸好增量具有适应性,其中较小的增量令其举动像是 MAE,而较大的 增量则更像 MSE。这允许控制稳健性与灵敏度的权衡。虽然在缺点一边,Huber-损失相对更复杂,不仅其公式是分段式的,如上所示,理想增量的计算和判定往往是一项繁重的运动。最终,我认为 MQL5 实现所依据的标准矩阵和向量库,我未能参考,它未透露内置函数如何计算 Huber-损失、和 Huber-损失梯度增量值。尽管它可与各种激活函数配对,但往往建议线性激活,因为它更合适。


回归模型的损失函数

那么,这些损失算法中哪种最适合回归网络呢?答案是 MSE、MAE 和 Huber-损失。此处是原因。回归网络的特点是其预测连续数值的目标,而非类别标签或离散数据。这意味着这些网络的输出层典型情况下会产生跨越很宽范围的实数值。回归任务的性质要求在优化过程中最大限度地减少测量预测值和真实值之间大范围偏差,不像分类网络,其输出所需列举的项往往较少,并且事先知道数量。

因此,导致我们转向 MSE。如上所观,它针对大误差有较大的二次惩罚,这意味着它立即引导梯度下降,且优化朝着更窄的偏差发展,这对于回归网络高效运行非常重要。此外,平滑性和易区分性令其性质适合回归网络的连续数据处理。

回归网络也很容易受到异常值的影响,因此损失函数在处理这个问题时必须健壮。进入 MAE。不像 MSE 会对其误差施加二次惩罚,MAE 施加线性惩罚,与 MSE 相比,这令其对异常值不那么敏感。此外,它的误差量值是一个稳健的平均误差,这在嘈杂的数据中可能很实用。

最后,有争论,除了上述之外,回归网络还需要在针对小误差敏感性、和稳健性之间形成平衡或权衡机制。这是 Huber-损失函数提供的两个属性,甚至它们还提供平滑性,这有助于贯穿整个优化过程进行差异化。

若所有三个“理想”损失函数归零,在回归网络中使用它们时要参考的激活函数理想主色调是什么?官面上,这些建议是针对线性激活和一致性激活,后者意味着网络输出被保留在其量级中,以便尽可能多地捕获数据可变性。这两个的主要论点是它们输出的无约束性质,确保经由网络投喂前向进程和训练不会丢失数据。个人而言,我是有约束输出的信徒,故我宁愿选择 Soft-Sign 和 TANH,在于它们同时捕获负实数和正实数,但它们被约束在 -1.0 到 +1.0。我认为有约束输出很重要,因为它们避免了反向传播过程中梯度膨胀和消失的问题,而这些都是令人头疼的主要来源。


分类模型的损失函数

分类神经网络有关内容是什么?如何选择损失和激活函数更值当?好吧,我们于此选择的过程并没有太大区别,我们本质上是查看网络关键特征,由它们指导我们的选择。

分类网络的目的,是从预定义的可能类别池中预测离散类别标签。这些网络输出指示每个类别似然性的概率,其主要目标是通过最小化损失来最大化这些预测的准确性。因此,损失函数的选择在训练网络区分和识别类别方面扮演关键角色。基于这些关键特征,共识则是,在 MQL5 提供的列举之外,类别交叉熵和二元交叉熵是最适合分类网络的两个关键损失函数。

BCE 分为两个可能的类别,不过要进行的预测数量往往超过两个,并且该批次的大小决定了梯度向量的范数。故此,梯度向量的每个索引处的数值都是上面分享公式中高亮显示的 BCE 损失函数的偏导数,这些数值将用于反向传播。不过,网络输出值将是上述正类别的概率,并且它们将对应输出向量中的每个投影值。

BCE 适用于分类网络,因为它们都指向正类别,故概率很容易解释。它对于每个输出值的各种概率很敏感,因为它专注于最大化批次中正确类别的对数似然。因为它不能作为常数进行微分,而是一个变量,这极大地促进了反向传播中平滑高效的梯度计算。

CCE 通过允许对 2 个以上的类别进行分类扩展了 BCE,并且输出向量的范数或大小,始终是类别的数量,且每个都给定了概率。这与 BCE 不同,在 BCE 中,我们可以预测最多 5 个值,并且所有数值的每一个,既可是真,亦或是假。配以 CCE,输出大小会以匹配的类别编号为前缀。如早前所述,独热编码在衡量目标向量与预测差距之前进行归一化很实用。

因此,与此配对的理想激活形式是超出约束范围 0.0 到 +1.0 的任何函数。这包括 Soft-Max、Sigmoid 和 Hard-Sigmoid。


测试

由此,我们执行了 2 组测试,一组针对回归 MLP,另一组针对分类器。测试的目的是演示本文讨论的损失函数和激活思路在 MQL5 和智能系统形式的实现。这些呈现的测试结果并不是在任何真实账户上部署,及使用所附代码的证明,而是邀请读者在他认为合适的交易系统上,基于他的经纪商的真实报价数据进行长时间的尽职调查。如常,只有在交叉验证或前向测试得到满意结果后,达到理想后才能部署到实时设置。

故此,我们将在去年 2023 年的日线时间帧上测试 GBPCHF。为了获得回归网络,我们将引用我们在上一篇文章中介绍的 'Cmlp' 类,由于我们的输入数据将是价格变化百分比(而非点数),因此我们能配以 TANH 激活和 Huber 损失函数进行测试,看看我们的系统交易如何。自定义信号做多和做空条件的 MQL5 实现如下:

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalRegr::LongCondition(void)
{  int result = 0;
   vector _out;
   GetOutput(_out);
   m_close.Refresh(-1);
   if(_out[0] > 0.0)
   {  result = int(round(100.0*(fabs(_out[0])/(fabs(_out[0])+fabs(m_close.GetData(StartIndex()) - m_close.GetData(StartIndex()+1))))));
   }
//printf(__FUNCSIG__ + " output is: %.5f,  and result is: %i", _out[0],  result);return(0);
   return(result);
}
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalRegr::ShortCondition(void)
{  int result = 0;
   vector _out;
   GetOutput(_out);
   m_close.Refresh(-1);
   if(_out[0] < 0.0)
   {  result = int(round(100.0*(fabs(_out[0])/(fabs(_out[0])+fabs(m_close.GetData(StartIndex()) - m_close.GetData(StartIndex()+1))))));
   }
//printf(__FUNCSIG__ + " output is: %.5f,  and result is: %i", _out[0],  result);return(0);
   return(result);
}

我们的 EA 还同时依据网络训练的历史数据,及每根新价格柱线进行测试。这是另一个可以轻松更改的关键决策,其中每 6 个月进行一次训练,或其它预先确定的较长区间,以避免网络过度拟合短期动作。这个回归器网络采用 4-7-1 3 层大小(其中数字表示层大小),这意味着 4 次最近的价格变化作为输入,单个输出作为下一个价格变化。

基于 2023 年的 GBPCHF 日线数据运行测试,为我们给出以下报告:

r1

c1

对于分类器网络,我们仍然使用 'Cmlp' 类作为基础,我们的输入数据将是过去 3 个价格点的分类。这将投喂到一个 3-6-3 的简单 MLP 网络,也仅有 3 层,由于我们正在考察 3 种可能的分类,并且我们的损失函数是 CCE,故最终输出层的大小也应为 3,如此其可当作概率分布。生成做多和做空条件的 MQL5 实现如下:

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalClas::LongCondition(void)
{  int result = 0;
   vector _out;
   GetOutput(_out);
   m_close.Refresh(-1);
   if(_out[2] > _out[1] && _out[2] > _out[0])
   {  result = int(round(100.0 * _out[2]));
   }
//printf(__FUNCSIG__ + " output is: %.5f,  and result is: %i", _out[2],  result);return(0);
   return(result);
}
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalClas::ShortCondition(void)
{  int result = 0;
   vector _out;
   GetOutput(_out);
   m_close.Refresh(-1);
   if(_out[0] > _out[1] && _out[0] > _out[2])
   {  result = int(round(100.0 * _out[0]));
   }
//printf(__FUNCSIG__ + " output is: %.5f,  and result is: %i", _out[0],  result);return(0);
   return(result);
}

分类器智能系统的类似测试运行,为我们给出以下结果:

r2

c2

附加的代码经由向导组装生成的智能系统,此处此处都有指南。


结束语

总而言之,我们已走完了开发机器学习算法(如神经网络)时 MQL5 的损失函数的行程。具有讽刺意味的是,这是一个很长的清单,甚至并不详尽,不过我们已经强调了一些关键函数,这些函数与我们在之前的文章中介绍的特定激活函数配合得很好,重点是避免梯度膨胀/消失、和效率。许多可用的损失函数不一定适合典型的回归和分类器网络,不仅因为它们的输出是无约束的,还因为它们没有解决我们所强调的这些网络的关键特征要求,这就是为什么参考的损失函数数量较少。


本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15524

附加的文件 |
Cmlp-.mqh (24.72 KB)
wz_31_clas.mq5 (7.46 KB)
wz_31_regr.mq5 (7.46 KB)
因果网络分析(CNA)、随机模型最优控制(SMOC)和纳什博弈论结合深度学习的示例 因果网络分析(CNA)、随机模型最优控制(SMOC)和纳什博弈论结合深度学习的示例
我们将向之前发布的文章中的三个例子里加入深度学习,并与之前的版本进行比较。目标是学习如何将深度学习(DL)应用于其他EA。
从基础到中级:IF ELSE 从基础到中级:IF ELSE
在本文中,我们将讨论如何使用 IF 操作符及其伴随者 ELSE。这个语句是所有编程语言中最为重要且最有意义的语句。然而,尽管它易于使用,但如果我们没有使用它的经验以及与之相关的概念,它有时会令人困惑。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。
将 MQL5 与数据处理包集成 (第 2 部分):机器学习和预测分析 将 MQL5 与数据处理包集成 (第 2 部分):机器学习和预测分析
在我们关于将 MQL5 与数据处理包集成的系列文章中,我们深入研究了机器学习和预测分析的强大组合。我们将探索如何将 MQL5 与流行的机器学习库无缝连接,以便为金融市场提供复杂的预测模型。
在Python和MQL5中应用局部特征选择 在Python和MQL5中应用局部特征选择
本文探讨了Narges Armanfard等人在论文《数据分类的局部特征选择》中介绍的一种特征选择算法。该算法使用Python实现,用于构建二元分类器模型,这些模型可以与MetaTrader 5应用程序集成以进行推理。