神经网络变得轻松(第三十六部分):关系强化学习·进阶篇
(2/3)·卷积网络能认出对象却建不起联系,通道假突破正是被这点坑惨
「初始化时把神经网与指标缓冲区绑起来」
EA 在 OnInit 阶段先尝试加载已训练好的网络文件,后缀优先找 .icm,找不到再退回到 .nnw 配合 .fwd / .inv 描述文件,且硬性要求 6 个嵌入维度。若两者都缺失,则现场调用 CreateDescriptions 构造模型结构,任何一步 new 出来的对象若建网失败必须显式 delete 后返回 INIT_FAILED,避免 MT5 终端内存泄漏。 网络就绪后立刻切到训练模式(TrainMode(true)),并用 SetBufferSize(Batch, 10 * Batch) 给在线学习留 10 倍批量的滑动缓冲。随后通过 GetLayerOutput(0, temp) 读取输入层尺寸,用公式 (temp.Total() - 9) / 12 反推 HistoryBars——这意味着输入层每 12 个浮点对应 1 根 K 线,且头部有 9 个非 K 线占位。 RSI、CCI、ATR、MACD 四个指标缓冲区都按 HistoryBars 长度 resize,只要有一个失败就打印函数名与行号并 INIT_FAILED。外汇与贵金属杠杆品种波动剧烈,这类初始化若在生产环境静默失败,后续信号会全部错位,建议开 MT5 把这段直接挂到 EA 头里跑一次看日志。
class="kw">return INIT_FAILED; class=class="str">"cmt">//--- if(!MACD.Create(Symb.Name(), TimeFrame, FastPeriod, SlowPeriod, SignalPeriod, MACDPrice)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- if(!StudyNet.Load(FileName + ".icm", true)) if(!StudyNet.Load(FileName + ".nnw", FileName + ".fwd", FileName + ".inv", class="num">6, true)) { CArrayObj *model = new CArrayObj(); CArrayObj *forward = new CArrayObj(); CArrayObj *inverse = new CArrayObj(); if(!CreateDescriptions(model, forward, inverse)) { class="kw">delete model; class="kw">delete forward; class="kw">delete inverse; class="kw">return INIT_FAILED; } if(!StudyNet.Create(model, forward, inverse)) { class="kw">delete model; class="kw">delete forward; class="kw">delete inverse; class="kw">return INIT_FAILED; } StudyNet.SetStateEmbedingLayer(class="num">6); class="kw">delete model; class="kw">delete forward; class="kw">delete inverse; } if(!StudyNet.TrainMode(true)) class="kw">return INIT_FAILED; StudyNet.SetBufferSize(Batch, class="num">10 * Batch); class=class="str">"cmt">//--- CBufferFloat* temp; if(!StudyNet.GetLayerOutput(class="num">0, temp)) class="kw">return INIT_FAILED; HistoryBars = (temp.Total() - class="num">9) / class="num">12; class="kw">delete temp; if(!RSI.BufferResize(HistoryBars) || !CCI.BufferResize(HistoryBars) || !ATR.BufferResize(HistoryBars) || !MACD.BufferResize(HistoryBars)) { PrintFormat("%s -> %d", __FUNCTION__, __LINE__); class="kw">return INIT_FAILED; } class=class="str">"cmt">//--- if(!Trade.SetTypeFillingBySymbol(Symb.Name())) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class="type">bool CreateDescriptions(CArrayObj *Description, CArrayObj *Forward, CArrayObj *Inverse) { class=class="str">"cmt">//--- if(!Description) { Description = new CArrayObj(); if(!Description) class="kw">return class="kw">false; } class=class="str">"cmt">//--- if(!Forward) { Forward = new CArrayObj(); if(!Forward) class="kw">return class="kw">false; } class=class="str">"cmt">//--- if(!Inverse) {
用 OCL 神经层堆出行情编码器
在 MT5 里搭一套用于价格行为识别的神经网络,第一步是把层结构塞进 Description 容器。下面这段直接给出从输入层到多头注意力的 7 层定义,复制进 EA 的模型初始化函数即可编译跑通。 输入层把 HistoryBars*12*9 个基元神经元铺开,例如 HistoryBars 取 100 时,首层节点数就是 10800;激活函数设 None,优化器统一用 ADAM。第二层接 BatchNorm(batch=1000)做归一,第三层用窗口 3、步长 1 的卷积把节点压到 prev_count-2,再经 LReLU。 往后是两个 LSTM(300 与 100 单元)夹一层卷积,最后用 4 层、窗口 5、步长 64 的多头注意力收口。外汇与贵金属波动剧烈,这类模型仅作概率参考,实盘前务必在策略测试器用历史数据验证过拟合程度。 别把节点数当越大越好 首层 10800 节点在普通显卡上可能直接爆显存;HistoryBars 先调到 20 试水,节点数随之降到 2160,迭代速度会明显友好。
Inverse = new CArrayObj(); if(!Inverse) class="kw">return class="kw">false; class=class="str">"cmt">//--- Model Description.Clear(); CLayerDescription *descr; class=class="str">"cmt">//--- Input layer if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronBaseOCL; class="type">int prev_count = descr.count = HistoryBars * class="num">12 * class="num">9; descr.window = class="num">0; descr.activation = None; descr.optimization = ADAM; if(!Description.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">1 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronBatchNormOCL; descr.count = prev_count; descr.batch = class="num">1000; descr.activation = None; descr.optimization = ADAM; if(!Description.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">2 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronConvOCL; descr.count = prev_count - class="num">2; descr.window = class="num">3; descr.step = class="num">1; descr.window_out = class="num">16; descr.activation = LReLU; descr.optimization = ADAM; if(!Description.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">3 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronLSTMOCL; descr.count = class="num">300; descr.optimization = ADAM; if(!Description.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">4 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronConvOCL; descr.count = class="num">100; descr.window = class="num">3; descr.step = class="num">3; descr.window_out = class="num">10; descr.activation = LReLU; descr.optimization = ADAM; if(!Description.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">5 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronLSTMOCL; descr.count = class="num">100; descr.optimization = ADAM; if(!Description.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">6 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronMLMHAttentionOCL; descr.count = class="num">20; descr.window = class="num">5; descr.step = class="num">64; descr.window_out = class="num">16; descr.layers = class="num">4; descr.optimization = ADAM;
◍ 给自编码网络铺层与通道
这段逻辑在 MT5 里搭的是一个对称式自编码结构,分 Description、Forward、Inverse 三条链。Description 里第 7 层用 defNeuronFQF 类型,count=4、window_out=32、优化器 ADAM,负责把特征压缩成低频量子滤波输出。 Forward 链先 Clear 再建层:输入层 104 节点(defNeuronBaseOCL,激活 None),layer1 扩到 500 节点用 LReLU,layer2 收成 100 节点无激活,全程 ADAM。Inverse 链镜像回来,输入 200 节点、layer1 同是 500+LReLU、layer2 压到 4 节点,用来做重构。 每一层 new 出来若加不进容器就 delete 并 return false,这是防内存泄漏的硬写法。你在 EA 里改 count 数值时,Forward 输入 104 和 Inverse 输出 4 必须和 Description 的窗口/维度对上,否则训练会直接初始化失败。 外汇与贵金属行情用这类网络做特征提取,属于高波动高风险场景,参数过拟合可能导致实盘信号失真,回测顺不代表活盘稳。
if(!Description.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">7 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronFQF; descr.count = class="num">4; descr.window_out = class="num">32; descr.optimization = ADAM; if(!Description.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- Forward Forward.Clear(); class=class="str">"cmt">//--- Input layer if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronBaseOCL; descr.count = class="num">104; descr.window = class="num">0; descr.activation = None; descr.optimization = ADAM; if(!Forward.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">1 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronBaseOCL; descr.count = class="num">500; descr.activation = LReLU; descr.optimization = ADAM; if(!Forward.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">2 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronBaseOCL; descr.count = class="num">100; descr.activation = None; descr.optimization = ADAM; if(!Forward.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- Inverse Inverse.Clear(); class=class="str">"cmt">//--- Input layer if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronBaseOCL; descr.count = class="num">200; descr.window = class="num">0; descr.activation = None; descr.optimization = ADAM; if(!Inverse.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">1 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronBaseOCL; descr.count = class="num">500; descr.activation = LReLU; descr.optimization = ADAM; if(!Inverse.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">2 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false; descr.type = defNeuronBaseOCL; descr.count = class="num">4; descr.activation = None; descr.optimization = ADAM; if(!Inverse.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- layer class="num">3 if(!(descr = new CLayerDescription())) class="kw">return class="kw">false;
「给逆变换层挂上 SoftMax 输出」
上面这段是给神经网络逆变换(Inverse)部分追加一个 SoftMax 输出层的典型写法。descr 先被指定为 defNeuronSoftMaxOCL 类型,意味着该层走 OpenCL 的 SoftMax 实现,适合在 GPU 上跑多分类概率归一化。 descr.count = 4 表示这一层固定输出 4 个节点,step = 1 是层间连接步长,activation = None 说明 SoftMax 本身已含归一化,不再叠额外激活;optimization 选 ADAM,学习率自适应在金融序列里更稳。 Add 失败就 delete descr 并 return false,避免悬空描述符泄漏显存;成功才 return true。开 MT5 把这段塞进你的 CNet 派生类,改 count 就能直接验证不同输出维度对贵金属分类信号的影响,外汇与贵金属杠杆高,回测结论仅代表历史概率。
descr.type = defNeuronSoftMaxOCL; descr.count = class="num">4; descr.step = class="num">1; descr.activation = None; descr.optimization = ADAM; if(!Inverse.Add(descr)) { class="kw">delete descr; class="kw">return class="kw">false; } class=class="str">"cmt">//--- class="kw">return true; }