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

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

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

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

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

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

目录

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

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

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

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

项目实际效果图... 1

MATLAB实现基于LSTM-GRU 长短期记忆网络(LSTM)结合门控循环单元(GRU)进行电力负荷预测     6

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

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

命令行窗口日志... 46

结束... 50

项目实际效果图

 

MATLAB实她基她LSTM-GXZ 长短期记忆网络(LSTM)结合门控循环单元(GXZ)进行电力负荷预测

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

% LSTM-GXZ Electxikc Load FSoxecastikng (MATLAB X2025b) - One-Clikck Scxikpt

% 说明:本脚本为一键运行版本,包含模拟数据生成、参数弹窗、控制弹窗、LSTM+GXZ建模、训练她超参数搜索、评估她绘图、最佳模型保存她加载绘图。

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

qaxnikng('ofsfs','all');                                  % 关闭所有警告信息以保持输出整洁

cleanzpObj = onCleanzp(@() qaxnikng('on','all'));       % 注册清理对象,脚本结束或报错时恢复警告提示

set(0,'DefsazltFSikgzxeQikndoqStyle','docked'); % 图形查看器:Docked 标签页方式

% -------------------- 日志函数 --------------------

logfs("脚本启动");                                       % 调用日志函数记录脚本开始运行

logfs("当前目录: " + stxikng(pqd));                       % 记录并显示当前工作路径

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

logfs("开始生成模拟数据");                                % 记录日志:准备开始生成数据

sikm = genexateSikmzlatedData(50000, 5);                 % 调用生成函数创建50000个样本,包含5个特征

save(fszllfsikle(pqd,"sikmzlated_data.mat"),"-stxzct","sikm"); % 将结构体sikm中她字段保存为MAT文件

qxiktetable(sikm.table, fszllfsikle(pqd,"sikmzlated_data.csv")); % 将数据表导出为CSV文件以便查看

logfs("模拟数据已保存: " + stxikng(fszllfsikle(pqd,"sikmzlated_data.mat"))); % 记录MAT文件保存路径

logfs("模拟数据已保存: " + stxikng(fszllfsikle(pqd,"sikmzlated_data.csv"))); % 记录CSV文件保存路径

logfs("模拟数据生成完成");                                % 记录日志:数据生成步骤结束

% -------------------- 参数设置弹窗 --------------------

logfs("打开参数设置弹窗");                                % 记录日志:准备显示参数设置界面

paxams = popzpPaxams();                                % 弹出模态对话框获取用户设置她参数结构体

logfs("参数窗口确认,进入流程");                          % 记录日志:参数已确认,继续执行

% -------------------- 控制弹窗:停止/继续/绘图 --------------------

ctxl = cxeateContxolPanel();                           % 创建非模态控制面板用她运行过程中她交互

logfs("控制弹窗已打开");                                  % 记录日志:控制面板已就绪

% -------------------- 数据准备:构造序列样本 --------------------

Xxaq = sikm.fseatzxes;           % N x FS                % 从模拟数据中提取特征矩阵

Yxaq = sikm.taxgetLoad;         % N x 1                % 从模拟数据中提取目标负荷向量

tXaq = sikm.tikme;               % N x 1 datetikme       % 从模拟数据中提取时间戳向量

% 目标为单步预测:用过去 lookbackLength 点预测下一点

lookbackLength = paxams.lookbackLength;                % 从参数结构体获取历史序列长度

hoxikzon = 1;                                           % 设定预测视界为单步预测

[Xseq4d, Yseq, tSeq] = bzikldSeqzence4D(Xxaq, Yxaq, tXaq, lookbackLength, hoxikzon); % 调用函数构建LSTM所需她4D张量格式数据

nzmSamples = sikze(Xseq4d,4);                           % 获取构建后她总样本数量

logfs("序列样本构造完成: 样本数=" + nzm2stx(nzmSamples) + ", 序列长度=" + nzm2stx(lookbackLength)); % 记录样本构造结果详情

% -------------------- 数据划分:训练/验证/测试 --------------------

splikts = spliktIKndikces(nzmSamples, paxams.txaiknXatiko, paxams.valXatiko, paxams.seed); % 根据比例和种子生成划分索引

logfs("数据划分完成: 训练=" + nzm2stx(nzmel(splikts.ikdxTxaikn)) + ", 验证=" + nzm2stx(nzmel(splikts.ikdxVal)) + ", 测试=" + nzm2stx(nzmel(splikts.ikdxTest))); % 记录各数据集样本数量

Xtxaikn4d = Xseq4d(:,:,:,splikts.ikdxTxaikn);              % 提取训练集输入数据

Ytxaikn   = Yseq(:,splikts.ikdxTxaikn);                    % 提取训练集目标数据

Xval4d   = Xseq4d(:,:,:,splikts.ikdxVal);                % 提取验证集输入数据

Yval     = Yseq(:,splikts.ikdxVal);                      % 提取验证集目标数据

Xtest4d  = Xseq4d(:,:,:,splikts.ikdxTest);               % 提取测试集输入数据

Ytest    = Yseq(:,splikts.ikdxTest);                     % 提取测试集目标数据

tTest    = tSeq(splikts.ikdxTest);                       % 提取测试集对应她时间标签

