文章 "神经网络变得简单(第 66 部分):离线学习中的探索问题"

 

新文章 神经网络变得简单(第 66 部分):离线学习中的探索问题已发布:

使用准备好的训练数据集中的数据对模型进行离线训练,这种方法虽然有一定的优势,但其不利的一面是,环境信息被大大压缩到训练数据集的大小。这反过来又限制了探索的可能性。在本文中,我们将探讨一种方法,这种方法可以用尽可能多样化的数据来填充训练数据集。

ExORL 方法可分为 3 个主要阶段。第一阶段是收集未标记的探索性数据。这个阶段可以使用各种无监督学习算法。该方法的作者并没有限制适用算法的范围。此外,在与环境互动的过程中,在每个回合(episode)中,我们会根据之前互动的历史记录使用一种策略 π。每一回合都以状态St、行动At和后续状态St+1 的序列保存在数据集中。训练数据的收集一直持续到训练数据集全部填满为止。该训练数据集的规模受到技术规格或可用资源的限制。

在收集了状态和行动数据集之后,下一阶段就是利用给定的奖励函数对数据进行重新标记。这一阶段意味着对数据集中每个元组的奖励进行评估。

实际经验表明,通过不同方法收集的回放缓冲区可以并行使用。我使用了之前讨论过的 EA Research.mq5 和 EA ResearchExORL.mq5 收集的轨迹。第一,指出了学习完毕的 Actor 策略的优缺点。其次,我们可以尽可能多地探索环境,评估未被考虑的机会。

在反复训练模型的过程中,我设法提高了模型的性能。

测试结果

测试结果

虽然测试期间的交易次数总体减少了 3 倍(56 对 176),但利润却增加了近 3 倍。最大盈利交易额增加了一倍多。平均盈利交易增加了 5 倍。此外,我们还发现,在整个测试期间,余额都在增加。因此,模型的利润系数从 1.3 提高到 2.96。 

作者:Dmitriy Gizlyk

 
德米特里 你好。上面说您使用了 5 个代理,收集了 100 张通行证。您是否进行了 20 次收集?否则 5 个代理只能收集到 5 次。此外,如果您使用相同的参数运行 Expert Advisor,它不会重新计算,而是从缓存中提取结果。还是说您在每次收集时都会转移这 5 个代理?请解释这一点。
 
Viktor Kudriavtsev #:
德米特里 你好。上面说您使用了 5 个代理,收集了 100 张通行证。您是否进行了 20 次收集?否则 5 个代理只能收集到 5 次。此外,如果您使用相同的参数运行 Expert Advisor,它不会重新计算,而是从缓存中提取结果。还是说您在每次收集时都会转移这 5 个代理?请解释这一点。

我运行了 20 次,但运行前清除了缓存。您不能转移代理,因为模型文件与代理编号绑定。因此,如果您每次都移动代理,就会创建一个新的随机模型,这样我们就无法获得理想的效果。

 
Dmitriy Gizlyk #:

我运行了 20 次,但启动前清除了缓存。您不能转移代理,因为模型文件与代理编号相关联。因此,如果转移代理,每次都会创建一个新的随机模型,我们将无法获得理想的效果。

嗨,Dmitriy,我用这个方法做到了...效果一样吗?

输入 ENUM_TIMEFRAMES TimeFrame = PERIOD_H1;

输入 double MinProfit = 10;
输入 int Agent = 1;
input int Optimisation = 1;

然后将代理设为 5,优化设为 20
总计 100...


 
JimReaper #:
嗨,德米特里,我是用这个...效果一样吗?

输入 ENUM_TIMEFRAMES TimeFrame = PERIOD_H1;

输入 double MinProfit = 10;
输入 int Agent = 1;
input int Optimisation = 1;

然后将代理设为 5,优化设为 20
总计 100...


您好,
您使用了多少个内核?

 
Dmitriy Gizlyk #:

你好,
你使用了多少个内核?

我只用了 4 个内核。


附加的文件:
Dimi_1.png  2 kb
Dimi_2.png  5 kb
 
JimReaper #:
我只用了 4 个核心。


我不知道 MetaTrader Tester 如何为每个内核选择输入。在线研究的主要想法是在一个通道到另一个通道之间使用预训练模型。但是,如果测试者在一次测试中运行 Optimithation 1...4 到 Agent 1,它们都会使用随机(非预训练)模型。

 
Dmitriy Gizlyk #:

我不知道 MetaTrader Tester 如何为每个核心选择输入。在线研究的主要想法是,从一个通道到另一个通道使用预训练模型。但是,如果测试员一次运行 Optimithation 1...4 到 Agent 1,它们都使用随机(非预训练)模型。

明白了!非常感谢!

我还添加了一些指标和参数,共有 27 个 BarDescr....动量、波段和一目均衡 Kinko Hyo =)

int OnInit()

{

设置符号并刷新

if(! Symb.Name(_Symbol))

返回 INIT_FAILED;

Symb.Refresh();

//---

如果(!Create(Symb.Name(), TimeFrame, RSIPeriod, RSIPrice))

return INIT_FAILED;

//---

如果(!CCI.Create(Symb.Name(), TimeFrame, CCIPeriod, CCIPrice))

返回 INIT_FAILED;

//---

if(!Create(Symb.Name(), TimeFrame, ATRPeriod))

return INIT_FAILED;

//---

if(!Create(Symb.Name(), TimeFrame, FastPeriod, SlowPeriod, SignalPeriod, MACDPrice))

返回 INIT_FAILED;

//---

if (! Momentum.Create(Symb.Name(), TimeFrame, MomentumMaPeriod, MomentumApplied))

返回 INIT_FAILED;

初始化 Ichimoku Kinko Hyo 指标

如果 (! Ichimoku.Create(Symb.Name(), TimeFrame, Ichimokutenkan_senPeriod, Ichimokukijun_senPeriod, Ichimokusenkou_span_bPeriod))

返回 INIT_FAILED;

//---

if (! Bands.Create(Symb.Name(), TimeFrame, BandsMaPeriod, BandsMaShift, BandsDeviation, BandsApplied))

返回 INIT_FAILED;

//---

if(!BufferResize(HistoryBars) || !CCI.BufferResize(HistoryBars) ||| !

!ATR.BufferResize(HistoryBars)BufferResize(HistoryBars) || !MACD.BufferResize(HistoryBars)MACD.BufferResize(HistoryBars))

{

PrintFormat("%s -> %d", __FUNCTION__, __LINE__);

返回 INIT_FAILED;

}

//---


void OnTick()

{

//---

if(! IsNewBar())

返回;

//---

int bars = CopyRates(Symb.Name(), TimeFrame, iTime(Symb.Name(), TimeFrame, 1), HistoryBars, Rates);

if(! ArraySetAsSeries(Rates, true))

返回;

//---

RSI.Refresh();

CCI.Refresh();

ATR.Refresh()刷新();

MACD.刷新(); Symb.Refresh()

Symb.Refresh();

Momentum.Refresh();

Bands.Refresh();

Symb.RefreshRates();

刷新当前条形图的 Ichimoku 值

Ichimoku.Refresh();

--- 历史数据

float atr = 0;

for (int b = 0; b < (int)HistoryBars; b++)

{

float open = (float)Rates[b].open;

float close = (float)Rates[b].close;

float rsi = (float)RSI.Main(b);

float cci = (float)CCI.Main(b);

atr = (float)ATR.Main(b);

float macd = (float)MACD.Main(b);

float sign = (float)MACD.Signal(b);

float mome = (float)Momentum.Main(b);

float bandzup = (float)Bands.Upper(b);

float bandzb = (float)Bands.Base(b);

float bandzlo = (float)Bands.Lower(b);

float tenkan = (float)Ichimoku.TenkanSen(0); 使用计算值

float kijun = (float)Ichimoku.KijunSen(1); 使用计算值

float senkasa = (float)Ichimoku.SenkouSpanA(2); 使用计算值

float senkb = (float)Ichimoku.SenkouSpanB(3); 使用计算值

检查是否存在 EMPTY_VALUE 和除以零的情况

如果 (rsi == EMPTY_VALUE || cci == EMPTY_VALUE || atr == EMPTY_VALUE || macd == EMPTY_VALUE |||)

sign == EMPTY_VALUE || mome == EMPTY_VALUE || bandzup == EMPTY_VALUE || bandzb == EMPTY_VALUE ||| bandzb == EMPTY_VALUE |||| bandzlo == EMPTY_VALUE |||...

bandzlo == EMPTY_VALUE || tenkan == EMPTY_VALUE || kijun == EMPTY_VALUE || senkasa == EMPTY_VALUE || senkasa == EMPTY_VALUE ||...

senkb == EMPTY_VALUE || kijun == 0.0 || senkb == 0.0)

{

继续

}

确保不在循环中调整缓冲区大小

int shift = b * BarDescr;

sState.state[shift] = (float)(Rates[b].close - open);

sState.state[shift + 1] = ((float)(Rates[b].close - open) + (tenkan - kijun)) / 2.0f;

sState.state[shift + 2] = (float)(Rates[b].high - open);

sState.state[shift + 3] = (float)(Rates[b].low - open);

sState.state[shift + 4] = (float)(Rates[b].high - close);

sState.state[shift + 5] = (float)(Rates[b].low - close);

sState.state[shift + 6] = (tenkan - kijun);

sState.state[shift + 7] = (float)(Rates[b].tick_volume / 1000.0f);

sState.state[shift + 8] = ((float)(Rates[b].high) - (float)(Rates[b].low));

sState.state[shift + 9] = (bandzup - bandzlo);

sState.state[shift + 10] = rsi;

sState.state[shift + 11] = cci;

sState.state[shift + 12] = atr;

sState.state[shift + 13] = macd;

sState.state[shift + 14] = sign;

sState.state[shift + 15] = mome;

sState.state[shift + 16] = (float)(Rates[b].open - tenkan);

sState.state[shift + 17] = (float)(Rates[b].open - kijun);

sState.state[shift + 18] = (float)(Rates[b].open - bandzb);

sState.state[shift + 19] = (float)(Rates[b].open - senkasa);

sState.state[shift + 20] = (float)(Rates[b].open - senkb);

sState.state[shift + 21] = (float)(Rates[b].close - tenkan);

sState.state[shift + 22] = (float)(Rates[b].close - kijun);

sState.state[shift + 23] = (float)(Rates[b].close - bandzb);

sState.state[shift + 24] = (float)(Rates[b].close - senkasa);

sState.state[shift + 25] = (float)(Rates[b].close - senkb);

sState.state[shift + 26] = senkasa - senkb;

//---

RSI.Refresh();

CCI.Refresh();

ATR.Refresh();

MACD.Refresh();

Symb.Refresh();

Momentum.Refresh();

Bands.Refresh();

Symb.RefreshRates();

// 刷新当前条形图的 Ichimoku 值

Ichimoku.Refresh();

//---

Print("State 0: ", sState.state[shift]);

Print("State 1: ", sState.state[shift + 1]);

Print("State 2: ", sState.state[shift + 2]);

打印("状态 3:",sState.state[shift + 3]);

打印("状态 4:",sState.state[shift + 4]);

打印("状态 5:",sState.state[shift + 5]);

打印("状态 6:",sState.state[shift + 6]);

打印("状态 7:",sState.state[shift + 7]);

打印("状态 8:",sState.state[shift + 8]);

打印("状态 9:",sState.state[shift + 9]);

打印("状态 10:",sState.state[shift + 10]);

打印("状态 11:",sState.state[shift + 11]);

打印("状态 12:",sState.state[shift + 12]);

打印("状态 13:",sState.state[shift + 13]);

打印("状态 14:",sState.state[shift + 14]);

打印("状态 15:",sState.state[shift + 15]);

打印("状态 16:",sState.state[shift + 16]);

打印("状态 17:",sState.state[shift + 17]);

打印("状态 18:",sState.state[shift + 18]);

打印("状态 19:",sState.state[shift + 19]);

打印("状态 20:",sState.state[shift + 20]);

打印("状态 21:",sState.state[shift + 21]);

打印("状态 22:",sState.state[shift + 22]);

打印("状态 23:",sState.state[shift + 23]);

打印("状态 24:",sState.state[shift + 24]);

打印("状态 25:",sState.state[shift + 25]);

打印("状态 26:",sState.state[shift + 26]);

Print("Tenkan Sen: ", tenkan);

Print("Kijun Sen: ", kijun);

Print("Senkou Span A: ", senkasa);

Print("Senkou Span B: ", senkb);

}

bState.AssignArray(sState.state);


附加的文件:
 
JimReaper #:
明白了!非常感谢!

我还添加了一些指标和参数,共计 27 BarDescr....Momentum, Bands & Ichimoku Kinko Hyo =)

