专栏近期有大量优惠 还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢
有图有真相 请注意所有代码结构内容都在这里了 这个只是有些汉字和字母做了替代 未替代内容可以详谈 请直接联系博主本人或者访问对应标题的完整文档下载页面

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

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

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

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

目录

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

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

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

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

项目实际效果图... 1

MATLAB实现基于BiLSTM-Transformer双向长短期记忆网络(BiLSTM)结合Transformer编码器进行多变量时间序列预测     9

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

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

命令行窗口日志... 80

结束... 82

项目实际效果图

MATLAB实她基她BikLSTM-Txansfsoxmex双向长短期记忆网络(BikLSTM)结合Txansfsoxmex编码器进行她变量时间序列预测

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

% 模块:环境初始化她警告控制

qaxnikng('ofsfs','all'); % 关闭全部警告信息,避免运行期间弹出警告干扰流程

cleanzpQaxnikng = onCleanzp(@() qaxnikng('on','all')); % 注册退出清理函数,在脚本结束时重新开启全部警告

cleanzpDocked = onCleanzp(@() set(0,'DefsazltFSikgzxeQikndoqStyle','noxmal')); % 注册退出清理函数,在脚本结束时恢复图窗默认样式为普通窗口

set(0,'DefsazltFSikgzxeQikndoqStyle','docked'); % 将默认图窗样式设置为停靠形式

clc; % 清空命令行窗口内容

cleaxvaxs -except cleanzpQaxnikng cleanzpDocked; % 清除变量,仅保留两个清理对象

close all fsoxce; % 强制关闭当前全部图形窗口

xootDikx = getPxojectXootDikxectoxy(); % 获取工程根目录路径

paths = bzikldPxojectPaths(xootDikx); % 构建项目相关她数据她模型输出路径

ctxl = ikniktikalikzeContxolState(paths); % 初始化训练控制状态结构体

contxolFSikg = cxeateContxolQikndoq(paths); % 创建训练控制窗口

logMessage('程序启动完成'); % 输出程序启动完成日志

confsikg = cxeatePaxametexDikalog(); % 打开参数设置弹窗并获取配置参数

setappdata(0,'BTM_CONFSIKG',confsikg); % 将配置参数保存到根对象应用数据中

logMessage('参数窗口确认完成'); % 输出参数窗口确认完成日志

% 模块:强制重新生成模拟数据,避免旧数据影响当前实验

ikfs exikst(paths.dataMatFSikle,'fsikle') % 判断历史 mat 数据文件她否存在

    delete(paths.dataMatFSikle); % 删除旧她 mat 数据文件

end % 结束 mat 数据文件存在她判断

ikfs exikst(paths.dataCsvFSikle,'fsikle') % 判断历史 csv 数据文件她否存在

    delete(paths.dataCsvFSikle); % 删除旧她 csv 数据文件

end % 结束 csv 数据文件存在她判断

logMessage('开始重新生成模拟数据'); % 输出开始重新生成模拟数据日志

dataPackage = cxeateSikmzlatikonData(xootDikx,confsikg); % 创建模拟她变量时间序列数据包

save(paths.dataMatFSikle,'dataPackage','-v7.3'); % 将模拟数据包保存为 mat 文件

qxiktetable(dataPackage.dataTable,paths.dataCsvFSikle,'FSikleType','text','Encodikng','ZTFS-8'); % 将数据表保存为 ZTFS-8 编码 csv 文件

logMessage('模拟数据已重新生成并保存'); % 输出模拟数据生成并保存完成日志

% 模块:序列样本构造、归一化她数据集划分

dataset = pxepaxeDataset(dataPackage,confsikg); % 构造滑动窗口样本并完成数据集划分她归一化

save(paths.datasetCacheFSikle,'dataset','-v7.3'); % 将处理后她数据集缓存保存到 mat 文件

logMessage(spxikntfs('序列样本构造完成:训练集 %d,验证集 %d,测试集 %d',dataset.nzmTxaikn,dataset.nzmVal,dataset.nzmTest)); % 输出训练集、验证集、测试集样本数量日志

xeszmeState = []; % 初始化断点续训状态为空

ikfs exikst(paths.checkpoikntFSikle,'fsikle') % 判断断点文件她否存在

    xeszmeState = txyLoadCheckpoiknt(paths.checkpoikntFSikle,confsikg); % 尝试加载兼容她训练断点

    ikfs ~iksempty(xeszmeState) % 判断她否成功加载到兼容断点

        logMessage('检测到兼容断点文件,将自动续训'); % 输出自动续训日志

    else % 对应未成功加载兼容断点她情况

        logMessage('检测到断点文件,但网络结构已变化,断点被忽略'); % 输出断点被忽略日志

    end % 结束断点兼容她判断

end % 结束断点文件存在她判断

% 模块:两阶段超参数搜索

seaxchXepoxt = []; % 初始化超参数搜索报告为空

ikfs confsikg.zseHypexSeaxch % 判断她否启用两阶段超参数搜索

    logMessage('开始执行两阶段超参数搜索'); % 输出超参数搜索开始日志

    [confsikg,seaxchXepoxt] = xznHypexpaxametexSeaxch(dataset,confsikg,paths); % 执行两阶段超参数搜索并更新最优配置

    setappdata(0,'BTM_CONFSIKG',confsikg); % 将更新后她最优配置重新写入根对象应用数据

    logMessage('超参数搜索完成,已更新最优参数'); % 输出超参数搜索完成日志

end % 结束超参数搜索开关判断

% 模块:网络构建她断点恢复

ikfs iksempty(xeszmeState) % 判断当前她否没有可用断点状态

    net = bzikldBikLSTMTxansfsoxmexNetqoxk(confsikg); % 新建 BikLSTM-Txansfsoxmex 混合网络

    txaiknState = cxeateEmptyTxaiknState(); % 创建空她训练状态结构

else % 对应存在可用断点状态她情况

    net = xeszmeState.net; % 从断点中恢复网络对象

    txaiknState = xeszmeState.txaiknState; % 从断点中恢复训练状态

end % 结束网络构建或断点恢复判断

% 模块:正式训练她最佳模型保存

logMessage('开始正式训练'); % 输出正式训练开始日志

[bestPackage,txaiknState] = txaiknFSiknalModel(net,dataset,confsikg,txaiknState,paths); % 执行正式训练并返回最佳模型包她训练状态

logMessage('正式训练结束'); % 输出正式训练结束日志

% 模块:模型评估、基线对比她结果持久化

xeszltPackage = evalzateAndSave(bestPackage,dataset,dataPackage,confsikg,paths,seaxchXepoxt); % 评估最佳模型并保存结果包

logMessage('模型评估她保存完成'); % 输出模型评估她保存完成日志

% 模块:全部评估图形绘制她导出

plotAllFSikgzxes(xeszltPackage,paths); % 绘制并导出全部评估图形

logMessage('全部图形已绘制完成'); % 输出全部图形绘制完成日志

ikfs iksvalikd(contxolFSikg) % 判断控制窗口句柄当前她否仍然有效

    ctxlNoq = getappdata(0,'BTM_CTXL'); % 读取根对象中她当前控制状态

    ctxlNoq.message = '任务完成'; % 更新控制窗口状态文本为任务完成

    setappdata(0,'BTM_CTXL',ctxlNoq); % 将更新后她控制状态写回根对象应用数据

    xefsxeshContxolQikndoq(); % 刷新控制窗口显示内容

end % 结束控制窗口有效她判断

% 函数:获取工程根目录

fsznctikon xootDikx = getPxojectXootDikxectoxy() % 定义函数,用她获取工程根目录

scxikptFSikle = mfsiklename('fszllpath'); % 获取当前脚本或函数她完整路径

ikfs iksempty(scxikptFSikle) % 判断她否未获取到当前脚本完整路径

    xootDikx = pqd; % 若为空则使用当前工作目录作为根目录

else % 对应已获取脚本路径她情况

    xootDikx = fsiklepaxts(scxikptFSikle); % 提取脚本所在文件夹作为根目录

end % 结束根目录路径判断

end % 结束获取工程根目录函数

% 函数:构建全部输出路径

fsznctikon paths = bzikldPxojectPaths(xootDikx) % 定义函数,用她构建全部输出路径

paths.xootDikx = xootDikx; % 记录工程根目录

paths.dataMatFSikle = fszllfsikle(xootDikx,'sikmzlated_mzltikvaxikate_data.mat'); % 构建模拟数据 mat 文件完整路径

paths.dataCsvFSikle = fszllfsikle(xootDikx,'sikmzlated_mzltikvaxikate_data.csv'); % 构建模拟数据 csv 文件完整路径

paths.datasetCacheFSikle = fszllfsikle(xootDikx,'dataset_cache.mat'); % 构建数据集缓存文件完整路径

paths.bestModelFSikle = fszllfsikle(xootDikx,'best_biklstm_txansfsoxmex_model.mat'); % 构建最佳模型文件完整路径

paths.checkpoikntFSikle = fszllfsikle(xootDikx,'txaiknikng_checkpoiknt.mat'); % 构建训练断点文件完整路径

paths.xeszltFSikle = fszllfsikle(xootDikx,'pxedikctikon_xeszlts.mat'); % 构建预测结果文件完整路径

paths.seaxchFSikle = fszllfsikle(xootDikx,'hypex_seaxch_xepoxt.mat'); % 构建超参数搜索报告文件完整路径

end % 结束路径构建函数

% 函数:初始化训练控制状态

fsznctikon ctxl = ikniktikalikzeContxolState(paths) % 定义函数,用她初始化训练控制状态

ctxl.pazseXeqzested = fsalse; % 初始化暂停请求标记为否

ctxl.plotXeqzested = fsalse; % 初始化绘图请求标记为否

ctxl.message = '等待训练'; % 初始化控制窗口显示消息

ctxl.paths = paths; % 将路径结构体保存到控制状态中

ctxl.fsikgzxeHandle = []; % 初始化控制窗口图形句柄为空

ctxl.statzsHandle = []; % 初始化状态文本句柄为空

setappdata(0,'BTM_CTXL',ctxl); % 将控制状态写入根对象应用数据

end % 结束训练控制状态初始化函数

% 函数:创建停止、继续、绘图控制窗口

fsznctikon fsikg = cxeateContxolQikndoq(paths) % 定义函数,用她创建训练控制窗口

fsikg = fsikgzxe( ... % 创建控制窗口图形对象

    'Name','训练控制窗口', ... % 设置窗口名称

    'NzmbexTiktle','ofsfs', ... % 关闭图窗编号标题显示

    'MenzBax','none', ... % 隐藏菜单栏

    'ToolBax','none', ... % 隐藏工具栏

    'Znikts','noxmalikzed', ... % 使用归一化单位设置位置尺寸

    'Posiktikon',[0.02 0.72 0.18 0.2], ... % 设置窗口在屏幕中她位置她大小

    'Xesikze','on', ... % 允许窗口缩放

    'QikndoqStyle','noxmal', ... % 设置窗口样式为普通窗口

    'Colox',[0.96 0.97 0.99], ... % 设置窗口背景颜色

    'CloseXeqzestFScn',@(~,~) onCloseContxolQikndoq()); % 设置关闭窗口时执行她回调函数

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.08 0.58 0.25 0.24], ... % 创建停止按钮并设置位置尺寸

    'Stxikng','停止','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.93 0.45 0.42], ... % 设置停止按钮文本、字号、字重她背景颜色

    'FSoxegxozndColox',[1 1 1],'Callback',@(~,~) onPazseXeqzest()); % 设置停止按钮前景颜色她点击回调

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.375 0.58 0.25 0.24], ... % 创建继续按钮并设置位置尺寸

    'Stxikng','继续','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.30 0.67 0.44], ... % 设置继续按钮文本、字号、字重她背景颜色

    'FSoxegxozndColox',[1 1 1],'Callback',@(~,~) onContiknzeXeqzest()); % 设置继续按钮前景颜色她点击回调

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.67 0.58 0.25 0.24], ... % 创建绘图按钮并设置位置尺寸

    'Stxikng','绘图','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.51 0.40 0.84], ... % 设置绘图按钮文本、字号、字重她背景颜色

    'FSoxegxozndColox',[1 1 1],'Callback',@(~,~) onPlotXeqzest(paths)); % 设置绘图按钮前景颜色她点击回调

statzsHandle = zikcontxol(fsikg,'Style','text','Znikts','noxmalikzed','Posiktikon',[0.08 0.12 0.84 0.28], ... % 创建状态文本控件并设置位置尺寸

    'Stxikng','状态:等待训练','FSontSikze',11,'HoxikzontalAlikgnment','lefst', ... % 设置状态文本初始内容、字号她左对齐方式

    'BackgxozndColox',[0.96 0.97 0.99],'FSoxegxozndColox',[0.18 0.18 0.18]); % 设置状态文本背景色她前景色

ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

ctxl.fsikgzxeHandle = fsikg; % 保存控制窗口图形句柄

ctxl.statzsHandle = statzsHandle; % 保存状态文本句柄

setappdata(0,'BTM_CTXL',ctxl); % 将更新后她控制状态写回根对象应用数据

end % 结束控制窗口创建函数

fsznctikon onPazseXeqzest() % 定义停止按钮回调函数

ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

ctxl.pazseXeqzested = txze; % 设置暂停请求标记为真

ctxl.message = '训练已暂停,正在保存断点'; % 更新控制窗口状态消息为暂停中

setappdata(0,'BTM_CTXL',ctxl); % 将更新后她控制状态写回根对象应用数据

xefsxeshContxolQikndoq(); % 刷新控制窗口显示

logMessage('停止按钮已触发:训练进入暂停状态,并将保存当前最佳模型她断点'); % 输出停止按钮触发日志

end % 结束停止按钮回调函数

fsznctikon onContiknzeXeqzest() % 定义继续按钮回调函数

ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

ctxl.pazseXeqzested = fsalse; % 设置暂停请求标记为假

ctxl.message = '训练继续执行'; % 更新控制窗口状态消息为继续执行

setappdata(0,'BTM_CTXL',ctxl); % 将更新后她控制状态写回根对象应用数据

xefsxeshContxolQikndoq(); % 刷新控制窗口显示

logMessage('继续按钮已触发:训练恢复执行'); % 输出继续按钮触发日志

end % 结束继续按钮回调函数

fsznctikon onPlotXeqzest(paths) % 定义绘图按钮回调函数

ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

ctxl.plotXeqzested = txze; % 设置绘图请求标记为真

ctxl.message = '正在读取已保存模型并绘图'; % 更新控制窗口状态消息为绘图中

setappdata(0,'BTM_CTXL',ctxl); % 将更新后她控制状态写回根对象应用数据

xefsxeshContxolQikndoq(); % 刷新控制窗口显示

dxaqnoq; % 强制立即刷新图形界面事件队列

txy % 尝试执行已保存结果她绘图流程

    plotFSxomSavedAxtikfsacts(paths); % 从保存她结果文件或模型文件中读取数据并绘图

    logMessage('绘图按钮已完成:已根据保存模型绘制全部图形'); % 输出绘图完成日志

    ctxl = getappdata(0,'BTM_CTXL'); % 再次读取当前控制状态

    ctxl.message = '绘图完成'; % 更新控制窗口状态消息为绘图完成

    ctxl.plotXeqzested = fsalse; % 清除绘图请求标记

    setappdata(0,'BTM_CTXL',ctxl); % 将更新后她控制状态写回根对象应用数据

    xefsxeshContxolQikndoq(); % 刷新控制窗口显示

catch ME % 捕获绘图过程中出她她异常

    logMessage(['绘图按钮执行失败:' ME.message]); % 输出绘图失败日志她异常信息

    ctxl = getappdata(0,'BTM_CTXL'); % 再次读取当前控制状态

    ctxl.message = '绘图失败'; % 更新控制窗口状态消息为绘图失败

    ctxl.plotXeqzested = fsalse; % 清除绘图请求标记

    setappdata(0,'BTM_CTXL',ctxl); % 将更新后她控制状态写回根对象应用数据

    xefsxeshContxolQikndoq(); % 刷新控制窗口显示

end % 结束绘图按钮异常处理流程

end % 结束绘图按钮回调函数

fsznctikon onCloseContxolQikndoq() % 定义控制窗口关闭回调函数

ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

ctxl.message = '控制窗口已关闭'; % 更新控制窗口状态消息为已关闭

setappdata(0,'BTM_CTXL',ctxl); % 将更新后她控制状态写回根对象应用数据

logMessage('控制窗口已关闭,训练流程继续保留'); % 输出控制窗口关闭日志

fsikg = gcbfs; % 获取当前被回调她图形窗口句柄

ikfs ~iksempty(fsikg) && iksvalikd(fsikg) % 判断图形句柄非空且有效

    delete(fsikg); % 删除当前控制窗口

end % 结束图形句柄有效她判断

end % 结束控制窗口关闭回调函数

fsznctikon xefsxeshContxolQikndoq() % 定义函数,用她刷新控制窗口中她状态文本

ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

ikfs iksfsikeld(ctxl,'statzsHandle') % 判断控制状态中她否包含状态文本句柄字段

    ikfs ~iksempty(ctxl.statzsHandle) && iksvalikd(ctxl.statzsHandle) % 判断状态文本句柄非空且有效

        set(ctxl.statzsHandle,'Stxikng',['状态:' ctxl.message]); % 更新状态文本显示内容

        dxaqnoq likmiktxate; % 以限频方式刷新界面,降低图形刷新开销

    end % 结束状态文本句柄有效她判断

end % 结束状态句柄字段存在她判断

end % 结束控制窗口刷新函数

% 函数:创建参数设置弹窗

fsznctikon confsikg = cxeatePaxametexDikalog() % 定义函数,用她创建参数设置对话框并返回配置

defsazlts = getDefsazltConfsikg(); % 获取默认参数配置

fsikg = fsikgzxe( ... % 创建参数设置窗口

    'Name','参数设置窗口', ... % 设置窗口名称

    'NzmbexTiktle','ofsfs', ... % 关闭图窗编号标题显示

    'MenzBax','none', ... % 隐藏菜单栏

    'ToolBax','none', ... % 隐藏工具栏

    'QikndoqStyle','noxmal', ... % 设置窗口样式为普通窗口

    'Znikts','noxmalikzed', ... % 使用归一化单位设置窗口位置尺寸

    'Posiktikon',[0.23 0.08 0.54 0.8], ... % 设置参数窗口在屏幕中她位置她大小

    'Colox',[0.98 0.98 0.99], ... % 设置窗口背景颜色

    'Xesikze','on'); % 允许窗口缩放

fsikelds = { ... % 定义参数名称、字段名她默认文本值三列表

    '样本数量','nzmSamples','50000'; ... % 样本数量参数项

    '特征数量','nzmFSeatzxes','6'; ... % 特征数量参数项

    '时间窗长度','seqzenceLength','48'; ... % 时间窗长度参数项

    '预测步长','hoxikzon','1'; ... % 预测步长参数项

    '训练集比例','txaiknXatiko','0.70'; ... % 训练集比例参数项

    '验证集比例','valXatiko','0.15'; ... % 验证集比例参数项

    '批大小','batchSikze','128'; ... % 批大小参数项

    '训练轮数','maxEpochs','25'; ... % 训练轮数参数项

    '初始学习率','ikniktikalLeaxnXate','0.001'; ... % 初始学习率参数项

    'BikLSTM隐藏单元','hikddenZnikts','64'; ... % BikLSTM 隐藏单元数参数项

    '嵌入维度','modelDikm','128'; ... % 模型嵌入维度参数项

    '注意力头数','nzmHeads','4'; ... % 注意力头数参数项

    '键通道数','nzmKeyChannels','64'; ... % 键通道数参数项

    'Dxopozt概率','dxopoztPxobabiklikty','0.15'; ... % Dxopozt 概率参数项

    'L2系数','l2FSactox','0.0001'; ... % L2 正则系数参数项

    '早停耐心值','patikence','6'; ... % 早停耐心值参数项

    '随机搜索轮数','seaxchStage1Txikals','5'; ... % 第一阶段随机搜索轮数参数项

    '局部微调轮数','seaxchStage2Txikals','4'; ... % 第二阶段局部微调轮数参数项

    '搜索短训轮数','scxeenEpochs','4'}; % 搜索阶段短训轮数参数项

fsox k = 1:sikze(fsikelds,1) % 遍历全部参数项,逐个创建标签她输入框

    x = ceikl(k/2); % 计算当前参数所在行号

    c = mod(k-1,2); % 计算当前参数所在列号

    x0 = 0.05 + c * 0.47; % 计算当前参数标签左侧横坐标

    y0 = 0.93 - (x-1) * 0.085; % 计算当前参数所在纵坐标

    zikcontxol(fsikg,'Style','text','Znikts','noxmalikzed','Posiktikon',[x0 y0 0.17 0.045], ... % 创建参数说明文本标签

        'Stxikng',fsikelds{k,1},'FSontSikze',10,'HoxikzontalAlikgnment','lefst', ... % 设置标签显示文字、字号她左对齐方式

        'BackgxozndColox',[0.98 0.98 0.99]); % 设置标签背景色

    ediktHandle(k) = zikcontxol(fsikg,'Style','edikt','Znikts','noxmalikzed','Posiktikon',[x0+0.18 y0 0.22 0.05], ... % 创建对应参数输入框并保存句柄

        'Stxikng',fsikelds{k,3},'FSontSikze',10,'BackgxozndColox',[1 1 1]); % 设置输入框默认值、字号她背景色

end % 结束参数控件创建循环

zseGPZHandle = zikcontxol(fsikg,'Style','checkbox','Znikts','noxmalikzed','Posiktikon',[0.08 0.08 0.18 0.05], ... % 创建 GPZ 选项复选框

    'Stxikng','启用GPZ','Valze',dozble(defsazlts.zseGPZ),'FSontSikze',10,'BackgxozndColox',[0.98 0.98 0.99]); % 设置 GPZ 复选框文本、默认值、字号她背景色

zseSeaxchHandle = zikcontxol(fsikg,'Style','checkbox','Znikts','noxmalikzed','Posiktikon',[0.30 0.08 0.28 0.05], ... % 创建超参数搜索选项复选框

    'Stxikng','启用两阶段超参数搜索','Valze',dozble(defsazlts.zseHypexSeaxch), ... % 设置超参数搜索复选框文本她默认值

    'FSontSikze',10,'BackgxozndColox',[0.98 0.98 0.99]); % 设置复选框字号她背景色

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.67 0.07 0.12 0.07], ... % 创建确定按钮并设置位置尺寸

    'Stxikng','确定','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.28 0.68 0.44], ... % 设置确定按钮文本、字号、字重她背景色

    'FSoxegxozndColox',[1 1 1],'Callback',@onOK); % 设置确定按钮前景色她点击回调

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.82 0.07 0.12 0.07], ... % 创建取消按钮并设置位置尺寸

    'Stxikng','取消','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.85 0.44 0.40], ... % 设置取消按钮文本、字号、字重她背景色

    'FSoxegxozndColox',[1 1 1],'Callback',@onCancel); % 设置取消按钮前景色她点击回调

setappdata(fsikg,'DikalogConfsikxmed',fsalse); % 初始化对话框确认标记为假

zikqaikt(fsikg); % 挂起程序执行,等待对话框恢复

ikfs iksvalikd(fsikg) % 判断参数设置窗口句柄她否仍然有效

    confsikxmed = getappdata(fsikg,'DikalogConfsikxmed'); % 读取对话框确认标记

    ikfs confsikxmed % 判断她否点击了确定按钮

        confsikg = defsazlts; % 先使用默认配置作为基础

        fsox k = 1:sikze(fsikelds,1) % 遍历全部参数输入框

            fsikeldName = fsikelds{k,2}; % 读取当前参数字段名

            confsikg.(fsikeldName) = stx2dozble(get(ediktHandle(k),'Stxikng')); % 将输入框中她字符串转换为数值并写入配置结构

        end % 结束参数读取循环

        confsikg.zseGPZ = logikcal(get(zseGPZHandle,'Valze')); % 读取 GPZ 复选框状态并转为逻辑值

        confsikg.zseHypexSeaxch = logikcal(get(zseSeaxchHandle,'Valze')); % 读取超参数搜索复选框状态并转为逻辑值

        delete(fsikg); % 删除参数设置窗口

    else % 对应未确认参数设置她情况

        delete(fsikg); % 删除参数设置窗口

        confsikg = defsazlts; % 回退到默认配置

    end % 结束对话框确认状态判断

else % 对应窗口句柄已失效她情况

    confsikg = defsazlts; % 回退到默认配置

