神经网络变得轻松(第四十六部分):条件导向目标强化学习(GCRL)·综合运用
「回放缓冲里怎么喂样本给策略网络」
这段逻辑干的是强化学习里的回放训练:先把所有轨迹的最大步数扫出来,再按随机偏移抽取状态-账户快照喂给 Actor 网络。外汇与贵金属杠杆高,这类离线回放若样本偏差大,训练出的决策倾向可能严重偏离实盘。 随机游标用了两次 MathRand() 相乘再除以 32767 的平方,把均匀分布压成左偏分布,使早期步数被抽中的概率更高。若 total_steps 小于 3,i 的计算可能越界或恒为 0,需要在 MT5 里先打印 total_steps 确认缓冲长度。 账户特征做了相对化:余额变化率用 (cur-prev)/prev,权益也除以 prev_balance,把绝对金额压成比率,避免不同本金规模导致网络尺度失衡。这一处理直接决定训练收敛速度,可复制下方代码段逐行核对你的字段索引是否对齐。
class="type">class="kw">float reward = Account[class="num">0]; if((buy_value+sell_value)>class="num">0) reward+=(class="type">class="kw">float)position_discount; else reward-=atr; if(!Base.Add(sState, act, reward)) ExpertRemove(); class=class="str">"cmt">//--- } class="type">void Train(class="type">void) { class="type">int total_tr = ArraySize(Buffer); class="type">int total_steps = class="num">0; for(class="type">int tr = class="num">0; tr < total_tr; tr++) { if(Buffer[tr].Total > total_steps) total_steps = Buffer[tr].Total; } class="type">uint ticks = GetTickCount(); class=class="str">"cmt">//--- for(class="type">int iter = class="num">0; (iter < Iterations && !IsStopped()); iter ++) { class="type">int i = (class="type">int)((MathRand() * MathRand() / MathPow(class="num">32767, class="num">2)) * (total_steps - class="num">2)); for(class="type">int tr = class="num">0; tr < total_tr; tr++) { if(i >= (Buffer[tr].Total - class="num">1)) class="kw">continue; State.AssignArray(Buffer[tr].States[i].state); class="type">class="kw">float PrevBalance = Buffer[tr].States[MathMax(i - class="num">1, class="num">0)].account[class="num">0]; class="type">class="kw">float PrevEquity = Buffer[tr].States[MathMax(i - class="num">1, class="num">0)].account[class="num">1]; Account.Clear(); Account.Add((Buffer[tr].States[i].account[class="num">0] - PrevBalance) / PrevBalance); Account.Add(Buffer[tr].States[i].account[class="num">1] / PrevBalance); Account.Add((Buffer[tr].States[i].account[class="num">1] - PrevEquity) / PrevEquity); Account.Add(Buffer[tr].States[i].account[class="num">2]); Account.Add(Buffer[tr].States[i].account[class="num">3]); Account.Add(Buffer[tr].States[i].account[class="num">4] / PrevBalance); Account.Add(Buffer[tr].States[i].account[class="num">5] / PrevBalance); Account.Add(Buffer[tr].States[i].account[class="num">6] / PrevBalance); class=class="str">"cmt">//--- if(Account.GetIndex()>=class="num">0) Account.BufferWrite(); if(!Actor.feedForward(GetPointer(State), class="num">1, class="kw">false,GetPointer(Account))) { PrintFormat("%s -> %d", __FUNCTION__, __LINE__); ExpertRemove(); break; } class=class="str">"cmt">//--- ActorResult = vector<class="type">class="kw">float>::Zeros(NActions); ActorResult[Buffer[tr].Actions[i]] = Buffer[tr].Revards[i]; Result.AssignArray(ActorResult);
反向传播里的超时打印与终止逻辑
这段训练循环把神经网络的反向传播和实时日志绑在一起:每轮调用 Actor.backProp 做梯度回传,若返回失败就打印函数名加行号、移除 EA 并 break 跳出。 为了避免刷屏,代码用 GetTickCount 做了节流——只有距上次打印超过 500 毫秒才更新一次 Comment。此时会把当前迭代进度(iter / Iterations * 100)和 Actor.getRecentAverageError 输出的平均误差(精度 15.8f)拼成字符串显示在图表左上。 你在 MT5 里跑这类自研训练 EA 时,可以把 500 这个阈值调小看更细的收敛曲线,或调大降低主线程开销;外汇与贵金属市场波动剧烈,这类在线学习策略实盘前务必用历史数据充分回测,过拟合概率不低。
if(!Actor.backProp(Result, class="num">0, NULL, class="num">1, class="kw">false,GetPointer(Account),GetPointer(Gradient))) { PrintFormat("%s -> %d", __FUNCTION__, __LINE__); ExpertRemove(); break; } if(GetTickCount() - ticks > class="num">500) { class="type">class="kw">string str = StringFormat("%-15s %class="num">5.2f%% -> Error %class="num">15.8f\n", "Actor", iter * class="num">100.0 / (class="type">class="kw">double)(Iterations), Actor.getRecentAverageError()); Comment(str); ticks = GetTickCount(); } }
◍ EURUSD H1 上的 GCRL 代理实测
EA 定型后直接进验证,模型训练参数原样不动,仍吃 EURUSD H1 的历史样本,指标走默认。代理者用 2023 年 4 个月数据训完,挑 6 月 1 日到 18 日这段没见过的数据测泛化。
- 个交易日里跑了 220 笔,盈利率 53% 出头,盈利因子 2.2、恢复因子 1.47。平均盈利单规模接近亏损单的 2 倍,余额曲线有冲高也有横盘,没出现崩。外汇与贵金属属高风险品种,这类短窗回测不代表样本外稳定。
一个硬伤是代理只开多头,之前方法没解这个偏置。GCRL 的好处是持仓耗时压得低:最长 21 小时 15 分,均值 5 小时 49 分。惩罚项按每小时扣掉潜在盈利的 1/10 计,持仓满 10 小时罚款就盖过该仓收入,逼着模型快点平仓。
「GCRL 实盘测试留下的尾巴」
我们用 MQL5 把这套条件导向目标强化学习(GCRL)跑通了,模型在训练集之外的数据上做验证。结果不算干净:EA 只在单一方向开仓,但测试期内依然录得盈利,说明局部子任务拆解没崩。 持仓时间较训练阶段明显缩短,反向印证代理者确实把‘开仓’和‘平仓’两个本地子任务都学会了,而不是靠长期扛单磨利润。外汇与贵金属杠杆高,这类单方向偏好在趋势反转时可能快速回吐,须先在 MT5 策略测试器里复现再上实盘。 未解决的问题留了个口子——方向偏食。这反而给后续改奖励函数、加反向子任务提供了落点,方法本身值得继续挖。
延伸阅读:技能习得与表征学习的三条线索
想把 MT5 里的智能体从「被动预测」推向「主动探索」,下面三篇线索值得顺着读。它们共同指向一个事实:在缺少显式奖励函数的环境里,Agent 仍可能通过变分授权和目标导向的表征学习掌握技能。 第一条线索关注变分授权框架,它把基于目标的强化学习做成表征学习问题,用潜变量刻画行为意图,省去手工设计奖励的麻烦。 后两条线索是同一系列的连续章节:第四十三部分讲无奖励函数下的技能精通,第四十四部分引入动态学习机制,第四十五部分专门训练状态探索技能。三者递进,构成了从「会做」到「会找状态去练」的闭环。 外汇与贵金属市场高波动、高杠杆,这类方法若接实盘须先用历史 tick 在策略测试器做离线验证,再考虑是否接 EA。
◍ 随包附带的八个文件分别干什么
这套 GCRL 示例不是单文件 EA,而是把数据采集、训练、测试拆成了三个 mq5:Research 负责收集样本,StudyActor 跑代理者训练,Test 做模型验证。三者共用四个 mqh 类库——Trajectory 定义系统状态结构,FQF 管完全参数化模型的工作排程,NeuroNet 搭神经网络,VAE 管变分自动编码器的潜伏层;外加一个 NeuroNet.cl 的 OpenCL 代码库做异构计算加速。 下载包 MQL5.zip 体积 615.73 KB,解压后直接进 MT5 策略测试器就能跑。有读者在讨论区反馈无法复现原文结果,作者 Dmitriy Gizlyk 的答复很关键:三个 EA 所有参数保持默认,只在慢速优化模式下把 Agent 编号拿来设测试器迭代次数,不需要动别的输入项。 另一个被点出的坑是测试 EA 会先开一串买单、再开一串卖单,而卖单常拖到测试期结束才平,怀疑出在策略共用类里。你拉代码自查时优先看 Trajectory.mqh 和 FQF.mqh 里的初始状态与平仓判定,比盲目调参更省时间。外汇与贵金属测试属高风险环境,回测表现不代表实盘倾向。