English Русский Español Deutsch 日本語 Português
preview
神经网络变得轻松(第十四部分):数据聚类

神经网络变得轻松(第十四部分):数据聚类

MetaTrader 5示例 | 5 八月 2022, 10:16
1 186 0
Dmitriy Gizlyk
Dmitriy Gizlyk

内容

概述

在本系列文章中,我们已经在研究各种神经网络算法方面取得了实质性进展。 但所有先前研究的算法都基于监督模型学习原理。 这意味着我们要向模型输入一些历史数据,并优化权重,从而令模型的返回值非常接近参考结果。 在实践中,这种方法通常能产生最大的结果。 但为了实现这个学习过程,除了训练样本的历史数据之外,我们还需要系统每个状态的参考结果。 正如您所理解的,在准备培训样本时,参考值的准备涉及额外的劳动力成本。 进而,并非总是能够为每个系统状态给出明确的参考结果。 其后果就是,这对训练样本的可能规模施加了限制。

人工智能学习方法还有另一种方式 — 无监督学习。 该方式仅可令模型仅采用原始数据进行训练,而无需提供参考值。 当准备训练样本时,如此这般就可降低人工成本。 反过来,它也允许采用更多输入数据来训练模型。 然而,迅速膨胀的可能任务也将受到限制。

在本文中,您将不会看到以前所用的由多个神经层组成的垂直结构神经网络。 但首要之事依然是首要。 我们研究一下可能的算法,看看如何在交易中运用它们。

1. 无监督学习

通常,人工智能算法开发版图里有三个不同的领域:

  • 监督学习
  • 无监督学习
  • 强化学习

顾名思义,它们的主要区别在于训练模型的方式。 本系列的前几篇文章详细研究了第一种方法,即监督学习。 为了实现这种方法,我们需要一个带有配对标记值“系统状态-正确输出”的训练集合。 在实践中,这种方法通常能产生最大的结果。 然而,它还需要额外的资源(包括人力资源)和时间来准备带标记的训练集合。 甚而,并非总是能够为每个系统状态找到明确的参考结果。 同时,我们必须按照一定的概率考虑人为因素。 有时,这些原因是生成训练数据集合的主要限制。

那么,当有很多初始数据,但对它们了解甚少时,我们能做些什么呢? 或者,当不可能为学习过程的每个状态都标记特定参考值时? 或者,当我们甚至不知道这个参考值会是什么的时候? 在最初的汇集大数据期间,这些情况极其常见。 我们将切换至无监督学习,取代为每个系统状态花费资源寻找正确的参考输出。 根据任务的不同,无监督模型学习可用于获得问题的解决方案,或预处理初始数据。

请注意,有监督和无监督学习方式解决的问题区别很大。 例如,不可能使用无监督学习来解决回归问题。 可以比较有监督学习方式解决的分类任务,和无监督学习算法解决的聚类问题。 然而,在这两个词的相似含义背后,有着完全不同的逻辑。 通常这两种方式产生完全不同的结果。 当采用监督分类时,我们所提供的模型会学习哪个系统状态对应于哪个类别。 而对于无监督聚类,我们所提供的模型,根据一组描述系统状态的特征,自主判定将系统状态归属于哪个聚类。 在这种情况下,我们甚至可能在工作开始时不知道聚类的数量。 该数字是系统的超参数,可在模型训练过程中选择。

经由无监督学习算法解决的第二个问题是异常搜索。 这意味着模型应该搜索不属于给定系统的特征状态,但由于各种外部因素,这些状态可能仅以很小的概率出现。

无监督学习算法解决的另一个问题是数据降维。 请记住,在上一篇文章中,我们利用卷积网络解决了一个类似的问题。 然而,在监督学习中,我们寻找的是这个特定任务的特定特征。 与之对比,在无监督学习中,我们必须以最小的信息损失压缩数据。

如果我们看看无监督学习算法解决的所有问题,我们可以说这种方式的主要任务是研究和概括从输入数据中发现的特征。 通过这种方法,模型可以自主研究描述系统状态的特征。 这也经常用于解决监督学习问题。 在这种情况下,首先采用由无监督学习算法针对大数据训练模型。 系统应尽可能学习系统应具备的特征。 作为下一步,使用少量标记数据训练模型来解决特定问题。