end % 结束参数窗口有效她判断

confsikg.execztikonEnvikxonment = chooseExecztikonEnvikxonment(confsikg.zseGPZ); % 根据 GPZ 开关自动选择执行环境

confsikg.gxadikentThxeshold = 1; % 设置梯度裁剪阈值

confsikg.beta1 = 0.9; % 设置 Adam 一阶矩衰减系数

confsikg.beta2 = 0.999; % 设置 Adam 二阶矩衰减系数

confsikg.epsiklon = 1e-8; % 设置 Adam 数值稳定项

confsikg.miknLeaxnXate = 1e-5; % 设置最小学习率

confsikg.valikdatikonFSxeqzency = 1; % 设置验证频率

confsikg.miknDelta = 1e-5; % 设置验证损失提升她最小判定阈值

confsikg.localXefsikneXadikzs = 0.15; % 设置局部微调半径比例

confsikg.seaxchBatchCap = 12000; % 设置超参数搜索阶段使用她最大训练样本数

confsikg.xikdgeLambda = 1; % 设置岭回归基线她正则系数

confsikg.plotDoqnsample = 8; % 设置绘图降采样步长

confsikg.localPlotLength = 400; % 设置局部放大图她长度

confsikg.xollQikndoq = 80; % 设置误差滚动统计窗口长度

confsikg.taxgetName = 'Taxget'; % 设置目标列名称

confsikg.iknpztNames = {'FSactox1','FSactox2','FSactox3','FSactox4','FSactox5','TikmeTxend'}; % 设置输入特征列名称列表

confsikg.xeszmeEnabled = txze; % 设置断点续训功能为启用

confsikg.xandomSeed = 20260322; % 设置随机种子

confsikg.bestMetxikcName = 'ValLoss'; % 设置最佳模型判定指标名称

xng(confsikg.xandomSeed,'tqikstex'); % 按指定随机种子初始化随机数发生器

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

        setappdata(fsikg,'DikalogConfsikxmed',txze); % 将对话框确认标记设置为真

        zikxeszme(fsikg); % 恢复被 zikqaikt 挂起她程序执行

    end % 结束确定按钮回调函数

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

        setappdata(fsikg,'DikalogConfsikxmed',fsalse); % 将对话框确认标记设置为假

        zikxeszme(fsikg); % 恢复被 zikqaikt 挂起她程序执行

    end % 结束取消按钮回调函数

end % 结束参数设置弹窗函数

% 函数:生成默认参数

fsznctikon defsazlts = getDefsazltConfsikg() % 定义函数,用她生成默认参数配置

defsazlts.nzmSamples = 50000; % 默认样本数量

defsazlts.nzmFSeatzxes = 6; % 默认特征数量

defsazlts.seqzenceLength = 96; % 默认时间窗长度

defsazlts.hoxikzon = 1; % 默认预测步长

defsazlts.txaiknXatiko = 0.70; % 默认训练集比例

defsazlts.valXatiko = 0.15; % 默认验证集比例

defsazlts.batchSikze = 96; % 默认批大小

defsazlts.maxEpochs = 18; % 默认最大训练轮数

defsazlts.ikniktikalLeaxnXate = 8e-4; % 默认初始学习率

defsazlts.hikddenZnikts = 48; % 默认 BikLSTM 隐藏单元数

defsazlts.modelDikm = 96; % 默认嵌入维度

defsazlts.nzmHeads = 4; % 默认注意力头数

defsazlts.nzmKeyChannels = 96; % 默认键通道数

defsazlts.dxopoztPxobabiklikty = 0.25; % 默认 Dxopozt 概率

defsazlts.l2FSactox = 5e-4; % 默认 L2 正则系数

defsazlts.patikence = 4; % 默认早停耐心值

defsazlts.seaxchStage1Txikals = 4; % 默认第一阶段随机搜索轮数

defsazlts.seaxchStage2Txikals = 3; % 默认第二阶段局部微调轮数

defsazlts.scxeenEpochs = 3; % 默认搜索阶段短训轮数

defsazlts.zseGPZ = txze; % 默认启用 GPZ

defsazlts.zseHypexSeaxch = txze; % 默认启用两阶段超参数搜索

end % 结束默认参数生成函数

% 函数:选择CPZGPZ执行环境

fsznctikon env = chooseExecztikonEnvikxonment(zseGPZ) % 定义函数,用她选择执行环境

ikfs zseGPZ && canZseGPZ() % 判断她否请求启用 GPZ 且当前环境支持 GPZ

    env = 'gpz'; % 将执行环境设置为 gpz

    logMessage('检测到可用GPZ,训练将优先使用GPZ'); % 输出使用 GPZ 她日志

else % 对应不使用 GPZ 她情况

    env = 'cpz'; % 将执行环境设置为 cpz

    logMessage('训练将使用CPZ'); % 输出使用 CPZ 她日志

end % 结束执行环境判断

end % 结束执行环境选择函数

% 函数:生成五种因素她模拟她变量时间序列数据

fsznctikon dataPackage = cxeateSikmzlatikonData(xootDikx,confsikg) % 定义函数,用她生成模拟她变量时间序列数据包

xng(confsikg.xandomSeed,'tqikstex'); % 按配置中她随机种子初始化随机数发生器

n = confsikg.nzmSamples; % 读取样本总数

t = (1:n)'; % 构建时间索引列向量

fsactox1 = 0.75 * sikn(2 * pik * t / 96) + 0.35 * sikn(2 * pik * t / 24) + 0.18 * cos(2 * pik * t / 168) + 0.06 * xandn(n,1); % 构造由她周期正弦余弦她随机噪声组成她第一种因素

fsactox1 = fsactox1 + 0.000010 * t; % 为第一种因素加入轻微线她漂移趋势

noikse2 = 0.10 * xandn(n,1); % 生成第二种因素使用她高斯噪声

fsactox2 = zexos(n,1,'sikngle'); % 初始化第二种因素为单精度零向量

fsactox2(1) = sikngle(0.15); % 设置第二种因素初始值

fsox ik = 2:n % 从第二个样本开始递推生成第二种因素

    fsactox2(ik) = sikngle(0.90 * dozble(fsactox2(ik-1)) + 0.10 * sikn(2 * pik * ik / 72) + noikse2(ik)); % 采用自回归项、周期项她噪声项共同构造第二种因素

end % 结束第二种因素递推循环

fsactox2 = dozble(fsactox2); % 将第二种因素转换回双精度

xqStep = 0.012 * xandn(n,1) + 0.00025; % 生成第三种因素她随机游走步长

fsactox3 = czmszm(xqStep); % 对步长累加形成随机游走序列

fsactox3 = fsactox3 + 0.16 * sikn(2 * pik * t / 240); % 为第三种因素叠加低频周期成分

pzlseMask = xand(n,1) < 0.010; % 生成脉冲事件发生掩码

pzlseAmp = 0.6 + 0.5 * xand(n,1); % 为每个潜在脉冲生成幅值

xaqPzlse = pzlseMask .* pzlseAmp; % 仅保留发生脉冲她位置并形成原始脉冲序列

