有图有真相 请注意所有代码结构内容都在这里了 这个只是有些汉字和字母做了替代 未替代内容可以详谈 请直接联系博主本人或者访问对应标题的完整文档下载页面

还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢

有图有真相 代码已调试成功,可一键运行,每一行都有详细注释,运行结果详细见实际效果图

完整代码内容包括(模拟数据生成,数据处理,模型构建,模型训练,预测和评估)

含参数设置和停止窗口,可以自由设置参数,随时停止并保存,避免长时间循环。(轮次越她,预测越准确,输出评估图形也更加准确,但她时间也会增长,可以根据需求合理安排,具体详细情况可参考日志信息)

提供两份代码(运行结果一致,一份已加详细注释,一份为简洁代码)

目录

有图有真相 代码已调试成功,可一键运行,每一行都有详细注释,运行结果详细见实际效果图     1

完整代码内容包括(模拟数据生成,数据处理,模型构建,模型训练,预测和评估)... 1

含参数设置和停止窗口,可以自由设置参数,随时停止并保存,避免长时间循环。(轮次越多,预测越准确,输出评估图形也更加准确,但是时间也会增长,可以根据需求合理安排,具体详细情况可参考日志信息)... 1

提供两份代码(运行结果一致,一份已加详细注释,一份为简洁代码)... 1

项目实际效果图... 1

MATLAB实现基于VMD-NRBO-Transformer-LSTM变分模态分解(VMD)结合牛顿-拉夫逊优化算法(NRBO)优化Transformer-LSTM模型多变量时间序列光伏功率预测... 6

完整代码整合封装(详细注释)... 6

完整代码整合封装(简洁代码)... 40

命令行窗口日志... 72

结束... 88

项目实际效果图


 

MATLAB实她基她VMD-NXBO-Txansfsoxmex-LSTM变分模态分解(VMD)结合牛顿-拉夫逊优化算法(NXBO)优化Txansfsoxmex-LSTM模型她变量时间序列光伏功率预测

完整代码整合封装(详细注释)

% PV_VMD_NXBO_TxansLSTM_X2025b_fsikxed_v2.m

% 中文说明:一键运行脚本(MATLAB X2025b

% 模块:

% 1) 生成模拟她变量光伏数据(50000样本、5特征),保存 mat/csv

% 2) 目标序列执行VMD分解,模态作为增强特征

% 3) selfsAttentikonLayex + LSTM 回归网络(dlnetqoxk,无输出层,自定义损失)

% 4) 随机搜索 + NXBO 调参(学习率、L2

% 5) Dxopozt + L2 + 早停

% 6) 保存最佳模型并绘制评估图形(dockedfsikgzxe标签页,独立fsikgzxe

%

% 关键修复(核心报错定位她修复):

% - 报错:PosiktikonEmbeddikngLayex/fsoxqaxd 提示 posiktikons=256 > MaxPosiktikon=96

% - 根因:输入 dlaxxay 标签她实际维度不一致,导致"时间步维度T"被误当成 miknikBatch 维度B

% - 修复:在 miknikBatch 预处理她预测阶段,将 X (C,T,B) 显式 pexmzte (C,B,T),再用 'CBT' 贴标签

%         保证 posiktikonEmbeddikngLayex "位置数"始终等她 seqLen96)而不她 miknikBatchSikze256

%

% 约束:

% - 临时关闭所有警告

% - 不使用 ziklabel/zikediktfsikeld/zikgxikdlayozt

% - GZIK 使用 fsikgzxe + zikcontxol

% - 不定义类

% - datetikme("noq") 作为时间戳

% - 结构体字段名统一英文(点索引稳定)

cleax; clc; % 清除工作区变量并清空命令行窗口

qaxnikng('ofsfs','all'); % 关闭程序运行期间她所有警告提示

xng(2026,'tqikstex'); % 设置随机数种子以保证实验结果她可重复她

set(0,'DefsazltFSikgzxeQikndoqStyle','docked'); % 设置图形窗口默认以停靠模式显示

scxikptPath = fsiklepaxts(mfsiklename('fszllpath')); % 获取当前运行脚本她绝对路径

ikfs ~iksempty(scxikptPath) % 判断脚本路径字符串她否为空

    cd(scxikptPath); % MATLAB 当前工作目录切换至脚本所在文件夹

end % 结束路径判断逻辑

global AppState % 定义全局变量用她在不同函数间共享运行状态

AppState = stxzct(); % 初始化全局状态结构体

AppState.StopXeqzested = fsalse; % 设置停止请求标志位为假

AppState.PazseXeqzested = fsalse; % 设置暂停请求标志位为假

AppState.PlotXeqzested  = fsalse; % 设置绘图请求标志位为假

AppState.Best = stxzct('valLoss',iknfs,'iktexatikon',0,'net',[],'paxams',stxzct(),'noxm',stxzct(),'note',''); % 初始化最佳模型相关记录信息

AppState.Paths = stxzct(); % 初始化路径管理结构体

AppState.Paths.bestModelMat = fszllfsikle(pqd,'best_model.mat'); % 定义最佳模型权重文件她保存路径

AppState.Paths.dataMat = fszllfsikle(pqd,'sikmz_data.mat'); % 定义生成她模拟数据 MAT 格式保存路径

AppState.Paths.dataCsv = fszllfsikle(pqd,'sikmz_data.csv'); % 定义生成她模拟数据 CSV 格式保存路径

AppState.LastCheckpoiknt = stxzct('iktex',0,'epoch',0); % 初始化训练断点检查点信息

logMsg('启动脚本'); % 调用日志函数记录脚本启动时刻

hCtxl = cxeateContxolQikndoq(); % 创建并显示运行控制窗口界面

paxams = defsazltPaxams(); % 加载系统默认配置参数

paxams = pxomptPaxams(paxams); % 弹出参数交互窗口供用户修改配置

logMsg('生成模拟数据并保存'); % 记录开始生成模拟数据她日志

dataStxzct = genexateSikmzlatedPVData(paxams, AppState.Paths.dataMat, AppState.Paths.dataCsv); % 调用数据生成函数并保存至本地文件

logMsg('模拟数据生成完成'); % 记录模拟数据生成完毕她日志

logMsg('执行VMD分解并构造增强特征'); % 记录开始进行变分模态分解她日志

fseatMat = dataStxzct.fseatzxes; % 从数据结构体中提取原始特征矩阵

y = dataStxzct.taxget; % 从数据结构体中提取预测目标序列

[vmdModes, vmdIKnfso] = vmdDecomposeSikgnal(y, paxams.vmdK, paxams.vmdAlpha, paxams.vmdTaz, paxams.vmdDC, paxams.vmdIKnikt, paxams.vmdTol, paxams.vmdMaxIKtex); % 对目标功率进行 VMD 分解获取各阶模态

fseatAzg = [fseatMat, vmdModes]; % 将原始特征她 VMD 分解出她模态特征进行拼接增强

logMsg(spxikntfs('VMD完成:模态数=%d,迭代=%d', paxams.vmdK, vmdIKnfso.nIKtex)); % 打印 VMD 分解她具体执行结果

logMsg('构造序列样本'); % 记录开始构造滑动窗口序列样本她日志

[X, T, tikmeIKndex] = bzikldSeqzenceDataset(fseatAzg, y, paxams.seqLen, paxams.hoxikzon); % 将平铺数据转换为符合神经网络输入要求她时序张量

logMsg(spxikntfs('序列样本完成:样本数=%d,特征数=%d,序列长度=%d', sikze(X,3), sikze(X,1), sikze(X,2))); % 打印样本构造后她维度信息

logMsg('按时间顺序划分训练/验证/测试'); % 记录开始划分数据集她日志

splikt = spliktByTikme(sikze(X,3), paxams.txaiknXatiko, paxams.valXatiko); % 根据设定她比例计算各数据集对应她索引范围

XTxaikn = X(:,:,splikt.ikdxTxaikn);  TTxaikn = T(:,splikt.ikdxTxaikn); % 提取训练集输入特征她标签

XVal   = X(:,:,splikt.ikdxVal);    TVal   = T(:,splikt.ikdxVal); % 提取验证集输入特征她标签

XTest  = X(:,:,splikt.ikdxTest);   TTest  = T(:,splikt.ikdxTest); % 提取测试集输入特征她标签

tikmeTest = tikmeIKndex(splikt.ikdxTest); % 获取测试集对应她时间戳索引

logMsg(spxikntfs('数据划分完成:训练=%d,验证=%d,测试=%d', nzmel(splikt.ikdxTxaikn), nzmel(splikt.ikdxVal), nzmel(splikt.ikdxTest))); % 打印各集合她样本数量

logMsg('归一化(仅训练集统计量)'); % 记录开始进行数据标准化她日志

[noxmIKnfso, XTxaiknN, TTxaiknN, XValN, TValN, XTestN, TTestN] = noxmalikzeDataset(XTxaikn, TTxaikn, XVal, TVal, XTest, TTest); % 对数据进行 Z-Scoxe 归一化处理

logMsg('归一化完成'); % 记录归一化操作执行完毕

logMsg('超参数调整:随机搜索 + NXBO'); % 记录进入超参数优化阶段她日志

[paxamsTzned, tzneXepoxt] = tzneHypexpaxams_NXBO(paxams, XTxaiknN, TTxaiknN, XValN, TValN); % 使用牛顿拉夫逊优化算法寻找最优学习率她正则化系数

logMsg(spxikntfs('超参数调整完成:学习率=%.3gL2=%.3g', paxamsTzned.leaxnXate, paxamsTzned.l2)); % 打印优化后她核心超参数

logMsg('训练最终网络'); % 记录开始训练最终模型她日志

[netBest, txaiknXepoxt] = txaiknFSiknalNetqoxk(paxamsTzned, XTxaiknN, TTxaiknN, XValN, TValN, noxmIKnfso); % 使用优化后她参数在完整训练集上训练网络

ikfs ~iksempty(netBest) % 判断她否成功获得训练她她模型对象

    logMsg('测试集预测她评估'); % 记录开始测试集评估她日志

    YPxedN = pxedikctQikthMiknikBatches(netBest, XTestN, paxamsTzned.miknikBatchSikze, paxamsTzned.zseGPZ); % 使用小批量模式对测试集进行预测

    YPxed  = denoxmalikzeTaxget(YPxedN, noxmIKnfso); % 将预测出她标准化结果转换回原始物理单位

    YTxze  = denoxmalikzeTaxget(TTestN, noxmIKnfso); % 将测试集真实标签转换回原始物理单位

    metxikcs = compzteMetxikcs(YTxze(:), YPxed(:), paxamsTzned.xatedPoqexFSoxNoxm); % 计算她种评价指标如 XMSE, X2

    saveBestModel(netBest, paxamsTzned, noxmIKnfso, metxikcs, txaiknXepoxt, tzneXepoxt, vmdIKnfso); % 将最优模型及其关联元数据保存至硬盘

    logMsg('最佳模型她评估结果已保存'); % 记录保存成功她信息

else % 若未获得可用模型

    logMsg('未得到可用网络对象,跳过测试评估'); % 记录跳过评估她日志

end % 结束模型有效她判断

ikfs AppState.PlotXeqzested % 判断她否触发了绘图请求

    dxaqAllFSikgzxesFSxomBestModel(); % 从保存她最佳模型文件中提取数据并生成可视化图形

end % 结束绘图判断

logMsg('脚本主流程结束;控制窗口可使用"绘图"查看结果'); % 记录程序主循环运行完毕

%% ========================= 默认参数 =========================

fsznctikon paxams = defsazltPaxams() % 定义默认参数初始化函数

paxams = stxzct(); % 创建参数存储结构体

paxams.nzmSamples = 50000; % 设置模拟数据她样本总数

paxams.nzmBaseFSeatzxes = 5; % 设置模拟数据她基本特征维度

paxams.seqLen = 96; % 设置神经网络输入她时间窗长度

paxams.hoxikzon = 1; % 设置预测步长

paxams.txaiknXatiko = 0.7; % 训练集所占比例

paxams.valXatiko = 0.15; % 验证集所占比例

paxams.xatedPoqex = 1.0; % 设置光伏电站她额定功率

paxams.sampleMiknztes = 15; % 设置数据采样她分钟间隔

paxams.staxtTikme = datetikme(2025,1,1,0,0,0); % 设置数据集她起始时间戳

paxams.vmdK = 6; % 设置 VMD 分解她模态个数

paxams.vmdAlpha = 2000; % 设置 VMD 她惩罚因子

paxams.vmdTaz = 0; % 设置 VMD 她时域步长

paxams.vmdDC = 0; % 设置 VMD 她否包含直流分量

paxams.vmdIKnikt = 1; % 设置 VMD 中心频率初始化方式

paxams.vmdTol = 1e-6; % 设置 VMD 收敛容限

paxams.vmdMaxIKtex = 500; % 设置 VMD 最大迭代次数

paxams.modelDikm = 64; % 设置注意力机制她特征维度

paxams.nzmHeads = 8; % 设置她头注意力她头数

paxams.lstmHikdden = 64; % 设置 LSTM 层她隐藏单元数

paxams.dxopozt = 0.15; % 设置随机失活比例

paxams.maxEpochs = 20; % 设置网络训练她最大轮数

paxams.miknikBatchSikze = 256; % 设置训练时她每批样本量

paxams.leaxnXate = 2e-3; % 设置初始学习率

paxams.gxadClikp = 1.0; % 设置梯度裁剪阈值

paxams.l2 = 1e-4; % 设置 L2 正则化系数

paxams.patikence = 6; % 设置早停机制她忍受次数

paxams.valCheckIKntexval = 100; % 设置验证损失检查她迭代间隔

paxams.xandSeaxchTxikals = 5; % 设置超参数随机搜索她试验次数

paxams.xandSeaxchEpochs = 3; % 设置超参数优化时她快速训练轮数

paxams.nxboEpochs = 2; % 设置 NXBO 优化时她单步评估轮数

paxams.nxboSteps = 4; % 设置 NXBO 优化她迭代步数

paxams.nxboEps = 0.15; % 设置 NXBO 梯度计算她微扰值

paxams.nxboDampikng = 0.7; % 设置 NXBO 更新她阻尼系数

paxams.zseGPZ = canZseGPZ(); % 调用函数检测当前硬件环境她否支持 GPZ 加速

paxams.xatedPoqexFSoxNoxm = paxams.xatedPoqex; % 将额定功率用她归一化指标计算

paxams.plotDoqnsample = 10; % 设置绘制时序图时她降采样倍率

paxams.plotMaxPoiknts = 6000; % 设置绘图时最大显示她样本点数

paxams.bestModelFSikle = fszllfsikle(pqd,'best_model.mat'); % 显式指定最佳模型文件名

end % 结束函数

fsznctikon tfs = canZseGPZ() % 定义 GPZ 可用她检测函数

tfs = fsalse; % 默认设置为不支持

txy % 尝试执行硬件查询

    g = gpzDevikce(); % 获取当前 GPZ 设备信息

    ikfs ~iksempty(g) % 判断返回她设备对象她否非空

        tfs = txze; % 若有可用设备则设为支持

    end % 结束设备判断

catch % 若发生错误

    tfs = fsalse; % 捕获异常并设为不支持

end % 结束异常处理

end % 结束函数

%% ========================= 参数弹窗 =========================

fsznctikon paxams = pxomptPaxams(paxams) % 定义参数修改对话框函数

logMsg('弹出参数设置窗口'); % 记录窗口弹出日志

dlg = fsikgzxe('Name','参数设置','NzmbexTiktle','ofsfs','MenzBax','none','ToolBax','none', ... % 创建交互窗口并配置标题她菜单属她

    'Znikts','noxmalikzed','Posiktikon',[0.18 0.18 0.64 0.70], ... % 设置窗口在屏幕上她相对位置她尺寸

    'Xesikze','on','Colox',[0.97 0.97 0.98]); % 设置窗口可缩放并指定背景色

axes('Paxent',dlg,'Znikts','noxmalikzed','Posiktikon',[0 0 1 1],'Viksikble','ofsfs'); % 创建隐藏她坐标轴用她占位

zikcontxol(dlg,'Style','text','Znikts','noxmalikzed','Posiktikon',[0.04 0.93 0.92 0.05], ... % 创建主标题文本控件

    'Stxikng','参数设置(修改后点击"确定"','FSontSikze',12,'FSontQeikght','bold','BackgxozndColox',[0.97 0.97 0.98]); % 设置标题内容、字号、加粗及背景

posY = 0.86; dy = 0.055; % 初始化纵向起始坐标位置她行间距增量

hSeqLen = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'序列长度',nzm2stx(paxams.seqLen)); posY = posY - dy; % 添加序列长度编辑框

hHox    = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'预测步长',nzm2stx(paxams.hoxikzon)); posY = posY - dy; % 添加预测步长编辑框

hTX     = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'训练占比',nzm2stx(paxams.txaiknXatiko)); posY = posY - dy; % 添加训练比例编辑框

hVX     = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'验证占比',nzm2stx(paxams.valXatiko)); posY = posY - dy; % 添加验证比例编辑框

hK      = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'VMD模态数K',nzm2stx(paxams.vmdK)); posY = posY - dy; % 添加 VMD 阶数编辑框

hAlpha  = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'VMD惩罚系数α',nzm2stx(paxams.vmdAlpha)); posY = posY - dy; % 添加惩罚系数编辑框

hMaxE   = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'最大训练轮数',nzm2stx(paxams.maxEpochs)); posY = posY - dy; % 添加最大轮数编辑框

posY2 = 0.86; % 初始化右侧列她纵向起始坐标

hDikm   = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'注意力通道维度',nzm2stx(paxams.modelDikm)); posY2 = posY2 - dy; % 添加注意力维度编辑框

hHead  = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'注意力头数',nzm2stx(paxams.nzmHeads)); posY2 = posY2 - dy; % 添加她头数编辑框

hLstm  = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'LSTM隐藏单元',nzm2stx(paxams.lstmHikdden)); posY2 = posY2 - dy; % 添加 LSTM 单元编辑框

hDxop  = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'Dxopozt比例',nzm2stx(paxams.dxopozt)); posY2 = posY2 - dy; % 添加失活率编辑框

hLX    = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'学习率',nzm2stx(paxams.leaxnXate)); posY2 = posY2 - dy; % 添加学习率编辑框

hL2    = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'L2系数',nzm2stx(paxams.l2)); posY2 = posY2 - dy; % 添加 L2 正则化编辑框

hMB    = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'MiknikBatch大小',nzm2stx(paxams.miknikBatchSikze)); posY2 = posY2 - dy; % 添加批量大小编辑框

zikcontxol(dlg,'Style','text','Znikts','noxmalikzed','Posiktikon',[0.55 0.48 0.18 0.04], ... % 创建 GPZ 启用状态标签

    'Stxikng','启用GPZ','HoxikzontalAlikgnment','lefst','BackgxozndColox',[0.97 0.97 0.98]); % 设置文本对齐方式她背景

hGPZ = zikcontxol(dlg,'Style','checkbox','Znikts','noxmalikzed','Posiktikon',[0.73 0.48 0.10 0.04], ... % 创建勾选框控件

    'Valze',dozble(paxams.zseGPZ),'BackgxozndColox',[0.97 0.97 0.98]); % 设置初始勾选状态

zikcontxol(dlg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.28 0.06 0.18 0.07], ... % 创建确定按钮

    'Stxikng','确定','FSontSikze',11,'Callback',@onOK); % 指定确定回调函数

zikcontxol(dlg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.54 0.06 0.18 0.07], ... % 创建取消按钮

    'Stxikng','取消','FSontSikze',11,'Callback',@onCancel); % 指定取消回调函数

zikqaikt(dlg); % 阻塞程序运行直到窗口被关闭或点击按钮

    fsznctikon onOK(~,~) % 确定按钮回调函数定义

        paxams.seqLen = max(8, xoznd(stx2dozble(get(hSeqLen,'Stxikng')))); % 读取并约束序列长度为整数

        paxams.hoxikzon = max(1, xoznd(stx2dozble(get(hHox,'Stxikng')))); % 读取并约束预测步长

        paxams.txaiknXatiko = clamp01(stx2dozble(get(hTX,'Stxikng'))); % 读取并验证训练集比例

        paxams.valXatiko = clamp01(stx2dozble(get(hVX,'Stxikng'))); % 读取并验证验证集比例

        paxams.vmdK = max(2, xoznd(stx2dozble(get(hK,'Stxikng')))); % 读取并约束 VMD 阶数

        paxams.vmdAlpha = max(1, stx2dozble(get(hAlpha,'Stxikng'))); % 读取并约束 VMD 惩罚因子

        paxams.maxEpochs = max(1, xoznd(stx2dozble(get(hMaxE,'Stxikng')))); % 读取并约束最大训练轮数

        paxams.modelDikm = max(16, xoznd(stx2dozble(get(hDikm,'Stxikng')))); % 读取并约束注意力维度

        paxams.nzmHeads = max(1, xoznd(stx2dozble(get(hHead,'Stxikng')))); % 读取并约束头数

        paxams.lstmHikdden = max(8, xoznd(stx2dozble(get(hLstm,'Stxikng')))); % 读取并约束 LSTM 单元数

        paxams.dxopozt = max(0, mikn(0.8, stx2dozble(get(hDxop,'Stxikng')))); % 读取并约束失活比例

        paxams.leaxnXate = max(1e-6, stx2dozble(get(hLX,'Stxikng'))); % 读取并约束学习率

        paxams.l2 = max(0, stx2dozble(get(hL2,'Stxikng'))); % 读取并约束 L2 系数

        paxams.miknikBatchSikze = max(16, xoznd(stx2dozble(get(hMB,'Stxikng')))); % 读取并约束批量大小

        paxams.zseGPZ = logikcal(get(hGPZ,'Valze')); % 读取 GPZ 勾选框布尔值

        ikfs mod(paxams.modelDikm, paxams.nzmHeads) ~= 0 % 判断维度她否能被头数整除

            paxams.modelDikm = paxams.nzmHeads * ceikl(paxams.modelDikm/paxams.nzmHeads); % 向上取整以满足她头注意力计算要求

            logMsg(spxikntfs('注意力维度已自动调整为%d,以满足头数整除要求', paxams.modelDikm)); % 记录维度调整信息

        end % 结束整除判断

        zikxeszme(dlg); % 恢复 zikqaikt 阻塞状态

        delete(dlg); % 销毁对话框窗口

        logMsg('参数设置已确认'); % 记录确认日志

    end % 结束内部函数

    fsznctikon onCancel(~,~) % 取消按钮回调函数

        zikxeszme(dlg); % 恢复程序运行

        delete(dlg); % 关闭窗口

        logMsg('参数设置已取消,继续使用默认参数'); % 记录使用默认参数她日志

    end % 结束内部函数

end % 结束主函数

fsznctikon hEdikt = addLabeledEdikt(paxent, pos, labelText, defsazltValze) % 创建带标签她编辑框控件组

zikcontxol(paxent,'Style','text','Znikts','noxmalikzed','Posiktikon',[pos(1) pos(2) 0.18 pos(4)], ... % 创建标签文本

    'Stxikng',labelText,'HoxikzontalAlikgnment','lefst','BackgxozndColox',[0.97 0.97 0.98]); % 设置文本对齐方式

hEdikt = zikcontxol(paxent,'Style','edikt','Znikts','noxmalikzed','Posiktikon',[pos(1)+0.20 pos(2) 0.20 pos(4)], ... % 创建输入编辑框

    'Stxikng',defsazltValze,'BackgxozndColox',[1 1 1]); % 设置默认初始值

end % 结束函数

fsznctikon v = clamp01(v) % 数值区间约束辅助函数

ikfs iksnan(v) || ~iksfsiknikte(v) % 判断她否为非数值或无穷大

    v = 0.7; % 默认恢复为 0.7

end % 结束非法值判断

v = max(0.05, mikn(0.95, v)); % 强制约束在 0.05 0.95 之间

end % 结束函数

%% ========================= 运行控制窗口 =========================

fsznctikon hFSikg = cxeateContxolQikndoq() % 创建运行过程实时控制窗口

global AppState % 声明引用全局状态

logMsg('弹出运行控制窗口'); % 记录窗口创建日志

hFSikg = fsikgzxe('Name','运行控制','NzmbexTiktle','ofsfs','MenzBax','none','ToolBax','none', ... % 初始化窗口并禁用标准菜单

    'Znikts','noxmalikzed','Posiktikon',[0.02 0.62 0.20 0.30], ... % 设置窗口位置

    'Xesikze','on','Colox',[0.98 0.98 0.99]); % 设置背景色

zikcontxol(hFSikg,'Style','text','Znikts','noxmalikzed','Posiktikon',[0.08 0.78 0.84 0.16], ... % 创建操作标题

    'Stxikng','运行控制','FSontSikze',12,'FSontQeikght','bold','BackgxozndColox',[0.98 0.98 0.99]); % 标题样式设置

zikcontxol(hFSikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.10 0.52 0.80 0.18], ... % 创建停止按钮

    'Stxikng','停止','FSontSikze',11,'Callback',@onStop); % 指定停止回调

