
风速预测这件事做过风电功率预测或者新能源调度的人都有体会数值天气预报NWP给的大趋势通常没问题但一旦要往前推未来6到12小时的多步风速序列误差就开始放飞自我。风速序列的脾气很古怪——低风速时段静稳得像一潭死水强风过程一到又陡升陡降、阵风频发单峰分布的假设在它面前根本不成立。最近我在做风电场后评估项目需要在小时级尺度上持续输出风速序列单模型怎么调都差口气最后把思路转到了混合模型上用高斯混合模型GMM给风况分型再组合几种异构的回归模型做多步预测。折腾完一圈回头看这条路线的每一步都有取舍也踩了不少有意思的坑值得好好复盘一篇。1. 风况的脾气为什么这么难拿捏单模型在多峰数据上的局限1.1 风速序列的“多峰”本质以及它对全局模型做了什么风速的统计分布通常不是高斯分布而是更接近Weibull分布——低风速段密度极高中段稀疏强风段又会拖出一条尾巴。大多数预测模型是在全局数据上做拟合损失函数天然倾向于照顾样本量最大的区域也就是低风速静稳段。结果就是模型在小风天看起来特别准一到强风过境就大幅低估而强风恰恰是功率输出最大、对调度影响最严重的时段。时间维度上更麻烦。同一个风电场夏季午后热对流带来的阵风和冬季冷锋过境的风速爬升统计特性完全不同前者高频振荡强后者趋势性明显。用一个全局模型去拟合所有这些状态本质上是让模型在几个不同的“子问题”上取折中。折中意味着没有一个状态拟合得特别好。1.2 “混合模型”这次到底指什么两层含义别搞混标题里的“混合模型”其实是两层东西叠在一起很多文章只讲了一层。第一层是数据层面的混合建模。用高斯混合模型对历史风速数据做无监督聚类把样本划分成若干风况状态比如静稳型、平稳过渡型、强风阵风型。注意重点这里不是简单地给样本贴个离散标签而是让GMM输出每个样本属于每个状态的概率。第二层是模型层面的混合集成。选几个异构的基学习器比如岭回归、LightGBM、轻量级MLP各自训练后再融合输出。这一层解决的是“单一算法在某类风况下短板明显”的问题。这两层一个管“识别当前是什么状态”一个管“在这个状态下怎么预测”组合起来才是完整的多步风速预测混合方案。如果只把GMM当成一个特征工程步骤就完事了忽略了模型融合这一半效果会差不少。1.3 为什么多步预测对“状态区分”的要求更苛刻单步预测时输入里的最近几个真实观测值已经携带大量状态信息模型就算不显式分型也能从观测值里“猜”个大概。但多步预测是另一回事第3步以后输入里混入了模型自己生成的预测值状态信息被逐步污染。这时候一个显式的状态概率输出就变得重要——它相当于多步递归过程中的一条“先验线索”告诉后面的预测步骤“你大概率还在强风状态别往静稳方向漂”。我最初做12步全递归预测的时候第6步以后预测曲线明显向均值方向收缩这就是典型的状态漂移模型在不断自我修正的过程中把极端状态逐渐磨平了。后来引入GMM分型信息这个收缩现象明显改善虽然不能根除但误差增长确实慢下来半拍。2. 多步预测的两条技术路线递归派与直接派2.1 递归预测误差雪球是怎么滚起来的递归预测的核心思路是把单步模型当作迭代器。用观测到的风速序列预测出t1再把这个预测值作为输入去预测t2一路迭代到目标步长。实现极简任何单步模型都能直接扛。致命问题在于误差传播。假如单步模型的RMSE是0.5 m/s第2步的输入里就混入了这个噪声误差方差会逐步放大。更隐蔽的问题是每一步的预测值都会向训练集的均值方向收缩一点。因为模型是在全局分布上拟合的极端输入经过模型处理后输出往往被“拉”回常见区间。递归次数越多预测序列越平——这就是为什么递归预测第6步以后曲线明显变“秃”。误差雪球还有一个特点它会在特定气象条件下滚得特别快。阵风段误差本来就大第一轮预测出来偏高或偏低第二轮输入就被带偏了后面的偏差就会被放大成系统性偏移而不是随机噪声。2.2 直接预测代价是样本利用率和模型数量直接策略的思路是为每个目标步长单独训练一个模型或者用一个多输出头的模型一次输出所有步长。第2步模型只吃真实观测特征不依赖前一步的预测值因此完全避开了误差传播。代价也很直接。第一个代价是样本利用率下降训练第12步模型的时候能有效使用的历史样本天然就比第1步模型少——因为你需要更长的前置序列才能构造同样数量的样本。第二个代价是模型数量12步预测就意味着12个模型训练和维护成本翻倍。实际里很常用的折中方案是多输出头multi-head模型共享底层的时序特征提取器输出层分出12个分支每个分支负责一个步长。这样既避免了误差传播又控制了模型数量。2.3 我的落地方案前3步递归后面走直接多输出实测下来我最终的方案是组合拳前1到3步用递归模型后面4到12步用直接多输出模型。这个拐点不是拍脑袋定的是我在验证集上逐项对比后确定的误差膨胀的拐点大概出现在第3到第4步之间这是普遍规律但具体位置每个站点可能有差异建议读者拿着自己的数据扫一遍。背后的逻辑很简单前几步递归模型能充分利用最近的观测趋势误差膨胀还不严重从第4步开始误差雪球的代价已经超过直接模型样本利用率的代价此时切换最划算。维度递归预测直接预测混合方案误差传播有逐步放大无前几步轻微4步后无样本利用率高低每步单独模型更明显中模型数量1个每步1个或多头约2个短步长优势强一般强长步长稳定性差好好直接预测还有个隐藏好处它和GMM分型特征的配合更顺。直接模型输入的是真实观测GMM后验概率不会因递归误差被污染这条后续还会细讲。3. 高斯混合模型怎么给风速“分型”参数选择与特征用法3.1 为什么拒绝KMeans坚持用GMMKMeans做风速聚类也能分但它做的是硬聚类样本非此即彼。问题在于风况状态之间不是一刀切的。一段风速从5 m/s爬向12 m/s的过程既有静稳状态的影子又有强风状态的苗头KMeans会武断地贴上某一个标签。GMM输出的是每个样本属于每个簇的后验概率上一秒风速还在爬升模型给过渡簇60%的概率同时给强风簇40%的概率——这种软信息对下游的预测模型来说珍贵得多。GMM的另一个天然优势是它的密度估计能力。风速历史数据用单个高斯拟合会很差但混合几个高斯之后能勾勒出低风速高密度、强风重尾的分布形状。在大规模风电区域聚类出来的簇往往直接对应天气学意义上的“风况模态”具有很强的可解释性。3.2 BIC选K、协方差约束和初始化代码与参数GMM落地时最常被问到的参数有三个分量数K、协方差结构和正则化强度。分量数K的选择我用的是BIC贝叶斯信息准则同时叠加一个业务约束每个簇的样本量不得低于总样本的5%否则该K值直接判废。纯BIC容易在数据密集区过度细分加上业务约束能避免分出一堆没有物理意义的“微簇”。协方差结构我最终选了diagonal而不是full。full的拟合能力更强但风速预测的特征维度不高、相关性结构相对稳定full的额外参数反而容易过拟合特别是在样本量有限的站点diagonal计算稳定实测效果差距不大。初始化要用n_init10减少EM算法陷入局部最优的概率。reg_covar给到1e-3防止某个簇的协方差矩阵奇异。from sklearn.mixture import GaussianMixture import numpy as np # 特征可以选择滞后风速和滚动波动率核心是能刻画风况状态 X train[[ws_lag1, ws_lag2, ws_lag3, ws_rolling_std_1h]].values bics [] models {} for k in range(2, 9): gmm GaussianMixture( n_componentsk, covariance_typediag, reg_covar1e-3, n_init10, random_state42 ) gmm.fit(X) bics.append(gmm.bic(X)) models[k] gmm best_k np.argmin(bics) 2 print(best_k:, best_k)选好K之后还有一个自查步骤打印每个簇的权重和均值看是否符合物理直觉。正常的风速分型一般会得到三个有清晰含义的簇低风速静稳型均值约2 m/s、平稳过渡型均值约6 m/s、强风阵风型均值约10 m/s以上。如果某个簇的均值落在两个簇之间或者权重集中在某一个簇说明K可能选大了。3.3 分型之后的正确用法后验概率比硬标签更值钱很多人拿到聚类结果后习惯把每样本的簇编号做成one-hot特征塞进模型。我试过两种做法实测下来直接把GMM的K维后验概率作为连续特征效果明显优于离散的簇编号特征。原因在于后验概率保留了状态的“过渡感”。一步预测时风速在5 m/s后验概率可能是静稳簇0.3、过渡簇0.6、强风簇0.1离散的one-hot编码则强行把中间状态归到过渡簇丢失了“正在离开静稳状态”这个信息。预测模型拿到连续概率能自己学会在概率分布之间做插值。还可以进一步构造簇内上下文特征比如计算每个簇的风速均值和波动率统计量作为额外的全局特征。但注意这些统计量必须只用训练集计算测试集只能做transform否则会引入未来信息。4. 混合模型整体落地特征体系、基学习器选型与融合策略4.1 特征体系搭建时序滞后、气象外生变量与GMM输出特征体系是我踩坑最多的地方这里给出一份可直接复用的清单时序滞后特征ws_lag1到ws_lag6前6小时的风速观测。这是预测的基础盘。气象外生变量NWP提供的未来小时风速预报、风向的sin/cos编码、气温、气压。风向必须拆成sin和cos两列否则0度和360度会被当成差360度模型没法正确处理。派生统计特征过去1小时和3小时的滚动均值、滚动标准差。标准差刻画的是波动强度这在强风阵风型状态下特别有区分度。GMM输出特征K维后验概率向量加上最大概率对应的簇编号离散特征备用。所有数值特征在进入模型前都要做标准化但scaler只能在训练集上fit这个细节会在第五节展开因为它是导致验证分数虚高的头号杀手。4.2 基学习器选型的思路稳定垫底非线性主攻时序感知选基学习器不是挑一个“最好的”而是选误差模式差异足够大的。我最终用了三个岭回归作为垫底模型。它在特征线性组合假设下表现稳定预测值偏差小方差低是很好的参照物。虽然非线性拟合能力弱但它的存在保证了融合结果的下限。LightGBM作为非线性主攻。它对特征交互的拟合能力强对异常值鲁棒几乎不用做特征归一化但既然有其他模型统一标准化也无妨。在风速强风段和非线性爬升段它的表现通常是最好的。轻量级MLP作为时序感知层。我用的是一个两隐藏层的MLP输入窗口取了12小时滞后特征专门去捕捉持续加速或持续减速的趋势模式。它比LightGBM更容易过拟合所以必须加早停隐藏层宽度控制在32以内预防在少量样本上记硬编码。基学习器优势劣势适合的风况岭回归稳定、方差低非线性不足静稳型LightGBM强非线性、稳健容易过冲强风阵风型MLP时序模式捕捉好易过拟合过渡型三个模型在强风段的偏差方向不同岭回归偏保守LightGBM容易跟着特征过冲MLP有时能捕捉到持续的加速趋势。恰恰是这个差异让集成有了价值——融合之后单一模型的“冒进”和“保守”会相互抵消。4.3 融合输出加权平均与Stacking融合我一开始用的是验证集上搜索权重GridSearch三个权重组合效果中规中矩。后来换成Stacking收益更稳定把三个基学习器在验证期的预测结果作为特征训练一个岭回归作为元学习器让它自己学习每个模型在每个情形下的信任度。from sklearn.linear_model import Ridge import numpy as np # 基学习器在验证集上的预测结果 meta_X np.column_stack([pred_ridge_val, pred_lgbm_val, pred_mlp_val]) meta_y y_val meta_model Ridge(alpha0.1) meta_model.fit(meta_X, meta_y) # 测试期 test_ridge ridge.predict(X_test) test_lgbm lgbm.predict(X_test) test_mlp mlp.predict(X_test) final_pred meta_model.predict( np.column_stack([test_ridge, test_lgbm, test_mlp]) )这里有个关键细节Stacking的元特征必须在交叉验证下生成。如果用训练期的预测结果当元特征元学习器会学出一套“训练集专用”的信任度测试期直接失效。这一点和归一化泄漏一样是集成学习最容易踩的坑。5. 评估多步风速预测的指标与实验陷阱5.1 别只看RMSEPBIAS、分档评价、逐步退化曲线多步预测评估最常规的指标是RMSE、MAE和R²。但风速预测里有一个特别容易被忽略的指标平均偏差百分比或者标准化后的PBIAS。RMSE很好看但如果系统性地低估0.2 m/s在功率预测场景里可能是灾难——功率曲线是非线性S型低估在额定风速附近会造成巨大的功率误差。强风段误差也必须单独看。我把风速划分成三档小于3 m/s静稳、3到8 m/s过渡、大于8 m/s强风分别计算RMSE。混合模型单独看全局RMSE的提升可能只有5%到8%但拆开看之后强风档的RMSE经常能降15%以上这才是它真正的价值点。逐步退化曲线是好东西。把1到12步的RMSE全画出来你能一眼看清两件事递归预测在第几步开始崩直接预测从第几步开始反超。我实测的结果是递归在1到3步占优第4步被直接反超这个曲线帮我定下了混合策略中的拐点。5.2 时间序列交叉验证随机K折在这个场景里是错的时间序列数据用随机K折是经典事故现场。随机打乱之后训练集里混着测试期的未来样本模型等于提前看了答案验证分数自然漂亮滚动上线就现原形。这里必须用TimeSeriesSplit或者滚动窗口验证。归一化泄漏是最隐蔽的一环。我的第一版代码里用了StandardScaler对全序列fit交叉验证分数非常好看结果在滚动测试时崩了。原因在于全序列的均值和标准差包含了测试时段的信息scaler实际上让测试集的信息“见光”了。from sklearn.model_selection import TimeSeriesSplit from sklearn.preprocessing import StandardScaler tscv TimeSeriesSplit(n_splits5) for train_idx, test_idx in tscv.split(X): scaler StandardScaler() scaler.fit(X[train_idx]) X_train scaler.transform(X[train_idx]) X_test scaler.transform(X[test_idx]) # 训练模型、评估……NWP特征同理——它的历史统计量也只能从训练期算测试期只能用训练期的转换参数做transform。5.3 中心化的baseline比你想的重要persistence模型风速预测界有一个难以击败的简单基线持久性预测persistence直接假设未来h小时的风速等于当前观测风速。对1到3小时的短时预测这个基线强得离谱很多复杂模型在它面前都抬不起头。大风口的真相是短时风速序列自相关性极高过去一小时的风速基本决定了下一小时的风速。凡是做风速预测的文章不跟persistence对比基本等于耍流氓。我自己的实测结果是混合模型在1到3步跟persistence打平从第4步开始稳定跑赢到第12步时优势大约有20%到25%的RMSE下降。这也说明混合模型的价值主要在中长期多步而不是在超短期。6. 实测翻车记录三个最典型的坑与修复6.1 坑一GMM空簇问题以及为什么K不是越多越好现象很典型K设成6之后训练日志里出现一个后验概率长期接近0的“幽灵簇”预测时某些样本会被赋予极低概率。根因在于EM算法在样本稀疏区域收敛异常某个高斯成分的权重趋近于0、方差却特别大形式上是个合法簇实际上没有任何判别能力。低风速高密度的样本最容易被强行细分从而导致上述状况。修复方案有三步。第一K不要贪大风速数据三到四个簇足够了。第二reg_covar加上1e-3防止协方差矩阵奇异。第三每次聚类后检查component weights_低于阈值0.05的簇直接视为无效重新训练。这个检查应当固定为常规步骤。6.2 坑二归一化泄漏导致验证分数虚高现象前面已经说过时序交叉验证分数漂亮滚动测试一塌糊涂。排查过程是这样的我先把交叉验证改成滚动窗口对比发现差异极大随后逐环节排查最后定位到scaler。用全序列fit过的scaler等于把测试集min/max和均值方差的信息都织进了特征里模型在验证时自然“开卷考试”。修复起来很简单但排查很费时间。所有标准化器只能用训练集fit。另一个类似的问题是特征工程的统计量比如滚动标准差如果窗口中间跨越了训练/测试切分点也要小心处理最稳妥的做法是在每个fold内部重新计算这些滚动特征。6.3 坑三递归误差把GMM分型带偏这个坑很隐蔽。多步递归时第8步以后预测序列漂到了另一个簇的分布范围GMM后验概率越来越混乱。举个例子实际是强风过程但递归预测的风速只有4 m/sGMM判断它更像静稳簇后面所有预测步骤都被带回了静稳模式整体风速被严重低估。这让我彻底理解了分型信息在多步场景下的脆弱性它的有效性依赖输入特征的保真度一旦输入被预测误差污染分型本身就失去了意义。修复方案就是我前面说的混合策略——前3步递归后面改用直接多输出。在直接输出的框架里GMM后验概率始终基于真实观测不会被递归误差带偏。如果一定要全递归可以考虑在每一步重新用观测值做teacher forcing训练期来缓解错位。故障现象根因修复GMM空簇某簇权重趋近0EM陷入局部最优、K过大K≤4、reg_covar1e-3、权重自查归一化泄漏验证好、滚动崩scaler用全序列fit只fit训练集fold内重建特征递归分型错乱长步误差偏低估预测值污染GMM输入4步后切直接输出7. 这条路线还能往哪走7.1 从点预测走向概率预测GMM天然是概率模型分型之后每个簇的高斯分布可以近似看作风速的条件分布。进一步可以针对每个预测时点输出分位数区间而不是一个期望值。风电功率预测的考核指标往往需要置信区间概率输出比点预测更有实用价值。把GMM的对数似然或者分位数回归头加进框架里改造路径是现成的。7.2 季节漂移与在线更新风电场的风况随季节变化夏季和冬季的簇结构差异明显。离线训练的GMM时间长了会失配。可以考虑周期性重新聚类或者用滑动窗口更新基学习器——每天用新进来的观测数据做增量训练保持模型的时效性。这个方向对长期上线的系统尤其重要。7.3 空间相关性多个风电场的风速序列存在地理相关性。如果混合模型扩展到多站点联合建模GMM分型的“状态”含义就更丰富了——不再是单点风况而是区域天气系统的移动过程。这个方向我还没有实测但初步构思是先把多站特征拼到一个向量空间里做分型再对各站点分别输出预测。理论上比单站模型更稳值得投入。最后分享一点个人体会混合模型这条路真正起作用的地方不在于把一堆模型堆在一起而在于它逼着你想清楚“数据分布结构”和“预测任务结构”。GMM帮你回答“当前处于什么状态”多步策略帮你回答“误差从哪来、在哪一步膨胀”基学习器融合帮你回答“不同算法的盲区如何互补”。这三个问题想通了哪怕后面换工具换框架思路都能直接迁移。