English Русский 日本語 Português
preview
群体自适应矩估计(ADAM)优化算法

群体自适应矩估计(ADAM)优化算法

MetaTrader 5示例 |
86 0
Andrey Dik
Andrey Dik

内容

  1. 概述
  2. 算法实现
  3. 测试结果


概述

在机器学习领域,数据量正在迅猛增长,算法也日益复杂,而优化在取得卓越成果方面发挥着关键作用。在众多试图解决这一问题的算法中,自适应矩估计(ADAM,Adaptive Moment Estimation)算法凭借其稳定性和高效性脱颖而出。

2014年,两位杰出人才D. P. Kingma和J. Ba提出了ADAM算法,该算法融合了其前身(如AdaGrad算法和RMSProp算法)的优点。此算法是专门为利用神经元激活函数的梯度来优化神经网络权重而设计的。它基于自适应的一阶和二阶矩估计,不仅实现简单,而且计算效率极高。该算法所需的内存资源极少,且不依赖于梯度的对角缩放,这使其特别适用于数据量和参数庞大的问题。

ADAM算法在非平稳目标和梯度可能存在噪声或稀疏的情况下也表现出色。该算法的超参数易于解释,通常无需复杂的调优。

然而,尽管ADAM算法在神经网络领域效率极高,但它仅限于使用解析梯度,这限制了其应用范围。在本文中,我们提出了一种创新方法,通过将ADAM算法转变为一种能够处理数值梯度的基于群体的优化算法,对其进行改进。这一改进不仅将ADAM算法的应用范围扩展到了神经网络之外,还为解决各种优化问题开辟了新的可能性。

我们的研究旨在创建一种通用优化器,它既能保留原始ADAM算法的优点,又能在无法获取解析梯度的情况下有效运行。这将使改进后的ADAM算法能够应用于全局优化和多目标优化等领域,从而显著拓展了其潜力和实用价值。


算法实现

ADAM算法通常被归类为一种基于随机梯度的优化方法。然而,值得注意的是,ADAM算法的核心逻辑中并不包含任何内部随机元素。与ADAM算法相关的随机性实际上源自数据的准备和输入算法的方式,而非其内部机制。区分数据准备中的随机性与优化算法本身的确定性是非常重要的。

ADAM算法本身是完全确定性的。在相同的输入数据和初始条件下,它总是会产生相同的结果。ADAM算法中的参数是根据明确界定的方程进行更新的,这些方程中不包含随机元素。

区分ADAM算法的确定性本质与为其准备数据的随机性,对于正确理解其运行机制和修改潜力至关重要。认识到这一点,就为将ADAM算法适配到那些不希望或无法使用随机准备数据的场景提供了机会,同时仍能保留其强大的优化特性。

让我们来看看带有方程的伪代码:

1. 初始化:
m₀ = 0(一阶矩初始化)
v₀ = 0(二阶矩初始化)
t = 0(步数计数器)

2. 每一步 t:
   t = t + 1
gₜ = ∇ₜf(θₜ₋₁)(计算梯度)

3. 更新偏差修正后的一阶和二阶矩:
   mₜ = β₁ · mₜ₋₁ + (1 - β₁) · gₜ
   vₜ = β₂ · vₜ₋₁ + (1 - β₂) · gₜ²

   m̂ₜ = mₜ / (1 - β₁ᵗ)
   v̂ₜ = vₜ / (1 - β₂ᵗ)

4. 参数更新:
   θₜ = θₜ₋₁ - α · m̂ₜ / (√v̂ₜ + ε)

其中:
θₜ — 第 t 步时的模型参数
f(θ) —目标函数
α — 学习率(通常取0.001)
β₁、β₂ — 一阶和二阶矩的衰减系数(通常 β₁ = 0.9,β₂ = 0.999)
ε — 防止除0的小常数(通常取 10⁻⁸)
mₜ、vₜ — 梯度的一阶矩和二阶矩估计
m̂ₜ、v̂ₜ — 修正后的一阶矩和二阶矩估计

上述公式总结了ADAM算法的核心:它根据梯度的一阶和二阶矩估计,自适应地为每个参数调整学习率。正如我们所见,该算法本身没有任何的随机性。通常情况下,ADAM 算法在各种软件实现中都被紧密地嵌入到神经网络架构之中。然而,在本文中,我们将施展一点“魔法”:不仅要让它成为独立而自洽的个体,还要把它转化为一种群体化、真正随机的方法。

首先,我们需要以群体形式实现ADAM,在保留其原始方程的同时,仅在待优化参数的初始初始化阶段引入随机元素。但这仅仅是开始!随后,我们将把随机性引入这一梯度方法的动力学行为中,看看会带来怎样的结果。

我们定义S_Gradients结构,用于存储梯度以及两个矩向量(一阶和二阶)。通过Init(int coords) 方法设定数组大小,并将它们初始化为0。

//——————————————————————————————————————————————————————————————————————————————
// Structure for storing gradients and moments
struct S_Gradients
{
    double g [];  // Gradients
    double m [];  // Vectors of the first moment
    double v [];  // Vectors of the second moment