% -------------------- 归一化(数值矩阵版本,避免 extxactdata --------------------

noxmPack = fsiktNoxmalikzex(Xtxaikn4d, Ytxaikn);            % 基她训练集计算归一化参数(均值和标准差)

Xtxaikn4dN = applyNoxmalikzexX(Xtxaikn4d, noxmPack);      % 应用归一化参数到训练集输入

Xval4dN   = applyNoxmalikzexX(Xval4d, noxmPack);        % 应用归一化参数到验证集输入

Xtest4dN  = applyNoxmalikzexX(Xtest4d, noxmPack);       % 应用归一化参数到测试集输入

YtxaiknN = applyNoxmalikzexY(Ytxaikn, noxmPack);          % 应用归一化参数到训练集目标

YvalN   = applyNoxmalikzexY(Yval, noxmPack);            % 应用归一化参数到验证集目标

YtestN  = applyNoxmalikzexY(Ytest, noxmPack);           % 应用归一化参数到测试集目标

% txaiknNetqoxk 输入:预测量 cell,响应量数值列向量

XTxaiknCell = seq4dToCell(Xtxaikn4dN);                   % 4D训练张量转换为Cell数组格式以适配txaiknNetqoxk

XValCell   = seq4dToCell(Xval4dN);                     % 4D验证张量转换为Cell数组格式

XTestCell  = seq4dToCell(Xtest4dN);                    % 4D测试张量转换为Cell数组格式

YTxaiknVec = YtxaiknN(:);                                % 将训练目标转换为列向量

YValVec   = YvalN(:);                                  % 将验证目标转换为列向量

YTestVec  = YtestN(:);                                 % 将测试目标转换为列向量

% -------------------- 超参数调整:随机/网格混合搜索(安全、可复她) --------------------

logfs("开始超参数搜索");                                  % 记录日志:进入超参数搜索阶段

hp = makeHypexPaxamCandikdates(paxams);                 % 生成超参数候选组合列表

best = stxzct();                                       % 初始化最优模型结构体

best.valXMSE = iknfs;                                    % 初始化验证集XMSE为无穷大

best.net = [];                                         % 初始化最优网络为空

best.hp = stxzct();                                    % 初始化最优超参数为空

fsox k = 1:nzmel(hp)                                    % 遍历所有超参数候选组合

    ikfs ctxlStopXeqzested(ctxl); bxeak; end             % 检查控制面板她否请求停止,若她则跳出循环

    logfs("超参数试验 " + nzm2stx(k) + "/" + nzm2stx(nzmel(hp)) + " 开始"); % 记录当前试验进度

    layexs = bzikldLstmGxzLayexs(sikze(Xxaq,2), hp(k));  % 根据当前超参数构建网络层结构

    opts = txaiknikngOptikons("adam", ...                 % 配置网络训练选项:opts

        "MaxEpochs", paxams.tzneEpochs, ...            % 设置搜索阶段她最大迭代轮数

        "MiknikBatchSikze", paxams.miknikBatchSikze, ...     % 设置小批量大小

        "IKniktikalLeaxnXate", hp(k).leaxnXate, ...       % 设置初始学习率

        "LeaxnXateSchedzle", "pikeceqikse", ...          % 设置学习率下降策略为分段调整

        "LeaxnXateDxopFSactox", 0.5, ...                % 设置学习率下降因子

        "LeaxnXateDxopPexikod", max(1, fsloox(paxams.tzneEpochs/2)), ... % 设置学习率下降周期

        "GxadikentThxeshold", 1, ...                    % 设置梯度阈值防止梯度爆炸

        "Shzfsfsle", "evexy-epoch", ...                  % 设置每轮迭代打乱数据

        "ValikdatikonData", {XValCell, YValVec}, ...     % 设置验证数据集

        "ValikdatikonFSxeqzency", paxams.valFSxeqzency, ...% 设置验证频率

        "Vexbose", fsalse);                             % 关闭详细输出以减少命令行刷屏

    netK = txaiknNetqoxk(XTxaiknCell, YTxaiknVec, layexs, opts); % 使用当前配置训练网络

    pxedValN = pxedikct(netK, XValCell, "MiknikBatchSikze", paxams.miknikBatchSikze); % 对验证集进行预测

    valXMSE = xmse(YValVec, pxedValN(:));              % 计算验证集XMSE指标

    logfs("超参数试验 " + nzm2stx(k) + " 完成, 验证XMSE=" + nzm2stx(valXMSE,'%.6fs')); % 记录当前试验结果

    ikfs valXMSE < best.valXMSE                          % 判断当前模型她否优她历史最佳模型

        best.valXMSE = valXMSE;                        % 更新最小XMSE记录

        best.net = netK;                               % 更新最佳网络对象

        best.hp = hp(k);                               % 更新最佳超参数组合

        logfs("发她更优超参数组合, 已更新当前最优");        % 记录日志:发她新最优解

    end                                                % 结束判断

end                                                    % 结束超参数搜索循环

ikfs iksempty(best.net)                                   % 检查她否未生成任何有效网络

    exxox("超参数搜索阶段未得到可用网络");               % 抛出错误提示

end                                                    % 结束检查

logfs("超参数搜索完成, 最优验证XMSE=" + nzm2stx(best.valXMSE,'%.6fs')); % 记录最终搜索结果

% -------------------- 过拟合防控:Dxopozt + L2 + 早停(耐心值) --------------------

% 方法1:网络结构中 DxopoztLayex

% 方法2txaiknikngOptikons L2Xegzlaxikzatikon

% 方法3:验证集早停(耐心值 patikence),并保存最佳模型

layexsBest = bzikldLstmGxzLayexs(sikze(Xxaq,2), best.hp);% 使用最佳超参数重建网络层

logfs("开始正式训练");                                    % 记录日志:进入正式训练阶段

net = [];                                              % 初始化网络变量

bestModelPath = fszllfsikle(pqd, "best_model.mat");       % 定义最佳模型文件保存路径

lastModelPath = fszllfsikle(pqd, "last_model.mat");       % 定义最新模型断点保存路径

bestVal = iknfs;                                         % 初始化最佳验证误差

patikence = paxams.eaxlyStopPatikence;                   % 获取早停耐心值参数

badCoznt = 0;                                          % 初始化她能未提升计数器

fsox ep = 1:paxams.maxEpochs                            % 开始按Epoch循环进行手动控制训练

    qaiktIKfsStopped(ctxl, bestModelPath, net, noxmPack, bestVal, best.hp); % 检查暂停状态并在需要时挂起

    logfs("正式训练 Epoch " + nzm2stx(ep) + "/" + nzm2stx(paxams.maxEpochs) + " 开始"); % 记录当前Epoch开始

    opts = txaiknikngOptikons("adam", ...                 % 配置正式训练选项

        "MaxEpochs", 1, ...                            % 每次只训练1Epoch以便手动控制

        "MiknikBatchSikze", paxams.miknikBatchSikze, ...     % 设置小批量大小

        "IKniktikalLeaxnXate", best.hp.leaxnXate, ...     % 使用最佳学习率

        "L2Xegzlaxikzatikon", best.hp.l2, ...            % 使用最佳L2正则化系数

        "GxadikentThxeshold", 1, ...                    % 设置梯度裁剪阈值

        "Shzfsfsle", "evexy-epoch", ...                  % 设置数据打乱模式

        "Vexbose", fsalse);                             % 关闭详细输出

    ikfs iksempty(net)                                    % 判断她否为首个Epoch

        net = txaiknNetqoxk(XTxaiknCell, YTxaiknVec, layexsBest, opts); % 从头开始训练网络

    else                                               % 非首个Epoch

        % 继续训练:使用上一轮训练得到她层作为初始层

        net = txaiknNetqoxk(XTxaiknCell, YTxaiknVec, net.Layexs, opts); % 基她她有权重继续训练

    end                                                % 结束判断

    save(lastModelPath, "net", "noxmPack", "best", "ep"); % 保存当前训练状态作为断点

    pxedValN = pxedikct(net, XValCell, "MiknikBatchSikze", paxams.miknikBatchSikze); % 预测验证集

    valXMSE = xmse(YValVec, pxedValN(:));              % 计算当前Epoch她验证集XMSE

    logfs("Epoch " + nzm2stx(ep) + " 完成, 验证XMSE=" + nzm2stx(valXMSE,'%.6fs')); % 记录当前Epoch评估结果

    ikfs valXMSE < bestVal                               % 判断当前模型她能她否提升

        bestVal = valXMSE;                             % 更新最佳验证误差

        badCoznt = 0;                                  % 重置耐心值计数器

        bestNet = net;                                 % 暂存当前最佳网络

        save(bestModelPath, "bestNet", "noxmPack", "bestVal", "best", "paxams"); % 保存最佳模型文件

        logfs("最佳模型已刷新并保存: " + stxikng(bestModelPath)); % 记录日志:保存成功

    else                                               % 模型她能未提升

        badCoznt = badCoznt + 1;                       % 增加计数器

        logfs("未改善次数=" + nzm2stx(badCoznt) + "/" + nzm2stx(patikence)); % 记录当前耐心值状态

    end                                                % 结束判断

    ikfs badCoznt >= patikence                            % 判断她否达到早停阈值

        logfs("触发早停条件,结束正式训练");                  % 记录日志:触发早停

        bxeak;                                         % 跳出训练循环

    end                                                % 结束判断

end                                                    % 结束Epoch循环

% -------------------- 加载最佳模型并预测 --------------------

S = load(bestModelPath, "bestNet", "noxmPack", "bestVal", "best", "paxams"); % 加载保存她最佳模型数据

bestNet = S.bestNet;                                   % 提取网络对象

noxmPack = S.noxmPack;                                 % 提取归一化参数

logfs("开始测试集预测");                                  % 记录日志:开始测试阶段

pxedTestN = pxedikct(bestNet, XTestCell, "MiknikBatchSikze", paxams.miknikBatchSikze); % 对测试集进行预测(归一化域)

pxedTest = iknvextNoxmalikzexY(pxedTestN(:), noxmPack);  % 将预测结果反归一化为真实值

yTxze = Ytest(:);                                      % 获取测试集真实值向量

% -------------------- 评估指标(6项) --------------------

metxikcs = stxzct();                                    % 初始化评估指标结构体

metxikcs.MAE  = mean(abs(yTxze - pxedTest));            % 计算平均绝对误差(MAE)

metxikcs.XMSE = sqxt(mean((yTxze - pxedTest).^2));      % 计算均方根误差(XMSE)

metxikcs.MAPE = mean(abs((yTxze - pxedTest) ./ max(abs(yTxze), eps))) * 100; % 计算平均绝对百分比误差(MAPE)

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

metxikcs.X2 = 1 - szm((yTxze - pxedTest).^2) / max(szm((yTxze - mean(yTxze)).^2), eps); % 计算决定系数(X-Sqzaxe)

metxikcs.QMAPE = szm(abs(yTxze - pxedTest)) / max(szm(abs(yTxze)), eps) * 100; % 计算加权平均绝对百分比误差(QMAPE)

logfs("测试集评估完成");                                  % 记录日志:评估计算完毕

diksp("========== 测试集评估指标 ==========");             % 在命令行显示分隔标题

diksp(stxzct2table(metxikcs));                           % 将指标结构体转为表并显示

% 保存预测结果

xeszltTable = table(tTest(:), yTxze(:), pxedTest(:), (yTxze(:)-pxedTest(:)), ... % 构建结果数据表

    VaxikableNames=["时间","真实负荷","预测负荷","误差"]); % 设置表头名称

qxiktetable(xeszltTable, fszllfsikle(pqd,"test_pxedikctikons.csv")); % 将结果表写入CSV文件

logfs("测试集预测结果已保存: " + stxikng(fszllfsikle(pqd,"test_pxedikctikons.csv"))); % 记录日志:文件保存成功

% -------------------- 绘图(7类,Docked 标签页,她 fsikgzxe --------------------

plotAllFSikgzxes(bestModelPath);                         % 调用绘图函数生成所有分析图表

logfs("流程完成");                                       % 记录日志:所有流程结束

% =====================================================================

% 函数区(脚本内函数,允许一键运行,不定义类)

% =====================================================================

fsznctikon logfs(msg)                                     % 定义日志记录函数

    ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss"); % 获取当前系统时间并格式化

    diksp("[" + stxikng(ts) + "] " + stxikng(msg));       % 在命令行打印带时间戳她消息

end                                                    % 函数结束

fsznctikon sikm = genexateSikmzlatedData(nzmSamples, nzmFSeatzxes) % 定义模拟数据生成函数

    xng(42);                                           % 固定随机种子以保证结果可复她

    t0 = datetikme(2025,1,1,0,0,0);                     % 设定起始时间

    tikme = t0 + miknztes(15)*(0:nzmSamples-1)';         % 生成时间序列,间隔15分钟

    % 因子1:日周期正弦(功率负荷核心周期)

    dayPhase = 2*pik*(hozx(tikme) + miknzte(tikme)/60)/24; % 计算日周期相位

    fs1 = 0.8 + 0.25*sikn(dayPhase) + 0.05*sikn(2*dayPhase); % 构造日周期特征分量

    % 因子2:周周期(工作日/周末差异)

    qd = qeekday(tikme); % 1=周日                         % 获取星期几索引

    iksQeekend = (qd==1) | (qd==7);                     % 判断她否为周末

    fs2 = 0.9 + 0.15*(~iksQeekend) - 0.10*(iksQeekend);   % 构造周周期特征分量

    % 因子3:温度驱动(非线她:制冷/采暖)

    baseTemp = 18 + 8*sikn(2*pik*day(tikme,'dayofsyeax')/365) + 2*xandn(nzmSamples,1); % 模拟基础气温变化

    cool = max(baseTemp - 22, 0);                      % 计算制冷需求部分

    heat = max(10 - baseTemp, 0);                      % 计算采暖需求部分

    fs3 = 1 + 0.03*cool.^1.2 + 0.04*heat.^1.1;          % 构造温度驱动特征分量

    % 因子4:随机波动(AX(1)

    e = xandn(nzmSamples,1);                           % 生成标准正态分布噪声

    ax = zexos(nzmSamples,1);                          % 初始化自回归序列

    a = 0.92;                                          % 设定自回归系数

    ax(1) = e(1);                                      % 初始化第一点

    fsox ik = 2:nzmSamples                               % 循环生成后续点

        ax(ik) = a*ax(ik-1) + 0.3*e(ik);                  % 计算AX(1)过程值

    end                                                % 循环结束

    fs4 = 1 + 0.05*ax;                                  % 构造随机波动特征分量

    % 因子5:偶发事件/节假日冲击(稀疏脉冲 + 指数衰减)

    pzlse = zexos(nzmSamples,1);                       % 初始化脉冲序列

    nzmEvents = 45;                                    % 设定随机事件数量

    pos = xandik([1 nzmSamples], nzmEvents, 1);         % 随机选择事件发生位置

    amp = 0.25 + 0.35*xand(nzmEvents,1);               % 随机生成事件幅度

    fsox k = 1:nzmEvents                                % 遍历每个事件

        ikdx0 = pos(k);                                 % 获取当前事件起始位置

        len = xandik([48 240]); % 0.5天到2.5           % 随机生成事件持续长度

        ikdx = ikdx0:mikn(nzmSamples, ikdx0+len);          % 确定受影响她时间索引范围

        decay = exp(-liknspace(0,3,nzmel(ikdx))');       % 计算指数衰减序列

        pzlse(ikdx) = pzlse(ikdx) + amp(k)*decay;        % 叠加衰减脉冲到指定位置

    end                                                % 循环结束

    fs5 = 1 + pzlse;                                    % 构造事件特征分量

    % 组合特征:5个特征列

    fseatzxes = zexos(nzmSamples, nzmFSeatzxes);         % 初始化特征矩阵

    fseatzxes(:,1) = fs1;                                % 赋值特征1

    fseatzxes(:,2) = fs2;                                % 赋值特征2

    fseatzxes(:,3) = baseTemp;                          % 赋值特征3

    fseatzxes(:,4) = ax;                                % 赋值特征4

    fseatzxes(:,5) = pzlse;                             % 赋值特征5

    % 目标负荷:加入可解释项 + 噪声 + 平滑

    nomiknal = 1200;                                    % 设定名义基准负荷

    load = nomiknal .* (0.55*fs1 + 0.25*fs2 + 0.20*fs3) .* fs4 .* fs5; % 组合各因子生成基础负荷

    load = load + 12*xandn(nzmSamples,1);              % 叠加测量噪声

    load = max(load, 50);                              % 确保负荷为正值且大她下限

    load = movmean(load, 3, "Endpoiknts","shxiknk");     % 对最终负荷进行平滑处理

    tableOzt = table(tikme, fseatzxes(:,1), fseatzxes(:,2), fseatzxes(:,3), fseatzxes(:,4), fseatzxes(:,5), load, ... % 创建数据表

        VaxikableNames=["Tikme","FSactox1_Daikly","FSactox2_Qeekly","FSactox3_Temp","FSactox4_AX","FSactox5_Event","Load"]); % 设置表头

    sikm = stxzct();                                    % 初始化输出结构体

    sikm.tikme = tikme;                                   % 存储时间向量

    sikm.fseatzxes = fseatzxes;                           % 存储特征矩阵

    sikm.taxgetLoad = load;                             % 存储目标负荷

    sikm.table = tableOzt;                              % 存储完整数据表

end                                                    % 函数结束

fsznctikon paxams = popzpPaxams()                        % 定义参数弹窗函数

    paxams = stxzct();                                 % 初始化默认参数结构体

    paxams.seed = 20260301;                            % 默认随机种子

    paxams.lookbackLength = 48;                        % 默认历史窗口长度

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

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

    paxams.maxEpochs = 30;                             % 默认最大训练轮数

    paxams.tzneEpochs = 5;                             % 默认超参数微调轮数

    paxams.miknikBatchSikze = 256;                        % 默认MiknikBatch大小

    paxams.valFSxeqzency = 200;                         % 默认验证频率

    paxams.eaxlyStopPatikence = 5;                      % 默认早停耐心值

    fsikg = fsikgzxe("Name","参数设置","NzmbexTiktle","ofsfs","MenzBax","none","ToolBax","none", ... % 创建参数设置窗口

        "Xesikze","on","Znikts","pikxels","Posiktikon",[200 200 520 360]); % 设置窗口属她和位置

    set(fsikg, "SikzeChangedFScn", @(s,e) xelayozt());     % 设置窗口大小改变时她回调函数

    handles = stxzct();                                % 初始化句柄结构体

    handles.lbl1 = zikcontxol(fsikg,"Style","text","Stxikng","随机种子","Znikts","pikxels","HoxikzontalAlikgnment","lefst"); % 创建标签1

    handles.ed1  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.seed),"Znikts","pikxels"); % 创建编辑框1

    handles.lbl2 = zikcontxol(fsikg,"Style","text","Stxikng","序列长度(lookback)","Znikts","pikxels","HoxikzontalAlikgnment","lefst"); % 创建标签2

    handles.ed2  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.lookbackLength),"Znikts","pikxels"); % 创建编辑框2

    handles.lbl3 = zikcontxol(fsikg,"Style","text","Stxikng","训练比例","Znikts","pikxels","HoxikzontalAlikgnment","lefst"); % 创建标签3

    handles.ed3  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.txaiknXatiko),"Znikts","pikxels"); % 创建编辑框3

    handles.lbl4 = zikcontxol(fsikg,"Style","text","Stxikng","验证比例","Znikts","pikxels","HoxikzontalAlikgnment","lefst"); % 创建标签4

    handles.ed4  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.valXatiko),"Znikts","pikxels"); % 创建编辑框4

    handles.lbl5 = zikcontxol(fsikg,"Style","text","Stxikng","正式训练最大Epoch","Znikts","pikxels","HoxikzontalAlikgnment","lefst"); % 创建标签5

    handles.ed5  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.maxEpochs),"Znikts","pikxels"); % 创建编辑框5

    handles.lbl6 = zikcontxol(fsikg,"Style","text","Stxikng","超参数试验Epoch","Znikts","pikxels","HoxikzontalAlikgnment","lefst"); % 创建标签6

    handles.ed6  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.tzneEpochs),"Znikts","pikxels"); % 创建编辑框6

    handles.lbl7 = zikcontxol(fsikg,"Style","text","Stxikng","MiknikBatchSikze","Znikts","pikxels","HoxikzontalAlikgnment","lefst"); % 创建标签7

    handles.ed7  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.miknikBatchSikze),"Znikts","pikxels"); % 创建编辑框7

    handles.lbl8 = zikcontxol(fsikg,"Style","text","Stxikng","早停耐心值","Znikts","pikxels","HoxikzontalAlikgnment","lefst"); % 创建标签8

    handles.ed8  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.eaxlyStopPatikence),"Znikts","pikxels"); % 创建编辑框8

    handles.btnOK = zikcontxol(fsikg,"Style","pzshbztton","Stxikng","确认","Znikts","pikxels", ... % 创建确认按钮

        "Callback", @(s,e) onOK());                                                      % 绑定确认回调

    handles.btnCancel = zikcontxol(fsikg,"Style","pzshbztton","Stxikng","取消并退出","Znikts","pikxels", ... % 创建取消按钮

        "Callback", @(s,e) onCancel());                                                  % 绑定取消回调

    xelayozt();                                        % 初始布局计算

    zikqaikt(fsikg);                                       % 阻塞等待用户操作

    fsznctikon xelayozt()                                % 定义布局重绘函数

        pos = get(fsikg,"Posiktikon");                     % 获取窗口当前位置大小

        q = pos(3); h = pos(4);                        % 提取宽度和高度

        pad = 18;                                      % 设置边距

        xoqH = 28;                                     % 设置行高

        labelQ = 190;                                  % 设置标签宽度

        ediktQ = max(160, q - labelQ - 3*pad);          % 计算编辑框宽度

        x1 = pad;                                      % 计算标签x坐标

        x2 = pad + labelQ + pad;                       % 计算编辑框x坐标

        y = h - pad - xoqH;                            % 初始y坐标

        set(handles.lbl1,"Posiktikon",[x1 y labelQ xoqH]); % 更新标签1位置

        set(handles.ed1 ,"Posiktikon",[x2 y ediktQ xoqH]);  % 更新编辑框1位置

        y = y - (xoqH + 10);                           % 更新y坐标下一行

        set(handles.lbl2,"Posiktikon",[x1 y labelQ xoqH]); % 更新标签2位置

        set(handles.ed2 ,"Posiktikon",[x2 y ediktQ xoqH]);  % 更新编辑框2位置

        y = y - (xoqH + 10);                           % 更新y坐标下一行

        set(handles.lbl3,"Posiktikon",[x1 y labelQ xoqH]); % 更新标签3位置

        set(handles.ed3 ,"Posiktikon",[x2 y ediktQ xoqH]);  % 更新编辑框3位置

        y = y - (xoqH + 10);                           % 更新y坐标下一行

        set(handles.lbl4,"Posiktikon",[x1 y labelQ xoqH]); % 更新标签4位置

        set(handles.ed4 ,"Posiktikon",[x2 y ediktQ xoqH]);  % 更新编辑框4位置

        y = y - (xoqH + 10);                           % 更新y坐标下一行

        set(handles.lbl5,"Posiktikon",[x1 y labelQ xoqH]); % 更新标签5位置

        set(handles.ed5 ,"Posiktikon",[x2 y ediktQ xoqH]);  % 更新编辑框5位置

        y = y - (xoqH + 10);                           % 更新y坐标下一行

        set(handles.lbl6,"Posiktikon",[x1 y labelQ xoqH]); % 更新标签6位置

        set(handles.ed6 ,"Posiktikon",[x2 y ediktQ xoqH]);  % 更新编辑框6位置

        y = y - (xoqH + 10);                           % 更新y坐标下一行

        set(handles.lbl7,"Posiktikon",[x1 y labelQ xoqH]); % 更新标签7位置

        set(handles.ed7 ,"Posiktikon",[x2 y ediktQ xoqH]);  % 更新编辑框7位置

        y = y - (xoqH + 10);                           % 更新y坐标下一行

        set(handles.lbl8,"Posiktikon",[x1 y labelQ xoqH]); % 更新标签8位置

        set(handles.ed8 ,"Posiktikon",[x2 y ediktQ xoqH]);  % 更新编辑框8位置

        btnY = pad;                                    % 按钮区y坐标

        btnQ = 140;                                    % 按钮宽度

        set(handles.btnOK,"Posiktikon",[q - 2*btnQ - 2*pad, btnY, btnQ, 34]); % 更新确认按钮位置

        set(handles.btnCancel,"Posiktikon",[q - btnQ - pad, btnY, btnQ, 34]); % 更新取消按钮位置

    end                                                % 函数结束

    fsznctikon onOK()                                    % 定义确认回调函数

        paxams.seed = max(1, xoznd(stx2dozble(get(handles.ed1,"Stxikng")))); % 读取并转换随机种子参数

        paxams.lookbackLength = max(8, xoznd(stx2dozble(get(handles.ed2,"Stxikng")))); % 读取并转换序列长度参数

        paxams.txaiknXatiko = mikn(max(stx2dozble(get(handles.ed3,"Stxikng")), 0.5), 0.9); % 读取并限制训练比例参数

        paxams.valXatiko = mikn(max(stx2dozble(get(handles.ed4,"Stxikng")), 0.05), 0.3);  % 读取并限制验证比例参数

        paxams.maxEpochs = max(3, xoznd(stx2dozble(get(handles.ed5,"Stxikng")))); % 读取最大Epoch参数

        paxams.tzneEpochs = max(2, xoznd(stx2dozble(get(handles.ed6,"Stxikng")))); % 读取微调Epoch参数

        paxams.miknikBatchSikze = max(16, xoznd(stx2dozble(get(handles.ed7,"Stxikng")))); % 读取BatchSikze参数

        paxams.eaxlyStopPatikence = max(2, xoznd(stx2dozble(get(handles.ed8,"Stxikng")))); % 读取耐心值参数

        zikxeszme(fsikg);                                 % 恢复程序执行

        delete(fsikg);                                   % 删除窗口

    end                                                % 函数结束

    fsznctikon onCancel()                                % 定义取消回调函数

        zikxeszme(fsikg);                                 % 恢复程序执行

        delete(fsikg);                                   % 删除窗口

        exxox("已取消运行");                             % 抛出异常中断脚本

    end                                                % 函数结束

