神经网络变得轻松(第十二部分):舍弃·综合运用
「在 OpenCL 层里挂 Dropout 与多头注意力」
这段代码是神经网络工厂方法里的两个 case 分支,专门在 GPU(OpenCL)上下文里实例化正则化与注意力层。先看 defNeuronMLMHAttentionOCL:它 new 一个 CNeuronMLMHAttentionOCL,马上用 CheckPointer 判 POINTER_INVALID,失败就删掉临时容器 temp 并 return,避免野指针进网络图。 初始化调用 Init 传了 9 个参数,其中 desc.window 与 desc.window_out 决定注意力滑动窗口的入出长度,desc.step 是步长,desc.count 是头数,desc.layers 是堆叠层数;任一项返回 false 都会连续 delete 神经元与 temp 后退出。 被高亮的 defNeuronDropoutOCL 分支逻辑同构:new CNeuronDropoutOCL 后判指针,再 Init(outputs,0,opencl,desc.count,desc.probability,desc.optimization)。这里 desc.probability 是丢弃概率,训练时按该比例置零激活值,对过拟合有概率上的抑制倾向。 两个分支末尾都把局部指针置 NULL 并 break,说明工厂只负责装配不持有所有权,temp 容器接管生命周期。开 MT5 把 desc.probability 从 0.1 调到 0.3,可能明显改变验证集波动,但外汇与贵金属训练属高风险,过拟合缓解不预示实盘收益。
neuron_attention_ocl=NULL; class="kw">break; class=class="str">"cmt">//--- case defNeuronMLMHAttentionOCL: neuron_mlattention_ocl=new CNeuronMLMHAttentionOCL(); if(CheckPointer(neuron_mlattention_ocl)==POINTER_INVALID) { class="kw">delete temp; class="kw">return; } if(!neuron_mlattention_ocl.Init(outputs,class="num">0,opencl,desc.window,desc.window_out,desc.step,desc.count,desc.layers,desc.optimization)) { class="kw">delete neuron_mlattention_ocl; class="kw">delete temp; class="kw">return; } neuron_mlattention_ocl.SetActivationFunction(desc.activation); if(!temp.Add(neuron_mlattention_ocl)) { class="kw">delete neuron_mlattention_ocl; class="kw">delete temp; class="kw">return; } neuron_mlattention_ocl=NULL; class="kw">break; class=class="str">"cmt">//--- case defNeuronDropoutOCL: dropout=new CNeuronDropoutOCL(); if(CheckPointer(dropout)==POINTER_INVALID) { class="kw">delete temp; class="kw">return; } if(!dropout.Init(outputs,class="num">0,opencl,desc.count,desc.probability,desc.optimization)) { class="kw">delete dropout; class="kw">delete temp; class="kw">return; } if(!temp.Add(dropout)) { class="kw">delete dropout;
◍ 神经网络层里的 dropout 与 OpenCL 内核挂载
在 MT5 的 GPU 加速神经网络实现里,dropout 作为一种正则化层被单独封装成 CNeuronDropoutOCL,并在 CLayer::CreateElement 中通过指针 temp_drop_ocl 参与元素构建。当从模型文件读取时,switch(type) 命中 defNeuron 之外的 dropout 分支才会实例化它,否则直接 delete temp 并 return,避免脏指针残留。 内核注册阶段调用 opencl.SetKernelsCount(24) 显式声明了 24 个计算内核,随后用 KernelCreate 逐一绑定名称字符串,例如 FeedForward、UpdateWeightsAdam、AttentionScore 等。其中高亮行 opencl.KernelCreate(def_k_Dropout,"Dropout") 把 dropout 的前向掩码逻辑也推到显卡执行,训练时随机置零部分神经元输出,可能降低外汇时序过拟合倾向,但 GPU 占用与显存带宽压力会随层数线性抬升。 CreateElement 开头先做边界判断 if(index>=m_data_max) return false,再声明一批临时神经元指针(含 temp_drop_ocl=NULL)。若 iFileHandle<=0 走新建分支用 new CNeuron() 初始化;否则读文件按类型还原。这一套在贵金属小时线训练上属于高风险实验,参数没调稳前回测曲线可能剧烈抖动。
class="kw">delete temp; class="kw">return; } dropout=NULL; class="kw">break; class=class="str">"cmt">//--- class="kw">default: class="kw">return; class="kw">break; } } opencl.SetKernelsCount(class="num">24); opencl.KernelCreate(def_k_FeedForward,"FeedForward"); opencl.KernelCreate(def_k_CalcOutputGradient,"CalcOutputGradient"); opencl.KernelCreate(def_k_CalcHiddenGradient,"CalcHiddenGradient"); opencl.KernelCreate(def_k_UpdateWeightsMomentum,"UpdateWeightsMomentum"); opencl.KernelCreate(def_k_UpdateWeightsAdam,"UpdateWeightsAdam"); opencl.KernelCreate(def_k_AttentionGradients,"AttentionInsideGradients"); opencl.KernelCreate(def_k_AttentionOut,"AttentionOut"); opencl.KernelCreate(def_k_AttentionScore,"AttentionScore"); opencl.KernelCreate(def_k_CalcHiddenGradientConv,"CalcHiddenGradientConv"); opencl.KernelCreate(def_k_CalcInputGradientProof,"CalcInputGradientProof"); opencl.KernelCreate(def_k_FeedForwardConv,"FeedForwardConv"); opencl.KernelCreate(def_k_FeedForwardProof,"FeedForwardProof"); opencl.KernelCreate(def_k_MatrixSum,"SumMatrix"); opencl.KernelCreate(def_k_Matrix5Sum,"Sum5Matrix"); opencl.KernelCreate(def_k_UpdateWeightsConvAdam,"UpdateWeightsConvAdam"); opencl.KernelCreate(def_k_UpdateWeightsConvMomentum,"UpdateWeightsConvMomentum"); opencl.KernelCreate(def_k_Normilize,"Normalize"); opencl.KernelCreate(def_k_NormilizeWeights,"NormalizeWeights"); opencl.KernelCreate(def_k_ConcatenateMatrix,"ConcatenateBuffers"); opencl.KernelCreate(def_k_DeconcatenateMatrix,"DeconcatenateBuffers"); opencl.KernelCreate(def_k_MHAttentionGradients,"MHAttentionInsideGradients"); opencl.KernelCreate(def_k_MHAttentionScore,"MHAttentionScore"); opencl.KernelCreate(def_k_MHAttentionOut,"MHAttentionOut"); opencl.KernelCreate(def_k_Dropout,"Dropout"); class="type">bool CLayer::CreateElement(class="type">int index) { if(index>=m_data_max) class="kw">return class="kw">false; class=class="str">"cmt">//--- class="type">bool result=class="kw">false; CNeuronBase *temp=NULL; CNeuronProof *temp_p=NULL; CNeuronBaseOCL *temp_ocl=NULL; CNeuronConvOCL *temp_con_ocl=NULL; CNeuronAttentionOCL *temp_at_ocl=NULL; CNeuronMLMHAttentionOCL *temp_mlat_ocl=NULL; CNeuronDropoutOCL *temp_drop_ocl=NULL; if(iFileHandle<=class="num">0) { temp=new CNeuron(); if(CheckPointer(temp)==POINTER_INVALID || !temp.Init(iOutputs,index,SGD)) class="kw">return class="kw">false; result=true; } else { class="type">int type=FileReadInteger(iFileHandle); class="kw">switch(type) { case defNeuron:
工厂方法里的神经元分支构造
在 MT5 的神经网络类体系里,通常用工厂分支按类型实例化具体神经元。下面这段 switch 逻辑覆盖了基础神经元、证明层、卷积、LSTM 以及 OpenCL 加速基类的创建路径,可直接拷进你自己的 CNet 派生类做对照。 普通分支先 new 出对象,立刻用 CheckPointer 判 POINTER_INVALID;若失败就把 result 置 false 并 break,避免后续对空指针调用 Init。基础神经元只传 iOutputs、index 与优化器 ADAM,不接管 OpenCL 上下文。 Proof / Conv / LSTM 三个分支结构一致:new 出 temp_p 后判指针,再调 Init(iOutputs,index,1,1,1,ADAM),成功才把 temp_p 赋给 temp 并 result=true。注意这里卷积和 LSTM 的 Init 参数写死为 1,1,1,意味着默认窗口与步长都极小,实盘前大概率要按品种波动重调。 OpenCL 分支多一道守卫:先查全局 OpenCL 指针无效就直接 return false,随后 new CNeuronBaseOCL 并 Init(iOutputs,index,OpenCL,1,ADAM),成功直接写 m_data[index] 并返回 true,不再走外层 result 变量。外汇与贵金属行情跳空频繁,GPU 加速虽降延迟,但模型过拟合风险偏高,参数须逐步验证。
temp=new CNeuron(); if(CheckPointer(temp)==POINTER_INVALID) result=class="kw">false; result=temp.Init(iOutputs,index,ADAM); class="kw">break; case defNeuronProof: temp_p=new CNeuronProof(); if(CheckPointer(temp_p)==POINTER_INVALID) result=class="kw">false; if(temp_p.Init(iOutputs,index,class="num">1,class="num">1,class="num">1,ADAM)) { temp=temp_p; result=true; } class="kw">break; case defNeuronConv: temp_p=new CNeuronConv(); if(CheckPointer(temp_p)==POINTER_INVALID) result=class="kw">false; if(temp_p.Init(iOutputs,index,class="num">1,class="num">1,class="num">1,ADAM)) { temp=temp_p; result=true; } class="kw">break; case defNeuronLSTM: temp_p=new CNeuronLSTM(); if(CheckPointer(temp_p)==POINTER_INVALID) result=class="kw">false; if(temp_p.Init(iOutputs,index,class="num">1,class="num">1,class="num">1,ADAM)) { temp=temp_p; result=true; } class="kw">break; case defNeuronBaseOCL: if(CheckPointer(OpenCL)==POINTER_INVALID) class="kw">return class="kw">false; temp_ocl=new CNeuronBaseOCL(); if(CheckPointer(temp_ocl)==POINTER_INVALID) result=class="kw">false; if(temp_ocl.Init(iOutputs,index,OpenCL,class="num">1,ADAM)) { m_data[index]=temp_ocl; class="kw">return true;
「卷积与注意力神经元的 OpenCL 实例化分支」
在 MT5 的神经网络容器里,按类型枚举去 new 对应的 OpenCL 神经元对象,是跑 GPU 加速训练的前置动作。下面这段 switch 分支覆盖了卷积、单头注意力、多头注意力及多层多头注意力四类 OCL 神经元,每一种都先校验 OpenCL 上下文指针,无效直接返回 false。 卷积分支调用 CNeuronConvOCL 的 Init,传入的核尺寸与步长全是 1(1,1,1,1),优化器写死 ADAM;注意力类分支的 Init 参数少一个空间维度,同样是 1×1 窗口加 ADAM。若 new 出来的指针无效,就把外层 result 标为 false,但注意它没立刻 return,而是 break 后由调用方去读 result。 这种写法意味着:你在改写自己的 EA 神经网络层时,若漏了 OpenCL 上下文校验,GPU 端初始化会静默失败,回测里可能表现为某层权重不更新。外汇与贵金属杠杆交易高风险,GPU 加速只解决算力,不解决过拟合。
class="kw">break; case defNeuronConvOCL: if(CheckPointer(OpenCL)==POINTER_INVALID) class="kw">return class="kw">false; temp_con_ocl=new CNeuronConvOCL(); if(CheckPointer(temp_con_ocl)==POINTER_INVALID) result=class="kw">false; if(temp_con_ocl.Init(iOutputs,index,OpenCL,class="num">1,class="num">1,class="num">1,class="num">1,ADAM)) { m_data[index]=temp_con_ocl; class="kw">return true; } class="kw">break; case defNeuronAttentionOCL: if(CheckPointer(OpenCL)==POINTER_INVALID) class="kw">return class="kw">false; temp_at_ocl=new CNeuronAttentionOCL(); if(CheckPointer(temp_at_ocl)==POINTER_INVALID) result=class="kw">false; if(temp_at_ocl.Init(iOutputs,index,OpenCL,class="num">1,class="num">1,ADAM)) { m_data[index]=temp_at_ocl; class="kw">return true; } class="kw">break; case defNeuronMHAttentionOCL: if(CheckPointer(OpenCL)==POINTER_INVALID) class="kw">return class="kw">false; temp_at_ocl=new CNeuronMHAttentionOCL(); if(CheckPointer(temp_at_ocl)==POINTER_INVALID) result=class="kw">false; if(temp_at_ocl.Init(iOutputs,index,OpenCL,class="num">1,class="num">1,ADAM)) { m_data[index]=temp_at_ocl; class="kw">return true; } class="kw">break; case defNeuronMLMHAttentionOCL: if(CheckPointer(OpenCL)==POINTER_INVALID) class="kw">return class="kw">false; temp_mlat_ocl=new CNeuronMLMHAttentionOCL();
◍ Dropout 层在 OpenCL 神经网络里的挂载点
在 MT5 用 OpenCL 跑神经网络时,Dropout 层不是单独存在的黑盒,而是在工厂函数里通过 case 分支动态挂进 m_data 数组。下面这段工厂代码里,defNeuronDropoutOCL 分支先校验 OpenCL 上下文指针有效,再 new 一个 CNeuronDropoutOCL 对象,Init 参数中 0.1 就是丢弃概率——意味着前向传播时约 10% 的神经元输出会被随机置零,这是抑制过拟合的常用比例。 FeedForward 的 switch 同样要把 defNeuronDropoutOCL 列进可接收的上游类型,否则该层收不到前一层的 CNeuronBaseOCL 指针,直接返回 false 导致整图前向中断。calcHiddenGradients 里也声明了 CNeuronDropoutOCL *dropout 变量,说明反向传播时该层需要单独处理梯度掩码。 实际调参时,把 Init 里的 0.1 改成 0.2 或 0.05 能在 MT5 策略测试器里看到训练曲线平滑度变化;外汇与贵金属杠杆高、滑点无常,这类模型仅作概率参考,实盘前务必用历史数据跑通前向与反向链路。
if(CheckPointer(temp_mlat_ocl)==POINTER_INVALID) result=class="kw">false; if(temp_mlat_ocl.Init(iOutputs,index,OpenCL,class="num">1,class="num">1,class="num">1,class="num">1,class="num">0,ADAM)) { m_data[index]=temp_mlat_ocl; class="kw">return true; } class="kw">break; case defNeuronDropoutOCL: if(CheckPointer(OpenCL)==POINTER_INVALID) class="kw">return class="kw">false; temp_drop_ocl=new CNeuronDropoutOCL(); if(CheckPointer(temp_drop_ocl)==POINTER_INVALID) result=class="kw">false; if(temp_drop_ocl.Init(iOutputs,index,OpenCL,class="num">1,class="num">0.1,ADAM)) { m_data[index]=temp_drop_ocl; class="kw">return true; } class="kw">break; class="kw">default: result=class="kw">false; class="kw">break; } } if(result) m_data[index]=temp; class=class="str">"cmt">//--- class="kw">return (result); } class="type">bool CNeuronBaseOCL::FeedForward(CObject *SourceObject) { if(CheckPointer(SourceObject)==POINTER_INVALID) class="kw">return class="kw">false; class=class="str">"cmt">//--- CNeuronBaseOCL *temp=NULL; class="kw">switch(SourceObject.Type()) { case defNeuronBaseOCL: case defNeuronConvOCL: case defNeuronAttentionOCL: case defNeuronMHAttentionOCL: case defNeuronMLMHAttentionOCL: case defNeuronDropoutOCL: temp=SourceObject; class="kw">return feedForward(temp); class="kw">break; } class=class="str">"cmt">//--- class="kw">return class="kw">false; } class="type">bool CNeuronBaseOCL::calcHiddenGradients(CObject *TargetObject) { if(CheckPointer(TargetObject)==POINTER_INVALID) class="kw">return class="kw">false; class=class="str">"cmt">//--- CNeuronBaseOCL *temp=NULL; CNeuronAttentionOCL *at=NULL; CNeuronMLMHAttentionOCL *mlat=NULL; CNeuronConvOCL *conv=NULL; CNeuronDropoutOCL *dropout=NULL; class="kw">switch(TargetObject.Type()) { case defNeuronBaseOCL:
反向传播里如何把梯度甩给 Dropout 层
在自定义神经网络基类里,反向传播要靠 SourceObject 的类型分支把梯度计算派发给不同神经元。原代码用 switch 把 defNeuronDropoutOCL 也纳入了派发路径,说明 Dropout 层在训练阶段同样参与梯度回传,而不是只在正向丢神经元。 [CODE] temp=TargetObject; return calcHiddenGradients(temp); break; case defNeuronConvOCL: conv=TargetObject; temp=GetPointer(this); return conv.calcInputGradients(temp); break; case defNeuronAttentionOCL: case defNeuronMHAttentionOCL: at=TargetObject; temp=GetPointer(this); return at.calcInputGradients(temp); break; case defNeuronMLMHAttentionOCL: mlat=TargetObject; temp=GetPointer(this); return mlat.calcInputGradients(temp); break; case defNeuronDropoutOCL: dropout=TargetObject; temp=GetPointer(this); return dropout.calcInputGradients(temp); break; } //--- return false; } bool CNeuronBaseOCL::UpdateInputWeights(CObject *SourceObject) { if(CheckPointer(SourceObject)==POINTER_INVALID) return false; //--- CNeuronBaseOCL *temp=NULL; switch(SourceObject.Type()) { case defNeuronBaseOCL: case defNeuronConvOCL: case defNeuronAttentionOCL: case defNeuronMHAttentionOCL: case defNeuronMLMHAttentionOCL: case defNeuronDropoutOCL: temp=SourceObject; return updateInputWeights(temp); break; } //--- return false; } [/CODE] 逐行看:temp=TargetObject 是把目标层指针接住;GetPointer(this) 则把当前层自身指针传给对方层的 calcInputGradients,让卷积、注意力、Dropout 各自算输入梯度。UpdateInputWeights 里把 Dropout 和基类等并列,统一调 updateInputWeights(temp),少写一套重载。 在 MT5 里跑这套网络时,若你发现自己加的 Dropout 层完全不更新权重,先查 switch 分支有没有把 defNeuronDropoutOCL 列进去——漏了这行,return false 会直接截断回传。外汇与贵金属模型训练波动大,漏梯度可能让过拟合风险升高,建议用 EURUSD 的 M15 样本先验证回传链是否闭合。
temp=TargetObject; class="kw">return calcHiddenGradients(temp); class="kw">break; case defNeuronConvOCL: conv=TargetObject; temp=GetPointer(this); class="kw">return conv.calcInputGradients(temp); class="kw">break; case defNeuronAttentionOCL: case defNeuronMHAttentionOCL: at=TargetObject; temp=GetPointer(this); class="kw">return at.calcInputGradients(temp); class="kw">break; case defNeuronMLMHAttentionOCL: mlat=TargetObject; temp=GetPointer(this); class="kw">return mlat.calcInputGradients(temp); class="kw">break; case defNeuronDropoutOCL: dropout=TargetObject; temp=GetPointer(this); class="kw">return dropout.calcInputGradients(temp); class="kw">break; } class=class="str">"cmt">//--- class="kw">return class="kw">false; } class="type">bool CNeuronBaseOCL::UpdateInputWeights(CObject *SourceObject) { if(CheckPointer(SourceObject)==POINTER_INVALID) class="kw">return class="kw">false; class=class="str">"cmt">//--- CNeuronBaseOCL *temp=NULL; class="kw">switch(SourceObject.Type()) { case defNeuronBaseOCL: case defNeuronConvOCL: case defNeuronAttentionOCL: case defNeuronMHAttentionOCL: case defNeuronMLMHAttentionOCL: case defNeuronDropoutOCL: temp=SourceObject; class="kw">return updateInputWeights(temp); class="kw">break; } class=class="str">"cmt">//--- class="kw">return class="kw">false; }
「在 EURUSD H1 上对比 dropout 的实盘回测」
为了压住外部变量,测试直接复用同一套 EA 结构,只在四个位置插了舍弃层:初始数据后、嵌入代码后、关注模块后、全连接层后各一层。所有架构都喂入 EURUSD H1 的最后 20 根烛条,相似数据集下比性能才公平。 前 30 个迭代里,带 dropout 和不带 dropout 的误差曲线几乎平行,此时无舍弃的网反而数值更好看。转折出现在第 33 迭代,带舍弃的 EA 误差开始下探;到第 35 迭代后,它稳定跑出更低误差且仍有下降趋势,而无舍弃网把误差焊死在同一水平。 错失形态图更直白:带 dropout 的 EA 立刻显出差距收窄倾向,无舍弃网却让错失区域慢慢膨胀。预测命中两张图靠得很近,训练 44 迭代后,带舍弃的 EA 只比对手多捞了 0.5% 命中率——提升不大,但泛化倾向更稳。 外汇与贵金属属高风险品种,上述回测仅反映历史样本表现,实盘可能偏离。下面这段是插了两层 dropout 的网结构核心,黄底两段即舍弃层。
class=class="str">"cmt">//--- class="num">0 CLayerDescription *desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=(class="type">int)HistoryBars*class="num">12; desc.type=defNeuronBaseOCL; desc.optimization=ADAM; desc.activation=TANH; if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">1 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=(class="type">int)HistoryBars*class="num">12; desc.type=defNeuronDropoutOCL; desc.probability=class="num">0.2; desc.optimization=ADAM; desc.activation=TANH; if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">2 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=(class="type">int)HistoryBars; desc.type=defNeuronConvOCL; desc.window=class="num">12; desc.step=class="num">12; desc.window_out=class="num">24; desc.optimization=ADAM; desc.activation=SIGMOID; if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">3 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=(class="type">int)HistoryBars; desc.type=defNeuronDropoutOCL; desc.probability=class="num">0.2; desc.optimization=ADAM; desc.activation=SIGMOID; if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">4 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=(class="type">int)HistoryBars; desc.type=defNeuronMLMHAttentionOCL; desc.window=class="num">24; desc.window_out=class="num">4; desc.step=class="num">8; class=class="str">"cmt">//heads desc.layers=class="num">5; desc.optimization=ADAM; desc.activation=SIGMOID;
◍ 堆叠带 Dropout 的隐层与输出层
这段初始化逻辑在往 Topology 里连续塞 5 层网络描述,每一层都先 new 一个 CLayerDescription,用 CheckPointer 判空,失败直接 INIT_FAILED,避免悬空指针带崩后续训练。 第 5 层是 Dropout 变体(defNeuronDropoutOCL),神经元数量取 HistoryBars 的整型值,丢弃概率写死 0.2,激活用 SIGMOID、优化用 ADAM;它和第 6 层形成对照——第 6 层是普通 defNeuron,固定 200 个节点、TANH 激活。 第 7 层又回到 Dropout 结构,200 节点、概率 0.2、TANH,和第 8 层普通 200 节点 TANH 层交错。最后第 9 层是输出层,只有 3 个 SIGMOID 节点,适合做三分类信号(如多 / 空 / 观望)。外汇与贵金属波动剧烈,这类网络只是概率输出,实盘前务必在 MT5 策略测试器里跑样本外验证。 调参时先把 0.2 的 dropout 概率改到 0.3 试一轮,若过拟合明显再动隐层节点数;直接改代码里的 desc.probability 即可,不用重构整体拓扑。
if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">5 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=(class="type">int)HistoryBars; desc.type=defNeuronDropoutOCL; desc.probability=class="num">0.2; desc.optimization=ADAM; desc.activation=SIGMOID; if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">6 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=class="num">200; desc.type=defNeuron; desc.activation=TANH; desc.optimization=ADAM; if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">7 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=class="num">200; desc.type=defNeuronDropoutOCL; desc.probability=class="num">0.2; desc.optimization=ADAM; desc.activation=TANH; if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">8 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=class="num">200; desc.type=defNeuron; desc.activation=TANH; desc.optimization=ADAM; if(!Topology.Add(desc)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="num">9 desc=new CLayerDescription(); if(CheckPointer(desc)==POINTER_INVALID) class="kw">return INIT_FAILED; desc.count=class="num">3; desc.type=defNeuron; desc.activation=SIGMOID; desc.optimization=ADAM;
把舍弃法塞进你已有的 EA 里试一轮
前面几节我们把舍弃(dropout)接进了原先的 MQL5 智能交易系统,EA 回测里能直接看到收敛稳定性比不加时更耐造。代价是神经网络训练耗时往上走,不过实测中被最终结果质量提升盖过去了。 外汇和贵金属杠杆高、滑点跳空频繁,任何训练优化都只是概率层面的改善,不是免死金牌。 找你手上跑得最别扭的那个 EA,把 dropout 层参数从 0 调到 0.2 附近,跑一遍 2023 年 XAUUSD 的 H1 样本,对比训练损失曲线就知道值不值。
「顺着这条线继续深挖」
上面列的这些篇目,基本把从基础卷积、循环结构到 OpenCL 多线程、自适应优化乃至自 GPT 获取的一整条 MQL5 神经网络实践链路铺开了。如果你已经在 MT5 里跑过前几篇的示范 EA,建议直接按这个顺序把第十、十一部关于多目击者关注和自 GPT 获取的源码拉下来对照编译。 其中「通过防止特征检测器的协同适应来改善神经网络」这一篇,核心在于 dropout 思路在 MQL5 端的落地——训练时随机屏蔽部分神经元,验证集误差往往比不加约束的版本低 3%~8%(视特征维度而定)。外汇与贵金属行情序列信噪比低,直接套用易过拟合,高风险环境下更该用这套方法压住泛化缺口。 统计评估那节则给出回测样本外的夏普波动对照,数字虽不保证实盘复现,但足以判断哪类网络结构在 XAUUSD 的 H1 上更耐打。打开终端把对应 mqh 丢进 Include 目录,先本地跑一轮再谈调参。
◍ 把这条线请下神坛
这套 GTP 分类网络 + 5 关注层的 EA 在附件里拆成了五个实体:主程序 Fractal_OCL_AttentionMLMH.mq5、带舍弃逻辑的 _d 版本、NeuroNet.mqh 类库、NeuroNet.cl 的 OpenCL 核,以及 NN.chm 离线帮助。ZIP 体积 2459.71 KB,直接丢进 MT5 的 MQL5 目录就能编译,但评论区已有人实测:训练时若算法报未定义信号,遗漏模式占比会冲到 100%,换历时才回落。 OpenCL 依赖显卡或多核 CPU 的运行时驱动,老卡用户跑 _d 版本基本只能当处理器软解,速度惨但能验证逻辑。窗口=3、步长=1 的卷积设定,本质是滑动 3 根 K 线搜分形形态,相关值向量就是这么挤出来的。 外汇和贵金属自带高杠杆高风险,这类神经网络 EA 只是概率模型,回测亮眼不等于实盘能活。下掉神坛的意思很简单:开 MT5 把代码跑一遍,看遗漏率什么时候爆,比信任何「框架很牛」的叙事都实在。