int OnInit()

{

设置符号并刷新

if(! Symb.Name(_Symbol))

返回 INIT_FAILED;

Symb.Refresh();

//---

如果(!Create(Symb.Name(), TimeFrame, RSIPeriod, RSIPrice))

返回 INIT_FAILED;

//---

如果(!CCI.Create(Symb.Name(), TimeFrame, CCIPeriod, CCIPrice))

返回 INIT_FAILED;

//---

if(! ATR.Create(Symb.Name(, TimeFrame, ATRPeriod))Create(Symb.Name(), TimeFrame, ATRPeriod))

返回 INIT_FAILED;

//---

如果(!Create(Symb.Name(), TimeFrame, FastPeriod, SlowPeriod, SignalPeriod, MACDPrice))

返回 INIT_FAILED;

//---

如果 (! Momentum.Create(Symb.Name(), TimeFrame, MomentumMaPeriod, MomentumApplied))

返回 INIT_FAILED;

初始化 Ichimoku Kinko Hyo 指标

如果 (! Ichimoku.Create(Symb.Name(), TimeFrame, Ichimokutenkan_senPeriod, Ichimokukijun_senPeriod, Ichimokusenkou_span_bPeriod))

返回 INIT_FAILED;

//---

如果 (! Bands.Create(Symb.Name(), TimeFrame, BandsMaPeriod, BandsMaShift, BandsDeviation, BandsApplied))

返回 INIT_FAILED;

//---

if(! RSI.BufferResize(HistoryBars)BufferResize(HistoryBars) || !CCI.BufferResize(HistoryBars) || !

!ATR.BufferResize(HistoryBars)BufferResize(HistoryBars) || !MACD.BufferResize(HistoryBars)BufferResize(HistoryBars))

{

PrintFormat("%s -> %d", __FUNCTION__, __LINE__);

返回 INIT_FAILED;

}

//---


void OnTick()

{

//---

if(! IsNewBar())

返回;

//---

int bars = CopyRates(Symb.Name(), TimeFrame, iTime(Symb.Name(), TimeFrame, 1), HistoryBars, Rates);

if(! ArraySetAsSeries(Rates, true))

返回;

//---

RSI.刷新();

CCI.Refresh();

ATR.刷新();

MACD.刷新();

Symb.Refresh();

Momentum.刷新();

Bands.Refresh();

Symb.RefreshRates();

刷新当前条形图的 Ichimoku 值

Ichimoku.Refresh();

--- 历史数据

float atr = 0;

for (int b = 0; b < (int)HistoryBars; b++)

{

float open = (float)Rates[b].open;

float close = (float)Rates[b].close;

float rsi = (float)RSI.Main(b);

float cci = (float)CCI.Main(b);

atr = (float)ATR.Main(b);

float macd = (float)MACD.Main(b);

float sign = (float)MACD.Signal(b);

float mome = (float)Momentum.Main(b);

float bandzup = (float)Bands.Upper(b);

float bandzb = (float)Bands.Base(b);

float bandzlo = (float)Bands.Lower(b);

float tenkan = (float)Ichimoku.TenkanSen(0); 使用计算值

float kijun = (float)Ichimoku.KijunSen(1); 使用计算值

float senkasa = (float)Ichimoku.SenkouSpanA(2); 使用计算值

float senkb = (float)Ichimoku.SenkouSpanB(3); 使用计算值

检查是否存在 EMPTY_VALUE 和除以零的情况

if (rsi == EMPTY_VALUE || cci == EMPTY_VALUE || atr == EMPTY_VALUE || macd == EMPTY_VALUE |||)

sign == EMPTY_VALUE || mome == EMPTY_VALUE || bandzup == EMPTY_VALUE || bandzb == EMPTY_VALUE || bandzb == EMPTY_VALUE ||...

bandzlo == EMPTY_VALUE || tenkan == EMPTY_VALUE || kijun == EMPTY_VALUE || senkasa == EMPTY_VALUE || senkasa == EMPTY_VALUE |||...

senkb == EMPTY_VALUE || kijun == 0.0 || senkb == 0.0)

{

继续;

}

确保不在循环中调整缓冲区大小

int shift = b * BarDescr;

sState.state[shift] = (float)(Rates[b].close - open);

sState.state[shift + 1] = ((float)(Rates[b].close - open) + (tenkan - kijun)) / 2.0f;

sState.state[shift + 2] = (float)(Rates[b].high - open);

sState.state[shift + 3] = (float)(Rates[b].low - open);

sState.state[shift + 4] = (float)(Rates[b].high - close);

sState.state[shift + 5] = (float)(Rates[b].low - close);

sState.state[shift + 6] = (tenkan - kijun);

sState.state[shift + 7] = (float)(Rates[b].tick_volume / 1000.0f);

sState.state[shift + 8] = ((float)(Rates[b].high) - (float)(Rates[b].low));

sState.state[shift + 9] = (bandzup - bandzlo);

sState.state[shift + 10] = rsi;

sState.state[shift + 11] = cci;

sState.state[shift + 12] = atr;

sState.state[shift + 13] = macd;

sState.state[shift + 14] = sign;

sState.state[shift + 15] = mome;

sState.state[shift + 16] = (float)(Rates[b].open - tenkan);

sState.state[shift + 17] = (float)(Rates[b].open - kijun);

sState.state[shift + 18] = (float)(Rates[b].open - bandzb);

sState.state[shift + 19] = (float)(Rates[b].open - senkasa);

sState.state[shift + 20] = (float)(Rates[b].open - senkb);

sState.state[shift + 21] = (float)(Rates[b].close - tenkan);

sState.state[shift + 22] = (float)(Rates[b].close - kijun);

sState.state[shift + 23] = (float)(Rates[b].close - bandzb);

sState.state[shift + 24] = (float)(Rates[b].close - senkasa);

sState.state[shift + 25] = (float)(Rates[b].close - senkb);

sState.state[shift + 26] = senkasa - senkb;

//---

RSI.Refresh();

CCI.Refresh();

ATR.Refresh();

MACD.Refresh();

Symb.Refresh();

Momentum.Refresh();

Bands.Refresh();

Symb.RefreshRates();

// 刷新当前条形图的一目均衡数值

Ichimoku.Refresh();

//---

Print("State 0: ", sState.state[shift]);

Print("State 1: ", sState.state[shift + 1]);

打印("状态 2:",sState.state[shift + 2]);

打印("状态 3:",sState.state[shift + 3]);

打印("状态 4:",sState.state[shift + 4]);

打印("状态 5:",sState.state[shift + 5]);

打印("状态 6:",sState.state[shift + 6]);

打印("状态 7:",sState.state[shift + 7]);

打印("状态 8:",sState.state[shift + 8]);

打印("状态 9:",sState.state[shift + 9]);

打印("状态 10:",sState.state[shift + 10]);

打印("状态 11:",sState.state[shift + 11]);

打印("状态 12:",sState.state[shift + 12]);

打印("状态 13:",sState.state[shift + 13]);

打印("状态 14:",sState.state[shift + 14]);

打印("状态 15:",sState.state[shift + 15]);

打印("状态 16:",sState.state[shift + 16]);

打印("状态 17:",sState.state[shift + 17]);

打印("状态 18:",sState.state[shift + 18]);

打印("状态 19:",sState.state[shift + 19]);

打印("状态 20:",sState.state[shift + 20]);

打印("状态 21:",sState.state[shift + 21]);

打印("状态 22:",sState.state[shift + 22]);

打印("状态 23:",sState.state[shift + 23]);

打印("状态 24:",sState.state[shift + 24]);

打印("状态 25:",sState.state[shift + 25]);

打印("状态 26:",sState.state[shift + 26]);

Print("Tenkan Sen: ", tenkan);

Print("Kijun Sen: ", kijun);

Print("Senkou Span A: ", senkasa);

Print("Senkou Span B: ", senkb);

}

bState.AssignArray(sState.state);


JimReaper - 在得到图片中的结果之前,您研究了多少次您的版本?花了多长时间?


您的计算机配置(处理器、显卡、内存)如何?


谢谢

 
亲爱的朋友们,我从 5 个代理处收集信息大约需要 8 个小时。我使用的是 8 核处理器。速度太慢还是正常?请与我们分享。
 
JimReaper #:
嗨,德米特里,我是用这个做的......效果一样吗?

输入 ENUM_TIMEFRAMES TimeFrame = PERIOD_H1;

输入 double MinProfit = 10;
输入 int Agent = 1;
input int Optimisation = 1;

然后将代理设为 5,优化设为 20
总计 100...


嗨,吉姆

我看到代码中提到了代理,但没有看到优化。为了使用这个新参数,您是否对代码作了进一步的补充?
谢谢
保罗