    // Method for initializing gradients
    void Init (int coords)
    {
      ArrayResize (g, coords);
      ArrayResize (m, coords);
      ArrayResize (v, coords);

      ArrayInitialize (g, 0.0); // Initialize gradients to zeros
      ArrayInitialize (m, 0.0); // Initialize the first moment with zeros
      ArrayInitialize (v, 0.0); // Initialize the second moment with zeros
    }
};
//——————————————————————————————————————————————————————————————————————————————

C_AO_ADAM类实现ADAM优化算法。该类的主要功能包括:

  1. 构造函数(Constructor) 用于初始化算法参数,如种群规模、学习率及衰减率。
  2. SetParams () 函数用于从params数组中设置参数值,从而允许在初始化后对这些参数进行修改。
  3. Init() 函数通过接收参数范围和迭代次数(epoch数)来为算法的运行做准备。
  4. Moving ()Revision () 这两个函数旨在执行优化步骤、更新模型参数并检查算法状态。

//——————————————————————————————————————————————————————————————————————————————
class C_AO_ADAM : public C_AO
{
  public: //--------------------------------------------------------------------

  // Class destructor
  ~C_AO_ADAM () { }

  // Class constructor
  C_AO_ADAM ()
  {
    ao_name = "ADAM";                                   // Algorithm name
    ao_desc = "Adaptive Moment Estimation";             // Algorithm description
    ao_link = "https://www.mql5.com/en/articles/16443"; // Link to the article

    popSize = 50;       // Population size
    alpha   = 0.001;    // Learning ratio
    beta1   = 0.9;      // Exponential decay ratio for the first moment
    beta2   = 0.999;    // Exponential decay ratio for the second moment
    epsilon = 1e-8;     // Small constant to prevent division by zero

    // Initialize the parameter array
    ArrayResize (params, 5);
    params [0].name = "popSize"; params [0].val = popSize;
    params [1].name = "alpha";   params [1].val = alpha;
    params [2].name = "beta1";   params [2].val = beta1;
    params [3].name = "beta2";   params [3].val = beta2;
    params [4].name = "epsilon"; params [4].val = epsilon;
  }

  // Method for setting parameters
  void SetParams ()
  {
    popSize = (int)params [0].val;   // Set population size
    alpha   = params      [1].val;   // Set the learning ratio
    beta1   = params      [2].val;   // Set beta1
    beta2   = params      [3].val;   // Set beta2
    epsilon = params      [4].val;   // Set epsilon
  }

  // Initialization method
  bool Init (const double &rangeMinP  [],  // minimum search range
             const double &rangeMaxP  [],  // maximum search range
             const double &rangeStepP [],  // search step
             const int     epochsP = 0);   // number of epochs

  void Moving   (); // Moving method
  void Revision (); // Revision method

  //----------------------------------------------------------------------------
  double alpha;   // Learning ratio
  double beta1;   //  Exponential decay ratio for the first moment
  double beta2;   // Exponential decay ratio for the second moment
  double epsilon; // Small constant

  S_Gradients grad []; // Array of gradients

  private: //-------------------------------------------------------------------
  int step; // Iteration step
  int t;    // Iteration counter
};
//——————————————————————————————————————————————————————————————————————————————

C_AO_ADAM类的Init方法用于执行算法初始化:

  1. 通过调用StandardInit()进行默认参数设置。如果设置不成功,则返回false
  2. 重置step(步数)和迭代计数器t
  3. 根据popSize种群规模调整grad梯度数组的大小。
  4. 使用coords坐标为种群中的每个个体初始化梯度。

如果所有操作都成功,该方法返回true

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_ADAM::Init (const double &rangeMinP  [],
                      const double &rangeMaxP  [],
                      const double &rangeStepP [],
                      const int     epochsP = 0)
{
  // Standard initialization
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  step = 0; // Reset step counter
  t    = 1; // Reset iteration counter

  ArrayResize (grad, popSize);                              // Resize the gradient array
  for (int i = 0; i < popSize; i++) grad [i].Init (coords); // Initialize gradients for each individual

  return true;
}
//——————————————————————————————————————————————————————————————————————————————

C_AO_ADAM类的Moving方法实现了ADAM算法中的优化步骤:

1. 检查步数:如果步数小于2,

  • 保存种群中每个个体的函数值和坐标的先前值。
  • 在指定范围内生成新的随机坐标,并将其调整为可接受的值。
  • 步数计数器递增,方法终止。

2. 计算梯度:如果步数达到2或更多,则计算每个个体的目标函数值和坐标的变化。

  • 为防止除以0,添加一个较小的epsilon值。此外,该值是算法的一个外部参数,会影响搜索特性。
  • 为每个参数计算梯度。

3. 更新每个个体的参数:

  • 保留函数值和坐标的先前值。
  • 更新梯度的一阶和二阶矩的有偏估计。
  • 使用ADAM方程计算调整后的矩估计,并更新坐标。
  • 调整坐标以确保其保持在可接受的范围内。

4. 迭代计数器t递增。

因此,该方法负责在优化过程中使用自适应梯度矩更新个体的位置。算法的前两个步骤是计算适应度函数值变化的梯度所必需的,因为梯度是数值计算的,无需了解所求解优化问题的解析方程。计算梯度至少需要两个点,前两个步骤中获得的解可用于后续步骤。