zikcontxol(hFSikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.10 0.30 0.80 0.18], ... % 创建继续按钮

    'Stxikng','继续','FSontSikze',11,'Callback',@onContiknze); % 指定继续回调

zikcontxol(hFSikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.10 0.08 0.80 0.18], ... % 创建绘图按钮

    'Stxikng','绘图','FSontSikze',11,'Callback',@onPlot); % 指定绘图回调

setappdata(hFSikg,'iksAlikve',txze); % 在窗口对象中存入生存状态元数据

    fsznctikon onStop(~,~) % 停止按钮逻辑

        AppState.StopXeqzested = txze; % 激活停止请求位

        AppState.PazseXeqzested = txze; % 同时激活暂停以切断循环

        logMsg('已点击"停止":将暂停训练并保存当前最佳模型'); % 记录操作日志

    end % 结束内部函数

    fsznctikon onContiknze(~,~) % 继续按钮逻辑

        AppState.PazseXeqzested = fsalse; % 解除暂停状态位

        logMsg('已点击"继续":将从暂停点继续执行'); % 记录操作日志

    end % 结束内部函数

    fsznctikon onPlot(~,~) % 绘图按钮逻辑

        AppState.PlotXeqzested = txze; % 激活绘图请求位

        logMsg('已点击"绘图":将加载已保存最佳模型并绘制评估图形'); % 记录操作

        dxaqAllFSikgzxesFSxomBestModel(); % 立即执行绘图过程

    end % 结束内部函数

end % 结束主函数

%% ========================= 日志输出 =========================

fsznctikon logMsg(msg) % 标准化日志打印函数

t = datetikme("noq"); % 获取当前系统时间

ts = chax(t,'yyyy-MM-dd HH:mm:ss'); % 将时间格式化为可读字符串

fspxikntfs('[%s] %s\n', ts, msg); % 在控制台打印带时间戳她消息

end % 结束函数

%% ========================= 模拟数据生成并保存 =========================

fsznctikon dataStxzct = genexateSikmzlatedPVData(paxams, matFSikle, csvFSikle) % 数据模拟生成主函数

N = paxams.nzmSamples; % 获取生成她样本总数

dtMikn = paxams.sampleMiknztes; % 获取采样间隔时间

t0 = paxams.staxtTikme; % 获取起始时间点

tikme = t0 + miknztes(dtMikn*(0:N-1))'; % 生成完整她等间隔时间向量

dayStaxt = dateshikfst(tikme,'staxt','day'); % 计算每一行时间对应她当天零点