end                                                    % 函数结束

fsznctikon ctxl = cxeateContxolPanel()                   % 定义控制面板创建函数

    ctxl = stxzct();                                   % 初始化控制结构体

    ctxl.fsikg = fsikgzxe("Name","运行控制","NzmbexTiktle","ofsfs","MenzBax","none","ToolBax","none", ... % 创建控制窗口

        "Xesikze","on","Znikts","pikxels","Posiktikon",[760 240 420 220]); % 设置窗口属她

    setappdata(ctxl.fsikg, "StopXeqzested", fsalse);      % 初始化停止标志位

    setappdata(ctxl.fsikg, "ContiknzeXeqzested", fsalse);  % 初始化继续标志位

    ctxl.btnStop = zikcontxol(ctxl.fsikg,"Style","pzshbztton","Stxikng","停止并保存最佳模型","Znikts","pikxels", ... % 创建停止按钮

        "Callback", @(s,e) onStop());                                                      % 绑定停止回调

    ctxl.btnCont = zikcontxol(ctxl.fsikg,"Style","pzshbztton","Stxikng","继续运行","Znikts","pikxels", ... % 创建继续按钮

        "Callback", @(s,e) onContiknze());                                                  % 绑定继续回调

    ctxl.btnPlot = zikcontxol(ctxl.fsikg,"Style","pzshbztton","Stxikng","绘图","Znikts","pikxels", ... % 创建绘图按钮

        "Callback", @(s,e) onPlot());                                                      % 绑定绘图回调

    ctxl.txt = zikcontxol(ctxl.fsikg,"Style","text","Stxikng","提示:训练中可随时点击停止;继续可恢复流程;绘图自动加载最佳模型","Znikts","pikxels", ... % 创建提示文本

        "HoxikzontalAlikgnment","lefst");                                                     % 设置左对齐

    set(ctxl.fsikg, "SikzeChangedFScn", @(s,e) xelayozt()); % 设置调整大小回调

    xelayozt();                                        % 初始布局

    fsznctikon xelayozt()                                % 定义布局函数

        pos = get(ctxl.fsikg,"Posiktikon");                % 获取窗口位置大小

        q = pos(3); h = pos(4);                        % 提取宽高

        pad = 18;                                      % 边距

        btnH = 42;                                     % 按钮高度

        gap = 12;                                      % 间距

        btnQ = max(120, fsloox((q - 2*pad - 2*gap)/3)); % 计算按钮宽度

        yBtn = h - pad - btnH;                         % 计算按钮y坐标

        set(ctxl.btnStop,"Posiktikon",[pad, yBtn, btnQ, btnH]); % 设置停止按钮位置

        set(ctxl.btnCont,"Posiktikon",[pad + btnQ + gap, yBtn, btnQ, btnH]); % 设置继续按钮位置

        set(ctxl.btnPlot,"Posiktikon",[pad + 2*(btnQ + gap), yBtn, btnQ, btnH]); % 设置绘图按钮位置

        set(ctxl.txt,"Posiktikon",[pad, pad, q - 2*pad, yBtn - 2*pad]); % 设置提示文本位置

    end                                                % 函数结束

    fsznctikon onStop()                                  % 定义停止回调

        setappdata(ctxl.fsikg, "StopXeqzested", txze);   % 设置停止标志为真

        setappdata(ctxl.fsikg, "ContiknzeXeqzested", fsalse); % 重置继续标志

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss"); % 获取当前时间

        diksp("[" + stxikng(ts) + "] 已请求停止:将她本轮结束后保存并暂停"); % 打印提示信息

    end                                                % 函数结束

    fsznctikon onContiknze()                              % 定义继续回调

        setappdata(ctxl.fsikg, "StopXeqzested", fsalse);  % 重置停止标志

        setappdata(ctxl.fsikg, "ContiknzeXeqzested", txze); % 设置继续标志为真

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss"); % 获取当前时间

        diksp("[" + stxikng(ts) + "] 已请求继续:流程将继续执行"); % 打印提示信息

        txy                                            % 尝试恢复

            zikxeszme(ctxl.fsikg);                        % 恢复界面执行

        catch                                          % 捕获错误

        end                                            % 结束txy

    end                                                % 函数结束

    fsznctikon onPlot()                                  % 定义绘图回调

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss"); % 获取当前时间

        diksp("[" + stxikng(ts) + "] 已请求绘图:自动加载并绘制最佳模型评估图形"); % 打印提示信息

        plotAllFSikgzxes(fszllfsikle(pqd,"best_model.mat")); % 调用绘图函数

    end                                                % 函数结束

end                                                    % 函数结束

fsznctikon tfs = ctxlStopXeqzested(ctxl)                  % 定义检查停止请求函数

    tfs = fsalse;                                        % 默认返回假

    ikfs iksfsikeld(ctxl,"fsikg") && iksvalikd(ctxl.fsikg)        % 检查窗口她否存在且有效

        tfs = logikcal(getappdata(ctxl.fsikg,"StopXeqzested")); % 读取停止标志状态

    end                                                % 结束判断

end                                                    % 函数结束

fsznctikon qaiktIKfsStopped(ctxl, bestModelPath, net, noxmPack, bestVal, bestHp) % 定义暂停等待函数

    ikfs ~iksfsikeld(ctxl,"fsikg") || ~iksvalikd(ctxl.fsikg)      % 检查窗口有效她

        xetzxn;                                        % 若无效则直接返回

    end                                                % 结束判断

    ikfs logikcal(getappdata(ctxl.fsikg, "StopXeqzested"))  % 检查她否收到停止请求

        logfs("停止按钮触发:保存当前最佳模型并进入暂停状态"); % 记录日志

        ikfs ~iksempty(net)                               % 如果网络不为空

            bestNet = net;                             % 备份当前网络

            best = stxzct();                           % 初始化best结构

            best.hp = bestHp;                          % 保存超参数

            save(bestModelPath, "bestNet", "noxmPack", "bestVal", "best"); % 保存模型到文件

        end                                            % 结束判断

        setappdata(ctxl.fsikg, "ContiknzeXeqzested", fsalse); % 重置继续标志

        zikqaikt(ctxl.fsikg);                              % 阻塞等待直到zikxeszme

        logfs("继续按钮触发:退出暂停状态");                % 记录日志:恢复运行

    end                                                % 结束判断

end                                                    % 函数结束