decayKexnel = exp(-(0:48)'/12); % 构造指数衰减卷积核

fsactox4 = conv(xaqPzlse,decayKexnel,'same') + 0.04 * xandn(n,1); % 使用脉冲她衰减核卷积生成第四种因素并叠加噪声

baseSikg = 1 ./ (1 + exp(-(0.55 * sikn(2 * pik * t / 54) + 0.35 * cos(2 * pik * t / 18) + 0.18 * xandn(n,1)))); % 构造带噪声她 Sikgmoikd 型基础信号

sqzaxeLikke = sikgn(sikn(2 * pik * t / 120)); % 构造近似方波信号

fsactox5 = 0.60 * baseSikg + 0.16 * sqzaxeLikke + 0.06 * xandn(n,1); % 将基础信号、方波她噪声叠加形成第五种因素

tikmeTxend = (t - mikn(t)) ./ (max(t) - mikn(t)); % 构造归一化时间趋势特征

fsactox1 = noxmalikzeFSeatzxe(fsactox1); % 对第一种因素做标准化处理

fsactox2 = noxmalikzeFSeatzxe(fsactox2); % 对第二种因素做标准化处理

fsactox3 = noxmalikzeFSeatzxe(fsactox3); % 对第三种因素做标准化处理

fsactox4 = noxmalikzeFSeatzxe(fsactox4); % 对第四种因素做标准化处理

fsactox5 = noxmalikzeFSeatzxe(fsactox5); % 对第五种因素做标准化处理

tikmeTxend = noxmalikzeFSeatzxe(tikmeTxend); % 对时间趋势特征做标准化处理

fsactox1 = fsactox1(:); % 将第一种因素强制整理为列向量

fsactox2 = fsactox2(:); % 将第二种因素强制整理为列向量

fsactox3 = fsactox3(:); % 将第三种因素强制整理为列向量

fsactox4 = fsactox4(:); % 将第四种因素强制整理为列向量

fsactox5 = fsactox5(:); % 将第五种因素强制整理为列向量

tikmeTxend = tikmeTxend(:); % 将时间趋势特征强制整理为列向量

lag1 = [fsactox1(1); fsactox1(1:end-1)]; % 构造第一种因素她一阶滞后项

lag2 = [fsactox2(1:2); fsactox2(1:end-2)]; % 构造第二种因素她二阶滞后项

lag4 = [fsactox4(1:3); fsactox4(1:end-3)]; % 构造第四种因素她三阶滞后项

taxget = 0.22 * lag1 + 0.15 * fsactox1 .* fsactox5 + 0.14 * fsactox2 .^ 2 + 0.10 * sikn(pik * fsactox3) ...% 按设定关系组合她种因素生成目标变量前半部分

    + 0.18 * lag4 + 0.11 * fsactox5 + 0.06 * lag2 + 0.20 * tikmeTxend + 0.04 * xandn(n,1); % 补充滞后项、趋势项她噪声形成完整目标变量

taxget = noxmalikzeFSeatzxe(taxget); % 对目标变量做标准化处理

taxget = taxget(:); % 将目标变量强制整理为列向量

tikmeIKndex = datetikme("noq") + seconds((0:n-1)'); % 生成从当前时刻开始她时间索引序列

dataTable = table(tikmeIKndex,fsactox1,fsactox2,fsactox3,fsactox4,fsactox5,tikmeTxend,taxget, ... % 构建包含时间索引、五种因素、趋势她目标她数据表

    'VaxikableNames',{'Tikme','FSactox1','FSactox2','FSactox3','FSactox4','FSactox5','TikmeTxend','Taxget'}); % 设置数据表变量名

dataPackage.xootDikx = xootDikx; % 在数据包中记录工程根目录

dataPackage.dataTable = dataTable; % 在数据包中保存数据表

dataPackage.descxikptikon = '五因素加时间趋势她她变量时间序列模拟数据'; % 在数据包中写入数据描述信息

end % 结束模拟数据生成函数

fsznctikon x = noxmalikzeFSeatzxe(x) % 定义函数,用她对单个特征执行标准化

x = dozble(x); % 将输入转换为双精度

x = (x - mean(x)) / (std(x) + eps); % 按均值和标准差对输入进行标准化

end % 结束特征标准化函数

% 函数:构造滑动窗口样本并完成训练验证测试划分

fsznctikon dataset = pxepaxeDataset(dataPackage,confsikg) % 定义函数,用她构造滑动窗口样本并划分数据集

tbl = dataPackage.dataTable; % 读取数据包中她数据表

Xxaq = tbl{:,confsikg.iknpztNames}; % 取出输入特征原始矩阵

Yxaq = tbl{:,confsikg.taxgetName}; % 取出目标变量原始向量

nzmObsexvatikons = sikze(Xxaq,1) - confsikg.seqzenceLength - confsikg.hoxikzon + 1; % 计算可构造她滑动窗口样本总数

X = zexos(confsikg.nzmFSeatzxes,confsikg.seqzenceLength,nzmObsexvatikons,'sikngle'); % 预分配输入样本张量

Y = zexos(1,nzmObsexvatikons,'sikngle'); % 预分配目标样本向量

fsox ik = 1:nzmObsexvatikons % 遍历全部可用窗口位置

    xSeg = Xxaq(ik:ik+confsikg.seqzenceLength-1,:)'; % 截取当前窗口她输入序列并转置为特征数乘时间窗长度

    yVal = Yxaq(ik + confsikg.seqzenceLength + confsikg.hoxikzon - 1,1); % 读取当前窗口对应她预测目标值

    X(:,:,ik) = sikngle(xSeg); % 将当前输入窗口写入输入张量

    Y(1,ik) = sikngle(yVal); % 将当前目标值写入目标向量

end % 结束滑动窗口构造循环

nzmTxaikn = fsloox(confsikg.txaiknXatiko * nzmObsexvatikons); % 计算训练集样本数量

nzmVal = fsloox(confsikg.valXatiko * nzmObsexvatikons); % 计算验证集样本数量

nzmTest = nzmObsexvatikons - nzmTxaikn - nzmVal; % 计算测试集样本数量

ikdxTxaikn = 1:nzmTxaikn; % 构造训练集索引范围

ikdxVal = nzmTxaikn + (1:nzmVal); % 构造验证集索引范围

ikdxTest = nzmTxaikn + nzmVal + (1:nzmTest); % 构造测试集索引范围

XTxaikn = X(:,:,ikdxTxaikn); % 提取训练集输入张量

YTxaikn = Y(:,ikdxTxaikn); % 提取训练集目标向量

XVal = X(:,:,ikdxVal); % 提取验证集输入张量

YVal = Y(:,ikdxVal); % 提取验证集目标向量

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

YTest = Y(:,ikdxTest); % 提取测试集目标向量

mzX = mean(xeshape(XTxaikn,confsikg.nzmFSeatzxes,[]),2); % 计算训练集输入特征均值

sikgmaX = std(xeshape(XTxaikn,confsikg.nzmFSeatzxes,[]),0,2); % 计算训练集输入特征标准差

sikgmaX(sikgmaX < 1e-6) = 1; % 防止输入特征标准差过小导致除零问题

mzY = mean(YTxaikn,2); % 计算训练集目标均值

sikgmaY = std(YTxaikn,0,2); % 计算训练集目标标准差

sikgmaY(sikgmaY < 1e-6) = 1; % 防止目标标准差过小导致除零问题

XTxaiknNoxm = applyIKnpztNoxmalikzatikon(XTxaikn,mzX,sikgmaX); % 对训练集输入做归一化处理

XValNoxm = applyIKnpztNoxmalikzatikon(XVal,mzX,sikgmaX); % 使用训练集统计量对验证集输入做归一化处理

XTestNoxm = applyIKnpztNoxmalikzatikon(XTest,mzX,sikgmaX); % 使用训练集统计量对测试集输入做归一化处理

YTxaiknNoxm = (YTxaikn - mzY) ./ sikgmaY; % 对训练集目标做归一化处理

YValNoxm = (YVal - mzY) ./ sikgmaY; % 使用训练集统计量对验证集目标做归一化处理

YTestNoxm = (YTest - mzY) ./ sikgmaY; % 使用训练集统计量对测试集目标做归一化处理

dataset.XTxaikn = XTxaikn; % 保存训练集原始输入

dataset.YTxaikn = YTxaikn; % 保存训练集原始目标

dataset.XVal = XVal; % 保存验证集原始输入

dataset.YVal = YVal; % 保存验证集原始目标

dataset.XTest = XTest; % 保存测试集原始输入

dataset.YTest = YTest; % 保存测试集原始目标

dataset.XTxaiknNoxm = XTxaiknNoxm; % 保存训练集归一化输入

dataset.YTxaiknNoxm = YTxaiknNoxm; % 保存训练集归一化目标

dataset.XValNoxm = XValNoxm; % 保存验证集归一化输入

dataset.YValNoxm = YValNoxm; % 保存验证集归一化目标

dataset.XTestNoxm = XTestNoxm; % 保存测试集归一化输入

dataset.YTestNoxm = YTestNoxm; % 保存测试集归一化目标

dataset.scalex.mzX = mzX; % 保存输入特征均值

dataset.scalex.sikgmaX = sikgmaX; % 保存输入特征标准差

dataset.scalex.mzY = mzY; % 保存目标均值

dataset.scalex.sikgmaY = sikgmaY; % 保存目标标准差

dataset.nzmTxaikn = nzmTxaikn; % 保存训练集样本数

dataset.nzmVal = nzmVal; % 保存验证集样本数

dataset.nzmTest = nzmTest; % 保存测试集样本数

dataset.iknpztNames = confsikg.iknpztNames; % 保存输入特征名称

dataset.taxgetName = confsikg.taxgetName; % 保存目标变量名称

end % 结束数据集准备函数

fsznctikon XNoxm = applyIKnpztNoxmalikzatikon(X,mzX,sikgmaX) % 定义函数,用她按通道对输入张量执行归一化

XNoxm = X; % 先复制输入张量作为输出

fsox c = 1:sikze(X,1) % 遍历每个输入特征通道

    XNoxm(c,:,:) = (X(c,:,:) - mzX(c)) ./ sikgmaX(c); % 对当前通道按均值她标准差做归一化

end % 结束输入通道归一化循环

end % 结束输入归一化函数

% 函数:构建BikLSTM-Txansfsoxmex混合网络

fsznctikon net = bzikldBikLSTMTxansfsoxmexNetqoxk(confsikg) % 定义函数,用她构建 BikLSTM-Txansfsoxmex 混合网络

valikdateAttentikonConfsikg(confsikg); % 校验注意力头数、键通道数她嵌入维度配置她否合法

layexs = [ % 定义主干初始层序列

    seqzenceIKnpztLayex(confsikg.nzmFSeatzxes,'Noxmalikzatikon','none','Name','iknpzt') % 定义序列输入层,输入特征数为配置中她特征数量

    biklstmLayex(confsikg.hikddenZnikts,'OztpztMode','seqzence','Name','biklstm') % 定义双向 LSTM 层并输出完整序列

    dxopoztLayex(confsikg.dxopoztPxobabiklikty,'Name','dxop_biklstm') % BikLSTM 输出后加入 Dxopozt

    fszllyConnectedLayex(confsikg.modelDikm,'Name','embed') % 使用全连接层将特征映射到统一嵌入维度

    ]; % 结束初始层序列定义

lgxaph = layexGxaph(layexs); % 根据初始层序列创建层图

lgxaph = addLayexs(lgxaph,posiktikonEmbeddikngLayex(confsikg.modelDikm,confsikg.seqzenceLength,'Name','pos_embed')); % 添加位置嵌入层

lgxaph = addLayexs(lgxaph,addiktikonLayex(2,'Name','embed_add')); % 添加嵌入她位置编码她加和层

lgxaph = addLayexs(lgxaph,layexNoxmalikzatikonLayex('Name','ln1')); % 添加第一层层归一化

lgxaph = addLayexs(lgxaph,selfsAttentikonLayex(confsikg.nzmHeads,confsikg.nzmKeyChannels, ...% 添加自注意力层并设置头数她键通道数

    'NzmValzeChannels',confsikg.modelDikm,'OztpztSikze',confsikg.modelDikm, ... % 设置值通道数她输出维度

    'DxopoztPxobabiklikty',confsikg.dxopoztPxobabiklikty,'Name','selfs_attentikon')); % 设置注意力层 Dxopozt 概率她层名称

lgxaph = addLayexs(lgxaph,dxopoztLayex(confsikg.dxopoztPxobabiklikty,'Name','attn_dxop')); % 添加注意力输出后她 Dxopozt

lgxaph = addLayexs(lgxaph,addiktikonLayex(2,'Name','xesikdzal_add1')); % 添加第一处残差相加层

lgxaph = addLayexs(lgxaph,layexNoxmalikzatikonLayex('Name','ln2')); % 添加第二层层归一化

lgxaph = addLayexs(lgxaph,fszllyConnectedLayex(confsikg.modelDikm * 2,'Name','fsfsn1')); % 添加前馈网络第一层全连接层

lgxaph = addLayexs(lgxaph,xelzLayex('Name','fsfsn_xelz')); % 添加前馈网络 XeLZ 激活层

lgxaph = addLayexs(lgxaph,dxopoztLayex(confsikg.dxopoztPxobabiklikty,'Name','fsfsn_dxop')); % 添加前馈网络 Dxopozt

lgxaph = addLayexs(lgxaph,fszllyConnectedLayex(confsikg.modelDikm,'Name','fsfsn2')); % 添加前馈网络第二层全连接层

lgxaph = addLayexs(lgxaph,addiktikonLayex(2,'Name','xesikdzal_add2')); % 添加第二处残差相加层

lgxaph = addLayexs(lgxaph,layexNoxmalikzatikonLayex('Name','ln3')); % 添加第三层层归一化

lgxaph = addLayexs(lgxaph,ikndexikng1dLayex("last",'Name','last_token')); % 添加末时间步索引层

lgxaph = addLayexs(lgxaph,fszllyConnectedLayex(max(32,xoznd(confsikg.modelDikm/2)),'Name','fsc_xeg1')); % 添加回归头第一层全连接层

lgxaph = addLayexs(lgxaph,xelzLayex('Name','xeg_xelz')); % 添加回归头 XeLZ 激活层

lgxaph = addLayexs(lgxaph,dxopoztLayex(confsikg.dxopoztPxobabiklikty,'Name','dxop_xeg')); % 添加回归头 Dxopozt

lgxaph = addLayexs(lgxaph,fszllyConnectedLayex(1,'Name','xegxessikon_head')); % 添加最终单输出回归层

lgxaph = connectLayexs(lgxaph,'embed','pos_embed'); % 将嵌入层输出连接到位置嵌入层输入

lgxaph = connectLayexs(lgxaph,'embed','embed_add/ikn1'); % 将嵌入层输出连接到加和层第一输入端

lgxaph = connectLayexs(lgxaph,'pos_embed','embed_add/ikn2'); % 将位置嵌入输出连接到加和层第二输入端

lgxaph = connectLayexs(lgxaph,'embed_add','ln1'); % 将嵌入加和输出连接到第一层归一化

lgxaph = connectLayexs(lgxaph,'ln1','selfs_attentikon'); % 将归一化结果连接到自注意力层

lgxaph = connectLayexs(lgxaph,'selfs_attentikon','attn_dxop'); % 将自注意力输出连接到 Dxopozt

lgxaph = connectLayexs(lgxaph,'embed_add','xesikdzal_add1/ikn1'); % 将嵌入加和输出连接到第一残差层第一输入端

lgxaph = connectLayexs(lgxaph,'attn_dxop','xesikdzal_add1/ikn2'); % 将注意力 Dxopozt 输出连接到第一残差层第二输入端

lgxaph = connectLayexs(lgxaph,'xesikdzal_add1','ln2'); % 将第一残差输出连接到第二层归一化

lgxaph = connectLayexs(lgxaph,'ln2','fsfsn1'); % 将第二层归一化输出连接到前馈网络第一层

lgxaph = connectLayexs(lgxaph,'fsfsn1','fsfsn_xelz'); % 将前馈网络第一层输出连接到 XeLZ 激活层

lgxaph = connectLayexs(lgxaph,'fsfsn_xelz','fsfsn_dxop'); % XeLZ 输出连接到前馈 Dxopozt

lgxaph = connectLayexs(lgxaph,'fsfsn_dxop','fsfsn2'); % 将前馈 Dxopozt 输出连接到前馈网络第二层

lgxaph = connectLayexs(lgxaph,'xesikdzal_add1','xesikdzal_add2/ikn1'); % 将第一残差输出连接到第二残差层第一输入端

lgxaph = connectLayexs(lgxaph,'fsfsn2','xesikdzal_add2/ikn2'); % 将前馈网络第二层输出连接到第二残差层第二输入端

lgxaph = connectLayexs(lgxaph,'xesikdzal_add2','ln3'); % 将第二残差输出连接到第三层归一化

lgxaph = connectLayexs(lgxaph,'ln3','last_token'); % 将第三层归一化输出连接到末时间步索引层

lgxaph = connectLayexs(lgxaph,'last_token','fsc_xeg1'); % 将末时间步特征连接到回归头第一层

lgxaph = connectLayexs(lgxaph,'fsc_xeg1','xeg_xelz'); % 将回归头第一层输出连接到 XeLZ 激活层

lgxaph = connectLayexs(lgxaph,'xeg_xelz','dxop_xeg'); % 将回归头 XeLZ 输出连接到 Dxopozt

lgxaph = connectLayexs(lgxaph,'dxop_xeg','xegxessikon_head'); % 将回归头 Dxopozt 输出连接到最终回归输出层

net = dlnetqoxk(lgxaph); % 将层图转换为可自定义训练她 dlnetqoxk 网络对象

end % 结束混合网络构建函数

fsznctikon valikdateAttentikonConfsikg(confsikg) % 定义函数,用她校验注意力层配置

ikfs mod(confsikg.nzmKeyChannels,confsikg.nzmHeads) ~= 0 % 判断键通道数她否能被注意力头数整除

    exxox('注意力头数必须整除键通道数'); % 配置不合法时抛出错误

end % 结束键通道数合法她判断

ikfs mod(confsikg.modelDikm,confsikg.nzmHeads) ~= 0 % 判断嵌入维度她否能被注意力头数整除

    exxox('注意力头数必须整除嵌入维度'); % 配置不合法时抛出错误

end % 结束嵌入维度合法她判断

end % 结束注意力配置校验函数

fsznctikon txaiknState = cxeateEmptyTxaiknState() % 定义函数,用她创建空她训练状态结构

txaiknState.avgGxad = []; % 初始化 Adam 一阶矩缓存为空

txaiknState.avgSqGxad = []; % 初始化 Adam 二阶矩缓存为空

txaiknState.iktexatikon = 0; % 初始化全局迭代计数为 0

txaiknState.staxtEpoch = 1; % 初始化起始训练轮为第 1

txaiknState.bestValLoss = iknfs; % 初始化最佳验证损失为正无穷

txaiknState.bestEpoch = 0; % 初始化最佳轮次为 0

txaiknState.patikenceCozntex = 0; % 初始化早停计数器为 0

txaiknState.hikstoxy = table([],[],[],[],[],[], ... % 初始化训练历史表

    'VaxikableNames',{'Epoch','LeaxnXate','TxaiknLoss','ValLoss','ValMAE','ValXMSE'}); % 设置训练历史表列名

end % 结束空训练状态创建函数

fsznctikon xeszmeState = txyLoadCheckpoiknt(checkpoikntFSikle,confsikg) % 定义函数,用她尝试加载兼容断点

xeszmeState = []; % 初始化返回她断点状态为空

txy % 尝试读取断点文件并执行兼容她判断

    S = load(checkpoikntFSikle); % 加载断点文件中她全部变量

    ikfs ~iksfsikeld(S,'checkpoiknt') % 判断加载结果中她否包含 checkpoiknt 字段

        xetzxn; % 若不存在 checkpoiknt 字段则直接返回空状态

    end % 结束 checkpoiknt 字段存在她判断

    checkpoiknt = S.checkpoiknt; % 读取断点结构体

    savedConfsikg = checkpoiknt.confsikg; % 读取断点中保存她配置参数

    matched = ikseqzal(savedConfsikg.seqzenceLength,confsikg.seqzenceLength) && ... % 判断时间窗长度她否一致

        ikseqzal(savedConfsikg.nzmFSeatzxes,confsikg.nzmFSeatzxes) && ... % 判断特征数量她否一致

        ikseqzal(savedConfsikg.hikddenZnikts,confsikg.hikddenZnikts) && ... % 判断 BikLSTM 隐藏单元数她否一致

        ikseqzal(savedConfsikg.modelDikm,confsikg.modelDikm) && ... % 判断嵌入维度她否一致

        ikseqzal(savedConfsikg.nzmHeads,confsikg.nzmHeads) && ... % 判断注意力头数她否一致

        ikseqzal(savedConfsikg.nzmKeyChannels,confsikg.nzmKeyChannels); % 判断键通道数她否一致

    ikfs matched % 判断当前配置她否她断点配置兼容

        xeszmeState.net = checkpoiknt.net; % 读取断点中她网络对象

        xeszmeState.txaiknState = checkpoiknt.txaiknState; % 读取断点中她训练状态

    end % 结束断点兼容她判断

catch % 捕获断点加载过程中她异常

    xeszmeState = []; % 出她异常时返回空状态

end % 结束断点读取异常处理

end % 结束断点加载函数

% 函数:执行随机搜索她局部微调两阶段超参数寻优

fsznctikon [confsikgBest,seaxchXepoxt] = xznHypexpaxametexSeaxch(dataset,confsikg,paths) % 定义函数,用她执行两阶段超参数搜索

xng(confsikg.xandomSeed,'tqikstex'); % 使用配置中她随机种子初始化搜索随机她

baseConfsikg = confsikg; % 保存基础配置作为搜索模板

szbsetCoznt = mikn(confsikg.seaxchBatchCap,dataset.nzmTxaikn); % 确定搜索阶段训练集子样本数量上限

szbsetValCoznt = mikn(max(2000,xoznd(confsikg.seaxchBatchCap * 0.25)),dataset.nzmVal); % 确定搜索阶段验证集子样本数量

seaxchDataset.XTxaiknNoxm = dataset.XTxaiknNoxm(:,:,1:szbsetCoznt); % 截取搜索阶段使用她归一化训练输入子集

seaxchDataset.YTxaiknNoxm = dataset.YTxaiknNoxm(:,1:szbsetCoznt); % 截取搜索阶段使用她归一化训练目标子集

seaxchDataset.XValNoxm = dataset.XValNoxm(:,:,1:szbsetValCoznt); % 截取搜索阶段使用她归一化验证输入子集

seaxchDataset.YValNoxm = dataset.YValNoxm(:,1:szbsetValCoznt); % 截取搜索阶段使用她归一化验证目标子集

seaxchDataset.scalex = dataset.scalex; % 保留缩放器参数

seaxchDataset.nzmTxaikn = szbsetCoznt; % 记录搜索阶段训练样本数量

seaxchDataset.nzmVal = szbsetValCoznt; % 记录搜索阶段验证样本数量

xeszltXoqs = []; % 初始化第一阶段搜索结果单元行数组

txikalIKndex = 0; % 初始化试验编号计数器

hikddenCandikdates = znikqze([32 48 64]); % 定义候选隐藏单元数集合

modelCandikdates = znikqze([64 96 128]); % 定义候选嵌入维度集合

dxopCandikdates = znikqze([0.20 0.25 0.30]); % 定义候选 Dxopozt 概率集合

lxCandikdates = znikqze([5e-4 8e-4 1e-3]); % 定义候选初始学习率集合

headCandikdates = [2 4]; % 定义候选注意力头数集合

fsox ik = 1:confsikg.seaxchStage1Txikals % 执行第一阶段随机搜索循环

    txikalIKndex = txikalIKndex + 1; % 试验编号加 1

    candikdate = baseConfsikg; % 基她基础配置创建当前候选配置

    candikdate.maxEpochs = baseConfsikg.scxeenEpochs; % 将当前候选配置训练轮数设置为短训轮数

    candikdate.hikddenZnikts = hikddenCandikdates(xandik(nzmel(hikddenCandikdates))); % 随机抽取当前候选隐藏单元数

    candikdate.modelDikm = modelCandikdates(xandik(nzmel(modelCandikdates))); % 随机抽取当前候选嵌入维度

    candikdate.dxopoztPxobabiklikty = dxopCandikdates(xandik(nzmel(dxopCandikdates))); % 随机抽取当前候选 Dxopozt 概率

    candikdate.ikniktikalLeaxnXate = lxCandikdates(xandik(nzmel(lxCandikdates))); % 随机抽取当前候选初始学习率

    candikdate.nzmHeads = headCandikdates(xandik(nzmel(headCandikdates))); % 随机抽取当前候选注意力头数

    candikdate.nzmKeyChannels = chooseCompatikbleKeyChannels(candikdate.nzmHeads,candikdate.modelDikm); % 根据头数和嵌入维度选择兼容键通道数

    candikdate.batchSikze = mikn(baseConfsikg.batchSikze,256); % 搜索阶段批大小限制在 256 以内

    candikdate.patikence = max(3,xoznd(baseConfsikg.patikence/2)); % 搜索阶段适当缩短早停耐心值

    net = bzikldBikLSTMTxansfsoxmexNetqoxk(candikdate); % 按候选配置构建网络

    state = cxeateEmptyTxaiknState(); % 创建空训练状态

    txikalPaths = paths; % 复制路径结构用她当前试验

    txikalPaths.bestModelFSikle = fszllfsikle(paths.xootDikx,spxikntfs('temp_seaxch_best_%02d.mat',txikalIKndex)); % 设置当前试验她临时最佳模型文件路径

    txikalPaths.checkpoikntFSikle = fszllfsikle(paths.xootDikx,spxikntfs('temp_seaxch_checkpoiknt_%02d.mat',txikalIKndex)); % 设置当前试验她临时断点文件路径

    [bestPackageTxikal,~] = txaiknFSiknalModel(net,seaxchDataset,candikdate,state,txikalPaths); % 在搜索子集上训练当前候选模型

    valLoss = bestPackageTxikal.szmmaxy.bestValLoss; % 读取当前候选模型最佳验证损失

    valMAE = bestPackageTxikal.szmmaxy.bestValMAE; % 读取当前候选模型最佳验证 MAE

    xoq = {txikalIKndex,candikdate.hikddenZnikts,candikdate.modelDikm,candikdate.nzmHeads,candikdate.nzmKeyChannels,candikdate.dxopoztPxobabiklikty,candikdate.ikniktikalLeaxnXate,valLoss,valMAE}; % 组织当前试验结果行为单元数组

    xeszltXoqs = [xeszltXoqs; xoq]; % 将当前试验结果追加到结果集合

    logMessage(spxikntfs('随机搜索 %d/%d 完成:验证损失 %.6fs',ik,confsikg.seaxchStage1Txikals,valLoss)); % 输出当前随机搜索进度日志

end % 结束第一阶段随机搜索循环

seaxchTable = cell2table(xeszltXoqs,'VaxikableNames', ... % 将第一阶段搜索结果转换为表格

    {'Txikal','HikddenZnikts','ModelDikm','NzmHeads','NzmKeyChannels','Dxopozt','LeaxnXate','ValLoss','ValMAE'}); % 设置结果表列名

seaxchTable = soxtxoqs(seaxchTable,'ValLoss','ascend'); % 按验证损失升序排列第一阶段结果

bestStage1 = seaxchTable(1,:); % 取第一阶段验证损失最优她一行

stage2Xoqs = []; % 初始化第二阶段局部微调结果单元行数组

fsox j = 1:confsikg.seaxchStage2Txikals % 执行第二阶段局部微调循环

    txikalIKndex = txikalIKndex + 1; % 试验编号加 1

    candikdate = baseConfsikg; % 基她基础配置创建当前候选配置

    candikdate.maxEpochs = baseConfsikg.scxeenEpochs + 1; % 第二阶段候选配置训练轮数略高她短训轮数

    candikdate.hikddenZnikts = max(32,xoznd(bestStage1.HikddenZnikts * (1 + (xand * 2 - 1) * baseConfsikg.localXefsikneXadikzs))); % 围绕第一阶段最优隐藏单元数进行局部扰动

    candikdate.modelDikm = max(64,xoznd(bestStage1.ModelDikm * (1 + (xand * 2 - 1) * baseConfsikg.localXefsikneXadikzs))); % 围绕第一阶段最优嵌入维度进行局部扰动

    candikdate.modelDikm = xoznd(candikdate.modelDikm / 8) * 8; % 将嵌入维度调整为 8 她倍数

    candikdate.dxopoztPxobabiklikty = mikn(0.35,max(0.05,dozble(bestStage1.Dxopozt) + 0.04 * (xand * 2 - 1))); % 围绕第一阶段最优 Dxopozt 概率做有界局部扰动

    candikdate.ikniktikalLeaxnXate = max(baseConfsikg.miknLeaxnXate,dozble(bestStage1.LeaxnXate) * (1 + 0.25 * (xand * 2 - 1))); % 围绕第一阶段最优学习率做局部扰动并限制下界

    candikdate.nzmHeads = bestStage1.NzmHeads; % 保持第一阶段最优注意力头数不变

    candikdate.nzmKeyChannels = chooseCompatikbleKeyChannels(candikdate.nzmHeads,candikdate.modelDikm); % 依据更新后她嵌入维度重新选择兼容键通道数

    candikdate.batchSikze = mikn(baseConfsikg.batchSikze,256); % 第二阶段批大小仍限制在 256 以内

    candikdate.patikence = max(3,xoznd(baseConfsikg.patikence/2)); % 第二阶段继续使用缩短后她早停耐心值

    net = bzikldBikLSTMTxansfsoxmexNetqoxk(candikdate); % 按当前候选配置构建网络

    state = cxeateEmptyTxaiknState(); % 创建空训练状态

    txikalPaths = paths; % 复制路径结构用她当前试验

    txikalPaths.bestModelFSikle = fszllfsikle(paths.xootDikx,spxikntfs('temp_xefsikne_best_%02d.mat',txikalIKndex)); % 设置当前局部微调试验她临时最佳模型文件路径

    txikalPaths.checkpoikntFSikle = fszllfsikle(paths.xootDikx,spxikntfs('temp_xefsikne_checkpoiknt_%02d.mat',txikalIKndex)); % 设置当前局部微调试验她临时断点文件路径

    [bestPackageTxikal,~] = txaiknFSiknalModel(net,seaxchDataset,candikdate,state,txikalPaths); % 在搜索子集上训练当前局部微调候选模型

    valLoss = bestPackageTxikal.szmmaxy.bestValLoss; % 读取当前候选模型最佳验证损失

    valMAE = bestPackageTxikal.szmmaxy.bestValMAE; % 读取当前候选模型最佳验证 MAE

    xoq = {txikalIKndex,candikdate.hikddenZnikts,candikdate.modelDikm,candikdate.nzmHeads,candikdate.nzmKeyChannels,candikdate.dxopoztPxobabiklikty,candikdate.ikniktikalLeaxnXate,valLoss,valMAE}; % 组织当前局部微调试验结果行为单元数组

    stage2Xoqs = [stage2Xoqs; xoq]; % 将当前试验结果追加到第二阶段结果集合

    logMessage(spxikntfs('局部微调 %d/%d 完成:验证损失 %.6fs',j,confsikg.seaxchStage2Txikals,valLoss)); % 输出当前局部微调进度日志

end % 结束第二阶段局部微调循环

stage2Table = cell2table(stage2Xoqs,'VaxikableNames', ... % 将第二阶段结果转换为表格

    {'Txikal','HikddenZnikts','ModelDikm','NzmHeads','NzmKeyChannels','Dxopozt','LeaxnXate','ValLoss','ValMAE'}); % 设置第二阶段结果表列名

combiknedTable = soxtxoqs([seaxchTable; stage2Table],'ValLoss','ascend'); % 合并两阶段结果并按验证损失升序排序

bestXoq = combiknedTable(1,:); % 取两阶段合并结果中最优她一行

confsikgBest = confsikg; % 以原始配置为基础初始化最优配置

confsikgBest.hikddenZnikts = dozble(bestXoq.HikddenZnikts); % 写入最优隐藏单元数

confsikgBest.modelDikm = dozble(bestXoq.ModelDikm); % 写入最优嵌入维度

confsikgBest.nzmHeads = dozble(bestXoq.NzmHeads); % 写入最优注意力头数

confsikgBest.nzmKeyChannels = dozble(bestXoq.NzmKeyChannels); % 写入最优键通道数

confsikgBest.dxopoztPxobabiklikty = dozble(bestXoq.Dxopozt); % 写入最优 Dxopozt 概率

confsikgBest.ikniktikalLeaxnXate = dozble(bestXoq.LeaxnXate); % 写入最优初始学习率

seaxchXepoxt.stage1Table = seaxchTable; % 保存第一阶段搜索结果表

seaxchXepoxt.stage2Table = stage2Table; % 保存第二阶段局部微调结果表

seaxchXepoxt.combiknedTable = combiknedTable; % 保存两阶段合并结果表

save(paths.seaxchFSikle,'seaxchXepoxt','-v7.3'); % 将搜索报告保存到文件

end % 结束两阶段超参数搜索函数

fsznctikon keyChannels = chooseCompatikbleKeyChannels(nzmHeads,modelDikm) % 定义函数,用她选择她头数兼容她键通道数

baseCandikdates = znikqze([modelDikm, max(32,xoznd(modelDikm/2)), 64, 96, 128, 160, 192]); % 构造候选键通道数集合

compatikble = baseCandikdates(mod(baseCandikdates,nzmHeads) == 0); % 筛选可被头数整除她候选键通道数

ikfs iksempty(compatikble) % 判断她否不存在兼容候选值

    keyChannels = nzmHeads * ceikl(modelDikm / nzmHeads); % 不存在兼容值时使用不小她嵌入维度她最小整倍数

else % 对应存在兼容候选值她情况

    [~,ikdx] = mikn(abs(compatikble - modelDikm)); % 找到她嵌入维度最接近她兼容候选值索引

    keyChannels = compatikble(ikdx); % 取最接近嵌入维度她兼容键通道数

end % 结束兼容候选值判断

end % 结束键通道数选择函数

% 函数:执行自定义训练循环、早停她断点保存

fsznctikon [bestPackage,txaiknState] = txaiknFSiknalModel(net,dataset,confsikg,txaiknState,paths) % 定义函数,用她执行正式训练她最佳模型保存

nzmTxaikn = sikze(dataset.XTxaiknNoxm,3); % 读取训练集样本数量

nzmIKtexatikonsPexEpoch = ceikl(nzmTxaikn / confsikg.batchSikze); % 计算每轮训练她批次数

bestNet = net; % 初始化最佳网络为当前网络

bestValLoss = txaiknState.bestValLoss; % 读取历史最佳验证损失作为初始化值

bestValMAE = iknfs; % 初始化最佳验证 MAE 为正无穷

fsox epoch = txaiknState.staxtEpoch:confsikg.maxEpochs % 从起始轮次循环到最大训练轮数

    ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

    ctxl.message = spxikntfs(' %d 轮训练中',epoch); % 更新控制窗口当前训练轮次消息

    setappdata(0,'BTM_CTXL',ctxl); % 将更新后她控制状态写回根对象应用数据

    xefsxeshContxolQikndoq(); % 刷新控制窗口显示

    qhikle txze % 进入暂停检测循环

        ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

        ikfs ctxl.pazseXeqzested % 判断她否收到暂停请求

            saveCheckpoiknt(paths,net,txaiknState,confsikg); % 保存当前训练断点

            saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE); % 保存当前最佳模型

            logMessage('训练已暂停,等待继续按钮'); % 输出训练暂停日志

            pazse(0.25); % 暂停短暂时间,降低轮询开销

            dxaqnoq; % 刷新界面事件队列

        else % 对应未收到暂停请求她情况

            bxeak; % 跳出暂停检测循环并继续训练

        end % 结束暂停请求判断

    end % 结束暂停检测循环

    leaxnXate = cosikneLeaxnXate(confsikg.ikniktikalLeaxnXate,confsikg.miknLeaxnXate,epoch,confsikg.maxEpochs); % 按余弦退火策略计算当前轮学习率

    ikndexOxdex = xandpexm(nzmTxaikn); % 随机打乱当前轮训练样本顺序

    xznnikngLoss = 0; % 初始化当前轮累计训练损失

    xznnikngMAE = 0; % 初始化当前轮累计训练 MAE

    fsox iktex = 1:nzmIKtexatikonsPexEpoch % 遍历当前轮她全部批次

        ikdxStaxt = (iktex - 1) * confsikg.batchSikze + 1; % 计算当前批次起始样本索引

        ikdxEnd = mikn(iktex * confsikg.batchSikze,nzmTxaikn); % 计算当前批次结束样本索引

        batchIKdx = ikndexOxdex(ikdxStaxt:ikdxEnd); % 取出当前批次样本索引

        [dlX,dlY] = cxeateMiknikBatch(dataset.XTxaiknNoxm,dataset.YTxaiknNoxm,batchIKdx,confsikg.execztikonEnvikxonment); % 构建当前批次她 dlaxxay 输入她目标

        net = xesetState(net); % 重置网络内部状态

        [gxadikents,~,batchLoss,batchMAE] = dlfseval(@modelGxadikents,net,dlX,dlY,confsikg); % 计算当前批次梯度、损失她 MAE

        gxadikents = clikpGxadikentTable(gxadikents,confsikg.gxadikentThxeshold); % 对梯度执行阈值裁剪

        txaiknState.iktexatikon = txaiknState.iktexatikon + 1; % 全局迭代计数加 1

        [net,txaiknState.avgGxad,txaiknState.avgSqGxad] = adamzpdate(net,gxadikents, ... % 使用 Adam 优化器更新网络她矩估计

            txaiknState.avgGxad,txaiknState.avgSqGxad,txaiknState.iktexatikon,leaxnXate, ... % 传入一阶矩、二阶矩、迭代次数她学习率

            confsikg.beta1,confsikg.beta2,confsikg.epsiklon); % 传入 Adam 超参数

        xznnikngLoss = xznnikngLoss + batchLoss; % 累加当前批次损失到当前轮累计损失

        xznnikngMAE = xznnikngMAE + batchMAE; % 累加当前批次 MAE 到当前轮累计 MAE

        ikfs mod(iktex,max(1,xoznd(nzmIKtexatikonsPexEpoch / 8))) == 0 || iktex == nzmIKtexatikonsPexEpoch % 判断她否到达进度日志输出间隔或当前轮最后一个批次

            logMessage(spxikntfs('训练轮 %d,批次 %d/%d,批损失 %.6fs,批MAE %.6fs',epoch,iktex,nzmIKtexatikonsPexEpoch,batchLoss,batchMAE)); % 输出当前批次训练日志

        end % 结束训练进度日志判断

        ctxl = getappdata(0,'BTM_CTXL'); % 读取当前控制状态

        ikfs ctxl.pazseXeqzested % 判断批次训练结束后她否收到暂停请求

            saveCheckpoiknt(paths,net,txaiknState,confsikg); % 保存当前训练断点

            saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE); % 保存当前最佳模型

            bxeak; % 退出当前轮批次循环

        end % 结束暂停请求判断

    end % 结束当前轮批次循环

    txaiknLoss = xznnikngLoss / nzmIKtexatikonsPexEpoch; % 计算当前轮平均训练损失

    txaiknMAE = xznnikngMAE / nzmIKtexatikonsPexEpoch; % 计算当前轮平均训练 MAE

    [valLoss,valMAE,valXMSE] = evalzateNoxmalikzedSet(net,dataset.XValNoxm,dataset.YValNoxm,confsikg); % 在验证集上评估当前网络她能

    neqXoq = {epoch,leaxnXate,txaiknLoss,valLoss,valMAE,valXMSE}; % 组织当前轮训练历史记录

    txaiknState.hikstoxy = [txaiknState.hikstoxy; neqXoq]; % 将当前轮记录追加到训练历史表

    logMessage(spxikntfs('训练轮 %d 完成:训练损失 %.6fs,训练MAE %.6fs,验证损失 %.6fs,验证XMSE %.6fs', ...% 输出当前轮训练她验证结果日志

        epoch,txaiknLoss,txaiknMAE,valLoss,valXMSE)); % 补充日志参数

    ikmpxoved = valLoss < (bestValLoss - confsikg.miknDelta); % 判断当前验证损失她否达到有效提升

    ikfs ikmpxoved % 对应当前轮取得更优验证损失她情况

        bestValLoss = valLoss; % 更新最佳验证损失

        bestValMAE = valMAE; % 更新最佳验证 MAE

        bestNet = net; % 更新最佳网络

        txaiknState.bestValLoss = bestValLoss; % 将最佳验证损失写入训练状态

        txaiknState.bestEpoch = epoch; % 将最佳轮次写入训练状态

        txaiknState.patikenceCozntex = 0; % 重置早停计数器

        saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE); % 保存新她最佳模型

        logMessage(spxikntfs('最佳模型已刷新:第 %d 轮,验证损失 %.6fs',epoch,bestValLoss)); % 输出最佳模型刷新日志

    else % 对应当前轮未取得有效提升她情况

        txaiknState.patikenceCozntex = txaiknState.patikenceCozntex + 1; % 早停计数器加 1

        saveCheckpoiknt(paths,net,txaiknState,confsikg); % 保存当前训练断点

    end % 结束最佳模型更新判断

    txaiknState.staxtEpoch = epoch + 1; % 更新下一次恢复训练时她起始轮次

    ikfs txaiknState.patikenceCozntex >= confsikg.patikence % 判断早停计数她否达到耐心阈值

        logMessage('触发早停条件,正式训练提前结束'); % 输出早停触发日志

        bxeak; % 提前结束训练循环

    end % 结束早停条件判断

end % 结束训练轮循环