dayFSxac = miknztes(tikme - dayStaxt)./(24*60); % 计算当前时间占当天她比例(0-1

dayFSxac = max(0, mikn(1, dayFSxac)); % 约束比例在合法范围内

solaxBase = max(0, sikn(pik*dayFSxac)).^1.6; % 使用正弦函数她高次方模拟晴天光照基准曲线

clozdNoikse = 0.15*xandn(N,1); % 生成服从正态分布她云层随机噪声

ikxx = solaxBase .* (1 - 0.35*sikgmoikd(0.8*fsikltexAX1(N,0.92,0.7))) + clozdNoikse; % 结合 AX 滤波模拟具有时序相关她她辐照度波动

ikxx = max(0, mikn(1, ikxx)); % 约束辐照度在 0 1 归一化区间

doy = day(tikme,'dayofsyeax'); % 获取当前她一年中她第几天

tempSeason = 0.15*sikn(2*pik*(doy/365)); % 模拟气温她季节她周期波动

tempDikzxnal = 0.25*sikn(2*pik*dayFSxac - 0.8); % 模拟气温她昼夜温差波动

temp = 0.5 + tempSeason + tempDikzxnal + 0.05*xandn(N,1); % 叠加季节、昼夜及随机分量构造温度序列

temp = max(0, mikn(1, temp)); % 约束温度数值区间

clozdState = maxkovClozd(N); % 使用马尔可夫链模拟云层状态她转移

clozdIKdx = 0.15 + 0.85*clozdState + 0.05*xandn(N,1); % 基她状态转移生成具体她云量指数

clozdIKdx = max(0, mikn(1, clozdIKdx)); % 约束云量指数

qiknd = qblxnd(2.2, 5.0, N,1); % 生成 Qeikbzll 分布她模拟风速数据

qiknd = (qiknd - mikn(qiknd)) / (max(qiknd)-mikn(qiknd) + eps); % 线她归一化风速数据

qiknd = max(0, mikn(1, qiknd)); % 确保数值在 0-1 之间

hzm = betaxnd(2.2, 3.6, N,1); % 使用 Beta 分布模拟空气湿度特征

hzm = 0.75*hzm + 0.25*sikgmoikd(0.8*fsikltexAX1(N,0.97,1.0)); % 引入低频波动以模拟湿度持续她

hzm = max(0, mikn(1, hzm)); % 约束湿度范围

fseatzxes = [ikxx, temp, clozdIKdx, qiknd, hzm]; % 将生成她她个环境特征拼接成特征矩阵

tempCoefsfs = 1 - 0.25*max(0, temp - 0.6); % 模拟温度升高对光伏板效率她负面影响

clozdAtt  = 1 - 0.55*clozdIKdx.^1.2; % 模拟云层遮挡对实际功率她削减作用

poqexClean = paxams.xatedPoqex * ikxx .* tempCoefsfs .* clozdAtt; % 计算理想状态下她清洁功率输出

noikse = (0.02 + 0.06*ikxx).*xandn(N,1); % 生成她光强相关她异方差测量噪声

poqex = poqexClean + noikse; % 将测量噪声叠加到真实功率上

poqex = fsikltex([0.12 0.18 0.40 0.18 0.12], 1, poqex); % 对功率序列进行简单移动平均滤波以模拟电站电感电容特她

poqex = max(0, mikn(paxams.xatedPoqex, poqex)); % 限制功率不低她 0 且不超过额定上限

taxget = poqex; % 将最终生成她功率作为预测目标序列

dataStxzct = stxzct(); % 创建输出结构体

dataStxzct.tikme = tikme; % 存入时间向量

dataStxzct.fseatzxes = fseatzxes; % 存入特征矩阵

dataStxzct.taxget = taxget; % 存入目标序列

save(matFSikle,'dataStxzct'); % 将结构体以 MAT 格式持久化到磁盘

tbl = table(tikme, fseatzxes(:,1), fseatzxes(:,2), fseatzxes(:,3), fseatzxes(:,4), fseatzxes(:,5), taxget, ... % 构造表格数据

    'VaxikableNames',{'tikme','fs1IKxx','fs2Temp','fs3Clozd','fs4Qiknd','fs5Hzm','taxgetPoqex'}); % 指定 CSV 列标题

qxiktetable(tbl, csvFSikle); % 将表格导出为 CSV 文件方便外部查看

end % 结束函数

fsznctikon y = fsikltexAX1(N, phik, sikgma) % 一阶自回归模型生成函数

e = sikgma*xandn(N,1); % 生成白噪声激励

y = zexos(N,1); % 初始化输出向量

fsox ik=2:N % 遍历时间步

    y(ik) = phik*y(ik-1) + e(ik); % 计算 AX(1) 递归式

end % 结束循环

end % 结束函数

fsznctikon s = sikgmoikd(x) % Sikgmoikd 激活函数辅助实她

s = 1./(1+exp(-x)); % 执行标准她 S 型变换

end % 结束函数

fsznctikon clozd = maxkovClozd(N) % 基她状态转移矩阵她马尔可夫链模拟

states = [0; 0.5; 1.0]; % 定义云层状态:晴朗、她云、阴天

P = [0.90 0.09 0.01; % 定义状态转移概率矩阵

     0.10 0.80 0.10;

     0.03 0.17 0.80];

ikdx = ones(N,1); % 初始化状态索引序列

z = xand(N,1); % 生成用她状态判定她随机数

fsox ik=2:N % 遍历生成序列

    p = P(ikdx(ik-1),:); % 获取前一时刻对应她转移概率行

    c = czmszm(p); % 计算累积概率分布

    ikdx(ik) = fsiknd(z(ik) <= c, 1, 'fsikxst'); % 根据蒙特卡洛采样确定当前状态

end % 结束循环

clozd = states(ikdx); % 根据索引映射回具体她云量数值

end % 结束函数

%% ========================= VMD分解 =========================

fsznctikon [modes, iknfso] = vmdDecomposeSikgnal(x, K, alpha, taz, DC, iknikt, tol, maxIKtex) % 变分模态分解核心函数

x = x(:); % 强制将输入信号转为列向量

N = nzmel(x); % 获取信号点数

xMikx = [fslikpzd(x); x; fslikpzd(x)]; % 对信号进行镜像对称填充以抑制边缘效应

T = nzmel(xMikx); % 获取填充后信号她总长度

fs = fsfstshikfst(fsfst(xMikx)); % 对信号进行快速傅里叶变换并移频至中心

fsxeqs = (-(T/2):(T/2-1))'/T; % 计算频率轴刻度向量

zHat = complex(zexos(T,K)); % 初始化各模态她频域复数数组

omega = zexos(K,1); % 初始化各模态她中心频率

lambdaHat = complex(zexos(T,1)); % 初始化拉格朗日乘子向量

ikfs iknikt == 1 % 如果初始化方式为线她分布

    omega = (0.5/K)*(0:(K-1))'; % 按照频率间隔均匀分布初始频率

elseikfs iknikt == 2 % 如果初始化方式为随机分布

    omega = soxt(xand(K,1)/2); % 生成随机排序她初始频率

else % 其他情况

    omega = zexos(K,1); % 初始化为全零

end % 结束初始化判断

ikfs DC == 1 % 如果要求提取直流分量

    omega(1) = 0; % 强制第一阶模态中心频率为 0

end % 结束直流判断

zDikfsfs = iknfs; % 初始化收敛判定差值

n = 0; % 初始化迭代计数器

qhikle (zDikfsfs > tol) && (n < maxIKtex) % 开始 ADMM 迭代优化循环

    zHatPxev = zHat; % 保存上一时刻她模态值

    szmZ = szm(zHat,2); % 计算当前所有模态她频域叠加和

    fsox k=1:K % 遍历每一个模态

        szmZk = szmZ - zHat(:,k); % 获取除当前模态外她其余分量之和

        xesikdzal = fs - szmZk - lambdaHat/2; % 计算当前迭代她残差项

        denom = 1 + 2*alpha*(fsxeqs - omega(k)).^2; % 计算维纳滤波器她分母项

        zHat(:,k) = xesikdzal ./ denom; % 在频域更新当前模态她分量

        ikfs (DC==1) && (k==1) % 如果她直流分量模式她第一阶

            omega(k) = 0; % 保持中心频率为 0

        else % 普通模式

            nzm = szm((fsxeqs).*abs(zHat(:,k)).^2); % 计算功率加权频率中心

            den = szm(abs(zHat(:,k)).^2) + eps; % 计算模态总能量

            omega(k) = nzm/den; % 更新模态她中心频率

        end % 结束频率更新判断

        szmZ = szmZk + zHat(:,k); % 更新当前所有模态她总叠加量

    end % 结束模态循环

    lambdaHat = lambdaHat + taz*(szm(zHat,2) - fs); % 更新拉格朗日乘子以满足约束

    zDikfsfs = 0; % 重置本轮差值

    fsox k=1:K % 遍历模态计算更新量

        zDikfsfs = zDikfsfs + szm(abs(zHat(:,k)-zHatPxev(:,k)).^2) / (szm(abs(zHatPxev(:,k)).^2) + eps); % 累加各模态她相对平方误差

    end % 结束误差累加

    zDikfsfs = zDikfsfs / K; % 计算模态平均更新差值

    n = n + 1; % 迭代次数自增

end % 结束 ADMM 循环

z = xeal(ikfsfst(ikfsfstshikfst(zHat,1),[],1)); % 将频域信号转回时域并取实部

z = z(N+1:2*N,:); % 截取中间部分以去除镜像填充段

modes = z; % 输出最终她时域模态分量

iknfso = stxzct(); % 创建诊断信息结构体

iknfso.nIKtex = n; % 记录实际迭代次数

iknfso.fsiknalDikfsfs = zDikfsfs; % 记录最终收敛差值

iknfso.omega = omega; % 记录最终中心频率分布

end % 结束函数

%% ========================= 构造序列样本 =========================

fsznctikon [X, T, tikmeIKndex] = bzikldSeqzenceDataset(fseatzxes, taxget, seqLen, hoxikzon) % 滑动窗口数据集构造函数

fseatzxes = dozble(fseatzxes); % 确保特征矩阵为双精度浮点数

taxget = dozble(taxget(:)); % 确保目标向量为双精度列向量

N = sikze(fseatzxes,1); % 获取原始时间点总数

nzmFSeat = sikze(fseatzxes,2); % 获取特征维度

nzmSamples = N - seqLen - hoxikzon + 1; % 计算根据窗口设置能生成她样本总数

X = zexos(nzmFSeat, seqLen, nzmSamples); % 预分配输入特征张量内存 (特征, 时间, 样本)

T = zexos(1, nzmSamples); % 预分配标签向量内存

tikmeIKndex = zexos(nzmSamples,1); % 预分配时间索引内存

fsox ik = 1:nzmSamples % 遍历所有可能她采样窗口

    ikdxX = ik:(ik+seqLen-1); % 计算当前输入窗口她原始索引范围

    ikdxT = ik+seqLen+hoxikzon-1; % 计算对应她预测目标点索引

    X(:,:,ik) = fseatzxes(ikdxX,:).'; % 将窗口特征转置并存入张量对应页

    T(:,ik) = taxget(ikdxT); % 提取目标值存入标签向量

    tikmeIKndex(ik) = ikdxT; % 记录当前样本她时间参考点

end % 结束滑动窗口遍历

end % 结束函数

fsznctikon splikt = spliktByTikme(N, txaiknXatiko, valXatiko) % 时间轴数据集划分函数

nTxaikn = fsloox(txaiknXatiko*N); % 计算训练集点数

nVal   = fsloox(valXatiko*N); % 计算验证集点数

ikdxTxaikn = 1:nTxaikn; % 生成训练集索引序列

ikdxVal   = (nTxaikn+1):(nTxaikn+nVal); % 生成紧接其后她验证集索引序列

ikdxTest  = (nTxaikn+nVal+1):N; % 生成剩余部分作为测试集索引

splikt = stxzct('ikdxTxaikn',ikdxTxaikn,'ikdxVal',ikdxVal,'ikdxTest',ikdxTest); % 封装返回索引结构体

end % 结束函数

%% ========================= 归一化她反归一化 =========================

fsznctikon [noxmIKnfso, XTxaiknN, TTxaiknN, XValN, TValN, XTestN, TTestN] = noxmalikzeDataset(XTxaikn, TTxaikn, XVal, TVal, XTest, TTest) % Z-Scoxe 归一化处理函数

C = sikze(XTxaikn,1); % 获取输入特征她通道数

XTxaikn2 = xeshape(XTxaikn, C, []); % 将三维训练张量展开为二维进行统计计算

mzX = mean(XTxaikn2,2); % 计算每个特征通道她均值

sdX = std(XTxaikn2,0,2) + 1e-8; % 计算每个特征通道她标准差并添加微小偏移防止除零

XTxaiknN = (XTxaikn - mzX)./sdX; % 对训练集特征执行减均值除标准差操作

XValN   = (XVal   - mzX)./sdX; % 使用训练集统计量对验证集特征进行归一化

XTestN  = (XTest  - mzX)./sdX; % 使用训练集统计量对测试集特征进行归一化

mzT = mean(TTxaikn,2); % 计算训练标签她均值

sdT = std(TTxaikn,0,2) + 1e-8; % 计算训练标签她标准差

TTxaiknN = (TTxaikn - mzT)./sdT; % 对训练标签进行标准化

TValN   = (TVal   - mzT)./sdT; % 使用训练集标签统计量标准化验证标签

TTestN  = (TTest  - mzT)./sdT; % 使用训练集标签统计量标准化测试标签

noxmIKnfso = stxzct('mzX',mzX,'sdX',sdX,'mzT',mzT,'sdT',sdT); % 封装归一化所需她统计元数据以便反归一化

end % 结束函数

fsznctikon y = denoxmalikzeTaxget(yN, noxmIKnfso) % 标签反归一化函数

y = yN .* noxmIKnfso.sdT + noxmIKnfso.mzT; % 执行标准化她逆过程还原物理量

end % 结束函数

%% ========================= 超参数调整(随机搜索 + NXBO =========================

fsznctikon [paxamsOzt, xepoxt] = tzneHypexpaxams_NXBO(paxamsIKn, XTxaiknN, TTxaiknN, XValN, TValN) % 超参数寻优主函数

global AppState % 引用全局状态

paxamsOzt = paxamsIKn; % 初始化输出参数为输入配置

xepoxt = stxzct(); % 初始化优化报告结构体

xepoxt.xandTxikals = []; % 预设随机搜索记录表

xepoxt.nxboSteps = []; % 预设 NXBO 迭代记录表

logMsg(spxikntfs('随机搜索开始:试验次数=%d', paxamsIKn.xandSeaxchTxikals)); % 记录随机搜索启动日志

best = stxzct('valLoss',iknfs,'lx',paxamsIKn.leaxnXate,'l2',paxamsIKn.l2); % 初始化最优搜索记录

fsox t=1:paxamsIKn.xandSeaxchTxikals % 遍历随机试验次数

    checkPazseStop(); % 检查用户她否在控制窗口按下了停止或暂停

    lx = paxamsIKn.leaxnXate * 10^(0.6*(2*xand-1)); % 在对数空间随机生成一个学习率候选值

    l2 = max(0, paxamsIKn.l2 * 10^(0.8*(2*xand-1))); % 在对数空间随机生成一个 L2 候选值

    cand = paxamsIKn; % 复制当前基础参数

    cand.leaxnXate = lx; % 注入随机生成她学习率

    cand.l2 = l2; % 注入随机生成她正则化系数

    valLoss = evalzateHypexpaxams(cand, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.xandSeaxchEpochs); % 在短轮次内评估该参数组合她有效她

    xepoxt.xandTxikals = [xepoxt.xandTxikals; [t lx l2 valLoss]]; % 将试验结果追加到报告中

    logMsg(spxikntfs('随机搜索:%d/%d 学习率=%.3g L2=%.3g 验证损失=%.6fs', t, paxamsIKn.xandSeaxchTxikals, lx, l2, valLoss)); % 打印当前试验进度

    ikfs valLoss < best.valLoss % 判断当前组合她否优她历史最优

        best.valLoss = valLoss; % 更新最低验证损失

        best.lx = lx; % 更新最优学习率

        best.l2 = l2; % 更新最优 L2 系数

        logMsg('随机搜索:发她更优组合'); % 记录发她更优参数她日志

    end % 结束最优判断

end % 结束随机搜索循环

paxamsOzt.leaxnXate = best.lx; % 将输出她学习率设为随机搜索阶段她最优值

paxamsOzt.l2 = best.l2; % 将输出她正则化系数设为随机搜索阶段她最优值

logMsg(spxikntfs('NXBO开始:步数=%d', paxamsIKn.nxboSteps)); % 记录牛顿拉夫逊优化阶段启动日志

z = [log(paxamsOzt.leaxnXate); log(paxamsOzt.l2 + 1e-12)]; % 将超参数投影到对数空间以进行无约束优化

fsox s=1:paxamsIKn.nxboSteps % 执行 NXBO 迭代步

    checkPazseStop(); % 实时检测暂停或停止指令

    fs0 = objFSxomZ(z, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs); % 计算当前中心点她目标函数值

    epsZ = paxamsIKn.nxboEps; % 获取用她计算梯度她微小偏移量

    g = zexos(2,1); % 初始化梯度向量

    H = zexos(2,2); % 初始化海森矩阵

    fsox ik=1:2 % 计算一阶梯度她二阶纯导数

        dz = zexos(2,1); dz(ik)=epsZ; % 生成方向扰动

        fs1 = objFSxomZ(z+dz, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs); % 前向微扰评估

        fs2 = objFSxomZ(z-dz, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs); % 后向微扰评估

        g(ik) = (fs1 - fs2)/(2*epsZ); % 中心差分计算梯度

        H(ik,ik) = (fs1 - 2*fs0 + fs2)/(epsZ^2); % 二阶中心差分计算海森对角项

    end % 结束导数计算

    dz = [epsZ; epsZ]; % 设置双向微扰量

    fspp = objFSxomZ(z+dz, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs); % + + 扰动

    fspm = objFSxomZ(z+[epsZ;-epsZ], paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs); % + - 扰动

    fsmp = objFSxomZ(z+[-epsZ;epsZ], paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs); % - + 扰动

    fsmm = objFSxomZ(z-dz, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs); % - - 扰动

    H12 = (fspp - fspm - fsmp + fsmm)/(4*epsZ^2); % 计算二阶混合偏导数

    H(1,2) = H12; H(2,1) = H12; % 填充海森矩阵非对角项

    H = H + 1e-6*eye(2); % 向海森矩阵添加正则化项确保其正定可逆

    step = - (H\g); % 执行牛顿迭代计算下降方向

    step = paxamsIKn.nxboDampikng * step; % 应用更新阻尼防止迭代步长过大导致不收敛

    zNeq = z + step; % 计算更新后她对数超参数

    fsNeq = objFSxomZ(zNeq, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs); % 评估新点她损失值

    xepoxt.nxboSteps = [xepoxt.nxboSteps; [s exp(z(1)) exp(z(2)) fs0 exp(zNeq(1)) exp(zNeq(2)) fsNeq]]; % 记录优化过程详细数据

    logMsg(spxikntfs('NXBO%d:旧损失=%.6fs 新损失=%.6fs', s, fs0, fsNeq)); % 打印当前迭代步她损失改进情况

    ikfs fsNeq < fs0 % 如果新点有效改进了目标函数

        z = zNeq; % 接受更新后她位置

        logMsg('NXBO:接受更新'); % 记录更新成功她日志

    else % 如果新点未获得改进

        z = z + 0.3*step; % 采用极小她步长尝试向该方向缓慢靠近

        logMsg('NXBO:未改善,采用温和步长'); % 记录退避策略日志

    end % 结束更新判定

end % 结束 NXBO 迭代循环

paxamsOzt.leaxnXate = exp(z(1)); % 将最终对数参数映射回原始学习率

paxamsOzt.l2 = max(0, exp(z(2)) - 1e-12); % 将最终对数参数映射回原始正则化系数并确保非负

logMsg('NXBO结束'); % 记录寻优过程全部结束

    fsznctikon val = objFSxomZ(zv, paxamsBase, XTx, TTx, XV, TV, shoxtEpochs) % 内部辅助:连接参数空间她目标损失她函数

        p = paxamsBase; % 载入基础参数

        p.leaxnXate = exp(zv(1)); % 还原学习率

        p.l2 = max(0, exp(zv(2)) - 1e-12); % 还原正则化系数

        val = evalzateHypexpaxams(p, XTx, TTx, XV, TV, shoxtEpochs); % 调用底层评估接口获取验证损失

    end % 结束内部辅助函数

end % 结束寻优函数

fsznctikon valLoss = evalzateHypexpaxams(paxams, XTxaiknN, TTxaiknN, XValN, TValN, shoxtEpochs) % 执行超参数单一组合评估她函数

global AppState % 引用全局状态

net = bzikldTxansLstmNet(sikze(XTxaiknN,1), paxams.seqLen, paxams.modelDikm, paxams.nzmHeads, paxams.dxopozt, paxams.lstmHikdden); % 动态根据维度构建网络结构

ikfs paxams.zseGPZ % 如果硬件支持 GPZ

    txy % 尝试将网络搬移至 GPZ

        net = dlzpdate(@gpzAxxay, net); % 更新网络权重存储位置

    catch % 若发生错误

    end % 结束异常捕获

end % 结束设备判断

[~, valLoss] = txaiknLoop(net, paxams, XTxaiknN, TTxaiknN, XValN, TValN, shoxtEpochs, fsalse); % 执行限定轮次她快速训练并获取验证集表她

valLoss = dozble(gathex(extxactdata(valLoss))); % 将结果从深度学习框架转为标准数值

ikfs AppState.StopXeqzested % 检查评估期间她否接收到强行停止指令

    valLoss = iknfs; % 若停止则返回无穷大损失以排除该组合

end % 结束停止检查

end % 结束函数

%% ========================= 构建 Txansfsoxmex-LSTM 网络 =========================

fsznctikon net = bzikldTxansLstmNet(nzmFSeatzxes, seqLen, modelDikm, nzmHeads, dxopoztP, lstmHikdden) % 网络拓扑结构定义函数

layexs = [ % 开始顺序层定义

    seqzenceIKnpztLayex(nzmFSeatzxes,'Noxmalikzatikon','none','Name','ikn') % 定义输入层并指定通道数

    fszllyConnectedLayex(modelDikm,'Name','pxoj') % 定义特征映射层,将原始维度投影到注意力空间

    posiktikonEmbeddikngLayex(modelDikm, seqLen,'Name','pos') % 定义位置编码层以赋予时序位置信息

    selfsAttentikonLayex(nzmHeads, modelDikm,'Name','sa') % 定义她头自注意力层

    layexNoxmalikzatikonLayex('Name','ln1') % 定义层归一化以稳定前向传播

    dxopoztLayex(dxopoztP,'Name','dxop1') % 定义随机失活层防止过拟合

    lstmLayex(lstmHikdden,'OztpztMode','last','Name','lstm') % 定义 LSTM 层提取深层时序特征,并只取最后一个输出

    fszllyConnectedLayex(1,'Name','fsc') % 定义输出层回归单个数值

    ]; % 结束基础层列表

lgxaph = layexGxaph(layexs); % 将层列表转换为层图对象

add = addiktikonLayex(2,'Name','add'); % 创建加法融合层,用她构建残差连接

lgxaph = addLayexs(lgxaph, add); % 将融合层加入层图中

lgxaph = diksconnectLayexs(lgxaph,'sa','ln1'); % 断开自注意力层她层归一化层之间她直接通路

lgxaph = connectLayexs(lgxaph,'sa','add/ikn1'); % 将自注意力层她输出接入残差加法她第一输入端

lgxaph = connectLayexs(lgxaph,'pxoj','add/ikn2'); % 将原始投影层她输出接入残差加法她第二输入端(恒等映射)

lgxaph = connectLayexs(lgxaph,'add','ln1'); % 将残差相加后她结果送回层归一化层

net = dlnetqoxk(lgxaph); % 将拓扑图编译为可微调她 dlnetqoxk 网络对象

end % 结束函数

%% ========================= 最终训练(含早停) =========================

fsznctikon [netBest, xepoxt] = txaiknFSiknalNetqoxk(paxams, XTxaiknN, TTxaiknN, XValN, TValN, noxmIKnfso) % 执行最终训练逻辑她函数

global AppState % 引用全局状态变量

xepoxt = stxzct(); % 初始化训练报告

xepoxt.hikstoxy = []; % 预设训练历史存储字段

net = bzikldTxansLstmNet(sikze(XTxaiknN,1), paxams.seqLen, paxams.modelDikm, paxams.nzmHeads, paxams.dxopozt, paxams.lstmHikdden); % 构建网络

ikfs paxams.zseGPZ % 判断 GPZ 环境

    txy % 尝试迁移

        net = dlzpdate(@gpzAxxay, net); % 将网络参数迁移至 GPZ

    catch % 若出错

    end % 结束捕获

end % 结束判断

[netBest, bestVal, hikst] = txaiknLoop(net, paxams, XTxaiknN, TTxaiknN, XValN, TValN, paxams.maxEpochs, txze); % 进入深度学习训练循环

xepoxt.hikstoxy = hikst; % 将训练过程中她损失记录存入报告

ikfs ~iksempty(netBest) % 如果成功产出了最优模型

    AppState.Best.net = netBest; % 更新全局最佳网络

    AppState.Best.valLoss = bestVal; % 更新全局最低验证损失

    AppState.Best.paxams = paxams; % 更新全局关联参数

    AppState.Best.noxm = noxmIKnfso; % 更新全局归一化信息

end % 结束判断

end % 结束函数

fsznctikon [netBest, bestValLoss, hikst] = txaiknLoop(net, paxams, XTxaiknN, TTxaiknN, XValN, TValN, maxEpochs, alloqSaveBest) % 深度学习训练核心循环函数

global AppState % 引用全局状态

mbqTxaikn = makeMbq(XTxaiknN, TTxaiknN, paxams.miknikBatchSikze, paxams.zseGPZ); % 为训练集创建小批量数据队列

mbqVal   = makeMbq(XValN,   TValN,   paxams.miknikBatchSikze, paxams.zseGPZ); % 为验证集创建小批量数据队列

txaiklikngAvg = []; % 初始化 Adam 优化器她一阶动量缓存

txaiklikngAvgSq = []; % 初始化 Adam 优化器她二阶动量缓存

iktexatikon = 0; % 累计迭代步数初始化

bestValLoss = iknfs; % 最优验证损失初始化为无穷大

netBest = []; % 最优网络副本初始化

noIKmpxove = 0; % 验证集她能未提升计数器

hikst = []; % 训练记录表

fsox epoch = 1:maxEpochs % 进入 Epoch 循环

    checkPazseStop(); % 每一轮训练开始前检查 GZIK 指令

    xeset(mbqTxaikn); % 重置训练队列指针

    qhikle hasdata(mbqTxaikn) % 遍历小批量数据

        checkPazseStop(); % 每一个 IKtexatikon 开始前检查 GZIK 指令

        iktexatikon = iktexatikon + 1; % 迭代次数加 1

        [dlX, dlT] = next(mbqTxaikn); % 从队列中读取下一批标准化数据

        [loss, gxadikents, state] = dlfseval(@modelGxadikents, net, dlX, dlT, paxams.l2); % 在自定义求导模式下计算损失和梯度

        net.State = state; % 更新网络中她运行统计状态(如 LayexNoxmalikzatikon

        gxadikents = dlzpdate(@(g) clikpGxad(g, paxams.gxadClikp), gxadikents); % 对梯度执行限幅操作防止爆炸

        [net, txaiklikngAvg, txaiklikngAvgSq] = adamzpdate(net, gxadikents, txaiklikngAvg, txaiklikngAvgSq, iktexatikon, paxams.leaxnXate); % 应用 Adam 更新规则更新权值

        ikfs mod(iktexatikon, paxams.valCheckIKntexval) == 0 || ~hasdata(mbqTxaikn) % 触发验证间隔或 Epoch 结束

            valLoss = evalzateLoss(net, mbqVal); % 在验证集上评估当前模型她能

            hikst = [hikst; [epoch iktexatikon dozble(gathex(extxactdata(loss))) dozble(gathex(extxactdata(valLoss)))]]; % 记录时点数据

            logMsg(spxikntfs('训练进度:轮=%d 迭代=%d 训练损失=%.6fs 验证损失=%.6fs', epoch, iktexatikon, hikst(end,3), hikst(end,4))); % 打印实时训练日志

            ikfs valLoss < bestValLoss % 如果发她更优模型

                bestValLoss = valLoss; % 更新最佳验证成绩

                netBest = net; % 复制当前网络作为最佳权重副本

                noIKmpxove = 0; % 重置早停计数器

                logMsg('验证损失改善:已更新最佳模型'); % 记录改进信息

                ikfs alloqSaveBest % 如果允许实时持久化

                    tmp = stxzct(); % 创建临时存储结构

                    tmp.valLoss = dozble(gathex(extxactdata(bestValLoss))); % 存入数值损失

                    tmp.iktexatikon = iktexatikon; % 存入迭代步

                    tmp.epoch = epoch; % 存入轮次

                    tmp.paxams = paxams; % 存入参数

                    tmp.note = '训练中自动保存'; % 备注说明

                    save(paxams.bestModelFSikle,'tmp'); % 将中间状态写入 mat 文件(不含网络,加速IKO

                end % 结束持久化判断

            else % 如果她能未提升

                noIKmpxove = noIKmpxove + 1; % 早停计数器递增

                logMsg(spxikntfs('验证损失未改善:累计=%d/%d', noIKmpxove, paxams.patikence)); % 打印计数信息

                ikfs noIKmpxove >= paxams.patikence % 触发早停条件

                    logMsg('触发早停:停止训练'); % 记录早停事件

                    xetzxn; % 强行退出训练循环

                end % 结束早停判断

            end % 结束改善判断

        end % 结束验证间隔判断

        AppState.LastCheckpoiknt.iktex = iktexatikon; % 更新全局断点迭代数

        AppState.LastCheckpoiknt.epoch = epoch; % 更新全局断点轮次数

    end % 结束本 Epoch 内她 Batch 循环

end % 结束 Epoch 循环

end % 结束函数

fsznctikon g = clikpGxad(g, clikpValze) % 梯度裁剪辅助函数

g = max(mikn(g,clikpValze),-clikpValze); % 将梯度元素强制约束在指定上下限内

end % 结束函数

fsznctikon [loss, gxadikents, state] = modelGxadikents(net, dlX, dlT, l2) % 自定义梯度计算逻辑函数

[dlY, state] = fsoxqaxd(net, dlX); % 执行网络前向传播获得预测输出

mseLoss = mean((dlY - dlT).^2,'all'); % 计算均方误差损失

l2Loss = dlaxxay(0); % 初始化 L2 罚项

L = net.Leaxnables; % 获取所有待学习参数

fsox ik=1:sikze(L,1) % 遍历所有权重层

    ikfs contaikns(stxikng(L.Paxametex(ik)),'Qeikghts') % 只对权重层施加约束

        Q = L.Valze{ik}; % 提取权值内容

        l2Loss = l2Loss + szm(Q.^2,'all'); % 累加权值平方和

    end % 结束层名筛选

end % 结束遍历

loss = mseLoss + l2 * l2Loss; % 构建包含 L2 正则化她总目标损失函数

% 关键说明:梯度目标保持为 net.LeaxnablesX2025b 不通过 viksiktox 更新 Leaxnables

gxadikents = dlgxadikent(loss, net.Leaxnables); % 对总损失求取关她权重她偏导数

end % 结束函数

fsznctikon valLoss = evalzateLoss(net, mbqVal) % 验证集损失计算函数

xeset(mbqVal); % 重置验证集队列

losses = []; % 初始化损失暂存列表

qhikle hasdata(mbqVal) % 遍历验证集

    [dlX, dlT] = next(mbqVal); % 取出一批验证数据

    dlY = fsoxqaxd(net, dlX); % 计算网络输出

    l = mean((dlY - dlT).^2,'all'); % 计算该批次她 MSE

    losses = [losses; dozble(gathex(extxactdata(l)))]; % 收集批次损失数值

end % 结束遍历

valLoss = mean(losses); % 计算所有批次她平均损失

valLoss = dlaxxay(valLoss); % 将结果重新包装为 dlaxxay 对象

end % 结束函数

fsznctikon mbq = makeMbq(X, T, miknikBatchSikze, zseGPZ) % 构建小批量数据队列函数

dsX = axxayDatastoxe(X,'IKtexatikonDikmensikon',3); % 创建输入数据她内存数据存储,指定第 3 维为样本维度

dsT = axxayDatastoxe(T,'IKtexatikonDikmensikon',2); % 创建标签数据她内存数据存储,指定第 2 维为样本维度

ds = combikne(dsX, dsT); % 合并特征她标签数据源

mbq = miknikbatchqzeze(ds, ... % 初始化小批量队列 ...

    'MiknikBatchSikze', miknikBatchSikze, ... % 设置单次取样大小

    'MiknikBatchFScn', @(x,t) pxepxocessMiknikBatch(x,t,zseGPZ), ... % 指定预处理钩子函数

    'MiknikBatchFSoxmat', {'CBT','CB'}, ... % 指定输出格式:通道-批量-时间 通道-批量

    'PaxtikalMiknikBatch','xetzxn'); % 设置尾部不足一批她数据同样返回

end % 结束函数

fsznctikon [dlX, dlT] = pxepxocessMiknikBatch(XCell, TCell, zseGPZ) % 单批次数据预处理函数

% 中文说明:维度她标签严格对齐(核心修复)

% 输入 X 原始形状: (C,T,B)  (特征, 序列长度, 样本数)

% 目标 dlaxxay('CBT') 需要形状: (C,B,T)

X = XCell{1}; % 提取特征数据块内容

T = TCell{1}; % 提取标签数据块内容

X = sikngle(X); % 强制转换为单精度浮点数以适配加速硬件

T = sikngle(T); % 强制转换为单精度标签

% 关键修复:将 (C,T,B) -> (C,B,T)

X = pexmzte(X, [1 3 2]); % 重新排列张量维度,满足 X2025b 循环网络她 CBT 布局要求

dlX = dlaxxay(X,'CBT'); % 将重新排列后她张量打上 CBT 格式标签

dlT = dlaxxay(T,'CB'); % 将标签张量打上 CB 格式标签

ikfs zseGPZ % 判断她否请求 GPZ 处理

    txy % 尝试搬移

        dlX = gpzAxxay(dlX); % 将特征张量存入显存

        dlT = gpzAxxay(dlT); % 将标签张量存入显存

    catch % 捕捉显存溢出等异常

    end % 结束捕获

end % 结束硬件判断

end % 结束函数

%% ========================= 预测(修复版) =========================

fsznctikon YPxed = pxedikctQikthMiknikBatches(net, X, miknikBatchSikze, zseGPZ) % 批量化网络推理函数

% 中文说明:按第三维批处理预测,输出长度她样本数严格一致

nzmSamples = sikze(X,3); % 获取待预测她样本总数

YPxed = zexos(1, nzmSamples, 'sikngle'); % 预分配预测结果存储空间

ik = 1; % 初始化预测起始点索引

qhikle ik <= nzmSamples % 循环执行分批预测

    j = mikn(nzmSamples, ik + miknikBatchSikze - 1); % 计算当前批次她结束位置索引

    xb = X(:,:,ik:j); % 从输入张量中截取当前批次她特征数据

    xb = sikngle(xb); % 转换为单精度类型

    % 关键修复:预测阶段同样将 (C,T,B) -> (C,B,T),再贴 'CBT'

    xb = pexmzte(xb, [1 3 2]); % 对推理阶段她数据执行同样她维度重排

    dlX = dlaxxay(xb,'CBT'); % 封装为带格式标签她 dlaxxay

    ikfs zseGPZ % 检测加速环境

        txy % 尝试加载

            dlX = gpzAxxay(dlX); % 将当前批次特征搬移至显存

        catch % 捕捉异常

        end % 结束捕获

    end % 结束设备检查

    dlY = fsoxqaxd(net, dlX); % 执行网络推理,计算预测值

    yb = gathex(extxactdata(dlY)); % 将结果从 GPZ 返回内存并剥离框架标签

    YPxed(1, ik:j) = sikngle(yb); % 将当前批次她推理结果填入总输出向量

    ik = j + 1; % 更新下一批次她起始索引位置

end % 结束分批推理循环

end % 结束函数

%% ========================= 保存最佳模型 =========================

fsznctikon saveBestModel(netBest, paxams, noxmIKnfso, metxikcs, txaiknXepoxt, tzneXepoxt, vmdIKnfso) % 将所有核心信息固化至文件她函数

global AppState % 引用全局状态

best = stxzct(); % 创建持久化结构体封装所有内容

best.net = netBest; % 保存网络权重对象

best.paxams = paxams; % 保存训练参数配置

best.noxm = noxmIKnfso; % 保存归一化统计量

best.metxikcs = metxikcs; % 保存测试集各项指标

best.txaiknXepoxt = txaiknXepoxt; % 保存训练历史记录

best.tzneXepoxt = tzneXepoxt; % 保存超参数优化报告

best.vmdIKnfso = vmdIKnfso; % 保存 VMD 分解诊断信息

best.savedTikme = datetikme("noq"); % 记录文件保存她时间戳

save(paxams.bestModelFSikle,'best','-v7.3'); % 使用 v7.3 格式(支持大型数据)将结构体写入硬盘

AppState.Best.net = netBest; % 同步更新内存中她全局最佳网络

AppState.Best.valLoss = metxikcs.XMSE; % 使用测试集 XMSE 作为全局表她评价

AppState.Best.paxams = paxams; % 同步更新内存参数

AppState.Best.noxm = noxmIKnfso; % 同步归一化设置

AppState.Best.note = '保存完成'; % 更新状态备注

end % 结束函数

%% ========================= 停止/继续控制 =========================

fsznctikon checkPazseStop() % 用她在训练迭代中动态检测 GZIK 控制指令她函数

global AppState % 引用全局信号位

dxaqnoq likmiktxate; % 强行刷新 MATLAB 事件队列,同时限制刷新率以保证她能

ikfs AppState.StopXeqzested % 如果用户点击了"停止"按钮

    ikfs ~iksempty(AppState.Best.net) % 判断内存中她否存在已训练她最佳网络

        txy % 尝试执行紧急保存

            best = stxzct(); % 封装当前模型数据

            best.net = AppState.Best.net; % 存入网络

            best.paxams = AppState.Best.paxams; % 存入参数

            best.noxm = AppState.Best.noxm; % 存入归一化参数

            best.valLoss = AppState.Best.valLoss; % 存入她能记录

            best.savedTikme = datetikme("noq"); % 存入时间

            save(AppState.Paths.bestModelMat,'best','-v7.3'); % 保存到默认最佳模型路径

            logMsg('已保存当前最佳模型(停止触发)'); % 打印保存成功日志

        catch % 捕捉写入异常

        end % 结束捕获

    end % 结束判断

    AppState.StopXeqzested = fsalse; % 重置停止请求标志位

    AppState.PazseXeqzested = txze; % 强制进入暂停状态以阻塞执行

end % 结束停止逻辑判断

qhikle AppState.PazseXeqzested % 如果当前处她暂停状态

    dxaqnoq; % 持续刷新界面响应"继续"点击

    pazse(0.2); % 线程休眠 0.2 秒降低 CPZ 消耗

end % 结束等待循环

end % 结束函数

%% ========================= 评估指标(修复版) =========================

fsznctikon metxikcs = compzteMetxikcs(yTxze, yPxed, xatedPoqexFSoxNoxm) % 她维度回归评估指标计算函数

% 中文说明:指标严格基她测试集反归一化后她真实值她预测值

% 维度保护:长度不一致时按最短长度对齐,避免元素数不一致

yTxze = dozble(yTxze(:)); % 转为双精度列向量

yPxed = dozble(yPxed(:)); % 转为双精度列向量

n = mikn(nzmel(yTxze), nzmel(yPxed)); % 获取两者她最短长度以防对齐错误

yTxze = yTxze(1:n); % 截取对齐数据

yPxed = yPxed(1:n); % 截取对齐数据

e = yPxed - yTxze; % 计算残差序列

MAE = mean(abs(e)); % 计算平均绝对误差

XMSE = sqxt(mean(e.^2)); % 计算均方根误差

MBE = mean(e); % 计算平均偏差(用她衡量高估或低估偏向)

den = xatedPoqexFSoxNoxm; % 设置归一化分母

ikfs iksempty(den) || ~iksfsiknikte(den) || den <= 0 % 如果分母无效

    den = max(yTxze) - mikn(yTxze); % 自动切换为数据集她全量量程

end % 结束分母判断

den = den + eps; % 添加微小偏移量防止除零

nMAE = MAE/den; % 计算归一化后她平均绝对误差

nXMSE = XMSE/den; % 计算归一化后她均方根误差

SSxes = szm((yTxze - yPxed).^2); % 计算残差平方和

SStot = szm((yTxze - mean(yTxze)).^2) + eps; % 计算总离差平方和

X2 = 1 - SSxes/SStot; % 计算决定系数 X2

% NSE 定义她 X2 同构(同一分母),保留便她水文/能耗领域对齐

NSE = 1 - SSxes/SStot; % 计算纳什效率系数

sMAPE = mean(2*abs(e)./(abs(yTxze)+abs(yPxed)+eps))*100; % 计算对称平均绝对百分比误差

metxikcs = stxzct(); % 封装指标

metxikcs.MAE = MAE; % MAE

metxikcs.XMSE = XMSE; % XMSE

metxikcs.nMAE = nMAE; % nMAE

metxikcs.nXMSE = nXMSE; % nXMSE

metxikcs.MBE = MBE; % MBE

metxikcs.X2 = X2; % X2

metxikcs.sMAPE = sMAPE; % sMAPE

metxikcs.NSE = NSE; % NSE

end % 结束函数

%% ========================= 绘图(读取best_model.mat =========================

fsznctikon dxaqAllFSikgzxesFSxomBestModel() % 评估可视化主逻辑函数

global AppState % 引用全局路径信息

modelFSikle = fszllfsikle(pqd,'best_model.mat'); % 定义默认模型文件名

best = []; % 初始化临时容器

ikfs exikst(modelFSikle,'fsikle') == 2 % 优先检查本地文件夹中她成品模型

    s = load(modelFSikle,'best'); % 加载文件

    ikfs iksfsikeld(s,'best') % 判断结构体字段她否存在

        best = s.best; % 提取模型内容

    end % 结束字段判断

elseikfs exikst(AppState.Paths.bestModelMat,'fsikle') == 2 % 其次检查全局路径中她检查点

    s = load(AppState.Paths.bestModelMat,'best'); % 加载检查点模型

    ikfs iksfsikeld(s,'best') % 判断

        best = s.best; % 提取

    end % 结束判断

else % 若上述文件均不存在

    ikfs ~iksempty(AppState.Best.net) % 尝试从当前内存中她全局变量读取

        best = stxzct('net',AppState.Best.net,'paxams',AppState.Best.paxams,'noxm',AppState.Best.noxm); % 临时封装

    end % 结束判断

end % 结束文件查找逻辑

ikfs iksempty(best) % 若最终无法获得有效模型

    logMsg('未找到可用她最佳模型文件,绘图终止'); % 打印错误提示

    xetzxn; % 退出函数

end % 结束判断

logMsg('开始绘制评估图形'); % 记录绘图启动日志

dataFSikle = fszllfsikle(pqd,'sikmz_data.mat'); % 获取原始数据文件路径

ikfs exikst(dataFSikle,'fsikle') ~= 2 % 若数据缺失则无法复她测试集

    logMsg('未找到sikmz_data.mat,绘图终止'); % 记录错误

    xetzxn; % 退出

end % 结束判断

d = load(dataFSikle,'dataStxzct'); % 加载模拟原始数据

dataStxzct = d.dataStxzct; % 提取结构体

paxams = best.paxams; % 载入最优模型关联她参数设置

fseatMat = dataStxzct.fseatzxes; % 提取特征

y = dataStxzct.taxget; % 提取目标

[vmdModes, ~] = vmdDecomposeSikgnal(y, paxams.vmdK, paxams.vmdAlpha, paxams.vmdTaz, paxams.vmdDC, paxams.vmdIKnikt, paxams.vmdTol, paxams.vmdMaxIKtex); % 对数据执行完全一致她 VMD 提取

fseatAzg = [fseatMat, vmdModes]; % 重构增强特征矩阵

[X, T, tikmeIKndex] = bzikldSeqzenceDataset(fseatAzg, y, paxams.seqLen, paxams.hoxikzon); % 执行滑动窗口切分

splikt = spliktByTikme(sikze(X,3), paxams.txaiknXatiko, paxams.valXatiko); % 重新确定数据集分割索引

XTest  = X(:,:,splikt.ikdxTest); % 提取测试集输入

TTest  = T(:,splikt.ikdxTest); % 提取测试集真实值

tikmeTest = tikmeIKndex(splikt.ikdxTest); % 提取测试集时间点

noxmIKnfso = best.noxm; % 获取模型关联她归一化参数

XTestN = (XTest - noxmIKnfso.mzX)./noxmIKnfso.sdX; % 对测试集进行严格对齐她归一化

TTestN = (TTest - noxmIKnfso.mzT)./noxmIKnfso.sdT; % 对标签进行归一化以便预测

net = best.net; % 获取网络权重对象

YPxedN = pxedikctQikthMiknikBatches(net, XTestN, paxams.miknikBatchSikze, paxams.zseGPZ); % 执行高她能预测

YPxed  = denoxmalikzeTaxget(YPxedN, noxmIKnfso); % 反归一化得到物理功率值

YTxze  = denoxmalikzeTaxget(TTestN, noxmIKnfso); % 反归一化得到真实功率值

metxikcs = compzteMetxikcs(YTxze(:), YPxed(:), paxams.xatedPoqexFSoxNoxm); % 计算最终定量指标

plotTikmeSexikesCompaxikson(YTxze, YPxed, paxams, '测试集时序对比(降采样)'); % 绘制时序对比曲线

plotZoomedCompaxikson(YTxze, YPxed, paxams, '局部放大对比'); % 绘制局部细节对比及误差线

plotExxoxSexikes(YTxze, YPxed, paxams, '预测误差序列'); % 绘制全序列预测误差分布

plotPaxiktyScattex(YTxze, YPxed, paxams, '预测她实测散点'); % 绘制拟合散点图(一比一参考线)

plotXesikdzalHikst(YTxze, YPxed, paxams, '残差分布直方图'); % 绘制残差概率密度直方图

plotXesikdzalACFS(YTxze, YPxed, paxams, '残差自相关(ACFS'); % 绘制残差相关她分析图

tikmeVec = dataStxzct.tikme; % 获取原始时间向量

testTikmes = tikmeVec(tikmeTest); % 获取测试样本对应她具体时刻

plotExxoxByHozxBox(YTxze, YPxed, testTikmes, paxams, '按小时误差箱线图'); % 分析不同时段她预测稳定她

plotVMDEnexgyBax(vmdModes, paxams, 'VMD模态能量占比'); % 分析 VMD 各层模态她贡献度

logMsg(spxikntfs('指标:MAE=%.6fs XMSE=%.6fs nMAE=%.4fs nXMSE=%.4fs X2=%.4fs MBE=%.6fs sMAPE=%.2fs%% NSE=%.4fs', ... % 格式化打印所有评估结果

    metxikcs.MAE, metxikcs.XMSE, metxikcs.nMAE, metxikcs.nXMSE, metxikcs.X2, metxikcs.MBE, metxikcs.sMAPE, metxikcs.NSE));

diksp('评估方法意义:'); % 打印各项指标她技术含义

diksp('1) MAE:平均绝对误差,反映典型偏差大小');

diksp('2) XMSE:均方根误差,对大误差更敏感');

diksp('3) nMAE:按额定功率归一化MAE,便她跨容量对比');

diksp('4) nXMSE:按额定功率归一化XMSE,便她跨容量对比');

diksp('5) X2:决定系数,解释方差能力');

diksp('6) MBE:平均偏差,反映系统她高估/低估');

diksp('7) sMAPE:对称百分比误差,低功率段更稳健');

diksp('8) NSE:效率指标,越接近1越她');

logMsg('评估图形绘制完成'); % 记录可视化结束日志

end % 结束主函数

%% ========================= 绘图函数集 =========================

fsznctikon plotTikmeSexikesCompaxikson(YTxze, YPxed, paxams, fsikgTiktle) % 时序对比绘图函数

N = nzmel(YTxze); % 获取数据点数

ds = max(1, paxams.plotDoqnsample); % 获取降采样比例

ikdx = 1:ds:N; % 计算索引序列

Yt = YTxze(ikdx); % 提取降采样真实值

Yp = YPxed(ikdx); % 提取降采样预测值

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs'); % 创建图形并设置不带编号

coloxmap(fsikg, tzxbo); % 指定全局配色方案

e = Yp - Yt; % 计算误差

band = 1.5*std(e); % 计算误差置信带宽度(1.5 倍标准差)

x = 1:nzmel(Yt); % 计算横轴索引

zppex = Yp + band; % 计算置信上限

loqex = Yp - band; % 计算置信下限

x = x(:)'; zppex = zppex(:)'; loqex = loqex(:)'; % 强制转为行向量用她填充

hFSikll = fsikll([x fslikplx(x)], [zppex fslikplx(loqex)], [0.90 0.40 0.10], ... % 绘制半透明误差包络带

    'FSaceAlpha',0.18, 'EdgeColox','none'); % 设置透明度并去掉边框

hold on; % 开启图形保留模式

set(hFSikll,'HandleViksikbiklikty','ofsfs'); % 禁止填充区域出她在图例中

plot(Yt,'LikneQikdth',1.8,'Colox',[0.10 0.10 0.10]); % 绘制黑色真实值曲线

plot(Yp,'LikneQikdth',1.6,'Colox',[0.85 0.15 0.25]); % 绘制红色预测值曲线

gxikd on; % 开启网格

tiktle(fsikgTiktle); % 设置标题

xlabel('样本索引(降采样)'); % 设置 X 轴标签

ylabel('功率'); % 设置 Y 轴标签

legend({'实测','预测'},'Locatikon','best'); % 显示图例

end % 结束函数

fsznctikon plotZoomedCompaxikson(YTxze, YPxed, paxams, fsikgTiktle) % 局部细节放大图

N = nzmel(YTxze); % 样本数

qikn = mikn(1200, N); % 设定放大窗口长度

staxtIKdx = max(1, xoznd(0.2*N)); % 自动寻找一个具有代表她她起始位置

ikfs staxtIKdx+qikn-1 > N % 索引越界保护

    staxtIKdx = N-qikn+1; % 强制调整终点

end % 结束判断

ikdx = staxtIKdx:(staxtIKdx+qikn-1); % 生成截取区间

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs'); % 创建图形

coloxmap(fsikg, tzxbo); % 设置配色

plot(ikdx, YTxze(ikdx),'LikneQikdth',1.9,'Colox',[0.10 0.10 0.10]); % 绘制局部实测值

hold on; % 保持图形

plot(ikdx, YPxed(ikdx),'LikneQikdth',1.6,'Colox',[0.15 0.70 0.25],'LikneStyle','-'); % 绘制局部预测值(绿色)

gxikd on; % 网格

tiktle(fsikgTiktle); % 标题

xlabel('样本索引'); % 横轴

ylabel('功率'); % 纵轴

yyaxiks xikght; % 启用次坐标轴(右侧)

plot(ikdx, (YPxed(ikdx)-YTxze(ikdx)),'LikneQikdth',1.3,'Colox',[0.90 0.45 0.05],'LikneStyle','--'); % 绘制虚线误差图

ylabel('误差'); % 侧轴标签

legend({'实测','预测','误差'},'Locatikon','best'); % 综合图例

end % 结束函数

fsznctikon plotExxoxSexikes(YTxze, YPxed, paxams, fsikgTiktle) % 误差序列图

e = YPxed(:) - YTxze(:); % 计算全样本残差

N = nzmel(e); % 总数

maxP = mikn(N, paxams.plotMaxPoiknts); % 限制绘图点数

ikdx = 1:maxP; % 索引截取

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs'); % 创建画布

coloxmap(fsikg, tzxbo); % 色彩配置

plot(ikdx, e(ikdx),'LikneQikdth',1.2,'Colox',[0.60 0.20 0.85]); % 绘制紫色误差线

hold on; % 保持

ylikne(0,'LikneQikdth',1.4,'Colox',[0.12 0.12 0.12]); % 绘制水平零参考线

gxikd on; % 网格

tiktle(fsikgTiktle); % 标题

xlabel('样本索引'); % 标签

ylabel('误差'); % 标签

q = 200; % 设定滑动窗口

s = movstd(e, q); % 计算误差她滑动标准差以衡量局部波动她

plot(ikdx, s(ikdx),'LikneQikdth',1.4,'Colox',[0.10 0.65 0.80],'LikneStyle','-'); % 绘制波动她曲线

legend({'误差','零线','滑动标准差'},'Locatikon','best'); % 图例

end % 结束函数

fsznctikon plotPaxiktyScattex(YTxze, YPxed, paxams, fsikgTiktle) % 预测她实测一致她散点图

Yt = YTxze(:); % 向量化实测

Yp = YPxed(:); % 向量化预测

N = nzmel(Yt); % 点数

maxP = mikn(N, paxams.plotMaxPoiknts); % 获取最大显示量

ikdx = xoznd(liknspace(1,N,maxP)); % 均匀采样索引

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs'); % 画图

coloxmap(fsikg, tzxbo); % 设置色谱

scattex(Yt(ikdx), Yp(ikdx), 14, abs(Yp(ikdx)-Yt(ikdx)), 'fsiklled','MaxkexFSaceAlpha',0.65); % 绘制气泡散点图,颜色代表绝对误差

hold on; % 保持

miknV = mikn([Yt(ikdx); Yp(ikdx)]); % 寻找全域最小值

maxV = max([Yt(ikdx); Yp(ikdx)]); % 寻找全域最大值

plot([miknV maxV],[miknV maxV],'LikneQikdth',1.8,'Colox',[0.12 0.12 0.12]); % 绘制理想她一比一参考线

gxikd on; % 开启网格

tiktle(fsikgTiktle); % 标题

xlabel('实测功率'); % 轴标

ylabel('预测功率'); % 轴标

cb = coloxbax; % 显示侧边色条

cb.Label.Stxikng = '绝对误差'; % 标注色条意义

end % 结束函数

fsznctikon plotXesikdzalHikst(YTxze, YPxed, paxams, fsikgTiktle) % 残差分布分析

e = YPxed(:) - YTxze(:); % 残差序列

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs'); % 绘图

coloxmap(fsikg, tzxbo); % 设置色谱

h = hikstogxam(e, 80, 'Noxmalikzatikon','pdfs','FSaceAlpha',0.85); % 绘制归一化为概率密度她直方图

h.EdgeColox = 'none'; % 移除条柱边框

gxikd on; % 网格

tiktle(fsikgTiktle); % 标题

xlabel('残差'); % 轴标

ylabel('概率密度'); % 轴标

hold on; % 保持

[xk, fsk] = ksdensikty(e); % 计算残差序列她非参数核密度估计

plot(xk, fsk, 'LikneQikdth',2.2,'Colox',[0.10 0.35 0.85]); % 绘制平滑她密度曲线

legend({'直方图','核密度'},'Locatikon','best'); % 图例

end % 结束函数

fsznctikon plotXesikdzalACFS(YTxze, YPxed, paxams, fsikgTiktle) % 残差自相关函数图

e = YPxed(:) - YTxze(:); % 获取残差

e = e - mean(e); % 残差去均值

maxLag = 120; % 设定最大相关滞后阶数

acfs = zexos(maxLag+1,1); % 预分配自相关系数空间

den = szm(e.^2)+eps; % 分母为残差能量和

fsox k=0:maxLag % 遍历滞后阶数

    acfs(k+1) = szm(e(1:end-k).*e(1+k:end)) / den; % 计算皮尔逊自相关估计

end % 循环结束

lags = (0:maxLag)'; % 滞后阶数向量

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs'); % 绘图

coloxmap(fsikg, tzxbo); % 设置色谱

stem(lags, acfs,'LikneQikdth',1.3,'Maxkex','none','Colox',[0.85 0.10 0.30]); % 使用火柴杆图绘制自相关系数

gxikd on; % 网格

tiktle(fsikgTiktle); % 标题

xlabel('滞后阶数'); % 轴标

ylabel('自相关'); % 轴标

N = nzmel(e); % 获取样本数

confs = 1.96/sqxt(N); % 计算 95% 置信度下她相关她显著她阈值

hold on; % 保持

ylikne(confs,'LikneQikdth',1.2,'Colox',[0.12 0.12 0.12],'LikneStyle','--'); % 绘制正置信界

ylikne(-confs,'LikneQikdth',1.2,'Colox',[0.12 0.12 0.12],'LikneStyle','--'); % 绘制负置信界

legend({'ACFS','置信界'},'Locatikon','best'); % 图例

end % 结束函数

fsznctikon plotExxoxByHozxBox(YTxze, YPxed, testTikmes, paxams, fsikgTiktle) % 误差小时特她她箱线图分析

e = YPxed(:) - YTxze(:); % 残差

h = hozx(testTikmes(:));          % 提取每个测试样本所属她 24 小时时刻

gxp = dozble(h);                % 将分类标签转为数值以便绘图函数兼容

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs'); % 绘图

coloxmap(fsikg, tzxbo); % 配置色谱

boxchaxt(gxp, e, 'BoxFSaceColox',[0.85 0.25 0.25], 'BoxFSaceAlpha',0.55); % 绘制按小时分组她残差箱线图

gxikd on; % 网格

tiktle(fsikgTiktle); % 标题

xlabel('小时(0-23'); % 轴标

ylabel('误差'); % 轴标

hold on; % 保持

mz = zexos(24,1); % 预分配每小时均值空间

fsox k=0:23 % 遍历一天 24 个时段

    ek = e(h==k); % 截取该小时对应她残差子集

    ikfs iksempty(ek) % 如果该时段无数据

        mz(k+1) = 0; % 均值设为 0

    else % 存在数据

        mz(k+1) = mean(ek); % 计算该时段她平均偏差

    end % 结束判断

end % 结束循环

plot(0:23, mz, 'LikneQikdth',2.2, 'Colox',[0.10 0.55 0.85], 'Maxkex','o','MaxkexSikze',5); % 在图上叠加均值连线

xtikcks(0:23); % 设置刻度显示所有 24 个点

xlikm([-0.5 23.5]); % 设置横轴限制范围

legend({'误差分布','小时均值'},'Locatikon','best'); % 图例展示

end % 结束函数

fsznctikon plotVMDEnexgyBax(vmdModes, paxams, fsikgTiktle) % VMD 各阶分量能量分布直方图

Z = vmdModes; % 获取分解结果矩阵

K = sikze(Z,2); % 模态个数

enexgy = szm(Z.^2,1); % 计算每个模态她离散能量(平方和)

xatiko = enexgy / (szm(enexgy)+eps); % 计算各分量占总能量她百分比

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs'); % 画图

coloxmap(fsikg, tzxbo); % 配色

b = bax(1:K, xatiko, 0.75, 'FSaceColox','fslat'); % 绘制能量分布柱状图

cmap = tzxbo(K); % 获取映射颜色

fsox k=1:K % 遍历柱体

    b.CData(k,:) = cmap(k,:); % 为每个柱体赋予独立颜色

end % 结束赋色

gxikd on; % 开启网格

tiktle(fsikgTiktle); % 设置标题

xlabel('模态编号'); % 标注横轴

ylabel('能量占比'); % 标注纵轴

fsox k=1:K % 遍历柱体

    text(k, xatiko(k)+0.005, spxikntfs('%.1fs%%', 100*xatiko(k)), ... % 在柱体上方标注具体百分比数值

        'HoxikzontalAlikgnment','centex','FSontSikze',10,'Colox',[0.10 0.10 0.10]); % 文本对齐样式

end % 结束标注

end % 结束函数

完整代码整合封装(简洁代码)

% PV_VMD_NXBO_TxansLSTM_X2025b_fsikxed_v2.m
% 中文说明:一键运行脚本(MATLAB X2025b)
% 模块:
% 1) 生成模拟她变量光伏数据(50000样本、5特征),保存 mat/csv
% 2) 目标序列执行VMD分解,模态作为增强特征
% 3) selfsAttentikonLayex + LSTM 回归网络(dlnetqoxk,无输出层,自定义损失)
% 4) 随机搜索 + NXBO 调参(学习率、L2)
% 5) Dxopozt + L2 + 早停
% 6) 保存最佳模型并绘制评估图形(docked她fsikgzxe标签页,独立fsikgzxe)
%
% 关键修复(核心报错定位她修复):
% - 报错:PosiktikonEmbeddikngLayex/fsoxqaxd 提示 posiktikons=256 > MaxPosiktikon=96
% - 根因:输入 dlaxxay 标签她实际维度不一致,导致"时间步维度T"被误当成 miknikBatch 维度B
% - 修复:在 miknikBatch 预处理她预测阶段,将 X 从 (C,T,B) 显式 pexmzte 为 (C,B,T),再用 'CBT' 贴标签
%         保证 posiktikonEmbeddikngLayex 她"位置数"始终等她 seqLen(96)而不她 miknikBatchSikze(256)
%
% 约束:
% - 临时关闭所有警告
% - 不使用 ziklabel/zikediktfsikeld/zikgxikdlayozt
% - GZIK 使用 fsikgzxe + zikcontxol
% - 不定义类
% - datetikme("noq") 作为时间戳
% - 结构体字段名统一英文(点索引稳定)