ADAM算法的逻辑并未明确指定计算梯度的具体方式。梯度既可以通过解析方式计算,也可以通过数值方式计算,其计算过程发生在算法本身之外。将算法从其使用方式中抽象出来,对于理解整体机器学习项目中各个组件的作用至关重要。这样有助于更好地评估每个元素对最终结果的影响,并使算法更容易适应不同的任务。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ADAM::Moving ()
{
  //----------------------------------------------------------------------------
  if (step < 2) // If step is less than 2
  {
    for (int i = 0; i < popSize; i++)
    {
      a [i].fP = a [i].f; // Save the previous value of the function

      for (int c = 0; c < coords; c++)
      {
        a [i].cP [c] = a [i].c [c]; // Save the previous coordinate value

        // Generate new coordinates randomly
        a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        // Bringing new coordinates to acceptable values
        a [i].c [c] = u.SeInDiSp  (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    step++; // Increase the step counter
    return; // Exit the method
  }

  //----------------------------------------------------------------------------
  double ΔF, ΔX; // Changes in function and coordinates

  for (int i = 0; i < popSize; i++)
  {
    ΔF = a [i].f - a [i].fP;           // Calculate the change of the function

    for (int c = 0; c < coords; c++)
    {
      ΔX = a [i].c [c] - a [i].cP [c]; // Calculate the change in coordinates

      if (ΔX == 0.0) ΔX = epsilon;     // If change is zero, set it to epsilon

      grad [i].g [c] = ΔF / ΔX;        // Calculate the gradient
    }
  }

  // Update parameters using ADAM algorithm
  for (int i = 0; i < popSize; i++)
  {
    // Save the previous value of the function
    a [i].fP = a [i].f;                

    for (int c = 0; c < coords; c++)
    {
      // Save the previous coordinate value
      a [i].cP [c] = a [i].c [c];

      // Update the biased first moment estimate
      grad [i].m [c] = beta1 * grad [i].m [c] + (1.0 - beta1) * grad [i].g [c];

      // Update the biased second moment estimate
      grad [i].v [c] = beta2 * grad [i].v [c] + (1.0 - beta2) * grad [i].g [c] * grad [i].g [c];

      // Calculate the adjusted first moment estimate
      double m_hat = grad [i].m [c] / (1.0 - MathPow (beta1, t));

      // Calculate the adjusted estimate of the second moment
      double v_hat = grad [i].v [c] / (1.0 - MathPow (beta2, t));

      // Update coordinates
      a [i].c [c] = a [i].c [c] + (alpha * m_hat / (MathSqrt (v_hat) + epsilon));

      // Make sure the coordinates stay within the allowed range
      a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  t++; // Increase the iteration counter
}
//——————————————————————————————————————————————————————————————————————————————

C_AO_ADAM类的Revision方法执行以下操作:

  1. 它将最优个体索引ind初始化为-1。
  2. 遍历种群中的所有个体,如果当前个体的函数值大于已找到的最优值fB,则更新全局最优解,并存储最优个体的索引。
  3. 如果找到了最优个体,则将其坐标复制到cB数组中。

因此,该方法根据适应度函数的值查找并存储最优个体的坐标。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ADAM::Revision ()
{
  int ind = -1;       // Best individual index
  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > fB) // If the current value of the function is greater than the best one
    {
      fB = a [i].f;   // Update the best value of the function
      ind = i;        // Store the index of the best individual
    }
  }

  if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY); // Copy the coordinates of the best individual
}
//——————————————————————————————————————————————————————————————————————————————

正如我们所见,如今的ADAM算法已演变为基于种群的算法;而若将外部种群规模参数设置为1,该算法将完全如同常规的非种群版ADAM算法一样运行。现在,我们可以在测试函数上对该算法进行测试。让我们来看一下测试结果:

ADAM|Adaptive Moment Estimation|50.0|0.001|0.9|0.999|0.00000001|
=============================
5 Hilly's; Func runs: 10000; result: 0.3857584301959297
25 Hilly's; Func runs: 10000; result: 0.29733109680042824
500 Hilly's; Func runs: 10000; result: 0.25390478702062613
=============================
5 Forest's; Func runs: 10000; result: 0.30772687797850234
25 Forest's; Func runs: 10000; result: 0.1982664040653052
500 Forest's; Func runs: 10000; result: 0.15554626746207786
=============================
5 Megacity's; Func runs: 10000; result: 0.18153846153846154
25 Megacity's; Func runs: 10000; result: 0.12430769230769231
500 Megacity's; Func runs: 10000; result: 0.09503076923077
=============================
总分:1.99941 (22.22%)

遗憾的是,结果并不尽如人意,但这却为潜在的改进开辟了机会,也让我们有空间去实施优化,尤其是为算法引入真正的随机性成分。


在上述ADAM种群算法的实现中,每个智能体都代表着作者原始逻辑的一个独立“执行线程”,它们如同在搜索空间的山丘上爬行的蛇,因初始随机初始化而分散在场地各处。这些“蛇”之间互不交互,也不共享关于最优解的信息。由于该算法是基于梯度的,因此对于其而言,尽可能考虑彼此位置接近的点处的曲面变化情况至关重要。若减小数值微分步长,我们可能会遇到收敛缓慢的问题;而增大步长则会导致空间中的大幅跳跃,使得难以获取点之间曲面的信息。

为了解决这些问题,我们将使部分种群个体成为混合个体,这些个体将由其他智能体的解的元素组成。具体思路是这样的:我们根据个体的适应度对种群进行排序,并在列表末尾(即最弱个体所在的位置)创建混合个体。对于这些个体,我们将通过基于更适应个体的解的元素在空间中生成一个新点来形成解。个体的适应度越高,其向混合个体传递位置信息的可能性就越大。

因此,种群中的部分个体将按照算法的原始逻辑表示解,而另一部分则将是所谓的混合个体,它们是种群中解的元素的组合。所得的混合个体并非简单地由其他个体的部分复制而来;这些部分各自按照幂律概率分布发生变化。我们将这种规律的度数称为“混合稳定性”:度数越高,混合个体经历的变化就越小,也就越类似于种群中最佳解的元素。


现在,让我们来看一下算法的更新版本。C_AO_ADAMm类对C_AO_ADAM类进行了几处修改,从理论上讲,这些修改可以对其功能性和行为产生积极影响。以下是主要的修改内容:

1. 新增参数:

  • hybridsPercentage — 确定种群中混合个体的百分比。
  • hybridsResistance — 调节混合个体对变化的抗性。

2. 在C_AO_ADAMm类构造函数中,我们初始化新的hybridsPercentagehybridsResistance参数。它们的值被添加到params数组中。 

3. SetParams方法中包含用于设置新hybridsPercentage和hybridsResistance参数的字符串,这使我们能够动态更改它们的值。

将混合个体百分比参数设置为“1”,将有效禁用ADAM逻辑。将此值设置为“0”,则算法将不具有组合特性。因此,经过一些试验后,我发现最优值为“0.5”,呈现效果最佳。

第二个参数负责调节混合个体对变化的抗性。当设置较低的值时,混合个体在继承特征后会发生显著变化,并可能覆盖优化参数的整个可接受值范围。同时,如果我们设置过高的值,例如“20”或更高,混合个体的变异性将趋于“0”,它们就会仅成为父代个体最优特质的携带者。同样,通过试验,我发现该参数的最优值为“10”。

//——————————————————————————————————————————————————————————————————————————————
class C_AO_ADAMm : public C_AO
{
  public: //--------------------------------------------------------------------

  // Class destructor
  ~C_AO_ADAMm () { }

  // Class constructor
  C_AO_ADAMm ()
  {
    ao_name = "ADAMm";                                  // Algorithm name
    ao_desc = "Adaptive Moment Estimation M";           // Algorithm description
    ao_link = "https://www.mql5.com/en/articles/16443"; // Link to the article

    popSize           = 100;    // Population size
    hybridsPercentage = 0.5;    // Percentage of hybrids in the population
    hybridsResistance = 10;     // Resistance of hybrids to changes
    alpha             = 0.001;  // Learning ratio
    beta1             = 0.9;    // Exponential decay ratio for the first moment
    beta2             = 0.999;  // Exponential decay ratio for the second moment
    epsilon           = 0.1;    // Small constant to prevent division by zero

    // Initialize the parameter array
    ArrayResize (params, 7);
    params [0].name = "popSize";           params [0].val = popSize;
    params [1].name = "hybridsPercentage"; params [1].val = hybridsPercentage;
    params [2].name = "hybridsResistance"; params [2].val = hybridsResistance;
    params [3].name = "alpha";             params [3].val = alpha;
    params [4].name = "beta1";             params [4].val = beta1;
    params [5].name = "beta2";             params [5].val = beta2;
    params [6].name = "epsilon";           params [6].val = epsilon;
  }

  // Method for setting parameters
  void SetParams ()
  {
    popSize           = (int)params [0].val;   // Set population size
    hybridsPercentage = params      [1].val;   // Set the percentage of hybrids in the population
    hybridsResistance = params      [2].val;   // Set hybrids' resistance to change
    alpha             = params      [3].val;   // Set the learning ratio
    beta1             = params      [4].val;   // Set beta1
    beta2             = params      [5].val;   // Set beta2
    epsilon           = params      [6].val;   // Set epsilon
  }

  // Initialization method
  bool Init (const double &rangeMinP  [],  // minimum search range
             const double &rangeMaxP  [],  // maximum search range
             const double &rangeStepP [],  // search step
             const int     epochsP = 0);   // number of epochs

  void Moving   (); // Moving method
  void Revision (); // Revision method

  //----------------------------------------------------------------------------
  double hybridsPercentage;  // Percentage of hybrids in the population
  double hybridsResistance;  // Resistance of hybrids to changes
  double alpha;              // Learning ratio
  double beta1;              // Exponential decay ratio for the first moment
  double beta2;              // Exponential decay ratio for the second moment
  double epsilon;            // Small constant

  S_Gradients grad []; // Array of gradients

  private: //-------------------------------------------------------------------
  int step;          // Iteration step
  int t;             // Iteration counter
  int hybridsNumber; // Number of hybrids in the population
};
//——————————————————————————————————————————————————————————————————————————————

C_AO_ADAMm类的Init方法中,与之前类的类似方法相比,发生了以下变化:

  1. 根据hybridsPercentage(混合个体比例)百分比计算种群中混合个体的数量。使用新的hybridsNumber(混合个体数量)值控制种群的构成。
  2. 新增了一项检查,以确保混合个体的数量不超过popSize(种群规模)。这样可以防止出现与数组越界相关的错误。

这些变化使Init方法能够更好地适应算法中与混合个体相关的特性,并确保正确管理种群中个体的状态和初始化。

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_ADAMm::Init (const double &rangeMinP  [],
                       const double &rangeMaxP  [],
                       const double &rangeStepP [],
                       const int     epochsP = 0)
{
  // Standard initialization
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  step          = 0;                                        // Reset step counter
  t             = 1;                                        // Reset iteration counter
  hybridsNumber = int(popSize * hybridsPercentage);         // Calculation of the number of hybrids in the population 
  if (hybridsNumber > popSize) hybridsNumber = popSize;     // Correction

  ArrayResize (grad, popSize);                              // Resize the gradient array
  for (int i = 0; i < popSize; i++) grad [i].Init (coords); // Initialize gradients for each individual

  return true;
}
//——————————————————————————————————————————————————————————————————————————————

与先前版本相比,Moving方法也进行了一些改动。

使用ADAM算法更新参数。在此代码块中添加了针对混合个体的处理条件。如果个体索引i大于或等于popSize - hybridsNumber(即该个体为混合个体),则使用随机分布和hybridsResistance(混合个体抗性)参数生成新坐标。这使得混合个体在继承父代个体特征时能够产生微小偏差(类似于特征突变的一种形式)。否则,对于非混合个体,则更新有偏一阶和二阶矩估计值,然后计算调整后的估计值。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ADAMm::Moving ()
{
  //----------------------------------------------------------------------------
  if (step < 2) // If step is less than 2
  {
    for (int i = 0; i < popSize; i++)
    {
      a [i].fP = a [i].f; // Save the previous value of the function

      for (int c = 0; c < coords; c++)
      {
        a [i].cP [c] = a [i].c [c]; // Save the previous coordinate value

        // Generate new coordinates randomly
        a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        // Bringing new coordinates to acceptable values
        a [i].c [c] = u.SeInDiSp  (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    step++; // Increase the step counter
    return; // Exit the method
  }

  //----------------------------------------------------------------------------
  double ΔF, ΔX; // Changes in function and coordinates
  double cNew;

  for (int i = 0; i < popSize; i++)
  {
    ΔF = a [i].f - a [i].fP;           // Calculate the change of the function

    for (int c = 0; c < coords; c++)
    {
      ΔX = a [i].c [c] - a [i].cP [c]; // Calculate the change in coordinates

      if (ΔX == 0.0) ΔX = epsilon;     // If change is zero, set it to epsilon

      grad [i].g [c] = ΔF / ΔX;        // Calculate the gradient
    }
  }

  // Update parameters using ADAM algorithm
  for (int i = 0; i < popSize; i++)
  {
    // Save the previous value of the function
    a [i].fP = a [i].f;

    for (int c = 0; c < coords; c++)
    {
      // Save the previous coordinate value
      a [i].cP [c] = a [i].c [c];

      if (i >= popSize - hybridsNumber)
      {
        double pr = u.RNDprobab ();
        pr *= pr;

        int ind = (int)u.Scale (pr, 0, 1, 0, popSize - 1);

        cNew = u.PowerDistribution (a [ind].c [c], rangeMin [c], rangeMax [c], hybridsResistance);
      }
      else
      {
        // Update the biased first moment estimate
        grad [i].m [c] = beta1 * grad [i].m [c] + (1.0 - beta1) * grad [i].g [c];

        // Update the biased second moment estimate
        grad [i].v [c] = beta2 * grad [i].v [c] + (1.0 - beta2) * grad [i].g [c] * grad [i].g [c];

        // Calculate the adjusted first moment estimate
        double m_hat = grad [i].m [c] / (1.0 - MathPow (beta1, t));

        // Calculate the adjusted estimate of the second moment
        double v_hat = grad [i].v [c] / (1.0 - MathPow (beta2, t));

        // Update coordinates
        //a [i].c [c] = a [i].c [c] + (alpha * m_hat / (MathSqrt (v_hat) + epsilon));
        cNew = a [i].c [c] + (alpha * m_hat / (MathSqrt (v_hat) + epsilon));
      }

      // Make sure the coordinates stay within the allowed range
      a [i].c [c] = u.SeInDiSp (cNew, rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  t++; // Increase the iteration counter
}
//——————————————————————————————————————————————————————————————————————————————

与先前版本相比,Revision方法进行了如下改动:

准备用于排序的数组:创建一个与种群规模大小相同的临时数组aT,然后调用u.Sorting()排序方法。排序后的种群数组可使混合个体更有可能从适应度更高的个体那里继承特征。临时种群数组本可以移至类字段中,但放在此处这样做是为了使代码逻辑更加清晰。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ADAMm::Revision ()
{
  int ind = -1;       // Best individual index
  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > fB) // If the current value of the function is greater than the best one
    {
      fB = a [i].f;   // Update the best value of the function
      ind = i;        // Store the index of the best individual
    }
  }

  if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY); // Copy the coordinates of the best individual

  //----------------------------------------------------------------------------
  S_AO_Agent aT [];
  ArrayResize (aT, popSize);
  u.Sorting (a, aT, popSize);
}
//——————————————————————————————————————————————————————————————————————————————


测试结果

让我们来看一下经过修改的真正随机种群ADAMm版本的结果:

ADAMm|Adaptive Moment Estimation M|100.0|0.5|10.0|0.001|0.9|0.999|0.1|
=============================
5 Hilly's; Func runs: 10000; result: 0.8863499654810468
25 Hilly's; Func runs: 10000; result: 0.4476644542595641
500 Hilly's; Func runs: 10000; result: 0.2661291031673467
=============================
5 Forest's; Func runs: 10000; result: 0.8449728914068644
25 Forest's; Func runs: 10000; result: 0.3849320103361983
500 Forest's; Func runs: 10000; result: 0.16889385703816007
=============================
5 Megacity's; Func runs: 10000; result: 0.6615384615384616
25 Megacity's; Func runs: 10000; result: 0.2704615384615384
500 Megacity's; Func runs: 10000; result: 0.10593846153846247
=============================
总分:4.03688 (44.85%)

所获得的结果有了显著提升。下图展示了一个简单ADAM种群算法的可视化效果,可以看到个体“蛇”在探索搜索空间时向各个方向爬行的独特运动方式。此外,还展示了随机种群ADAMm的可视化效果,从中可以看出搜索智能体更积极地朝着全局最优解移动,不过在这种情况下,失去了具有特征的“蛇形”外观。

Hilly值

  ADAM在Hilly测试函数上

Hilly值

  ADAMm在Hilly测试函数上

Forest值

  ADAMm在Forest测试函数上

Megacity值

ADAMm在Megacity测试函数上

根据测试结果,随机种群版ADAM在排行榜中位列第32名,表现相当不错。原版ADAM由于效果不佳,未能被列入该排行榜。

# AO 描述 Hilly值 Hilly最终值 Forest值 Forest最终值 Megacity (离散) Megacity最终值 最终结果 最大百分比
10 p (5 F) 50 p (25 F) 1000 p (500 F) 10 p (5 F) 50 p (25 F) 1000 p (500 F) 10 p (5 F) 50 p (25 F) 1000 p (500 F)
1 ANS 跨邻域搜索 0.94948 0.84776 0.43857 2.23581 1.00000 0.92334 0.39988 2.32323 0.70923 0.63477 0.23091 1.57491 6.134 68.15
2 CLA 密码锁算法(joo) 0.95345 0.87107 0.37590 2.20042 0.98942 0.91709 0.31642 2.22294 0.79692 0.69385 0.19303 1.68380 6.107 67.86
3 AMOm 动物迁徙优化M 0.90358 0.84317 0.46284 2.20959 0.99001 0.92436 0.46598 2.38034 0.56769 0.59132 0.23773 1.39675 5.987 66.52
4 (P+O)ES (P+O) 进化策略 0.92256 0.88101 0.40021 2.20379 0.97750 0.87490 0.31945 2.17185 0.67385 0.62985 0.18634 1.49003 5.866 65.17
5 CTA 彗星尾算法(joo) 0.95346 0.86319 0.27770 2.09435 0.99794 0.85740 0.33949 2.19484 0.88769 0.56431 0.10512 1.55712 5.846 64.96
6 SDSm 随机扩散搜索 M 0.93066 0.85445 0.39476 2.17988 0.99983 0.89244 0.19619 2.08846 0.72333 0.61100 0.10670 1.44103 5.709 63.44
7 AAm 射箭算法M 0.91744 0.70876 0.42160 2.04780 0.92527 0.75802 0.35328 2.03657 0.67385 0.55200 0.23738 1.46323 5.548 61.64
8 ESG 社会群体的进化(joo) 0.99906 0.79654 0.35056 2.14616 1.00000 0.82863 0.13102 1.95965 0.82333 0.55300 0.04725 1.42358 5.529 61.44
9 SIA 模拟各向同性退火(joo) 0.95784 0.84264 0.41465 2.21513 0.98239 0.79586 0.20507 1.98332 0.68667 0.49300 0.09053 1.27020 5.469 60.76
10 ACS 人工协同搜索 0.75547 0.74744 0.30407 1.80698 1.00000 0.88861 0.22413 2.11274 0.69077 0.48185 0.13322 1.30583 5.226 58.06
11 ASO 无序社会优化 0.84872 0.74646 0.31465 1.90983 0.96148 0.79150 0.23803 1.99101 0.57077 0.54062 0.16614 1.27752 5.178 57.54
12 AOSm 原子轨道搜索M 0.80232 0.70449 0.31021 1.81702 0.85660 0.69451 0.21996 1.77107 0.74615 0.52862 0.14358 1.41835 5.006 55.63
13 TSEA 龟壳演化算法(joo) 0.96798 0.64480 0.29672 1.90949 0.99449 0.61981 0.22708 1.84139 0.69077 0.42646 0.13598 1.25322 5.004 55.60
14 DE 差分进化 0.95044 0.61674 0.30308 1.87026 0.95317 0.78896 0.16652 1.90865 0.78667 0.36033 0.02953 1.17653 4.955 55.06
15 CRO 化学反应优化 0.94629 0.66112 0.29853 1.90593 0.87906 0.58422 0.21146 1.67473 0.75846 0.42646 0.12686 1.31178 4.892 54.36
16 BSA 鸟群算法 0.89306 0.64900 0.26250 1.80455 0.92420 0.71121 0.24939 1.88479 0.69385 0.32615 0.10012 1.12012 4.809 53.44
17 HS 和声搜索 0.86509 0.68782 0.32527 1.87818 0.99999 0.68002 0.09590 1.77592 0.62000 0.42267 0.05458 1.09725 4.751 52.79
18 SSG 树苗播种和生长 0.77839 0.64925 0.39543 1.82308 0.85973 0.62467 0.17429 1.65869 0.64667 0.44133 0.10598 1.19398 4.676 51.95
19 BCOm 细菌趋化性优化算法M 0.75953 0.62268 0.31483 1.69704 0.89378 0.61339 0.22542 1.73259 0.65385 0.42092 0.14435 1.21912 4.649 51.65
20 ABO 非洲水牛优化 0.83337 0.62247 0.29964 1.75548 0.92170 0.58618 0.19723 1.70511 0.61000 0.43154 0.13225 1.17378 4.634 51.49
21 (PO)ES (PO) 进化策略 0.79025 0.62647 0.42935 1.84606 0.87616 0.60943 0.19591 1.68151 0.59000 0.37933 0.11322 1.08255 4.610 51.22
22 TSm 禁忌搜索M 0.87795 0.61431 0.29104 1.78330 0.92885 0.51844 0.19054 1.63783 0.61077 0.38215 0.12157 1.11449 4.536 50.40
23 BSO 头脑风暴优化 0.93736 0.57616 0.29688 1.81041 0.93131 0.55866 0.23537 1.72534 0.55231 0.29077 0.11914 0.96222 4.498 49.98
24 WOAm 鲸鱼优化算法M 0.84521 0.56298 0.26263 1.67081 0.93100 0.52278 0.16365 1.61743 0.66308 0.41138 0.11357 1.18803 4.476 49.74
25 AEFA 人工电场算法 0.87700 0.61753 0.25235 1.74688 0.92729 0.72698 0.18064 1.83490 0.66615 0.11631 0.09508 0.87754 4.459 49.55
26 AEO 基于人工生态系统的优化算法 0.91380 0.46713 0.26470 1.64563 0.90223 0.43705 0.21400 1.55327 0.66154 0.30800 0.28563 1.25517 4.454 49.49
27 ACOm 蚁群优化 M 0.88190 0.66127 0.30377 1.84693 0.85873 0.58680 0.15051 1.59604 0.59667 0.37333 0.02472 0.99472 4.438 49.31
28 BFO-GA 细菌觅食优化 - ga 0.89150 0.55111 0.31529 1.75790 0.96982 0.39612 0.06305 1.42899 0.72667 0.27500 0.03525 1.03692 4.224 46.93
29 SOA 简单优化算法 0.91520 0.46976 0.27089 1.65585 0.89675 0.37401 0.16984 1.44060 0.69538 0.28031 0.10852 1.08422 4.181 46.45
30 ABHA 人工蜂巢算法 0.84131 0.54227 0.26304 1.64663 0.87858 0.47779 0.17181 1.52818 0.50923 0.33877 0.10397 0.95197 4.127 45.85
31 ACMO 大气云模型优化 0.90321 0.48546 0.30403 1.69270 0.80268 0.37857 0.19178 1.37303 0.62308 0.24400 0.10795 0.97503 4.041 44.90
32 ADAMm 群体自适应矩估计M 0.88635 0.44766 0.26613 1.60014 0.84497 0.38493 0.16889 1.39880 0.66154 0.27046 0.10594 1.03794 4.037 44.85
33 ASHA 人工淋浴算法 0.89686 0.40433 0.25617 1.55737 0.80360 0.35526 0.19160 1.35046 0.47692 0.18123 0.09774 0.75589 3.664 40.71
34 ASBO 适应性社会行为优化 0.76331 0.49253 0.32619 1.58202 0.79546 0.40035 0.26097 1.45677 0.26462 0.17169 0.18200 0.61831 3.657 40.63
35 MEC 思维进化计算 0.69533 0.53376 0.32661 1.55569 0.72464 0.33036 0.07198 1.12698 0.52500 0.22000 0.04198 0.78698 3.470 38.55
36 IWO 入侵杂草优化 0.72679 0.52256 0.33123 1.58058 0.70756 0.33955 0.07484 1.12196 0.42333 0.23067 0.04617 0.70017 3.403 37.81
37 Micro-AIS 微型人工免疫系统 0.79547 0.51922 0.30861 1.62330 0.72956 0.36879 0.09398 1.19233 0.37667 0.15867 0.02802 0.56335 3.379 37.54
38 COAm 布谷鸟优化算法 M 0.75820 0.48652 0.31369 1.55841 0.74054 0.28051 0.05599 1.07704 0.50500 0.17467 0.03380 0.71347 3.349 37.21
39 SDOm 螺旋动力学优化 M 0.74601 0.44623 0.29687 1.48912 0.70204 0.34678 0.10944 1.15826 0.42833 0.16767 0.03663 0.63263 3.280 36.44
40 NMm Nelder-Mead方法 M 0.73807 0.50598 0.31342 1.55747 0.63674 0.28302 0.08221 1.00197 0.44667 0.18667 0.04028 0.67362 3.233 35.92
41 FAm 萤火虫算法 M 0.58634 0.47228 0.32276 1.38138 0.68467 0.37439 0.10908 1.16814 0.28667 0.16467 0.04722 0.49855 3.048 33.87
42 GSA 引力搜索算法 0.64757 0.49197 0.30062 1.44016 0.53962 0.36353 0.09945 1.00260 0.32667 0.12200 0.01917 0.46783 2.911 32.34
43 BFO 细菌觅食优化 0.61171 0.43270 0.31318 1.35759 0.54410 0.21511 0.05676 0.81597 0.42167 0.13800 0.03195 0.59162 2.765 30.72
44 ABC 人工蜂群 0.63377 0.42402 0.30892 1.36671 0.55103 0.21874 0.05623 0.82600 0.34000 0.14200 0.03102 0.51302 2.706 30.06
45 BA 蝙蝠算法 0.59761 0.45911 0.35242 1.40915 0.40321 0.19313 0.07175 0.66810 0.21000 0.10100 0.03517 0.34617 2.423 26.93



总结

本文尝试将传统上用于神经网络的知名梯度方法ADAM进行适应性调整,以解决一般的优化问题。这一尝试取得了成功,因为最终得到的真正随机种群ADAMm算法能够与解决全局优化问题的最强算法相媲美。本文表明,在多维搜索空间中,确定性方法在寻找最优解时往往不如随机方法有效,只有引入额外的随机性元素才能拓展各优化算法的搜索能力。

需要注意的是,在神经网络训练中,集成梯度方法(如常规的ADAM方法)仍然被广泛使用,因为它们在反向传播过程中使用了精确的梯度值。然而,正如许多机器学习文章作者所指出的,当使用比误差最小化函数更复杂的评估标准来训练神经网络时,梯度方法可能会遇到困难,并陷入局部最优解。

本文提出的方法在神经网络中集成方法的经典应用中可能大有用处,它在保持卓越的准确性和收敛速度的同时,利用神经元激活函数的解析形式,大幅提升了神经网络训练过程中的抗干扰能力。这将使得在训练过程中,能够在具有非常复杂指标和标准的任务中使用经典方法。我希望这项工作能够帮助研究人员和从业者从一个全新的视角看待一般的优化问题,特别是机器学习方法。

标签

图1. 根据相关测试,将算法的颜色等级大于或等于0.99的结果以白色突出显示。

图表

图例2. 算法测试结果的直方图(标尺从 0 到 100,数字越大结果越好,

其中 100 是理论上的最大可能结果,将计算评级表格的脚本存档)


ADAMm的优缺点:

优点:

  1. 在低维问题上表现良好。
  2. 结果离散度低(结果波动小)。

缺点:

  1. 许多外部参数。

文章附有一个包含当前版本算法代码的归档文件。本文作者对标准算法描述的绝对准确性不承担责任。为提升搜索能力,已经对其中的许多算法进行了修改。文章中表述的结论和论断都是基于实验的结果。

文中所用的程序

# 名称 类型 描述
1 #C_AO.mqh

种群优化算法的基类
2 #C_AO_enum.mqh

种群优化算法的枚举说明
3 TestFunctions.mqh

测试函数库
4
TestStandFunctions.mqh

测试台函数库
5
Utilities.mqh

辅助函数库
6
CalculationTestResults.mqh

用于计算比较表结果的脚本
7
Testing AOs.mq5
脚本 面向所有种群优化算法的统一测试平台
8
Simple use of population optimization algorithms.mq5
脚本
种群优化算法非可视化简易使用案例
9
Test_AO_ADAM.mq5
脚本 ADAM与ADAMm测试

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/16443

附加的文件 |
ADAMm.zip (143.2 KB)
MQL5 交易工具包(第 3 部分):开发挂单管理 EX5 库 MQL5 交易工具包(第 3 部分):开发挂单管理 EX5 库
了解如何在 MQL5 代码或项目中开发和实现全面的挂单 EX5库。本文将向您展示如何创建一个全面的挂单管理 EX5 库,并通过构建交易面板或图形用户界面(GUI)来指导您导入和实现它。EA 交易订单面板将允许用户直接从图表窗口上的图形界面打开、监控和删除与指定幻数相关的挂单。
价格行为分析工具包开发系列(第4部分):分析预测型EA 价格行为分析工具包开发系列(第4部分):分析预测型EA
我们不再局限于仅在图表上查看分析后的指标,而是将视野拓展至更广阔的范畴,其中包括与Telegram的集成。这一增强功能使得重要结果能够通过Telegram应用程序直接发送至您的移动设备。请随我们一同在本篇文章中探索这一过程。
使用MQL5经济日历进行交易(第五部分):添加响应式控件和过滤按钮的增强型仪表盘 使用MQL5经济日历进行交易(第五部分):添加响应式控件和过滤按钮的增强型仪表盘
在本文中,我们创建了用于货币对过滤、重要性级别过滤、时间过滤以及取消选项的按钮,以改进仪表盘的控制功能。通过编程让这些按钮能够动态响应用户操作,实现无缝交互。我们还对其行为进行了自动化处理,以便在仪表盘上实时反映变化。这样就提升了面板的整体功能性、灵活性和响应速度。
将 MQL5 与数据处理包集成(第 3 部分):增强的数据可视化 将 MQL5 与数据处理包集成(第 3 部分):增强的数据可视化
在本文中,我们将通过结合交互性、分层数据和动态元素等功能,超越基本图表,实现增强的数据可视化,使交易者能够更有效地探索趋势、形态和相关性。