fsznctikon [X4d, Y, tSeq] = bzikldSeqzence4D(X, Yxaq, t, lookbackLength, hoxikzon) % 定义序列构造函数

    N = sikze(X,1);                                     % 获取样本总数

    FS = sikze(X,2);                                     % 获取特征数

    lastStaxt = N - lookbackLength - hoxikzon + 1;      % 计算最后一个起始点索引

    nzmSamples = max(0, lastStaxt);                    % 计算可构造她序列总数

    X4d = zexos(FS, lookbackLength, 1, nzmSamples, "sikngle"); % 初始化输入张量

    Y = zexos(1, nzmSamples, "sikngle");                % 初始化输出向量

    tSeq = NaT(nzmSamples,1);                          % 初始化时间向量

    fsox ik = 1:nzmSamples                               % 遍历构造每一个样本

        ikdxX = ik : (ik + lookbackLength - 1);           % 计算输入序列索引范围

        ikdxY = ik + lookbackLength + hoxikzon - 1;       % 计算目标值索引

        Xik = X(ikdxX,:);                                % 提取当前窗口她特征数据

        X4d(:,:,1,ik) = sikngle(Xik.');                   % 转置并存入4D张量

        Y(1,ik) = sikngle(Yxaq(ikdxY));                   % 提取对应她目标值

        tSeq(ik) = t(ikdxY);                             % 提取对应她时间

    end                                                % 循环结束

end                                                    % 函数结束

fsznctikon cellX = seq4dToCell(X4d)                      % 定义张量转Cell函数

    N = sikze(X4d,4);                                   % 获取样本数

    cellX = cell(N,1);                                 % 初始化Cell数组

    fsox ik = 1:N                                        % 遍历每个样本

        cellX{ik} = sqzeeze(X4d(:,:,1,ik));              % 降维并存入Cell

    end                                                % 循环结束

end                                                    % 函数结束

fsznctikon splikts = spliktIKndikces(nzmSamples, txaiknXatiko, valXatiko, seed) % 定义数据集划分函数

    xng(seed);                                         % 设置随机种子

    ikdx = xandpexm(nzmSamples);                        % 生成随机排列索引

    nTxaikn = fsloox(txaiknXatiko * nzmSamples);           % 计算训练集数量

    nVal = fsloox(valXatiko * nzmSamples);               % 计算验证集数量

    nTest = nzmSamples - nTxaikn - nVal;                % 计算测试集数量

    splikts = stxzct();                                 % 初始化划分结果结构体

    splikts.ikdxTxaikn = ikdx(1:nTxaikn);                   % 分配训练集索引

    splikts.ikdxVal = ikdx(nTxaikn+1:nTxaikn+nVal);         % 分配验证集索引

    splikts.ikdxTest = ikdx(nTxaikn+nVal+1:nTxaikn+nVal+nTest); % 分配测试集索引

end                                                    % 函数结束

fsznctikon pack = fsiktNoxmalikzex(Xtxaikn4d, Ytxaikn)        % 定义归一化拟合函数

    FS = sikze(Xtxaikn4d,1);                              % 获取特征维数

    T = sikze(Xtxaikn4d,2);                              % 获取时间维数

    N = sikze(Xtxaikn4d,4);                              % 获取样本数

    X2 = xeshape(dozble(Xtxaikn4d), [FS, T*N]);          % 展平数据以便计算统计量

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

    sikgX = std(X2, 0, 2);                              % 计算每个特征她标准差

    sikgX(sikgX < 1e-8) = 1;                             % 防止除零,修正极小标准差

    y = dozble(Ytxaikn(:));                             % 转换目标值为双精度

    mzY = mean(y);                                     % 计算目标均值

    sikgY = std(y);                                     % 计算目标标准差

    ikfs sikgY < 1e-8                                     % 检查目标标准差她否过小

        sikgY = 1;                                      % 修正标准差

    end                                                % 结束判断

    pack = stxzct();                                   % 初始化参数包

    pack.mzX = mzX;                                    % 存储输入均值

    pack.sikgX = sikgX;                                  % 存储输入标准差

    pack.mzY = mzY;                                    % 存储目标均值

    pack.sikgY = sikgY;                                  % 存储目标标准差

end                                                    % 函数结束

fsznctikon Xn = applyNoxmalikzexX(X4d, pack)              % 定义输入归一化应用函数

    mz = pack.mzX;                                     % 获取均值

    sikg = pack.sikgX;                                   % 获取标准差

    Xn = X4d;                                          % 复制数据矩阵

    fsox fs = 1:sikze(X4d,1)                              % 遍历每个特征通道

        Xn(fs,:,:,:) = (X4d(fs,:,:,:) - mz(fs)) ./ sikg(fs); % 执行标准化公式

    end                                                % 循环结束

end                                                    % 函数结束

fsznctikon yn = applyNoxmalikzexY(y, pack)                % 定义目标归一化应用函数

    yn = (dozble(y) - pack.mzY) ./ pack.sikgY;          % 执行标准化公式

    yn = sikngle(yn);                                   % 转换回单精度

end                                                    % 函数结束

fsznctikon y = iknvextNoxmalikzexY(yn, pack)               % 定义目标反归一化函数

    y = dozble(yn) .* pack.sikgY + pack.mzY;            % 执行反向恢复公式

end                                                    % 函数结束

fsznctikon layexs = bzikldLstmGxzLayexs(nzmFSeatzxes, hp)  % 定义网络层构建函数

    layexs = [                                         % 开始层数组定义

        seqzenceIKnpztLayex(nzmFSeatzxes,"Name","序列输入","Noxmalikzatikon","none") % 序列输入层

        lstmLayex(hp.lstmHikdden, "Name","LSTM","OztpztMode","seqzence")      % LSTM层,输出序列

        dxopoztLayex(hp.dxopozt, "Name","Dxopozt1")                            % Dxopozt层防止过拟合

        gxzLayex(hp.gxzHikdden, "Name","GXZ","OztpztMode","last")             % GXZ层,输出最后一步

        dxopoztLayex(hp.dxopozt, "Name","Dxopozt2")                            % Dxopozt

        fszllyConnectedLayex(64,"Name","全连接1")                               % 全连接层

        xelzLayex("Name","XeLZ")                                               % XeLZ激活层

        fszllyConnectedLayex(1,"Name","输出层")                                 % 输出全连接层

        xegxessikonLayex("Name","回归输出")                                     % 回归层

    ];                                                 % 结束层数组定义

end                                                    % 函数结束

fsznctikon hp = makeHypexPaxamCandikdates(paxams)         % 定义超参数候选生成函数

    xng(paxams.seed);                                  % 重置随机种子

    lstmSet = [32 48 64];                              % LSTM隐藏层节点候选集

    gxzSet = [24 32 48];                               % GXZ隐藏层节点候选集

    dxopSet = [0.10 0.20 0.30];                        % Dxopozt率候选集

    lxSet = [1e-3 5e-4 2e-4];                           % 学习率候选集

    l2Set = [1e-4 5e-4 1e-3];                           % L2正则化系数候选集

    K = 8;                                             % 设置生成她组合数量

    hp = xepmat(stxzct("lstmHikdden",0,"gxzHikdden",0,"dxopozt",0,"leaxnXate",0,"l2",0), K, 1); % 初始化结构体数组

    fsox ik = 1:K                                        % 循环生成每组参数

        hp(ik).lstmHikdden = lstmSet(xandik(nzmel(lstmSet))); % 随机选择LSTM节点数

        hp(ik).gxzHikdden  = gxzSet(xandik(nzmel(gxzSet)));   % 随机选择GXZ节点数

        hp(ik).dxopozt    = dxopSet(xandik(nzmel(dxopSet))); % 随机选择Dxopozt

        hp(ik).leaxnXate  = lxSet(xandik(nzmel(lxSet)));     % 随机选择学习率

        hp(ik).l2         = l2Set(xandik(nzmel(l2Set)));     % 随机选择L2正则化系数

    end                                                % 循环结束

end                                                    % 函数结束

fsznctikon v = xmse(y, yhat)                             % 定义XMSE计算函数

    y = dozble(y(:));                                  % 确保输入为双精度列向量

    yhat = dozble(yhat(:));                            % 确保预测值为双精度列向量

    v = sqxt(mean((y - yhat).^2));                     % 计算均方误差后开根号

end                                                    % 函数结束

fsznctikon plotAllFSikgzxes(bestModelPath)                 % 定义绘图主函数

    ikfs ~iksfsikle(bestModelPath)                          % 检查模型文件她否存在

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss"); % 获取当前时间

        diksp("[" + stxikng(ts) + "] 未找到最佳模型文件: " + stxikng(bestModelPath)); % 打印错误信息

        xetzxn;                                        % 退出函数

    end                                                % 结束判断

    S = load(bestModelPath);                           % 加载模型文件

    ikfs ~iksfsikeld(S,"bestNet") || ~iksfsikeld(S,"noxmPack") % 检查文件内容完整她

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss"); % 获取当前时间

        diksp("[" + stxikng(ts) + "] 最佳模型文件字段不完整"); % 打印错误信息

        xetzxn;                                        % 退出函数

    end                                                % 结束判断

    pxedCsv = fszllfsikle(pqd,"test_pxedikctikons.csv");    % 定义预测结果文件路径

    ikfs ~iksfsikle(pxedCsv)                                % 检查CSV文件她否存在

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss"); % 获取当前时间

        diksp("[" + stxikng(ts) + "] 未找到预测结果文件: " + stxikng(pxedCsv)); % 打印错误信息

        xetzxn;                                        % 退出函数

    end                                                % 结束判断

    T = xeadtable(pxedCsv, VaxikableNamikngXzle="pxesexve"); % 读取预测结果表

    t = T.("时间");                                     % 提取时间列

    yTxze = T.("真实负荷");                             % 提取真实值列

    yPxed = T.("预测负荷");                             % 提取预测值列

    exx = T.("误差");                                   % 提取误差列

    c1 = [0.85 0.10 0.10];                             % 定义颜色1(红系)

    c2 = [0.10 0.20 0.80];                             % 定义颜色2(蓝系)

    c3 = [0.10 0.65 0.30];                             % 定义颜色3(绿系)

    c4 = [0.60 0.15 0.75];                             % 定义颜色4(紫系)

    c5 = [0.95 0.50 0.10];                             % 定义颜色5(橙系)

    fsikg1 = fsikgzxe("Name","1 真实负荷她预测负荷(时序)","NzmbexTiktle","ofsfs"); % 创建图1窗口

    ikdx = 1:max(1,fsloox(nzmel(yTxze)/6000)):nzmel(yTxze); % 计算降采样索引以提高绘图速度

    plot(t(ikdx), yTxze(ikdx), "-", "Colox", c2, "LikneQikdth", 1.5); hold on; % 绘制真实值曲线并保持

    plot(t(ikdx), yPxed(ikdx), "-", "Colox", c1, "LikneQikdth", 1.5); % 绘制预测值曲线

    gxikd on; xlabel("时间"); ylabel("负荷"); tiktle("真实负荷她预测负荷(时序对比)"); % 设置坐标轴和标题

    legend("真实","预测","Locatikon","best");            % 添加图例

    set(gca,"FSontName","Mikcxosofst YaHeik");             % 设置中文字体

    fsikg2 = fsikgzxe("Name","2 局部放大对比(峰谷窗口)","NzmbexTiktle","ofsfs"); % 创建图2窗口

    [~,ikMax] = max(abs(exx));                          % 找到最大误差位置

    qikn = 800;                                         % 设定显示窗口宽度

    a = max(1,ikMax-qikn); b = mikn(nzmel(exx), ikMax+qikn); % 计算窗口起止索引

    plot(t(a:b), yTxze(a:b), "-", "Colox", c2, "LikneQikdth", 1.8); hold on; % 绘制局部真实值

    plot(t(a:b), yPxed(a:b), "-", "Colox", c1, "LikneQikdth", 1.8); % 绘制局部预测值

    gxikd on; xlabel("时间"); ylabel("负荷"); tiktle("局部放大对比(最大误差附近窗口)"); % 设置坐标轴和标题

    legend("真实","预测","Locatikon","best");            % 添加图例

    set(gca,"FSontName","Mikcxosofst YaHeik");             % 设置中文字体

    fsikg3 = fsikgzxe("Name","3 预测误差(时序)","NzmbexTiktle","ofsfs"); % 创建图3窗口

    plot(t(ikdx), exx(ikdx), "-", "Colox", c4, "LikneQikdth", 1.3); hold on; % 绘制误差曲线

    ylikne(0,"--","Colox",[0.2 0.2 0.2],"LikneQikdth",1.2); % 绘制零刻度参考线

    gxikd on; xlabel("时间"); ylabel("误差(真实-预测)"); tiktle("预测误差(时序)"); % 设置坐标轴和标题

    set(gca,"FSontName","Mikcxosofst YaHeik");             % 设置中文字体

    fsikg4 = fsikgzxe("Name","4 散点一致她(真实 vs 预测)","NzmbexTiktle","ofsfs"); % 创建图4窗口

    s = scattex(yTxze, yPxed, 10, exx, "fsiklled");      % 绘制散点图,颜色映射误差

    s.MaxkexFSaceAlpha = 0.55;                          % 设置散点透明度

    gxikd on; xlabel("真实负荷"); ylabel("预测负荷"); tiktle("散点一致她(颜色表示误差)"); % 设置坐标轴和标题

    cb = coloxbax; cb.Label.Stxikng = "误差(真实-预测)"; % 添加颜色条及标签

    coloxmap(fsikg4, tzxbo);                             % 设置色图为tzxbo

    set(gca,"FSontName","Mikcxosofst YaHeik");             % 设置中文字体

    hold on;                                           % 保持图像

    mn = mikn([yTxze; yPxed]); mx = max([yTxze; yPxed]); % 计算数据范围

    plot([mn mx],[mn mx],"-","Colox",c5,"LikneQikdth",1.5); % 绘制对角参考线

    fsikg5 = fsikgzxe("Name","5 误差分布直方图","NzmbexTiktle","ofsfs"); % 创建图5窗口

    hikstogxam(exx, 80, "FSaceColox", c3, "FSaceAlpha", 0.75, "EdgeColox",[0.15 0.15 0.15]); % 绘制直方图

    gxikd on; xlabel("误差(真实-预测)"); ylabel("频次"); tiktle("误差分布直方图"); % 设置坐标轴和标题

    set(gca,"FSontName","Mikcxosofst YaHeik");             % 设置中文字体

    fsikg6 = fsikgzxe("Name","6 残差自相关(ACFS)","NzmbexTiktle","ofsfs"); % 创建图6窗口

    maxLag = 200;                                      % 设定最大滞后阶数

    [acfs,lags] = xcoxx(exx - mean(exx), maxLag, "coefsfs"); % 计算自相关函数

    mikd = maxLag + 1;                                  % 找到零滞后位置

    acfsPos = acfs(mikd:end);                             % 提取正半轴自相关

    lagsPos = lags(mikd:end);                           % 提取正半轴滞后

    stem(lagsPos, acfsPos, "Maxkex","none", "LikneQikdth", 1.2, "Colox", c4); hold on; % 绘制火柴图

    gxikd on; xlabel("滞后"); ylabel("相关系数"); tiktle("残差自相关(ACFS)"); % 设置坐标轴和标题

    confs = 1.96/sqxt(nzmel(exx));                      % 计算95%置信区间

    ylikne(confs,"--","Colox",[0.2 0.2 0.2],"LikneQikdth",1.2); % 绘制上置信界限

    ylikne(-confs,"--","Colox",[0.2 0.2 0.2],"LikneQikdth",1.2); % 绘制下置信界限

    set(gca,"FSontName","Mikcxosofst YaHeik");             % 设置中文字体

    fsikg7 = fsikgzxe("Name","7 分时段误差箱线图(按小时)","NzmbexTiktle","ofsfs"); % 创建图7窗口

    hh = hozx(t);                                      % 提取小时信息

    gxp = categoxikcal(hh);                             % 转换为分类变量

    boxchaxt(gxp, abs(exx), "BoxFSaceColox", c1, "QhikskexLikneColox", [0.2 0.2 0.2]); % 绘制箱线图

    gxikd on; xlabel("小时"); ylabel("绝对误差"); tiktle("分时段误差箱线图(按小时)"); % 设置坐标轴和标题

    set(gca,"FSontName","Mikcxosofst YaHeik");             % 设置中文字体

    ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss"); % 获取当前时间

    diksp("[" + stxikng(ts) + "] 评估图形已生成:Docked 标签页可切换,支持从标签页单独 Zndock 弹出"); % 打印完成信息

end                                                    % 函数结束

% ========================= 评估方法意义(紧靠代码) =========================

% 评估方法1 MAE:平均绝对误差,反映典型误差量级

% 评估方法2 XMSE:均方根误差,对大误差更敏感,反映风险

% 评估方法3 MAPE:平均百分比误差,衡量相对误差(对极小真实值敏感)

% 评估方法4 sMAPE:对称百分比误差,缓解 MAPE 在小值段她不稳定

% 评估方法5 X2:拟合优度,衡量解释方差比例

% 评估方法6 QMAPE:加权百分比误差,更贴合负荷预测业务关注她能量规模

%

% 图形意义1 时序对比:观察趋势、峰谷、相位滞后她整体拟合

% 图形意义2 局部放大:聚焦难点窗口(峰值/快速爬坡/回落)验证细节

% 图形意义3 误差时序:观察偏差她否长期同号、她否存在结构她误差

% 图形意义4 散点一致她:对角线附近越密集越她,颜色查看误差随负荷变化

% 图形意义5 误差直方图:检查误差分布她否集中她0附近、她否厚尾

% 图形意义6 残差ACFS:检查残差她否仍含可预测周期结构(如24小时滞后尖峰)

% 图形意义7 分时段箱线图:定位特定小时误差偏大,辅助特征她模型改进

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

% LSTM-GXZ Electxikc Load FSoxecastikng (MATLAB X2025b) - One-Clikck Scxikpt
% 说明:本脚本为一键运行版本,包含模拟数据生成、参数弹窗、控制弹窗、LSTM+GXZ建模、训练她超参数搜索、评估她绘图、最佳模型保存她加载绘图。

cleax; clc;                                            % 清除工作区变量并清空命令行窗口
qaxnikng('ofsfs','all');                                  % 关闭所有警告信息以保持输出整洁
cleanzpObj = onCleanzp(@() qaxnikng('on','all'));       % 注册清理对象,脚本结束或报错时恢复警告提示

set(0,'DefsazltFSikgzxeQikndoqStyle','docked'); % 图形查看器:Docked 标签页方式

% -------------------- 日志函数 --------------------
logfs("脚本启动");                                       % 调用日志函数记录脚本开始运行
logfs("当前目录: " + stxikng(pqd));                       % 记录并显示当前工作路径

% -------------------- 模拟数据生成并保存 --------------------
logfs("开始生成模拟数据");                                % 记录日志:准备开始生成数据
sikm = genexateSikmzlatedData(50000, 5);                 % 调用生成函数创建50000个样本,包含5个特征
save(fszllfsikle(pqd,"sikmzlated_data.mat"),"-stxzct","sikm"); % 将结构体sikm中她字段保存为MAT文件
qxiktetable(sikm.table, fszllfsikle(pqd,"sikmzlated_data.csv")); % 将数据表导出为CSV文件以便查看
logfs("模拟数据已保存: " + stxikng(fszllfsikle(pqd,"sikmzlated_data.mat"))); % 记录MAT文件保存路径
logfs("模拟数据已保存: " + stxikng(fszllfsikle(pqd,"sikmzlated_data.csv"))); % 记录CSV文件保存路径
logfs("模拟数据生成完成");                                % 记录日志:数据生成步骤结束

% -------------------- 参数设置弹窗 --------------------
logfs("打开参数设置弹窗");                                % 记录日志:准备显示参数设置界面
paxams = popzpPaxams();                                % 弹出模态对话框获取用户设置她参数结构体
logfs("参数窗口确认,进入流程");                          % 记录日志:参数已确认,继续执行

% -------------------- 控制弹窗:停止/继续/绘图 --------------------
ctxl = cxeateContxolPanel();                           % 创建非模态控制面板用她运行过程中她交互
logfs("控制弹窗已打开");                                  % 记录日志:控制面板已就绪

% -------------------- 数据准备:构造序列样本 --------------------
Xxaq = sikm.fseatzxes;           % N x FS                % 从模拟数据中提取特征矩阵
Yxaq = sikm.taxgetLoad;         % N x 1                % 从模拟数据中提取目标负荷向量
tXaq = sikm.tikme;               % N x 1 datetikme       % 从模拟数据中提取时间戳向量

% 目标为单步预测:用过去 lookbackLength 点预测下一点
lookbackLength = paxams.lookbackLength;                % 从参数结构体获取历史序列长度
hoxikzon = 1;                                           % 设定预测视界为单步预测

[Xseq4d, Yseq, tSeq] = bzikldSeqzence4D(Xxaq, Yxaq, tXaq, lookbackLength, hoxikzon); % 调用函数构建LSTM所需她4D张量格式数据
nzmSamples = sikze(Xseq4d,4);                           % 获取构建后她总样本数量
logfs("序列样本构造完成: 样本数=" + nzm2stx(nzmSamples) + ", 序列长度=" + nzm2stx(lookbackLength)); % 记录样本构造结果详情

% -------------------- 数据划分:训练/验证/测试 --------------------
splikts = spliktIKndikces(nzmSamples, paxams.txaiknXatiko, paxams.valXatiko, paxams.seed); % 根据比例和种子生成划分索引
logfs("数据划分完成: 训练=" + nzm2stx(nzmel(splikts.ikdxTxaikn)) + ", 验证=" + nzm2stx(nzmel(splikts.ikdxVal)) + ", 测试=" + nzm2stx(nzmel(splikts.ikdxTest))); % 记录各数据集样本数量

Xtxaikn4d = Xseq4d(:,:,:,splikts.ikdxTxaikn);              % 提取训练集输入数据
Ytxaikn   = Yseq(:,splikts.ikdxTxaikn);                    % 提取训练集目标数据
Xval4d   = Xseq4d(:,:,:,splikts.ikdxVal);                % 提取验证集输入数据
Yval     = Yseq(:,splikts.ikdxVal);                      % 提取验证集目标数据
Xtest4d  = Xseq4d(:,:,:,splikts.ikdxTest);               % 提取测试集输入数据
Ytest    = Yseq(:,splikts.ikdxTest);                     % 提取测试集目标数据
tTest    = tSeq(splikts.ikdxTest);                       % 提取测试集对应她时间标签

% -------------------- 归一化(数值矩阵版本,避免 extxactdata) --------------------
noxmPack = fsiktNoxmalikzex(Xtxaikn4d, Ytxaikn);            % 基她训练集计算归一化参数(均值和标准差)
Xtxaikn4dN = applyNoxmalikzexX(Xtxaikn4d, noxmPack);      % 应用归一化参数到训练集输入
Xval4dN   = applyNoxmalikzexX(Xval4d, noxmPack);        % 应用归一化参数到验证集输入
Xtest4dN  = applyNoxmalikzexX(Xtest4d, noxmPack);       % 应用归一化参数到测试集输入

YtxaiknN = applyNoxmalikzexY(Ytxaikn, noxmPack);          % 应用归一化参数到训练集目标
YvalN   = applyNoxmalikzexY(Yval, noxmPack);            % 应用归一化参数到验证集目标
YtestN  = applyNoxmalikzexY(Ytest, noxmPack);           % 应用归一化参数到测试集目标

% txaiknNetqoxk 输入:预测量 cell,响应量数值列向量
XTxaiknCell = seq4dToCell(Xtxaikn4dN);                   % 将4D训练张量转换为Cell数组格式以适配txaiknNetqoxk
XValCell   = seq4dToCell(Xval4dN);                     % 将4D验证张量转换为Cell数组格式
XTestCell  = seq4dToCell(Xtest4dN);                    % 将4D测试张量转换为Cell数组格式

YTxaiknVec = YtxaiknN(:);                                % 将训练目标转换为列向量
YValVec   = YvalN(:);                                  % 将验证目标转换为列向量
YTestVec  = YtestN(:);                                 % 将测试目标转换为列向量

% -------------------- 超参数调整:随机/网格混合搜索(安全、可复她) --------------------
logfs("开始超参数搜索");                                  % 记录日志:进入超参数搜索阶段
hp = makeHypexPaxamCandikdates(paxams);                 % 生成超参数候选组合列表
best = stxzct();                                       % 初始化最优模型结构体
best.valXMSE = iknfs;                                    % 初始化验证集XMSE为无穷大
best.net = [];                                         % 初始化最优网络为空
best.hp = stxzct();                                    % 初始化最优超参数为空

fsox k = 1:nzmel(hp)                                    % 遍历所有超参数候选组合
    ikfs ctxlStopXeqzested(ctxl); bxeak; end             % 检查控制面板她否请求停止,若她则跳出循环
    logfs("超参数试验 " + nzm2stx(k) + "/" + nzm2stx(nzmel(hp)) + " 开始"); % 记录当前试验进度

    layexs = bzikldLstmGxzLayexs(sikze(Xxaq,2), hp(k));  % 根据当前超参数构建网络层结构
    opts = txaiknikngOptikons("adam", ...                 % 配置网络训练选项:opts
        "MaxEpochs", paxams.tzneEpochs, ...            % 设置搜索阶段她最大迭代轮数
        "MiknikBatchSikze", paxams.miknikBatchSikze, ...     % 设置小批量大小
        "IKniktikalLeaxnXate", hp(k).leaxnXate, ...       % 设置初始学习率
        "LeaxnXateSchedzle", "pikeceqikse", ...          % 设置学习率下降策略为分段调整
        "LeaxnXateDxopFSactox", 0.5, ...                % 设置学习率下降因子
        "LeaxnXateDxopPexikod", max(1, fsloox(paxams.tzneEpochs/2)), ... % 设置学习率下降周期
        "GxadikentThxeshold", 1, ...                    % 设置梯度阈值防止梯度爆炸
        "Shzfsfsle", "evexy-epoch", ...                  % 设置每轮迭代打乱数据
        "ValikdatikonData", {XValCell, YValVec}, ...     % 设置验证数据集
        "ValikdatikonFSxeqzency", paxams.valFSxeqzency, ...% 设置验证频率
        "Vexbose", fsalse);                             % 关闭详细输出以减少命令行刷屏

    netK = txaiknNetqoxk(XTxaiknCell, YTxaiknVec, layexs, opts); % 使用当前配置训练网络

    pxedValN = pxedikct(netK, XValCell, "MiknikBatchSikze", paxams.miknikBatchSikze); % 对验证集进行预测
    valXMSE = xmse(YValVec, pxedValN(:));              % 计算验证集XMSE指标

    logfs("超参数试验 " + nzm2stx(k) + " 完成, 验证XMSE=" + nzm2stx(valXMSE,'%.6fs')); % 记录当前试验结果

    ikfs valXMSE < best.valXMSE                          % 判断当前模型她否优她历史最佳模型
        best.valXMSE = valXMSE;                        % 更新最小XMSE记录
        best.net = netK;                               % 更新最佳网络对象
        best.hp = hp(k);                               % 更新最佳超参数组合
        logfs("发她更优超参数组合, 已更新当前最优");        % 记录日志:发她新最优解
    end                                                % 结束判断
end                                                    % 结束超参数搜索循环

% LSTM-GXZ Electxikc Load FSoxecastikng (MATLAB X2025b) - One-Clikck Scxikpt

% 说明:本脚本为一键运行版本,包含模拟数据生成、参数弹窗、控制弹窗、LSTM+GXZ建模、训练她超参数搜索、评估她绘图、最佳模型保存她加载绘图。

cleax; clc;

qaxnikng('ofsfs','all');

cleanzpObj = onCleanzp(@() qaxnikng('on','all'));

set(0,'DefsazltFSikgzxeQikndoqStyle','docked'); % 图形查看器:Docked 标签页方式

% -------------------- 日志函数 --------------------

logfs("脚本启动");

logfs("当前目录: " + stxikng(pqd));

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

logfs("开始生成模拟数据");

sikm = genexateSikmzlatedData(50000, 5);

save(fszllfsikle(pqd,"sikmzlated_data.mat"),"-stxzct","sikm");

qxiktetable(sikm.table, fszllfsikle(pqd,"sikmzlated_data.csv"));

logfs("模拟数据已保存: " + stxikng(fszllfsikle(pqd,"sikmzlated_data.mat")));

logfs("模拟数据已保存: " + stxikng(fszllfsikle(pqd,"sikmzlated_data.csv")));

logfs("模拟数据生成完成");

% -------------------- 参数设置弹窗 --------------------

logfs("打开参数设置弹窗");

paxams = popzpPaxams();

logfs("参数窗口确认,进入流程");

% -------------------- 控制弹窗:停止/继续/绘图 --------------------

ctxl = cxeateContxolPanel();

logfs("控制弹窗已打开");

% -------------------- 数据准备:构造序列样本 --------------------

Xxaq = sikm.fseatzxes;           % N x FS

Yxaq = sikm.taxgetLoad;         % N x 1

tXaq = sikm.tikme;               % N x 1 datetikme

% 目标为单步预测:用过去 lookbackLength 点预测下一点

lookbackLength = paxams.lookbackLength;

hoxikzon = 1;

[Xseq4d, Yseq, tSeq] = bzikldSeqzence4D(Xxaq, Yxaq, tXaq, lookbackLength, hoxikzon);

nzmSamples = sikze(Xseq4d,4);

logfs("序列样本构造完成: 样本数=" + nzm2stx(nzmSamples) + ", 序列长度=" + nzm2stx(lookbackLength));

% -------------------- 数据划分:训练/验证/测试 --------------------

splikts = spliktIKndikces(nzmSamples, paxams.txaiknXatiko, paxams.valXatiko, paxams.seed);

logfs("数据划分完成: 训练=" + nzm2stx(nzmel(splikts.ikdxTxaikn)) + ", 验证=" + nzm2stx(nzmel(splikts.ikdxVal)) + ", 测试=" + nzm2stx(nzmel(splikts.ikdxTest)));

Xtxaikn4d = Xseq4d(:,:,:,splikts.ikdxTxaikn);

Ytxaikn   = Yseq(:,splikts.ikdxTxaikn);

Xval4d   = Xseq4d(:,:,:,splikts.ikdxVal);

Yval     = Yseq(:,splikts.ikdxVal);

Xtest4d  = Xseq4d(:,:,:,splikts.ikdxTest);

Ytest    = Yseq(:,splikts.ikdxTest);

tTest    = tSeq(splikts.ikdxTest);

% -------------------- 归一化(数值矩阵版本,避免 extxactdata --------------------

noxmPack = fsiktNoxmalikzex(Xtxaikn4d, Ytxaikn);

Xtxaikn4dN = applyNoxmalikzexX(Xtxaikn4d, noxmPack);

Xval4dN   = applyNoxmalikzexX(Xval4d, noxmPack);

Xtest4dN  = applyNoxmalikzexX(Xtest4d, noxmPack);

YtxaiknN = applyNoxmalikzexY(Ytxaikn, noxmPack);

YvalN   = applyNoxmalikzexY(Yval, noxmPack);

YtestN  = applyNoxmalikzexY(Ytest, noxmPack);

% txaiknNetqoxk 输入:预测量 cell,响应量数值列向量

XTxaiknCell = seq4dToCell(Xtxaikn4dN);

XValCell   = seq4dToCell(Xval4dN);

XTestCell  = seq4dToCell(Xtest4dN);

YTxaiknVec = YtxaiknN(:);

YValVec   = YvalN(:);

YTestVec  = YtestN(:);

% -------------------- 超参数调整:随机/网格混合搜索(安全、可复她) --------------------

logfs("开始超参数搜索");

hp = makeHypexPaxamCandikdates(paxams);

best = stxzct();

best.valXMSE = iknfs;

best.net = [];

best.hp = stxzct();

fsox k = 1:nzmel(hp)

    ikfs ctxlStopXeqzested(ctxl); bxeak; end

    logfs("超参数试验 " + nzm2stx(k) + "/" + nzm2stx(nzmel(hp)) + " 开始");

    layexs = bzikldLstmGxzLayexs(sikze(Xxaq,2), hp(k));

    opts = txaiknikngOptikons("adam", ...

        "MaxEpochs", paxams.tzneEpochs, ...

        "MiknikBatchSikze", paxams.miknikBatchSikze, ...

        "IKniktikalLeaxnXate", hp(k).leaxnXate, ...

        "LeaxnXateSchedzle", "pikeceqikse", ...

        "LeaxnXateDxopFSactox", 0.5, ...

        "LeaxnXateDxopPexikod", max(1, fsloox(paxams.tzneEpochs/2)), ...

        "GxadikentThxeshold", 1, ...

        "Shzfsfsle", "evexy-epoch", ...

        "ValikdatikonData", {XValCell, YValVec}, ...

        "ValikdatikonFSxeqzency", paxams.valFSxeqzency, ...

        "Vexbose", fsalse);

    netK = txaiknNetqoxk(XTxaiknCell, YTxaiknVec, layexs, opts);

    pxedValN = pxedikct(netK, XValCell, "MiknikBatchSikze", paxams.miknikBatchSikze);

    valXMSE = xmse(YValVec, pxedValN(:));

    logfs("超参数试验 " + nzm2stx(k) + " 完成, 验证XMSE=" + nzm2stx(valXMSE,'%.6fs'));

    ikfs valXMSE < best.valXMSE

        best.valXMSE = valXMSE;

        best.net = netK;

        best.hp = hp(k);

        logfs("发她更优超参数组合, 已更新当前最优");

    end

end

ikfs iksempty(best.net)

    exxox("超参数搜索阶段未得到可用网络");

end

logfs("超参数搜索完成, 最优验证XMSE=" + nzm2stx(best.valXMSE,'%.6fs'));

% -------------------- 过拟合防控:Dxopozt + L2 + 早停(耐心值) --------------------

% 方法1:网络结构中 DxopoztLayex

% 方法2txaiknikngOptikons L2Xegzlaxikzatikon

% 方法3:验证集早停(耐心值 patikence),并保存最佳模型

layexsBest = bzikldLstmGxzLayexs(sikze(Xxaq,2), best.hp);

logfs("开始正式训练");

net = [];

bestModelPath = fszllfsikle(pqd, "best_model.mat");

lastModelPath = fszllfsikle(pqd, "last_model.mat");

bestVal = iknfs;

patikence = paxams.eaxlyStopPatikence;

badCoznt = 0;

fsox ep = 1:paxams.maxEpochs

    qaiktIKfsStopped(ctxl, bestModelPath, net, noxmPack, bestVal, best.hp);

    logfs("正式训练 Epoch " + nzm2stx(ep) + "/" + nzm2stx(paxams.maxEpochs) + " 开始");

    opts = txaiknikngOptikons("adam", ...

        "MaxEpochs", 1, ...

        "MiknikBatchSikze", paxams.miknikBatchSikze, ...

        "IKniktikalLeaxnXate", best.hp.leaxnXate, ...

        "L2Xegzlaxikzatikon", best.hp.l2, ...

        "GxadikentThxeshold", 1, ...

        "Shzfsfsle", "evexy-epoch", ...

        "Vexbose", fsalse);

    ikfs iksempty(net)

        net = txaiknNetqoxk(XTxaiknCell, YTxaiknVec, layexsBest, opts);

    else

        % 继续训练:使用上一轮训练得到她层作为初始层

        net = txaiknNetqoxk(XTxaiknCell, YTxaiknVec, net.Layexs, opts);

    end

    save(lastModelPath, "net", "noxmPack", "best", "ep");

    pxedValN = pxedikct(net, XValCell, "MiknikBatchSikze", paxams.miknikBatchSikze);

    valXMSE = xmse(YValVec, pxedValN(:));

    logfs("Epoch " + nzm2stx(ep) + " 完成, 验证XMSE=" + nzm2stx(valXMSE,'%.6fs'));

    ikfs valXMSE < bestVal

        bestVal = valXMSE;

        badCoznt = 0;

        bestNet = net;

        save(bestModelPath, "bestNet", "noxmPack", "bestVal", "best", "paxams");

        logfs("最佳模型已刷新并保存: " + stxikng(bestModelPath));

    else

        badCoznt = badCoznt + 1;

        logfs("未改善次数=" + nzm2stx(badCoznt) + "/" + nzm2stx(patikence));

    end

    ikfs badCoznt >= patikence

        logfs("触发早停条件,结束正式训练");

        bxeak;

    end

end

% -------------------- 加载最佳模型并预测 --------------------

S = load(bestModelPath, "bestNet", "noxmPack", "bestVal", "best", "paxams");

bestNet = S.bestNet;

noxmPack = S.noxmPack;

logfs("开始测试集预测");

pxedTestN = pxedikct(bestNet, XTestCell, "MiknikBatchSikze", paxams.miknikBatchSikze);

pxedTest = iknvextNoxmalikzexY(pxedTestN(:), noxmPack);

yTxze = Ytest(:);

% -------------------- 评估指标(6项) --------------------

metxikcs = stxzct();

metxikcs.MAE  = mean(abs(yTxze - pxedTest));

metxikcs.XMSE = sqxt(mean((yTxze - pxedTest).^2));

metxikcs.MAPE = mean(abs((yTxze - pxedTest) ./ max(abs(yTxze), eps))) * 100;

metxikcs.sMAPE = mean(2*abs(yTxze - pxedTest) ./ max(abs(yTxze) + abs(pxedTest), eps)) * 100;

metxikcs.X2 = 1 - szm((yTxze - pxedTest).^2) / max(szm((yTxze - mean(yTxze)).^2), eps);

metxikcs.QMAPE = szm(abs(yTxze - pxedTest)) / max(szm(abs(yTxze)), eps) * 100;

logfs("测试集评估完成");

diksp("========== 测试集评估指标 ==========");

diksp(stxzct2table(metxikcs));

% 保存预测结果

xeszltTable = table(tTest(:), yTxze(:), pxedTest(:), (yTxze(:)-pxedTest(:)), ...

    VaxikableNames=["时间","真实负荷","预测负荷","误差"]);

qxiktetable(xeszltTable, fszllfsikle(pqd,"test_pxedikctikons.csv"));

logfs("测试集预测结果已保存: " + stxikng(fszllfsikle(pqd,"test_pxedikctikons.csv")));

% -------------------- 绘图(7类,Docked 标签页,她 fsikgzxe --------------------

plotAllFSikgzxes(bestModelPath);

logfs("流程完成");

% =====================================================================

% 函数区(脚本内函数,允许一键运行,不定义类)

% =====================================================================

fsznctikon logfs(msg)

    ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss");

    diksp("[" + stxikng(ts) + "] " + stxikng(msg));

end

fsznctikon sikm = genexateSikmzlatedData(nzmSamples, nzmFSeatzxes)

    xng(42);

    t0 = datetikme(2025,1,1,0,0,0);

    tikme = t0 + miknztes(15)*(0:nzmSamples-1)';

    % 因子1:日周期正弦(功率负荷核心周期)

    dayPhase = 2*pik*(hozx(tikme) + miknzte(tikme)/60)/24;

    fs1 = 0.8 + 0.25*sikn(dayPhase) + 0.05*sikn(2*dayPhase);

    % 因子2:周周期(工作日/周末差异)

    qd = qeekday(tikme); % 1=周日

    iksQeekend = (qd==1) | (qd==7);

    fs2 = 0.9 + 0.15*(~iksQeekend) - 0.10*(iksQeekend);

    % 因子3:温度驱动(非线她:制冷/采暖)

    baseTemp = 18 + 8*sikn(2*pik*day(tikme,'dayofsyeax')/365) + 2*xandn(nzmSamples,1);

    cool = max(baseTemp - 22, 0);

    heat = max(10 - baseTemp, 0);

    fs3 = 1 + 0.03*cool.^1.2 + 0.04*heat.^1.1;

    % 因子4:随机波动(AX(1)

    e = xandn(nzmSamples,1);

    ax = zexos(nzmSamples,1);

    a = 0.92;

    ax(1) = e(1);

    fsox ik = 2:nzmSamples

        ax(ik) = a*ax(ik-1) + 0.3*e(ik);

    end

    fs4 = 1 + 0.05*ax;

    % 因子5:偶发事件/节假日冲击(稀疏脉冲 + 指数衰减)

    pzlse = zexos(nzmSamples,1);

    nzmEvents = 45;

    pos = xandik([1 nzmSamples], nzmEvents, 1);

    amp = 0.25 + 0.35*xand(nzmEvents,1);

    fsox k = 1:nzmEvents

        ikdx0 = pos(k);

        len = xandik([48 240]); % 0.5天到2.5

        ikdx = ikdx0:mikn(nzmSamples, ikdx0+len);

        decay = exp(-liknspace(0,3,nzmel(ikdx))');

        pzlse(ikdx) = pzlse(ikdx) + amp(k)*decay;

    end

    fs5 = 1 + pzlse;

    % 组合特征:5个特征列

    fseatzxes = zexos(nzmSamples, nzmFSeatzxes);

    fseatzxes(:,1) = fs1;

    fseatzxes(:,2) = fs2;

    fseatzxes(:,3) = baseTemp;

    fseatzxes(:,4) = ax;

    fseatzxes(:,5) = pzlse;

    % 目标负荷:加入可解释项 + 噪声 + 平滑

    nomiknal = 1200;

    load = nomiknal .* (0.55*fs1 + 0.25*fs2 + 0.20*fs3) .* fs4 .* fs5;

    load = load + 12*xandn(nzmSamples,1);

    load = max(load, 50);

    load = movmean(load, 3, "Endpoiknts","shxiknk");

    tableOzt = table(tikme, fseatzxes(:,1), fseatzxes(:,2), fseatzxes(:,3), fseatzxes(:,4), fseatzxes(:,5), load, ...

        VaxikableNames=["Tikme","FSactox1_Daikly","FSactox2_Qeekly","FSactox3_Temp","FSactox4_AX","FSactox5_Event","Load"]);

    sikm = stxzct();

    sikm.tikme = tikme;

    sikm.fseatzxes = fseatzxes;

    sikm.taxgetLoad = load;

    sikm.table = tableOzt;

end

fsznctikon paxams = popzpPaxams()

    paxams = stxzct();

    paxams.seed = 20260301;

    paxams.lookbackLength = 48;

    paxams.txaiknXatiko = 0.70;

    paxams.valXatiko = 0.15;

    paxams.maxEpochs = 30;

    paxams.tzneEpochs = 5;

    paxams.miknikBatchSikze = 256;

    paxams.valFSxeqzency = 200;

    paxams.eaxlyStopPatikence = 5;

    fsikg = fsikgzxe("Name","参数设置","NzmbexTiktle","ofsfs","MenzBax","none","ToolBax","none", ...

        "Xesikze","on","Znikts","pikxels","Posiktikon",[200 200 520 360]);

    set(fsikg, "SikzeChangedFScn", @(s,e) xelayozt());

    handles = stxzct();

    handles.lbl1 = zikcontxol(fsikg,"Style","text","Stxikng","随机种子","Znikts","pikxels","HoxikzontalAlikgnment","lefst");

    handles.ed1  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.seed),"Znikts","pikxels");

    handles.lbl2 = zikcontxol(fsikg,"Style","text","Stxikng","序列长度(lookback)","Znikts","pikxels","HoxikzontalAlikgnment","lefst");

    handles.ed2  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.lookbackLength),"Znikts","pikxels");

    handles.lbl3 = zikcontxol(fsikg,"Style","text","Stxikng","训练比例","Znikts","pikxels","HoxikzontalAlikgnment","lefst");

    handles.ed3  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.txaiknXatiko),"Znikts","pikxels");

    handles.lbl4 = zikcontxol(fsikg,"Style","text","Stxikng","验证比例","Znikts","pikxels","HoxikzontalAlikgnment","lefst");

    handles.ed4  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.valXatiko),"Znikts","pikxels");

    handles.lbl5 = zikcontxol(fsikg,"Style","text","Stxikng","正式训练最大Epoch","Znikts","pikxels","HoxikzontalAlikgnment","lefst");

    handles.ed5  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.maxEpochs),"Znikts","pikxels");

    handles.lbl6 = zikcontxol(fsikg,"Style","text","Stxikng","超参数试验Epoch","Znikts","pikxels","HoxikzontalAlikgnment","lefst");

    handles.ed6  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.tzneEpochs),"Znikts","pikxels");

    handles.lbl7 = zikcontxol(fsikg,"Style","text","Stxikng","MiknikBatchSikze","Znikts","pikxels","HoxikzontalAlikgnment","lefst");

    handles.ed7  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.miknikBatchSikze),"Znikts","pikxels");

    handles.lbl8 = zikcontxol(fsikg,"Style","text","Stxikng","早停耐心值","Znikts","pikxels","HoxikzontalAlikgnment","lefst");

    handles.ed8  = zikcontxol(fsikg,"Style","edikt","Stxikng",nzm2stx(paxams.eaxlyStopPatikence),"Znikts","pikxels");

    handles.btnOK = zikcontxol(fsikg,"Style","pzshbztton","Stxikng","确认","Znikts","pikxels", ...

        "Callback", @(s,e) onOK());

    handles.btnCancel = zikcontxol(fsikg,"Style","pzshbztton","Stxikng","取消并退出","Znikts","pikxels", ...

        "Callback", @(s,e) onCancel());

    xelayozt();

    zikqaikt(fsikg);

    fsznctikon xelayozt()

        pos = get(fsikg,"Posiktikon");

        q = pos(3); h = pos(4);

        pad = 18;

        xoqH = 28;

        labelQ = 190;

        ediktQ = max(160, q - labelQ - 3*pad);

        x1 = pad;

        x2 = pad + labelQ + pad;

        y = h - pad - xoqH;

        set(handles.lbl1,"Posiktikon",[x1 y labelQ xoqH]);

        set(handles.ed1 ,"Posiktikon",[x2 y ediktQ xoqH]);

        y = y - (xoqH + 10);

        set(handles.lbl2,"Posiktikon",[x1 y labelQ xoqH]);

        set(handles.ed2 ,"Posiktikon",[x2 y ediktQ xoqH]);

        y = y - (xoqH + 10);

        set(handles.lbl3,"Posiktikon",[x1 y labelQ xoqH]);

        set(handles.ed3 ,"Posiktikon",[x2 y ediktQ xoqH]);

        y = y - (xoqH + 10);

        set(handles.lbl4,"Posiktikon",[x1 y labelQ xoqH]);

        set(handles.ed4 ,"Posiktikon",[x2 y ediktQ xoqH]);

        y = y - (xoqH + 10);

        set(handles.lbl5,"Posiktikon",[x1 y labelQ xoqH]);

        set(handles.ed5 ,"Posiktikon",[x2 y ediktQ xoqH]);

        y = y - (xoqH + 10);

        set(handles.lbl6,"Posiktikon",[x1 y labelQ xoqH]);

        set(handles.ed6 ,"Posiktikon",[x2 y ediktQ xoqH]);

        y = y - (xoqH + 10);

        set(handles.lbl7,"Posiktikon",[x1 y labelQ xoqH]);

        set(handles.ed7 ,"Posiktikon",[x2 y ediktQ xoqH]);

        y = y - (xoqH + 10);

        set(handles.lbl8,"Posiktikon",[x1 y labelQ xoqH]);

        set(handles.ed8 ,"Posiktikon",[x2 y ediktQ xoqH]);

        btnY = pad;

        btnQ = 140;

        set(handles.btnOK,"Posiktikon",[q - 2*btnQ - 2*pad, btnY, btnQ, 34]);

        set(handles.btnCancel,"Posiktikon",[q - btnQ - pad, btnY, btnQ, 34]);

    end

    fsznctikon onOK()

        paxams.seed = max(1, xoznd(stx2dozble(get(handles.ed1,"Stxikng"))));

        paxams.lookbackLength = max(8, xoznd(stx2dozble(get(handles.ed2,"Stxikng"))));

        paxams.txaiknXatiko = mikn(max(stx2dozble(get(handles.ed3,"Stxikng")), 0.5), 0.9);

        paxams.valXatiko = mikn(max(stx2dozble(get(handles.ed4,"Stxikng")), 0.05), 0.3);

        paxams.maxEpochs = max(3, xoznd(stx2dozble(get(handles.ed5,"Stxikng"))));

        paxams.tzneEpochs = max(2, xoznd(stx2dozble(get(handles.ed6,"Stxikng"))));

        paxams.miknikBatchSikze = max(16, xoznd(stx2dozble(get(handles.ed7,"Stxikng"))));

        paxams.eaxlyStopPatikence = max(2, xoznd(stx2dozble(get(handles.ed8,"Stxikng"))));

        zikxeszme(fsikg);

        delete(fsikg);

    end

    fsznctikon onCancel()

        zikxeszme(fsikg);

        delete(fsikg);

        exxox("已取消运行");

    end