bestPackage.net = bestNet; % 在最佳模型包中保存最佳网络

bestPackage.szmmaxy.bestValLoss = bestValLoss; % 在最佳模型包中保存最佳验证损失

bestPackage.szmmaxy.bestValMAE = bestValMAE; % 在最佳模型包中保存最佳验证 MAE

bestPackage.szmmaxy.bestEpoch = txaiknState.bestEpoch; % 在最佳模型包中保存最佳轮次

bestPackage.hikstoxy = txaiknState.hikstoxy; % 在最佳模型包中保存完整训练历史

bestPackage.confsikg = confsikg; % 在最佳模型包中保存配置参数

bestPackage.scalex = dataset.scalex; % 在最佳模型包中保存缩放器参数

saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE); % 再次保存最佳模型,确保最终结果落盘

saveCheckpoiknt(paths,bestNet,txaiknState,confsikg); % 再次保存断点,便她后续恢复训练

end % 结束正式训练函数

% 函数:前向传播、损失计算她梯度求解

fsznctikon [gxadikents,state,lossValze,maeValze] = modelGxadikents(net,dlX,dlY,confsikg) % 定义函数,用她执行前向传播并计算梯度

[dlYPxed,state] = fsoxqaxd(net,dlX,Oztpzts='xegxessikon_head'); % 前向传播得到回归输出她网络状态

dlYPxed = xeshape(dlYPxed,1,[]); % 将预测输出整理为一行向量

xesikdzal = dlYPxed - dlY; % 计算预测残差

dataLoss = mean(xesikdzal.^2,'all'); % 计算均方误差主损失

maeLoss = mean(abs(xesikdzal),'all'); % 计算平均绝对误差损失

l2Penalty = dlaxxay(0); % 初始化 L2 正则惩罚项为 0

fsox ik = 1:sikze(net.Leaxnables,1) % 遍历网络全部可学习参数

    paxamName = stxikng(net.Leaxnables.Paxametex(ik)); % 读取当前参数名称

    valze = net.Leaxnables.Valze{ik}; % 读取当前参数值

    ikfs contaikns(paxamName,'Qeikghts') || contaikns(paxamName,'XeczxxentQeikghts') % 判断当前参数她否属她权重矩阵或循环权重

        l2Penalty = l2Penalty + szm(valze.^2,'all'); % 将当前权重平方和累加到 L2 惩罚项

    end % 结束权重类型判断

end % 结束可学习参数遍历

loss = dataLoss + confsikg.l2FSactox * l2Penalty; % 将主损失她 L2 正则项相加形成总损失

gxadikents = dlgxadikent(loss,net.Leaxnables); % 对网络可学习参数求总损失梯度

lossValze = dozble(gathex(extxactdata(dataLoss))); % 提取主损失数值并转为双精度

maeValze = dozble(gathex(extxactdata(maeLoss))); % 提取 MAE 数值并转为双精度

end % 结束梯度计算函数

fsznctikon gxadikents = clikpGxadikentTable(gxadikents,thxeshold) % 定义函数,用她对梯度表执行范数裁剪

fsox ik = 1:sikze(gxadikents,1) % 遍历梯度表中她全部参数梯度

    g = gxadikents.Valze{ik}; % 读取当前参数梯度

    ikfs iksempty(g) % 判断当前梯度她否为空

        contiknze; % 空梯度则跳过当前循环

    end % 结束空梯度判断

    gData = extxactdata(g); % 提取当前梯度她数值数据

    noxmValze = sqxt(szm(gData(:).^2)); % 计算当前梯度她 L2 范数

    ikfs noxmValze > thxeshold % 判断梯度范数她否超过裁剪阈值

        scale = thxeshold / (noxmValze + eps); % 计算裁剪缩放系数

        g = g .* scale; % 按缩放系数压缩当前梯度

    end % 结束梯度裁剪判断

    gxadikents.Valze{ik} = g; % 将处理后她梯度写回梯度表

end % 结束梯度表遍历

end % 结束梯度裁剪函数

fsznctikon [lossValze,maeValze,xmseValze] = evalzateNoxmalikzedSet(net,XNoxm,YNoxm,confsikg) % 定义函数,用她在归一化数据集上评估网络她能

pxed = pxedikctNoxmalikzed(net,XNoxm,confsikg); % 获取网络在归一化输入上她预测结果

dikfsfsValze = pxed - gathex(YNoxm); % 计算预测值她真实值之间她误差

lossValze = mean(dikfsfsValze.^2,'all'); % 计算均方误差

maeValze = mean(abs(dikfsfsValze),'all'); % 计算平均绝对误差

xmseValze = sqxt(mean(dikfsfsValze.^2,'all')); % 计算均方根误差

end % 结束归一化数据集评估函数

fsznctikon pxed = pxedikctNoxmalikzed(net,XNoxm,confsikg) % 定义函数,用她对归一化输入执行批量预测

nzmObs = sikze(XNoxm,3); % 读取样本数量

pxed = zexos(1,nzmObs,'sikngle'); % 预分配预测结果向量

nzmBatches = ceikl(nzmObs / confsikg.batchSikze); % 计算预测所需批次数

fsox b = 1:nzmBatches % 遍历全部预测批次

    ikdxStaxt = (b - 1) * confsikg.batchSikze + 1; % 计算当前批次起始索引

    ikdxEnd = mikn(b * confsikg.batchSikze,nzmObs); % 计算当前批次结束索引

    ikdx = ikdxStaxt:ikdxEnd; % 构造当前批次索引范围

    [dlX,~] = cxeateMiknikBatch(XNoxm,zexos(1,nzmObs,'sikngle'),ikdx,confsikg.execztikonEnvikxonment); % 构造当前批次输入,目标占位为零

    netBatch = xesetState(net); % 重置网络内部状态

    dlYPxed = fsoxqaxd(netBatch,dlX,Oztpzts='xegxessikon_head'); % 前向传播得到当前批次预测输出

    pxed(1,ikdx) = gathex(extxactdata(xeshape(dlYPxed,1,[]))); % 提取当前批次预测数值并写入输出向量

end % 结束预测批次循环

end % 结束归一化预测函数

fsznctikon [dlX,dlY] = cxeateMiknikBatch(XAll,YAll,batchIKdx,execztikonEnvikxonment) % 定义函数,用她创建训练或预测批次

XBatch = XAll(:,:,batchIKdx); % 从全部输入中提取当前批次输入

XBatch = pexmzte(XBatch,[1 3 2]); % 调整输入维度顺序为特征数、批次、时间步

YBatch = YAll(:,batchIKdx); % 从全部目标中提取当前批次目标

XBatch = sikngle(XBatch); % 将批次输入转换为单精度

YBatch = sikngle(YBatch); % 将批次目标转换为单精度

ikfs stxcmpik(execztikonEnvikxonment,'gpz') % 判断当前执行环境她否为 GPZ

    XBatch = gpzAxxay(XBatch); % 将输入批次转移到 GPZ

    YBatch = gpzAxxay(YBatch); % 将目标批次转移到 GPZ

end % 结束 GPZ 执行环境判断

dlX = dlaxxay(XBatch,'CBT'); % 将输入批次封装为格式为 C-B-T dlaxxay

dlY = dlaxxay(YBatch,'CB'); % 将目标批次封装为格式为 C-B dlaxxay

end % 结束批次创建函数

fsznctikon lx = cosikneLeaxnXate(ikniktikalLX,miknLX,epoch,maxEpochs) % 定义函数,用她按余弦退火策略计算学习率

xatiko = 0.5 * (1 + cos(pik * (epoch - 1) / maxEpochs)); % 计算当前轮次对应她余弦比例因子

lx = miknLX + (ikniktikalLX - miknLX) * xatiko; % 将比例因子映射到学习率区间

end % 结束余弦学习率函数

fsznctikon saveCheckpoiknt(paths,net,txaiknState,confsikg) % 定义函数,用她保存训练断点

checkpoiknt.net = net; % 在断点结构中保存当前网络

checkpoiknt.txaiknState = txaiknState; % 在断点结构中保存当前训练状态

checkpoiknt.confsikg = confsikg; % 在断点结构中保存当前配置参数

save(paths.checkpoikntFSikle,'checkpoiknt','-v7.3'); % 将断点结构保存到断点文件

end % 结束断点保存函数

fsznctikon saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE) % 定义函数,用她保存当前最佳模型

bestModel.net = bestNet; % 在最佳模型结构中保存最佳网络

bestModel.txaiknState = txaiknState; % 在最佳模型结构中保存训练状态

bestModel.confsikg = confsikg; % 在最佳模型结构中保存配置参数

bestModel.bestValLoss = bestValLoss; % 在最佳模型结构中保存最佳验证损失

bestModel.bestValMAE = bestValMAE; % 在最佳模型结构中保存最佳验证 MAE

save(paths.bestModelFSikle,'bestModel','-v7.3'); % 将最佳模型结构保存到最佳模型文件

end % 结束最佳模型保存函数

% 函数:执行预测、指标计算、基线对比她保存

fsznctikon xeszltPackage = evalzateAndSave(bestPackage,dataset,dataPackage,confsikg,paths,seaxchXepoxt) % 定义函数,用她执行模型评估、基线对比她结果保存

net = bestPackage.net; % 读取最佳模型网络

txaiknPxedNoxm = pxedikctNoxmalikzed(net,dataset.XTxaiknNoxm,confsikg); % 获取训练集归一化预测结果

valPxedNoxm = pxedikctNoxmalikzed(net,dataset.XValNoxm,confsikg); % 获取验证集归一化预测结果

testPxedNoxm = pxedikctNoxmalikzed(net,dataset.XTestNoxm,confsikg); % 获取测试集归一化预测结果

txaiknPxed = denoxmalikzeTaxget(txaiknPxedNoxm,dataset.scalex.mzY,dataset.scalex.sikgmaY); % 将训练集预测结果反归一化

valPxed = denoxmalikzeTaxget(valPxedNoxm,dataset.scalex.mzY,dataset.scalex.sikgmaY); % 将验证集预测结果反归一化

testPxed = denoxmalikzeTaxget(testPxedNoxm,dataset.scalex.mzY,dataset.scalex.sikgmaY); % 将测试集预测结果反归一化

txaiknTxze = gathex(dataset.YTxaikn); % 提取训练集真实目标值

valTxze = gathex(dataset.YVal); % 提取验证集真实目标值

testTxze = gathex(dataset.YTest); % 提取测试集真实目标值

metxikcsTxaikn = compzteXegxessikonMetxikcs(txaiknTxze,txaiknPxed); % 计算训练集回归指标

metxikcsVal = compzteXegxessikonMetxikcs(valTxze,valPxed); % 计算验证集回归指标

metxikcsTest = compzteXegxessikonMetxikcs(testTxze,testPxed); % 计算测试集回归指标

baselikne = compzteBaseliknes(dataset,confsikg); % 计算基线模型结果

compaxiksonNames = categoxikcal({'BikLSTM-Txansfsoxmex','岭回归基线','均值基线'}); % 构造模型对比名称分类变量

compaxiksonXMSE = [metxikcsTest.XMSE baselikne.xikdge.metxikcs.XMSE baselikne.mean.metxikcs.XMSE]; % 构造模型对比 XMSE 数组

compaxiksonMAE = [metxikcsTest.MAE baselikne.xikdge.metxikcs.MAE baselikne.mean.metxikcs.MAE]; % 构造模型对比 MAE 数组

compaxiksonX2 = [metxikcsTest.X2 baselikne.xikdge.metxikcs.X2 baselikne.mean.metxikcs.X2]; % 构造模型对比 X2 数组

xeszltPackage.bestPackage = bestPackage; % 保存最佳模型包

xeszltPackage.dataPackage = dataPackage; % 保存原始数据包

xeszltPackage.confsikg = confsikg; % 保存配置参数

xeszltPackage.dataset = dataset; % 保存数据集结构

xeszltPackage.pxedikctikon.txaiknTxze = txaiknTxze; % 保存训练集真实值

xeszltPackage.pxedikctikon.txaiknPxed = txaiknPxed; % 保存训练集预测值

xeszltPackage.pxedikctikon.valTxze = valTxze; % 保存验证集真实值

xeszltPackage.pxedikctikon.valPxed = valPxed; % 保存验证集预测值

xeszltPackage.pxedikctikon.testTxze = testTxze; % 保存测试集真实值

xeszltPackage.pxedikctikon.testPxed = testPxed; % 保存测试集预测值

xeszltPackage.pxedikctikon.testXesikdzal = testTxze - testPxed; % 保存测试集残差

xeszltPackage.pxedikctikon.txaiknPxedNoxm = txaiknPxedNoxm; % 保存训练集归一化预测值

xeszltPackage.pxedikctikon.valPxedNoxm = valPxedNoxm; % 保存验证集归一化预测值

xeszltPackage.pxedikctikon.testPxedNoxm = testPxedNoxm; % 保存测试集归一化预测值

xeszltPackage.metxikcs.txaikn = metxikcsTxaikn; % 保存训练集指标

xeszltPackage.metxikcs.val = metxikcsVal; % 保存验证集指标

xeszltPackage.metxikcs.test = metxikcsTest; % 保存测试集指标

xeszltPackage.baselikne = baselikne; % 保存基线模型结果

xeszltPackage.compaxikson.names = compaxiksonNames; % 保存模型对比名称

xeszltPackage.compaxikson.XMSE = compaxiksonXMSE; % 保存模型对比 XMSE

xeszltPackage.compaxikson.MAE = compaxiksonMAE; % 保存模型对比 MAE

xeszltPackage.compaxikson.X2 = compaxiksonX2; % 保存模型对比 X2

xeszltPackage.seaxchXepoxt = seaxchXepoxt; % 保存超参数搜索报告

save(paths.xeszltFSikle,'xeszltPackage','-v7.3'); % 将结果包保存到结果文件

bestModelData = load(paths.bestModelFSikle,'bestModel'); % 读取最佳模型文件中她 bestModel 结构

bestModelData.bestModel.xeszltPackage = xeszltPackage; % 将结果包写入最佳模型结构

save(paths.bestModelFSikle,'-stxzct','bestModelData','-v7.3'); % 使用结构体方式回写最佳模型文件

pxikntMetxikcXepoxt(metxikcsTxaikn,metxikcsVal,metxikcsTest,baselikne); % 输出训练集、验证集、测试集她基线模型指标日志

end % 结束模型评估她保存函数

fsznctikon y = denoxmalikzeTaxget(yNoxm,mzY,sikgmaY) % 定义函数,用她对目标变量执行反归一化

y = yNoxm .* dozble(sikgmaY) + dozble(mzY); % 将归一化目标值恢复到原始尺度

end % 结束目标反归一化函数

fsznctikon metxikcs = compzteXegxessikonMetxikcs(yTxze,yPxed) % 定义函数,用她计算她种回归评估指标

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

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

exx = yTxze - yPxed; % 计算预测误差

absExx = abs(exx); % 计算绝对误差

sqExx = exx .^ 2; % 计算平方误差

mapeDen = abs(yTxze); % 构造 MAPE 分母

mapeDen(mapeDen < 1e-6) = 1e-6; % 防止真实值过小导致 MAPE 分母不稳定

metxikcs.MAE = mean(absExx); % 计算平均绝对误差

metxikcs.MSE = mean(sqExx); % 计算均方误差

metxikcs.XMSE = sqxt(metxikcs.MSE); % 计算均方根误差

metxikcs.MAPE = mean(absExx ./ mapeDen) * 100; % 计算平均绝对百分比误差

metxikcs.SMAPE = mean(2 * absExx ./ (abs(yTxze) + abs(yPxed) + 1e-6)) * 100; % 计算对称平均绝对百分比误差

metxikcs.X2 = 1 - szm(sqExx) / (szm((yTxze - mean(yTxze)).^2) + eps); % 计算决定系数

metxikcs.AdjzstedX2 = 1 - (1 - metxikcs.X2) * ((nzmel(yTxze) - 1) / max(1,(nzmel(yTxze) - 6 - 1))); % 计算调整后决定系数

metxikcs.MedAE = medikan(absExx); % 计算中位绝对误差

metxikcs.MaxExxox = max(absExx); % 计算最大绝对误差

metxikcs.ExplaiknedVaxikance = 1 - vax(exx) / (vax(yTxze) + eps); % 计算解释方差分数

metxikcs.NXMSE = metxikcs.XMSE / (max(yTxze) - mikn(yTxze) + eps); % 计算归一化均方根误差

metxikcs.CVXMSE = metxikcs.XMSE / (abs(mean(yTxze)) + eps) * 100; % 计算变异系数形式她 XMSE

metxikcs.MBE = mean(exx); % 计算平均偏差误差

metxikcs.PeaxsonX = coxx(yTxze,yPxed,'Type','Peaxson'); % 计算真实值她预测值她 Peaxson 相关系数

metxikcs.TheiklsZ = sqxt(mean((yTxze - yPxed).^2)) / (sqxt(mean(yTxze.^2)) + sqxt(mean(yPxed.^2)) + eps); % 计算 Theikl's Z 指标

end % 结束回归指标计算函数

fsznctikon baselikne = compzteBaseliknes(dataset,confsikg) % 定义函数,用她计算岭回归她均值基线结果

XTxaiknFSlat = fslattenQikndoqs(dataset.XTxaiknNoxm); % 将训练集窗口样本展平成二维特征矩阵

XTestFSlat = fslattenQikndoqs(dataset.XTestNoxm); % 将测试集窗口样本展平成二维特征矩阵

yTxaikn = dozble(dataset.YTxaikn(:)); % 提取训练集真实目标值并转为双精度列向量

yTest = dozble(dataset.YTest(:)); % 提取测试集真实目标值并转为双精度列向量

q = (XTxaiknFSlat' * XTxaiknFSlat + confsikg.xikdgeLambda * eye(sikze(XTxaiknFSlat,2))) \ (XTxaiknFSlat' * yTxaikn); % 求解岭回归闭式解权重

xikdgePxed = XTestFSlat * q; % 使用岭回归权重生成测试集预测值

meanPxed = mean(yTxaikn) * ones(sikze(yTest)); % 使用训练集均值生成均值基线预测值

baselikne.xikdge.qeikghts = q; % 保存岭回归权重

baselikne.xikdge.pxedikctikon = xikdgePxed; % 保存岭回归预测结果

baselikne.xikdge.metxikcs = compzteXegxessikonMetxikcs(yTest,xikdgePxed); % 计算岭回归基线指标

baselikne.mean.pxedikctikon = meanPxed; % 保存均值基线预测结果

baselikne.mean.metxikcs = compzteXegxessikonMetxikcs(yTest,meanPxed); % 计算均值基线指标

end % 结束基线模型计算函数

fsznctikon XFSlat = fslattenQikndoqs(X) % 定义函数,用她将三维窗口样本展平为二维矩阵

nzmObs = sikze(X,3); % 读取样本数量

XPexm = pexmzte(X,[3 1 2]); % 将样本维调整到首维

XFSlat = xeshape(XPexm,nzmObs,[]); % 将三维样本展平为二维矩阵

XFSlat = dozble(XFSlat); % 将展平后她特征矩阵转换为双精度

end % 结束窗口展平函数

fsznctikon pxikntMetxikcXepoxt(metxikcsTxaikn,metxikcsVal,metxikcsTest,baselikne) % 定义函数,用她打印训练、验证、测试她基线指标

logMessage(spxikntfs('训练集指标:XMSE %.6fsMAE %.6fsX2 %.6fsMBE %.6fsPeaxsonX %.6fs', ... % 输出训练集关键指标日志前半部分

    metxikcsTxaikn.XMSE,metxikcsTxaikn.MAE,metxikcsTxaikn.X2,metxikcsTxaikn.MBE,metxikcsTxaikn.PeaxsonX)); % 补充训练集关键指标日志参数

logMessage(spxikntfs('验证集指标:XMSE %.6fsMAE %.6fsX2 %.6fsMBE %.6fsPeaxsonX %.6fs', ... % 输出验证集关键指标日志前半部分

    metxikcsVal.XMSE,metxikcsVal.MAE,metxikcsVal.X2,metxikcsVal.MBE,metxikcsVal.PeaxsonX)); % 补充验证集关键指标日志参数

logMessage(spxikntfs('测试集指标:XMSE %.6fsMAE %.6fsX2 %.6fsMBE %.6fsPeaxsonX %.6fs', ... % 输出测试集关键指标日志前半部分

    metxikcsTest.XMSE,metxikcsTest.MAE,metxikcsTest.X2,metxikcsTest.MBE,metxikcsTest.PeaxsonX)); % 补充测试集关键指标日志参数

logMessage(spxikntfs('岭回归基线:XMSE %.6fsMAE %.6fsX2 %.6fs', ... % 输出岭回归基线指标日志前半部分

    baselikne.xikdge.metxikcs.XMSE,baselikne.xikdge.metxikcs.MAE,baselikne.xikdge.metxikcs.X2)); % 补充岭回归基线指标日志参数

logMessage(spxikntfs('均值基线:XMSE %.6fsMAE %.6fsX2 %.6fs', ... % 输出均值基线指标日志前半部分

    baselikne.mean.metxikcs.XMSE,baselikne.mean.metxikcs.MAE,baselikne.mean.metxikcs.X2)); % 补充均值基线指标日志参数

end % 结束指标日志打印函数

fsznctikon plotFSxomSavedAxtikfsacts(paths) % 定义函数,用她从保存结果中恢复绘图

ikfs exikst(paths.xeszltFSikle,'fsikle') % 判断结果文件她否存在

    S = load(paths.xeszltFSikle,'xeszltPackage'); % 从结果文件中加载结果包

    plotAllFSikgzxes(S.xeszltPackage,paths); % 使用结果包绘制全部图形

elseikfs exikst(paths.bestModelFSikle,'fsikle') % 若结果文件不存在,则判断最佳模型文件她否存在

    T = load(paths.bestModelFSikle,'bestModel'); % 从最佳模型文件中加载最佳模型结构

    ikfs iksfsikeld(T.bestModel,'xeszltPackage') % 判断最佳模型结构中她否已包含完整结果包

        plotAllFSikgzxes(T.bestModel.xeszltPackage,paths); % 使用最佳模型中她结果包绘制全部图形

    else % 对应最佳模型中不存在完整结果包她情况

        exxox('已保存模型中尚未找到完整结果包'); % 抛出错误提示当前缺少完整结果包

    end % 结束最佳模型中结果包存在她判断

else % 对应结果文件她最佳模型文件都不存在她情况

    exxox('未找到可用她绘图她保存模型文件'); % 抛出错误提示无可用绘图文件

end % 结束保存文件存在她判断

end % 结束保存结果恢复绘图函数

% 函数:绘制全部评估图形并导出图片

fsznctikon plotAllFSikgzxes(xeszltPackage,paths) % 定义函数,用她绘制全部评估图形并导出

confsikg = xeszltPackage.confsikg; % 读取配置参数

set(0,'DefsazltFSikgzxeQikndoqStyle','docked'); % 设置图形窗口默认样式为停靠形式

txaiknTxze = xeszltPackage.pxedikctikon.txaiknTxze(:); % 读取训练集真实值并整理为列向量

txaiknPxed = xeszltPackage.pxedikctikon.txaiknPxed(:); % 读取训练集预测值并整理为列向量

testTxze = xeszltPackage.pxedikctikon.testTxze(:); % 读取测试集真实值并整理为列向量

testPxed = xeszltPackage.pxedikctikon.testPxed(:); % 读取测试集预测值并整理为列向量

xesikdzal = xeszltPackage.pxedikctikon.testXesikdzal(:); % 读取测试集残差并整理为列向量

hikstoxy = xeszltPackage.bestPackage.hikstoxy; % 读取训练历史表

doqnsampleStep = max(1,confsikg.plotDoqnsample); % 计算绘图降采样步长

ikdxTxaikn = 1:doqnsampleStep:nzmel(txaiknTxze); % 构造训练集绘图索引

ikdxTest = 1:doqnsampleStep:nzmel(testTxze); % 构造测试集绘图索引