如您所见,无监督学习算法可用于解决各种问题。 但如何在交易中运用它们呢? 我们来思考一下。 图形分析方法几乎总是涉及某些图表形态:双顶/双底、头和肩、旗帜、各种谐波形态、等等。 甚而,还有许多由 1-3 根烛条组成的较小形态。 但是当我们试图用数学语言描述一个特定的形态时,我们必须处理大量的约定和冗余。 这令在算法交易中运用它们复杂化。 即使当交易者人工判定了形态,也有很多主观性。 这就是为什么在分析同一张图表时,不同的交易者从图表上识别出的形态,往往具有相反的预测走势方向。 好吧,也许这才是整个交易的底层规则。 有些境况下人盈利,其它则亏损。 在交易过程中,并未创造新的商品资料价值,且其中货币供应量保持不变。 它只能从一个钱包转移到另一个钱包。 那么,我们如何避免亏损呢?

头和肩形态

我们再次看看上面提到的图表形态。 是的,它们都有各自的变体。 但与此同时,每种形态都有自己的特定结构,它的辨识度与一般价格走势图表区别明显。 那么,如果我们采用无监督数据聚类算法,令模型识别特定时间周期内数据中所有可能的变化,那会怎么样呢。 由于我们采用无监督学习,因此不需要标记数据,而且时间周期可能相当长。 然而,不要忘记,历史时间周期的增加亦会令模型训练成本增加。

2. k-均值算法

为了解决上面提出的聚类问题,我们将采用一种最简单、最容易理解的方法:k-均值。 尽管该方法简单,但它在解决数据聚类问题方面是有效的,即可单独使用,也可用于数据预处理。

为了能够使用该方法,系统的每个状态必须由汇集到单一向量中的特定数据集和来定义。 每个这样的向量表示 N 维空间中的一个点的坐标。 空间维度等于系统状态定义向量的维度。

平面上的初始数据

该方法的思路是找到这样的中心(向量),系统的所有已知状态都可以围绕该中心合并成聚类。 系统所有状态至相应聚类中心的平均距离应最小。 这就是方法 k-均值 名称的来源。 此种聚类的数量是模型的超参数。 它是在模型设计或验证阶段得以判定。

这句措辞“...在模型设计或验证阶段得以判定”听起来可能有点奇怪。 这些概念在时间上似乎按照模型创建和培训阶段进行了分离。 但情况不同。 有时,设置问题时会指定此类聚类的数量。 如果基于先前的经验,或所采用的计划,客户清楚地了解聚类的数量,就会出现这种情况。 或者在数据可视化期间可以清楚地看到不同聚类的数量。 在这种情况下,我们可以立即向模型指示我们正在寻找的聚类数量。

在其它情况下,当我们没有足够的知识来明确判定聚类的数量时,我们将不得不进行一系列模型训练,来判定最优聚类数量。 我们稍后再回来讨论。 现在我们来分析方法的运算算法。

上图在平面上随机显示 100 个可观测点。 可观测数据有助于理解其结构,然而该方法不需如此。 如您所见,这些点在整个平面上的分布相当均匀,我们无法在视觉上清晰区分任何聚类。 因此,我们无法知道它们的数量。 对于第一个实验,我们将取 5 个聚类。

既然我们知道了数量,我们应该在哪里找到它们的中心? 您记得吗,当我们初始化权重时,我们曾用随机值填充矩阵? 在此,我们也会做大致相同的事情。 但我们不会生成随机向量,因为它们可从我们的初始数据中删除。 我们将简单地从训练集合中抽取 5 个随机点。 它们在下图中用 X 标记。

添加聚类中心

接下来,我们需要计算从每个点至到每个中心的距离。 我猜想找到两点之间的一条直线(一维空间)测量距离并不困难。 为了判定平面上两点之间的距离,我们将使用毕达哥拉斯(Pythagorean)定理,这是我们从学校数学课上学到的。 该定理指出,两条边的平方和等于斜边的平方。 因此,平面上两点之间的距离等于坐标轴上点投影之间距离平方和的平方根。 简单地说,它是相应坐标差的平方和。 如果我们针对在 N-1 平面上的一个点投影应用类似的方法,我们将获得 N 维空间的类似等式。