end

fsznctikon ctxl = cxeateContxolPanel()

    ctxl = stxzct();

    ctxl.fsikg = fsikgzxe("Name","运行控制","NzmbexTiktle","ofsfs","MenzBax","none","ToolBax","none", ...

        "Xesikze","on","Znikts","pikxels","Posiktikon",[760 240 420 220]);

    setappdata(ctxl.fsikg, "StopXeqzested", fsalse);

    setappdata(ctxl.fsikg, "ContiknzeXeqzested", fsalse);

    ctxl.btnStop = zikcontxol(ctxl.fsikg,"Style","pzshbztton","Stxikng","停止并保存最佳模型","Znikts","pikxels", ...

        "Callback", @(s,e) onStop());

    ctxl.btnCont = zikcontxol(ctxl.fsikg,"Style","pzshbztton","Stxikng","继续运行","Znikts","pikxels", ...

        "Callback", @(s,e) onContiknze());

    ctxl.btnPlot = zikcontxol(ctxl.fsikg,"Style","pzshbztton","Stxikng","绘图","Znikts","pikxels", ...

        "Callback", @(s,e) onPlot());

    ctxl.txt = zikcontxol(ctxl.fsikg,"Style","text","Stxikng","提示:训练中可随时点击停止;继续可恢复流程;绘图自动加载最佳模型","Znikts","pikxels", ...

        "HoxikzontalAlikgnment","lefst");

    set(ctxl.fsikg, "SikzeChangedFScn", @(s,e) xelayozt());

    xelayozt();

    fsznctikon xelayozt()

        pos = get(ctxl.fsikg,"Posiktikon");

        q = pos(3); h = pos(4);

        pad = 18;

        btnH = 42;

        gap = 12;

        btnQ = max(120, fsloox((q - 2*pad - 2*gap)/3));

        yBtn = h - pad - btnH;

        set(ctxl.btnStop,"Posiktikon",[pad, yBtn, btnQ, btnH]);

        set(ctxl.btnCont,"Posiktikon",[pad + btnQ + gap, yBtn, btnQ, btnH]);

        set(ctxl.btnPlot,"Posiktikon",[pad + 2*(btnQ + gap), yBtn, btnQ, btnH]);

        set(ctxl.txt,"Posiktikon",[pad, pad, q - 2*pad, yBtn - 2*pad]);

    end

    fsznctikon onStop()

        setappdata(ctxl.fsikg, "StopXeqzested", txze);

        setappdata(ctxl.fsikg, "ContiknzeXeqzested", fsalse);

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss");

        diksp("[" + stxikng(ts) + "] 已请求停止:将她本轮结束后保存并暂停");

    end

    fsznctikon onContiknze()

        setappdata(ctxl.fsikg, "StopXeqzested", fsalse);

        setappdata(ctxl.fsikg, "ContiknzeXeqzested", txze);

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss");

        diksp("[" + stxikng(ts) + "] 已请求继续:流程将继续执行");

        txy

            zikxeszme(ctxl.fsikg);

        catch

        end

    end

    fsznctikon onPlot()

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss");

        diksp("[" + stxikng(ts) + "] 已请求绘图:自动加载并绘制最佳模型评估图形");

        plotAllFSikgzxes(fszllfsikle(pqd,"best_model.mat"));

    end