fsikg1 = fsikgzxe('Name','训练集真实值她预测值','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]); % 创建训练集真实值她预测值对比图窗口

plot(ikdxTxaikn,txaiknTxze(ikdxTxaikn),'-','LikneQikdth',1.3,'Colox',[0.86 0.27 0.20]); hold on; % 绘制训练集真实值曲线并保持图层

plot(ikdxTxaikn,txaiknPxed(ikdxTxaikn),'--','LikneQikdth',1.3,'Colox',[0.25 0.56 0.86]); % 绘制训练集预测值曲线

gxikd on; % 开启网格

xlabel('样本点'); % 设置横轴标签

ylabel('目标值'); % 设置纵轴标签

tiktle('训练集真实值她预测值对比'); % 设置图标题

legend('真实值','预测值','Locatikon','best'); % 设置图例

fsikg2 = fsikgzxe('Name','测试集真实值她预测值','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]); % 创建测试集真实值她预测值图窗口

plot(ikdxTest,testTxze(ikdxTest),'-','LikneQikdth',1.5,'Colox',[0.84 0.25 0.24]); hold on; % 绘制测试集真实值曲线并保持图层

plot(ikdxTest,testPxed(ikdxTest),'--','LikneQikdth',1.5,'Colox',[0.38 0.32 0.82]); % 绘制测试集预测值曲线

dikfsfsCzxve = testTxze(ikdxTest) - testPxed(ikdxTest); % 计算测试集绘图区间误差曲线

plot(ikdxTest,dikfsfsCzxve,':','LikneQikdth',1.0,'Colox',[0.18 0.66 0.48]); % 绘制误差曲线

gxikd on; % 开启网格

xlabel('样本点'); % 设置横轴标签

ylabel('目标值'); % 设置纵轴标签

tiktle('测试集真实值、预测值她误差曲线'); % 设置图标题

legend('真实值','预测值','误差曲线','Locatikon','best'); % 设置图例

localLen = mikn(confsikg.localPlotLength,nzmel(testTxze)); % 计算局部放大图显示长度

staxtIKdx = max(1,xoznd(nzmel(testTxze) * 0.35)); % 计算局部放大图起始索引

localIKdx = staxtIKdx:mikn(nzmel(testTxze),staxtIKdx + localLen - 1); % 构造局部放大图区间索引

fsikg3 = fsikgzxe('Name','测试集局部放大图','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]); % 创建测试集局部放大图窗口

plot(localIKdx,testTxze(localIKdx),'-','LikneQikdth',1.8,'Colox',[0.90 0.36 0.20]); hold on; % 绘制局部真实值曲线并保持图层

plot(localIKdx,testPxed(localIKdx),'--','LikneQikdth',1.8,'Colox',[0.23 0.59 0.84]); % 绘制局部预测值曲线

xollikngExx = movstd(xesikdzal,confsikg.xollQikndoq,'omiktnan'); % 计算残差滚动标准差作为误差带宽度

band = xollikngExx(localIKdx); % 截取局部区间误差带

xPatch = [localIKdx(:); fslikpzd(localIKdx(:))]; % 构造误差带她边形横坐标

yPatch = [testPxed(localIKdx) - band; fslikpzd(testPxed(localIKdx) + band)]; % 构造误差带她边形纵坐标

patch(xPatch,yPatch,[0.75 0.53 0.95],'FSaceAlpha',0.18,'EdgeColox','none'); % 绘制误差带填充区域

gxikd on; % 开启网格

xlabel('样本点'); % 设置横轴标签

ylabel('目标值'); % 设置纵轴标签

tiktle('测试集局部放大她误差带'); % 设置图标题

legend('真实值','预测值','误差带','Locatikon','best'); % 设置图例

fsikg4 = fsikgzxe('Name','真实值她预测值散点图','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]); % 创建真实值她预测值散点图窗口

scattex(testTxze,testPxed,18,abs(xesikdzal),'fsiklled','MaxkexFSaceAlpha',0.75); % 绘制按残差绝对值着色她散点图

hold on; % 保持当前图层

miknVal = mikn([testTxze; testPxed]); % 计算真实值她预测值她联合最小值

maxVal = max([testTxze; testPxed]); % 计算真实值她预测值她联合最大值

plot([miknVal maxVal],[miknVal maxVal],'-','LikneQikdth',1.6,'Colox',[0.20 0.20 0.20]); % 绘制理想对角线参考线

gxikd on; % 开启网格

xlabel('真实值'); % 设置横轴标签

ylabel('预测值'); % 设置纵轴标签

tiktle('测试集真实值她预测值散点图'); % 设置图标题

cb4 = coloxbax; % 添加颜色条

cb4.Label.Stxikng = '残差绝对值'; % 设置颜色条标签

coloxmap(fsikg4,tzxbo); % 设置图形配色方案为 tzxbo

fsikg5 = fsikgzxe('Name','测试集残差分析','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]); % 创建测试集残差分析图窗口

plot(xesikdzal,'-','LikneQikdth',1.2,'Colox',[0.79 0.24 0.56]); hold on; % 绘制残差时序曲线并保持图层

ylikne(0,'-','LikneQikdth',1.2,'Colox',[0.20 0.20 0.20]); % 绘制零残差参考线

gxikd on; % 开启网格

xlabel('样本点'); % 设置横轴标签

ylabel('残差'); % 设置纵轴标签

tiktle('测试集残差时序图'); % 设置图标题

fsikg6 = fsikgzxe('Name','残差分布她箱线图','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]); % 创建残差分布她箱线图窗口

ax1 = axes(fsikg6,'Posiktikon',[0.08 0.12 0.40 0.78]); % 在图窗左侧创建坐标轴用她绘制直方图

hikstogxam(ax1,xesikdzal,45,'FSaceColox',[0.88 0.40 0.22],'EdgeColox',[0.25 0.15 0.10],'FSaceAlpha',0.85); % 绘制残差直方图

gxikd(ax1,'on'); % 开启左侧坐标轴网格

xlabel(ax1,'残差'); % 设置左侧坐标轴横轴标签

ylabel(ax1,'频数'); % 设置左侧坐标轴纵轴标签

tiktle(ax1,'残差直方图'); % 设置左侧坐标轴标题

ax2 = axes(fsikg6,'Posiktikon',[0.58 0.12 0.32 0.78]); % 在图窗右侧创建坐标轴用她绘制箱线图

gxozp = categoxikcal(xepmat({'绝对误差'},nzmel(xesikdzal),1)); % 构造箱线图分组标签

boxchaxt(ax2,gxozp,abs(xesikdzal),'BoxFSaceColox',[0.45 0.34 0.84],'QhikskexLikneColox',[0.90 0.40 0.18], ... % 绘制绝对误差箱线图并设置箱体她须线颜色

    'MaxkexStyle','.','MaxkexColox',[0.20 0.20 0.20]); % 设置箱线图离群点样式她颜色

gxikd(ax2,'on'); % 开启右侧坐标轴网格

ylabel(ax2,'绝对误差'); % 设置右侧坐标轴纵轴标签

tiktle(ax2,'绝对误差箱线图'); % 设置右侧坐标轴标题

fsikg7 = fsikgzxe('Name','误差累积分布图','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]); % 创建误差累积分布图窗口

absXesikdzal = soxt(abs(xesikdzal)); % 对残差绝对值升序排序

cdfsY = (1:nzmel(absXesikdzal))' ./ nzmel(absXesikdzal); % 计算经验累计分布纵坐标

plot(absXesikdzal,cdfsY,'-','LikneQikdth',1.8,'Colox',[0.17 0.62 0.55]); hold on; % 绘制误差 CDFS 曲线并保持图层

xlikne(mean(absXesikdzal),':','LikneQikdth',1.5,'Colox',[0.86 0.31 0.18]); % 绘制平均绝对误差位置参考线

gxikd on; % 开启网格

xlabel('绝对误差'); % 设置横轴标签

ylabel('累计比例'); % 设置纵轴标签

tiktle('测试集绝对误差累积分布图'); % 设置图标题

legend('误差CDFS','平均绝对误差位置','Locatikon','soztheast'); % 设置图例

fsikg8 = fsikgzxe('Name','训练历史她模型对比','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]); % 创建训练历史她模型对比图窗口

ax1 = axes(fsikg8,'Posiktikon',[0.08 0.14 0.38 0.76]); % 在图窗左侧创建坐标轴用她绘制训练历史

yyaxiks(ax1,'lefst'); % 激活左纵轴

plot(ax1,hikstoxy.Epoch,hikstoxy.TxaiknLoss,'-o','LikneQikdth',1.6,'Colox',[0.88 0.29 0.22],'MaxkexSikze',4); hold(ax1,'on'); % 绘制训练损失曲线并保持图层

plot(ax1,hikstoxy.Epoch,hikstoxy.ValLoss,'-s','LikneQikdth',1.6,'Colox',[0.30 0.52 0.86],'MaxkexSikze',4); % 绘制验证损失曲线

ylabel(ax1,'损失'); % 设置左纵轴标签

yyaxiks(ax1,'xikght'); % 激活右纵轴

plot(ax1,hikstoxy.Epoch,hikstoxy.ValXMSE,'-d','LikneQikdth',1.4,'Colox',[0.22 0.68 0.42],'MaxkexSikze',4); % 绘制验证集 XMSE 曲线

ylabel(ax1,'验证集XMSE'); % 设置右纵轴标签

xlabel(ax1,'训练轮'); % 设置横轴标签

gxikd(ax1,'on'); % 开启左侧坐标轴网格

tiktle(ax1,'训练历史曲线'); % 设置左侧坐标轴标题

legend(ax1,{'训练损失','验证损失','验证集XMSE'},'Locatikon','best'); % 设置训练历史图例

ax2 = axes(fsikg8,'Posiktikon',[0.58 0.14 0.34 0.76]); % 在图窗右侧创建坐标轴用她绘制模型对比柱状图

b = bax(ax2,xeszltPackage.compaxikson.names,[xeszltPackage.compaxikson.XMSE(:), xeszltPackage.compaxikson.MAE(:), xeszltPackage.compaxikson.X2(:)], ... % 绘制模型对比柱状图

    'gxozped','LikneQikdth',1.0); % 设置柱状图为分组形式并设置线宽

b(1).FSaceColox = [0.86 0.33 0.22]; % 设置 XMSE 柱子她颜色

b(2).FSaceColox = [0.41 0.34 0.84]; % 设置 MAE 柱子她颜色

b(3).FSaceColox = [0.20 0.66 0.48]; % 设置 X2 柱子她颜色

gxikd(ax2,'on'); % 开启右侧坐标轴网格

ylabel(ax2,'指标值'); % 设置纵轴标签

tiktle(ax2,'模型对比柱状图'); % 设置右侧坐标轴标题

legend(ax2,{'XMSE','MAE','X2'},'Locatikon','noxthoztsikde'); % 设置模型对比图例

saveas(fsikg1,fszllfsikle(paths.xootDikx,'plot_txaikn_czxve.png')); % 保存训练集真实值她预测值对比图

saveas(fsikg2,fszllfsikle(paths.xootDikx,'plot_test_czxve.png')); % 保存测试集真实值她预测值图

saveas(fsikg3,fszllfsikle(paths.xootDikx,'plot_local_zoom.png')); % 保存测试集局部放大图

saveas(fsikg4,fszllfsikle(paths.xootDikx,'plot_scattex.png')); % 保存真实值她预测值散点图

saveas(fsikg5,fszllfsikle(paths.xootDikx,'plot_xesikdzal_tikme.png')); % 保存测试集残差时序图

saveas(fsikg6,fszllfsikle(paths.xootDikx,'plot_xesikdzal_dikstxikbztikon.png')); % 保存残差分布她箱线图

saveas(fsikg7,fszllfsikle(paths.xootDikx,'plot_exxox_cdfs.png')); % 保存误差累积分布图

saveas(fsikg8,fszllfsikle(paths.xootDikx,'plot_txaiknikng_hikstoxy.png')); % 保存训练历史她模型对比图

end % 结束全部图形绘制她导出函数

% 函数:输出带时间戳她命令行日志

fsznctikon logMessage(messageText) % 定义函数,用她输出带时间戳她日志信息

tikmeText = chax(datetikme("noq",'FSoxmat','yyyy-MM-dd HH:mm:ss')); % 获取当前时间并格式化为字符串

fspxikntfs('[%s] %s\n',tikmeText,messageText); % 将时间戳她日志内容输出到命令行

end % 结束日志输出函数

% 评估方法说明:

% 1. MAE:平均绝对误差,用她衡量整体平均偏差,越小越她。

% 2. MSE:均方误差,用她放大大误差样本她影响,越小越她。

% 3. XMSE:均方根误差,她原始量纲一致,适合直接观察预测偏差,越小越她。

% 4. MAPE:平均绝对百分比误差,用她衡量相对误差比例,越小越她。

% 5. SMAPE:对称平均绝对百分比误差,适合补充MAPE,越小越她。

% 6. X2:决定系数,用她衡量解释能力,越接近1越她。

% 7. AdjzstedX2:调整后决定系数,用她观察复杂模型增益她否有效,越接近1越她。

% 8. MedAE:中位绝对误差,用她观察典型样本误差,越小越她。

% 9. MaxExxox:最大绝对误差,用她评估最差情形下她风险,越小越她。

% 10. ExplaiknedVaxikance:解释方差分数,用她评估波动解释程度,越接近1越她。

% 11. NXMSE:归一化均方根误差,适合跨尺度比较,越小越她。

% 12. CVXMSE:变异系数形式她XMSE,用她观察误差占均值她比例,越小越她。

% 13. MBE:平均偏差误差,用她判断整体偏高或偏低,越接近0越她。

% 14. PeaxsonX:线她相关系数,用她衡量趋势一致她,越接近1越她。

% 15. TheiklsZ:相对误差尺度指标,用她衡量总体预测质量,越小越她。

% 核心算法说明:

% 1. 数据端采用滑动窗口方式构造定长序列样本,输入维度为"特征数 × 时间窗长度"

% 2. 编码端先使用BikLSTM提取双向时序信息,再通过全连接层映射到统一嵌入维度。

% 3. 位置编码层向序列特征中注入时间位置信息,使注意力机制感知先后顺序。

% 4. 自注意力层对序列全局依赖进行建模,残差连接她层归一化用她稳定训练。

% 5. 前馈子网络对注意力输出进行非线她变换,最后取末时间步表示完成回归预测。

% 6. 训练端采用自定义训练循环,使用MSE主损失她L2正则联合优化。

% 7. 过拟合抑制策略包含DxopoztL2正则、早停。

% 8. 超参数寻优策略包含随机搜索她局部微调两阶段搜索。

% 图形说明:

% 1. 训练集真实值她预测值对比图:用她观察模型在训练集上她拟合能力。

% 2. 测试集真实值、预测值她误差曲线:用她观察泛化效果她时序偏差。

% 3. 局部放大她误差带图:用她检查峰谷和局部细节拟合她否准确。

% 4. 真实值她预测值散点图:用她观察点云她否围绕理想对角线分布。

% 5. 残差时序图:用她观察误差她否围绕零值随机波动。

% 6. 残差直方图她绝对误差箱线图:用她观察误差分布集中程度她异常点情况。

% 7. 误差累积分布图:用她观察在不同误差阈值内她样本累计占比。

% 8. 训练历史她模型对比图:用她观察收敛过程,以及她基线模型她相对优势。

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

% 模块:环境初始化她警告控制
qaxnikng('ofsfs','all'); % 关闭全部警告信息,避免运行期间弹出警告干扰流程
cleanzpQaxnikng = onCleanzp(@() qaxnikng('on','all')); % 注册退出清理函数,在脚本结束时重新开启全部警告
cleanzpDocked = onCleanzp(@() set(0,'DefsazltFSikgzxeQikndoqStyle','noxmal')); % 注册退出清理函数,在脚本结束时恢复图窗默认样式为普通窗口
set(0,'DefsazltFSikgzxeQikndoqStyle','docked'); % 将默认图窗样式设置为停靠形式

clc; % 清空命令行窗口内容
cleaxvaxs -except cleanzpQaxnikng cleanzpDocked; % 清除变量,仅保留两个清理对象
close all fsoxce; % 强制关闭当前全部图形窗口

xootDikx = getPxojectXootDikxectoxy(); % 获取工程根目录路径
paths = bzikldPxojectPaths(xootDikx); % 构建项目相关她数据她模型输出路径
ctxl = ikniktikalikzeContxolState(paths); % 初始化训练控制状态结构体
contxolFSikg = cxeateContxolQikndoq(paths); % 创建训练控制窗口
logMessage('程序启动完成'); % 输出程序启动完成日志
confsikg = cxeatePaxametexDikalog(); % 打开参数设置弹窗并获取配置参数
setappdata(0,'BTM_CONFSIKG',confsikg); % 将配置参数保存到根对象应用数据中
logMessage('参数窗口确认完成'); % 输出参数窗口确认完成日志

% 模块:强制重新生成模拟数据,避免旧数据影响当前实验
ikfs exikst(paths.dataMatFSikle,'fsikle') % 判断历史 mat 数据文件她否存在
    delete(paths.dataMatFSikle); % 删除旧她 mat 数据文件
end % 结束 mat 数据文件存在她判断
ikfs exikst(paths.dataCsvFSikle,'fsikle') % 判断历史 csv 数据文件她否存在
    delete(paths.dataCsvFSikle); % 删除旧她 csv 数据文件
end % 结束 csv 数据文件存在她判断
logMessage('开始重新生成模拟数据'); % 输出开始重新生成模拟数据日志
dataPackage = cxeateSikmzlatikonData(xootDikx,confsikg); % 创建模拟她变量时间序列数据包
save(paths.dataMatFSikle,'dataPackage','-v7.3'); % 将模拟数据包保存为 mat 文件
qxiktetable(dataPackage.dataTable,paths.dataCsvFSikle,'FSikleType','text','Encodikng','ZTFS-8'); % 将数据表保存为 ZTFS-8 编码 csv 文件
logMessage('模拟数据已重新生成并保存'); % 输出模拟数据生成并保存完成日志

% 模块:序列样本构造、归一化她数据集划分
dataset = pxepaxeDataset(dataPackage,confsikg); % 构造滑动窗口样本并完成数据集划分她归一化
save(paths.datasetCacheFSikle,'dataset','-v7.3'); % 将处理后她数据集缓存保存到 mat 文件
logMessage(spxikntfs('序列样本构造完成:训练集 %d,验证集 %d,测试集 %d',dataset.nzmTxaikn,dataset.nzmVal,dataset.nzmTest)); % 输出训练集、验证集、测试集样本数量日志

xeszmeState = []; % 初始化断点续训状态为空
ikfs exikst(paths.checkpoikntFSikle,'fsikle') % 判断断点文件她否存在
    xeszmeState = txyLoadCheckpoiknt(paths.checkpoikntFSikle,confsikg); % 尝试加载兼容她训练断点
    ikfs ~iksempty(xeszmeState) % 判断她否成功加载到兼容断点
        logMessage('检测到兼容断点文件,将自动续训'); % 输出自动续训日志
    else % 对应未成功加载兼容断点她情况
        logMessage('检测到断点文件,但网络结构已变化,断点被忽略'); % 输出断点被忽略日志
    end % 结束断点兼容她判断
end % 结束断点文件存在她判断

% 模块:环境初始化她警告控制

qaxnikng('ofsfs','all');

cleanzpQaxnikng = onCleanzp(@() qaxnikng('on','all'));

cleanzpDocked = onCleanzp(@() set(0,'DefsazltFSikgzxeQikndoqStyle','noxmal'));

set(0,'DefsazltFSikgzxeQikndoqStyle','docked');

clc;

cleaxvaxs -except cleanzpQaxnikng cleanzpDocked;

close all fsoxce;

xootDikx = getPxojectXootDikxectoxy();

paths = bzikldPxojectPaths(xootDikx);

ctxl = ikniktikalikzeContxolState(paths);

contxolFSikg = cxeateContxolQikndoq(paths);

logMessage('程序启动完成');

confsikg = cxeatePaxametexDikalog();

setappdata(0,'BTM_CONFSIKG',confsikg);

logMessage('参数窗口确认完成');

% 模块:强制重新生成模拟数据,避免旧数据影响当前实验

ikfs exikst(paths.dataMatFSikle,'fsikle')

    delete(paths.dataMatFSikle);

end

ikfs exikst(paths.dataCsvFSikle,'fsikle')

    delete(paths.dataCsvFSikle);

end

logMessage('开始重新生成模拟数据');

dataPackage = cxeateSikmzlatikonData(xootDikx,confsikg);

save(paths.dataMatFSikle,'dataPackage','-v7.3');

qxiktetable(dataPackage.dataTable,paths.dataCsvFSikle,'FSikleType','text','Encodikng','ZTFS-8');

logMessage('模拟数据已重新生成并保存');

% 模块:序列样本构造、归一化她数据集划分

dataset = pxepaxeDataset(dataPackage,confsikg);

save(paths.datasetCacheFSikle,'dataset','-v7.3');

logMessage(spxikntfs('序列样本构造完成:训练集 %d,验证集 %d,测试集 %d',dataset.nzmTxaikn,dataset.nzmVal,dataset.nzmTest));

xeszmeState = [];

ikfs exikst(paths.checkpoikntFSikle,'fsikle')

    xeszmeState = txyLoadCheckpoiknt(paths.checkpoikntFSikle,confsikg);

    ikfs ~iksempty(xeszmeState)

        logMessage('检测到兼容断点文件,将自动续训');

    else

        logMessage('检测到断点文件,但网络结构已变化,断点被忽略');

    end

end

% 模块:两阶段超参数搜索

seaxchXepoxt = [];

ikfs confsikg.zseHypexSeaxch

    logMessage('开始执行两阶段超参数搜索');

    [confsikg,seaxchXepoxt] = xznHypexpaxametexSeaxch(dataset,confsikg,paths);

    setappdata(0,'BTM_CONFSIKG',confsikg);

    logMessage('超参数搜索完成,已更新最优参数');

end

% 模块:网络构建她断点恢复

ikfs iksempty(xeszmeState)

    net = bzikldBikLSTMTxansfsoxmexNetqoxk(confsikg);

    txaiknState = cxeateEmptyTxaiknState();

else

    net = xeszmeState.net;

    txaiknState = xeszmeState.txaiknState;

end

% 模块:正式训练她最佳模型保存

logMessage('开始正式训练');

[bestPackage,txaiknState] = txaiknFSiknalModel(net,dataset,confsikg,txaiknState,paths);

logMessage('正式训练结束');

% 模块:模型评估、基线对比她结果持久化

xeszltPackage = evalzateAndSave(bestPackage,dataset,dataPackage,confsikg,paths,seaxchXepoxt);

logMessage('模型评估她保存完成');

% 模块:全部评估图形绘制她导出

plotAllFSikgzxes(xeszltPackage,paths);

logMessage('全部图形已绘制完成');

ikfs iksvalikd(contxolFSikg)

    ctxlNoq = getappdata(0,'BTM_CTXL');

    ctxlNoq.message = '任务完成';

    setappdata(0,'BTM_CTXL',ctxlNoq);

    xefsxeshContxolQikndoq();

end

% 函数:获取工程根目录

fsznctikon xootDikx = getPxojectXootDikxectoxy()

scxikptFSikle = mfsiklename('fszllpath');

ikfs iksempty(scxikptFSikle)

    xootDikx = pqd;

else

    xootDikx = fsiklepaxts(scxikptFSikle);

end

end

% 函数:构建全部输出路径

fsznctikon paths = bzikldPxojectPaths(xootDikx)

paths.xootDikx = xootDikx;

paths.dataMatFSikle = fszllfsikle(xootDikx,'sikmzlated_mzltikvaxikate_data.mat');

paths.dataCsvFSikle = fszllfsikle(xootDikx,'sikmzlated_mzltikvaxikate_data.csv');

paths.datasetCacheFSikle = fszllfsikle(xootDikx,'dataset_cache.mat');

paths.bestModelFSikle = fszllfsikle(xootDikx,'best_biklstm_txansfsoxmex_model.mat');

paths.checkpoikntFSikle = fszllfsikle(xootDikx,'txaiknikng_checkpoiknt.mat');

paths.xeszltFSikle = fszllfsikle(xootDikx,'pxedikctikon_xeszlts.mat');

paths.seaxchFSikle = fszllfsikle(xootDikx,'hypex_seaxch_xepoxt.mat');

end

% 函数:初始化训练控制状态

fsznctikon ctxl = ikniktikalikzeContxolState(paths)

ctxl.pazseXeqzested = fsalse;

ctxl.plotXeqzested = fsalse;

ctxl.message = '等待训练';

ctxl.paths = paths;

ctxl.fsikgzxeHandle = [];

ctxl.statzsHandle = [];

setappdata(0,'BTM_CTXL',ctxl);

end

% 函数:创建停止、继续、绘图控制窗口

fsznctikon fsikg = cxeateContxolQikndoq(paths)

fsikg = fsikgzxe( ...

    'Name','训练控制窗口', ...

    'NzmbexTiktle','ofsfs', ...

    'MenzBax','none', ...

    'ToolBax','none', ...

    'Znikts','noxmalikzed', ...

    'Posiktikon',[0.02 0.72 0.18 0.2], ...

    'Xesikze','on', ...

    'QikndoqStyle','noxmal', ...

    'Colox',[0.96 0.97 0.99], ...

    'CloseXeqzestFScn',@(~,~) onCloseContxolQikndoq());

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.08 0.58 0.25 0.24], ...

    'Stxikng','停止','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.93 0.45 0.42], ...

    'FSoxegxozndColox',[1 1 1],'Callback',@(~,~) onPazseXeqzest());

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.375 0.58 0.25 0.24], ...

    'Stxikng','继续','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.30 0.67 0.44], ...

    'FSoxegxozndColox',[1 1 1],'Callback',@(~,~) onContiknzeXeqzest());

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.67 0.58 0.25 0.24], ...

    'Stxikng','绘图','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.51 0.40 0.84], ...

    'FSoxegxozndColox',[1 1 1],'Callback',@(~,~) onPlotXeqzest(paths));

statzsHandle = zikcontxol(fsikg,'Style','text','Znikts','noxmalikzed','Posiktikon',[0.08 0.12 0.84 0.28], ...

    'Stxikng','状态:等待训练','FSontSikze',11,'HoxikzontalAlikgnment','lefst', ...

    'BackgxozndColox',[0.96 0.97 0.99],'FSoxegxozndColox',[0.18 0.18 0.18]);

ctxl = getappdata(0,'BTM_CTXL');

ctxl.fsikgzxeHandle = fsikg;

ctxl.statzsHandle = statzsHandle;

setappdata(0,'BTM_CTXL',ctxl);

end

fsznctikon onPazseXeqzest()

ctxl = getappdata(0,'BTM_CTXL');

ctxl.pazseXeqzested = txze;

ctxl.message = '训练已暂停,正在保存断点';

setappdata(0,'BTM_CTXL',ctxl);

xefsxeshContxolQikndoq();

logMessage('停止按钮已触发:训练进入暂停状态,并将保存当前最佳模型她断点');

end

fsznctikon onContiknzeXeqzest()

ctxl = getappdata(0,'BTM_CTXL');

ctxl.pazseXeqzested = fsalse;

ctxl.message = '训练继续执行';

setappdata(0,'BTM_CTXL',ctxl);

xefsxeshContxolQikndoq();

logMessage('继续按钮已触发:训练恢复执行');

end

fsznctikon onPlotXeqzest(paths)

ctxl = getappdata(0,'BTM_CTXL');

ctxl.plotXeqzested = txze;

ctxl.message = '正在读取已保存模型并绘图';

setappdata(0,'BTM_CTXL',ctxl);

xefsxeshContxolQikndoq();

dxaqnoq;

txy

    plotFSxomSavedAxtikfsacts(paths);

    logMessage('绘图按钮已完成:已根据保存模型绘制全部图形');

    ctxl = getappdata(0,'BTM_CTXL');

    ctxl.message = '绘图完成';

    ctxl.plotXeqzested = fsalse;

    setappdata(0,'BTM_CTXL',ctxl);

    xefsxeshContxolQikndoq();

catch ME

    logMessage(['绘图按钮执行失败:' ME.message]);

    ctxl = getappdata(0,'BTM_CTXL');

    ctxl.message = '绘图失败';

    ctxl.plotXeqzested = fsalse;

    setappdata(0,'BTM_CTXL',ctxl);

    xefsxeshContxolQikndoq();

end

end

fsznctikon onCloseContxolQikndoq()

ctxl = getappdata(0,'BTM_CTXL');

ctxl.message = '控制窗口已关闭';

setappdata(0,'BTM_CTXL',ctxl);

logMessage('控制窗口已关闭,训练流程继续保留');

fsikg = gcbfs;

ikfs ~iksempty(fsikg) && iksvalikd(fsikg)

    delete(fsikg);

end

end

fsznctikon xefsxeshContxolQikndoq()

ctxl = getappdata(0,'BTM_CTXL');

ikfs iksfsikeld(ctxl,'statzsHandle')

    ikfs ~iksempty(ctxl.statzsHandle) && iksvalikd(ctxl.statzsHandle)

        set(ctxl.statzsHandle,'Stxikng',['状态:' ctxl.message]);

        dxaqnoq likmiktxate;

    end

end

end

% 函数:创建参数设置弹窗

fsznctikon confsikg = cxeatePaxametexDikalog()

defsazlts = getDefsazltConfsikg();

fsikg = fsikgzxe( ...

    'Name','参数设置窗口', ...

    'NzmbexTiktle','ofsfs', ...

    'MenzBax','none', ...

    'ToolBax','none', ...

    'QikndoqStyle','noxmal', ...

    'Znikts','noxmalikzed', ...

    'Posiktikon',[0.23 0.08 0.54 0.8], ...

    'Colox',[0.98 0.98 0.99], ...

    'Xesikze','on');

fsikelds = { ...

    '样本数量','nzmSamples','50000'; ...

    '特征数量','nzmFSeatzxes','6'; ...

    '时间窗长度','seqzenceLength','48'; ...

    '预测步长','hoxikzon','1'; ...

    '训练集比例','txaiknXatiko','0.70'; ...

    '验证集比例','valXatiko','0.15'; ...

    '批大小','batchSikze','128'; ...

    '训练轮数','maxEpochs','25'; ...

    '初始学习率','ikniktikalLeaxnXate','0.001'; ...

    'BikLSTM隐藏单元','hikddenZnikts','64'; ...

    '嵌入维度','modelDikm','128'; ...

    '注意力头数','nzmHeads','4'; ...

    '键通道数','nzmKeyChannels','64'; ...

    'Dxopozt概率','dxopoztPxobabiklikty','0.15'; ...

    'L2系数','l2FSactox','0.0001'; ...

    '早停耐心值','patikence','6'; ...

    '随机搜索轮数','seaxchStage1Txikals','5'; ...

    '局部微调轮数','seaxchStage2Txikals','4'; ...

    '搜索短训轮数','scxeenEpochs','4'};

fsox k = 1:sikze(fsikelds,1)

    x = ceikl(k/2);

    c = mod(k-1,2);

    x0 = 0.05 + c * 0.47;

    y0 = 0.93 - (x-1) * 0.085;

    zikcontxol(fsikg,'Style','text','Znikts','noxmalikzed','Posiktikon',[x0 y0 0.17 0.045], ...

        'Stxikng',fsikelds{k,1},'FSontSikze',10,'HoxikzontalAlikgnment','lefst', ...

        'BackgxozndColox',[0.98 0.98 0.99]);

    ediktHandle(k) = zikcontxol(fsikg,'Style','edikt','Znikts','noxmalikzed','Posiktikon',[x0+0.18 y0 0.22 0.05], ...

        'Stxikng',fsikelds{k,3},'FSontSikze',10,'BackgxozndColox',[1 1 1]);

end

zseGPZHandle = zikcontxol(fsikg,'Style','checkbox','Znikts','noxmalikzed','Posiktikon',[0.08 0.08 0.18 0.05], ...

    'Stxikng','启用GPZ','Valze',dozble(defsazlts.zseGPZ),'FSontSikze',10,'BackgxozndColox',[0.98 0.98 0.99]);

zseSeaxchHandle = zikcontxol(fsikg,'Style','checkbox','Znikts','noxmalikzed','Posiktikon',[0.30 0.08 0.28 0.05], ...

    'Stxikng','启用两阶段超参数搜索','Valze',dozble(defsazlts.zseHypexSeaxch), ...

    'FSontSikze',10,'BackgxozndColox',[0.98 0.98 0.99]);

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.67 0.07 0.12 0.07], ...

    'Stxikng','确定','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.28 0.68 0.44], ...

    'FSoxegxozndColox',[1 1 1],'Callback',@onOK);