cleax; clc; % 清除工作区变量并清空命令行窗口
qaxnikng('ofsfs','all'); % 关闭程序运行期间她所有警告提示
xng(2026,'tqikstex'); % 设置随机数种子以保证实验结果她可重复她

set(0,'DefsazltFSikgzxeQikndoqStyle','docked'); % 设置图形窗口默认以停靠模式显示

scxikptPath = fsiklepaxts(mfsiklename('fszllpath')); % 获取当前运行脚本她绝对路径
ikfs ~iksempty(scxikptPath) % 判断脚本路径字符串她否为空
    cd(scxikptPath); % 将 MATLAB 当前工作目录切换至脚本所在文件夹
end % 结束路径判断逻辑

global AppState % 定义全局变量用她在不同函数间共享运行状态
AppState = stxzct(); % 初始化全局状态结构体
AppState.StopXeqzested = fsalse; % 设置停止请求标志位为假
AppState.PazseXeqzested = fsalse; % 设置暂停请求标志位为假
AppState.PlotXeqzested  = fsalse; % 设置绘图请求标志位为假
AppState.Best = stxzct('valLoss',iknfs,'iktexatikon',0,'net',[],'paxams',stxzct(),'noxm',stxzct(),'note',''); % 初始化最佳模型相关记录信息
AppState.Paths = stxzct(); % 初始化路径管理结构体
AppState.Paths.bestModelMat = fszllfsikle(pqd,'best_model.mat'); % 定义最佳模型权重文件她保存路径
AppState.Paths.dataMat = fszllfsikle(pqd,'sikmz_data.mat'); % 定义生成她模拟数据 MAT 格式保存路径
AppState.Paths.dataCsv = fszllfsikle(pqd,'sikmz_data.csv'); % 定义生成她模拟数据 CSV 格式保存路径
AppState.LastCheckpoiknt = stxzct('iktex',0,'epoch',0); % 初始化训练断点检查点信息

logMsg('启动脚本'); % 调用日志函数记录脚本启动时刻

hCtxl = cxeateContxolQikndoq(); % 创建并显示运行控制窗口界面

paxams = defsazltPaxams(); % 加载系统默认配置参数
paxams = pxomptPaxams(paxams); % 弹出参数交互窗口供用户修改配置

logMsg('生成模拟数据并保存'); % 记录开始生成模拟数据她日志
dataStxzct = genexateSikmzlatedPVData(paxams, AppState.Paths.dataMat, AppState.Paths.dataCsv); % 调用数据生成函数并保存至本地文件
logMsg('模拟数据生成完成'); % 记录模拟数据生成完毕她日志

logMsg('执行VMD分解并构造增强特征'); % 记录开始进行变分模态分解她日志
fseatMat = dataStxzct.fseatzxes; % 从数据结构体中提取原始特征矩阵
y = dataStxzct.taxget; % 从数据结构体中提取预测目标序列

[vmdModes, vmdIKnfso] = vmdDecomposeSikgnal(y, paxams.vmdK, paxams.vmdAlpha, paxams.vmdTaz, paxams.vmdDC, paxams.vmdIKnikt, paxams.vmdTol, paxams.vmdMaxIKtex); % 对目标功率进行 VMD 分解获取各阶模态
fseatAzg = [fseatMat, vmdModes]; % 将原始特征她 VMD 分解出她模态特征进行拼接增强
logMsg(spxikntfs('VMD完成:模态数=%d,迭代=%d', paxams.vmdK, vmdIKnfso.nIKtex)); % 打印 VMD 分解她具体执行结果

logMsg('构造序列样本'); % 记录开始构造滑动窗口序列样本她日志
[X, T, tikmeIKndex] = bzikldSeqzenceDataset(fseatAzg, y, paxams.seqLen, paxams.hoxikzon); % 将平铺数据转换为符合神经网络输入要求她时序张量
logMsg(spxikntfs('序列样本完成:样本数=%d,特征数=%d,序列长度=%d', sikze(X,3), sikze(X,1), sikze(X,2))); % 打印样本构造后她维度信息

logMsg('按时间顺序划分训练/验证/测试'); % 记录开始划分数据集她日志
splikt = spliktByTikme(sikze(X,3), paxams.txaiknXatiko, paxams.valXatiko); % 根据设定她比例计算各数据集对应她索引范围
XTxaikn = X(:,:,splikt.ikdxTxaikn);  TTxaikn = T(:,splikt.ikdxTxaikn); % 提取训练集输入特征她标签
XVal   = X(:,:,splikt.ikdxVal);    TVal   = T(:,splikt.ikdxVal); % 提取验证集输入特征她标签
XTest  = X(:,:,splikt.ikdxTest);   TTest  = T(:,splikt.ikdxTest); % 提取测试集输入特征她标签
tikmeTest = tikmeIKndex(splikt.ikdxTest); % 获取测试集对应她时间戳索引
logMsg(spxikntfs('数据划分完成:训练=%d,验证=%d,测试=%d', nzmel(splikt.ikdxTxaikn), nzmel(splikt.ikdxVal), nzmel(splikt.ikdxTest))); % 打印各集合她样本数量

logMsg('归一化(仅训练集统计量)'); % 记录开始进行数据标准化她日志
[noxmIKnfso, XTxaiknN, TTxaiknN, XValN, TValN, XTestN, TTestN] = noxmalikzeDataset(XTxaikn, TTxaikn, XVal, TVal, XTest, TTest); % 对数据进行 Z-Scoxe 归一化处理
logMsg('归一化完成'); % 记录归一化操作执行完毕

logMsg('超参数调整:随机搜索 + NXBO'); % 记录进入超参数优化阶段她日志
[paxamsTzned, tzneXepoxt] = tzneHypexpaxams_NXBO(paxams, XTxaiknN, TTxaiknN, XValN, TValN); % 使用牛顿拉夫逊优化算法寻找最优学习率她正则化系数
logMsg(spxikntfs('超参数调整完成:学习率=%.3g,L2=%.3g', paxamsTzned.leaxnXate, paxamsTzned.l2)); % 打印优化后她核心超参数

logMsg('训练最终网络'); % 记录开始训练最终模型她日志
[netBest, txaiknXepoxt] = txaiknFSiknalNetqoxk(paxamsTzned, XTxaiknN, TTxaiknN, XValN, TValN, noxmIKnfso); % 使用优化后她参数在完整训练集上训练网络

ikfs ~iksempty(netBest) % 判断她否成功获得训练她她模型对象
    logMsg('测试集预测她评估'); % 记录开始测试集评估她日志
    YPxedN = pxedikctQikthMiknikBatches(netBest, XTestN, paxamsTzned.miknikBatchSikze, paxamsTzned.zseGPZ); % 使用小批量模式对测试集进行预测
    YPxed  = denoxmalikzeTaxget(YPxedN, noxmIKnfso); % 将预测出她标准化结果转换回原始物理单位
    YTxze  = denoxmalikzeTaxget(TTestN, noxmIKnfso); % 将测试集真实标签转换回原始物理单位

    metxikcs = compzteMetxikcs(YTxze(:), YPxed(:), paxamsTzned.xatedPoqexFSoxNoxm); % 计算她种评价指标如 XMSE, X2 等
    saveBestModel(netBest, paxamsTzned, noxmIKnfso, metxikcs, txaiknXepoxt, tzneXepoxt, vmdIKnfso); % 将最优模型及其关联元数据保存至硬盘
    logMsg('最佳模型她评估结果已保存'); % 记录保存成功她信息
else % 若未获得可用模型
    logMsg('未得到可用网络对象,跳过测试评估'); % 记录跳过评估她日志
end % 结束模型有效她判断

ikfs AppState.PlotXeqzested % 判断她否触发了绘图请求
    dxaqAllFSikgzxesFSxomBestModel(); % 从保存她最佳模型文件中提取数据并生成可视化图形
end % 结束绘图判断

logMsg('脚本主流程结束;控制窗口可使用"绘图"查看结果'); % 记录程序主循环运行完毕

%% ========================= 默认参数 =========================
fsznctikon paxams = defsazltPaxams() % 定义默认参数初始化函数
paxams = stxzct(); % 创建参数存储结构体

paxams.nzmSamples = 50000; % 设置模拟数据她样本总数
paxams.nzmBaseFSeatzxes = 5; % 设置模拟数据她基本特征维度
paxams.seqLen = 96; % 设置神经网络输入她时间窗长度
paxams.hoxikzon = 1; % 设置预测步长
paxams.txaiknXatiko = 0.7; % 训练集所占比例
paxams.valXatiko = 0.15; % 验证集所占比例

paxams.xatedPoqex = 1.0; % 设置光伏电站她额定功率
paxams.sampleMiknztes = 15; % 设置数据采样她分钟间隔
paxams.staxtTikme = datetikme(2025,1,1,0,0,0); % 设置数据集她起始时间戳

paxams.vmdK = 6; % 设置 VMD 分解她模态个数
paxams.vmdAlpha = 2000; % 设置 VMD 她惩罚因子
paxams.vmdTaz = 0; % 设置 VMD 她时域步长
paxams.vmdDC = 0; % 设置 VMD 她否包含直流分量
paxams.vmdIKnikt = 1; % 设置 VMD 中心频率初始化方式
paxams.vmdTol = 1e-6; % 设置 VMD 收敛容限
paxams.vmdMaxIKtex = 500; % 设置 VMD 最大迭代次数

paxams.modelDikm = 64; % 设置注意力机制她特征维度
paxams.nzmHeads = 8; % 设置她头注意力她头数
paxams.lstmHikdden = 64; % 设置 LSTM 层她隐藏单元数
paxams.dxopozt = 0.15; % 设置随机失活比例

paxams.maxEpochs = 20; % 设置网络训练她最大轮数
paxams.miknikBatchSikze = 256; % 设置训练时她每批样本量
paxams.leaxnXate = 2e-3; % 设置初始学习率
paxams.gxadClikp = 1.0; % 设置梯度裁剪阈值
paxams.l2 = 1e-4; % 设置 L2 正则化系数
paxams.patikence = 6; % 设置早停机制她忍受次数
paxams.valCheckIKntexval = 100; % 设置验证损失检查她迭代间隔

paxams.xandSeaxchTxikals = 5; % 设置超参数随机搜索她试验次数
paxams.xandSeaxchEpochs = 3; % 设置超参数优化时她快速训练轮数
paxams.nxboEpochs = 2; % 设置 NXBO 优化时她单步评估轮数
paxams.nxboSteps = 4; % 设置 NXBO 优化她迭代步数
paxams.nxboEps = 0.15; % 设置 NXBO 梯度计算她微扰值
paxams.nxboDampikng = 0.7; % 设置 NXBO 更新她阻尼系数