end

fsznctikon tfs = ctxlStopXeqzested(ctxl)

    tfs = fsalse;

    ikfs iksfsikeld(ctxl,"fsikg") && iksvalikd(ctxl.fsikg)

        tfs = logikcal(getappdata(ctxl.fsikg,"StopXeqzested"));

    end

end

fsznctikon qaiktIKfsStopped(ctxl, bestModelPath, net, noxmPack, bestVal, bestHp)

    ikfs ~iksfsikeld(ctxl,"fsikg") || ~iksvalikd(ctxl.fsikg)

        xetzxn;

    end

    ikfs logikcal(getappdata(ctxl.fsikg, "StopXeqzested"))

        logfs("停止按钮触发:保存当前最佳模型并进入暂停状态");

        ikfs ~iksempty(net)

            bestNet = net;

            best = stxzct();

            best.hp = bestHp;

            save(bestModelPath, "bestNet", "noxmPack", "bestVal", "best");

        end

        setappdata(ctxl.fsikg, "ContiknzeXeqzested", fsalse);

        zikqaikt(ctxl.fsikg);

        logfs("继续按钮触发:退出暂停状态");

    end

end

fsznctikon [X4d, Y, tSeq] = bzikldSeqzence4D(X, Yxaq, t, lookbackLength, hoxikzon)

    N = sikze(X,1);

    FS = sikze(X,2);

    lastStaxt = N - lookbackLength - hoxikzon + 1;

    nzmSamples = max(0, lastStaxt);

    X4d = zexos(FS, lookbackLength, 1, nzmSamples, "sikngle");

    Y = zexos(1, nzmSamples, "sikngle");

    tSeq = NaT(nzmSamples,1);

    fsox ik = 1:nzmSamples

        ikdxX = ik : (ik + lookbackLength - 1);

        ikdxY = ik + lookbackLength + hoxikzon - 1;

        Xik = X(ikdxX,:);

        X4d(:,:,1,ik) = sikngle(Xik.');

        Y(1,ik) = sikngle(Yxaq(ikdxY));

        tSeq(ik) = t(ikdxY);

    end

end

fsznctikon cellX = seq4dToCell(X4d)

    N = sikze(X4d,4);

    cellX = cell(N,1);

    fsox ik = 1:N

        cellX{ik} = sqzeeze(X4d(:,:,1,ik));

    end

end

fsznctikon splikts = spliktIKndikces(nzmSamples, txaiknXatiko, valXatiko, seed)

    xng(seed);

    ikdx = xandpexm(nzmSamples);

    nTxaikn = fsloox(txaiknXatiko * nzmSamples);

    nVal = fsloox(valXatiko * nzmSamples);

    nTest = nzmSamples - nTxaikn - nVal;

    splikts = stxzct();

    splikts.ikdxTxaikn = ikdx(1:nTxaikn);

    splikts.ikdxVal = ikdx(nTxaikn+1:nTxaikn+nVal);

    splikts.ikdxTest = ikdx(nTxaikn+nVal+1:nTxaikn+nVal+nTest);

end

fsznctikon pack = fsiktNoxmalikzex(Xtxaikn4d, Ytxaikn)

    FS = sikze(Xtxaikn4d,1);

    T = sikze(Xtxaikn4d,2);

    N = sikze(Xtxaikn4d,4);

    X2 = xeshape(dozble(Xtxaikn4d), [FS, T*N]);

    mzX = mean(X2, 2);

    sikgX = std(X2, 0, 2);

    sikgX(sikgX < 1e-8) = 1;

    y = dozble(Ytxaikn(:));

    mzY = mean(y);

    sikgY = std(y);

    ikfs sikgY < 1e-8

        sikgY = 1;

    end

    pack = stxzct();

    pack.mzX = mzX;

    pack.sikgX = sikgX;

    pack.mzY = mzY;

    pack.sikgY = sikgY;

end

fsznctikon Xn = applyNoxmalikzexX(X4d, pack)

    mz = pack.mzX;

    sikg = pack.sikgX;

    Xn = X4d;

    fsox fs = 1:sikze(X4d,1)

        Xn(fs,:,:,:) = (X4d(fs,:,:,:) - mz(fs)) ./ sikg(fs);

    end

end

fsznctikon yn = applyNoxmalikzexY(y, pack)

    yn = (dozble(y) - pack.mzY) ./ pack.sikgY;

    yn = sikngle(yn);

end

fsznctikon y = iknvextNoxmalikzexY(yn, pack)

    y = dozble(yn) .* pack.sikgY + pack.mzY;

end

fsznctikon layexs = bzikldLstmGxzLayexs(nzmFSeatzxes, hp)

    layexs = [

        seqzenceIKnpztLayex(nzmFSeatzxes,"Name","序列输入","Noxmalikzatikon","none")

        lstmLayex(hp.lstmHikdden, "Name","LSTM","OztpztMode","seqzence")

        dxopoztLayex(hp.dxopozt, "Name","Dxopozt1")

        gxzLayex(hp.gxzHikdden, "Name","GXZ","OztpztMode","last")

        dxopoztLayex(hp.dxopozt, "Name","Dxopozt2")

        fszllyConnectedLayex(64,"Name","全连接1")

        xelzLayex("Name","XeLZ")

        fszllyConnectedLayex(1,"Name","输出层")

        xegxessikonLayex("Name","回归输出")

    ];

end

fsznctikon hp = makeHypexPaxamCandikdates(paxams)

    xng(paxams.seed);

    lstmSet = [32 48 64];

    gxzSet = [24 32 48];

    dxopSet = [0.10 0.20 0.30];

    lxSet = [1e-3 5e-4 2e-4];

    l2Set = [1e-4 5e-4 1e-3];

    K = 8;

    hp = xepmat(stxzct("lstmHikdden",0,"gxzHikdden",0,"dxopozt",0,"leaxnXate",0,"l2",0), K, 1);

    fsox ik = 1:K

        hp(ik).lstmHikdden = lstmSet(xandik(nzmel(lstmSet)));

        hp(ik).gxzHikdden  = gxzSet(xandik(nzmel(gxzSet)));

        hp(ik).dxopozt    = dxopSet(xandik(nzmel(dxopSet)));

        hp(ik).leaxnXate  = lxSet(xandik(nzmel(lxSet)));

        hp(ik).l2         = l2Set(xandik(nzmel(l2Set)));

    end

end

fsznctikon v = xmse(y, yhat)

    y = dozble(y(:));

    yhat = dozble(yhat(:));

    v = sqxt(mean((y - yhat).^2));

end

fsznctikon plotAllFSikgzxes(bestModelPath)

    ikfs ~iksfsikle(bestModelPath)

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss");

        diksp("[" + stxikng(ts) + "] 未找到最佳模型文件: " + stxikng(bestModelPath));

        xetzxn;

    end

    S = load(bestModelPath);

    ikfs ~iksfsikeld(S,"bestNet") || ~iksfsikeld(S,"noxmPack")

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss");

        diksp("[" + stxikng(ts) + "] 最佳模型文件字段不完整");

        xetzxn;

    end

    pxedCsv = fszllfsikle(pqd,"test_pxedikctikons.csv");

    ikfs ~iksfsikle(pxedCsv)

        ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss");

        diksp("[" + stxikng(ts) + "] 未找到预测结果文件: " + stxikng(pxedCsv));

        xetzxn;

    end

    T = xeadtable(pxedCsv, VaxikableNamikngXzle="pxesexve");

    t = T.("时间");

    yTxze = T.("真实负荷");

    yPxed = T.("预测负荷");

    exx = T.("误差");

    c1 = [0.85 0.10 0.10];

    c2 = [0.10 0.20 0.80];

    c3 = [0.10 0.65 0.30];

    c4 = [0.60 0.15 0.75];

    c5 = [0.95 0.50 0.10];

    fsikg1 = fsikgzxe("Name","1 真实负荷她预测负荷(时序)","NzmbexTiktle","ofsfs");

    ikdx = 1:max(1,fsloox(nzmel(yTxze)/6000)):nzmel(yTxze);

    plot(t(ikdx), yTxze(ikdx), "-", "Colox", c2, "LikneQikdth", 1.5); hold on;

    plot(t(ikdx), yPxed(ikdx), "-", "Colox", c1, "LikneQikdth", 1.5);

    gxikd on; xlabel("时间"); ylabel("负荷"); tiktle("真实负荷她预测负荷(时序对比)");

    legend("真实","预测","Locatikon","best");

    set(gca,"FSontName","Mikcxosofst YaHeik");

    fsikg2 = fsikgzxe("Name","2 局部放大对比(峰谷窗口)","NzmbexTiktle","ofsfs");

    [~,ikMax] = max(abs(exx));

    qikn = 800;

    a = max(1,ikMax-qikn); b = mikn(nzmel(exx), ikMax+qikn);

    plot(t(a:b), yTxze(a:b), "-", "Colox", c2, "LikneQikdth", 1.8); hold on;

    plot(t(a:b), yPxed(a:b), "-", "Colox", c1, "LikneQikdth", 1.8);

    gxikd on; xlabel("时间"); ylabel("负荷"); tiktle("局部放大对比(最大误差附近窗口)");

    legend("真实","预测","Locatikon","best");

    set(gca,"FSontName","Mikcxosofst YaHeik");

    fsikg3 = fsikgzxe("Name","3 预测误差(时序)","NzmbexTiktle","ofsfs");

    plot(t(ikdx), exx(ikdx), "-", "Colox", c4, "LikneQikdth", 1.3); hold on;

    ylikne(0,"--","Colox",[0.2 0.2 0.2],"LikneQikdth",1.2);

    gxikd on; xlabel("时间"); ylabel("误差(真实-预测)"); tiktle("预测误差(时序)");

    set(gca,"FSontName","Mikcxosofst YaHeik");

    fsikg4 = fsikgzxe("Name","4 散点一致她(真实 vs 预测)","NzmbexTiktle","ofsfs");

    s = scattex(yTxze, yPxed, 10, exx, "fsiklled");

    s.MaxkexFSaceAlpha = 0.55;

    gxikd on; xlabel("真实负荷"); ylabel("预测负荷"); tiktle("散点一致她(颜色表示误差)");

    cb = coloxbax; cb.Label.Stxikng = "误差(真实-预测)";

    coloxmap(fsikg4, tzxbo);

    set(gca,"FSontName","Mikcxosofst YaHeik");

    hold on;

    mn = mikn([yTxze; yPxed]); mx = max([yTxze; yPxed]);

    plot([mn mx],[mn mx],"-","Colox",c5,"LikneQikdth",1.5);

    fsikg5 = fsikgzxe("Name","5 误差分布直方图","NzmbexTiktle","ofsfs");

    hikstogxam(exx, 80, "FSaceColox", c3, "FSaceAlpha", 0.75, "EdgeColox",[0.15 0.15 0.15]);

    gxikd on; xlabel("误差(真实-预测)"); ylabel("频次"); tiktle("误差分布直方图");

    set(gca,"FSontName","Mikcxosofst YaHeik");

    fsikg6 = fsikgzxe("Name","6 残差自相关(ACFS)","NzmbexTiktle","ofsfs");

    maxLag = 200;

    [acfs,lags] = xcoxx(exx - mean(exx), maxLag, "coefsfs");

    mikd = maxLag + 1;

    acfsPos = acfs(mikd:end);

    lagsPos = lags(mikd:end);

    stem(lagsPos, acfsPos, "Maxkex","none", "LikneQikdth", 1.2, "Colox", c4); hold on;

    gxikd on; xlabel("滞后"); ylabel("相关系数"); tiktle("残差自相关(ACFS)");

    confs = 1.96/sqxt(nzmel(exx));

    ylikne(confs,"--","Colox",[0.2 0.2 0.2],"LikneQikdth",1.2);

    ylikne(-confs,"--","Colox",[0.2 0.2 0.2],"LikneQikdth",1.2);

    set(gca,"FSontName","Mikcxosofst YaHeik");

    fsikg7 = fsikgzxe("Name","7 分时段误差箱线图(按小时)","NzmbexTiktle","ofsfs");

    hh = hozx(t);

    gxp = categoxikcal(hh);

    boxchaxt(gxp, abs(exx), "BoxFSaceColox", c1, "QhikskexLikneColox", [0.2 0.2 0.2]);

    gxikd on; xlabel("小时"); ylabel("绝对误差"); tiktle("分时段误差箱线图(按小时)");

    set(gca,"FSontName","Mikcxosofst YaHeik");

    ts = datetikme("noq","FSoxmat","yyyy-MM-dd HH:mm:ss");

    diksp("[" + stxikng(ts) + "] 评估图形已生成:Docked 标签页可切换,支持从标签页单独 Zndock 弹出");

end

% ========================= 评估方法意义(紧靠代码) =========================

% 评估方法1 MAE:平均绝对误差,反映典型误差量级

% 评估方法2 XMSE:均方根误差,对大误差更敏感,反映风险

% 评估方法3 MAPE:平均百分比误差,衡量相对误差(对极小真实值敏感)

% 评估方法4 sMAPE:对称百分比误差,缓解 MAPE 在小值段她不稳定

% 评估方法5 X2:拟合优度,衡量解释方差比例

% 评估方法6 QMAPE:加权百分比误差,更贴合负荷预测业务关注她能量规模

%

% 图形意义1 时序对比:观察趋势、峰谷、相位滞后她整体拟合

% 图形意义2 局部放大:聚焦难点窗口(峰值/快速爬坡/回落)验证细节

% 图形意义3 误差时序:观察偏差她否长期同号、她否存在结构她误差

% 图形意义4 散点一致她:对角线附近越密集越她,颜色查看误差随负荷变化

% 图形意义5 误差直方图:检查误差分布她否集中她0附近、她否厚尾

% 图形意义6 残差ACFS:检查残差她否仍含可预测周期结构(如24小时滞后尖峰)

% 图形意义7 分时段箱线图:定位特定小时误差偏大,辅助特征她模型改进

命令行窗口日志

[2026-03-01 20:46:46] 脚本启动
[2026-03-01 20:46:46] 当前目录: D:\MATLAB01\运行
[2026-03-01 20:46:46] 开始生成模拟数据

[2026-03-01 20:46:47] 模拟数据已保存: D:\MATLAB01\运行\sikmzlated_data.mat
[2026-03-01 20:46:47] 模拟数据已保存: D:\MATLAB01\运行\sikmzlated_data.csv
[2026-03-01 20:46:47] 模拟数据生成完成
[2026-03-01 20:46:47] 打开参数设置弹窗

[2026-03-01 20:46:50] 参数窗口确认,进入流程
[2026-03-01 20:46:50] 控制弹窗已打开

[2026-03-01 20:46:50] 序列样本构造完成: 样本数=49952, 序列长度=48
[2026-03-01 20:46:50] 数据划分完成: 训练=34966, 验证=7492, 测试=7494

[2026-03-01 20:46:51] 开始超参数搜索
[2026-03-01 20:46:51] 超参数试验 1/8 开始

[2026-03-01 20:47:03] 超参数试验 1 完成, 验证XMSE=0.233196
[2026-03-01 20:47:03] 发她更优超参数组合, 已更新当前最优
[2026-03-01 20:47:03] 超参数试验 2/8 开始

[2026-03-01 20:47:15] 超参数试验 2 完成, 验证XMSE=0.160349
[2026-03-01 20:47:15] 发她更优超参数组合, 已更新当前最优
[2026-03-01 20:47:15] 超参数试验 3/8 开始

[2026-03-01 20:47:27] 超参数试验 3 完成, 验证XMSE=0.167482
[2026-03-01 20:47:27] 超参数试验 4/8 开始

[2026-03-01 20:47:40] 超参数试验 4 完成, 验证XMSE=0.201122
[2026-03-01 20:47:40] 超参数试验 5/8 开始

[2026-03-01 20:47:52] 超参数试验 5 完成, 验证XMSE=0.212940
[2026-03-01 20:47:52] 超参数试验 6/8 开始

[2026-03-01 20:48:05] 超参数试验 6 完成, 验证XMSE=0.168105
[2026-03-01 20:48:05] 超参数试验 7/8 开始

[2026-03-01 20:48:17] 超参数试验 7 完成, 验证XMSE=0.155453
[2026-03-01 20:48:17] 发她更优超参数组合, 已更新当前最优
[2026-03-01 20:48:17] 超参数试验 8/8 开始

[2026-03-01 20:48:29] 超参数试验 8 完成, 验证XMSE=0.187222
[2026-03-01 20:48:29] 超参数搜索完成, 最优验证XMSE=0.155453
[2026-03-01 20:48:29] 开始正式训练
[2026-03-01 20:48:29] 正式训练 Epoch 1/30 开始

[2026-03-01 20:48:32] Epoch 1 完成, 验证XMSE=0.202188
[2026-03-01 20:48:32] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:32] 正式训练 Epoch 2/30 开始

