神经网络: 智能交易系统自我优化·进阶篇
(2/3)· 定好策略只是起点,输入参数何时失效、怎样自动重训才是EA活下来的关键
「在MT5里搭一层可训练的神经网络壳」
想让价格行为模型跑在MT5本地,第一步是把多层感知机(MLP)的容器建出来。下面这段把学习率锁在0.001、训练循环只跑2次,属于轻量验证用的配置,真要上外汇或贵金属实盘前务必调参,这两类品种杠杆高、跳空频繁,过拟合会带来明显回撤风险。 double tasaAprende=0.001 决定每次权重更新的步长,ciclosEntren=2 表示整体样本只过两遍网络;若你拿EURUSD的M15收盘价做输入,2次循环往往连趋势毛刺都学不干净,仅适合先确认管道通不通。 creaRedNeuronal 里按隐藏层数量自动分派:两层隐藏都没设就走MLPCreate0(纯线性),只设第一层走MLPCreate1,两层都设才走MLPCreate2。建完用existeRed查nEntradas和nSalidas是否大于0,并打印层数、各层神经元数和权重总数,方便你直接对照日志看网络拓扑对不对。 别把0.001和2次当默认值焊死。打开MT5的EA向导把这段塞进OnInit,跑完看Experts标签里的「权重 Nº」输出,再决定要不要把ciclosEntren拉到50以上做严肃训练。
class="type">class="kw">double tasaAprende= class="num">0.001; class=class="str">"cmt">//网络训练比率 class="type">int ciclosEntren= class="num">2; class=class="str">"cmt">//训练循环次数 ResetLastError(); class="type">bool creada= creaRedNeuronal(objRed); class=class="str">"cmt">//创建神经网络 if(creada) { preparaDatosEntra(objRed, simb, arDatosAprende); class=class="str">"cmt">//在 arDatosAprende 里下载输入/输出数据 if(imprDatos) imprimeDatosEntra(simb, arDatosAprende); class=class="str">"cmt">//显示数据以评估可信度 if(normEntrada || normSalida) normalizaDatosRed(objRed, arDatosAprende, normEntrada, normSalida); class=class="str">"cmt">//输入/输出数据规范化选项 if(barajar) barajaDatosEntra(arDatosAprende, nNeuronEntra+nNeuronSal); class=class="str">"cmt">//遍历数据数组字符串 errorMedioEntren= entrenaEvalRed(objRed, arDatosAprende, ciclosEntren, tasaAprende); class=class="str">"cmt">//执行训练/优化 salvaRedFich(arObjRed[codS], "copiaSegurRed_"+simb); class=class="str">"cmt">//保存网络至磁盘文件 } else infoError(GetLastError(), __FUNCTION__); class="kw">return(_LastError==class="num">0); } class=class="str">"cmt">//--------------------------------- 创建网络 -------------------------------------- class="type">bool creaRedNeuronal(CMultilayerPerceptronShell &objRed) { class="type">bool creada= false; class="type">int nEntradas= class="num">0, nSalidas= class="num">0, nPesos= class="num">0; if(nNeuronCapa1<class="num">1 && nNeuronCapa2<class="num">1) CAlglib::MLPCreate0(nNeuronEntra, nNeuronSal, objRed); class=class="str">"cmt">//线性输出 else if(nNeuronCapa2<class="num">1) CAlglib::MLPCreate1(nNeuronEntra, nNeuronCapa1, nNeuronSal, objRed); class=class="str">"cmt">//线性输出 else CAlglib::MLPCreate2(nNeuronEntra, nNeuronCapa1, nNeuronCapa2, nNeuronSal, objRed); class=class="str">"cmt">//线性输出 creada= existeRed(objRed); if(!creada) Print("创建神经网络错误 ==> ", __FUNCTION__, " ", _LastError); else { CAlglib::MLPProperties(objRed, nEntradas, nSalidas, nPesos); Print("创建网络的层数 nº", propiedadRed(objRed, N_CAPAS)); Print("输入层的神经元 Nº ", nEntradas); Print("隐藏层 class="num">1 的神经元 Nº ", nNeuronCapa1); Print("隐藏层 class="num">2 的神经元 Nº ", nNeuronCapa2); Print("输出层的神经元 Nº ", nSalidas); Print("权重 Nº", nPesos); } class="kw">return(creada); } class=class="str">"cmt">//--------------------------------- 存在网络 -------------------------------------------- class="type">bool existeRed(CMultilayerPerceptronShell &objRed) { class="type">bool resp= false; class="type">int nEntradas= class="num">0, nSalidas= class="num">0, nPesos= class="num">0; CAlglib::MLPProperties(objRed, nEntradas, nSalidas, nPesos); resp= nEntradas>class="num">0 && nSalidas>class="num">0; class="kw">return(resp); } enum mis_PROPIEDADES_RED {N_CAPAS, N_NEURONAS, N_ENTRADAS, N_SALIDAS, N_PESOS};
◍ 把网络属性和训练数据塞进 MT5
做外汇或贵金属的 AI 辅助判断,第一步不是跑模型,而是先把多层感知机的结构和样本矩阵摸清楚。下面这个函数用枚举开关直接问网络:有几层、某层几个神经元、输入输出维度、总权重数,全部走 ALGLIB 封装,省得你手动维护。 int propiedadRed(CMultilayerPerceptronShell &objRed, mis_PROPIEDADES_RED prop=N_CAPAS, int numCapa=0) 里,prop 超过 N_NEURONAS 时才调 CAlglib::MLPProperties 拿全局参数;查单层神经元数必须传 numCapa,否则返回 0。这种懒加载在样本超过 5000 根 K 线时,能少调一次底层接口。 preparaDatosEntra 负责把指标值和策略结果写进 CMatrixDouble。arDatos.Resize(historialEntrena, nEntras+nSals) 决定了矩阵列数 = 输入神经元 + 输出神经元;外盘黄金 m5 上若 historialEntrena=1000、NUM_INDIC=5,矩阵就是 1000×7。 imprimeDatosEntra 把矩阵导成 dataEntrenaRed_符号.csv,表头写死 indic1;indic2;indic3...;resultEstrat。你开 MT5 文件目录找这个 csv,用 Excel 打开就能肉眼看哪根 bar 的特征异常——外汇杠杆高、贵金属跳空多,数据清洗前先盯这份表。 别把正态当圣经:导出的原始指标值若不做 normEntrada 归一,ALGLIB 在价差突变周可能训出偏移权重,回测好看实盘概率掉。
class="type">int propiedadRed(CMultilayerPerceptronShell &objRed, mis_PROPIEDADES_RED prop= N_CAPAS, class="type">int numCapa= class="num">0) { class=class="str">"cmt">//如果请求神经元数量 N_NEURONAS, 设置层索引 numCapa class="type">int resp= class="num">0, numEntras= class="num">0, numSals= class="num">0, numPesos= class="num">0; if(prop>N_NEURONAS) CAlglib::MLPProperties(objRed, numEntras, numSals, numPesos); class="kw">switch(prop) { case N_CAPAS: resp= CAlglib::MLPGetLayersCount(objRed); class="kw">break; case N_NEURONAS: resp= CAlglib::MLPGetLayerSize(objRed, numCapa); class="kw">break; case N_ENTRADAS: resp= numEntras; class="kw">break; case N_SALIDAS: resp= numSals; class="kw">break; case N_PESOS: resp= numPesos; } class="kw">return(resp); } class=class="str">"cmt">//---------------------------------- 准备输入/输出数据 -------------------------------------------------- class="type">void preparaDatosEntra(CMultilayerPerceptronShell &objRed, class="type">class="kw">string simb, CMatrixDouble &arDatos, class="type">bool normEntrada= true , class="type">bool normSalida= true) { class="type">int fin= class="num">0, fila= class="num">0, colum= class="num">0, nEntras= propiedadRed(objRed, N_ENTRADAS), nSals= propiedadRed(objRed, N_SALIDAS); class="type">class="kw">double valor= class="num">0, arResp[]; arDatos.Resize(historialEntrena, nEntras+nSals); fin= velaIniDesc+historialEntrena; for(fila= velaIniDesc; fila<fin; fila++) { for(colum= class="num">0; colum<NUM_INDIC; colum++) { valor= valorIndic(codS, fila, colum); arDatos[fila-class="num">1].Set(colum, valor); } calcEstrat(fila-nVelasPredic, arResp); for(colum= class="num">0; colum<nSals; colum++) arDatos[fila-class="num">1].Set(colum+nEntras, arResp[colum]); } class="kw">return; } class=class="str">"cmt">//---------------------------------- 显示输入/输出数据 -------------------------------------------------- class="type">void imprimeDatosEntra(class="type">class="kw">string simb, CMatrixDouble &arDatos) { class="type">class="kw">string encabeza= "indic1;indic2;indic3...;resultEstrat", class=class="str">"cmt">//指标名称以 ";" 分隔 fichImprime= "dataEntrenaRed_"+simb+".csv"; class="type">bool entrar= false, copiado= false; class="type">int fila= class="num">0, colum= class="num">0, resultEstrat= -class="num">1, nBuff= class="num">0, nFilas= arDatos.Size(), nColum= nNeuronEntra+nNeuronSal, puntFich= FileOpen(fichImprime, FILE_WRITE|FILE_CSV|FILE_COMMON); FileWrite(puntFich, encabeza); for(fila= class="num">0; fila<nFilas; fila++) { linea= IntegerToString(fila)+";"+TimeToString(iTime(simb, PERIOD_CURRENT, velaIniDesc+fila), TIME_MINUTES)+";"; for(colum= class="num">0; colum<nColum; colum++)
把训练矩阵压进神经网络能吃的区间
写文件那段只是收尾:把二维数组 arDatos 按分号拼成行,DoubleToString 保留 8 位小数,FileWrite 落盘后 FileFlush+FileClose,弹窗告诉你文件在 TerminalInfoString(TERMINAL_COMMONDATA_PATH)+\"\\Files\"。真正喂给多层感知机前,得先过 normalizaDatosRed 这关。 规范化不是可选项。神经网络对量纲极敏感,欧元小时图收盘价和成交量差好几个数量级,直接塞进去梯度会崩。函数对输入列(前 nEntradas 列)和输出列(其后 nSalidas 列)分别扫一遍,先找每列 maxAbs/minAbs,再按 intervEntrada / intervSalida 里给的相对区间做线性映射。 注意输出列多存了两个全局量:minAbsSalida 和 maxAbsSalida。反归一化预测值时要用,漏掉就会把网络输出的 0~1 映射回原始量纲时整体偏移。外汇与贵金属杠杆高,信号偏差会被放大,验证时务必核对这两个值。 调用时 normEntrada 和 normSalida 默认都 true,若你只训输入不规输出(比如自编码器),显式传 false 省一轮扫描。StringSplit 用 ';' 拆区间字符串,格式必须是「下界;上界」两段,少一段 StringToDouble 会返回 0 把映射压扁。
linea= linea+DoubleToString(arDatos[fila][colum], class="num">8)+(colum<(nColum-class="num">1)? ";": ""); FileWrite(puntFich, linea); } FileFlush(puntFich); FileClose(puntFich); Alert("下载文件= ", fichImprime); Alert("路径= ", TerminalInfoString(TERMINAL_COMMONDATA_PATH)+"\\Files"); class="kw">return; } class=class="str">"cmt">//------------------------------------ 规范化输入/输出数据------------------------------------- class="type">void normalizaDatosRed(CMultilayerPerceptronShell &objRed, CMatrixDouble &arDatos, class="type">bool normEntrada= true, class="type">bool normSalida= true) { class="type">int fila= class="num">0, colum= class="num">0, maxFila= arDatos.Size(), nEntradas= propiedadRed(objRed, N_ENTRADAS), nSalidas= propiedadRed(objRed, N_SALIDAS); class="type">class="kw">double maxAbs= class="num">0, minAbs= class="num">0, maxRel= class="num">0, minRel= class="num">0, arMaxMinRelEntra[], arMaxMinRelSals[]; class="type">class="kw">ushort valCaract= StringGetCharacter(";", class="num">0); if(normEntrada) StringSplit(intervEntrada, valCaract, arMaxMinRelEntra); if(normSalida) StringSplit(intervSalida, valCaract, arMaxMinRelSals); for(colum= class="num">0; normEntrada && colum<nEntradas; colum++) { maxAbs= arDatos[class="num">0][colum]; minAbs= arDatos[class="num">0][colum]; minRel= StringToDouble(arMaxMinRelEntra[class="num">0]); maxRel= StringToDouble(arMaxMinRelEntra[class="num">1]); for(fila= class="num">0; fila<maxFila; fila++) class=class="str">"cmt">//定义每个数据列的 maxAbs 和 minAbs { if(maxAbs<arDatos[fila][colum]) maxAbs= arDatos[fila][colum]; if(minAbs>arDatos[fila][colum]) minAbs= arDatos[fila][colum]; } for(fila= class="num">0; fila<maxFila; fila++) class=class="str">"cmt">//设置新的规范化值 arDatos[fila].Set(colum, normValor(arDatos[fila][colum], maxAbs, minAbs, maxRel, minRel)); } for(colum= nEntradas; normSalida && colum<(nEntradas+nSalidas); colum++) { maxAbs= arDatos[class="num">0][colum]; minAbs= arDatos[class="num">0][colum]; minRel= StringToDouble(arMaxMinRelSals[class="num">0]); maxRel= StringToDouble(arMaxMinRelSals[class="num">1]); for(fila= class="num">0; fila<maxFila; fila++) { if(maxAbs<arDatos[fila][colum]) maxAbs= arDatos[fila][colum]; if(minAbs>arDatos[fila][colum]) minAbs= arDatos[fila][colum]; } minAbsSalida= minAbs; maxAbsSalida= maxAbs; for(fila= class="num">0; fila<maxFila; fila++) arDatos[fila].Set(colum, normValor(arDatos[fila][colum], maxAbs, minAbs, maxRel, minRel)); } class="kw">return; }
「把样本洗牌与LM训练接进MT5神经网络」
做外汇或贵金属模型训练时,数据顺序会带隐性偏置。下面这段先把训练矩阵按行随机打乱,再喂给多层感知机,属于可直接拷进EA工程的底层工具。 double normValor(...) 负责把任意数值线性映射到 [minRel, maxRel] 区间,默认 [-1,1];当 maxAbs<=minAbs 时返回0,调用前最好自己校验边界,否则归一化会静默失效。 barajaDatosEntra 用 MathSrand(GetTickCount()) 重置随机种子,再通过 randomEntero 选目标行做原地交换。注意它只推进 fila++ 当 filaDestino!=fila,若随机命中自身就重抽,最坏情况多耗几轮循环,但样本乱序是实打实完成的。 entrenaEvalRed 按权重规模分流:propiedadRed(objRed, N_PESOS)<500 走 MLPTrainLM,否则走 MLPTrainLBFGS(附带 0.01 的 Lipschitz 常数)。默认 tasaAprende=0.001、ciclosEntrena=2,小样本快速验证够用,实盘前建议把循环提到 50 以上观察误差曲线。 codResp 返回 2 或 6 才用 MLPRMSError 取平均误差;其余 code 只 Print 不报错,容易让上层以为训练成功。开 MT5 跑一遍,Alert 里的 NGrad / NHess 数量能直接看出优化器是否真在迭代。贵金属与外汇杠杆高,模型误差只是概率参考,别拿回测当仓位依据。
class="type">class="kw">double normValor(class="type">class="kw">double valor, class="type">class="kw">double maxAbs, class="type">class="kw">double minAbs, class="type">class="kw">double maxRel= class="num">1, class="type">class="kw">double minRel= -class="num">1) { class="type">class="kw">double valorNorm= class="num">0; if(maxAbs>minAbs) valorNorm= (valor-minAbs)*(maxRel-minRel))/(maxAbs-minAbs) + minRel; class="kw">return(valorNorm); } class=class="str">"cmt">//------------------------------------ 逐个遍历整个 输入/输出 字符串 ----------------------------------- class="type">void barajaDatosEntra(CMatrixDouble &arDatos, class="type">int nColum) { class="type">int fila= class="num">0, colum= class="num">0, filaDestino= class="num">0, nFilas= arDatos.Size(); class="type">class="kw">double filaTmp[]; ArrayResize(filaTmp, nColum); MathSrand(GetTickCount()); class=class="str">"cmt">//重置随机后代序列 class="kw">while(fila<nFilas) { filaDestino= randomEntero(class="num">0, nFilas-class="num">1); class=class="str">"cmt">//以任意方式接收目标字符串 if(filaDestino!=fila) { for(colum= class="num">0; colum<nColum; colum++) filaTmp[colum]= arDatos[filaDestino][colum]; for(colum= class="num">0; colum<nColum; colum++) arDatos[filaDestino].Set(colum, arDatos[fila][colum]); for(colum= class="num">0; colum<nColum; colum++) arDatos[fila].Set(colum, filaTmp[colum]); fila++; } } class="kw">return; } class=class="str">"cmt">//---------------------------------- 随机移动 ------------------------------------------------------- class="type">int randomEntero(class="type">int minRel= class="num">0, class="type">int maxRel= class="num">1000) { class="type">int num= (class="type">int)MathRound(randomDouble((class="type">class="kw">double)minRel, (class="type">class="kw">double)maxRel)); class="kw">return(num); } class=class="str">"cmt">//---------------------------------- 网络训练------------------------------------------- class="type">class="kw">double entrenaEvalRed(CMultilayerPerceptronShell &objRed, CMatrixDouble &arDatosEntrena, class="type">int ciclosEntrena= class="num">2, class="type">class="kw">double tasaAprende= class="num">0.001) { class="type">bool salir= false; class="type">class="kw">double errorMedio= class="num">0; class="type">class="kw">string mens= "Entrenamiento Red"; class="type">int k= class="num">0, i= class="num">0, codResp= class="num">0, historialEntrena= arDatosEntrena.Size(); CMLPReportShell infoEntren; ResetLastError(); class="type">class="kw">datetime tmpIni= TimeLocal(); Alert("神经网络优化开始..."); Alert("依据所应用的历史数据等待若干分钟。"); Alert("...class=class="str">"cmt">///..."); if(propiedadRed(objRed, N_PESOS)<class="num">500) CAlglib::MLPTrainLM(objRed, arDatosEntrena, historialEntrena, tasaAprende, ciclosEntrena, codResp, infoEntren); else CAlglib::MLPTrainLBFGS(objRed, arDatosEntrena, historialEntrena, tasaAprende, ciclosEntrena, class="num">0.01, class="num">0, codResp, infoEntren); if(codResp==class="num">2 || codResp==class="num">6) errorMedio= CAlglib::MLPRMSError(objRed, arDatosEntrena, historialEntrena); else Print("响应代码: ", codResp); class="type">class="kw">datetime tmpFin= TimeLocal(); Alert("NGrad ", infoEntren.GetNGrad(), " NHess ", infoEntren.GetNHess(), " NCholesky ", infoEntren.GetNCholesky()); Alert("codResp ", codResp," 平均训练误差 "+DoubleToString(errorMedio, class="num">8), " ciclosEntrena ", ciclosEntrena);
◍ 把训练好的多层感知机落盘成 .red
在 MT5 里跑完一轮神经网络训练,最怕 EA 重启后权重全丢。下面这段逻辑把 CMultilayerPerceptronShell 对象直接序列化到公共目录的二进制文件,默认名 copiaSegurRed.red,下次开机用 FileRead 逆向读回即可续用。
| 保存前先 FileDelete 同名的旧文件,再以 FILE_WRITE | FILE_BIN | FILE_COMMON 打开句柄;句柄不等于 INVALID_HANDLE 才继续,否则 redSalvada 直接为 false。写入顺序很死板:先层数 numCapas,再逐层神经元规模 arNeurCapa[k],随后按层遍历每个神经元的缩放、激活函数类型与偏置。 |
|---|
输入层(k==0)和输出层(k==numCapas-1)要额外写 media 与 sigma,这两个值来自 MLPGetInputScaling / MLPGetOutputScaling,是 ALGLIB 做特征归一化的关键参数。中间隐藏层只写 funcTipo 和 umbral,连接权重通过 MLPGetWeight(objRed, k, i, k+1, j) 按「本层→下一层」的拓扑逐个落盘。 任何一步 FileWriteDouble 返回小于等于 0 都会让 redSalvada 链式置否,因此外层 for 都用 redSalvada && 做短路保护。外汇与贵金属行情具有高杠杆与跳空风险,这类模型权重仅反映历史样本分布,实盘加载后仍需用小资金验证泛化能力。
class="type">bool salvaRedFich(CMultilayerPerceptronShell &objRed, class="type">class="kw">string nombArch= "") { class="type">bool redSalvada= false; class="type">int k= class="num">0, i= class="num">0, j= class="num">0, numCapas= class="num">0, arNeurCapa[], neurCapa1= class="num">1, funcTipo= class="num">0, puntFichRed= class="num">9999; class="type">class="kw">double umbral= class="num">0, peso= class="num">0, media= class="num">0, sigma= class="num">0; if(nombArch=="") nombArch= "copiaSegurRed"; nombArch= nombArch+".red"; FileDelete(nombArch, FILE_COMMON); ResetLastError(); puntFichRed= FileOpen(nombArch, FILE_WRITE|FILE_BIN|FILE_COMMON); redSalvada= puntFichRed!=INVALID_HANDLE; if(redSalvada) { numCapas= CAlglib::MLPGetLayersCount(objRed); redSalvada= redSalvada && FileWriteDouble(puntFichRed, numCapas)>class="num">0; ArrayResize(arNeurCapa, numCapas); for(k= class="num">0; redSalvada && k<numCapas; k++) { arNeurCapa[k]= CAlglib::MLPGetLayerSize(objRed, k); redSalvada= redSalvada && FileWriteDouble(puntFichRed, arNeurCapa[k])>class="num">0; } for(k= class="num">0; redSalvada && k<numCapas; k++) { for(i= class="num">0; redSalvada && i<arNeurCapa[k]; i++) { if(k==class="num">0) { CAlglib::MLPGetInputScaling(objRed, i, media, sigma); FileWriteDouble(puntFichRed, media); FileWriteDouble(puntFichRed, sigma); } else if(k==numCapas-class="num">1) { CAlglib::MLPGetOutputScaling(objRed, i, media, sigma); FileWriteDouble(puntFichRed, media); FileWriteDouble(puntFichRed, sigma); } CAlglib::MLPGetNeuronInfo(objRed, k, i, funcTipo, umbral); FileWriteDouble(puntFichRed, funcTipo); FileWriteDouble(puntFichRed, umbral); for(j= class="num">0; redSalvada && k<(numCapas-class="num">1) && j<arNeurCapa[k+class="num">1]; j++) { peso= CAlglib::MLPGetWeight(objRed, k, i, k+class="num">1, j);
从磁盘把训练好的网络读回来
把多层感知机落盘只是上半场,重启 EA 或换终端时还得把权重原样加载回内存。下面这段恢复函数默认读 copiaSegurRed.red,用 FILE_COMMON 标志保证文件在终端共享目录,换账户登录也能定位到。
恢复时先读层数 numCapas,按 2/3/4 层分别调 MLPCreate0/1/2 重建空网络壳子;若层数不在这三个区间,后面 existeRed 会返回 false 并打印 _LastError,网络等于没建起来。
重建后立刻用 MLPProperties 取回 nEntradas、nSalidas、nPesos,并把各层神经元数 Print 出来——这是验证读盘没串文件的直接手段。例如一层隐藏层结构会打出「隐藏层 1 的神经元 Nº」对应 nNeuronCapa1 的实际值。
输入层的缩放参数 media 与 sigma 存在文件头之后,按层、按神经元顺序 FileReadDouble 读回并 MLPSetInputScaling 写回。外汇与贵金属行情标准化若漏掉这一步,网络推理会概率性偏移,实盘信号可能失真。
class="type">bool recuperaRedFich(CMultilayerPerceptronShell &objRed, class="type">class="kw">string nombArch= "") { class="type">bool exito= false; class="type">int k= class="num">0, i= class="num">0, j= class="num">0, nEntradas= class="num">0, nSalidas= class="num">0, nPesos= class="num">0, numCapas= class="num">0, arNeurCapa[], funcTipo= class="num">0, puntFichRed= class="num">9999; class="type">class="kw">double umbral= class="num">0, peso= class="num">0, media= class="num">0, sigma= class="num">0; if(nombArch=="") nombArch= "copiaSegurRed"; nombArch= nombArch+".red"; puntFichRed= FileOpen(nombArch, FILE_READ|FILE_BIN|FILE_COMMON); exito= puntFichRed!=INVALID_HANDLE; if(exito) { numCapas= (class="type">int)FileReadDouble(puntFichRed); ArrayResize(arNeurCapa, numCapas); for(k= class="num">0; k<numCapas; k++) arNeurCapa[k]= (class="type">int)FileReadDouble(puntFichRed); if(numCapas==class="num">2) CAlglib::MLPCreate0(nNeuronEntra, nNeuronSal, objRed); else if(numCapas==class="num">3) CAlglib::MLPCreate1(nNeuronEntra, nNeuronCapa1, nNeuronSal, objRed); else if(numCapas==class="num">4) CAlglib::MLPCreate2(nNeuronEntra, nNeuronCapa1, nNeuronCapa2, nNeuronSal, objRed); exito= existeRed(arObjRed[class="num">0]); if(!exito) Print("神经网络生成错误 ==> ", __FUNCTION__, " ", _LastError); else { CAlglib::MLPProperties(objRed, nEntradas, nSalidas, nPesos); Print("恢复网络层数 nº", propiedadRed(objRed, N_CAPAS)); Print("输入层的神经元 Nº ", nEntradas); Print("隐藏层 class="num">1 的神经元 Nº ", nNeuronCapa1); Print("隐藏层 class="num">2 的神经元 Nº ", nNeuronCapa2); Print("输出层的神经元 Nº ", nSalidas); Print("权重 Nº", nPesos); for(k= class="num">0; k<numCapas; k++) { for(i= class="num">0; i<arNeurCapa[k]; i++) { if(k==class="num">0) { media= FileReadDouble(puntFichRed); sigma= FileReadDouble(puntFichRed); CAlglib::MLPSetInputScaling(objRed, i, media, sigma);