神经网络变得简单(第 90 部分):时间序列的频率插值(FITS)·进阶篇
(2/3)· 复杂模型在手机上跑不动?FITS 靠频域插值把计算量压到极致
很多交易者在手机上做决策时,发现传统频域模型要么算不动、要么延迟高。其实不必堆参数,轻量插值思路就能在有限资源下提取时态特征。
◍ FFT 神经元的 OpenCL 初始化细节
在 MT5 用 OpenCL 加速的 FITS 神经元里,CNeuronFITSOCL 类把时域行情窗口先转频域再跑前向传播。它的 Init 方法要求调用方传入 input 窗口 window、输出窗口 window_out 和通道数 count,任一为 0 会直接返回 false,所以实盘传参前务必校验指标句柄拿到的样本长度。 初始化时会按 2 的幂向上补齐 FFT 尺寸:先对 iWindow 取以 2 为底对数得 power,若 2^power 不等于原窗口就 power++,最终 iFFTin = 2^power。同样逻辑算 iIFFTin。这意味着你传 window=120 时,实际 FFT 缓冲会被扩到 128,多出的 8 个单元填零,显存和算子耗时按 128 算。 两个频域缓冲 cInputsRe / cInputsIm 分别用标签 0 和 1 建在 OpenCL 上下文,长度都是 iFFTin * iCount。若你改 count 从 1 到 4,输入缓冲直接翻四倍,老卡可能爆显存。外汇与贵金属杠杆高,这类 GPU 算子若因参数误配崩在实时训练,可能错过关键波段。
class="kw">virtual class="type">bool ComplexLayerGradient(CBufferFloat *inp_re, CBufferFloat *inp_im, CBufferFloat *out_re, CBufferFloat *out_im); class=class="str">"cmt">//--- class="kw">virtual class="type">bool feedForward(CNeuronBaseOCL *NeuronOCL); class="kw">virtual class="type">bool updateInputWeights(CNeuronBaseOCL *NeuronOCL); class="kw">virtual class="type">bool calcInputGradients(CNeuronBaseOCL *NeuronOCL); class="kw">public: CNeuronFITSOCL(class="type">void) {}; ~CNeuronFITSOCL(class="type">void) {}; class=class="str">"cmt">//--- class="kw">virtual class="type">bool Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl, class="type">uint window, class="type">uint window_out, class="type">uint count, class="type">float dropout, ENUM_OPTIMIZATION optimization_type, class="type">uint batch); class=class="str">"cmt">//--- class="kw">virtual class="type">bool Save(class="type">int const file_handle); class="kw">virtual class="type">bool Load(class="type">int const file_handle); class=class="str">"cmt">//--- class="kw">virtual class="type">int Type(class="type">void) const { class="kw">return defNeuronFITSOCL; } class="kw">virtual class="type">void SetOpenCL(COpenCLMy *obj); class="kw">virtual class="type">void TrainMode(class="type">bool flag); class=class="str">"cmt">//--- class="kw">virtual class="type">bool WeightsUpdate(CNeuronBaseOCL *source, class="type">float tau); }; class="type">bool CNeuronFITSOCL::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl, class="type">uint window, class="type">uint window_out, class="type">uint count, class="type">float dropout, ENUM_OPTIMIZATION optimization_type, class="type">uint batch) { if(window <= class="num">0) class="kw">return false; if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window_out * count, optimization_type, batch)) class="kw">return false; class=class="str">"cmt">//--- Save constants iWindow = window; iWindowOut = window_out; iCount = count; activation=None; class=class="str">"cmt">//--- Calculate FFT and iFFT size class="type">int power = class="type">int(MathLog(iWindow) / M_LN2); if(MathPow(class="num">2, power) != iWindow) power++; iFFTin = class="type">uint(MathPow(class="num">2, power)); power = class="type">int(MathLog(iWindowOut) / M_LN2); if(MathPow(class="num">2, power) != iWindowOut) power++; iIFFTin = class="type">uint(MathPow(class="num">2, power)); if(!cInputsRe.Init(class="num">0, class="num">0, OpenCL, iFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cInputsIm.Init(class="num">0, class="num">1, OpenCL, iFFTin * iCount, optimization, iBatch)) class="kw">return false;
「FITSO 网络在 OpenCL 上的层初始化与 FFT 派发」
把频域神经网络搬上 GPU,第一步是把每一层算子和缓冲在 OpenCL 上下文里登记好。下面这段初始化里,实部/虚部的 FFT 层、dropout 层、以及两组内部全连接层(cInsideRe1/Im1 用 TANH,cInsideRe2/Im2 用 None)都按编号 2~13 依次 Init,任何一层返回 false 就直接中断,说明显存申请或核参数绑定在某个设备上有概率失败。 注意 cInsideRe1 和 cInsideIm1 的输入输出维度是 iFFTin → iFFTin,但中间展开到 4*iIFFTin,这是频域特征升维的关键;而 cInsideRe2/Im2 再从 4*iIFFTin 压回 iIFFTin。维度错配在 MT5 终端里通常表现为 Kernel 执行返回 -1001,开终端拿 EURUSD 的 M15 数据跑一遍就能复现。 FFT 派发函数里,global_work_size 直接取 iCount(样本批次数),虚部输入若为空则复用实部缓冲索引。这意味着你喂实波信号时可以不传 inp_im,但反向变换 reverse=true 时务必确认 cIFFTRe/Im 已 Init,否则 OpenCL.SetArgumentBuffer 会报 line 错误并打印 GetLastError 码。外汇与贵金属杠杆高,这类 GPU 算子若在小众显卡上优化失败,可能拖慢盯盘 EA 的推理节奏,需自行在策略测试器验证延迟。
if(!cFFTRe.Init(class="num">0, class="num">2, OpenCL, iFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cFFTIm.Init(class="num">0, class="num">3, OpenCL, iFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cDropRe.Init(class="num">0, class="num">4, OpenCL, iFFTin * iCount, dropout, optimization, iBatch)) class="kw">return false; if(!cDropIm.Init(class="num">0, class="num">5, OpenCL, iFFTin * iCount, dropout, optimization, iBatch)) class="kw">return false; if(!cInsideRe1.Init(class="num">0, class="num">6, OpenCL, iFFTin, iFFTin, class="num">4*iIFFTin, iCount, optimization, iBatch)) class="kw">return false; cInsideRe1.SetActivationFunction(TANH); if(!cInsideIm1.Init(class="num">0, class="num">7, OpenCL, iFFTin, iFFTin, class="num">4*iIFFTin, iCount, optimization, iBatch)) class="kw">return false; cInsideIm1.SetActivationFunction(TANH); if(!cInsideRe2.Init(class="num">0, class="num">8, OpenCL, class="num">4*iIFFTin, class="num">4*iIFFTin, iIFFTin, iCount, optimization, iBatch)) class="kw">return false; cInsideRe2.SetActivationFunction(None); if(!cInsideIm2.Init(class="num">0, class="num">9, OpenCL, class="num">4*iIFFTin, class="num">4*iIFFTin, iIFFTin, iCount, optimization, iBatch)) class="kw">return false; cInsideIm2.SetActivationFunction(None); if(!cComplexRe.Init(class="num">0, class="num">10, OpenCL, iIFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cComplexIm.Init(class="num">0, class="num">11, OpenCL, iIFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cIFFTRe.Init(class="num">0, class="num">12, OpenCL, iIFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cIFFTIm.Init(class="num">0, class="num">13, OpenCL, iIFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cClear.BufferInit(MathMax(iFFTin, iIFFTin)*iCount, class="num">0)) class="kw">return false; cClear.BufferCreate(OpenCL); class=class="str">"cmt">//--- class="kw">return true; } class="type">bool CNeuronFITSOCL::FFT(CBufferFloat *inp_re, CBufferFloat *inp_im, CBufferFloat *out_re, CBufferFloat *out_im, class="type">bool reverse = false) { class="type">uint global_work_offset[class="num">1] = {class="num">0}; class="type">uint global_work_size[class="num">1] = {iCount}; if(!OpenCL.SetArgumentBuffer(def_k_FFT, def_k_fft_inputs_re, inp_re.GetIndex())) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); class="kw">return false; } if(!OpenCL.SetArgumentBuffer(def_k_FFT, def_k_fft_inputs_im, (!!inp_im ? inp_im.GetIndex() : inp_re.GetIndex()))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); class="kw">return false; }
FFT 核参数绑定与前向传播链路
在 MT5 的 OpenCL 封装里,FFT 算子执行前必须把每个 kernel 参数显式绑定。下面这段连续 SetArgument / SetArgumentBuffer 调用,分别把输入复数虚部指针、输出实部缓冲索引、输出虚部缓冲索引、输入窗口跨度(inp_re.Total()/iCount)、输出窗口跨度(out_re.Total()/iCount)以及反向变换标志 reverse 推给 def_k_FFT。任何一步返回 false 都会用 printf 打出函数名、GetLastError() 和 __LINE__,方便在终端直接定位是哪一行 OpenCL 参数没挂上。 [CODE] if(!OpenCL.SetArgument(def_k_FFT, def_k_fft_input_complex, int(!!inp_im))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); return false; } if(!OpenCL.SetArgumentBuffer(def_k_FFT, def_k_fft_outputs_re, out_re.GetIndex())) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); return false; } if(!OpenCL.SetArgumentBuffer(def_k_FFT, def_k_fft_outputs_im, out_im.GetIndex())) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); return false; } if(!OpenCL.SetArgument(def_k_FFT, def_k_fft_input_window, (int)(inp_re.Total() / iCount))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); return false; } if(!OpenCL.SetArgument(def_k_FFT, def_k_fft_output_window, (int)(out_re.Total() / iCount))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); return false; } if(!OpenCL.SetArgument(def_k_FFT, def_k_fft_reverse, int(reverse))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); return false; } if(!OpenCL.Execute(def_k_FFT, 1, global_work_offset, global_work_size)) { printf("Error of execution kernel %s: %d", __FUNCTION__, GetLastError()); return false; } //--- return true; } [/CODE] 绑定完参数后 OpenCL.Execute 以一维全局工作项(global_work_size)启动核函数;若显卡返回执行错误,同样走 false 分支。这一步若失败,后面所有复数层计算都没有意义,所以必须在 feedForward 里第一时间调用 FFT 并判错。 feedForward 的实际数据流是:先对 NeuronOCL 的输出做正向 FFT(reverse=false),再把得到的实部/虚部缓冲分别送进 cDropRe / cDropIm 做 DropOut,随后各走两层复数全连接(cInsideRe1→cInsideRe2,cInsideIm1→cInsideIm2)。任何一层 FeedForward 返回 false 都会中断传播,这意味着你在调参时若改了 iCount 或缓冲 Total(),要同步核对窗口跨度计算,否则 FFT 出来的频谱维度对不上,下游层会直接报错退出。外汇与贵金属行情高频噪声大,用这类 GPU 频谱层做特征提取时杠杆风险高,实盘前务必在策略测试器用历史数据跑通整链。
if(!OpenCL.SetArgument(def_k_FFT, def_k_fft_input_complex, class="type">int(!!inp_im))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); class="kw">return false; } if(!OpenCL.SetArgumentBuffer(def_k_FFT, def_k_fft_outputs_re, out_re.GetIndex())) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); class="kw">return false; } if(!OpenCL.SetArgumentBuffer(def_k_FFT, def_k_fft_outputs_im, out_im.GetIndex())) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); class="kw">return false; } if(!OpenCL.SetArgument(def_k_FFT, def_k_fft_input_window, (class="type">int)(inp_re.Total() / iCount))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); class="kw">return false; } if(!OpenCL.SetArgument(def_k_FFT, def_k_fft_output_window, (class="type">int)(out_re.Total() / iCount))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); class="kw">return false; } if(!OpenCL.SetArgument(def_k_FFT, def_k_fft_reverse, class="type">int(reverse))) { printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__); class="kw">return false; } if(!OpenCL.Execute(def_k_FFT, class="num">1, global_work_offset, global_work_size)) { printf("Error of execution kernel %s: %d", __FUNCTION__, GetLastError()); class="kw">return false; } class=class="str">"cmt">//--- class="kw">return true; } class="type">bool CNeuronFITSOCL::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) class="kw">return false; class=class="str">"cmt">//--- FFT if(!FFT(NeuronOCL.getOutput(), NULL, cFFTRe.getOutput(), cFFTIm.getOutput(), false)) class="kw">return false; class=class="str">"cmt">//--- DropOut if(!cDropRe.FeedForward(cFFTRe.AsObject())) class="kw">return false; if(!cDropIm.FeedForward(cFFTIm.AsObject())) class="kw">return false; class=class="str">"cmt">//--- Complex Layer if(!cInsideRe1.FeedForward(cDropRe.AsObject())) class="kw">return false; if(!cInsideRe2.FeedForward(cInsideRe1.AsObject())) class="kw">return false; if(!cInsideIm1.FeedForward(cDropIm.AsObject())) class="kw">return false; if(!cInsideIm2.FeedForward(cInsideIm1.AsObject())) class="kw">return false;
◍ FITS网络的反向传播与权重更新链路
这段 CNeuronFITSOCL 的实现把前向的逆过程拆得很直白:calcInputGradients 先对虚部输出做 SumAndNormilize,参数里写死 1 和 0 的偏移、长度 -1 表示跑满缓冲区,随后 Concat 把梯度拼回 iWindowOut 起、长 iIFFTin - iWindowOut 的段。 反向 FFT 调用 FFT(..., true) 做逆变换,再正向 FFT(..., false) 把梯度折回频域,ComplexLayerGradient 负责复数层的梯度回传,之后逐级 calcHiddenGradients 从 InsideRe2/Im2 一路退到 DropRe/Im 与 FFTRe/Im,共 8 次判错返回,任一步 OpenCL 内核失败就断链。 权重更新 updateInputWeights 只覆盖 InsideRe1/Im1 到 Drop 段、InsideRe2/Im2 到 InsideRe1/Im1 段,共 4 次 UpdateInputWeights,不碰最外层的 FFT 输入层权重——这意味着输入端到频域的映射在每轮里是冻结的。 在 MT5 里把这段接上你自己的特征窗口,重点看 iWindowOut 与 iIFFTin 的差值是否等于你喂进去的序列长度;外汇与贵金属行情下用这类频域网络,过拟合和 GPU 显存溢出风险都偏高,参数不对可能直接 return false 断训练。
if(!ComplexLayerOut(cInsideRe2.getOutput(), cInsideIm2.getOutput(), cComplexRe.getOutput(), cComplexIm.getOutput())) class="kw">return false; class=class="str">"cmt">//--- iFFT if(!FFT(cComplexRe.getOutput(), cComplexIm.getOutput(), cIFFTRe.getOutput(), cIFFTIm.getOutput(), true)) class="kw">return false; class=class="str">"cmt">//--- To Output if(!DeConcat(Output, cIFFTRe.getGradient(), cIFFTRe.getOutput(), iWindowOut, iIFFTin - iWindowOut, iCount)) class="kw">return false; class=class="str">"cmt">//--- class="kw">return true; } class="type">bool CNeuronFITSOCL::calcInputGradients(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) class="kw">return false; class=class="str">"cmt">//--- Copy Gradients if(!SumAndNormilize(cIFFTIm.getOutput(), GetPointer(cClear), cIFFTIm.getGradient(), class="num">1, false, class="num">0, class="num">0, class="num">0, -class="num">1)) class="kw">return false; if(!Concat(Gradient, GetPointer(cClear), cIFFTRe.getGradient(), iWindowOut, iIFFTin - iWindowOut, iCount)) class="kw">return false; class=class="str">"cmt">//--- FFT if(!FFT(cIFFTRe.getGradient(), cIFFTIm.getGradient(), cComplexRe.getGradient(), cComplexIm.getGradient(), false)) class="kw">return false; class=class="str">"cmt">//--- Complex Layer if(!ComplexLayerGradient(cInsideRe2.getGradient(), cInsideIm2.getGradient(), cComplexRe.getGradient(), cComplexIm.getGradient())) class="kw">return false; if(!cInsideRe1.calcHiddenGradients(cInsideRe2.AsObject())) class="kw">return false; if(!cInsideIm1.calcHiddenGradients(cInsideIm2.AsObject())) class="kw">return false; if(!cDropRe.calcHiddenGradients(cInsideRe1.AsObject())) class="kw">return false; if(!cDropIm.calcHiddenGradients(cInsideIm1.AsObject())) class="kw">return false; class=class="str">"cmt">//--- Dropout if(!cFFTRe.calcHiddenGradients(cDropRe.AsObject())) class="kw">return false; if(!cFFTIm.calcHiddenGradients(cDropIm.AsObject())) class="kw">return false; class=class="str">"cmt">//--- IFFT if(!FFT(cFFTRe.getGradient(), cFFTIm.getGradient(), cInputsRe.getGradient(), cInputsIm.getGradient(), true)) class="kw">return false; class=class="str">"cmt">//--- To Input Layer if(!DeConcat(NeuronOCL.getGradient(), cFFTIm.getGradient(), cFFTRe.getGradient(), iWindow, iFFTin - iWindow, iCount)) class="kw">return false; class=class="str">"cmt">//--- class="kw">return true; } class="type">bool CNeuronFITSOCL::updateInputWeights(CNeuronBaseOCL *NeuronOCL) { if(!cInsideRe1.UpdateInputWeights(cDropRe.AsObject())) class="kw">return false; if(!cInsideIm1.UpdateInputWeights(cDropIm.AsObject())) class="kw">return false; if(!cInsideRe2.UpdateInputWeights(cInsideRe1.AsObject())) class="kw">return false; if(!cInsideIm2.UpdateInputWeights(cInsideIm1.AsObject())) class="kw">return false; class=class="str">"cmt">//--- class="kw">return true; } class="type">bool CNeuronFITSOCL::Save(const class="type">int file_handle) {
「FITSOCL 神经元的存档与重载细节」
在 MT5 的 OpenCL 神经网络类里,CNeuronFITSOCL 的 Save 方法先调用基类 CNeuronBaseOCL::Save 落盘,再依次写出 5 个整型常量(iWindow、iWindowOut、iCount、iFFTin、iIFFTin),每个都用 FileWriteInteger 且校验返回值是否小于 INT_VALUE,任何一步失败立即 return false。 随后保存 6 个内部对象:cInsideRe1、cInsideIm1、cInsideRe2、cInsideIm2、cDropRe、cDropIm,它们各自实现 Save 接口,任一个返回 false 都会中断写入。这套顺序在回测中意味着模型参数文件头部结构固定为「基类+5常量+6对象」,文件损坏往往发生在第 6 个对象写入前。 Load 侧更谨慎:每读一个 uint 常量前都先用 FileIsEnding 判断文件是否到尾,避免越界读取返回脏数据。读完后显式置 activation=None,再靠 LoadInsideLayer 逐个恢复复数层与 dropout 层。若你改了类成员顺序,旧模型文件加载会直接在第 1 个 FileIsEnding 后失败,不会抛异常但返回 false。 实盘或复盘加载外汇/贵金属训练好的 FITSOCL 模型时,建议先打印 iWindow 与 iCount 的值确认版本匹配;该类涉及 GPU 计算与杠杆品种,参数误读可能导致信号偏移,属高风险操作。
if(!CNeuronBaseOCL::Save(file_handle)) class="kw">return false; class=class="str">"cmt">//--- Save constants if(FileWriteInteger(file_handle, class="type">int(iWindow)) < INT_VALUE) class="kw">return false; if(FileWriteInteger(file_handle, class="type">int(iWindowOut)) < INT_VALUE) class="kw">return false; if(FileWriteInteger(file_handle, class="type">int(iCount)) < INT_VALUE) class="kw">return false; if(FileWriteInteger(file_handle, class="type">int(iFFTin)) < INT_VALUE) class="kw">return false; if(FileWriteInteger(file_handle, class="type">int(iIFFTin)) < INT_VALUE) class="kw">return false; class=class="str">"cmt">//--- Save objects if(!cInsideRe1.Save(file_handle)) class="kw">return false; if(!cInsideIm1.Save(file_handle)) class="kw">return false; if(!cInsideRe2.Save(file_handle)) class="kw">return false; if(!cInsideIm2.Save(file_handle)) class="kw">return false; if(!cDropRe.Save(file_handle)) class="kw">return false; if(!cDropIm.Save(file_handle)) class="kw">return false; class=class="str">"cmt">//--- class="kw">return true; } class="type">bool CNeuronFITSOCL::Load(const class="type">int file_handle) { if(!CNeuronBaseOCL::Load(file_handle)) class="kw">return false; class=class="str">"cmt">//--- Load constants if(FileIsEnding(file_handle)) class="kw">return false; iWindow = class="type">uint(FileReadInteger(file_handle)); if(FileIsEnding(file_handle)) class="kw">return false; iWindowOut = class="type">uint(FileReadInteger(file_handle)); if(FileIsEnding(file_handle)) class="kw">return false; iCount = class="type">uint(FileReadInteger(file_handle)); if(FileIsEnding(file_handle)) class="kw">return false; iFFTin = class="type">uint(FileReadInteger(file_handle)); if(FileIsEnding(file_handle)) class="kw">return false; iIFFTin = class="type">uint(FileReadInteger(file_handle)); activation=None; class=class="str">"cmt">//--- Load objects if(!LoadInsideLayer(file_handle, cInsideRe1.AsObject())) class="kw">return false; if(!LoadInsideLayer(file_handle, cInsideIm1.AsObject())) class="kw">return false; if(!LoadInsideLayer(file_handle, cInsideRe2.AsObject())) class="kw">return false; if(!LoadInsideLayer(file_handle, cInsideIm2.AsObject())) class="kw">return false; if(!LoadInsideLayer(file_handle, cDropRe.AsObject()))
编码器对象与缓冲区的初始化落点
这段初始化逻辑紧接前文,把 OpenCL 张量对象逐个挂到设备上下文上。只要任意一个 Buffer 创建失败就直接 return false,说明整个推理前置条件是“全有或全无”,少一个缓冲层模型都不会往下走。 代码里 cInputsRe 到 cIFFTIm 共 8 个复数层,索引从 0 到 11 跳过了 4~7,那一段留给中间卷积或归一化层。iFFTin 与 iIFFTin 若不一致,清除缓冲按 MathMax(iFFTin, iIFFTin)*iCount 分配,避免逆变换尺寸更大时越界。 CreateEncoderDescriptions 里输入层节点数 = HistoryBars * BarDescr,批归一化层 batch 写死 1000,意味着训练时至少攒够 1000 条样本才跑一趟梯度。Transpose 层把 (HistoryBars, BarDescr) 维度对调,方便后续按特征通道做卷积。 开 MT5 把 HistoryBars 调小到 64、BarDescr 保持 5,输入层节点会从默认几千降到 320,显存占用可能明显回落,老卡上能验证这一改动是否让 Init 不再 return false。
class="kw">return false; if(!LoadInsideLayer(file_handle, cDropIm.AsObject())) class="kw">return false; class=class="str">"cmt">//--- Init objects if(!cInputsRe.Init(class="num">0, class="num">0, OpenCL, iFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cInputsIm.Init(class="num">0, class="num">1, OpenCL, iFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cFFTRe.Init(class="num">0, class="num">2, OpenCL, iFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cFFTIm.Init(class="num">0, class="num">3, OpenCL, iFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cComplexRe.Init(class="num">0, class="num">8, OpenCL, iIFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cComplexIm.Init(class="num">0, class="num">9, OpenCL, iIFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cIFFTRe.Init(class="num">0, class="num">10, OpenCL, iIFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cIFFTIm.Init(class="num">0, class="num">11, OpenCL, iIFFTin * iCount, optimization, iBatch)) class="kw">return false; if(!cClear.BufferInit(MathMax(iFFTin, iIFFTin)*iCount, class="num">0)) class="kw">return false; cClear.BufferCreate(OpenCL); class=class="str">"cmt">//--- class="kw">return true; } class="type">bool CreateEncoderDescriptions(CArrayObj *encoder) { class=class="str">"cmt">//--- CLayerDescription *descr; class=class="str">"cmt">//--- if(!encoder) { encoder = new CArrayObj(); if(!encoder) class="kw">return false; } class=class="str">"cmt">//--- Encoder encoder.Clear(); class=class="str">"cmt">//--- Input layer if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronBaseOCL; class="type">int prev_count = descr.count = (HistoryBars * BarDescr); descr.activation = None; descr.optimization = ADAM; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">1 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronBatchNormOCL; descr.count = prev_count; descr.batch = class="num">1000; descr.activation = None; descr.optimization = ADAM; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">2 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronTransposeOCL; descr.count = HistoryBars; descr.window = BarDescr; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">3