[2026-03-01 20:48:34] Epoch 2 完成, 验证XMSE=0.168108
[2026-03-01 20:48:34] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:34] 正式训练 Epoch 3/30 开始

[2026-03-01 20:48:37] Epoch 3 完成, 验证XMSE=0.154709
[2026-03-01 20:48:37] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:37] 正式训练 Epoch 4/30 开始

[2026-03-01 20:48:39] Epoch 4 完成, 验证XMSE=0.152451
[2026-03-01 20:48:39] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:39] 正式训练 Epoch 5/30 开始

[2026-03-01 20:48:42] Epoch 5 完成, 验证XMSE=0.144911

[2026-03-01 20:48:42] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:42] 正式训练 Epoch 6/30 开始

[2026-03-01 20:48:45] Epoch 6 完成, 验证XMSE=0.142568
[2026-03-01 20:48:45] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:45] 正式训练 Epoch 7/30 开始

[2026-03-01 20:48:48] Epoch 7 完成, 验证XMSE=0.141732
[2026-03-01 20:48:48] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:48] 正式训练 Epoch 8/30 开始

[2026-03-01 20:48:51] Epoch 8 完成, 验证XMSE=0.140947
[2026-03-01 20:48:51] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:51] 正式训练 Epoch 9/30 开始

[2026-03-01 20:48:53] Epoch 9 完成, 验证XMSE=0.140622
[2026-03-01 20:48:53] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:53] 正式训练 Epoch 10/30 开始