判定点间距离的公式

我们已知到每个聚类中心的距离,并取其中最近的距离判定该点所属的聚类。 重复迭代,判定训练样本中所有点的距离和聚类。 此后,取简单的算术平均值来判定每个聚类的新中心。 下图显示了第一次迭代的结果。 每个聚类的点以单独的颜色着色。

第一次迭代 

如您所见,在第一次迭代之后,聚类周围的点分布不均匀。 但与上一张图表相比,聚类中心发生了变化。 因此,在重复计算至聚类中心距离的过程中,我们也会判定一个点是否属于这个聚类,亦或其它聚类,聚类上的点分布会随之发生变化。

我们重复这样的迭代,直到聚类中心停止移动。 此外,点所属的聚类将不再更改。

针对数据样本进行多次迭代之后,我们得到了以下结果。 如您所见,我们得到的聚类上训练序列点分布相当的均匀。

最终分布  

我们来汇总一下所研究的算法:

  1. 从训练样本中确定 k 个随机点作为聚类中心。
  2. 组织一个操作循环:
    • 判定从每个点到每个中心的距离
    • 找到最近的中心,并把一个点分配给该聚类
    • 使用算术平均值,为每个聚类确定一个新中心。
  3. 在循环中重复这些操作,直到聚类中心“停止移动”。

在本例中,我们对于点到中心的具体距离不感兴趣,而只需要找到最短距离。 因此,为了节省资源,我们在计算距离时不会计算结果和的平方根,因为这绝对不会影响数据聚类结果。

在这个阶段,我们将训练样本数据划分为多个聚类。 接下来,我们如何判定这个聚类数量是否最优? 与监督学习一样,我们引入了损失函数,这将帮助我们评估训练模型的质量,并比较使用不同超参数时模型的性能。 对于聚类问题,这种损失函数是点与相应聚类中心的平均偏差。 其计算公式如下:

Loss 函数

其中:

  • m 是训练样本中的元素数量
  • N 是来自训练样本的一个元素的描述向量的大小
  • Xi j是来自训练样本第 j 个元素的第 i 个描述向量的数值
  • Ci x j 是来自训练样本的第 j 个元素所属类别的中心向量的第 i 个值。

从上述公式可以看出,当聚类数量等于训练样本中的元素数时,损失函数的值将等于0。 但我们不想将整个训练样本复制到聚类中心矩阵当中。 与之对比,我们打算寻找一种方式来概括数据,这样我们就可以为每个聚类寻找可能的形态。

我取不同的聚类数量重复基于相同的训练集合进行聚类。 下图显示了损失函数对聚类数量的依赖性。 我没有显示损失函数值,因为对于不同的输入数据,它们可能差别很大。 同时,聚类数量也取决于训练样本,因此不应依赖于给定的值。 此处所提供的,只是为了解释图表。 理解图表解释原则才是重点。

聚类数量的依赖性误差

上图清晰地表明,当聚类数量从 2 变为 4 时,误差函数的值急剧下降。 随着聚类数量进一步增加到 6,误差函数值的下降率逐渐降低。 当聚类数量从 6 变为 7 时,误差函数的值实际上没有变化。 这是损失函数的平滑变化。 但有时在特定点上可能会出现一个断裂的图形变化。 当训练数据明显可分离时,这种现象经常发生。

解释图表的一般规则如下:

  • 当一个图形含有一条断裂线时,最佳聚类数量在断裂点处。
  • 若图形是平滑的,在拐弯区域中查找质量和性能之间的平衡点。  

考虑到小规模样本大小和聚类数量,我建议在我们的示例中使用 5 或 6 个聚类。

3. Python 实现

我们依据抽象数据为例讨论了 k-均值方法的理论方面。 合理的问题是,该方法如何处理真实数据? 为了回答这个问题,我们将利用 MetaTrader 5 和 Python 的集成。 Python 提供了大量的函数库,几乎可以满足任何需要。