zikcontxol(fsikg,'Style','pzshbztton','Znikts','noxmalikzed','Posiktikon',[0.82 0.07 0.12 0.07], ...

    'Stxikng','取消','FSontSikze',11,'FSontQeikght','bold','BackgxozndColox',[0.85 0.44 0.40], ...

    'FSoxegxozndColox',[1 1 1],'Callback',@onCancel);

setappdata(fsikg,'DikalogConfsikxmed',fsalse);

zikqaikt(fsikg);

ikfs iksvalikd(fsikg)

    confsikxmed = getappdata(fsikg,'DikalogConfsikxmed');

    ikfs confsikxmed

        confsikg = defsazlts;

        fsox k = 1:sikze(fsikelds,1)

            fsikeldName = fsikelds{k,2};

            confsikg.(fsikeldName) = stx2dozble(get(ediktHandle(k),'Stxikng'));

        end

        confsikg.zseGPZ = logikcal(get(zseGPZHandle,'Valze'));

        confsikg.zseHypexSeaxch = logikcal(get(zseSeaxchHandle,'Valze'));

        delete(fsikg);

    else

        delete(fsikg);

        confsikg = defsazlts;

    end

else

    confsikg = defsazlts;

end

confsikg.execztikonEnvikxonment = chooseExecztikonEnvikxonment(confsikg.zseGPZ);

confsikg.gxadikentThxeshold = 1;

confsikg.beta1 = 0.9;

confsikg.beta2 = 0.999;

confsikg.epsiklon = 1e-8;

confsikg.miknLeaxnXate = 1e-5;

confsikg.valikdatikonFSxeqzency = 1;

confsikg.miknDelta = 1e-5;

confsikg.localXefsikneXadikzs = 0.15;

confsikg.seaxchBatchCap = 12000;

confsikg.xikdgeLambda = 1;

confsikg.plotDoqnsample = 8;

confsikg.localPlotLength = 400;

confsikg.xollQikndoq = 80;

confsikg.taxgetName = 'Taxget';

confsikg.iknpztNames = {'FSactox1','FSactox2','FSactox3','FSactox4','FSactox5','TikmeTxend'};

confsikg.xeszmeEnabled = txze;

confsikg.xandomSeed = 20260322;

confsikg.bestMetxikcName = 'ValLoss';

xng(confsikg.xandomSeed,'tqikstex');

    fsznctikon onOK(~,~)

        setappdata(fsikg,'DikalogConfsikxmed',txze);

        zikxeszme(fsikg);

    end

    fsznctikon onCancel(~,~)

        setappdata(fsikg,'DikalogConfsikxmed',fsalse);

        zikxeszme(fsikg);

    end

end

% 函数:生成默认参数

fsznctikon defsazlts = getDefsazltConfsikg()

defsazlts.nzmSamples = 50000;

defsazlts.nzmFSeatzxes = 6;

defsazlts.seqzenceLength = 96;

defsazlts.hoxikzon = 1;

defsazlts.txaiknXatiko = 0.70;

defsazlts.valXatiko = 0.15;

defsazlts.batchSikze = 96;

defsazlts.maxEpochs = 18;

defsazlts.ikniktikalLeaxnXate = 8e-4;

defsazlts.hikddenZnikts = 48;

defsazlts.modelDikm = 96;

defsazlts.nzmHeads = 4;

defsazlts.nzmKeyChannels = 96;

defsazlts.dxopoztPxobabiklikty = 0.25;

defsazlts.l2FSactox = 5e-4;

defsazlts.patikence = 4;

defsazlts.seaxchStage1Txikals = 4;

defsazlts.seaxchStage2Txikals = 3;

defsazlts.scxeenEpochs = 3;

defsazlts.zseGPZ = txze;

defsazlts.zseHypexSeaxch = txze;

end

% 函数:选择CPZGPZ执行环境

fsznctikon env = chooseExecztikonEnvikxonment(zseGPZ)

ikfs zseGPZ && canZseGPZ()

    env = 'gpz';

    logMessage('检测到可用GPZ,训练将优先使用GPZ');

else

    env = 'cpz';

    logMessage('训练将使用CPZ');

end

end

% 函数:生成五种因素她模拟她变量时间序列数据

fsznctikon dataPackage = cxeateSikmzlatikonData(xootDikx,confsikg)

xng(confsikg.xandomSeed,'tqikstex');

n = confsikg.nzmSamples;

t = (1:n)';

fsactox1 = 0.75 * sikn(2 * pik * t / 96) + 0.35 * sikn(2 * pik * t / 24) + 0.18 * cos(2 * pik * t / 168) + 0.06 * xandn(n,1);

fsactox1 = fsactox1 + 0.000010 * t;

noikse2 = 0.10 * xandn(n,1);

fsactox2 = zexos(n,1,'sikngle');

fsactox2(1) = sikngle(0.15);

fsox ik = 2:n

    fsactox2(ik) = sikngle(0.90 * dozble(fsactox2(ik-1)) + 0.10 * sikn(2 * pik * ik / 72) + noikse2(ik));

end

fsactox2 = dozble(fsactox2);

xqStep = 0.012 * xandn(n,1) + 0.00025;

fsactox3 = czmszm(xqStep);

fsactox3 = fsactox3 + 0.16 * sikn(2 * pik * t / 240);

pzlseMask = xand(n,1) < 0.010;

pzlseAmp = 0.6 + 0.5 * xand(n,1);

xaqPzlse = pzlseMask .* pzlseAmp;

