机器学习

这些方法用于机器学习。

神经网络激活函数根据输入的加权累加和来判定神经元的输出值。 激活函数的选择对神经网络性能有很大影响。 模型部件(层)不同可选用不同的激活函数。

除了所有已知的激活函数外,MQL5 还提供其衍生函数。 函数导数能够根据学习中接到的误差有效地更新模型参数。

神经网络旨在找到一种算法,将学习误差最小化,为此选用了损失函数。 损失函数的值表示模型预测值与实际值的偏差度。 根据问题采用不同的损失函数。 例如,均方误差(MSE) 用于回归问题;二元交叉熵(BCE)用于二元分类目的。

函数 动作
Activation 计算激活函数值,并将其写入传递的向量/矩阵
Derivative 计算激活函数导数值,并将其写入所传递的向量/矩阵
Loss 计算损失函数值,并将其写入所传递的向量/矩阵
LossGradient 计算亏损函数梯度的向量或矩阵
RegressionMetric 在指定数据数组上构建回归线,并计算回归量程作为偏差误差

举例

此示例演示如何选用矩阵运算训练模型。 训练的模型针对函数 (a + b + c)^2 / (a^2 + b^2 + c^2)。 我们输入初始数据矩阵,其中 a、b 和 c 包含在不同的列中。 函数结果在模型输出端获得。

matrix weights1, weights2, weights3;               // 权重矩阵

matrix output1, output2, result;                   // 神经层输出矩阵

input int layer1 = 200;                            // 第一个隐藏层的大小

input int layer2 = 200;                            // 第二个隐藏层的大小

input int Epochs = 20000;                          // 训练世代的数量

input double lr = 3e-6;                            // 学习率

input ENUM_ACTIVATION_FUNCTION ac_func = AF_SWISH; // 激活函数

//+------------------------------------------------------------------+

//| 脚本程序 start 函数                                                |

//+------------------------------------------------------------------+

void OnStart()

  {

//---

   int train = 1000;    // 训练样本大小

   int test = 10;       // 测试样本大小

   matrix m_data, m_target;

//--- 生成训练样本

   if(!CreateData(m_data, m_target, train))

      return;

//--- 训练模型

   if(!Train(m_data, m_target, Epochs))

      return;

//--- 生成测试样本

   if(!CreateData(m_data, m_target, test))

      return;

//--- 测试模型

   Test(m_data, m_target);

  }

//+------------------------------------------------------------------+

//| 样本生成方法                                                       |

//+------------------------------------------------------------------+

bool CreateData(matrix &data, matrix &target, const int count)

  {

//--- 初始化初始数据和结果矩阵

   if(!data.Init(count, 3) || !target.Init(count, 1))

      return false;

//--- 用随机值填充初始数据矩阵

   data.Random(-10, 10);

//--- 计算训练样本的目标值

   vector X1 = MathPow(data.Col(0) + data.Col(1) + data.Col(1), 2);

   vector X2 = MathPow(data.Col(0), 2) + MathPow(data.Col(1), 2) + MathPow(data.Col(2), 2);

   if(!target.Col(X1 / X2, 0))

      return false;

//--- 返回结果

   return true;

  }

//+------------------------------------------------------------------+

//| 模型训练方法                                                       |

//+------------------------------------------------------------------+

bool Train(matrix &data, matrix &target, const int epochs = 10000)

  {

//--- 创建模型

   if(!CreateNet())

      return false;

//--- 训练模型

   for(int ep = 0; ep < epochs; ep++)

     {

      //--- 前向验算

      if(!FeedForward(data))

         return false;

      PrintFormat("Epoch %d, loss %.5f", ep, result.Loss(target, LOSS_MSE));

      //--- 权重矩阵的反向传播与更新

      if(!Backprop(data, target))

         return false;

     }

//--- 返回结果

   return true;

  }

//+------------------------------------------------------------------+

//| 模型创建方法                                                       |

//+------------------------------------------------------------------+

bool CreateNet()

  {

//--- 初始化权重矩阵

   if(!weights1.Init(4, layer1) || !weights2.Init(layer1 + 1, layer2) || !weights3.Init(layer2 + 1, 1))

      return false;

//--- 用随机值填充权重矩阵

   weights1.Random(-0.1, 0.1);

   weights2.Random(-0.1, 0.1);

   weights3.Random(-0.1, 0.1);

//--- 返回结果

   return true;

  }