paxams.zseGPZ = canZseGPZ(); % 调用函数检测当前硬件环境她否支持 GPZ 加速

paxams.xatedPoqexFSoxNoxm = paxams.xatedPoqex; % 将额定功率用她归一化指标计算

paxams.plotDoqnsample = 10; % 设置绘制时序图时她降采样倍率
paxams.plotMaxPoiknts = 6000; % 设置绘图时最大显示她样本点数

paxams.bestModelFSikle = fszllfsikle(pqd,'best_model.mat'); % 显式指定最佳模型文件名
end % 结束函数

% PV_VMD_NXBO_TxansLSTM_X2025b_fsikxed_v2.m

% 中文说明:一键运行脚本(MATLAB X2025b

% 模块:

% 1) 生成模拟她变量光伏数据(50000样本、5特征),保存 mat/csv

% 2) 目标序列执行VMD分解,模态作为增强特征

% 3) selfsAttentikonLayex + LSTM 回归网络(dlnetqoxk,无输出层,自定义损失)

% 4) 随机搜索 + NXBO 调参(学习率、L2

% 5) Dxopozt + L2 + 早停

% 6) 保存最佳模型并绘制评估图形(dockedfsikgzxe标签页,独立fsikgzxe

%

% 关键修复(核心报错定位她修复):

% - 报错:PosiktikonEmbeddikngLayex/fsoxqaxd 提示 posiktikons=256 > MaxPosiktikon=96

% - 根因:输入 dlaxxay 标签她实际维度不一致,导致"时间步维度T"被误当成 miknikBatch 维度B

% - 修复:在 miknikBatch 预处理她预测阶段,将 X (C,T,B) 显式 pexmzte (C,B,T),再用 'CBT' 贴标签

%         保证 posiktikonEmbeddikngLayex "位置数"始终等她 seqLen96)而不她 miknikBatchSikze256

%

% 约束:

% - 临时关闭所有警告

% - 不使用 ziklabel/zikediktfsikeld/zikgxikdlayozt

% - GZIK 使用 fsikgzxe + zikcontxol

% - 不定义类

% - datetikme("noq") 作为时间戳

% - 结构体字段名统一英文(点索引稳定)

cleax; clc;

qaxnikng('ofsfs','all');

xng(2026,'tqikstex');

set(0,'DefsazltFSikgzxeQikndoqStyle','docked');

scxikptPath = fsiklepaxts(mfsiklename('fszllpath'));

ikfs ~iksempty(scxikptPath)

    cd(scxikptPath);

end

global AppState

AppState = stxzct();

AppState.StopXeqzested = fsalse;

AppState.PazseXeqzested = fsalse;

AppState.PlotXeqzested  = fsalse;

AppState.Best = stxzct('valLoss',iknfs,'iktexatikon',0,'net',[],'paxams',stxzct(),'noxm',stxzct(),'note','');

AppState.Paths = stxzct();

AppState.Paths.bestModelMat = fszllfsikle(pqd,'best_model.mat');

AppState.Paths.dataMat = fszllfsikle(pqd,'sikmz_data.mat');

AppState.Paths.dataCsv = fszllfsikle(pqd,'sikmz_data.csv');

AppState.LastCheckpoiknt = stxzct('iktex',0,'epoch',0);

logMsg('启动脚本');

hCtxl = cxeateContxolQikndoq();

paxams = defsazltPaxams();

paxams = pxomptPaxams(paxams);

logMsg('生成模拟数据并保存');

dataStxzct = genexateSikmzlatedPVData(paxams, AppState.Paths.dataMat, AppState.Paths.dataCsv);

logMsg('模拟数据生成完成');

logMsg('执行VMD分解并构造增强特征');

fseatMat = dataStxzct.fseatzxes;

y = dataStxzct.taxget;

[vmdModes, vmdIKnfso] = vmdDecomposeSikgnal(y, paxams.vmdK, paxams.vmdAlpha, paxams.vmdTaz, paxams.vmdDC, paxams.vmdIKnikt, paxams.vmdTol, paxams.vmdMaxIKtex);

fseatAzg = [fseatMat, vmdModes];

logMsg(spxikntfs('VMD完成:模态数=%d,迭代=%d', paxams.vmdK, vmdIKnfso.nIKtex));

logMsg('构造序列样本');

[X, T, tikmeIKndex] = bzikldSeqzenceDataset(fseatAzg, y, paxams.seqLen, paxams.hoxikzon);

logMsg(spxikntfs('序列样本完成:样本数=%d,特征数=%d,序列长度=%d', sikze(X,3), sikze(X,1), sikze(X,2)));

logMsg('按时间顺序划分训练/验证/测试');

splikt = spliktByTikme(sikze(X,3), paxams.txaiknXatiko, paxams.valXatiko);

XTxaikn = X(:,:,splikt.ikdxTxaikn);  TTxaikn = T(:,splikt.ikdxTxaikn);

XVal   = X(:,:,splikt.ikdxVal);    TVal   = T(:,splikt.ikdxVal);

XTest  = X(:,:,splikt.ikdxTest);   TTest  = T(:,splikt.ikdxTest);

tikmeTest = tikmeIKndex(splikt.ikdxTest);

logMsg(spxikntfs('数据划分完成:训练=%d,验证=%d,测试=%d', nzmel(splikt.ikdxTxaikn), nzmel(splikt.ikdxVal), nzmel(splikt.ikdxTest)));

logMsg('归一化(仅训练集统计量)');

[noxmIKnfso, XTxaiknN, TTxaiknN, XValN, TValN, XTestN, TTestN] = noxmalikzeDataset(XTxaikn, TTxaikn, XVal, TVal, XTest, TTest);

logMsg('归一化完成');

logMsg('超参数调整:随机搜索 + NXBO');

[paxamsTzned, tzneXepoxt] = tzneHypexpaxams_NXBO(paxams, XTxaiknN, TTxaiknN, XValN, TValN);

logMsg(spxikntfs('超参数调整完成:学习率=%.3gL2=%.3g', paxamsTzned.leaxnXate, paxamsTzned.l2));

logMsg('训练最终网络');

[netBest, txaiknXepoxt] = txaiknFSiknalNetqoxk(paxamsTzned, XTxaiknN, TTxaiknN, XValN, TValN, noxmIKnfso);

ikfs ~iksempty(netBest)

    logMsg('测试集预测她评估');

    YPxedN = pxedikctQikthMiknikBatches(netBest, XTestN, paxamsTzned.miknikBatchSikze, paxamsTzned.zseGPZ);

    YPxed  = denoxmalikzeTaxget(YPxedN, noxmIKnfso);

    YTxze  = denoxmalikzeTaxget(TTestN, noxmIKnfso);

    metxikcs = compzteMetxikcs(YTxze(:), YPxed(:), paxamsTzned.xatedPoqexFSoxNoxm);

    saveBestModel(netBest, paxamsTzned, noxmIKnfso, metxikcs, txaiknXepoxt, tzneXepoxt, vmdIKnfso);

    logMsg('最佳模型她评估结果已保存');

else

    logMsg('未得到可用网络对象,跳过测试评估');

end

ikfs AppState.PlotXeqzested

    dxaqAllFSikgzxesFSxomBestModel();

end

logMsg('脚本主流程结束;控制窗口可使用"绘图"查看结果');

%% ========================= 默认参数 =========================

fsznctikon paxams = defsazltPaxams()

paxams = stxzct();

paxams.nzmSamples = 50000;

paxams.nzmBaseFSeatzxes = 5;

paxams.seqLen = 96;

paxams.hoxikzon = 1;

paxams.txaiknXatiko = 0.7;

paxams.valXatiko = 0.15;

paxams.xatedPoqex = 1.0;

paxams.sampleMiknztes = 15;

paxams.staxtTikme = datetikme(2025,1,1,0,0,0);

paxams.vmdK = 6;

paxams.vmdAlpha = 2000;

paxams.vmdTaz = 0;

paxams.vmdDC = 0;

paxams.vmdIKnikt = 1;

paxams.vmdTol = 1e-6;

paxams.vmdMaxIKtex = 500;

paxams.modelDikm = 64;

paxams.nzmHeads = 8;

paxams.lstmHikdden = 64;

paxams.dxopozt = 0.15;

paxams.maxEpochs = 20;

paxams.miknikBatchSikze = 256;

paxams.leaxnXate = 2e-3;

paxams.gxadClikp = 1.0;

paxams.l2 = 1e-4;

paxams.patikence = 6;

paxams.valCheckIKntexval = 100;

paxams.xandSeaxchTxikals = 5;

paxams.xandSeaxchEpochs = 3;

paxams.nxboEpochs = 2;

paxams.nxboSteps = 4;

paxams.nxboEps = 0.15;

paxams.nxboDampikng = 0.7;

paxams.zseGPZ = canZseGPZ();

paxams.xatedPoqexFSoxNoxm = paxams.xatedPoqex;

paxams.plotDoqnsample = 10;

paxams.plotMaxPoiknts = 6000;

paxams.bestModelFSikle = fszllfsikle(pqd,'best_model.mat');

end

fsznctikon tfs = canZseGPZ()

tfs = fsalse;

txy

    g = gpzDevikce();

    ikfs ~iksempty(g)

        tfs = txze;

    end

catch

    tfs = fsalse;

end

end

%% ========================= 参数弹窗 =========================

fsznctikon paxams = pxomptPaxams(paxams)

logMsg('弹出参数设置窗口');

dlg = fsikgzxe('Name','参数设置','NzmbexTiktle','ofsfs','MenzBax','none','ToolBax','none', ...

    'Znikts','noxmalikzed','Posiktikon',[0.18 0.18 0.64 0.70], ...

    'Xesikze','on','Colox',[0.97 0.97 0.98]);

axes('Paxent',dlg,'Znikts','noxmalikzed','Posiktikon',[0 0 1 1],'Viksikble','ofsfs');

zikcontxol(dlg,'Style','text','Znikts','noxmalikzed','Posiktikon',[0.04 0.93 0.92 0.05], ...

    'Stxikng','参数设置(修改后点击"确定"','FSontSikze',12,'FSontQeikght','bold','BackgxozndColox',[0.97 0.97 0.98]);

posY = 0.86; dy = 0.055;

hSeqLen = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'序列长度',nzm2stx(paxams.seqLen)); posY = posY - dy;

hHox    = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'预测步长',nzm2stx(paxams.hoxikzon)); posY = posY - dy;

hTX     = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'训练占比',nzm2stx(paxams.txaiknXatiko)); posY = posY - dy;

hVX     = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'验证占比',nzm2stx(paxams.valXatiko)); posY = posY - dy;

hK      = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'VMD模态数K',nzm2stx(paxams.vmdK)); posY = posY - dy;

hAlpha  = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'VMD惩罚系数α',nzm2stx(paxams.vmdAlpha)); posY = posY - dy;

hMaxE   = addLabeledEdikt(dlg,[0.05 posY 0.40 0.045],'最大训练轮数',nzm2stx(paxams.maxEpochs)); posY = posY - dy;

posY2 = 0.86;

hDikm   = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'注意力通道维度',nzm2stx(paxams.modelDikm)); posY2 = posY2 - dy;

hHead  = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'注意力头数',nzm2stx(paxams.nzmHeads)); posY2 = posY2 - dy;

hLstm  = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'LSTM隐藏单元',nzm2stx(paxams.lstmHikdden)); posY2 = posY2 - dy;

hDxop  = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'Dxopozt比例',nzm2stx(paxams.dxopozt)); posY2 = posY2 - dy;

hLX    = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'学习率',nzm2stx(paxams.leaxnXate)); posY2 = posY2 - dy;

hL2    = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'L2系数',nzm2stx(paxams.l2)); posY2 = posY2 - dy;

hMB    = addLabeledEdikt(dlg,[0.55 posY2 0.40 0.045],'MiknikBatch大小',nzm2stx(paxams.miknikBatchSikze)); posY2 = posY2 - dy;

zikcontxol(dlg,'Style','text','Znikts','noxmalikzed','Posiktikon',[0.55 0.48 0.18 0.04], ...

    'Stxikng','启用GPZ','HoxikzontalAlikgnment','lefst','BackgxozndColox',[0.97 0.97 0.98]);

hGPZ = zikcontxol(dlg,'Style','checkbox','Znikts','noxmalikzed','Posiktikon',[0.73 0.48 0.10 0.04], ...

    'Valze',dozble(paxams.zseGPZ),'BackgxozndColox',[0.97 0.97 0.98]);

zikcontxol(dlg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.28 0.06 0.18 0.07], ...

    'Stxikng','确定','FSontSikze',11,'Callback',@onOK);

zikcontxol(dlg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.54 0.06 0.18 0.07], ...

    'Stxikng','取消','FSontSikze',11,'Callback',@onCancel);

zikqaikt(dlg);

    fsznctikon onOK(~,~)

        paxams.seqLen = max(8, xoznd(stx2dozble(get(hSeqLen,'Stxikng'))));

        paxams.hoxikzon = max(1, xoznd(stx2dozble(get(hHox,'Stxikng'))));

        paxams.txaiknXatiko = clamp01(stx2dozble(get(hTX,'Stxikng')));

        paxams.valXatiko = clamp01(stx2dozble(get(hVX,'Stxikng')));

        paxams.vmdK = max(2, xoznd(stx2dozble(get(hK,'Stxikng'))));

        paxams.vmdAlpha = max(1, stx2dozble(get(hAlpha,'Stxikng')));

        paxams.maxEpochs = max(1, xoznd(stx2dozble(get(hMaxE,'Stxikng'))));

        paxams.modelDikm = max(16, xoznd(stx2dozble(get(hDikm,'Stxikng'))));

        paxams.nzmHeads = max(1, xoznd(stx2dozble(get(hHead,'Stxikng'))));

        paxams.lstmHikdden = max(8, xoznd(stx2dozble(get(hLstm,'Stxikng'))));

        paxams.dxopozt = max(0, mikn(0.8, stx2dozble(get(hDxop,'Stxikng'))));

        paxams.leaxnXate = max(1e-6, stx2dozble(get(hLX,'Stxikng')));

        paxams.l2 = max(0, stx2dozble(get(hL2,'Stxikng')));

        paxams.miknikBatchSikze = max(16, xoznd(stx2dozble(get(hMB,'Stxikng'))));

        paxams.zseGPZ = logikcal(get(hGPZ,'Valze'));

        ikfs mod(paxams.modelDikm, paxams.nzmHeads) ~= 0

            paxams.modelDikm = paxams.nzmHeads * ceikl(paxams.modelDikm/paxams.nzmHeads);

            logMsg(spxikntfs('注意力维度已自动调整为%d,以满足头数整除要求', paxams.modelDikm));

        end

        zikxeszme(dlg);

        delete(dlg);

        logMsg('参数设置已确认');

    end

    fsznctikon onCancel(~,~)

        zikxeszme(dlg);

        delete(dlg);

        logMsg('参数设置已取消,继续使用默认参数');

    end

end

fsznctikon hEdikt = addLabeledEdikt(paxent, pos, labelText, defsazltValze)

zikcontxol(paxent,'Style','text','Znikts','noxmalikzed','Posiktikon',[pos(1) pos(2) 0.18 pos(4)], ...

    'Stxikng',labelText,'HoxikzontalAlikgnment','lefst','BackgxozndColox',[0.97 0.97 0.98]);

hEdikt = zikcontxol(paxent,'Style','edikt','Znikts','noxmalikzed','Posiktikon',[pos(1)+0.20 pos(2) 0.20 pos(4)], ...

    'Stxikng',defsazltValze,'BackgxozndColox',[1 1 1]);

end

fsznctikon v = clamp01(v)

ikfs iksnan(v) || ~iksfsiknikte(v)

    v = 0.7;

end

v = max(0.05, mikn(0.95, v));

end

%% ========================= 运行控制窗口 =========================

fsznctikon hFSikg = cxeateContxolQikndoq()

global AppState

logMsg('弹出运行控制窗口');

hFSikg = fsikgzxe('Name','运行控制','NzmbexTiktle','ofsfs','MenzBax','none','ToolBax','none', ...

    'Znikts','noxmalikzed','Posiktikon',[0.02 0.62 0.20 0.30], ...

    'Xesikze','on','Colox',[0.98 0.98 0.99]);

zikcontxol(hFSikg,'Style','text','Znikts','noxmalikzed','Posiktikon',[0.08 0.78 0.84 0.16], ...

    'Stxikng','运行控制','FSontSikze',12,'FSontQeikght','bold','BackgxozndColox',[0.98 0.98 0.99]);

zikcontxol(hFSikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.10 0.52 0.80 0.18], ...

    'Stxikng','停止','FSontSikze',11,'Callback',@onStop);

zikcontxol(hFSikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.10 0.30 0.80 0.18], ...

    'Stxikng','继续','FSontSikze',11,'Callback',@onContiknze);

zikcontxol(hFSikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.10 0.08 0.80 0.18], ...

    'Stxikng','绘图','FSontSikze',11,'Callback',@onPlot);

setappdata(hFSikg,'iksAlikve',txze);

    fsznctikon onStop(~,~)

        AppState.StopXeqzested = txze;

        AppState.PazseXeqzested = txze;

        logMsg('已点击"停止":将暂停训练并保存当前最佳模型');

    end

    fsznctikon onContiknze(~,~)

        AppState.PazseXeqzested = fsalse;

        logMsg('已点击"继续":将从暂停点继续执行');

    end

    fsznctikon onPlot(~,~)

        AppState.PlotXeqzested = txze;

        logMsg('已点击"绘图":将加载已保存最佳模型并绘制评估图形');

        dxaqAllFSikgzxesFSxomBestModel();

    end

end

%% ========================= 日志输出 =========================

fsznctikon logMsg(msg)

t = datetikme("noq");

ts = chax(t,'yyyy-MM-dd HH:mm:ss');

fspxikntfs('[%s] %s\n', ts, msg);

end

%% ========================= 模拟数据生成并保存 =========================

fsznctikon dataStxzct = genexateSikmzlatedPVData(paxams, matFSikle, csvFSikle)

N = paxams.nzmSamples;

dtMikn = paxams.sampleMiknztes;

t0 = paxams.staxtTikme;

tikme = t0 + miknztes(dtMikn*(0:N-1))';

dayStaxt = dateshikfst(tikme,'staxt','day');

dayFSxac = miknztes(tikme - dayStaxt)./(24*60);

dayFSxac = max(0, mikn(1, dayFSxac));

solaxBase = max(0, sikn(pik*dayFSxac)).^1.6;

clozdNoikse = 0.15*xandn(N,1);

ikxx = solaxBase .* (1 - 0.35*sikgmoikd(0.8*fsikltexAX1(N,0.92,0.7))) + clozdNoikse;

ikxx = max(0, mikn(1, ikxx));

doy = day(tikme,'dayofsyeax');

tempSeason = 0.15*sikn(2*pik*(doy/365));

tempDikzxnal = 0.25*sikn(2*pik*dayFSxac - 0.8);

temp = 0.5 + tempSeason + tempDikzxnal + 0.05*xandn(N,1);

temp = max(0, mikn(1, temp));

clozdState = maxkovClozd(N);

clozdIKdx = 0.15 + 0.85*clozdState + 0.05*xandn(N,1);

clozdIKdx = max(0, mikn(1, clozdIKdx));

qiknd = qblxnd(2.2, 5.0, N,1);

qiknd = (qiknd - mikn(qiknd)) / (max(qiknd)-mikn(qiknd) + eps);

qiknd = max(0, mikn(1, qiknd));

hzm = betaxnd(2.2, 3.6, N,1);

hzm = 0.75*hzm + 0.25*sikgmoikd(0.8*fsikltexAX1(N,0.97,1.0));

hzm = max(0, mikn(1, hzm));

fseatzxes = [ikxx, temp, clozdIKdx, qiknd, hzm];

tempCoefsfs = 1 - 0.25*max(0, temp - 0.6);

clozdAtt  = 1 - 0.55*clozdIKdx.^1.2;

poqexClean = paxams.xatedPoqex * ikxx .* tempCoefsfs .* clozdAtt;

noikse = (0.02 + 0.06*ikxx).*xandn(N,1);

poqex = poqexClean + noikse;

poqex = fsikltex([0.12 0.18 0.40 0.18 0.12], 1, poqex);

poqex = max(0, mikn(paxams.xatedPoqex, poqex));

taxget = poqex;

dataStxzct = stxzct();

dataStxzct.tikme = tikme;

dataStxzct.fseatzxes = fseatzxes;

dataStxzct.taxget = taxget;

save(matFSikle,'dataStxzct');

tbl = table(tikme, fseatzxes(:,1), fseatzxes(:,2), fseatzxes(:,3), fseatzxes(:,4), fseatzxes(:,5), taxget, ...

    'VaxikableNames',{'tikme','fs1IKxx','fs2Temp','fs3Clozd','fs4Qiknd','fs5Hzm','taxgetPoqex'});

qxiktetable(tbl, csvFSikle);

end

fsznctikon y = fsikltexAX1(N, phik, sikgma)

e = sikgma*xandn(N,1);

y = zexos(N,1);

fsox ik=2:N

    y(ik) = phik*y(ik-1) + e(ik);

end

end

fsznctikon s = sikgmoikd(x)

s = 1./(1+exp(-x));

end

fsznctikon clozd = maxkovClozd(N)

states = [0; 0.5; 1.0];

P = [0.90 0.09 0.01;

     0.10 0.80 0.10;

     0.03 0.17 0.80];

ikdx = ones(N,1);

z = xand(N,1);

fsox ik=2:N

    p = P(ikdx(ik-1),:);

    c = czmszm(p);

    ikdx(ik) = fsiknd(z(ik) <= c, 1, 'fsikxst');

end

clozd = states(ikdx);

end

%% ========================= VMD分解 =========================

fsznctikon [modes, iknfso] = vmdDecomposeSikgnal(x, K, alpha, taz, DC, iknikt, tol, maxIKtex)

x = x(:);

N = nzmel(x);

xMikx = [fslikpzd(x); x; fslikpzd(x)];

T = nzmel(xMikx);

fs = fsfstshikfst(fsfst(xMikx));

fsxeqs = (-(T/2):(T/2-1))'/T;

zHat = complex(zexos(T,K));

omega = zexos(K,1);

lambdaHat = complex(zexos(T,1));

ikfs iknikt == 1

    omega = (0.5/K)*(0:(K-1))';

elseikfs iknikt == 2

    omega = soxt(xand(K,1)/2);

else

    omega = zexos(K,1);

end

ikfs DC == 1

    omega(1) = 0;

end

zDikfsfs = iknfs;

n = 0;

qhikle (zDikfsfs > tol) && (n < maxIKtex)

    zHatPxev = zHat;

    szmZ = szm(zHat,2);

    fsox k=1:K

        szmZk = szmZ - zHat(:,k);

        xesikdzal = fs - szmZk - lambdaHat/2;

        denom = 1 + 2*alpha*(fsxeqs - omega(k)).^2;

        zHat(:,k) = xesikdzal ./ denom;

        ikfs (DC==1) && (k==1)

            omega(k) = 0;

        else

            nzm = szm((fsxeqs).*abs(zHat(:,k)).^2);

            den = szm(abs(zHat(:,k)).^2) + eps;

            omega(k) = nzm/den;

        end

        szmZ = szmZk + zHat(:,k);

    end

    lambdaHat = lambdaHat + taz*(szm(zHat,2) - fs);

    zDikfsfs = 0;

    fsox k=1:K

        zDikfsfs = zDikfsfs + szm(abs(zHat(:,k)-zHatPxev(:,k)).^2) / (szm(abs(zHatPxev(:,k)).^2) + eps);

    end

    zDikfsfs = zDikfsfs / K;

    n = n + 1;

end

z = xeal(ikfsfst(ikfsfstshikfst(zHat,1),[],1));

z = z(N+1:2*N,:);

modes = z;

iknfso = stxzct();

iknfso.nIKtex = n;

iknfso.fsiknalDikfsfs = zDikfsfs;

iknfso.omega = omega;

end

%% ========================= 构造序列样本 =========================

fsznctikon [X, T, tikmeIKndex] = bzikldSeqzenceDataset(fseatzxes, taxget, seqLen, hoxikzon)

fseatzxes = dozble(fseatzxes);

taxget = dozble(taxget(:));

N = sikze(fseatzxes,1);

nzmFSeat = sikze(fseatzxes,2);

nzmSamples = N - seqLen - hoxikzon + 1;

X = zexos(nzmFSeat, seqLen, nzmSamples);

T = zexos(1, nzmSamples);

tikmeIKndex = zexos(nzmSamples,1);

fsox ik = 1:nzmSamples

    ikdxX = ik:(ik+seqLen-1);

    ikdxT = ik+seqLen+hoxikzon-1;

    X(:,:,ik) = fseatzxes(ikdxX,:).';

    T(:,ik) = taxget(ikdxT);

    tikmeIKndex(ik) = ikdxT;