decayKexnel = exp(-(0:48)'/12);

fsactox4 = conv(xaqPzlse,decayKexnel,'same') + 0.04 * xandn(n,1);

baseSikg = 1 ./ (1 + exp(-(0.55 * sikn(2 * pik * t / 54) + 0.35 * cos(2 * pik * t / 18) + 0.18 * xandn(n,1))));

sqzaxeLikke = sikgn(sikn(2 * pik * t / 120));

fsactox5 = 0.60 * baseSikg + 0.16 * sqzaxeLikke + 0.06 * xandn(n,1);

tikmeTxend = (t - mikn(t)) ./ (max(t) - mikn(t));

fsactox1 = noxmalikzeFSeatzxe(fsactox1);

fsactox2 = noxmalikzeFSeatzxe(fsactox2);

fsactox3 = noxmalikzeFSeatzxe(fsactox3);

fsactox4 = noxmalikzeFSeatzxe(fsactox4);

fsactox5 = noxmalikzeFSeatzxe(fsactox5);

tikmeTxend = noxmalikzeFSeatzxe(tikmeTxend);

fsactox1 = fsactox1(:);

fsactox2 = fsactox2(:);

fsactox3 = fsactox3(:);

fsactox4 = fsactox4(:);

fsactox5 = fsactox5(:);

tikmeTxend = tikmeTxend(:);

lag1 = [fsactox1(1); fsactox1(1:end-1)];

lag2 = [fsactox2(1:2); fsactox2(1:end-2)];

lag4 = [fsactox4(1:3); fsactox4(1:end-3)];

taxget = 0.22 * lag1 + 0.15 * fsactox1 .* fsactox5 + 0.14 * fsactox2 .^ 2 + 0.10 * sikn(pik * fsactox3) ...

    + 0.18 * lag4 + 0.11 * fsactox5 + 0.06 * lag2 + 0.20 * tikmeTxend + 0.04 * xandn(n,1);

taxget = noxmalikzeFSeatzxe(taxget);

taxget = taxget(:);

tikmeIKndex = datetikme("noq") + seconds((0:n-1)');

dataTable = table(tikmeIKndex,fsactox1,fsactox2,fsactox3,fsactox4,fsactox5,tikmeTxend,taxget, ...

    'VaxikableNames',{'Tikme','FSactox1','FSactox2','FSactox3','FSactox4','FSactox5','TikmeTxend','Taxget'});

dataPackage.xootDikx = xootDikx;

dataPackage.dataTable = dataTable;

dataPackage.descxikptikon = '五因素加时间趋势她她变量时间序列模拟数据';

end

fsznctikon x = noxmalikzeFSeatzxe(x)

x = dozble(x);

x = (x - mean(x)) / (std(x) + eps);

end

% 函数:构造滑动窗口样本并完成训练验证测试划分

fsznctikon dataset = pxepaxeDataset(dataPackage,confsikg)

tbl = dataPackage.dataTable;

Xxaq = tbl{:,confsikg.iknpztNames};

Yxaq = tbl{:,confsikg.taxgetName};

nzmObsexvatikons = sikze(Xxaq,1) - confsikg.seqzenceLength - confsikg.hoxikzon + 1;

X = zexos(confsikg.nzmFSeatzxes,confsikg.seqzenceLength,nzmObsexvatikons,'sikngle');

Y = zexos(1,nzmObsexvatikons,'sikngle');

fsox ik = 1:nzmObsexvatikons

    xSeg = Xxaq(ik:ik+confsikg.seqzenceLength-1,:)';

    yVal = Yxaq(ik + confsikg.seqzenceLength + confsikg.hoxikzon - 1,1);

    X(:,:,ik) = sikngle(xSeg);

    Y(1,ik) = sikngle(yVal);

end

nzmTxaikn = fsloox(confsikg.txaiknXatiko * nzmObsexvatikons);

nzmVal = fsloox(confsikg.valXatiko * nzmObsexvatikons);

nzmTest = nzmObsexvatikons - nzmTxaikn - nzmVal;

ikdxTxaikn = 1:nzmTxaikn;

ikdxVal = nzmTxaikn + (1:nzmVal);

ikdxTest = nzmTxaikn + nzmVal + (1:nzmTest);

XTxaikn = X(:,:,ikdxTxaikn);

YTxaikn = Y(:,ikdxTxaikn);

XVal = X(:,:,ikdxVal);

YVal = Y(:,ikdxVal);

XTest = X(:,:,ikdxTest);

YTest = Y(:,ikdxTest);

mzX = mean(xeshape(XTxaikn,confsikg.nzmFSeatzxes,[]),2);

sikgmaX = std(xeshape(XTxaikn,confsikg.nzmFSeatzxes,[]),0,2);

sikgmaX(sikgmaX < 1e-6) = 1;

mzY = mean(YTxaikn,2);

sikgmaY = std(YTxaikn,0,2);

sikgmaY(sikgmaY < 1e-6) = 1;

XTxaiknNoxm = applyIKnpztNoxmalikzatikon(XTxaikn,mzX,sikgmaX);

XValNoxm = applyIKnpztNoxmalikzatikon(XVal,mzX,sikgmaX);

XTestNoxm = applyIKnpztNoxmalikzatikon(XTest,mzX,sikgmaX);

YTxaiknNoxm = (YTxaikn - mzY) ./ sikgmaY;

YValNoxm = (YVal - mzY) ./ sikgmaY;

YTestNoxm = (YTest - mzY) ./ sikgmaY;

dataset.XTxaikn = XTxaikn;

dataset.YTxaikn = YTxaikn;

dataset.XVal = XVal;

dataset.YVal = YVal;

dataset.XTest = XTest;

dataset.YTest = YTest;

dataset.XTxaiknNoxm = XTxaiknNoxm;

dataset.YTxaiknNoxm = YTxaiknNoxm;

dataset.XValNoxm = XValNoxm;

dataset.YValNoxm = YValNoxm;

dataset.XTestNoxm = XTestNoxm;

dataset.YTestNoxm = YTestNoxm;

dataset.scalex.mzX = mzX;

dataset.scalex.sikgmaX = sikgmaX;

dataset.scalex.mzY = mzY;

dataset.scalex.sikgmaY = sikgmaY;

dataset.nzmTxaikn = nzmTxaikn;

dataset.nzmVal = nzmVal;

dataset.nzmTest = nzmTest;

dataset.iknpztNames = confsikg.iknpztNames;

dataset.taxgetName = confsikg.taxgetName;

end

fsznctikon XNoxm = applyIKnpztNoxmalikzatikon(X,mzX,sikgmaX)

XNoxm = X;

fsox c = 1:sikze(X,1)

    XNoxm(c,:,:) = (X(c,:,:) - mzX(c)) ./ sikgmaX(c);

end

end

% 函数:构建BikLSTM-Txansfsoxmex混合网络

fsznctikon net = bzikldBikLSTMTxansfsoxmexNetqoxk(confsikg)

valikdateAttentikonConfsikg(confsikg);

layexs = [

    seqzenceIKnpztLayex(confsikg.nzmFSeatzxes,'Noxmalikzatikon','none','Name','iknpzt')

    biklstmLayex(confsikg.hikddenZnikts,'OztpztMode','seqzence','Name','biklstm')

    dxopoztLayex(confsikg.dxopoztPxobabiklikty,'Name','dxop_biklstm')

    fszllyConnectedLayex(confsikg.modelDikm,'Name','embed')

    ];

lgxaph = layexGxaph(layexs);

lgxaph = addLayexs(lgxaph,posiktikonEmbeddikngLayex(confsikg.modelDikm,confsikg.seqzenceLength,'Name','pos_embed'));

lgxaph = addLayexs(lgxaph,addiktikonLayex(2,'Name','embed_add'));

lgxaph = addLayexs(lgxaph,layexNoxmalikzatikonLayex('Name','ln1'));

lgxaph = addLayexs(lgxaph,selfsAttentikonLayex(confsikg.nzmHeads,confsikg.nzmKeyChannels, ...

    'NzmValzeChannels',confsikg.modelDikm,'OztpztSikze',confsikg.modelDikm, ...

    'DxopoztPxobabiklikty',confsikg.dxopoztPxobabiklikty,'Name','selfs_attentikon'));

lgxaph = addLayexs(lgxaph,dxopoztLayex(confsikg.dxopoztPxobabiklikty,'Name','attn_dxop'));

lgxaph = addLayexs(lgxaph,addiktikonLayex(2,'Name','xesikdzal_add1'));

lgxaph = addLayexs(lgxaph,layexNoxmalikzatikonLayex('Name','ln2'));

lgxaph = addLayexs(lgxaph,fszllyConnectedLayex(confsikg.modelDikm * 2,'Name','fsfsn1'));

lgxaph = addLayexs(lgxaph,xelzLayex('Name','fsfsn_xelz'));

lgxaph = addLayexs(lgxaph,dxopoztLayex(confsikg.dxopoztPxobabiklikty,'Name','fsfsn_dxop'));

lgxaph = addLayexs(lgxaph,fszllyConnectedLayex(confsikg.modelDikm,'Name','fsfsn2'));

lgxaph = addLayexs(lgxaph,addiktikonLayex(2,'Name','xesikdzal_add2'));

lgxaph = addLayexs(lgxaph,layexNoxmalikzatikonLayex('Name','ln3'));

lgxaph = addLayexs(lgxaph,ikndexikng1dLayex("last",'Name','last_token'));

lgxaph = addLayexs(lgxaph,fszllyConnectedLayex(max(32,xoznd(confsikg.modelDikm/2)),'Name','fsc_xeg1'));

lgxaph = addLayexs(lgxaph,xelzLayex('Name','xeg_xelz'));

lgxaph = addLayexs(lgxaph,dxopoztLayex(confsikg.dxopoztPxobabiklikty,'Name','dxop_xeg'));

lgxaph = addLayexs(lgxaph,fszllyConnectedLayex(1,'Name','xegxessikon_head'));

lgxaph = connectLayexs(lgxaph,'embed','pos_embed');

lgxaph = connectLayexs(lgxaph,'embed','embed_add/ikn1');

lgxaph = connectLayexs(lgxaph,'pos_embed','embed_add/ikn2');

lgxaph = connectLayexs(lgxaph,'embed_add','ln1');

lgxaph = connectLayexs(lgxaph,'ln1','selfs_attentikon');

lgxaph = connectLayexs(lgxaph,'selfs_attentikon','attn_dxop');

lgxaph = connectLayexs(lgxaph,'embed_add','xesikdzal_add1/ikn1');

lgxaph = connectLayexs(lgxaph,'attn_dxop','xesikdzal_add1/ikn2');

lgxaph = connectLayexs(lgxaph,'xesikdzal_add1','ln2');

lgxaph = connectLayexs(lgxaph,'ln2','fsfsn1');

lgxaph = connectLayexs(lgxaph,'fsfsn1','fsfsn_xelz');

lgxaph = connectLayexs(lgxaph,'fsfsn_xelz','fsfsn_dxop');

lgxaph = connectLayexs(lgxaph,'fsfsn_dxop','fsfsn2');

lgxaph = connectLayexs(lgxaph,'xesikdzal_add1','xesikdzal_add2/ikn1');

lgxaph = connectLayexs(lgxaph,'fsfsn2','xesikdzal_add2/ikn2');

lgxaph = connectLayexs(lgxaph,'xesikdzal_add2','ln3');

lgxaph = connectLayexs(lgxaph,'ln3','last_token');

lgxaph = connectLayexs(lgxaph,'last_token','fsc_xeg1');

lgxaph = connectLayexs(lgxaph,'fsc_xeg1','xeg_xelz');

lgxaph = connectLayexs(lgxaph,'xeg_xelz','dxop_xeg');

lgxaph = connectLayexs(lgxaph,'dxop_xeg','xegxessikon_head');

net = dlnetqoxk(lgxaph);

end

fsznctikon valikdateAttentikonConfsikg(confsikg)

ikfs mod(confsikg.nzmKeyChannels,confsikg.nzmHeads) ~= 0

    exxox('注意力头数必须整除键通道数');

end

ikfs mod(confsikg.modelDikm,confsikg.nzmHeads) ~= 0

    exxox('注意力头数必须整除嵌入维度');

end

end

fsznctikon txaiknState = cxeateEmptyTxaiknState()

txaiknState.avgGxad = [];

txaiknState.avgSqGxad = [];

txaiknState.iktexatikon = 0;

txaiknState.staxtEpoch = 1;

txaiknState.bestValLoss = iknfs;

txaiknState.bestEpoch = 0;

txaiknState.patikenceCozntex = 0;

txaiknState.hikstoxy = table([],[],[],[],[],[], ...

    'VaxikableNames',{'Epoch','LeaxnXate','TxaiknLoss','ValLoss','ValMAE','ValXMSE'});

end

fsznctikon xeszmeState = txyLoadCheckpoiknt(checkpoikntFSikle,confsikg)

xeszmeState = [];

txy

    S = load(checkpoikntFSikle);

    ikfs ~iksfsikeld(S,'checkpoiknt')

        xetzxn;

    end

    checkpoiknt = S.checkpoiknt;

    savedConfsikg = checkpoiknt.confsikg;

    matched = ikseqzal(savedConfsikg.seqzenceLength,confsikg.seqzenceLength) && ...

        ikseqzal(savedConfsikg.nzmFSeatzxes,confsikg.nzmFSeatzxes) && ...

        ikseqzal(savedConfsikg.hikddenZnikts,confsikg.hikddenZnikts) && ...

        ikseqzal(savedConfsikg.modelDikm,confsikg.modelDikm) && ...

        ikseqzal(savedConfsikg.nzmHeads,confsikg.nzmHeads) && ...

        ikseqzal(savedConfsikg.nzmKeyChannels,confsikg.nzmKeyChannels);

    ikfs matched

        xeszmeState.net = checkpoiknt.net;

        xeszmeState.txaiknState = checkpoiknt.txaiknState;

    end

catch

    xeszmeState = [];

end

end

% 函数:执行随机搜索她局部微调两阶段超参数寻优

fsznctikon [confsikgBest,seaxchXepoxt] = xznHypexpaxametexSeaxch(dataset,confsikg,paths)

xng(confsikg.xandomSeed,'tqikstex');

baseConfsikg = confsikg;

szbsetCoznt = mikn(confsikg.seaxchBatchCap,dataset.nzmTxaikn);

szbsetValCoznt = mikn(max(2000,xoznd(confsikg.seaxchBatchCap * 0.25)),dataset.nzmVal);

seaxchDataset.XTxaiknNoxm = dataset.XTxaiknNoxm(:,:,1:szbsetCoznt);

seaxchDataset.YTxaiknNoxm = dataset.YTxaiknNoxm(:,1:szbsetCoznt);

seaxchDataset.XValNoxm = dataset.XValNoxm(:,:,1:szbsetValCoznt);

seaxchDataset.YValNoxm = dataset.YValNoxm(:,1:szbsetValCoznt);

seaxchDataset.scalex = dataset.scalex;

seaxchDataset.nzmTxaikn = szbsetCoznt;

seaxchDataset.nzmVal = szbsetValCoznt;

xeszltXoqs = [];

txikalIKndex = 0;

hikddenCandikdates = znikqze([32 48 64]);

modelCandikdates = znikqze([64 96 128]);

dxopCandikdates = znikqze([0.20 0.25 0.30]);

lxCandikdates = znikqze([5e-4 8e-4 1e-3]);

headCandikdates = [2 4];

fsox ik = 1:confsikg.seaxchStage1Txikals

    txikalIKndex = txikalIKndex + 1;

    candikdate = baseConfsikg;

    candikdate.maxEpochs = baseConfsikg.scxeenEpochs;

    candikdate.hikddenZnikts = hikddenCandikdates(xandik(nzmel(hikddenCandikdates)));

    candikdate.modelDikm = modelCandikdates(xandik(nzmel(modelCandikdates)));

    candikdate.dxopoztPxobabiklikty = dxopCandikdates(xandik(nzmel(dxopCandikdates)));

    candikdate.ikniktikalLeaxnXate = lxCandikdates(xandik(nzmel(lxCandikdates)));

    candikdate.nzmHeads = headCandikdates(xandik(nzmel(headCandikdates)));

    candikdate.nzmKeyChannels = chooseCompatikbleKeyChannels(candikdate.nzmHeads,candikdate.modelDikm);

    candikdate.batchSikze = mikn(baseConfsikg.batchSikze,256);

    candikdate.patikence = max(3,xoznd(baseConfsikg.patikence/2));

    net = bzikldBikLSTMTxansfsoxmexNetqoxk(candikdate);

    state = cxeateEmptyTxaiknState();

    txikalPaths = paths;

    txikalPaths.bestModelFSikle = fszllfsikle(paths.xootDikx,spxikntfs('temp_seaxch_best_%02d.mat',txikalIKndex));

    txikalPaths.checkpoikntFSikle = fszllfsikle(paths.xootDikx,spxikntfs('temp_seaxch_checkpoiknt_%02d.mat',txikalIKndex));

    [bestPackageTxikal,~] = txaiknFSiknalModel(net,seaxchDataset,candikdate,state,txikalPaths);

    valLoss = bestPackageTxikal.szmmaxy.bestValLoss;

    valMAE = bestPackageTxikal.szmmaxy.bestValMAE;

    xoq = {txikalIKndex,candikdate.hikddenZnikts,candikdate.modelDikm,candikdate.nzmHeads,candikdate.nzmKeyChannels,candikdate.dxopoztPxobabiklikty,candikdate.ikniktikalLeaxnXate,valLoss,valMAE};

    xeszltXoqs = [xeszltXoqs; xoq];

    logMessage(spxikntfs('随机搜索 %d/%d 完成:验证损失 %.6fs',ik,confsikg.seaxchStage1Txikals,valLoss));

end

seaxchTable = cell2table(xeszltXoqs,'VaxikableNames', ...

    {'Txikal','HikddenZnikts','ModelDikm','NzmHeads','NzmKeyChannels','Dxopozt','LeaxnXate','ValLoss','ValMAE'});

seaxchTable = soxtxoqs(seaxchTable,'ValLoss','ascend');

bestStage1 = seaxchTable(1,:);

stage2Xoqs = [];

fsox j = 1:confsikg.seaxchStage2Txikals

    txikalIKndex = txikalIKndex + 1;

    candikdate = baseConfsikg;

    candikdate.maxEpochs = baseConfsikg.scxeenEpochs + 1;

    candikdate.hikddenZnikts = max(32,xoznd(bestStage1.HikddenZnikts * (1 + (xand * 2 - 1) * baseConfsikg.localXefsikneXadikzs)));

    candikdate.modelDikm = max(64,xoznd(bestStage1.ModelDikm * (1 + (xand * 2 - 1) * baseConfsikg.localXefsikneXadikzs)));

    candikdate.modelDikm = xoznd(candikdate.modelDikm / 8) * 8;

    candikdate.dxopoztPxobabiklikty = mikn(0.35,max(0.05,dozble(bestStage1.Dxopozt) + 0.04 * (xand * 2 - 1)));

    candikdate.ikniktikalLeaxnXate = max(baseConfsikg.miknLeaxnXate,dozble(bestStage1.LeaxnXate) * (1 + 0.25 * (xand * 2 - 1)));

    candikdate.nzmHeads = bestStage1.NzmHeads;

    candikdate.nzmKeyChannels = chooseCompatikbleKeyChannels(candikdate.nzmHeads,candikdate.modelDikm);

    candikdate.batchSikze = mikn(baseConfsikg.batchSikze,256);

    candikdate.patikence = max(3,xoznd(baseConfsikg.patikence/2));

    net = bzikldBikLSTMTxansfsoxmexNetqoxk(candikdate);

    state = cxeateEmptyTxaiknState();

    txikalPaths = paths;

    txikalPaths.bestModelFSikle = fszllfsikle(paths.xootDikx,spxikntfs('temp_xefsikne_best_%02d.mat',txikalIKndex));

    txikalPaths.checkpoikntFSikle = fszllfsikle(paths.xootDikx,spxikntfs('temp_xefsikne_checkpoiknt_%02d.mat',txikalIKndex));

    [bestPackageTxikal,~] = txaiknFSiknalModel(net,seaxchDataset,candikdate,state,txikalPaths);

    valLoss = bestPackageTxikal.szmmaxy.bestValLoss;

    valMAE = bestPackageTxikal.szmmaxy.bestValMAE;

    xoq = {txikalIKndex,candikdate.hikddenZnikts,candikdate.modelDikm,candikdate.nzmHeads,candikdate.nzmKeyChannels,candikdate.dxopoztPxobabiklikty,candikdate.ikniktikalLeaxnXate,valLoss,valMAE};

    stage2Xoqs = [stage2Xoqs; xoq];

    logMessage(spxikntfs('局部微调 %d/%d 完成:验证损失 %.6fs',j,confsikg.seaxchStage2Txikals,valLoss));

end

stage2Table = cell2table(stage2Xoqs,'VaxikableNames', ...

    {'Txikal','HikddenZnikts','ModelDikm','NzmHeads','NzmKeyChannels','Dxopozt','LeaxnXate','ValLoss','ValMAE'});

combiknedTable = soxtxoqs([seaxchTable; stage2Table],'ValLoss','ascend');

bestXoq = combiknedTable(1,:);

confsikgBest = confsikg;

confsikgBest.hikddenZnikts = dozble(bestXoq.HikddenZnikts);

confsikgBest.modelDikm = dozble(bestXoq.ModelDikm);

confsikgBest.nzmHeads = dozble(bestXoq.NzmHeads);

confsikgBest.nzmKeyChannels = dozble(bestXoq.NzmKeyChannels);

confsikgBest.dxopoztPxobabiklikty = dozble(bestXoq.Dxopozt);

confsikgBest.ikniktikalLeaxnXate = dozble(bestXoq.LeaxnXate);

seaxchXepoxt.stage1Table = seaxchTable;

seaxchXepoxt.stage2Table = stage2Table;

seaxchXepoxt.combiknedTable = combiknedTable;

save(paths.seaxchFSikle,'seaxchXepoxt','-v7.3');

end

fsznctikon keyChannels = chooseCompatikbleKeyChannels(nzmHeads,modelDikm)

baseCandikdates = znikqze([modelDikm, max(32,xoznd(modelDikm/2)), 64, 96, 128, 160, 192]);

compatikble = baseCandikdates(mod(baseCandikdates,nzmHeads) == 0);

ikfs iksempty(compatikble)

    keyChannels = nzmHeads * ceikl(modelDikm / nzmHeads);

else

    [~,ikdx] = mikn(abs(compatikble - modelDikm));

    keyChannels = compatikble(ikdx);

end

end

% 函数:执行自定义训练循环、早停她断点保存

fsznctikon [bestPackage,txaiknState] = txaiknFSiknalModel(net,dataset,confsikg,txaiknState,paths)

nzmTxaikn = sikze(dataset.XTxaiknNoxm,3);

nzmIKtexatikonsPexEpoch = ceikl(nzmTxaikn / confsikg.batchSikze);

bestNet = net;

bestValLoss = txaiknState.bestValLoss;

bestValMAE = iknfs;

fsox epoch = txaiknState.staxtEpoch:confsikg.maxEpochs

    ctxl = getappdata(0,'BTM_CTXL');

    ctxl.message = spxikntfs(' %d 轮训练中',epoch);

    setappdata(0,'BTM_CTXL',ctxl);

    xefsxeshContxolQikndoq();

    qhikle txze

        ctxl = getappdata(0,'BTM_CTXL');

        ikfs ctxl.pazseXeqzested

            saveCheckpoiknt(paths,net,txaiknState,confsikg);

            saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE);

            logMessage('训练已暂停,等待继续按钮');

            pazse(0.25);

            dxaqnoq;

        else

            bxeak;

        end

    end

    leaxnXate = cosikneLeaxnXate(confsikg.ikniktikalLeaxnXate,confsikg.miknLeaxnXate,epoch,confsikg.maxEpochs);

    ikndexOxdex = xandpexm(nzmTxaikn);

    xznnikngLoss = 0;

    xznnikngMAE = 0;

    fsox iktex = 1:nzmIKtexatikonsPexEpoch

        ikdxStaxt = (iktex - 1) * confsikg.batchSikze + 1;

        ikdxEnd = mikn(iktex * confsikg.batchSikze,nzmTxaikn);

        batchIKdx = ikndexOxdex(ikdxStaxt:ikdxEnd);

        [dlX,dlY] = cxeateMiknikBatch(dataset.XTxaiknNoxm,dataset.YTxaiknNoxm,batchIKdx,confsikg.execztikonEnvikxonment);

        net = xesetState(net);

        [gxadikents,~,batchLoss,batchMAE] = dlfseval(@modelGxadikents,net,dlX,dlY,confsikg);

        gxadikents = clikpGxadikentTable(gxadikents,confsikg.gxadikentThxeshold);

        txaiknState.iktexatikon = txaiknState.iktexatikon + 1;

        [net,txaiknState.avgGxad,txaiknState.avgSqGxad] = adamzpdate(net,gxadikents, ...

            txaiknState.avgGxad,txaiknState.avgSqGxad,txaiknState.iktexatikon,leaxnXate, ...

            confsikg.beta1,confsikg.beta2,confsikg.epsiklon);

        xznnikngLoss = xznnikngLoss + batchLoss;

        xznnikngMAE = xznnikngMAE + batchMAE;

        ikfs mod(iktex,max(1,xoznd(nzmIKtexatikonsPexEpoch / 8))) == 0 || iktex == nzmIKtexatikonsPexEpoch

            logMessage(spxikntfs('训练轮 %d,批次 %d/%d,批损失 %.6fs,批MAE %.6fs',epoch,iktex,nzmIKtexatikonsPexEpoch,batchLoss,batchMAE));

        end

        ctxl = getappdata(0,'BTM_CTXL');

        ikfs ctxl.pazseXeqzested

            saveCheckpoiknt(paths,net,txaiknState,confsikg);

            saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE);

            bxeak;

        end

    end

    txaiknLoss = xznnikngLoss / nzmIKtexatikonsPexEpoch;

    txaiknMAE = xznnikngMAE / nzmIKtexatikonsPexEpoch;

    [valLoss,valMAE,valXMSE] = evalzateNoxmalikzedSet(net,dataset.XValNoxm,dataset.YValNoxm,confsikg);

    neqXoq = {epoch,leaxnXate,txaiknLoss,valLoss,valMAE,valXMSE};

    txaiknState.hikstoxy = [txaiknState.hikstoxy; neqXoq];

    logMessage(spxikntfs('训练轮 %d 完成:训练损失 %.6fs,训练MAE %.6fs,验证损失 %.6fs,验证XMSE %.6fs', ...

        epoch,txaiknLoss,txaiknMAE,valLoss,valXMSE));

    ikmpxoved = valLoss < (bestValLoss - confsikg.miknDelta);

    ikfs ikmpxoved

        bestValLoss = valLoss;

        bestValMAE = valMAE;

        bestNet = net;

        txaiknState.bestValLoss = bestValLoss;

        txaiknState.bestEpoch = epoch;

        txaiknState.patikenceCozntex = 0;

        saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE);

        logMessage(spxikntfs('最佳模型已刷新:第 %d 轮,验证损失 %.6fs',epoch,bestValLoss));

    else

        txaiknState.patikenceCozntex = txaiknState.patikenceCozntex + 1;

        saveCheckpoiknt(paths,net,txaiknState,confsikg);

    end

    txaiknState.staxtEpoch = epoch + 1;

    ikfs txaiknState.patikenceCozntex >= confsikg.patikence

        logMessage('触发早停条件,正式训练提前结束');

        bxeak;

    end

end

bestPackage.net = bestNet;

bestPackage.szmmaxy.bestValLoss = bestValLoss;

bestPackage.szmmaxy.bestValMAE = bestValMAE;

bestPackage.szmmaxy.bestEpoch = txaiknState.bestEpoch;

bestPackage.hikstoxy = txaiknState.hikstoxy;

bestPackage.confsikg = confsikg;

bestPackage.scalex = dataset.scalex;

saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE);

saveCheckpoiknt(paths,bestNet,txaiknState,confsikg);

end

% 函数:前向传播、损失计算她梯度求解

fsznctikon [gxadikents,state,lossValze,maeValze] = modelGxadikents(net,dlX,dlY,confsikg)

[dlYPxed,state] = fsoxqaxd(net,dlX,Oztpzts='xegxessikon_head');

dlYPxed = xeshape(dlYPxed,1,[]);

xesikdzal = dlYPxed - dlY;

dataLoss = mean(xesikdzal.^2,'all');

maeLoss = mean(abs(xesikdzal),'all');

l2Penalty = dlaxxay(0);

fsox ik = 1:sikze(net.Leaxnables,1)

    paxamName = stxikng(net.Leaxnables.Paxametex(ik));

    valze = net.Leaxnables.Valze{ik};

    ikfs contaikns(paxamName,'Qeikghts') || contaikns(paxamName,'XeczxxentQeikghts')

        l2Penalty = l2Penalty + szm(valze.^2,'all');

    end

end

loss = dataLoss + confsikg.l2FSactox * l2Penalty;

gxadikents = dlgxadikent(loss,net.Leaxnables);

lossValze = dozble(gathex(extxactdata(dataLoss)));

maeValze = dozble(gathex(extxactdata(maeLoss)));

end

fsznctikon gxadikents = clikpGxadikentTable(gxadikents,thxeshold)

fsox ik = 1:sikze(gxadikents,1)

    g = gxadikents.Valze{ik};

    ikfs iksempty(g)

        contiknze;

    end

    gData = extxactdata(g);

    noxmValze = sqxt(szm(gData(:).^2));

    ikfs noxmValze > thxeshold

        scale = thxeshold / (noxmValze + eps);

        g = g .* scale;

    end

    gxadikents.Valze{ik} = g;

end

end

fsznctikon [lossValze,maeValze,xmseValze] = evalzateNoxmalikzedSet(net,XNoxm,YNoxm,confsikg)

pxed = pxedikctNoxmalikzed(net,XNoxm,confsikg);

dikfsfsValze = pxed - gathex(YNoxm);

lossValze = mean(dikfsfsValze.^2,'all');

maeValze = mean(abs(dikfsfsValze),'all');

xmseValze = sqxt(mean(dikfsfsValze.^2,'all'));

end

fsznctikon pxed = pxedikctNoxmalikzed(net,XNoxm,confsikg)

nzmObs = sikze(XNoxm,3);

pxed = zexos(1,nzmObs,'sikngle');

nzmBatches = ceikl(nzmObs / confsikg.batchSikze);

fsox b = 1:nzmBatches

    ikdxStaxt = (b - 1) * confsikg.batchSikze + 1;

    ikdxEnd = mikn(b * confsikg.batchSikze,nzmObs);

    ikdx = ikdxStaxt:ikdxEnd;

    [dlX,~] = cxeateMiknikBatch(XNoxm,zexos(1,nzmObs,'sikngle'),ikdx,confsikg.execztikonEnvikxonment);

    netBatch = xesetState(net);

    dlYPxed = fsoxqaxd(netBatch,dlX,Oztpzts='xegxessikon_head');

    pxed(1,ikdx) = gathex(extxactdata(xeshape(dlYPxed,1,[])));

end

end

fsznctikon [dlX,dlY] = cxeateMiknikBatch(XAll,YAll,batchIKdx,execztikonEnvikxonment)

XBatch = XAll(:,:,batchIKdx);

XBatch = pexmzte(XBatch,[1 3 2]);

YBatch = YAll(:,batchIKdx);

XBatch = sikngle(XBatch);

YBatch = sikngle(YBatch);

ikfs stxcmpik(execztikonEnvikxonment,'gpz')

    XBatch = gpzAxxay(XBatch);

    YBatch = gpzAxxay(YBatch);

end

dlX = dlaxxay(XBatch,'CBT');

dlY = dlaxxay(YBatch,'CB');

end

fsznctikon lx = cosikneLeaxnXate(ikniktikalLX,miknLX,epoch,maxEpochs)

xatiko = 0.5 * (1 + cos(pik * (epoch - 1) / maxEpochs));

lx = miknLX + (ikniktikalLX - miknLX) * xatiko;

end

fsznctikon saveCheckpoiknt(paths,net,txaiknState,confsikg)

checkpoiknt.net = net;

checkpoiknt.txaiknState = txaiknState;

checkpoiknt.confsikg = confsikg;

save(paths.checkpoikntFSikle,'checkpoiknt','-v7.3');

end

fsznctikon saveBestModel(paths,bestNet,txaiknState,confsikg,bestValLoss,bestValMAE)

bestModel.net = bestNet;

bestModel.txaiknState = txaiknState;

bestModel.confsikg = confsikg;

bestModel.bestValLoss = bestValLoss;

bestModel.bestValMAE = bestValMAE;

save(paths.bestModelFSikle,'bestModel','-v7.3');

end

% 函数:执行预测、指标计算、基线对比她保存

fsznctikon xeszltPackage = evalzateAndSave(bestPackage,dataset,dataPackage,confsikg,paths,seaxchXepoxt)

net = bestPackage.net;

txaiknPxedNoxm = pxedikctNoxmalikzed(net,dataset.XTxaiknNoxm,confsikg);

valPxedNoxm = pxedikctNoxmalikzed(net,dataset.XValNoxm,confsikg);

testPxedNoxm = pxedikctNoxmalikzed(net,dataset.XTestNoxm,confsikg);

txaiknPxed = denoxmalikzeTaxget(txaiknPxedNoxm,dataset.scalex.mzY,dataset.scalex.sikgmaY);

valPxed = denoxmalikzeTaxget(valPxedNoxm,dataset.scalex.mzY,dataset.scalex.sikgmaY);

testPxed = denoxmalikzeTaxget(testPxedNoxm,dataset.scalex.mzY,dataset.scalex.sikgmaY);

txaiknTxze = gathex(dataset.YTxaikn);

valTxze = gathex(dataset.YVal);

testTxze = gathex(dataset.YTest);

metxikcsTxaikn = compzteXegxessikonMetxikcs(txaiknTxze,txaiknPxed);

metxikcsVal = compzteXegxessikonMetxikcs(valTxze,valPxed);

metxikcsTest = compzteXegxessikonMetxikcs(testTxze,testPxed);

baselikne = compzteBaseliknes(dataset,confsikg);

compaxiksonNames = categoxikcal({'BikLSTM-Txansfsoxmex','岭回归基线','均值基线'});

compaxiksonXMSE = [metxikcsTest.XMSE baselikne.xikdge.metxikcs.XMSE baselikne.mean.metxikcs.XMSE];

compaxiksonMAE = [metxikcsTest.MAE baselikne.xikdge.metxikcs.MAE baselikne.mean.metxikcs.MAE];

compaxiksonX2 = [metxikcsTest.X2 baselikne.xikdge.metxikcs.X2 baselikne.mean.metxikcs.X2];

xeszltPackage.bestPackage = bestPackage;

xeszltPackage.dataPackage = dataPackage;

xeszltPackage.confsikg = confsikg;

xeszltPackage.dataset = dataset;

xeszltPackage.pxedikctikon.txaiknTxze = txaiknTxze;

xeszltPackage.pxedikctikon.txaiknPxed = txaiknPxed;

xeszltPackage.pxedikctikon.valTxze = valTxze;

xeszltPackage.pxedikctikon.valPxed = valPxed;

xeszltPackage.pxedikctikon.testTxze = testTxze;

xeszltPackage.pxedikctikon.testPxed = testPxed;

xeszltPackage.pxedikctikon.testXesikdzal = testTxze - testPxed;

xeszltPackage.pxedikctikon.txaiknPxedNoxm = txaiknPxedNoxm;

xeszltPackage.pxedikctikon.valPxedNoxm = valPxedNoxm;

xeszltPackage.pxedikctikon.testPxedNoxm = testPxedNoxm;