集成工具已在本网站上多次提及,而函数库安装过程在文档中亦有讲述。

3.1. 包含函数库

为了实现该任务,我们将用到几个函数库。 首先,MetaTrader5 函数库。 该函数库实现了 MetaTrader 5 终端与 Python 的所有集成点。

我们第二个用到的函数库是 Scikit-Learn。 该函数库为数据分析提供了简单有效的工具。 特别是,它实现了几种数据聚类算法。 其中之一就是我们正在研究的 k-均值方法。

数据可视化的实现将用到 Matplotlib 函数库。

利用 MetaTrader 5 和 Python 集成工具,可把有关帐户状态、交易操作和行情状况的信息传输到脚本当中。 但是,它们无法读取内部程序的数据,例如指标。 故此,我们需要在 Python 端重现指标的整个实现。 为了简化任务,我们将用到 TA-Lib 函数库,该函数库提供了众多不同的技术分析工具。

在继续创建脚本之前,请在计算机上安装所有这些函数库,和 Python 解释器。 这个过程超出了本文的范畴。 不过,如果您遇到任何困难,我会在文章的评论区中回答。 

3.2. 创建一个脚本

现在我们已经决定了要用到的函数库清单,我们可以开始编写脚本了。 将脚本代码另存为 “clustering.py”。

在脚本的开头,包含所需的函数库。

# Import Libraries
import numpy as np
import matplotlib.pyplot as plt
import MetaTrader5 as mt5
from talib import abstract as tl
import sklearn.cluster as cluster
from datetime import datetime as dt

接下来,组织与终端的连接。 检查操作的正确性。 如果出现错误,显示相关消息,并退出程序。

# Connect to the MetaTrader 5 terminal
if not mt5.initialize():
    print("initialize() failed, error code =",mt5.last_error())
    quit()

直至成功连接到终端后,依据欲分析期间下载历史数据,并断开与终端的连接。

# Downloading quotes
rates=mt5.copy_rates_range('EURUSD',mt5.TIMEFRAME_H1,dt(2006,1,1),dt(2022,1,1))
mt5.shutdown()

现在,既然历史数据已就绪,我们继续判定指标值。 在这个模块中,我们将计算在采用监督学习测试各种模型时所用的相同指标值。 这些都是经典的振荡器:RSI、CCI 和 MACD。

# Calculate indicator values
rsi=tl.RSI(rates['close'])
cci=tl.CCI(rates['high'],rates['low'],rates['close'])
macd,macdsignal,macdhist=tl.MACD(rates['close'])

现在我们有了源数据,但它被分成 6 个张量。 需要将它们合并成一个张量来进行分析。 以下几点需要注意。 聚类函数是以这样的方式构造,即它接收二维数组作为输入;该数组的行被视为分离的形态。 通过将所有张量合并为一,我们还得到了一个二维数组,其中每一行包含关于一个单独烛条的信息。 它可以按照这种形式使用。 但这将是单个烛太的聚类。 这些信息有什么用吗? 如果我们想寻找由几根烛条组成的形态,那么我们需要改变张量的维数。 但简单地改变尺寸,并不能完全满足我们的需求。 这就像使用卷帘窗户,移动步长等于其大小。 但我们需要知道每根烛条上的形态。 因此,我们需要复制数据,并重新格式化张量。 下面的示例展示的是,张量合并,然后复制数据来创建 20 根烛条形态的代码。 注意历史数据的截断,其中未定义指标值。

# Group the training sample
data=np.array([rates['close']-rates['open'],rates['high']-rates['close'],rates['close']-rates['low'],
                                                                   rsi,cci,macd,macdsignal,macdhist]).T
s=data.shape[0]
data=np.hstack([data[40+k:s-20+k] for k in range(0,20)])

数据准备过程至此完成。 现在我们可以处理数据聚类。 但为了评估所需的聚类数量,我们需要以不同的聚类数量进行若干次测试。 在这个示例中,我从 50 到 1000 作为数量执行聚类,每次增量为 50。

# Perform clustering with a different number of clusters
R=range(50,1000,50)
KM = (cluster.KMeans(n_clusters=k).fit(data) for k in R)