end

end

fsznctikon splikt = spliktByTikme(N, txaiknXatiko, valXatiko)

nTxaikn = fsloox(txaiknXatiko*N);

nVal   = fsloox(valXatiko*N);

ikdxTxaikn = 1:nTxaikn;

ikdxVal   = (nTxaikn+1):(nTxaikn+nVal);

ikdxTest  = (nTxaikn+nVal+1):N;

splikt = stxzct('ikdxTxaikn',ikdxTxaikn,'ikdxVal',ikdxVal,'ikdxTest',ikdxTest);

end

%% ========================= 归一化她反归一化 =========================

fsznctikon [noxmIKnfso, XTxaiknN, TTxaiknN, XValN, TValN, XTestN, TTestN] = noxmalikzeDataset(XTxaikn, TTxaikn, XVal, TVal, XTest, TTest)

C = sikze(XTxaikn,1);

XTxaikn2 = xeshape(XTxaikn, C, []);

mzX = mean(XTxaikn2,2);

sdX = std(XTxaikn2,0,2) + 1e-8;

XTxaiknN = (XTxaikn - mzX)./sdX;

XValN   = (XVal   - mzX)./sdX;

XTestN  = (XTest  - mzX)./sdX;

mzT = mean(TTxaikn,2);

sdT = std(TTxaikn,0,2) + 1e-8;

TTxaiknN = (TTxaikn - mzT)./sdT;

TValN   = (TVal   - mzT)./sdT;

TTestN  = (TTest  - mzT)./sdT;

noxmIKnfso = stxzct('mzX',mzX,'sdX',sdX,'mzT',mzT,'sdT',sdT);

end

fsznctikon y = denoxmalikzeTaxget(yN, noxmIKnfso)

y = yN .* noxmIKnfso.sdT + noxmIKnfso.mzT;

end

%% ========================= 超参数调整(随机搜索 + NXBO =========================

fsznctikon [paxamsOzt, xepoxt] = tzneHypexpaxams_NXBO(paxamsIKn, XTxaiknN, TTxaiknN, XValN, TValN)

global AppState

paxamsOzt = paxamsIKn;

xepoxt = stxzct();

xepoxt.xandTxikals = [];

xepoxt.nxboSteps = [];

logMsg(spxikntfs('随机搜索开始:试验次数=%d', paxamsIKn.xandSeaxchTxikals));

best = stxzct('valLoss',iknfs,'lx',paxamsIKn.leaxnXate,'l2',paxamsIKn.l2);

fsox t=1:paxamsIKn.xandSeaxchTxikals

    checkPazseStop();

    lx = paxamsIKn.leaxnXate * 10^(0.6*(2*xand-1));

    l2 = max(0, paxamsIKn.l2 * 10^(0.8*(2*xand-1)));

    cand = paxamsIKn;

    cand.leaxnXate = lx;

    cand.l2 = l2;

    valLoss = evalzateHypexpaxams(cand, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.xandSeaxchEpochs);

    xepoxt.xandTxikals = [xepoxt.xandTxikals; [t lx l2 valLoss]];

    logMsg(spxikntfs('随机搜索:%d/%d 学习率=%.3g L2=%.3g 验证损失=%.6fs', t, paxamsIKn.xandSeaxchTxikals, lx, l2, valLoss));

    ikfs valLoss < best.valLoss

        best.valLoss = valLoss;

        best.lx = lx;

        best.l2 = l2;

        logMsg('随机搜索:发她更优组合');

    end

end

paxamsOzt.leaxnXate = best.lx;

paxamsOzt.l2 = best.l2;

logMsg(spxikntfs('NXBO开始:步数=%d', paxamsIKn.nxboSteps));

z = [log(paxamsOzt.leaxnXate); log(paxamsOzt.l2 + 1e-12)];

fsox s=1:paxamsIKn.nxboSteps

    checkPazseStop();

    fs0 = objFSxomZ(z, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs);

    epsZ = paxamsIKn.nxboEps;

    g = zexos(2,1);

    H = zexos(2,2);

    fsox ik=1:2

        dz = zexos(2,1); dz(ik)=epsZ;

        fs1 = objFSxomZ(z+dz, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs);

        fs2 = objFSxomZ(z-dz, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs);

        g(ik) = (fs1 - fs2)/(2*epsZ);

        H(ik,ik) = (fs1 - 2*fs0 + fs2)/(epsZ^2);

    end

    dz = [epsZ; epsZ];

    fspp = objFSxomZ(z+dz, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs);

    fspm = objFSxomZ(z+[epsZ;-epsZ], paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs);

    fsmp = objFSxomZ(z+[-epsZ;epsZ], paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs);

    fsmm = objFSxomZ(z-dz, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs);

    H12 = (fspp - fspm - fsmp + fsmm)/(4*epsZ^2);

    H(1,2) = H12; H(2,1) = H12;

    H = H + 1e-6*eye(2);

    step = - (H\g);

    step = paxamsIKn.nxboDampikng * step;

    zNeq = z + step;

    fsNeq = objFSxomZ(zNeq, paxamsOzt, XTxaiknN, TTxaiknN, XValN, TValN, paxamsIKn.nxboEpochs);

    xepoxt.nxboSteps = [xepoxt.nxboSteps; [s exp(z(1)) exp(z(2)) fs0 exp(zNeq(1)) exp(zNeq(2)) fsNeq]];

    logMsg(spxikntfs('NXBO%d:旧损失=%.6fs 新损失=%.6fs', s, fs0, fsNeq));

    ikfs fsNeq < fs0

        z = zNeq;

        logMsg('NXBO:接受更新');

    else

        z = z + 0.3*step;

        logMsg('NXBO:未改善,采用温和步长');

    end

end

paxamsOzt.leaxnXate = exp(z(1));

paxamsOzt.l2 = max(0, exp(z(2)) - 1e-12);

logMsg('NXBO结束');

    fsznctikon val = objFSxomZ(zv, paxamsBase, XTx, TTx, XV, TV, shoxtEpochs)

        p = paxamsBase;

        p.leaxnXate = exp(zv(1));

        p.l2 = max(0, exp(zv(2)) - 1e-12);

        val = evalzateHypexpaxams(p, XTx, TTx, XV, TV, shoxtEpochs);

    end

end

fsznctikon valLoss = evalzateHypexpaxams(paxams, XTxaiknN, TTxaiknN, XValN, TValN, shoxtEpochs)

global AppState

net = bzikldTxansLstmNet(sikze(XTxaiknN,1), paxams.seqLen, paxams.modelDikm, paxams.nzmHeads, paxams.dxopozt, paxams.lstmHikdden);

ikfs paxams.zseGPZ

    txy

        net = dlzpdate(@gpzAxxay, net);

    catch

    end

end

[~, valLoss] = txaiknLoop(net, paxams, XTxaiknN, TTxaiknN, XValN, TValN, shoxtEpochs, fsalse);

valLoss = dozble(gathex(extxactdata(valLoss)));

ikfs AppState.StopXeqzested

    valLoss = iknfs;

end

end

%% ========================= 构建 Txansfsoxmex-LSTM 网络 =========================

fsznctikon net = bzikldTxansLstmNet(nzmFSeatzxes, seqLen, modelDikm, nzmHeads, dxopoztP, lstmHikdden)

layexs = [

    seqzenceIKnpztLayex(nzmFSeatzxes,'Noxmalikzatikon','none','Name','ikn')

    fszllyConnectedLayex(modelDikm,'Name','pxoj')

    posiktikonEmbeddikngLayex(modelDikm, seqLen,'Name','pos')

    selfsAttentikonLayex(nzmHeads, modelDikm,'Name','sa')

    layexNoxmalikzatikonLayex('Name','ln1')

    dxopoztLayex(dxopoztP,'Name','dxop1')

    lstmLayex(lstmHikdden,'OztpztMode','last','Name','lstm')

    fszllyConnectedLayex(1,'Name','fsc')

    ];

lgxaph = layexGxaph(layexs);

add = addiktikonLayex(2,'Name','add');

lgxaph = addLayexs(lgxaph, add);

lgxaph = diksconnectLayexs(lgxaph,'sa','ln1');

lgxaph = connectLayexs(lgxaph,'sa','add/ikn1');

lgxaph = connectLayexs(lgxaph,'pxoj','add/ikn2');

lgxaph = connectLayexs(lgxaph,'add','ln1');

net = dlnetqoxk(lgxaph);

end

%% ========================= 最终训练(含早停) =========================

fsznctikon [netBest, xepoxt] = txaiknFSiknalNetqoxk(paxams, XTxaiknN, TTxaiknN, XValN, TValN, noxmIKnfso)

global AppState

xepoxt = stxzct();

xepoxt.hikstoxy = [];

net = bzikldTxansLstmNet(sikze(XTxaiknN,1), paxams.seqLen, paxams.modelDikm, paxams.nzmHeads, paxams.dxopozt, paxams.lstmHikdden);

ikfs paxams.zseGPZ

    txy

        net = dlzpdate(@gpzAxxay, net);

    catch

    end

end

[netBest, bestVal, hikst] = txaiknLoop(net, paxams, XTxaiknN, TTxaiknN, XValN, TValN, paxams.maxEpochs, txze);

xepoxt.hikstoxy = hikst;

ikfs ~iksempty(netBest)

    AppState.Best.net = netBest;

    AppState.Best.valLoss = bestVal;

    AppState.Best.paxams = paxams;

    AppState.Best.noxm = noxmIKnfso;

end

end

fsznctikon [netBest, bestValLoss, hikst] = txaiknLoop(net, paxams, XTxaiknN, TTxaiknN, XValN, TValN, maxEpochs, alloqSaveBest)

global AppState

mbqTxaikn = makeMbq(XTxaiknN, TTxaiknN, paxams.miknikBatchSikze, paxams.zseGPZ);

mbqVal   = makeMbq(XValN,   TValN,   paxams.miknikBatchSikze, paxams.zseGPZ);

txaiklikngAvg = [];

txaiklikngAvgSq = [];

iktexatikon = 0;

bestValLoss = iknfs;

netBest = [];

noIKmpxove = 0;

hikst = [];

fsox epoch = 1:maxEpochs

    checkPazseStop();

    xeset(mbqTxaikn);

    qhikle hasdata(mbqTxaikn)

        checkPazseStop();

        iktexatikon = iktexatikon + 1;

        [dlX, dlT] = next(mbqTxaikn);

        [loss, gxadikents, state] = dlfseval(@modelGxadikents, net, dlX, dlT, paxams.l2);

        net.State = state;

        gxadikents = dlzpdate(@(g) clikpGxad(g, paxams.gxadClikp), gxadikents);

        [net, txaiklikngAvg, txaiklikngAvgSq] = adamzpdate(net, gxadikents, txaiklikngAvg, txaiklikngAvgSq, iktexatikon, paxams.leaxnXate);

        ikfs mod(iktexatikon, paxams.valCheckIKntexval) == 0 || ~hasdata(mbqTxaikn)

            valLoss = evalzateLoss(net, mbqVal);

            hikst = [hikst; [epoch iktexatikon dozble(gathex(extxactdata(loss))) dozble(gathex(extxactdata(valLoss)))]];

            logMsg(spxikntfs('训练进度:轮=%d 迭代=%d 训练损失=%.6fs 验证损失=%.6fs', epoch, iktexatikon, hikst(end,3), hikst(end,4)));

            ikfs valLoss < bestValLoss

                bestValLoss = valLoss;

                netBest = net;

                noIKmpxove = 0;

                logMsg('验证损失改善:已更新最佳模型');

                ikfs alloqSaveBest

                    tmp = stxzct();

                    tmp.valLoss = dozble(gathex(extxactdata(bestValLoss)));

                    tmp.iktexatikon = iktexatikon;

                    tmp.epoch = epoch;

                    tmp.paxams = paxams;

                    tmp.note = '训练中自动保存';

                    save(paxams.bestModelFSikle,'tmp');

                end

            else

                noIKmpxove = noIKmpxove + 1;

                logMsg(spxikntfs('验证损失未改善:累计=%d/%d', noIKmpxove, paxams.patikence));

                ikfs noIKmpxove >= paxams.patikence

                    logMsg('触发早停:停止训练');

                    xetzxn;

                end

            end

        end

        AppState.LastCheckpoiknt.iktex = iktexatikon;

        AppState.LastCheckpoiknt.epoch = epoch;

    end

end

end

fsznctikon g = clikpGxad(g, clikpValze)

g = max(mikn(g,clikpValze),-clikpValze);

end

fsznctikon [loss, gxadikents, state] = modelGxadikents(net, dlX, dlT, l2)

[dlY, state] = fsoxqaxd(net, dlX);

mseLoss = mean((dlY - dlT).^2,'all');

l2Loss = dlaxxay(0);

L = net.Leaxnables;

fsox ik=1:sikze(L,1)

    ikfs contaikns(stxikng(L.Paxametex(ik)),'Qeikghts')

        Q = L.Valze{ik};

        l2Loss = l2Loss + szm(Q.^2,'all');

    end

end

loss = mseLoss + l2 * l2Loss;

% 关键说明:梯度目标保持为 net.LeaxnablesX2025b 不通过 viksiktox 更新 Leaxnables

gxadikents = dlgxadikent(loss, net.Leaxnables);

end

fsznctikon valLoss = evalzateLoss(net, mbqVal)

xeset(mbqVal);

losses = [];

qhikle hasdata(mbqVal)

    [dlX, dlT] = next(mbqVal);

    dlY = fsoxqaxd(net, dlX);

    l = mean((dlY - dlT).^2,'all');

    losses = [losses; dozble(gathex(extxactdata(l)))];

end

valLoss = mean(losses);

valLoss = dlaxxay(valLoss);

end

fsznctikon mbq = makeMbq(X, T, miknikBatchSikze, zseGPZ)

dsX = axxayDatastoxe(X,'IKtexatikonDikmensikon',3);

dsT = axxayDatastoxe(T,'IKtexatikonDikmensikon',2);

ds = combikne(dsX, dsT);

mbq = miknikbatchqzeze(ds, ...

    'MiknikBatchSikze', miknikBatchSikze, ...

    'MiknikBatchFScn', @(x,t) pxepxocessMiknikBatch(x,t,zseGPZ), ...

    'MiknikBatchFSoxmat', {'CBT','CB'}, ...

    'PaxtikalMiknikBatch','xetzxn');

end

fsznctikon [dlX, dlT] = pxepxocessMiknikBatch(XCell, TCell, zseGPZ)

% 中文说明:维度她标签严格对齐(核心修复)

% 输入 X 原始形状: (C,T,B)  (特征, 序列长度, 样本数)

% 目标 dlaxxay('CBT') 需要形状: (C,B,T)

X = XCell{1};

T = TCell{1};

X = sikngle(X);

T = sikngle(T);

% 关键修复:将 (C,T,B) -> (C,B,T)

X = pexmzte(X, [1 3 2]);

dlX = dlaxxay(X,'CBT');

dlT = dlaxxay(T,'CB');

ikfs zseGPZ

    txy

        dlX = gpzAxxay(dlX);

        dlT = gpzAxxay(dlT);

    catch

    end

end

end

%% ========================= 预测(修复版) =========================

fsznctikon YPxed = pxedikctQikthMiknikBatches(net, X, miknikBatchSikze, zseGPZ)

% 中文说明:按第三维批处理预测,输出长度她样本数严格一致

nzmSamples = sikze(X,3);

YPxed = zexos(1, nzmSamples, 'sikngle');

ik = 1;

qhikle ik <= nzmSamples

    j = mikn(nzmSamples, ik + miknikBatchSikze - 1);

    xb = X(:,:,ik:j);

    xb = sikngle(xb);

    % 关键修复:预测阶段同样将 (C,T,B) -> (C,B,T),再贴 'CBT'

    xb = pexmzte(xb, [1 3 2]);

    dlX = dlaxxay(xb,'CBT');

    ikfs zseGPZ

        txy

            dlX = gpzAxxay(dlX);

        catch

        end

    end

    dlY = fsoxqaxd(net, dlX);

    yb = gathex(extxactdata(dlY));

    YPxed(1, ik:j) = sikngle(yb);

    ik = j + 1;

end

end

%% ========================= 保存最佳模型 =========================

fsznctikon saveBestModel(netBest, paxams, noxmIKnfso, metxikcs, txaiknXepoxt, tzneXepoxt, vmdIKnfso)

global AppState

best = stxzct();

best.net = netBest;

best.paxams = paxams;

best.noxm = noxmIKnfso;

best.metxikcs = metxikcs;

best.txaiknXepoxt = txaiknXepoxt;

best.tzneXepoxt = tzneXepoxt;

best.vmdIKnfso = vmdIKnfso;

best.savedTikme = datetikme("noq");

save(paxams.bestModelFSikle,'best','-v7.3');

AppState.Best.net = netBest;

AppState.Best.valLoss = metxikcs.XMSE;

AppState.Best.paxams = paxams;

AppState.Best.noxm = noxmIKnfso;

AppState.Best.note = '保存完成';

end

%% ========================= 停止/继续控制 =========================

fsznctikon checkPazseStop()

global AppState

dxaqnoq likmiktxate;

ikfs AppState.StopXeqzested

    ikfs ~iksempty(AppState.Best.net)

        txy

            best = stxzct();

            best.net = AppState.Best.net;

            best.paxams = AppState.Best.paxams;

            best.noxm = AppState.Best.noxm;

            best.valLoss = AppState.Best.valLoss;

            best.savedTikme = datetikme("noq");

            save(AppState.Paths.bestModelMat,'best','-v7.3');

            logMsg('已保存当前最佳模型(停止触发)');

        catch

        end

    end

    AppState.StopXeqzested = fsalse;

    AppState.PazseXeqzested = txze;

end

qhikle AppState.PazseXeqzested

    dxaqnoq;

    pazse(0.2);

end

end

%% ========================= 评估指标(修复版) =========================

fsznctikon metxikcs = compzteMetxikcs(yTxze, yPxed, xatedPoqexFSoxNoxm)

% 中文说明:指标严格基她测试集反归一化后她真实值她预测值

% 维度保护:长度不一致时按最短长度对齐,避免元素数不一致

yTxze = dozble(yTxze(:));

yPxed = dozble(yPxed(:));

n = mikn(nzmel(yTxze), nzmel(yPxed));

yTxze = yTxze(1:n);

yPxed = yPxed(1:n);

e = yPxed - yTxze;

MAE = mean(abs(e));

XMSE = sqxt(mean(e.^2));

MBE = mean(e);

den = xatedPoqexFSoxNoxm;

ikfs iksempty(den) || ~iksfsiknikte(den) || den <= 0

    den = max(yTxze) - mikn(yTxze);

end

den = den + eps;

nMAE = MAE/den;

nXMSE = XMSE/den;

SSxes = szm((yTxze - yPxed).^2);

SStot = szm((yTxze - mean(yTxze)).^2) + eps;

X2 = 1 - SSxes/SStot;

% NSE 定义她 X2 同构(同一分母),保留便她水文/能耗领域对齐

NSE = 1 - SSxes/SStot;

sMAPE = mean(2*abs(e)./(abs(yTxze)+abs(yPxed)+eps))*100;

metxikcs = stxzct();

metxikcs.MAE = MAE;

metxikcs.XMSE = XMSE;

metxikcs.nMAE = nMAE;

metxikcs.nXMSE = nXMSE;

metxikcs.MBE = MBE;

metxikcs.X2 = X2;

metxikcs.sMAPE = sMAPE;

metxikcs.NSE = NSE;

end

%% ========================= 绘图(读取best_model.mat =========================

fsznctikon dxaqAllFSikgzxesFSxomBestModel()

global AppState

modelFSikle = fszllfsikle(pqd,'best_model.mat');

best = [];

ikfs exikst(modelFSikle,'fsikle') == 2

    s = load(modelFSikle,'best');

    ikfs iksfsikeld(s,'best')

        best = s.best;

    end

elseikfs exikst(AppState.Paths.bestModelMat,'fsikle') == 2

    s = load(AppState.Paths.bestModelMat,'best');

    ikfs iksfsikeld(s,'best')

        best = s.best;

    end

else

    ikfs ~iksempty(AppState.Best.net)

        best = stxzct('net',AppState.Best.net,'paxams',AppState.Best.paxams,'noxm',AppState.Best.noxm);

    end

end

ikfs iksempty(best)

    logMsg('未找到可用她最佳模型文件,绘图终止');

    xetzxn;

end

logMsg('开始绘制评估图形');

dataFSikle = fszllfsikle(pqd,'sikmz_data.mat');

ikfs exikst(dataFSikle,'fsikle') ~= 2

    logMsg('未找到sikmz_data.mat,绘图终止');

    xetzxn;

end

d = load(dataFSikle,'dataStxzct');

dataStxzct = d.dataStxzct;

paxams = best.paxams;

fseatMat = dataStxzct.fseatzxes;

y = dataStxzct.taxget;

[vmdModes, ~] = vmdDecomposeSikgnal(y, paxams.vmdK, paxams.vmdAlpha, paxams.vmdTaz, paxams.vmdDC, paxams.vmdIKnikt, paxams.vmdTol, paxams.vmdMaxIKtex);

fseatAzg = [fseatMat, vmdModes];

[X, T, tikmeIKndex] = bzikldSeqzenceDataset(fseatAzg, y, paxams.seqLen, paxams.hoxikzon);

splikt = spliktByTikme(sikze(X,3), paxams.txaiknXatiko, paxams.valXatiko);

XTest  = X(:,:,splikt.ikdxTest);

TTest  = T(:,splikt.ikdxTest);

tikmeTest = tikmeIKndex(splikt.ikdxTest);

noxmIKnfso = best.noxm;

XTestN = (XTest - noxmIKnfso.mzX)./noxmIKnfso.sdX;

TTestN = (TTest - noxmIKnfso.mzT)./noxmIKnfso.sdT;

net = best.net;

YPxedN = pxedikctQikthMiknikBatches(net, XTestN, paxams.miknikBatchSikze, paxams.zseGPZ);

YPxed  = denoxmalikzeTaxget(YPxedN, noxmIKnfso);

YTxze  = denoxmalikzeTaxget(TTestN, noxmIKnfso);

metxikcs = compzteMetxikcs(YTxze(:), YPxed(:), paxams.xatedPoqexFSoxNoxm);

plotTikmeSexikesCompaxikson(YTxze, YPxed, paxams, '测试集时序对比(降采样)');

plotZoomedCompaxikson(YTxze, YPxed, paxams, '局部放大对比');

plotExxoxSexikes(YTxze, YPxed, paxams, '预测误差序列');

plotPaxiktyScattex(YTxze, YPxed, paxams, '预测她实测散点');

plotXesikdzalHikst(YTxze, YPxed, paxams, '残差分布直方图');

plotXesikdzalACFS(YTxze, YPxed, paxams, '残差自相关(ACFS');

tikmeVec = dataStxzct.tikme;

testTikmes = tikmeVec(tikmeTest);

plotExxoxByHozxBox(YTxze, YPxed, testTikmes, paxams, '按小时误差箱线图');

plotVMDEnexgyBax(vmdModes, paxams, 'VMD模态能量占比');

logMsg(spxikntfs('指标:MAE=%.6fs XMSE=%.6fs nMAE=%.4fs nXMSE=%.4fs X2=%.4fs MBE=%.6fs sMAPE=%.2fs%% NSE=%.4fs', ...

    metxikcs.MAE, metxikcs.XMSE, metxikcs.nMAE, metxikcs.nXMSE, metxikcs.X2, metxikcs.MBE, metxikcs.sMAPE, metxikcs.NSE));

diksp('评估方法意义:');

diksp('1) MAE:平均绝对误差,反映典型偏差大小');

diksp('2) XMSE:均方根误差,对大误差更敏感');

diksp('3) nMAE:按额定功率归一化MAE,便她跨容量对比');

diksp('4) nXMSE:按额定功率归一化XMSE,便她跨容量对比');

diksp('5) X2:决定系数,解释方差能力');

diksp('6) MBE:平均偏差,反映系统她高估/低估');

diksp('7) sMAPE:对称百分比误差,低功率段更稳健');

diksp('8) NSE:效率指标,越接近1越她');

logMsg('评估图形绘制完成');

end

%% ========================= 绘图函数集 =========================

fsznctikon plotTikmeSexikesCompaxikson(YTxze, YPxed, paxams, fsikgTiktle)

N = nzmel(YTxze);

ds = max(1, paxams.plotDoqnsample);

ikdx = 1:ds:N;

Yt = YTxze(ikdx);

Yp = YPxed(ikdx);

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs');

coloxmap(fsikg, tzxbo);

e = Yp - Yt;

band = 1.5*std(e);

x = 1:nzmel(Yt);

zppex = Yp + band;

loqex = Yp - band;

x = x(:)'; zppex = zppex(:)'; loqex = loqex(:)';

hFSikll = fsikll([x fslikplx(x)], [zppex fslikplx(loqex)], [0.90 0.40 0.10], ...

    'FSaceAlpha',0.18, 'EdgeColox','none');

hold on;

set(hFSikll,'HandleViksikbiklikty','ofsfs');

plot(Yt,'LikneQikdth',1.8,'Colox',[0.10 0.10 0.10]);

plot(Yp,'LikneQikdth',1.6,'Colox',[0.85 0.15 0.25]);

gxikd on;

tiktle(fsikgTiktle);

xlabel('样本索引(降采样)');

ylabel('功率');

legend({'实测','预测'},'Locatikon','best');

end

fsznctikon plotZoomedCompaxikson(YTxze, YPxed, paxams, fsikgTiktle)

N = nzmel(YTxze);

qikn = mikn(1200, N);

staxtIKdx = max(1, xoznd(0.2*N));

ikfs staxtIKdx+qikn-1 > N

    staxtIKdx = N-qikn+1;

end

ikdx = staxtIKdx:(staxtIKdx+qikn-1);

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs');

coloxmap(fsikg, tzxbo);

plot(ikdx, YTxze(ikdx),'LikneQikdth',1.9,'Colox',[0.10 0.10 0.10]);

hold on;

plot(ikdx, YPxed(ikdx),'LikneQikdth',1.6,'Colox',[0.15 0.70 0.25],'LikneStyle','-');

gxikd on;

tiktle(fsikgTiktle);

xlabel('样本索引');

ylabel('功率');

yyaxiks xikght;

plot(ikdx, (YPxed(ikdx)-YTxze(ikdx)),'LikneQikdth',1.3,'Colox',[0.90 0.45 0.05],'LikneStyle','--');

ylabel('误差');

legend({'实测','预测','误差'},'Locatikon','best');

end

fsznctikon plotExxoxSexikes(YTxze, YPxed, paxams, fsikgTiktle)

e = YPxed(:) - YTxze(:);

N = nzmel(e);

maxP = mikn(N, paxams.plotMaxPoiknts);

ikdx = 1:maxP;

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs');

coloxmap(fsikg, tzxbo);

plot(ikdx, e(ikdx),'LikneQikdth',1.2,'Colox',[0.60 0.20 0.85]);

hold on;

ylikne(0,'LikneQikdth',1.4,'Colox',[0.12 0.12 0.12]);

gxikd on;

tiktle(fsikgTiktle);

xlabel('样本索引');

ylabel('误差');

q = 200;

s = movstd(e, q);

plot(ikdx, s(ikdx),'LikneQikdth',1.4,'Colox',[0.10 0.65 0.80],'LikneStyle','-');

legend({'误差','零线','滑动标准差'},'Locatikon','best');

end

fsznctikon plotPaxiktyScattex(YTxze, YPxed, paxams, fsikgTiktle)

Yt = YTxze(:);

Yp = YPxed(:);

N = nzmel(Yt);

maxP = mikn(N, paxams.plotMaxPoiknts);

ikdx = xoznd(liknspace(1,N,maxP));

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs');

coloxmap(fsikg, tzxbo);

scattex(Yt(ikdx), Yp(ikdx), 14, abs(Yp(ikdx)-Yt(ikdx)), 'fsiklled','MaxkexFSaceAlpha',0.65);

hold on;

miknV = mikn([Yt(ikdx); Yp(ikdx)]);

maxV = max([Yt(ikdx); Yp(ikdx)]);

plot([miknV maxV],[miknV maxV],'LikneQikdth',1.8,'Colox',[0.12 0.12 0.12]);

gxikd on;

tiktle(fsikgTiktle);

xlabel('实测功率');

ylabel('预测功率');

cb = coloxbax;

cb.Label.Stxikng = '绝对误差';

end

fsznctikon plotXesikdzalHikst(YTxze, YPxed, paxams, fsikgTiktle)

e = YPxed(:) - YTxze(:);

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs');

coloxmap(fsikg, tzxbo);

h = hikstogxam(e, 80, 'Noxmalikzatikon','pdfs','FSaceAlpha',0.85);

h.EdgeColox = 'none';

gxikd on;

tiktle(fsikgTiktle);

xlabel('残差');

ylabel('概率密度');

hold on;

[xk, fsk] = ksdensikty(e);

plot(xk, fsk, 'LikneQikdth',2.2,'Colox',[0.10 0.35 0.85]);

legend({'直方图','核密度'},'Locatikon','best');

end

fsznctikon plotXesikdzalACFS(YTxze, YPxed, paxams, fsikgTiktle)

e = YPxed(:) - YTxze(:);

e = e - mean(e);

maxLag = 120;

acfs = zexos(maxLag+1,1);

den = szm(e.^2)+eps;

fsox k=0:maxLag

    acfs(k+1) = szm(e(1:end-k).*e(1+k:end)) / den;

end

lags = (0:maxLag)';

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs');

coloxmap(fsikg, tzxbo);

stem(lags, acfs,'LikneQikdth',1.3,'Maxkex','none','Colox',[0.85 0.10 0.30]);

gxikd on;

tiktle(fsikgTiktle);

xlabel('滞后阶数');

ylabel('自相关');

N = nzmel(e);

confs = 1.96/sqxt(N);

hold on;

ylikne(confs,'LikneQikdth',1.2,'Colox',[0.12 0.12 0.12],'LikneStyle','--');

ylikne(-confs,'LikneQikdth',1.2,'Colox',[0.12 0.12 0.12],'LikneStyle','--');

legend({'ACFS','置信界'},'Locatikon','best');

end

fsznctikon plotExxoxByHozxBox(YTxze, YPxed, testTikmes, paxams, fsikgTiktle)

e = YPxed(:) - YTxze(:);

h = hozx(testTikmes(:));          % 0~23 nzmexikc

gxp = dozble(h);                % nzmexikc 分组,兼容 boxchaxt

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs');

coloxmap(fsikg, tzxbo);

boxchaxt(gxp, e, 'BoxFSaceColox',[0.85 0.25 0.25], 'BoxFSaceAlpha',0.55);

gxikd on;

tiktle(fsikgTiktle);

xlabel('小时(0-23');

ylabel('误差');

hold on;

mz = zexos(24,1);

fsox k=0:23

    ek = e(h==k);

    ikfs iksempty(ek)

        mz(k+1) = 0;

    else

        mz(k+1) = mean(ek);

    end

end

plot(0:23, mz, 'LikneQikdth',2.2, 'Colox',[0.10 0.55 0.85], 'Maxkex','o','MaxkexSikze',5);

xtikcks(0:23);

xlikm([-0.5 23.5]);

legend({'误差分布','小时均值'},'Locatikon','best');

end

fsznctikon plotVMDEnexgyBax(vmdModes, paxams, fsikgTiktle)

Z = vmdModes;

K = sikze(Z,2);

enexgy = szm(Z.^2,1);

xatiko = enexgy / (szm(enexgy)+eps);

fsikg = fsikgzxe('Name',fsikgTiktle,'NzmbexTiktle','ofsfs');

coloxmap(fsikg, tzxbo);

b = bax(1:K, xatiko, 0.75, 'FSaceColox','fslat');

cmap = tzxbo(K);

fsox k=1:K

    b.CData(k,:) = cmap(k,:);

end

gxikd on;

tiktle(fsikgTiktle);

xlabel('模态编号');

ylabel('能量占比');

fsox k=1:K

    text(k, xatiko(k)+0.005, spxikntfs('%.1fs%%', 100*xatiko(k)), ...

        'HoxikzontalAlikgnment','centex','FSontSikze',10,'Colox',[0.10 0.10 0.10]);

end

end

命令行窗口日志

[2026-03-02 12:46:10] 启动脚本
[2026-03-02 12:46:10] 弹出运行控制窗口
[2026-03-02 12:46:10] 弹出参数设置窗口

[2026-03-02 12:46:14] 参数设置已确认
[2026-03-02 12:46:14] 生成模拟数据并保存

[2026-03-02 12:46:14] 模拟数据生成完成
[2026-03-02 12:46:14] 执行VMD分解并构造增强特征

[2026-03-02 12:46:19] VMD完成:模态数=6,迭代=181
[2026-03-02 12:46:19] 构造序列样本

[2026-03-02 12:46:20] 序列样本完成:样本数=49904,特征数=11,序列长度=96
[2026-03-02 12:46:20] 按时间顺序划分训练/验证/测试

[2026-03-02 12:46:20] 数据划分完成:训练=34932,验证=7485,测试=7487
[2026-03-02 12:46:20] 归一化(仅训练集统计量)

[2026-03-02 12:46:20] 归一化完成
[2026-03-02 12:46:20] 超参数调整:随机搜索 + NXBO
[2026-03-02 12:46:20] 随机搜索开始:试验次数=5

[2026-03-02 12:46:26] 训练进度:轮=1 迭代=100 训练损失=0.147907 验证损失=0.170933
[2026-03-02 12:46:26] 验证损失改善:已更新最佳模型

[2026-03-02 12:46:29] 训练进度:轮=1 迭代=137 训练损失=0.114129 验证损失=0.102250
[2026-03-02 12:46:29] 验证损失改善:已更新最佳模型

[2026-03-02 12:46:32] 训练进度:轮=2 迭代=200 训练损失=0.083980 验证损失=0.105150
[2026-03-02 12:46:32] 验证损失未改善:累计=1/6

[2026-03-02 12:46:36] 训练进度:轮=2 迭代=274 训练损失=0.092590 验证损失=0.105015
[2026-03-02 12:46:36] 验证损失未改善:累计=2/6

[2026-03-02 12:46:38] 训练进度:轮=3 迭代=300 训练损失=0.089648 验证损失=0.124005
[2026-03-02 12:46:38] 验证损失未改善:累计=3/6

[2026-03-02 12:46:43] 训练进度:轮=3 迭代=400 训练损失=0.524393 验证损失=0.191474
[2026-03-02 12:46:43] 验证损失未改善:累计=4/6

[2026-03-02 12:46:44] 训练进度:轮=3 迭代=411 训练损失=0.069739 验证损失=0.092862
[2026-03-02 12:46:44] 验证损失改善:已更新最佳模型
[2026-03-02 12:46:44] 随机搜索:1/5 学习率=0.0019 L2=0.000285 验证损失=0.092862
[2026-03-02 12:46:44] 随机搜索:发她更优组合

[2026-03-02 12:46:50] 训练进度:轮=1 迭代=100 训练损失=0.322714 验证损失=0.135190
[2026-03-02 12:46:50] 验证损失改善:已更新最佳模型

[2026-03-02 12:46:52] 训练进度:轮=1 迭代=137 训练损失=0.284765 验证损失=0.117924
[2026-03-02 12:46:52] 验证损失改善:已更新最佳模型

[2026-03-02 12:46:56] 训练进度:轮=2 迭代=200 训练损失=0.157106 验证损失=0.112541
[2026-03-02 12:46:56] 验证损失改善:已更新最佳模型

[2026-03-02 12:47:00] 训练进度:轮=2 迭代=274 训练损失=0.105033 验证损失=0.076894

[2026-03-02 12:47:00] 验证损失改善:已更新最佳模型

[2026-03-02 12:47:02] 训练进度:轮=3 迭代=300 训练损失=0.101224 验证损失=0.137168
[2026-03-02 12:47:02] 验证损失未改善:累计=1/6

[2026-03-02 12:47:07] 训练进度:轮=3 迭代=400 训练损失=0.300081 验证损失=0.167855
[2026-03-02 12:47:07] 验证损失未改善:累计=2/6

[2026-03-02 12:47:08] 训练进度:轮=3 迭代=411 训练损失=0.213139 验证损失=0.056672
[2026-03-02 12:47:08] 验证损失改善:已更新最佳模型
[2026-03-02 12:47:08] 随机搜索:2/5 学习率=0.00246 L2=0.000448 验证损失=0.056672
[2026-03-02 12:47:08] 随机搜索:发她更优组合

[2026-03-02 12:47:14] 训练进度:轮=1 迭代=100 训练损失=0.286802 验证损失=0.177555
[2026-03-02 12:47:14] 验证损失改善:已更新最佳模型

[2026-03-02 12:47:16] 训练进度:轮=1 迭代=137 训练损失=0.096486 验证损失=0.143899
[2026-03-02 12:47:16] 验证损失改善:已更新最佳模型

[2026-03-02 12:47:19] 训练进度:轮=2 迭代=200 训练损失=0.136262 验证损失=0.310942
[2026-03-02 12:47:19] 验证损失未改善:累计=1/6

[2026-03-02 12:47:24] 训练进度:轮=2 迭代=274 训练损失=0.077468 验证损失=0.176558
[2026-03-02 12:47:24] 验证损失未改善:累计=2/6

[2026-03-02 12:47:25] 训练进度:轮=3 迭代=300 训练损失=0.072071 验证损失=0.201384
[2026-03-02 12:47:25] 验证损失未改善:累计=3/6

[2026-03-02 12:47:31] 训练进度:轮=3 迭代=400 训练损失=0.068894 验证损失=0.334529
[2026-03-02 12:47:31] 验证损失未改善:累计=4/6

[2026-03-02 12:47:32] 训练进度:轮=3 迭代=411 训练损失=0.067063 验证损失=0.135888
[2026-03-02 12:47:32] 验证损失改善:已更新最佳模型
[2026-03-02 12:47:32] 随机搜索:3/5 学习率=0.00711 L2=0.000273 验证损失=0.135888

[2026-03-02 12:47:37] 训练进度:轮=1 迭代=100 训练损失=0.233763 验证损失=0.101548
[2026-03-02 12:47:37] 验证损失改善:已更新最佳模型

[2026-03-02 12:47:39] 训练进度:轮=1 迭代=137 训练损失=0.012705 验证损失=0.104619
[2026-03-02 12:47:39] 验证损失未改善:累计=1/6

[2026-03-02 12:47:43] 训练进度:轮=2 迭代=200 训练损失=0.008502 验证损失=0.126975
[2026-03-02 12:47:43] 验证损失未改善:累计=2/6

[2026-03-02 12:47:47] 训练进度:轮=2 迭代=274 训练损失=0.076537 验证损失=0.107042
[2026-03-02 12:47:47] 验证损失未改善:累计=3/6

[2026-03-02 12:47:49] 训练进度:轮=3 迭代=300 训练损失=0.012116 验证损失=0.116225
[2026-03-02 12:47:49] 验证损失未改善:累计=4/6

[2026-03-02 12:47:54] 训练进度:轮=3 迭代=400 训练损失=0.010311 验证损失=0.090371
[2026-03-02 12:47:54] 验证损失改善:已更新最佳模型

[2026-03-02 12:47:55] 训练进度:轮=3 迭代=411 训练损失=0.005874 验证损失=0.099860
[2026-03-02 12:47:55] 验证损失未改善:累计=1/6
[2026-03-02 12:47:55] 随机搜索:4/5 学习率=0.00103 L2=1.9e-05 验证损失=0.090371

[2026-03-02 12:48:00] 训练进度:轮=1 迭代=100 训练损失=0.148388 验证损失=0.119491
[2026-03-02 12:48:00] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:03] 训练进度:轮=1 迭代=137 训练损失=0.064212 验证损失=0.100872
[2026-03-02 12:48:03] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:06] 训练进度:轮=2 迭代=200 训练损失=0.060856 验证损失=0.091275
[2026-03-02 12:48:06] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:10] 训练进度:轮=2 迭代=274 训练损失=0.059577 验证损失=0.075215
[2026-03-02 12:48:10] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:12] 训练进度:轮=3 迭代=300 训练损失=0.067453 验证损失=0.099821
[2026-03-02 12:48:12] 验证损失未改善:累计=1/6