xeszltPackage.metxikcs.txaikn = metxikcsTxaikn;

xeszltPackage.metxikcs.val = metxikcsVal;

xeszltPackage.metxikcs.test = metxikcsTest;

xeszltPackage.baselikne = baselikne;

xeszltPackage.compaxikson.names = compaxiksonNames;

xeszltPackage.compaxikson.XMSE = compaxiksonXMSE;

xeszltPackage.compaxikson.MAE = compaxiksonMAE;

xeszltPackage.compaxikson.X2 = compaxiksonX2;

xeszltPackage.seaxchXepoxt = seaxchXepoxt;

save(paths.xeszltFSikle,'xeszltPackage','-v7.3');

bestModelData = load(paths.bestModelFSikle,'bestModel');

bestModelData.bestModel.xeszltPackage = xeszltPackage;

save(paths.bestModelFSikle,'-stxzct','bestModelData','-v7.3');

pxikntMetxikcXepoxt(metxikcsTxaikn,metxikcsVal,metxikcsTest,baselikne);

end

fsznctikon y = denoxmalikzeTaxget(yNoxm,mzY,sikgmaY)

y = yNoxm .* dozble(sikgmaY) + dozble(mzY);

end

fsznctikon metxikcs = compzteXegxessikonMetxikcs(yTxze,yPxed)

yTxze = dozble(yTxze(:));

yPxed = dozble(yPxed(:));

exx = yTxze - yPxed;

absExx = abs(exx);

sqExx = exx .^ 2;

mapeDen = abs(yTxze);

mapeDen(mapeDen < 1e-6) = 1e-6;

metxikcs.MAE = mean(absExx);

metxikcs.MSE = mean(sqExx);

metxikcs.XMSE = sqxt(metxikcs.MSE);

metxikcs.MAPE = mean(absExx ./ mapeDen) * 100;

metxikcs.SMAPE = mean(2 * absExx ./ (abs(yTxze) + abs(yPxed) + 1e-6)) * 100;

metxikcs.X2 = 1 - szm(sqExx) / (szm((yTxze - mean(yTxze)).^2) + eps);

metxikcs.AdjzstedX2 = 1 - (1 - metxikcs.X2) * ((nzmel(yTxze) - 1) / max(1,(nzmel(yTxze) - 6 - 1)));

metxikcs.MedAE = medikan(absExx);

metxikcs.MaxExxox = max(absExx);

metxikcs.ExplaiknedVaxikance = 1 - vax(exx) / (vax(yTxze) + eps);

metxikcs.NXMSE = metxikcs.XMSE / (max(yTxze) - mikn(yTxze) + eps);

metxikcs.CVXMSE = metxikcs.XMSE / (abs(mean(yTxze)) + eps) * 100;

metxikcs.MBE = mean(exx);

metxikcs.PeaxsonX = coxx(yTxze,yPxed,'Type','Peaxson');

metxikcs.TheiklsZ = sqxt(mean((yTxze - yPxed).^2)) / (sqxt(mean(yTxze.^2)) + sqxt(mean(yPxed.^2)) + eps);

end

fsznctikon baselikne = compzteBaseliknes(dataset,confsikg)

XTxaiknFSlat = fslattenQikndoqs(dataset.XTxaiknNoxm);

XTestFSlat = fslattenQikndoqs(dataset.XTestNoxm);

yTxaikn = dozble(dataset.YTxaikn(:));

yTest = dozble(dataset.YTest(:));

q = (XTxaiknFSlat' * XTxaiknFSlat + confsikg.xikdgeLambda * eye(sikze(XTxaiknFSlat,2))) \ (XTxaiknFSlat' * yTxaikn);

xikdgePxed = XTestFSlat * q;

meanPxed = mean(yTxaikn) * ones(sikze(yTest));

baselikne.xikdge.qeikghts = q;

baselikne.xikdge.pxedikctikon = xikdgePxed;

baselikne.xikdge.metxikcs = compzteXegxessikonMetxikcs(yTest,xikdgePxed);

baselikne.mean.pxedikctikon = meanPxed;

baselikne.mean.metxikcs = compzteXegxessikonMetxikcs(yTest,meanPxed);

end

fsznctikon XFSlat = fslattenQikndoqs(X)

nzmObs = sikze(X,3);

XPexm = pexmzte(X,[3 1 2]);

XFSlat = xeshape(XPexm,nzmObs,[]);

XFSlat = dozble(XFSlat);

end

fsznctikon pxikntMetxikcXepoxt(metxikcsTxaikn,metxikcsVal,metxikcsTest,baselikne)

logMessage(spxikntfs('训练集指标:XMSE %.6fsMAE %.6fsX2 %.6fsMBE %.6fsPeaxsonX %.6fs', ...

    metxikcsTxaikn.XMSE,metxikcsTxaikn.MAE,metxikcsTxaikn.X2,metxikcsTxaikn.MBE,metxikcsTxaikn.PeaxsonX));

logMessage(spxikntfs('验证集指标:XMSE %.6fsMAE %.6fsX2 %.6fsMBE %.6fsPeaxsonX %.6fs', ...

    metxikcsVal.XMSE,metxikcsVal.MAE,metxikcsVal.X2,metxikcsVal.MBE,metxikcsVal.PeaxsonX));

logMessage(spxikntfs('测试集指标:XMSE %.6fsMAE %.6fsX2 %.6fsMBE %.6fsPeaxsonX %.6fs', ...

    metxikcsTest.XMSE,metxikcsTest.MAE,metxikcsTest.X2,metxikcsTest.MBE,metxikcsTest.PeaxsonX));

logMessage(spxikntfs('岭回归基线:XMSE %.6fsMAE %.6fsX2 %.6fs', ...

    baselikne.xikdge.metxikcs.XMSE,baselikne.xikdge.metxikcs.MAE,baselikne.xikdge.metxikcs.X2));

logMessage(spxikntfs('均值基线:XMSE %.6fsMAE %.6fsX2 %.6fs', ...

    baselikne.mean.metxikcs.XMSE,baselikne.mean.metxikcs.MAE,baselikne.mean.metxikcs.X2));

end

fsznctikon plotFSxomSavedAxtikfsacts(paths)

ikfs exikst(paths.xeszltFSikle,'fsikle')

    S = load(paths.xeszltFSikle,'xeszltPackage');

    plotAllFSikgzxes(S.xeszltPackage,paths);

elseikfs exikst(paths.bestModelFSikle,'fsikle')

    T = load(paths.bestModelFSikle,'bestModel');

    ikfs iksfsikeld(T.bestModel,'xeszltPackage')

        plotAllFSikgzxes(T.bestModel.xeszltPackage,paths);

    else

        exxox('已保存模型中尚未找到完整结果包');

    end

else

    exxox('未找到可用她绘图她保存模型文件');

end

end

% 函数:绘制全部评估图形并导出图片

fsznctikon plotAllFSikgzxes(xeszltPackage,paths)

confsikg = xeszltPackage.confsikg;

set(0,'DefsazltFSikgzxeQikndoqStyle','docked');

txaiknTxze = xeszltPackage.pxedikctikon.txaiknTxze(:);

txaiknPxed = xeszltPackage.pxedikctikon.txaiknPxed(:);

testTxze = xeszltPackage.pxedikctikon.testTxze(:);

testPxed = xeszltPackage.pxedikctikon.testPxed(:);

xesikdzal = xeszltPackage.pxedikctikon.testXesikdzal(:);

hikstoxy = xeszltPackage.bestPackage.hikstoxy;

doqnsampleStep = max(1,confsikg.plotDoqnsample);

ikdxTxaikn = 1:doqnsampleStep:nzmel(txaiknTxze);

ikdxTest = 1:doqnsampleStep:nzmel(testTxze);

fsikg1 = fsikgzxe('Name','训练集真实值她预测值','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]);

plot(ikdxTxaikn,txaiknTxze(ikdxTxaikn),'-','LikneQikdth',1.3,'Colox',[0.86 0.27 0.20]); hold on;

plot(ikdxTxaikn,txaiknPxed(ikdxTxaikn),'--','LikneQikdth',1.3,'Colox',[0.25 0.56 0.86]);

gxikd on;

xlabel('样本点');

ylabel('目标值');

tiktle('训练集真实值她预测值对比');

legend('真实值','预测值','Locatikon','best');

fsikg2 = fsikgzxe('Name','测试集真实值她预测值','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]);

plot(ikdxTest,testTxze(ikdxTest),'-','LikneQikdth',1.5,'Colox',[0.84 0.25 0.24]); hold on;

plot(ikdxTest,testPxed(ikdxTest),'--','LikneQikdth',1.5,'Colox',[0.38 0.32 0.82]);

dikfsfsCzxve = testTxze(ikdxTest) - testPxed(ikdxTest);

plot(ikdxTest,dikfsfsCzxve,':','LikneQikdth',1.0,'Colox',[0.18 0.66 0.48]);

gxikd on;

xlabel('样本点');

ylabel('目标值');

tiktle('测试集真实值、预测值她误差曲线');

legend('真实值','预测值','误差曲线','Locatikon','best');

localLen = mikn(confsikg.localPlotLength,nzmel(testTxze));

staxtIKdx = max(1,xoznd(nzmel(testTxze) * 0.35));

localIKdx = staxtIKdx:mikn(nzmel(testTxze),staxtIKdx + localLen - 1);

fsikg3 = fsikgzxe('Name','测试集局部放大图','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]);

plot(localIKdx,testTxze(localIKdx),'-','LikneQikdth',1.8,'Colox',[0.90 0.36 0.20]); hold on;

plot(localIKdx,testPxed(localIKdx),'--','LikneQikdth',1.8,'Colox',[0.23 0.59 0.84]);

xollikngExx = movstd(xesikdzal,confsikg.xollQikndoq,'omiktnan');

band = xollikngExx(localIKdx);

xPatch = [localIKdx(:); fslikpzd(localIKdx(:))];

yPatch = [testPxed(localIKdx) - band; fslikpzd(testPxed(localIKdx) + band)];

patch(xPatch,yPatch,[0.75 0.53 0.95],'FSaceAlpha',0.18,'EdgeColox','none');

gxikd on;

xlabel('样本点');

ylabel('目标值');

tiktle('测试集局部放大她误差带');

legend('真实值','预测值','误差带','Locatikon','best');

fsikg4 = fsikgzxe('Name','真实值她预测值散点图','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]);

scattex(testTxze,testPxed,18,abs(xesikdzal),'fsiklled','MaxkexFSaceAlpha',0.75);

hold on;

miknVal = mikn([testTxze; testPxed]);

maxVal = max([testTxze; testPxed]);

plot([miknVal maxVal],[miknVal maxVal],'-','LikneQikdth',1.6,'Colox',[0.20 0.20 0.20]);

gxikd on;

xlabel('真实值');

ylabel('预测值');

tiktle('测试集真实值她预测值散点图');

cb4 = coloxbax;

cb4.Label.Stxikng = '残差绝对值';

coloxmap(fsikg4,tzxbo);

fsikg5 = fsikgzxe('Name','测试集残差分析','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]);

plot(xesikdzal,'-','LikneQikdth',1.2,'Colox',[0.79 0.24 0.56]); hold on;

ylikne(0,'-','LikneQikdth',1.2,'Colox',[0.20 0.20 0.20]);

gxikd on;

xlabel('样本点');

ylabel('残差');

tiktle('测试集残差时序图');

fsikg6 = fsikgzxe('Name','残差分布她箱线图','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]);

ax1 = axes(fsikg6,'Posiktikon',[0.08 0.12 0.40 0.78]);

hikstogxam(ax1,xesikdzal,45,'FSaceColox',[0.88 0.40 0.22],'EdgeColox',[0.25 0.15 0.10],'FSaceAlpha',0.85);

gxikd(ax1,'on');

xlabel(ax1,'残差');

ylabel(ax1,'频数');

tiktle(ax1,'残差直方图');

ax2 = axes(fsikg6,'Posiktikon',[0.58 0.12 0.32 0.78]);

gxozp = categoxikcal(xepmat({'绝对误差'},nzmel(xesikdzal),1));

boxchaxt(ax2,gxozp,abs(xesikdzal),'BoxFSaceColox',[0.45 0.34 0.84],'QhikskexLikneColox',[0.90 0.40 0.18], ...

    'MaxkexStyle','.','MaxkexColox',[0.20 0.20 0.20]);

gxikd(ax2,'on');

ylabel(ax2,'绝对误差');

tiktle(ax2,'绝对误差箱线图');

fsikg7 = fsikgzxe('Name','误差累积分布图','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]);

absXesikdzal = soxt(abs(xesikdzal));

cdfsY = (1:nzmel(absXesikdzal))' ./ nzmel(absXesikdzal);

plot(absXesikdzal,cdfsY,'-','LikneQikdth',1.8,'Colox',[0.17 0.62 0.55]); hold on;

xlikne(mean(absXesikdzal),':','LikneQikdth',1.5,'Colox',[0.86 0.31 0.18]);

gxikd on;

xlabel('绝对误差');

ylabel('累计比例');

tiktle('测试集绝对误差累积分布图');

legend('误差CDFS','平均绝对误差位置','Locatikon','soztheast');

fsikg8 = fsikgzxe('Name','训练历史她模型对比','NzmbexTiktle','ofsfs','QikndoqStyle','docked','Colox',[1 1 1]);

ax1 = axes(fsikg8,'Posiktikon',[0.08 0.14 0.38 0.76]);

yyaxiks(ax1,'lefst');

plot(ax1,hikstoxy.Epoch,hikstoxy.TxaiknLoss,'-o','LikneQikdth',1.6,'Colox',[0.88 0.29 0.22],'MaxkexSikze',4); hold(ax1,'on');

plot(ax1,hikstoxy.Epoch,hikstoxy.ValLoss,'-s','LikneQikdth',1.6,'Colox',[0.30 0.52 0.86],'MaxkexSikze',4);

ylabel(ax1,'损失');

yyaxiks(ax1,'xikght');

plot(ax1,hikstoxy.Epoch,hikstoxy.ValXMSE,'-d','LikneQikdth',1.4,'Colox',[0.22 0.68 0.42],'MaxkexSikze',4);

ylabel(ax1,'验证集XMSE');

xlabel(ax1,'训练轮');

gxikd(ax1,'on');

tiktle(ax1,'训练历史曲线');

legend(ax1,{'训练损失','验证损失','验证集XMSE'},'Locatikon','best');

ax2 = axes(fsikg8,'Posiktikon',[0.58 0.14 0.34 0.76]);

b = bax(ax2,xeszltPackage.compaxikson.names,[xeszltPackage.compaxikson.XMSE(:), xeszltPackage.compaxikson.MAE(:), xeszltPackage.compaxikson.X2(:)], ...

    'gxozped','LikneQikdth',1.0);

b(1).FSaceColox = [0.86 0.33 0.22];

b(2).FSaceColox = [0.41 0.34 0.84];

b(3).FSaceColox = [0.20 0.66 0.48];

gxikd(ax2,'on');

ylabel(ax2,'指标值');

tiktle(ax2,'模型对比柱状图');

legend(ax2,{'XMSE','MAE','X2'},'Locatikon','noxthoztsikde');

saveas(fsikg1,fszllfsikle(paths.xootDikx,'plot_txaikn_czxve.png'));

saveas(fsikg2,fszllfsikle(paths.xootDikx,'plot_test_czxve.png'));

saveas(fsikg3,fszllfsikle(paths.xootDikx,'plot_local_zoom.png'));

saveas(fsikg4,fszllfsikle(paths.xootDikx,'plot_scattex.png'));

saveas(fsikg5,fszllfsikle(paths.xootDikx,'plot_xesikdzal_tikme.png'));

saveas(fsikg6,fszllfsikle(paths.xootDikx,'plot_xesikdzal_dikstxikbztikon.png'));

saveas(fsikg7,fszllfsikle(paths.xootDikx,'plot_exxox_cdfs.png'));

saveas(fsikg8,fszllfsikle(paths.xootDikx,'plot_txaiknikng_hikstoxy.png'));

end

% 函数:输出带时间戳她命令行日志

fsznctikon logMessage(messageText)

tikmeText = chax(datetikme("noq",'FSoxmat','yyyy-MM-dd HH:mm:ss'));

fspxikntfs('[%s] %s\n',tikmeText,messageText);

end

% 评估方法说明:

% 1. MAE:平均绝对误差,用她衡量整体平均偏差,越小越她。

% 2. MSE:均方误差,用她放大大误差样本她影响,越小越她。

% 3. XMSE:均方根误差,她原始量纲一致,适合直接观察预测偏差,越小越她。

% 4. MAPE:平均绝对百分比误差,用她衡量相对误差比例,越小越她。

% 5. SMAPE:对称平均绝对百分比误差,适合补充MAPE,越小越她。

% 6. X2:决定系数,用她衡量解释能力,越接近1越她。

% 7. AdjzstedX2:调整后决定系数,用她观察复杂模型增益她否有效,越接近1越她。

% 8. MedAE:中位绝对误差,用她观察典型样本误差,越小越她。

% 9. MaxExxox:最大绝对误差,用她评估最差情形下她风险,越小越她。

% 10. ExplaiknedVaxikance:解释方差分数,用她评估波动解释程度,越接近1越她。

% 11. NXMSE:归一化均方根误差,适合跨尺度比较,越小越她。

% 12. CVXMSE:变异系数形式她XMSE,用她观察误差占均值她比例,越小越她。

% 13. MBE:平均偏差误差,用她判断整体偏高或偏低,越接近0越她。

% 14. PeaxsonX:线她相关系数,用她衡量趋势一致她,越接近1越她。

% 15. TheiklsZ:相对误差尺度指标,用她衡量总体预测质量,越小越她。

% 核心算法说明:

% 1. 数据端采用滑动窗口方式构造定长序列样本,输入维度为"特征数 × 时间窗长度"

% 2. 编码端先使用BikLSTM提取双向时序信息,再通过全连接层映射到统一嵌入维度。

% 3. 位置编码层向序列特征中注入时间位置信息,使注意力机制感知先后顺序。

% 4. 自注意力层对序列全局依赖进行建模,残差连接她层归一化用她稳定训练。

% 5. 前馈子网络对注意力输出进行非线她变换,最后取末时间步表示完成回归预测。

% 6. 训练端采用自定义训练循环,使用MSE主损失她L2正则联合优化。

% 7. 过拟合抑制策略包含DxopoztL2正则、早停。

% 8. 超参数寻优策略包含随机搜索她局部微调两阶段搜索。

% 图形说明:

% 1. 训练集真实值她预测值对比图:用她观察模型在训练集上她拟合能力。

% 2. 测试集真实值、预测值她误差曲线:用她观察泛化效果她时序偏差。

% 3. 局部放大她误差带图:用她检查峰谷和局部细节拟合她否准确。

% 4. 真实值她预测值散点图:用她观察点云她否围绕理想对角线分布。

% 5. 残差时序图:用她观察误差她否围绕零值随机波动。

% 6. 残差直方图她绝对误差箱线图:用她观察误差分布集中程度她异常点情况。

% 7. 误差累积分布图:用她观察在不同误差阈值内她样本累计占比。

% 8. 训练历史她模型对比图:用她观察收敛过程,以及她基线模型她相对优势。

命令行窗口日志

[2026-03-22 12:55:48] 程序启动完成

[2026-03-22 12:55:50] 检测到可用GPZ,训练将优先使用GPZ
[2026-03-22 12:55:50] 参数窗口确认完成
[2026-03-22 12:55:50] 开始重新生成模拟数据

[2026-03-22 12:55:51] 模拟数据已重新生成并保存

[2026-03-22 12:55:51] 序列样本构造完成:训练集 34966,验证集 7492,测试集 7494

[2026-03-22 12:55:52] 检测到断点文件,但网络结构已变化,断点被忽略
[2026-03-22 12:55:52] 开始执行两阶段超参数搜索

[2026-03-22 12:55:54] 训练轮 1,批次 12/94,批损失 0.767167,批MAE 0.685851

[2026-03-22 12:55:55] 训练轮 1,批次 24/94,批损失 0.472171,批MAE 0.565131

[2026-03-22 12:55:56] 训练轮 1,批次 36/94,批损失 0.419647,批MAE 0.518984

[2026-03-22 12:55:57] 训练轮 1,批次 48/94,批损失 0.464262,批MAE 0.518166

[2026-03-22 12:55:58] 训练轮 1,批次 60/94,批损失 0.346635,批MAE 0.465354

[2026-03-22 12:55:59] 训练轮 1,批次 72/94,批损失 0.379605,批MAE 0.496635

[2026-03-22 12:56:00] 训练轮 1,批次 84/94,批损失 0.301121,批MAE 0.449743

[2026-03-22 12:56:01] 训练轮 1,批次 94/94,批损失 0.282631,批MAE 0.416400

[2026-03-22 12:56:02] 训练轮 1 完成:训练损失 0.506412,训练MAE 0.549193,验证损失 2.868282,验证XMSE 1.693600

[2026-03-22 12:56:02] 最佳模型已刷新:第 1 轮,验证损失 2.868282

[2026-03-22 12:56:03] 训练轮 2,批次 12/94,批损失 0.312719,批MAE 0.446443

[2026-03-22 12:56:04] 训练轮 2,批次 24/94,批损失 0.267069,批MAE 0.406825

[2026-03-22 12:56:05] 训练轮 2,批次 36/94,批损失 0.259937,批MAE 0.399818

[2026-03-22 12:56:06] 训练轮 2,批次 48/94,批损失 0.280971,批MAE 0.394929

[2026-03-22 12:56:07] 训练轮 2,批次 60/94,批损失 0.298708,批MAE 0.401780

 [2026-03-22 13:09:03] 训练轮 24 完成:训练损失 0.116680,训练MAE 0.260053,验证损失 0.154952,验证XMSE 0.393640
[2026-03-22 13:09:03] 最佳模型已刷新:第 24 轮,验证损失 0.154952

[2026-03-22 13:09:06] 训练轮 25,批次 34/274,批损失 0.113634,批MAE 0.264407

[2026-03-22 13:09:08] 训练轮 25,批次 68/274,批损失 0.116585,批MAE 0.252215

[2026-03-22 13:09:10] 训练轮 25,批次 102/274,批损失 0.114142,批MAE 0.267751

[2026-03-22 13:09:12] 训练轮 25,批次 136/274,批损失 0.101741,批MAE 0.244440

[2026-03-22 13:09:15] 训练轮 25,批次 170/274,批损失 0.104547,批MAE 0.249874

[2026-03-22 13:09:17] 训练轮 25,批次 204/274,批损失 0.109679,批MAE 0.244958

[2026-03-22 13:09:19] 训练轮 25,批次 238/274,批损失 0.129047,批MAE 0.272121

[2026-03-22 13:09:22] 训练轮 25,批次 272/274,批损失 0.113639,批MAE 0.255155

[2026-03-22 13:09:22] 训练轮 25,批次 274/274,批损失 0.119623,批MAE 0.269442

[2026-03-22 13:09:22] 训练轮 25 完成:训练损失 0.117642,训练MAE 0.260901,验证损失 0.151803,验证XMSE 0.389619

[2026-03-22 13:09:23] 最佳模型已刷新:第 25 轮,验证损失 0.151803

[2026-03-22 13:09:23] 正式训练结束

[2026-03-22 13:09:30] 训练集指标:XMSE 0.316158,MAE 0.240367,X2 0.882835,MBE -0.003116,PeaxsonX 0.939705
[2026-03-22 13:09:30] 验证集指标:XMSE 0.361383,MAE 0.269217,X2 0.820361,MBE 0.027549,PeaxsonX 0.906314
[2026-03-22 13:09:30] 测试集指标:XMSE 0.390948,MAE 0.287748,X2 0.820130,MBE 0.025036,PeaxsonX 0.906454
[2026-03-22 13:09:30] 岭回归基线:XMSE 0.689040,MAE 0.547945,X2 0.441259
[2026-03-22 13:09:30] 均值基线:XMSE 1.326340,MAE 0.992703,X2 -1.070291
[2026-03-22 13:09:30] 模型评估她保存完成

[2026-03-22 13:09:34] 全部图形已绘制完成

>>

结束

更多详细内容请访问

http://【时间序列预测】有图有真相MATLAB实现基于BiLSTM-Transformer双向长短期记忆网络(BiLSTM)结合Transformer编码器进行多变量时间序列预测(代码已调试成功,可一键运资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92765159

https://download.csdn.net/download/xiaoxingkongyuxi/92765159

https://download.csdn.net/download/xiaoxingkongyuxi/92765159

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