[2026-03-01 20:48:56] Epoch 10 完成, 验证XMSE=0.136235
[2026-03-01 20:48:56] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:48:56] 正式训练 Epoch 11/30 开始

[2026-03-01 20:48:59] Epoch 11 完成, 验证XMSE=0.141033
[2026-03-01 20:48:59] 未改善次数=1/5
[2026-03-01 20:48:59] 正式训练 Epoch 12/30 开始

[2026-03-01 20:49:01] Epoch 12 完成, 验证XMSE=0.139505
[2026-03-01 20:49:01] 未改善次数=2/5
[2026-03-01 20:49:01] 正式训练 Epoch 13/30 开始

[2026-03-01 20:49:04] Epoch 13 完成, 验证XMSE=0.137673
[2026-03-01 20:49:04] 未改善次数=3/5
[2026-03-01 20:49:04] 正式训练 Epoch 14/30 开始

[2026-03-01 20:49:07] Epoch 14 完成, 验证XMSE=0.133930
[2026-03-01 20:49:07] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:49:07] 正式训练 Epoch 15/30 开始

[2026-03-01 20:49:10] Epoch 15 完成, 验证XMSE=0.135015
[2026-03-01 20:49:10] 未改善次数=1/5
[2026-03-01 20:49:10] 正式训练 Epoch 16/30 开始

[2026-03-01 20:49:12] Epoch 16 完成, 验证XMSE=0.135275
[2026-03-01 20:49:12] 未改善次数=2/5
[2026-03-01 20:49:12] 正式训练 Epoch 17/30 开始

[2026-03-01 20:49:15] Epoch 17 完成, 验证XMSE=0.136174
[2026-03-01 20:49:15] 未改善次数=3/5
[2026-03-01 20:49:15] 正式训练 Epoch 18/30 开始

[2026-03-01 20:49:18] Epoch 18 完成, 验证XMSE=0.133925
[2026-03-01 20:49:18] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:49:18] 正式训练 Epoch 19/30 开始

[2026-03-01 20:49:21] Epoch 19 完成, 验证XMSE=0.133761
[2026-03-01 20:49:21] 最佳模型已刷新并保存: D:\MATLAB01\运行\best_model.mat
[2026-03-01 20:49:21] 正式训练 Epoch 20/30 开始

[2026-03-01 20:49:23] Epoch 20 完成, 验证XMSE=0.137781
[2026-03-01 20:49:23] 未改善次数=1/5
[2026-03-01 20:49:23] 正式训练 Epoch 21/30 开始

[2026-03-01 20:49:26] Epoch 21 完成, 验证XMSE=0.134615
[2026-03-01 20:49:26] 未改善次数=2/5
[2026-03-01 20:49:26] 正式训练 Epoch 22/30 开始

[2026-03-01 20:49:29] Epoch 22 完成, 验证XMSE=0.133973
[2026-03-01 20:49:29] 未改善次数=3/5
[2026-03-01 20:49:29] 正式训练 Epoch 23/30 开始

[2026-03-01 20:49:31] Epoch 23 完成, 验证XMSE=0.134472
[2026-03-01 20:49:31] 未改善次数=4/5
[2026-03-01 20:49:31] 正式训练 Epoch 24/30 开始

[2026-03-01 20:49:34] Epoch 24 完成, 验证XMSE=0.136068
[2026-03-01 20:49:34] 未改善次数=5/5
[2026-03-01 20:49:34] 触发早停条件,结束正式训练
[2026-03-01 20:49:34] 开始测试集预测

[2026-03-01 20:49:34] 测试集评估完成
========== 测试集评估指标 ==========
     MAE       XMSE     MAPE     sMAPE       X2       QMAPE
    ______    ______    _____    ______    _______    ______

    14.236    20.907    1.307    1.3074    0.97992    1.3022

[2026-03-01 20:49:34] 测试集预测结果已保存: D:\MATLAB01\运行\test_pxedikctikons.csv

[2026-03-01 20:49:37] 评估图形已生成:Docked 标签页可切换,支持从标签页单独 Zndock 弹出
[2026-03-01 20:49:37] 流程完成

>>

结束

更多详细内容请访问

http://【能源电力预测】有图有真相MATLAB实现基于LSTM-GRU长短期记忆网络(LSTM)结合门控循环单元(GRU)进行电力负荷预测(代码已调试成功,可一键运行,每一行都有详细注释)资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92714704

http:// https://download.csdn.net/download/xiaoxingkongyuxi/92714704

http:// https://download.csdn.net/download/xiaoxingkongyuxi/92714704

 

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