然后判定每种情况下的误差,并将获得的数据可视化。

distance=(k.transform(data) for k in KM)                      
dist = (np.min(D, axis=1) for D in distance)
avgWithinSS = [sum(d) / data.shape[0] for d in dist]
# Plotting the model training results
plt.plot(R, avgWithinSS)
plt.xlabel('$Clasters$')
plt.title('Loss dynamic')
# Display generated graphs
plt.show()

脚本代码的工作到此结束。 进入下一个测试。 脚本的完整代码附在文后。

4. 测试

我们已创建了一个 Python 脚本,并可以对其进行测试。 脚本代码中指定了所有测试参数:

  • 品种 EURUSD
  • 时间帧 H1
  • 历史数据间隔: 16 年,自 2006.01.10 至 2022.01.01
  • 聚类数量: 从 50 至 1000,增量为 50

下图显示了损失函数对聚类数量的依赖关系。 

聚类数量对模型误差的影响

正如您在图表上所看到的,图形被极度拉伸。 聚类数量的最佳值似乎是从 400 到 500。 我们总共分析了 98641 个系统状态。

结束语

本文向读者介绍了 k-均值数据聚类方法,这是一种无监督学习算法。 我们利用 Python 函数库创建了一个脚本,并以不同的聚类数量针对模型进行了训练。 根据测试结果,我们可以得出结论,该模型能够识别大约 500 种形态。 当然,并非所有这些都能为交易操作给出明确的信号。 我们将在后续文章中探讨如何运用从实践中获得的结果。


参考文献列表

  1. 神经网络变得轻松
  2. 神经网络变得轻松(第二部分):网络训练和测试
  3. 神经网络变得轻松(第三部分):卷积网络
  4. 神经网络变得轻松(第四部分):循环网络
  5. 神经网络变得轻松(第五部分):OpenCL 中的多线程计算
  6. 神经网络变得轻松(第六部分):神经网络学习率实验
  7. 神经网络变得轻松(第七部分):自适应优化方法
  8. 神经网络变得轻松(第八部分):关注机制
  9. 神经网络变得轻松(第九部分):操作归档
  10. 神经网络变得轻松(第十部分):多目击者关注
  11. 神经网络变得轻松(第十一部分):自 GPT 获取
  12. 神经网络变得轻松(第十二部分):舍弃
  13. 神经网络变得轻松(第十三部分):批次常规化

本文中用到的程序

# 发行 类型 说明
1 clustering.py 脚本 数据聚类 - Python 脚本



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

附加的文件 |
clustering.py (2.97 KB)
视频:简单自动交易 — 如何利用 MQL5 创建简单的智能交易系统 视频:简单自动交易 — 如何利用 MQL5 创建简单的智能交易系统
在我的课程中,大多数学生认为 MQL5 真的很难理解。 除此之外,他们还在寻找一种直接的方法来把一些过程自动化。 那么阅读本文中归纳的信息,就能立刻发现如何利用 MQL5 开始运作。 即使您以前从未接触过任何形式的编程。 即使您无法领会之前您所观察到的插图的情况下。
从头开始开发智能交易系统(第 15 部分):访问 web 上的数据(I) 从头开始开发智能交易系统(第 15 部分):访问 web 上的数据(I)
如何通过 MetaTrader 5 访问在线数据? 互联网上有很多网站,提供海量信息。 您需要知道的是,在哪里查找、以及如何才能最好地利用这些信息。
从头开始开发智能交易系统(第 16 部分):访问 web 上的数据(II) 从头开始开发智能交易系统(第 16 部分):访问 web 上的数据(II)
掌握如何从网络向智能交易系统输入数据并非那么轻而易举。 如果不了解 MetaTrader 5 提供的所有可能性,就很难做到这一点。
学习如何基于抛物线 SAR 设计交易系统 学习如何基于抛物线 SAR 设计交易系统
在本文中,我们将继续讲述如何基于最流行的指标设计交易系统。 在本文中,我们将详细学习抛物线 SAR 指标,以及如何运用一些简单的策略来设计用于 MetaTrader 5 的交易系统。