ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

基于贝叶斯优化的PatchTST综合能源负荷多变量预测实现

基于贝叶斯优化的PatchTST综合能源负荷多变量预测实现 做综合能源系统的负荷预测最让人头疼的往往不是单条曲线怎么拟合而是几条不同能源品种的曲线放在一起时它们之间的耦合关系、量纲差异、突变规律怎么处理。我手头这个项目就是这么来的一个园区级的综合能源系统需要同时预测电负荷、热负荷和冷负荷外部还要叠加上温度、湿度、光照、风速这些气象因素以及工作日/节假日标记。如果用传统的LSTM一路堆到底效果很容易到瓶颈换标准Transformer又会遇到长序列计算量大、局部模式提取弱的问题。后来我把模型切换成PatchTST调参再用贝叶斯优化去搜整体思路就是标题里写的基于贝叶斯优化的PatchTST综合能源负荷多变量时间序列预测Python代码完整实现。这篇内容适合两类人看一类是做电力、能源、园区微网相关的算法工程师另一类是正在研究时间序列预测、想把PatchTST这类新模型落地到自己数据集上的同学。我会把模型机制、调参思路、数据构造以及实际训练中踩过的坑都串起来讲代码部分也会给出可以直接套用的骨架。1. 项目拆解为什么把宝押在PatchTST和贝叶斯优化上先说说整个项目的技术路线是怎么定下来的。综合能源负荷预测本质上是多变量时间序列预测问题输入是一段历史窗口的多维张量输出是未来一段时间的多条负荷曲线。和普通单序列预测比难点主要在三个地方。第一个难点是通道数多且量纲差异大。电负荷可能是几千千瓦热负荷可能是吉焦每小时冷负荷又不一样这几个变量如果直接拼成一个矩阵丢给模型训练时梯度很容易被量纲大的通道主导。所以需要做精细的归一化并且模型本身要具备一定的通道独立性。第二个难点是局部模式很重要。综合能源负荷曲线不像股票那样充满随机游走它有明显日内周期性、气温敏感性和节假日突变性。这些模式往往体现在几十个时间步的局部片段里比如早上电负荷爬坡这一段往往持续三五个采样点就有明显趋势特征。标准Transformer用的是逐点注意力每个时间步单独做embedding对局部片段的建模效率不高而PatchTST的核心就是把时间步先切成patch把一个patch当成一个token这样自注意力直接作用在语义更完整的patch上局部趋势一下就清楚了很多。第三个难点是调参空间大人工试错成本太高。PatchTST涉及的参数很多输入窗口长度、patch长度、stride步长、Transformer层数、头数、d_model、dropout、学习率再加上优化器和调度策略如果靠网格搜索或者手动试一组跑下来半个多小时根本试不完。贝叶斯优化正好解决这个问题它能根据历史试验结果选择下一组最有潜力的参数用更少的尝试次数找到更好的配置。所以整个项目的架构就很清晰了数据侧做好多变量窗口构造和归一化模型侧用PatchTST提取patch级语义特征优化侧用贝叶斯替代模型去搜超参数最终目标是让验证集上的多步预测误差尽量小。这个组合在实际跑下来之后效果确实比原来的LSTM基线好了不少尤其是热负荷这种带明显延迟惯性的曲线预测精度提升很直观。2. PatchTST核心机制从内到外讲透每个设计选择想用好一个模型不能只调库得把它的设计逻辑搞清楚。PatchTST的关键创新可以概括为三个词Patch、Channel Independence、Transformer Encoder。下面逐个拆开讲。2.1 时序分割与Patch化处理标准Transformer处理时间序列时通常是把每个时间步映射成一个token。假设输入序列长度是96那就有96个token每个token只代表一个采样点的值自注意力在这96个点之间做交互。这里的问题很明显单点信息密度低而且注意力矩阵是96x96计算和内存开销都不小。PatchTST的做法是把时间维切成小段每个小段叫一个patch把patch整体映射成一个token。假设patch长度是16stride是8序列长度96就能切出约11个patch注意力矩阵从96x96缩到11x11计算量少了将近一个数量级同时每个token包含16步的局部上下文语义信息丰富得多。这个设计背后的逻辑很像看图时的滑窗识别人眼看一幅图不会逐像素扫描而是先看一个个局部区域再把这些区域组合起来理解全局。PatchTST的patch化也是这个道理它让模型天然聚焦在局部形态上而局部形态恰恰是负荷曲线的核心特征。实际实现时patch化有两种常见做法。一种是用一维卷积配合合适的卷积核大小和步长来实现另一种是先unfold再线性映射。效果上两者差别不大但用unfold加Linear的方式更直观方便之后调整patch长度。有一点要特别注意patch数量不能太少否则序列全局信息会丢得太多。如果序列长度是96patch长度和stride都设16patch数量能到6个左右再往下减信息损失就会明显了。2.2 通道独立与多变量协作通道独立这个设计我刚开始觉得有点反直觉。多变量预测难道不应该让不同变量之间充分交互吗怎么还搞独立PatchTST的Channel Independence策略指的是每个变量的patch序列在embedding和编码阶段各自独立处理共用同一套映射和Transformer参数最后再通过预测头输出各自的结果。这样做的好处是不同通道的量纲差异不会在embedding层就被混在一起模型可以把更多容量花在通道内部的时序模式上。但独立并不等于完全不合作。在多变量场景里我们可以做两件事来引入通道间交互。第一在输入侧把外生变量气象、日历等作为额外通道一起喂进去让模型在通道维度上间接学到它们的联合分布第二在输出侧用多任务预测头把电、热、冷三个负荷同时输出共享损失和梯度更新通过共用底层特征实现隐性的跨通道协同。我在项目里实际采用的是“通道独立编码多任务输出”的组合。实测下来这种方式比完全把多通道concat成一个矩阵让模型自由混合效果更稳尤其是冷负荷和热负荷这种相关性弱的通道不会被强通道的信息淹没。2.3 Encoder与预测头PatchTST的编码器用的就是标准的Transformer Encoder由多头自注意力、前馈网络、残差连接和层归一化组成。注意这里的自注意力作用在patch序列上也就是说注意力对象是若干个patch之间的关系而不是时间点之间的关系。这样做的好处是注意力可以更高效地捕捉跨时间的依赖比如今天下午3点的负荷模式与昨天下午3点的负荷模式之间的相似性。预测头部分PatchTST用的是直接多步预测一次性输出未来所有时间点的值而不是像有些方法那样逐步递归生成。直接多步预测的好处非常明显避免了递归多步预测里的误差累积训练时也用不着teacher forcing。实现上Encoder输出的特征向量会被展平通过一个线性层直接映射到 pred_len 长度的输出。对于多变量版本预测头可以设计成输出形状为 (batch, num_channels, pred_len) 的张量。每一路用独立的线性映射但也共享底层的Encoder特征。这样电、热、冷三条曲线既各自有独立的输出投影又能共享同一套时序特征提取网络效果比较均衡。3. 贝叶斯优化如何介入深度学习调参模型选好之后下一个问题是参数怎么定。说实话PatchTST这个模型对超参数还算敏感尤其是patch长度和stride直接决定token序列的信息粒度手动试错非常痛苦。我一开始用的是传统网格搜索一组参数跑一次完整训练结果发现完全行不通。后来切到贝叶斯优化整个流程才顺利起来。3.1 为什么不用网格搜索网格搜索的本质是把参数空间切成均匀网格然后逐个组合尝试。它最大的问题是维度灾难参数一旦多起来组合数爆炸式增长。假设我们只搜6个参数每个参数取5个候选值组合数就是5的6次方一万五千多种组合一次训练要几分钟全跑完需要几个月根本不现实。随机搜索稍微好一些它可以指定尝试次数但因为是随机采样不会利用已经跑过的结果前面的经验对后面的搜索没有帮助。贝叶斯优化的思路完全不同。它把超参数搜索当成一个黑盒优化问题目标函数是一次完整的训练流程输入是一组超参数输出是验证集误差。贝叶斯优化会在每次试验后更新一个概率代理模型来描述“哪些参数组合更可能让误差小”下一次试验就选择代理模型认为最有潜力的参数组合去跑。这就像找餐厅网格搜索是把整条街每家店都吃一遍随机搜索是闭着眼挑几家贝叶斯优化则是吃完几家后总结出“这条街上的高分店都集中在某个区域”然后重点去那片区域继续找。3.2 搜索空间设计与采集函数机制贝叶斯优化里有两个关键组件一个是参数搜索空间一个是采集函数。搜索空间决定了我们允许模型在哪些范围里找参数。我设计PatchTST的搜索空间时大概分成三类。第一类是模型结构参数包括d_model、n_heads、e_layers、d_ff。这些参数决定模型容量。注意n_heads必须能整除d_model否则多注意力头没法均分维度。我一般把d_model设在64到128之间n_heads从4和8里面选e_layers控制在2到4d_ff按d_model的2到4倍去搜。第二类是patch化参数包括patch_len和stride。这是PatchTST特有的影响最大。patch_len太小局部语义不足太大patch数量太少序列信息压缩过狠。我通常把patch_len放在8到24之间stride放在4到12之间。第三类是训练参数包括学习率、dropout、batch_size、weight_decay。学习率我习惯用对数均匀分布采样范围1e-4到1e-2dropout在0.05到0.3之间batch_size在32到128之间weight_decay在1e-6到1e-3之间。采集函数的作用是从代理模型推荐下一组参数。常用的有期望改进EI和置信上界UCB。EI会权衡“这个区域可能达到多好”和“这个区域的不确定性有多大”既不会只盯着已知最优区域导致局部收敛也不会完全到处乱试。我用的优化库是Optuna它默认的TPE采样器使用的就是类似的思想把历史试验分成表现好和表现差的两组通过概率密度比值来指导下一步采样。3.3 基于Optuna的实现思路Optuna是目前最顺手的贝叶斯优化框架和PyTorch集成很方便。整个流程分三步定义objective函数、创建study、运行优化。objective函数内部就是一次完整的模型训练和评估流程最终返回验证集上的MAE或者RMSE作为需要最小化的目标值。import optuna from optuna.pruners import MedianPruner from optuna.samplers import TPESampler def objective(trial): params { d_model: trial.suggest_categorical(d_model, [64, 96, 128]), n_heads: trial.suggest_categorical(n_heads, [4, 8]), e_layers: trial.suggest_int(e_layers, 2, 4), patch_len: trial.suggest_int(patch_len, 8, 24, step4), stride: trial.suggest_int(stride, 4, 12, step4), dropout: trial.suggest_float(dropout, 0.05, 0.3), lr: trial.suggest_float(lr, 1e-4, 1e-2, logTrue), batch_size: trial.suggest_categorical(batch_size, [32, 64, 128]), weight_decay: trial.suggest_float(weight_decay, 1e-6, 1e-3, logTrue), } # 数据加载、模型构建、训练循环在这里完成 # val_loss train_and_evaluate(params) # 这个函数内部包含完整的训练和验证逻辑 return val_loss sampler TPESampler(seed42, n_startup_trials10) pruner MedianPruner(n_startup_trials5, n_warmup_steps10) study optuna.create_study(directionminimize, samplersampler, prunerpruner) study.optimize(objective, n_trials60, timeout7200)这里有两个细节值得说。第一n_startup_trials是指在真正用TPE决策之前先做多少次完全随机采样目的是给代理模型提供初始数据。设太小比如设成3代理模型容易在刚开始就锁死到错误的区域。我实际用下来设10到15比较稳。第二pruner的MedianPruner指的是如果某个trial在训练中途的中间结果比历史同期中位数差就直接终止它把计算资源让给更有潜力的试验。这类剪枝机制在调参任务里非常省时间尤其是在搜索空间很大的时候。4. 数据处理综合能源负荷预测的建模基础模型和调参框架都定了数据这边反而是最容易出问题但最容易被忽视的部分。综合能源系统的原始数据通常来自SCADA系统、气象站和日历表数据结构五花八门采样间隔可能不齐异常值和缺失值也很多。处理不好后面模型再先进也是白搭。4.1 多变量特征集合与来源我梳理这个项目的数据时把特征分成了三类。第一类是预测目标也就是电负荷、热负荷、冷负荷这三条主曲线采样间隔是15分钟一个点一天96个点。第二类是外部气象特征包括干球温度、相对湿度、水平面总辐射、风速等这些特征对空调负荷和供热负荷影响很大尤其是温度和辐射与冷负荷的相关系数相当高。第三类是日历特征比如是否工作日、是否节假日、一天中的第几个采样点。日历特征看起来简单但对模型帮助很大。综合能源负荷在工作日和周末的形态差异非常明显如果模型不确定“今天是星期几”它很难区分同一时刻两种不同的合理模式。还有一类容易被忽略的特征是滞后变量。综合能源系统里的热负荷有很强的热惯性今天的负荷不仅取决于今天的气象还和过去几个小时的热量积累有关。所以我把目标变量的前24个点也作为额外特征窗口输入让模型有机会捕捉这种惯性。不过要注意加了滞后变量后输入通道数会明显增加特征之间的多重共线性也会变强需要靠模型的正则化去吸收。4.2 归一化方案的取舍归一化这个环节我一开始吃过亏。直接用StandardScaler对整个特征矩阵做标准化表面上看没错但会把不同负荷通道的量纲差异压缩到同一范围反而丢掉了不同能源品种之间的相对规模信息。后来我改成每个通道单独做归一化也就是对电负荷、热负荷、冷负荷、温度、湿度这些列分别fit一个scaler。这样做的另一个重要原因是预测结果需要反变换回原始量纲才能计算误差指标。如果多个通道混合缩放反变换的时候矩阵操作容易出错分开处理后会清晰很多。预测目标通道建议使用MinMaxScaler让它严格落在0到1之间这样和模型输出层的激活范围匹配更好训练初期收敛更稳定。外部气象特征用StandardScaler就行不需要强制限定到某个区间。反变换的公式很简单但要注意在测试集上必须用训练集fit好的scaler不能在测试集上重新fit否则会造成信息泄漏评估结果虚高。4.3 滑动窗口与多步预测构造滑动窗口是时间序列预测里最基础的操作但细节里藏着不少坑。假设我们用前96个点预测后24个点那训练样本就是在一个完整的时间序列上以一定step滑动截取。比如step设24每天可以截出约4组样本0到95预测96到11924到119预测120到143依此类推。这里有一个很关键的选择输入窗口和预测窗口之间是否要有gap。有些场景里我们可以在输入末尾和预测开始之间留一段间隔比如输入96个点跳过6个点再预测未来24个点。这个gap的存在让模型不能只依赖最近几个点的惯性外推必须真正学到模式泛化能力更好些。但如果gap太大模型会丢掉近期的有效信息误差反而不降。我在实测里用gap等于1到2个采样点作为折中效果比完全无缝预测略好。窗口构造完成后数据要整理成模型需要的形状。PatchTST的多变量输入通常是 (batch, seq_len, num_channels) 这样的三维张量。注意通道维放在最后和图像任务的 (batch, channels, height, width) 不一样刚开始容易搞混。如果使用PyTorch建议在DataSet里直接返回这个形状的tensor避免在训练循环里频繁做permute。5. Python代码实现与核心逻辑解读这一节我把项目代码的主干逻辑拆出来讲。不贴全部源码但关键部分都会给到并且解释每一段在做什么、为什么要这么做。5.1 数据加载与窗口生成数据加载部分我习惯自己写DataSet类而不是直接用现成的TSDataset库因为综合能源数据通常要融合好几个csv自定义更灵活。class EnergyDataset(torch.utils.data.Dataset): def __init__(self, df, target_cols, feature_cols, input_len96, pred_len24, step24): self.df df.reset_index(dropTrue) self.target_cols target_cols self.feature_cols feature_cols self.input_len input_len self.pred_len pred_len self.step step self.inputs, self.targets self._build_samples() def _build_samples(self): data self.df[self.target_cols self.feature_cols].values.astype(float32) total len(data) max_start total - self.input_len - self.pred_len samples_in [] samples_out [] for start in range(0, max_start, self.step): end_in start self.input_len end_out end_in self.pred_len x data[start:end_in] y data[end_in:end_out, :len(self.target_cols)] samples_in.append(x) samples_out.append(y) return ( torch.from_numpy(np.stack(samples_in)), torch.from_numpy(np.stack(samples_out)) ) def __len__(self): return len(self.inputs) def __getitem__(self, idx): return self.inputs[idx], self.targets[idx]这里有几个设计点。第一切窗口时只保留完整覆盖输入和预测长度的样本末尾不足的部分直接丢掉避免padding带来的边界噪声。第二step控制窗口滑动步长数据量不够的时候step小一点可以增加样本数但要注意相邻样本高度重叠会导致训练集和验证集之间的数据泄漏风险我的做法是把时间靠前的80%作为训练集时间靠后的20%作为验证集不做随机划分。时间序列数据不能用随机划分这一点非常重要随机划分会让验证集提前看到训练集未来的信息。5.2 PatchTST模型实现模型主体我基于官方思路重新实现便于修改和加自己的预测头。核心分两块patch嵌入和Transformer编码器。import torch import torch.nn as nn class PatchEmbedding(nn.Module): def __init__(self, patch_len, stride, d_model, dropout): super().__init__() self.patch_len patch_len self.stride stride self.padding stride if patch_len stride else 0 self.linear nn.Linear(patch_len, d_model) self.pos_embed nn.Parameter(torch.randn(1, 100, d_model) * 0.02) self.dropout nn.Dropout(dropout) def forward(self, x): # x: (batch, seq_len, num_channels) batch, seq_len, num_channels x.shape x x.permute(0, 2, 1) # (batch, num_channels, seq_len) x nn.functional.unfold( x.unsqueeze(-1), kernel_size(self.patch_len, 1), dilation1, padding(self.padding, 0), stride(self.stride, 1) ) # 整理形状 x x.permute(0, 2, 1).reshape(batch * num_channels, -1, self.patch_len) x self.linear(x) # (batch*num_channels, num_patches, d_model) x x self.pos_embed[:, :x.size(1), :] x self.dropout(x) x x.reshape(batch, num_channels, -1, self.linear.out_features) return x这段代码里最容易出问题的是unfold的padding设置。设置padding为stride有一个作用保证序列末尾也能形成一个patch让尾部的信息不会直接被丢弃。虽然末位patch是部分填充的但线性层在训练过程中可以学到忽略无效位置这比直接截断要好。Transformer编码器部分直接用PyTorch的TransformerEncoderLayer堆叠即可。注意这里要让num_channels个通道的token进入同一个Transformer因此把通道维展开到序列维。也就是说注意力可以跨通道和跨patch同时发生这也是为什么模型在多变量场景下依然能捕捉到通道间交互。预测头部分我需要把Transformer输出的特征重新整理成每个通道独立的多步输出。因为输出是直接多步预测所以这里就是一个Linear层把特征映射到pred_len维度。我在实际实现时每个通道的输出投影是独立的Linear层使用nn.ModuleList包裹避免所有通道共享同一套投影权重。5.3 贝叶斯优化主流程集成贝叶斯优化的objective函数内部最关键的是要让每次试验在相同条件下公平竞争。我在试验里固定了随机种子包括PyTorch、NumPy和Python内置random否则有的试验跑得好纯粹是运气好贝叶斯优化会被假信号带偏。另一个要注意的是验证集的选择。我建议固定一个时间窗口作为验证集比如最近20%的时间段。不要在每次试验里重新划分验证集否则不同试验之间的比较基准不一致。如果数据量够大也可以抽一个固定的时间长段做验证但同样不能随机抽取。objective函数的执行流程通常是用传入的超参构建模型初始化数据加载器进入训练循环每个epoch结束后在固定验证集上计算MAE或RMSE记录历史最优模型返回验证集最优误差。整个过程相当于一次小型训练任务。为了降低单次试验耗时可以限制epoch数到50左右配合早停策略。早停的patience建议设5到10个epoch太小容易在曲线还没收敛时就提前停止太大又浪费时间。5.4 训练与评估闭环训练循环本身没有太多特别之处但有几个细节会影响最终效果。首先是优化器的选择我用的是AdamW而不是普通Adam。AdamW把权重衰减从梯度更新里去耦对Transformer这类大模型的正则化效果更可控不容易出现普通Adam里weight_decay和自适应学习率相互干扰的问题。学习率调度方面我推荐先用one cycle或余弦退火。PatchTST训练早期学习率太大会导致loss爆炸太小会导致收敛太慢one cycle策略在前几个epoch快速提升学习率探索然后平滑下降收敛实测比固定学习率效果稳定。损失函数用的是MSE因为MSE对大误差的惩罚更强烈会让模型更关注尖峰负荷的拟合。如果后续觉得预测结果太平均、尖峰不够尖可以换成MAE或者Huber损失做对比甚至组合起来用。评估指标方面除了RMSE我同时会算MAE、MAPE和R2因为RMSE对异常值敏感单看它容易被个别点的极端误差误导。MAPE对接近零的负荷值会失真所以计算时需要对真实值设置一个下限阈值避免除以接近0的数。6. 训练过程中踩过的坑与排查心得这个项目前前后后跑了很久踩的坑不少专门整理一节出来按问题类型分类写清楚。6.1 loss不降或突然变成NaNloss不降是最常见的问题。我的排查顺序是先看数据里有没有NaN或inf这一步可以通过一行代码检查然后看归一化是否完成如果原始值在几百上千的量纲直接送进模型Transformer的注意力softmax很容易溢出再看学习率PatchTST对学习率比LSTM更敏感如果初始学习率超过1e-2训练大概率直接飞掉建议先固定到1e-3跑一遍基线再让贝叶斯优化去搜索。loss突然变成NaN大概率有两个原因。一个是梯度爆炸解决办法是给模型加梯度裁剪max_norm设1.0比较稳妥。另一个原因是学习率调度策略里出现了过大的尖峰比如one cycle策略的峰值学习率设太高可以在实现时限制峰值不超过最大学习率的10倍。还有一个容易被忽略的原因输入数据里某条通道的标准差为0。比如某个传感器故障连续几天的值恒等于一个常数标准化之后变成NaN模型训练自然就崩了。6.2 Patch尺寸与序列长度约束Patch化和序列长度之间的约束关系我在调整参数时经常被绕进去。给定序列长度Lpatch长度Pstride Spatch数量N可以估算为 (L - P) / S 1如果加上末尾填充大约是 (L - P) / S 2。这个N不能太小否则输入到Transformer的token数量太少注意力机制的优势发挥不出来。经验上N至少应该在5到8个以上。如果序列长度是96我一般建议patch_len从8到16之间选stride从4到8之间选。patch数量大约在11到23个之间这个量级对Transformer来说很舒适。如果序列长度更长比如168或336可以把patch_len适当调大到12到24。还有一个需要注意的地方是patch_len必须能被输入序列长度大致整除否则末尾padding过多有效信息比例下降。如果发现未填充patch数量太少优先调大stride而不是调大patch_len这样能在保持patch粒度的情况下尽量维持语义完整度。6.3 贝叶斯优化的收敛与过拟合问题贝叶斯优化跑着跑着会出现两种让人困惑的现象。第一种是搜索后期目标值不再下降反而在某个区间震荡。这通常是搜索空间里某些参数组合方差太大导致的比如batch_size从32跳到128模型表现波动明显代理模型很难找到稳定的优化方向。解决办法是把参数限制到更窄的合理范围或者把离散参数的候选值减少到2到3个。第二种现象是贝叶斯优化找到的参数在训练集上效果很好但验证集效果变差典型的过拟合。这里要区分是模型过拟合还是调参过拟合。模型过拟合表现为训练loss远低于验证loss需要加大dropout或weight_decay或者减小d_model。调参过拟合则是试验次数过多代理模型在验证集上反复测试间接把验证集信息泄露给了搜索过程。解决办法是尽量保证验证集只有一个固定窗口不要频繁更换同时trials数控制在50到80次以内超过之后增益会明显递减。我还发现一个隐藏的坑早停的patience在贝叶斯优化里会影响公平性。某个trial训练特别久模型有机会充分收敛另一个trial因为patience设置不同提前结束了。这时候比较两者的最终loss就不公平。我的做法是每次试验固定使用相同的早停配置并且统一epoch上限。7. 跑通之后的效率观察与扩展思路模型跑通之后我对整体性能和扩展方向做了整理这里一并说说。7.1 整体效果与时间开销最终固定下来的配置大致是d_model128n_heads8e_layers3patch_len16stride8dropout0.1学习率约2e-3batch_size64。在这个配置下电负荷、热负荷、冷负荷三通道24步预测的RMSE对比原始LSTM基线大约下降了15%到20%。其中热负荷提升最明显因为热惯性带来的长程依赖恰好是PatchTST注意力机制的强项。冷负荷提升相对小因为冷负荷受瞬时辐射影响较大而气象预报本身的误差会限制预测上限。训练时间方面序列长度96、patch数量13左右、三通道展开后总共39个token一个epoch在中等规模数据集上只需要几秒50个epoch加早停大概在3到5分钟完成一次试验。60次贝叶斯优化试验算上剪枝节省的时间总耗时在2到3个小时左右。这个成本对应用来说可以接受比起手动调参一遍遍试要高效得多。7.2 还可以怎么往下走如果想把这套方案继续深化有几个方向我个人觉得很有价值。第一个是把模型输出从确定性预测改成概率预测具体做法是在预测头加一个分布层输出均值和方差损失函数换成负对数似然。这样得到的预测不仅有数值还有置信区间对调度决策来说参考意义更大。第二个方向是引入更细的外生变量比如电价、设备启停状态、天气预报的不确定性。综合能源系统调度往往和现货市场电价强相关把电价作为输入通道加入模型可以进一步挖掘负荷对价格信号的响应。第三是做多任务学习的变体不只预测负荷还同时预测光伏出力和储能状态。因为这些变量的变化规律和负荷有耦合联合建模可能带来额外的泛化提升。最后还想提一个工程上的小技巧模型训练完保存 checkpoint 时记得把scaler也一起保存。部署上线时新数据的归一化必须用训练时保存的scaler否则线上输入分布和训练分布不一致预测效果会莫名其妙地变差。我在项目里因为这个问题上线后翻过一次车排查了半天才发现是新写的预处理脚本没有复用原有scaler造成的。踩过那次坑之后我把scaler纳入模型artifacts统一管理之后的迭代就再没出过类似的问题。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表