概论
在准备进行实验的数据时, 我们将使用来自 前文 中有关评估和选择预测的变量。我们将形成初始样本, 清理它并选择重要的变量。
我们将研究为了训练、测试和验证而划分样本的方法。
我们将使用 "darch" 软件包构建一个 DBN 网络模型, 并用我们的数据对它进行训练。在模型之后测试, 我们将能获得评估模型质量的标尺。我们将研究软件包所提供的神经网络设置的多种配置机会。
另外, 我们会看到隐藏的马尔科夫模型将如何帮助我们改进神经网络预测。
我们将开发一款智能交易系统, 根据持续监测的结果, 在不打断交易的情况下对模型进行定期训练。在 EA 中将使用来自 "darch" 软件包的 DBN 模型。我
再有, 我们将指点改进模型的定性指标的方式i和方法。
1. 由栈式 RBM (DN_SRBM) 初始化的深层神经网络的结构
我记得 DN_SRBM 是由 n 个等于神经网络隐藏层数的 RBM 组成, 基本上是神经网络本身。训练包括两个阶段。
第一阶段涉及预训练。每个 RBM 无需监督者即可依据输入集合系统地训练 (无目标)。在隐藏层的权重之后, RBM 被转移到相关的神经网络隐藏层。
第二阶段涉及微调, 在此神经网络由监督者训练。前文中已提供了有关它的详细信息, 所以我们在此不再复述。我将简单叙述, 不像我们前文用过的 "deepnet" 软件包, "darch" 软件包帮助我们实现更广泛的机会来构建并微调模型。更多详细信息将在创建模型时提供。图例. 1 显示 DN_SRBM 的结构和处理过程
图例. 1. DN SRBM 的结构
2. 数据的准备及选择
2.1. 输入变量 (征兆, 预测器)
在前文中, 我们研究过预测器的评估和选择, 所以在此无需提供额外信息。我仅论及我们使用的 11 款指标 (所有的振荡器: ADX, aroon, ATR, CCI, chaikinVolatility, CMO, MACD, RSI, stoch, SMI, volatility)。选择了来自一些指标的若干变量。这样就形成了 17 个变量的输入集。我们从 EURUSD 里取最后 6000 根柱线的报价, 2016 年 02 月 14 日 M30 时间帧, 并使用 In() 函数计算指标值。
#---2--------------------------------------------- In <- function(p = 16){ require(TTR) require(dplyr) require(magrittr) adx <- ADX(price, n = p) %>% as.data.frame %>% mutate(.,oscDX = DIp - DIn) %>% transmute(.,DX, ADX, oscDX) %>% as.matrix() ar <- aroon(price[ ,c('High', 'Low')], n = p) %>% extract(,3) atr <- ATR(price, n = p, maType = "EMA") %>% extract(,1:2) cci <- CCI(price[ ,2:4], n = p) chv <- chaikinVolatility(price[ ,2:4], n = p) cmo <- CMO(price[ ,'Med'], n = p) macd <- MACD(price[ ,'Med'], 12, 26, 9) %>% as.data.frame() %>% mutate(., vsig = signal %>% diff %>% c(NA,.) %>% multiply_by(10)) %>% transmute(., sign = signal, vsig) %>% as.matrix() rsi <- RSI(price[ ,'Med'], n = p) stoh <- stoch(price[ ,2:4], nFastK = p, nFastD =3, nSlowD = 3, maType = "EMA") %>% as.data.frame() %>% mutate(., oscK = fastK - fastD) %>% transmute(.,slowD, oscK) %>% as.matrix() smi <- SMI(price[ ,2:4],n = p, nFast = 2, nSlow = 25, nSig = 9) kst <- KST(price[ ,4])%>% as.data.frame() %>% mutate(., oscKST = kst - signal) %>% select(.,oscKST) %>% as.matrix() In <- cbind(adx, ar, atr, cci, chv, cmo, macd, rsi, stoh, smi, kst) return(In) }
我们将在输出上获得输入数据矩阵。
2.2输出数据 (目标白能量)
我们采用取自 ZZ 的信号作为目标变量。函数计算之字折线信号:
#----3------------------------------------------------ ZZ <- function(pr = price, ch = ch , mode="m") { require(TTR) require(magrittr) if (ch > 1) ch <- ch/(10 ^ (Dig - 1)) if (mode == "m") {pr <- pr[ ,'Med']} if (mode == "hl") {pr <- pr[ ,c("High", "Low")]} if (mode == "cl") {pr <- pr[ ,c("Close")]} zz <- ZigZag(pr, change = ch, percent = F, retrace = F, lastExtreme = T) n <- 1:length(zz) dz <- zz %>% diff %>% c(., NA) sig <- sign(dz) for (i in n) { if (is.na(zz[i])) zz[i] = zz[i - 1]} return(cbind(zz, sig)) }
函数参数:
pr = 价格 – OHLCMed 报价的矩阵;
ch – 之字折线弯曲的最小长度点数 (4 位) 或是实际项 (例如, ch = 0.0035);
mode – 应用的价格 ("m" - 中间价, "hl" - 最高价和最低价, "cl" - 收盘价), 省缺使用中间价。
函数返回两个变量的矩阵 — 事实上, 之字折线和信号, 是在之字折线角度范围 [-1;1] 的基础上取得的。我们将信号向左平移一根柱线 (朝向未来)。这个特定的信号将用于训练神经网络。
我们计算信号时 ZZ 弯曲长度至少 37 点 (4 位)。
> out <- ZZ(ch = 37, mode = "m") Loading required package: TTR Loading required package: magrittr > table(out[ ,2]) -1 1 2828 3162
如我们所见, 分级略有失衡。当形成用于训练模型的样本时, 我们将采取必要的措施来令它们平衡。
2.3. 初始化数据帧
我们来编写创建初始数据帧的函数, 清理不确定的数据 (空) 并转换目标变量至两个分级的因子 "-1" 和 "+1"。函数组合之前编写的函数 In() 和 ZZ()。我们将立即裁剪最后 500 根柱线, 用于评估模型预测质量。
#-----4--------------------------------- form.data <- function(n = 16, z = 37, len = 500){ require(magrittr) x <- In(p = n) out <- ZZ(ch = z, mode = "m") data <- cbind(x, y = out[ ,2]) %>% as.data.frame %>% head(., (nrow(x)-len))%>% na.omit data$y <- as.factor(data$y) return(data) }
2.3.1. 删除高相关性变量
我们将从初始集合里面删除相关性系数高于 0.9 的变量。我们将编写形成初始数据帧的函数, 删除高相关性变量并返回清理后数据。
我们可以预先检查哪一个变量的相关性系数高于 0.9。
> data <- form.data(n = 16, z = 37) # 准备数据帧 > descCor <- cor(data[ ,-ncol(data)])# 删除目标变量 > summary(descCor[upper.tri(descCor)]) Min. 1st Qu. Median Mean 3rd Qu. Max. -0.1887 0.0532 0.2077 0.3040 0.5716 0.9588 > highCor <- caret::findCorrelation(descCor, cutoff = 0.9) > highCor [1] 12 9 15 > colnames(data[ ,highCor]) [1] "rsi" "cmo" "SMI"
因此, 以上列出的变量可以删除。我们将从数据帧里删除它们。
> data.f <- data[ ,-highCor] > colnames(data.f) [1] "DX" "ADX" "oscDX" "ar" "tr" [6] "atr" "cci" "chv" "sign" "vsig" [11] "slowD" "oscK" "signal" "vol" "Class"
我们可在一个函数里紧凑地编写它:
#---5----------------------------------------------- cleaning <- function(n = 16, z = 37, cut = 0.9){ data <- form.data(n, z) descCor <- cor(data[ ,-ncol(data)]) highCor <- caret::findCorrelation(descCor, cutoff = cut) data.f <- data[ ,-highCor] return(data.f) } > data.f <- cleaning()
并非所有软件包的作者和研究人员都同意将高相关性的数据从集合中删除。不过, 两种使用结果会在此进行比较。在我们的情况中, 我们选择的选项是删除。
2.4. 最重要变量的选择
重要变量的选择基于三个指标: 按照分级的全局重要性, 局部重要性 (结合) 和部分重要性。我们要把握前文详述的 "randomUniformForest" 软件包的机会。为了紧凑性, 所有之前和之后的动作均在一个函数收集。一旦执行, 我们将获取三个集合作为结果:
- 具有最佳贡献和交互的变量;
- 对于分级 "-1" 的最佳变量;
- 对于分级 "+1" 的最佳变量。
#-----6------------------------------------------------ prepareBest <- function(n, z, cut, method){ require(randomUniformForest) require(magrittr) data.f <<- cleaning(n = n, z = z, cut = cut) idx <- rminer::holdout(y = data.f$Class) prep <- caret::preProcess(x = data.f[idx$tr, -ncol(data.f)], method = method) x.train <- predict(prep, data.f[idx$tr, -ncol(data.f)]) x.test <- predict(prep, data.f[idx$ts, -ncol(data.f)]) y.train <- data.f[idx$tr, ncol(data.f)] y.test <- data.f[idx$ts, ncol(data.f)] #--------- ruf <- randomUniformForest( X = x.train, Y = y.train, xtest = x.test, ytest = y.test, mtry = 1, ntree = 300, threads = 2, nodesize = 1 ) imp.ruf <- importance(ruf, Xtest = x.test) best <- imp.ruf$localVariableImportance$classVariableImportance %>% head(., 10) %>% rownames() #-----部分重要 best.sell <- partialImportance(X = x.test, imp.ruf, whichClass = "-1", nLocalFeatures = 7) %>% row.names() %>% as.numeric() %>% colnames(x.test)[.] best.buy <- partialImportance(X = x.test, imp.ruf, whichClass = "1", nLocalFeatures = 7) %>% row.names() %>% as.numeric() %>% colnames(x.test)[.] dt <- list(best = best, buy = best.buy, sell = best.sell) return(dt) }
我们将声明函数计算的顺序。官方参数:
n – 输入数据参数;
z – 输出数据参数;
cut – 变量的相关性阀值;
method – 输入数据预处理方法。
计算顺序:
- 创建初始的 data.f 集合, 高相关性变量已移除, 并将之保存以备后用;
- 识别训练索引和 idx 的测试样本;
- 判断 prep 的预处理参数;
- 将初始样本划分为训练和测试样本, 输入数据规范化;
- 在所获取的集合之上获取并测试 ruf 模型;
- 计算 imp.ruf 变量的重要性;
- 选择 10 个贡献和交互度最重要的变量 — 最佳;
- 选择 7 个对于 "-1" 和 "+1" 分级最重要的变量 — 最佳买入, 最佳卖出;
- 创建三个预测器的集合列表 — 最佳, 最佳买入, 最佳卖出。
我们将计算这些样本, 并评估所选择变量在全局、局部和部分的重要性数值。
> dt <- prepareBest(16, 37, 0.9, c("center", "scale","spatialSign")) 加载所需软件包: randomUniformForest 标签 -1 1 已被转换到 1 2 为了 便于计算并将在内部使用 作为 替换。 1 - 全局变量重要性 (14 基于所获信息最重要) : 注意: 大多的预测特征按照 'score' 排序并作图。大多数判别式 应考虑搜索 'class' 和 'class.frequency'。 变量得分 class class.frequency 百分比 1 cci 4406 -1 0.51 100.00 2 signal 4344 -1 0.51 98.59 3 ADX 4337 -1 0.51 98.43 4 sign 4327 -1 0.51 98.21 5 slowD 4326 -1 0.51 98.18 6 chv 4296 -1 0.52 97.51 7 oscK 4294 -1 0.52 97.46 8 vol 4282 -1 0.51 97.19 9 ar 4271 -1 0.52 96.95 10 atr 4237 -1 0.51 96.16 11 oscDX 4200 -1 0.52 95.34 12 DX 4174 -1 0.51 94.73 13 vsig 4170 -1 0.52 94.65 14 tr 4075 -1 0.50 92.49 percent.importance 2 - 局部变量重要性 变量交互 (10 在首行和第二列顺序的最重要变量) : 对于每个变量 (每个顺序), 它们彼此间的交互性 也 已计算。 cci slowD atr tr DX atr 0.1804 0.1546 0.1523 0.1147 0.1127 cci 0.1779 0.1521 0.1498 0.1122 0.1102 slowD 0.1633 0.1375 0.1352 0.0976 0.0956 DX 0.1578 0.1319 0.1297 0.0921 0.0901 vsig 0.1467 0.1209 0.1186 0.0810 0.0790 oscDX 0.1452 0.1194 0.1171 0.0795 0.0775 tr 0.1427 0.1168 0.1146 0.0770 0.0750 oscK 0.1381 0.1123 0.1101 0.0725 0.0705 sign 0.1361 0.1103 0.1081 0.0704 0.0685 signal 0.1326 0.1068 0.1045 0.0669 0.0650 avg1rstOrder 0.1452 0.1194 0.1171 0.0795 0.0775 vsig oscDX oscK signal ar atr 0.1111 0.1040 0.1015 0.0951 0.0897 cci 0.1085 0.1015 0.0990 0.0925 0.0872 slowD 0.0940 0.0869 0.0844 0.0780 0.0726 DX 0.0884 0.0814 0.0789 0.0724 0.0671 vsig 0.0774 0.0703 0.0678 0.0614 0.0560 oscDX 0.0759 0.0688 0.0663 0.0599 0.0545 tr 0.0733 0.0663 0.0638 0.0573 0.0520 oscK 0.0688 0.0618 0.0593 0.0528 0.0475 sign 0.0668 0.0598 0.0573 0.0508 0.0455 signal 0.0633 0.0563 0.0537 0.0473 0.0419 avg1rstOrder 0.0759 0.0688 0.0663 0.0599 0.0545 chv vol sign ADX avg2ndOrder atr 0.0850 0.0850 0.0847 0.0802 0.1108 cci 0.0824 0.0824 0.0822 0.0777 0.1083 slowD 0.0679 0.0679 0.0676 0.0631 0.0937 DX 0.0623 0.0623 0.0620 0.0576 0.0881 vsig 0.051.0404 0.0359 0.0665 signal 0.0372 0.0372 0.0369 0.0324 0.0630 avg1rstOrder 0.0497 0.0497 0.0495 0.0450 0.0000 基于交互性的变量重要性 (10 最重要) : cci atr slowD DX tr vsig oscDX 0.1384 0.1284 0.1182 0.0796 0.0735 0.0727 0.0677 oscK signal sign 0.0599 0.0509 0.0464 覆盖标签的变量重要性 (10 对于每个标签最重要变量条件) : Class -1 Class 1 cci 0.17 0.23 slowD 0.20 0.09 atr 0.14 0.15 tr 0.04 0.12 oscK 0.08 0.03 vsig 0.06 0.08 oscDX 0.04 0.08 DX 0.07 0.08 signal 0.05 0.04 ar 0.04 0.02
结果
- 在全局重要性方面, 所有 14 个输入变量相等。
- 按照整体贡献 (全局重要性) 和交互性 (局部重要性) 定义的 10 个最佳。
- 每个分级的七个最佳部分重要性变量显示在以下图表。
图例. 2. 对于分级 "1", 变量的部分重要性
图例. 3. 对于分级 "-1", 变量的部分重要性
正如我们所看到的, 对于不同分级, 最重要的变量在结构和排名上均有差异。所以, 如果对于 "-1" 分级, slowD 变量是最重要的, 而对于 "+1" 分级它仅排位第 4。
因此, 我们的数据集合已就绪。现在我们可以进行实验。
3. 实验部分。
实验将指定以 R 语言进行 — 革命 R 开源, 版本 3.2.2, 由 Revolution Analytics 公司发行。http://www.revolutionanalytics.com/revolution-r-open
该发行版本在正规的 R 3.2.2 之上有编号 advantages :
- 通过应用 Intel® 数学内核库 的多线程处理, 进行快速且更定性的计算;
- 可重现 R 工具箱高级功能。一个小小的澄清: R 语言正在积极发展, 不断改进现有软件包, 并添加新功能。这种进展的反面则涉及到可重现的丧失。也就是说, 您几个月之前编写的产品运行良好, 但在下次软件包更新后突然停工。大量的时间会浪费在识别并清除由更新的软件包导致的错误。例如, 第一篇深度神经网络文章附加的智能交易系统在创建之时功能良好。然而, 发布几个月之后一些用户抱怨它已不可运行。分析表明是更新的 "svSocket" 软件包引起智能交易系统的故障, 而且我无法找到其背后的原因。最后版本的智能交易系统将附加在本文。这个问题已经成为一个迫切的问题, 在 Revolution Analytics 里它很容易解决。现在, 当新版本发行时, 所有在 CRAN 存储库中的软件包会在发布日期被固定, 并拷贝至它们的镜像。在此日期之后 CRAN 存储库中的任何变化都不会影响 Revolution 镜像上的 "冻结" 软件包。此外, 从 2014 年 10 月开始, 公司每天制作 CRAN 存储库的快照, 修复软件包相关的状态和版本。依照他们自己的 "检查点" 软件包, 我们现在可以下载所需日期的相关必要软件包。换言之, 我们是在操纵某种时间机器。
另一个新闻。九个月前, 当微软收购 Revolution Analytics 时, 它承诺支持他们的发展, 并保持革命 R 开源 (RRO) 的免费发行。随后是一些有关 RRO 和革命 R 企业版新颖特色的若干消息 (并非 R 语言与 SQL 服务器, PowerBI, Azure 和 Cortana Analitics 的集成)。现在我们得到情报, 下一个版本的 RRO 更新版将称为 Microsoft R Open, 而革命 R 企业版 — Microsoft R 服务器。不久以前, 微软已经宣布 R 语言将在 Visual Studio 中提供。R Tools for Visual Studio (RTVS) 随同 Python Tools for Visual Studio 模型。 它将被免费添加到 Visual Studio 中, 并为 R 语言提供一个完整的集成开发环境, 包括编辑和交互式调试脚本。
到本文完成时, Microsoft R Open (R 3.2.3) 已经发布, 因此, 在本文中, 我们将引用此版本的软件包。
3.1. 构建模型
3.1.1. "darch" 软件包的简述
"darch" 版本。0.10.0 软件包提供广泛的函数, 不仅可令我们创建和训练模型, 而且, 从字面上来理解, 它可根据您的喜好一砖一瓦地构建并调整。如前指出的, 深层神经网络由 n 个RBM (n =层-1) 和多层 MLP 神经网络组成。逐层 RBM 预训练在无格式数据基础上执行且无需监督者。神经网络的微调需依赖监督者和格式化数据才可执行。划分训练阶段为我们提供了使用各种体量数据的机会 (但不是结构!), 或者在单独预训练的基础上获得一些各种微调的模型。此外, 如果用于预训练和微调的数据相同, 则可以训练一次, 而不是分成两个阶段。或者, 您可以跳过预训练并只使用多层神经网络, 或在另一方面, 只使用 RBM, 而无需神经网络。同时我们可以访问所有内部参数。软件包适用于高级用户。更进一步, 我们将分析划分过程: 预训练和微调。
3.1.2. 构建 DBN 模型。参数。
我们将描述 DBN 模型建立、训练和测试的过程。
1. 我们使用具有必要参数的构造器来创建名为 "Darch" 的深层体系对象
newDArch(layers, batchSize, ff=FALSE, logLevel=INFO, genWeightFunc=generateWeights),
此处:
- layers: 数组指示层的数量和每一层神经元的数量。例如: layers = c(5,10,10,2) – 一个具有 5 个神经元的输入层 (可见), 两个每层具有 10 个神经元的隐藏层, 以及一个具有 2 个输出的输出层。
- BatchSize: 训练期间微型样本的尺寸。
- ff: 指示 ff 格式是否应用于权重、背离和退出。ff 格式用来压缩保存大体量数据。
- LogLevel: 执行此功能时日志和输出的级别。
- GenWeightFunction: 用于生成 RBM 权重矩阵的函数。这是一个使用用户激活功能的机会。
创建的 darch 对象包含 (layers - 1) RBM, 组合到累积的网络并将用于预训练神经网络。两个属性 fineTuneFunction 和 executeFunction 包括的函数用于微调 (backpropagation 省缺) 和用于执行 (runDarch 省缺)。神经网络的训练是通过两个训练函数来执行: preTrainDArch() 和 fineTuneDArch()。第一个函数使用对比背离的方法训练 RBM 网络且无需监督者。第二个函数使用在 fineTuneFunction 属性里指明的函数进行神经网络微调。在神经网络执行之后, 每层的输出可以在 executeOutputs 属性中找到, 或者 executeOutput 属性中仅有的输出层。
2. darch 对象预训练函数
preTrainDArch(darch, dataSet, numEpoch = 1, numCD = 1, ..., trainOutputLayer = F),
此处:
- darch: 'Darch' 类的实例;
- dataSet: 用于训练的数据;
- numEpoch: 训练周期的次数;
- numCD : 采样迭代次数。通常, 一就足够了;
- ... : 附加参数可传送到 trainRBM 函数;
- trainOutputLayer: 逻辑值显示是否 RBM 的输出层应该训练。
函数为每个 RBM 执行 trainRBM() 训练函数, 拷贝训练后的权重和背离至相关 darch 对象的神经网络层。
3. darch 对象的微调函数
fineTuneDArch(darch, dataSet, dataSetValid = NULL, numEpochs = 1, bootstrap = T, isBin = FALSE, isClass = TRUE, stopErr = -Inf, stopClassErr = 101, stopValidErr = -Inf, stopValidClassErr = 101, ...),
此处:
- darch: 'Darch' 类的样本;
- dataSet: 训练数据集合 (可用于验证) 和测试;
- dataSetValid : 用于验证的数据集合;
- numxEpoch: 训练周期的次数;
- bootstrap: 逻辑, 当创建验证数据时是否需要应用自举;
- isBin: 指示输出数据是否应解释为逻辑值. 省缺 — FALSE。如果 TRUE, 每个高于 0.5 的值解释为 1, 而低于 — 则为 0。
- isClass : 指示是否针对分类训练网络。如果 TRUE, 则确定针对分类统计。TRUE 为省缺。
- stopErr : 由于训练期间发生的错误而导致神经网络停止训练的标准。-Inf 为省缺;
- stopClassErr : 由于训练期间发生的分类错误而导致神经网络停止训练的标准。101 为省缺;
- stopValidErr : criterion for stopping the neural network due to error in validation data. -Inf 为省缺;
- stopValidClassErr : 由于验证期间发生的分类错误而导致神经网络停止的标准。101 为省缺;
- ... : 可以传递到训练函数的参数。
函数训练网络是通过保存在 darch 对象 fineTuneFunction 属性中的函数。属于它们 (targetData, validTargets, testTargets) 的输入数据 (trainData, validData, testData) 和类可以作为 dataSet 或 ff-matrix 转移。用来验证和测试的数据和类不是强制性的。如果提供它们, 则将使用这些数据集合来执行神经网络, 并且计算统计。属性 isBin 指示是否输出数据应解释为二进制。如果 isBin = TRUE, 每个高于 0.5 的输出数据解释为 1, 否则 — 为 0。还有, 我们可以在训练或验证集合上设置一个基于错误 (stopErr, stopValidErr) 或正确分类 (stopClassErr, stopValidClassErr) 的训练停止标准。
所有函数参数都有默认值。当然, 也可以使用其它值。因此, 例如:
神经元激活函数 — sigmoidUnitDerivative, linearUnitDerivative, softmaxUnitDerivative, tanSigmoidUnitDerivative 是可用的。 sigmoidUnitDerivative 作为省缺。
神经网络的微调函数 — backpropagation 作为省缺, 弹性传播 rpropagation 也可用在四个变量 ("Rprop+", "Rprop-", "iRprop+", "iRprop-") 和 minimizeClassifier (此函数经 Darch 网络分类器使用非线性共轭梯度法训练) 中。对于后两个算法以及对主题有深入了解的人, 提供了神经网络进行多参数配置微调的单独实现。例如:
rpropagation(darch, trainData, targetData, method="iRprop+", decFact=0.5, incFact=1.2, weightDecay=0, initDelta=0.0125, minDelta=0.000001, maxDelta=50, ...),
此处:
- darch – 用于训练的 darch 对象;
- trainData – 用于训练的输入数据;
- targetData – 期望的用于训练的输出集合;
- method – 训练方法。"iRprop+" 为省缺。"Rprop+", "Rprop-", "iRprop-" 是可能的;
- decFact – 用于训练的递减因子。0.5 为省缺;
- incFact - 用于训练的递增因子。1.2 为省缺;
- weightDecay – 在训练时递减权重。0 为省缺;
- initDelta – 在更新时的初始值。0.0125 为省缺;
- minDelta – 步长尺寸的最小边界。0.000001 为省缺;
- maxDelta – 步长尺寸的最大边界。50 为省缺。
函数返回已训练的神经网络 darch 对象。
3.2. 训练和测试样品的形成。
我们已形成了初始数据样本。现在, 我们需要将其分为训练、验证和测试样本。省缺比率为 2/3。各种软件包均有许多用于分割样本的函数。我使用 rminer::holdout(), 它计算将初始样本分解为训练和测试的样本。
holdout(y, ratio = 2/3, internalsplit = FALSE, mode = "stratified", iter = 1, seed = NULL, window=10, increment=1),
此处:
- y – 期望目标变量, 数字向量或因子, 在此情况下, 要进行分层拆分 (即, 分级间的比例对于所有部分均相同);
- ratio – 拆分比率 (百分比 — 训练样本的大小已建立; 或样本总数 — 测试样本大小已建立);
- internalsplit – 如果 TRUE, 则训练数据就要再次拆分为训练和验证样本。对于内部拆分应用相同的比率;
- mode – 采样模式。可用选项:
- stratified – 分层随机切分 (如果 у 因子; 否则标准随机切分);
- random – 标准随机切分;
- order – 静态模式, 当第一个例程用于训练时, 且其余的 — 用于测试 (广泛应用于时间序列);
- rolling – 滚动窗口, 通常称为滑动窗口 (广泛应用于股票和金融市场的预测), 类似于 order, 除了窗口指代窗口大小, iter — 滚动迭代和增量 — 每次迭代时窗口向前滑动的样本数量。窗口每次迭代时的训练样本大小是固定的, 而测试样本等于比率, 除了最后一次迭代 (这次可能较少)。
- incremental – 重新训练的增量模式, 也称为增增量窗口, 与 order 相同, 除了窗口是初始窗口大小, iter - 增量迭代和增量 - 每次迭代添加的样本数量。训练样本的大小在每次迭代时增长 (+增量), 而测试集合的大小等于比率, 除了最后一次迭代, 此处它可能较少。
- iter – 重新训练增量模式的迭代数量 (仅用在 mode = "rolling" 或 "incremental", iter 通常设为循环)。
- seed – 若是 NULL, 则使用随机种子, 否则种子为固定 (以后的计算一直返回相同结果);
- window – 训练窗口的大小 (若是 mode = "rolling") 或训练窗口初始尺寸 (若 mode = "incremental");
- increment – 每次迭代时训练窗口增加的样本数量 (若 mode="incremental" 或 mode="rolling")。
3.2.1. 分级平衡和预处理。
我们将编写一个函数, 此函数将样本中的分级数量向更高的数量对齐 (如果需要的话), 切分样本为训练和测试样本, 执行预处理 (若必要, 规范化), 并返回相对应样本的列表 — 训练, 测试。为了达成平衡, 我们将会使用 caret::upSample() 函数, 增加样本随机替换, 使得分级的分布相等。我必须说, 并不是所有的研究人员都发现有必要平衡分级。但是, 正如已知的, 实践是检验真理的标准, 并且我的多个实验结果表明, 样本平衡在训练中总是显示出更佳的结果。尽管, 这不会阻止我们进行自己的实验。
对于预处理我们将使用 caret::preProcess() 函数。预处理参数将保存在 prepr 变量。由于我们已在前文中研究并应用过它们, 因此我不会在这里进一步赘述。
#---7----------------------------------------------------
prepareTrain <- function(x , y,
rati, mod = "stratified",
balance = F,
norm, meth)
{
require(magrittr)
require(dplyr)
t <- rminer::holdout(y = y, ratio = rati,
mode = mod)
train <- cbind(x[t$tr, ], y = y[t$tr])
if(balance){
train <- caret::upSample(x = train[ ,best],
y = train$y,
list = F)%>% tbl_df
train <- cbind(train[ ,best], select(train, y = Class))
}
test <- cbind(x[t$ts, ], y = y[t$ts])
if (norm) {
prepr <<- caret::preProcess(train[ ,best], method = meth)
train = predict(prepr, train[ ,best])%>% cbind(., y = train$y)
test = predict(prepr, test[ ,best] %>% cbind(., y = test$y))
}
DT <- list(train = train,
test = test)
return(DT)
}
有关预处理的一个注释: 输入变量将被规范化到范围 (-1, 1)。
3.2.2. 目标变量编码
当求解分类任务时, 目标变量是具有几个级别的因子。在模型中, 它被设置为由后续目标状态组成的向量 (列)。例如, y = с("1", "1", "2", "3", "1")。为了训练神经网络, 目标变量必须被编码 (变换) 为列数等于分级数的矩阵。矩阵的每一行中, 只有一列可以包含 1。这种变换在输出层伴同使用 softmax() 激活函数, 可以获得每级预测目标变量的状态概率。函数 classvec2classmat() 将用来编码。这不仅是为目标变量编码的最佳方法, 而且因其简单我们才会用它。目标变量预测值的逆变换 (解码) 是通过我们即将涵盖的不同方法来实现的。
3.3. 训练模型
3.3.1. 预训练
如上所述, 首先, 我们创建名为 DArch 的深层体系对象, 包括所需数量的具有省缺初始训练参数的 RBM, 以及省缺采用随机权重和神经元激活函数集合的初始化神经网络。在对象创建阶段, 如有必要, 可以改变预训练参数。之后, 通过发送训练样本 (没有目标变量) 到输出来预训练 RPM 网络且无需监督者。当其完成之后, 我们得到 DАrch, 此处将在 RBM 训练期间所获得的权重和背离传递到神经网络。我们应以向量的形式预先设置隐藏神经元在层中的分布 (例如):
L<- c( 14, 50, 50, 2)
输入层中的神经元数量等于输入变量的数量。两个隐藏层每层将包含 50 个神经元, 输出层将有两个。我来解释最后一点。如果目标变量 (因子) 具有两个级别, 则事实上一个输出就足够了。但是将向量转换为两列矩阵, 它们当中的每一个对应于一个分级, 可令我们应用 softmax激活函数, 它会在分类任务的输出层操作良好。再有, 分级概率形式的输出令我们在随后的结果分析中拥有额外的机会。本主题将很快涵盖。
当训练设为实验时周期数通常在 10-50 的范围内。
采样迭代次数将保持省缺状态, 但如果您想要进行实验, 可以增加此参数。它将在单独的函数中定义:
#----8-------------------------------------------------------------- pretrainDBN <- function(L, Bs, dS, nE, nCD, InM = 0.5, FinM = 0.9) { require(darch) # 创建 DArch dbn <- newDArch(layers = L, batchSize = Bs, logLevel = 5) # 设置初始量 setInitialMomentum(dbn) <- InM # 设置最终量 setFinalMomentum(dbn) <- FinM # 设置从初始到最终量的切换时间 setMomentumSwitch(dbn) <- round(0.8 * nE) dbn <- preTrainDArch(dbn, dataSet = dS, numEpoch = nE, numCD = nCD, trainOutputLayer = T) return(dbn) }
3.3.2. 微调
正如之前讨论的, 软件包提供 backpropagation(), rpropagation(), minimizeClassifier(), minimizeAutoencoder() 用于微调。最后两个将不考虑, 因为它们在软件包里没有足够的文档, 且没有如何应用它们的例程。这些函数在我的实验中没有显示出良好的效果。
我也想添加一些关于软件包的更新。当我开始写这篇文章时, 当时的版本是 0.9, 可当我完成它时, 一个包含多个更新的 0.10 版本已发布。所有的计算必须重做。基于短期测试的结果, 我可以说操作速度增幅相当大, 不像结果的质量 (这更多是用户的错误, 然后是软件包)。
我们来研究前两个函数。第一个 (backpropagation) 在 DАrch 对象里设为省缺, 所使用的训练神经网络参数在此提供。第二个函数 (rpropagation) 也有省缺参数, 以及四个省缺为 "iRprop+" 的训练方法 (如上描述)。您可以毫无疑问地更改这两个参数和训练方法。这些函数很容易应用: 在 FineTuneDarch() 里改变微调函数
setFineTuneFunction(dbn) <- rpropagation
除了微调设置, 我们必须设置 (如有必要) 每层的激活神经元的函数。我们知道 sigmoidUnit 已在所有层省缺设置。软件包里可用函数 sigmoidUnitDerivative, linearUnitDerivative, tanSigmoidUnitDerivative, softmaxUnitDerivative。微调的定义将采用有能力选择微调函数的单独函数。我们将在单独的列表中收集可能的激活功能函数:
actFun <- list(sig = sigmoidUnitDerivative, tnh = tanSigmoidUnitDerivative, lin = linearUnitDerivative, soft = softmaxUnitDerivative)
我们将编写一个微调函数来训练和生成两个神经网络: 第一个 — 使用 backpropagation 函数训练, 第二个 — 采用 rpropagation:
#-----9----------------------------------------- fineMod <- function(variant=1, dbnin, dS, hd = 0.5, id = 0.2, act = c(2,1), nE = 10) { setDropoutOneMaskPerEpoch(dbnin) <- FALSE setDropoutHiddenLayers(dbnin) <- hd setDropoutInputLayer(dbnin) <- id layers <<- getLayers(dbnin) stopifnot(length(layers)==length(act)) if(vers(dbnin) <- 0.0 setFineTuneFunction(dbnin) <- rpropagation } mod = fineTuneDArch(darch = dbnin, dataSet = dS, numEpochs = nE, bootstrap = T) return(mod) } }
一些有关函数的形式参数的澄清。
- variant - 选择微调函数 (1- backpropagation, 2- rpropagation)。
- dbnin - 从预训练接收结果的模式。
- dS - 用于微调的数据集合 (dataSet)。
- hd - 神经网络隐藏层的采样系数 (hiddenDropout)。
- id - 神经网络输入层的采样系数 (inputDropout)。
- act - 神经网络每一层神经元激活函数的指引向量。向量长度比层的数量短一个单位。
- nE - 训练周期的次数。
dataSet — 在此版本中出现的新变量。我真的不理解其出现的背后原因。正常的话, 语言有两种方式转换变量到模型 — 使用 pair (x, y) 或 formula (y~., data)。这个变量的引入并未改进品质, 但却令用户混淆。不过, 作者可能有不为人知的原因。
3.4. 测试模型。标尺。
在测试样品上进行训练模型测试。必须要考虑的是, 我们将计算两个标尺: 正式精确度和定性 K。相关信息将在下面提供。为此目的, 我们将需要两套不同的数据样本, 我将向您解释为什么。为了计算精度, 我们需要目标变量的值, 和之字折线, 如我们之前印象, 最后一根柱线常常没有定义。因此, 计算精度的测试样本我们确定将用 prepareTrain() 函数, 对于定性指标我们将使用以下函数
#---10------------------------------------------- prepareTest <- function(n, z, norm, len = 501) { x <- In(p = n ) %>% na.omit %>% extract( ,best) %>% tail(., len) CO <- price[ ,"CO"] %>% tail(., len) if (norm) { x <- predict(prepr,x) } dt <- cbind(x = x, CO = CO) %>% as.data.frame() return(dt) }
模型将依据历史最后 500 根柱线进行测试。
为了实际测试, 将应用 testAcc() 和 testBal()。
#---11----- testAcc <- function(obj, typ = "bin"){ x.ts <- DT$test[ ,best] %>% as.matrix() y.ts <- DT$test$y %>% as.integer() %>% subtract(1) out <- predict(obj, newdata = x.ts, type = typ) if (soft){out <- max.col(out)-1} else {out %<>% as.vector()} acc <- length(y.ts[y.ts == out])/length(y.ts) %>% round(., digits = 4) return(list(Acc = acc, y.ts = y.ts, y = out)) } #---12----- testBal <- function(obj, typ = "bin") { require(fTrading) x <- DT.test[ ,best] CO <- DT.test$CO out <- predict(obj, newdata = x, type = typ) if(soft){out <- max.col(out)-1} else {out %<>% as.vector()} sig <- ifelse(out == 0, -1, 1) sig1 <- Hmisc::Lag(sig) %>% na.omit bal <- cumsum(sig1 * tail(CO, length(sig1))) K <- tail(bal, 1)/length(bal) * 10 ^ Dig Kmax <- max(bal)/which.max(bal) * 10 ^ Dig dd <- maxDrawDown(bal) return(list(sig = sig, bal = bal, K = K, Kmax = Kmax, dd = dd)) }
第一个函数返回 Acc 和目标变量值 (实际或预测) 用于可能的进一步分析。第二函数返回用于 EA 的预测信号 sig , 所获平衡基于这些信号 (bal), 品质系数 (К), 在测试区域的系数最大值 (Kmax) 以及同一区域的最大回撤 (dd)。
当计算平衡时, 要记住的重要是, 最后预测信号指向尚未形成的未来柱线, 因此, 应该在计算时将其删除。我们已经通过将 sig 向量右移一根柱线来解决它。
3.4.1. 预测解码。
所获结果可使用 "WTA" 方法解码 (从矩阵转换为向量)。分级等于具有最大概率值的列数, 并概率的阈值可以设置, 低于阈值时分级不可确定。
out <- classmat2classvec(out, threshold = 0.5) 或 out <- max.col(out)-1
如果阈值设置为 0.5, 且列中最大的概率低于此阈值, 我们将获得一个附加分级 ("未定义")。当计算像是 Accuracy 矩阵时应予以考虑。