//+------------------------------------------------------------------+

//| 前向验算方法                                                       |

//+------------------------------------------------------------------+

bool FeedForward(matrix &data)

  {

//--- 检查初始数据大小

   if(data.Cols() != weights1.Rows() - 1)

      return false;

//--- 计算第一个神经层

   matrix temp = data;

   if(!temp.Resize(temp.Rows(), weights1.Rows()) ||

      !temp.Col(vector::Ones(temp.Rows()), weights1.Rows() - 1))

      return false;

   output1 = temp.MatMul(weights1);

//--- 计算激活函数

   if(!output1.Activation(temp, ac_func))

      return false;

//--- 计算第二个神经层

   if(!temp.Resize(temp.Rows(), weights2.Rows()) ||

      !temp.Col(vector::Ones(temp.Rows()), weights2.Rows() - 1))

      return false;

   output2 = temp.MatMul(weights2);

//--- 计算激活函数

   if(!output2.Activation(temp, ac_func))

      return false;

//--- 计算第三个神经层

   if(!temp.Resize(temp.Rows(), weights3.Rows()) ||

      !temp.Col(vector::Ones(temp.Rows()), weights3.Rows() - 1))

      return false;

   result = temp.MatMul(weights3);

//--- 返回结果

   return true;

  }

//+------------------------------------------------------------------+

//| 反向传播法                                                         |

//+------------------------------------------------------------------+

bool Backprop(matrix &data, matrix &target)

  {

//--- 检查目标值矩阵的大小

   if(target.Rows() != result.Rows() ||

      target.Cols() != result.Cols())

      return false;

//--- 检测计算值与目标的偏差

   matrix loss = (target - result) * 2;

//--- 将梯度传播到上一层

   matrix gradient = loss.MatMul(weights3.Transpose());

//--- 更新最后一层的权重矩阵

   matrix temp;

   if(!output2.Activation(temp, ac_func))

      return false;

   if(!temp.Resize(temp.Rows(), weights3.Rows()) ||

      !temp.Col(vector::Ones(temp.Rows()), weights3.Rows() - 1))

      return false;

   weights3 = weights3 + temp.Transpose().MatMul(loss) * lr;

//--- 依据激活函数的导数调整误差梯度

   if(!output2.Derivative(temp, ac_func))

      return false;

   if(!gradient.Resize(gradient.Rows(), gradient.Cols() - 1))

      return false;

   loss = gradient * temp;

//--- 将梯度传播到更低一层

   gradient = loss.MatMul(weights2.Transpose());

//--- 更新第二隐藏层的权重矩阵

   if(!output1.Activation(temp, ac_func))

      return false;

   if(!temp.Resize(temp.Rows(), weights2.Rows()) ||

      !temp.Col(vector::Ones(temp.Rows()), weights2.Rows() - 1))

      return false;

   weights2 = weights2 + temp.Transpose().MatMul(loss) * lr;

//--- 依据激活函数的导数调整误差梯度

   if(!output1.Derivative(temp, ac_func))

      return false;

   if(!gradient.Resize(gradient.Rows(), gradient.Cols() - 1))

      return false;

   loss = gradient * temp;

//--- 更新第一隐藏层的权重矩阵

   temp = data;

   if(!temp.Resize(temp.Rows(), weights1.Rows()) ||

      !temp.Col(vector::Ones(temp.Rows()), weights1.Rows() - 1))

      return false;

   weights1 = weights1 + temp.Transpose().MatMul(loss) * lr;

//--- 返回结果

   return true;

  }

//+------------------------------------------------------------------+

//| 模型测试方法                                                       |

//+------------------------------------------------------------------+

bool Test(matrix &data, matrix &target)

  {

//--- 前向测试数据

   if(!FeedForward(data))

      return false;

//--- 记录模型计算结果和真实值

   PrintFormat("Test loss %.5f", result.Loss(target, LOSS_MSE));

   ulong total = data.Rows();

   for(ulong i = 0; i < total; i++)

      PrintFormat("(%.2f + %.2f + %.2f)^2 / (%.2f^2 + %.2f^2 + %.2f^2) =  Net %.2f, Target %.2f", data[i, 0], data[i, 1], data[i, 2],

                  data[i, 0], data[i, 1], data[i, 2], result[i, 0], target[i, 0]);

//--- 返回结果

   return true;

  }

//+------------------------------------------------------------------+