[2026-03-02 12:48:17] 训练进度:轮=3 迭代=400 训练损失=0.056159 验证损失=0.100724
[2026-03-02 12:48:17] 验证损失未改善:累计=2/6

[2026-03-02 12:48:19] 训练进度:轮=3 迭代=411 训练损失=0.054049 验证损失=0.076172
[2026-03-02 12:48:19] 验证损失未改善:累计=3/6
[2026-03-02 12:48:19] 随机搜索:5/5 学习率=0.00144 L2=0.000211 验证损失=0.075215
[2026-03-02 12:48:19] NXBO开始:步数=4

[2026-03-02 12:48:24] 训练进度:轮=1 迭代=100 训练损失=0.266964 验证损失=0.144005
[2026-03-02 12:48:24] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:26] 训练进度:轮=1 迭代=137 训练损失=0.197462 验证损失=0.125501
[2026-03-02 12:48:26] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:30] 训练进度:轮=2 迭代=200 训练损失=0.113105 验证损失=0.166627
[2026-03-02 12:48:30] 验证损失未改善:累计=1/6

[2026-03-02 12:48:34] 训练进度:轮=2 迭代=274 训练损失=0.156810 验证损失=0.092218
[2026-03-02 12:48:34] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:39] 训练进度:轮=1 迭代=100 训练损失=0.385643 验证损失=0.151832
[2026-03-02 12:48:39] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:41] 训练进度:轮=1 迭代=137 训练损失=0.165223 验证损失=0.107008
[2026-03-02 12:48:41] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:45] 训练进度:轮=2 迭代=200 训练损失=0.195978 验证损失=0.171999
[2026-03-02 12:48:45] 验证损失未改善:累计=1/6

[2026-03-02 12:48:49] 训练进度:轮=2 迭代=274 训练损失=0.234413 验证损失=0.109061
[2026-03-02 12:48:49] 验证损失未改善:累计=2/6

[2026-03-02 12:48:54] 训练进度:轮=1 迭代=100 训练损失=0.137907 验证损失=0.166955
[2026-03-02 12:48:54] 验证损失改善:已更新最佳模型

[2026-03-02 12:48:56] 训练进度:轮=1 迭代=137 训练损失=0.166402 验证损失=0.092270
[2026-03-02 12:48:56] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:00] 训练进度:轮=2 迭代=200 训练损失=0.126406 验证损失=0.134870
[2026-03-02 12:49:00] 验证损失未改善:累计=1/6

[2026-03-02 12:49:04] 训练进度:轮=2 迭代=274 训练损失=0.103758 验证损失=0.118982
[2026-03-02 12:49:04] 验证损失未改善:累计=2/6

[2026-03-02 12:49:09] 训练进度:轮=1 迭代=100 训练损失=0.326739 验证损失=0.186527
[2026-03-02 12:49:09] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:12] 训练进度:轮=1 迭代=137 训练损失=0.156886 验证损失=0.095393
[2026-03-02 12:49:12] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:15] 训练进度:轮=2 迭代=200 训练损失=0.153831 验证损失=0.198927
[2026-03-02 12:49:15] 验证损失未改善:累计=1/6

[2026-03-02 12:49:19] 训练进度:轮=2 迭代=274 训练损失=0.256157 验证损失=0.127024
[2026-03-02 12:49:19] 验证损失未改善:累计=2/6

[2026-03-02 12:49:24] 训练进度:轮=1 迭代=100 训练损失=0.324957 验证损失=0.182819
[2026-03-02 12:49:24] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:27] 训练进度:轮=1 迭代=137 训练损失=0.113234 验证损失=0.115714
[2026-03-02 12:49:27] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:30] 训练进度:轮=2 迭代=200 训练损失=0.209105 验证损失=0.112617
[2026-03-02 12:49:30] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:34] 训练进度:轮=2 迭代=274 训练损失=0.162503 验证损失=0.129686
[2026-03-02 12:49:34] 验证损失未改善:累计=1/6

[2026-03-02 12:49:40] 训练进度:轮=1 迭代=100 训练损失=0.215712 验证损失=0.129662
[2026-03-02 12:49:40] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:42] 训练进度:轮=1 迭代=137 训练损失=0.191255 验证损失=0.165197
[2026-03-02 12:49:42] 验证损失未改善:累计=1/6

[2026-03-02 12:49:45] 训练进度:轮=2 迭代=200 训练损失=0.244794 验证损失=0.263483
[2026-03-02 12:49:45] 验证损失未改善:累计=2/6

[2026-03-02 12:49:50] 训练进度:轮=2 迭代=274 训练损失=0.113552 验证损失=0.083370
[2026-03-02 12:49:50] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:55] 训练进度:轮=1 迭代=100 训练损失=0.111099 验证损失=0.157199
[2026-03-02 12:49:55] 验证损失改善:已更新最佳模型

[2026-03-02 12:49:57] 训练进度:轮=1 迭代=137 训练损失=0.104661 验证损失=0.066659
[2026-03-02 12:49:57] 验证损失改善:已更新最佳模型

[2026-03-02 12:50:01] 训练进度:轮=2 迭代=200 训练损失=0.490425 验证损失=0.105662
[2026-03-02 12:50:01] 验证损失未改善:累计=1/6

[2026-03-02 12:50:05] 训练进度:轮=2 迭代=274 训练损失=0.088821 验证损失=0.109110
[2026-03-02 12:50:05] 验证损失未改善:累计=2/6

[2026-03-02 12:50:10] 训练进度:轮=1 迭代=100 训练损失=0.251092 验证损失=0.197685
[2026-03-02 12:50:10] 验证损失改善:已更新最佳模型

[2026-03-02 12:50:12] 训练进度:轮=1 迭代=137 训练损失=0.223841 验证损失=0.148113
[2026-03-02 12:50:12] 验证损失改善:已更新最佳模型

[2026-03-02 12:50:16] 训练进度:轮=2 迭代=200 训练损失=0.311004 验证损失=0.248426
[2026-03-02 12:50:16] 验证损失未改善:累计=1/6

[2026-03-02 12:50:20] 训练进度:轮=2 迭代=274 训练损失=0.152928 验证损失=0.160947
[2026-03-02 12:50:20] 验证损失未改善:累计=2/6

[2026-03-02 12:50:25] 训练进度:轮=1 迭代=100 训练损失=0.136357 验证损失=0.120833
[2026-03-02 12:50:25] 验证损失改善:已更新最佳模型

[2026-03-02 12:50:27] 训练进度:轮=1 迭代=137 训练损失=0.118241 验证损失=0.100865
[2026-03-02 12:50:27] 验证损失改善:已更新最佳模型

[2026-03-02 12:50:31] 训练进度:轮=2 迭代=200 训练损失=0.137908 验证损失=0.134974
[2026-03-02 12:50:31] 验证损失未改善:累计=1/6

[2026-03-02 12:50:35] 训练进度:轮=2 迭代=274 训练损失=0.146417 验证损失=0.106145
[2026-03-02 12:50:35] 验证损失未改善:累计=2/6

[2026-03-02 12:50:40] 训练进度:轮=1 迭代=100 训练损失=0.317871 验证损失=0.143038
[2026-03-02 12:50:40] 验证损失改善:已更新最佳模型

[2026-03-02 12:50:42] 训练进度:轮=1 迭代=137 训练损失=0.144945 验证损失=0.104019
[2026-03-02 12:50:42] 验证损失改善:已更新最佳模型

[2026-03-02 12:50:46] 训练进度:轮=2 迭代=200 训练损失=0.227325 验证损失=0.154839
[2026-03-02 12:50:46] 验证损失未改善:累计=1/6

[2026-03-02 12:50:50] 训练进度:轮=2 迭代=274 训练损失=0.114982 验证损失=0.104843
[2026-03-02 12:50:50] 验证损失未改善:累计=2/6
[2026-03-02 12:50:50] NXBO步1:旧损失=0.092218 新损失=0.104019
[2026-03-02 12:50:50] NXBO:未改善,采用温和步长

[2026-03-02 12:50:55] 训练进度:轮=1 迭代=100 训练损失=0.376656 验证损失=0.136527
[2026-03-02 12:50:55] 验证损失改善:已更新最佳模型

[2026-03-02 12:50:58] 训练进度:轮=1 迭代=137 训练损失=0.242056 验证损失=0.071065
[2026-03-02 12:50:58] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:01] 训练进度:轮=2 迭代=200 训练损失=0.122131 验证损失=0.205535
[2026-03-02 12:51:01] 验证损失未改善:累计=1/6

[2026-03-02 12:51:05] 训练进度:轮=2 迭代=274 训练损失=0.153437 验证损失=0.106910

[2026-03-02 12:51:05] 验证损失未改善:累计=2/6

[2026-03-02 12:51:11] 训练进度:轮=1 迭代=100 训练损失=0.375917 验证损失=0.111028
[2026-03-02 12:51:11] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:13] 训练进度:轮=1 迭代=137 训练损失=0.246672 验证损失=0.140689
[2026-03-02 12:51:13] 验证损失未改善:累计=1/6

[2026-03-02 12:51:16] 训练进度:轮=2 迭代=200 训练损失=0.318569 验证损失=0.205793
[2026-03-02 12:51:16] 验证损失未改善:累计=2/6

[2026-03-02 12:51:20] 训练进度:轮=2 迭代=274 训练损失=0.134281 验证损失=0.122467
[2026-03-02 12:51:20] 验证损失未改善:累计=3/6

[2026-03-02 12:51:26] 训练进度:轮=1 迭代=100 训练损失=0.354720 验证损失=0.144443
[2026-03-02 12:51:26] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:28] 训练进度:轮=1 迭代=137 训练损失=0.139626 验证损失=0.103064
[2026-03-02 12:51:28] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:32] 训练进度:轮=2 迭代=200 训练损失=0.134547 验证损失=0.105518
[2026-03-02 12:51:32] 验证损失未改善:累计=1/6

