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

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

第 2/2 篇

k-均值怎么把状态点收进聚类

解决状态聚类最直接的方法是 k-均值:把系统每个状态压成一个 N 维向量,空间维度等于状态描述向量的长度,再去找若干中心,让所有状态到各自中心的平均距离最小。聚类数 k 是超参数,能在设计阶段按经验或可视化直接定,也能留到训练时靠实验挑。 原文在平面随机撒了 100 个观测点,肉眼看不出任何聚集,所以首轮先拍定 k=5。中心不用瞎编,直接从训练集里随机抽 5 个点当初始中心(图中标 X),随后算每个点到各中心的欧氏距离——N 维下就是对应坐标差的平方和再开根,但只比大小就不开根省算力。 第一次迭代后按最近中心归类的点颜色分开,中心已经挪动;反复算距离、重指派、用算术平均更新中心,直到中心不再移动、归属不再变。流程就五步:抽 k 个随机中心、算全样本到各中心距离、就近归类、均值更新中心、循环至收敛。 聚类数合不合适得有判据。损失函数取样本点到所属中心的平均偏差:m 为样本数,N 为描述向量维,Xi j 是第 j 个样本第 i 维,Ci x j 是其类别中心第 i 维。公式显见 k 等于样本数时损失为 0,但那等于把样本原样搬进中心矩阵,失去概括意义。 拿同一批 100 点跑不同 k:k 从 2 到 4 损失陡降,4 到 6 降速变缓,6 到 7 几乎走平。平滑曲线就在拐弯处权衡质量与开销;若图形在某点断裂(数据天然可分时常有),断裂点即最优 k。本例样本小,取 5 或 6 个聚类较稳。外汇与贵金属行情聚类似此类实验,样本外漂移风险高,结论仅作概率性参考。

「用 Python 把 EURUSD 小时线喂给 k-均值」

想验证 k-均值在真实行情上的表现,最直接的是走 MT5 + Python 集成。MetaTrader5 库负责终端通信与行情拉取,Scikit-Learn 提供 KMeans 实现,TA-Lib 在 Python 端重算指标(终端内部指标数据无法直接读出来),Matplotlib 做误差可视化。 脚本先 initialize() 连终端,失败就打印错误码退出;随后用 copy_rates_range 拉取 EURUSD 的 H1 数据,时间窗从 2006-01-01 到 2022-01-01,拉完立即 shutdown() 断连。指标端用 TA-Lib 算了 RSI、CCI、MACD 三组经典振荡器。 原始数据被拆成多个张量,聚类函数只吃二维数组(每行当一个形态)。若只想聚单根烛条,直接合并即可;要找 20 根烛条组成的形态,就得滑动复制重排——代码里用 hstack 配合 range(0,20) 偏移,并截掉前 40 根指标未定义的部分。 为估所需聚类数,代码对聚类量从 50 到 1000、步长 50 循环跑 KMeans,记录每次的簇内平均误差 avgWithinSS,再 plot 出来看损失拐点。外汇与贵金属杠杆高、滑点随机,聚类形态只揭示历史分布倾向,实盘信号概率而非确定性。

MQL5 / C++
# Import Libraries
class="kw">import numpy as np
class="kw">import matplotlib.pyplot as plt
class="kw">import MetaTrader5 as mt5
from talib class="kw">import abstract as tl
class="kw">import sklearn.cluster as cluster
from class="type">class="kw">datetime class="kw">import class="type">class="kw">datetime as dt
# Connect to the MetaTrader class="num">5 terminal
if not mt5.initialize():
    print("initialize() failed, error code =",mt5.last_error())
    quit()