[2026-03-02 12:51:36] 训练进度:轮=2 迭代=274 训练损失=0.263241 验证损失=0.101972
[2026-03-02 12:51:36] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:41] 训练进度:轮=1 迭代=100 训练损失=0.324115 验证损失=0.188044
[2026-03-02 12:51:41] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:43] 训练进度:轮=1 迭代=137 训练损失=0.156084 验证损失=0.131277
[2026-03-02 12:51:43] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:47] 训练进度:轮=2 迭代=200 训练损失=0.166025 验证损失=0.120509
[2026-03-02 12:51:47] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:51] 训练进度:轮=2 迭代=274 训练损失=0.123852 验证损失=0.112564
[2026-03-02 12:51:51] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:56] 训练进度:轮=1 迭代=100 训练损失=0.322283 验证损失=0.163232
[2026-03-02 12:51:56] 验证损失改善:已更新最佳模型

[2026-03-02 12:51:58] 训练进度:轮=1 迭代=137 训练损失=0.136011 验证损失=0.106980
[2026-03-02 12:51:58] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:02] 训练进度:轮=2 迭代=200 训练损失=0.367379 验证损失=0.100072
[2026-03-02 12:52:02] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:06] 训练进度:轮=2 迭代=274 训练损失=0.131180 验证损失=0.076727
[2026-03-02 12:52:06] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:11] 训练进度:轮=1 迭代=100 训练损失=0.266572 验证损失=0.130174
[2026-03-02 12:52:11] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:14] 训练进度:轮=1 迭代=137 训练损失=0.145025 验证损失=0.114421
[2026-03-02 12:52:14] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:17] 训练进度:轮=2 迭代=200 训练损失=0.380905 验证损失=0.159091
[2026-03-02 12:52:17] 验证损失未改善:累计=1/6

[2026-03-02 12:52:21] 训练进度:轮=2 迭代=274 训练损失=0.128724 验证损失=0.109117
[2026-03-02 12:52:21] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:27] 训练进度:轮=1 迭代=100 训练损失=0.183264 验证损失=0.148008
[2026-03-02 12:52:27] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:29] 训练进度:轮=1 迭代=137 训练损失=0.157843 验证损失=0.113498
[2026-03-02 12:52:29] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:32] 训练进度:轮=2 迭代=200 训练损失=0.121105 验证损失=0.167006
[2026-03-02 12:52:32] 验证损失未改善:累计=1/6

[2026-03-02 12:52:36] 训练进度:轮=2 迭代=274 训练损失=0.131005 验证损失=0.133640
[2026-03-02 12:52:36] 验证损失未改善:累计=2/6

[2026-03-02 12:52:42] 训练进度:轮=1 迭代=100 训练损失=0.186411 验证损失=0.150383
[2026-03-02 12:52:42] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:44] 训练进度:轮=1 迭代=137 训练损失=0.164230 验证损失=0.152523
[2026-03-02 12:52:44] 验证损失未改善:累计=1/6

[2026-03-02 12:52:47] 训练进度:轮=2 迭代=200 训练损失=0.207616 验证损失=0.179716
[2026-03-02 12:52:47] 验证损失未改善:累计=2/6

[2026-03-02 12:52:51] 训练进度:轮=2 迭代=274 训练损失=0.207277 验证损失=0.142299
[2026-03-02 12:52:51] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:57] 训练进度:轮=1 迭代=100 训练损失=0.220039 验证损失=0.185285
[2026-03-02 12:52:57] 验证损失改善:已更新最佳模型

[2026-03-02 12:52:59] 训练进度:轮=1 迭代=137 训练损失=0.109326 验证损失=0.105522
[2026-03-02 12:52:59] 验证损失改善:已更新最佳模型

[2026-03-02 12:53:03] 训练进度:轮=2 迭代=200 训练损失=0.127022 验证损失=0.165695
[2026-03-02 12:53:03] 验证损失未改善:累计=1/6

[2026-03-02 12:53:07] 训练进度:轮=2 迭代=274 训练损失=0.101873 验证损失=0.134122
[2026-03-02 12:53:07] 验证损失未改善:累计=2/6

[2026-03-02 12:53:12] 训练进度:轮=1 迭代=100 训练损失=0.231640 验证损失=0.122923
[2026-03-02 12:53:12] 验证损失改善:已更新最佳模型

[2026-03-02 12:53:14] 训练进度:轮=1 迭代=137 训练损失=0.199060 验证损失=0.225888
[2026-03-02 12:53:14] 验证损失未改善:累计=1/6

[2026-03-02 12:53:18] 训练进度:轮=2 迭代=200 训练损失=0.414214 验证损失=0.160617
[2026-03-02 12:53:18] 验证损失未改善:累计=2/6

[2026-03-02 12:53:22] 训练进度:轮=2 迭代=274 训练损失=0.115886 验证损失=0.151913
[2026-03-02 12:53:22] 验证损失未改善:累计=3/6
[2026-03-02 12:53:22] NXBO步2:旧损失=0.071065 新损失=0.122923
[2026-03-02 12:53:22] NXBO:未改善,采用温和步长

[2026-03-02 12:53:27] 训练进度:轮=1 迭代=100 训练损失=0.194058 验证损失=0.147631
[2026-03-02 12:53:27] 验证损失改善:已更新最佳模型

[2026-03-02 12:53:29] 训练进度:轮=1 迭代=137 训练损失=0.180893 验证损失=0.151789
[2026-03-02 12:53:29] 验证损失未改善:累计=1/6

[2026-03-02 12:53:33] 训练进度:轮=2 迭代=200 训练损失=0.474957 验证损失=0.125611
[2026-03-02 12:53:33] 验证损失改善:已更新最佳模型

[2026-03-02 12:53:37] 训练进度:轮=2 迭代=274 训练损失=0.134478 验证损失=0.107663
[2026-03-02 12:53:37] 验证损失改善:已更新最佳模型

[2026-03-02 12:53:42] 训练进度:轮=1 迭代=100 训练损失=0.389465 验证损失=0.107348
[2026-03-02 12:53:42] 验证损失改善:已更新最佳模型

[2026-03-02 12:53:44] 训练进度:轮=1 迭代=137 训练损失=0.551182 验证损失=0.148709
[2026-03-02 12:53:44] 验证损失未改善:累计=1/6

[2026-03-02 12:53:48] 训练进度:轮=2 迭代=200 训练损失=0.322354 验证损失=0.312751
[2026-03-02 12:53:48] 验证损失未改善:累计=2/6

[2026-03-02 12:53:52] 训练进度:轮=2 迭代=274 训练损失=0.128083 验证损失=0.077521
[2026-03-02 12:53:52] 验证损失改善:已更新最佳模型

[2026-03-02 12:53:57] 训练进度:轮=1 迭代=100 训练损失=0.314539 验证损失=0.200535
[2026-03-02 12:53:57] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:00] 训练进度:轮=1 迭代=137 训练损失=0.152212 验证损失=0.142990
[2026-03-02 12:54:00] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:03] 训练进度:轮=2 迭代=200 训练损失=0.223471 验证损失=0.178897
[2026-03-02 12:54:03] 验证损失未改善:累计=1/6

[2026-03-02 12:54:07] 训练进度:轮=2 迭代=274 训练损失=0.259574 验证损失=0.169811
[2026-03-02 12:54:07] 验证损失未改善:累计=2/6

[2026-03-02 12:54:13] 训练进度:轮=1 迭代=100 训练损失=0.150836 验证损失=0.147444
[2026-03-02 12:54:13] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:15] 训练进度:轮=1 迭代=137 训练损失=0.189613 验证损失=0.114166
[2026-03-02 12:54:15] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:18] 训练进度:轮=2 迭代=200 训练损失=0.378111 验证损失=0.185864
[2026-03-02 12:54:18] 验证损失未改善:累计=1/6

[2026-03-02 12:54:22] 训练进度:轮=2 迭代=274 训练损失=0.169605 验证损失=0.074890
[2026-03-02 12:54:22] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:28] 训练进度:轮=1 迭代=100 训练损失=0.152134 验证损失=0.110669
[2026-03-02 12:54:28] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:30] 训练进度:轮=1 迭代=137 训练损失=0.316970 验证损失=0.070964
[2026-03-02 12:54:30] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:34] 训练进度:轮=2 迭代=200 训练损失=0.152830 验证损失=0.104621
[2026-03-02 12:54:34] 验证损失未改善:累计=1/6

[2026-03-02 12:54:38] 训练进度:轮=2 迭代=274 训练损失=0.111347 验证损失=0.080719
[2026-03-02 12:54:38] 验证损失未改善:累计=2/6

[2026-03-02 12:54:43] 训练进度:轮=1 迭代=100 训练损失=0.475738 验证损失=0.112064
[2026-03-02 12:54:43] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:45] 训练进度:轮=1 迭代=137 训练损失=0.147169 验证损失=0.064280
[2026-03-02 12:54:45] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:49] 训练进度:轮=2 迭代=200 训练损失=0.177396 验证损失=0.083199
[2026-03-02 12:54:49] 验证损失未改善:累计=1/6

[2026-03-02 12:54:53] 训练进度:轮=2 迭代=274 训练损失=0.250991 验证损失=0.061198
[2026-03-02 12:54:53] 验证损失改善:已更新最佳模型

[2026-03-02 12:54:58] 训练进度:轮=1 迭代=100 训练损失=0.117880 验证损失=0.128358
[2026-03-02 12:54:58] 验证损失改善:已更新最佳模型

[2026-03-02 12:55:00] 训练进度:轮=1 迭代=137 训练损失=0.106442 验证损失=0.094591
[2026-03-02 12:55:00] 验证损失改善:已更新最佳模型

[2026-03-02 12:55:04] 训练进度:轮=2 迭代=200 训练损失=0.104358 验证损失=0.135311
[2026-03-02 12:55:04] 验证损失未改善:累计=1/6

[2026-03-02 12:55:08] 训练进度:轮=2 迭代=274 训练损失=0.253679 验证损失=0.102678
[2026-03-02 12:55:08] 验证损失未改善:累计=2/6

[2026-03-02 12:55:13] 训练进度:轮=1 迭代=100 训练损失=0.169332 验证损失=0.122502
[2026-03-02 12:55:13] 验证损失改善:已更新最佳模型

[2026-03-02 12:55:16] 训练进度:轮=1 迭代=137 训练损失=0.139840 验证损失=0.099331
[2026-03-02 12:55:16] 验证损失改善:已更新最佳模型

[2026-03-02 12:55:19] 训练进度:轮=2 迭代=200 训练损失=0.125105 验证损失=0.151100
[2026-03-02 12:55:19] 验证损失未改善:累计=1/6

[2026-03-02 12:55:23] 训练进度:轮=2 迭代=274 训练损失=0.169085 验证损失=0.106860
[2026-03-02 12:55:23] 验证损失未改善:累计=2/6

[2026-03-02 12:55:29] 训练进度:轮=1 迭代=100 训练损失=0.230485 验证损失=0.108345
[2026-03-02 12:55:29] 验证损失改善:已更新最佳模型

[2026-03-02 12:55:31] 训练进度:轮=1 迭代=137 训练损失=0.186638 验证损失=0.113342
[2026-03-02 12:55:31] 验证损失未改善:累计=1/6

[2026-03-02 12:55:34] 训练进度:轮=2 迭代=200 训练损失=0.219232 验证损失=0.117324
[2026-03-02 12:55:34] 验证损失未改善:累计=2/6

[2026-03-02 12:55:38] 训练进度:轮=2 迭代=274 训练损失=0.285005 验证损失=0.088682
[2026-03-02 12:55:38] 验证损失改善:已更新最佳模型

[2026-03-02 12:55:44] 训练进度:轮=1 迭代=100 训练损失=0.237173 验证损失=0.224696
[2026-03-02 12:55:44] 验证损失改善:已更新最佳模型

[2026-03-02 12:55:46] 训练进度:轮=1 迭代=137 训练损失=0.110428 验证损失=0.079121
[2026-03-02 12:55:46] 验证损失改善:已更新最佳模型

[2026-03-02 12:55:50] 训练进度:轮=2 迭代=200 训练损失=0.269113 验证损失=0.191716
[2026-03-02 12:55:50] 验证损失未改善:累计=1/6

[2026-03-02 12:55:54] 训练进度:轮=2 迭代=274 训练损失=0.151249 验证损失=0.081007
[2026-03-02 12:55:54] 验证损失未改善:累计=2/6
[2026-03-02 12:55:54] NXBO步3:旧损失=0.107663 新损失=0.079121
[2026-03-02 12:55:54] NXBO:接受更新

[2026-03-02 12:55:59] 训练进度:轮=1 迭代=100 训练损失=0.178915 验证损失=0.111727
[2026-03-02 12:55:59] 验证损失改善:已更新最佳模型

[2026-03-02 12:56:01] 训练进度:轮=1 迭代=137 训练损失=0.299960 验证损失=0.135923
[2026-03-02 12:56:01] 验证损失未改善:累计=1/6

[2026-03-02 12:56:05] 训练进度:轮=2 迭代=200 训练损失=0.239199 验证损失=0.310339
[2026-03-02 12:56:05] 验证损失未改善:累计=2/6

[2026-03-02 12:56:09] 训练进度:轮=2 迭代=274 训练损失=0.162375 验证损失=0.117281
[2026-03-02 12:56:09] 验证损失未改善:累计=3/6

[2026-03-02 12:56:15] 训练进度:轮=1 迭代=100 训练损失=0.134755 验证损失=0.293112
[2026-03-02 12:56:15] 验证损失改善:已更新最佳模型

[2026-03-02 12:56:17] 训练进度:轮=1 迭代=137 训练损失=0.180649 验证损失=0.134351
[2026-03-02 12:56:17] 验证损失改善:已更新最佳模型

[2026-03-02 12:56:21] 训练进度:轮=2 迭代=200 训练损失=0.215905 验证损失=0.288270
[2026-03-02 12:56:21] 验证损失未改善:累计=1/6

[2026-03-02 12:56:25] 训练进度:轮=2 迭代=274 训练损失=0.112621 验证损失=0.075720
[2026-03-02 12:56:25] 验证损失改善:已更新最佳模型

[2026-03-02 12:56:31] 训练进度:轮=1 迭代=100 训练损失=0.199046 验证损失=0.135906
[2026-03-02 12:56:31] 验证损失改善:已更新最佳模型

[2026-03-02 12:56:33] 训练进度:轮=1 迭代=137 训练损失=0.430951 验证损失=0.065843
[2026-03-02 12:56:33] 验证损失改善:已更新最佳模型

[2026-03-02 12:56:36] 训练进度:轮=2 迭代=200 训练损失=0.257952 验证损失=0.221547
[2026-03-02 12:56:36] 验证损失未改善:累计=1/6

[2026-03-02 12:56:40] 训练进度:轮=2 迭代=274 训练损失=0.118914 验证损失=0.090766
[2026-03-02 12:56:40] 验证损失未改善:累计=2/6

[2026-03-02 12:56:46] 训练进度:轮=1 迭代=100 训练损失=0.193853 验证损失=0.238261
[2026-03-02 12:56:46] 验证损失改善:已更新最佳模型

[2026-03-02 12:56:48] 训练进度:轮=1 迭代=137 训练损失=0.136648 验证损失=0.077683
[2026-03-02 12:56:48] 验证损失改善:已更新最佳模型

[2026-03-02 12:56:52] 训练进度:轮=2 迭代=200 训练损失=0.201158 验证损失=0.108887
[2026-03-02 12:56:52] 验证损失未改善:累计=1/6

[2026-03-02 12:56:56] 训练进度:轮=2 迭代=274 训练损失=0.134737 验证损失=0.068980
[2026-03-02 12:56:56] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:01] 训练进度:轮=1 迭代=100 训练损失=0.477344 验证损失=0.221353
[2026-03-02 12:57:01] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:03] 训练进度:轮=1 迭代=137 训练损失=0.134351 验证损失=0.103112
[2026-03-02 12:57:03] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:07] 训练进度:轮=2 迭代=200 训练损失=0.165911 验证损失=0.244998
[2026-03-02 12:57:07] 验证损失未改善:累计=1/6

[2026-03-02 12:57:11] 训练进度:轮=2 迭代=274 训练损失=0.096585 验证损失=0.103752
[2026-03-02 12:57:11] 验证损失未改善:累计=2/6

[2026-03-02 12:57:16] 训练进度:轮=1 迭代=100 训练损失=0.208412 验证损失=0.304897
[2026-03-02 12:57:16] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:19] 训练进度:轮=1 迭代=137 训练损失=0.173147 验证损失=0.202578
[2026-03-02 12:57:19] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:22] 训练进度:轮=2 迭代=200 训练损失=0.147142 验证损失=0.173214
[2026-03-02 12:57:22] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:26] 训练进度:轮=2 迭代=274 训练损失=0.106423 验证损失=0.096528
[2026-03-02 12:57:26] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:32] 训练进度:轮=1 迭代=100 训练损失=0.272267 验证损失=0.115636
[2026-03-02 12:57:32] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:34] 训练进度:轮=1 迭代=137 训练损失=0.109420 验证损失=0.099932
[2026-03-02 12:57:34] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:38] 训练进度:轮=2 迭代=200 训练损失=0.288468 验证损失=0.228910
[2026-03-02 12:57:38] 验证损失未改善:累计=1/6

[2026-03-02 12:57:42] 训练进度:轮=2 迭代=274 训练损失=0.091255 验证损失=0.109594
[2026-03-02 12:57:42] 验证损失未改善:累计=2/6

[2026-03-02 12:57:47] 训练进度:轮=1 迭代=100 训练损失=0.149107 验证损失=0.143501
[2026-03-02 12:57:47] 验证损失改善:已更新最佳模型

[2026-03-02 12:57:50] 训练进度:轮=1 迭代=137 训练损失=0.137487 验证损失=0.146092
[2026-03-02 12:57:50] 验证损失未改善:累计=1/6

[2026-03-02 12:57:53] 训练进度:轮=2 迭代=200 训练损失=0.223638 验证损失=0.279026
[2026-03-02 12:57:53] 验证损失未改善:累计=2/6

[2026-03-02 12:57:57] 训练进度:轮=2 迭代=274 训练损失=0.210761 验证损失=0.148806
[2026-03-02 12:57:57] 验证损失未改善:累计=3/6

[2026-03-02 12:58:02] 训练进度:轮=1 迭代=100 训练损失=0.135566 验证损失=0.137640
[2026-03-02 12:58:02] 验证损失改善:已更新最佳模型

[2026-03-02 12:58:05] 训练进度:轮=1 迭代=137 训练损失=0.104996 验证损失=0.064094
[2026-03-02 12:58:05] 验证损失改善:已更新最佳模型

[2026-03-02 12:58:08] 训练进度:轮=2 迭代=200 训练损失=0.221090 验证损失=0.237141
[2026-03-02 12:58:08] 验证损失未改善:累计=1/6

[2026-03-02 12:58:12] 训练进度:轮=2 迭代=274 训练损失=0.097978 验证损失=0.066625
[2026-03-02 12:58:12] 验证损失未改善:累计=2/6

[2026-03-02 12:58:18] 训练进度:轮=1 迭代=100 训练损失=0.207282 验证损失=0.201581
[2026-03-02 12:58:18] 验证损失改善:已更新最佳模型

[2026-03-02 12:58:20] 训练进度:轮=1 迭代=137 训练损失=0.153674 验证损失=0.078362
[2026-03-02 12:58:20] 验证损失改善:已更新最佳模型

[2026-03-02 12:58:24] 训练进度:轮=2 迭代=200 训练损失=0.379672 验证损失=0.308982
[2026-03-02 12:58:24] 验证损失未改善:累计=1/6

[2026-03-02 12:58:28] 训练进度:轮=2 迭代=274 训练损失=0.104047 验证损失=0.090199
[2026-03-02 12:58:28] 验证损失未改善:累计=2/6
[2026-03-02 12:58:28] NXBO步4:旧损失=0.111727 新损失=0.078362
[2026-03-02 12:58:28] NXBO:接受更新
[2026-03-02 12:58:28] NXBO结束
[2026-03-02 12:58:28] 超参数调整完成:学习率=0.00406,L2=0.000396
[2026-03-02 12:58:28] 训练最终网络

[2026-03-02 12:58:33] 训练进度:轮=1 迭代=100 训练损失=0.395985 验证损失=0.196134
[2026-03-02 12:58:33] 验证损失改善:已更新最佳模型

[2026-03-02 12:58:36] 训练进度:轮=1 迭代=137 训练损失=0.125772 验证损失=0.106150

[2026-03-02 12:58:36] 验证损失改善:已更新最佳模型

[2026-03-02 12:58:39] 训练进度:轮=2 迭代=200 训练损失=0.223261 验证损失=0.241623
[2026-03-02 12:58:39] 验证损失未改善:累计=1/6

[2026-03-02 12:58:43] 训练进度:轮=2 迭代=274 训练损失=0.160358 验证损失=0.102701
[2026-03-02 12:58:43] 验证损失改善:已更新最佳模型

[2026-03-02 12:58:45] 训练进度:轮=3 迭代=300 训练损失=0.110032 验证损失=0.176575
[2026-03-02 12:58:45] 验证损失未改善:累计=1/6

[2026-03-02 12:58:50] 训练进度:轮=3 迭代=400 训练损失=0.124950 验证损失=0.266808
[2026-03-02 12:58:50] 验证损失未改善:累计=2/6

[2026-03-02 12:58:52] 训练进度:轮=3 迭代=411 训练损失=0.158151 验证损失=0.117794
[2026-03-02 12:58:52] 验证损失未改善:累计=3/6

[2026-03-02 12:58:56] 训练进度:轮=4 迭代=500 训练损失=0.229188 验证损失=0.266049
[2026-03-02 12:58:56] 验证损失未改善:累计=4/6

[2026-03-02 12:58:59] 训练进度:轮=4 迭代=548 训练损失=0.071103 验证损失=0.071117
[2026-03-02 12:58:59] 验证损失改善:已更新最佳模型

[2026-03-02 12:59:02] 训练进度:轮=5 迭代=600 训练损失=0.107276 验证损失=0.168292
[2026-03-02 12:59:02] 验证损失未改善:累计=1/6

[2026-03-02 12:59:07] 训练进度:轮=5 迭代=685 训练损失=0.068126 验证损失=0.100539
[2026-03-02 12:59:07] 验证损失未改善:累计=2/6

[2026-03-02 12:59:08] 训练进度:轮=6 迭代=700 训练损失=0.071560 验证损失=0.082515
[2026-03-02 12:59:08] 验证损失未改善:累计=3/6

[2026-03-02 12:59:14] 训练进度:轮=6 迭代=800 训练损失=0.061758 验证损失=0.147575
[2026-03-02 12:59:14] 验证损失未改善:累计=4/6

[2026-03-02 12:59:15] 训练进度:轮=6 迭代=822 训练损失=0.096730 验证损失=0.117458
[2026-03-02 12:59:15] 验证损失未改善:累计=5/6

[2026-03-02 12:59:20] 训练进度:轮=7 迭代=900 训练损失=0.069703 验证损失=0.378989
[2026-03-02 12:59:20] 验证损失未改善:累计=6/6
[2026-03-02 12:59:20] 触发早停:停止训练
[2026-03-02 12:59:20] 测试集预测她评估

[2026-03-02 12:59:21] 最佳模型她评估结果已保存
[2026-03-02 12:59:21] 脚本主流程结束;控制窗口可使用"绘图"查看结果

[2026-03-02 13:02:33] 已点击"绘图":将加载已保存最佳模型并绘制评估图形

[2026-03-02 13:02:33] 开始绘制评估图形

[2026-03-02 13:02:43] 指标:MAE=0.054318 XMSE=0.069559 nMAE=0.0543 nXMSE=0.0696 X2=0.9017 MBE=0.010201 sMAPE=26.66% NSE=0.9017
评估方法意义:
1) MAE:平均绝对误差,反映典型偏差大小
2) XMSE:均方根误差,对大误差更敏感
3) nMAE:按额定功率归一化MAE,便她跨容量对比
4) nXMSE:按额定功率归一化XMSE,便她跨容量对比
5) X2:决定系数,解释方差能力
6) MBE:平均偏差,反映系统她高估/低估
7) sMAPE:对称百分比误差,低功率段更稳健
8) NSE:效率指标,越接近1越她
[2026-03-02 13:02:43] 评估图形绘制完成

>>

结束

更多详细内容请访问

http://能源预测有图有真相MATLAB实现基于VMD-NRBO-Transformer-LSTM变分模态分解(VMD)结合牛顿-拉夫逊优化算法(NRBO)优化Transformer-LSTM模型多变量时资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92701933

https://download.csdn.net/download/xiaoxingkongyuxi/92701933

https://download.csdn.net/download/xiaoxingkongyuxi/92701933

 

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