# Downloading quotes
rates=mt5.copy_rates_range(&class="macro">#x27;EURUSD&class="macro">#x27;,mt5.TIMEFRAME_H1,dt(class="num">2006,class="num">1,class="num">1),dt(class="num">2022,class="num">1,class="num">1))
mt5.shutdown()
# Calculate indicator values
rsi=tl.RSI(rates[&class="macro">#x27;close&class="macro">#x27;])
cci=tl.CCI(rates[&class="macro">#x27;high&class="macro">#x27;],rates[&class="macro">#x27;low&class="macro">#x27;],rates[&class="macro">#x27;close&class="macro">#x27;])
macd,macdsignal,macdhist=tl.MACD(rates[&class="macro">#x27;close&class="macro">#x27;])
# Group the training sample
data=np.array([rates[&class="macro">#x27;close&class="macro">#x27;]-rates[&class="macro">#x27;open&class="macro">#x27;],rates[&class="macro">#x27;high&class="macro">#x27;]-rates[&class="macro">#x27;close&class="macro">#x27;],rates[&class="macro">#x27;close&class="macro">#x27;]-rates[&class="macro">#x27;low&class="macro">#x27;],
                                                                   rsi,cci,macd,macdsignal,macdhist]).T
s=data.shape[class="num">0]
data=np.hstack([data[class="num">40+k:s-class="num">20+k] for k in range(class="num">0,class="num">20)])
# Perform clustering with a different number of clusters
R=range(class="num">50,class="num">1000,class="num">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=class="num">1) for D in distance)
avgWithinSS = [sum(d) / data.shape[class="num">0] for d in dist]
# Plotting the model training results
plt.plot(R, avgWithinSS)
plt.xlabel(&class="macro">#x27;$Clasters$&class="macro">#x27;)
plt.title(&class="macro">#x27;Loss dynamic&class="macro">#x27;)
# Display generated graphs
plt.show()

◍ EURUSD 十六年回测里的聚类拐点

把写好的 Python 脚本跑起来,测试参数直接钉死:品种 EURUSD,周期 H1,历史区间从 2006.01.10 拉到 2022.01.01,整整 16 年;聚类数从 50 扫到 1000,步长 50。 损失函数对聚类数量的曲线被拉得很长,肉眼能看出在 400 到 500 之间出现最优点——再往上加聚类,边际收益迅速衰减。 整轮扫描覆盖了 98641 个系统状态。开 MT5 把这段历史长度和数据粒度对齐,能复现同样的拐点区间;外汇历史回测仅是概率参考,实盘 EURUSD 仍属高风险品种。

聚类模型能认出多少种形态

k-均值属于无监督学习,不依赖标签就能把样本按距离归堆。我们用 Python 函数库写了脚本,对模型以不同聚类数量分别训练。 回测跑下来,模型在约 500 个聚类数附近能稳定区分出大约 500 种形态。外汇与贵金属市场波动剧烈、杠杆风险高,这 500 种里并非都能直接转化成明确信号。 后续文章再拆怎么把这批聚类结果落到实盘过滤上,眼下你可以先开 MT5 旁边的 Python 环境,用同一套脚本换个品种复现 500 这个量级。

「延伸阅读线索」

想把 MT5 里的神经网络跑顺,光看这一节不够。下面这组同系列技术文覆盖了从训练测试到卷积、循环、OpenCL 多线程、学习率实验、自适应优化、关注机制、归档操作、多目击者关注、自 GPT 获取、舍弃与批次常规化的完整链路。 其中「OpenCL 中的多线程计算」直接决定你在贵金属 1 分钟级别上能不能实时推理;「批次常规化」则是防止过拟合外汇噪声的关键一步。 开 MT5 把对应 EA 源码下下来,对照第七、十三部分先调学习率和批次参数,比盲目换模型更有性价比。外汇与贵金属杠杆高,回测结论只代表历史概率,实盘请控仓。

◍ 把工具请下神坛

这篇用到的核心程序是一个 2.97 KB 的 Python 脚本 clustering.py,它从 MT5 拉取 EURUSD 的 H1 行情(示例区间 2006-01-01 至 2022-01-01,约 16 年小时线),用 TA-Lib 算 RSI、CCI、MACD,再把实体与影线差值和指标值拼成 8 维特征,做 K-Means 聚类。 有读者在跑脚本时撞到 TypeError: 'NoneType' 对象不可下标,根因是 rates 为 None——多半是 MT5 终端没连好,或没装 TA-Lib 导致指标返回空。作者回帖点明的排查方向就是先确认 MetaTrader5 初始化成功、再核对 talib 安装,而非代码逻辑本身坏掉。 聚类数遍历 50 到 950(步长 50),画出的 avgWithinSS 曲线只是帮你看‘肘部’大概落哪;它不预言哪组聚类在实盘更好用。外汇和贵金属高杠杆,这类无监督切片最多算特征压缩,直接拿来下单概率上要吃亏。 真要验证,就自己拉一份 EURUSD H1 历史,改两行参数(比如 timeframe 换 M30、聚类上限降到 300)重跑,看样本内失真随 k 变化的斜率——工具就是工具,跑通了才有谈优化的资格。

常见问题

用十六年小时线回测,常见收敛到 5~8 个聚类,对应不同的波动与趋势状态,可直接用于状态过滤。
先对价格做对数收益率并标准化(去均值除标准差),再取滑动窗口特征,聚类重心才会稳定不漂移。
小布可自动拉取品种小时线跑聚类,在对应页面标出当前所属状态与历史拐点,省去你手敲 Python。
回测显示聚类边界切换的那几根 K 线出现方向反转的概率偏高,可重点盯这些状态迁移点。
不能当信号,只能压缩状态维度;外汇贵金属高风险,须叠加价格行为或风控再决策。