ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

光伏发电量短期预测:SARIMA与Prophet组合模型的工程实践

光伏发电量短期预测:SARIMA与Prophet组合模型的工程实践 1. 做这个预测项目前先把场景和难点想清楚做光伏发电量预测这个事我先说个结论单纯靠一个模型扛到底基本都会翻车。我在实际项目里试过只用ARIMA、只用Prophet、也试过深度学习那套LSTM最后稳定下来用的就是SARIMA和Prophet结合的方式。这个组合的定位很明确——短期预测也就是未来几小时到未来几天的发电量曲线。先说清楚这个项目的适用场景。如果你是做光伏电站的运维监控、电力交易申报、或者给微电网做日内调度那短期发电量预测就是刚需。比如第二天上午十点要上报次日的发电计划你总得有个能打的模型在背后撑着。如果是做中长期规划、电站选址评估那种按月按年的预测那这套方案就不太合适别硬套。光伏发电量预测之所以难核心在于它的输出受两个完全不同的因素控制一个是物理上确定性很强的部分比如太阳辐照度的日变化规律、季节变化规律这部分有非常明显的周期性和趋势性另一个是物理上随机性很强的部分比如云层遮挡、降雨、温度波动、空气质量变化这部分几乎无法精确建模。SARIMA的优势在于把第一部分的周期性、趋势性吃得很透Prophet的优势在于对趋势变化和节假日效应比较敏感而且对异常值和缺失值有天然的容忍度。两者结合等于一个负责抓住“规律”一个负责兜住“意外”。当然我得实话实说这套方案不是所有情况下都最优。如果项目数据量非常大、特征维度很高比如你有几十个气象站的高频数据要融合进来那梯度提升树或者深度模型可能会更好。但如果你的场景是数据量中等、预测周期在1到7天、手头主要就是历史发电量序列加上基础气象数据那SARIMA加Prophet的组合是目前性价比最高的选择之一调参工作量可控代码也不复杂最重要的是结果可解释。这项目适合谁来参考我觉得有三类人第一类是刚接触时序预测、想找一条完整技术路线的开发者第二类是在做能源相关项目的工程师需要快速落地一个预测模块第三类是学生做课题、写毕业论文时想用真实数据集跑出点成果的。这篇文章我会把从数据清洗到模型调参再到融合预测的全流程都拆开讲你跟着走一遍就能复现。2. 数据准备决定预测上限的第一道关卡2.1 数据粒度与时间跨度怎么定很多人一上来就急着调模型参数但最后发现怎么调都不准问题多半出在数据上。光伏发电量预测的数据粒度选择直接决定了后面所有工作的上限。我的经验是短期预测以15分钟或1小时为粒度最合适。为什么不用5分钟因为5分钟级别的数据噪声太大了逆变器本身的采样波动、局部云层快速移动造成的剧烈抖动都会让模型去拟合那些没有意义的细节反而把真正的趋势给淹没掉了。为什么不用1天粒度因为一天一条数据做短期预测等于用微信步数去预测一个人接下来三分钟的跑步速度信息密度完全不够。在实际操作里我倾向于用15分钟粒度做训练和预测。15分钟粒度在光伏领域是一个比较折中的选择太阳能辐照度的变化在这个时间尺度上能够体现出云层平滑效应又不会丢失日内峰值的形态特征。如果你手头原始数据是分钟级甚至秒级的处理方式也简单直接按15分钟重采样取均值就可以。时间跨度方面至少要有一年的历史数据这是硬底线。原因很简单光伏发电量的季节性至少包含两个维度一个是一天之内白天和夜晚的日内周期另一个是春夏秋冬的跨年周期。如果你只有三个月的数据SARIMA连年周期都辨识不出来Prophet也会把季节项拟合得歪七扭八。如果数据超过三年那可以直接做截断处理取近两年到三年的数据就足够更早的数据一方面电站设备可能经历过清洗或更换效率曲线已经变了另一方面计算开销也会白白增加。2.2 清洗与特征工程要点光伏数据最常见的脏数据有这么几类我挨个说- 夜间零值堆积。光伏电站夜晚发电量必然是0这些零值本身是正常的但对模型来说它会造成一种“零膨胀”的假象。处理方式有两种一是训练时段只取每天6点到20点之间的数据让模型专心学白天的光伏曲线二是保留全天数据但把0值作为正常值对待不额外处理。我建议用第一种因为SARIMA对序列的均值结构很敏感如果不分昼夜模型的季节项会在两个完全不同的状态之间来回拉扯。- 停机检修和通信故障造成的缺失段。这部分直接做线性插值往往是不准的因为光伏曲线的形状是两头低中间高的山峰形态不是直线。更好的做法是按“同时间点的相邻日期”来填充比如今天是上午10点到11点缺失就用昨天上午10点到11点的值做基础再乘一个当天整体辐照度水平的修正系数。这个系数可以简化为两天的最大出力比值。- 天气因素的引入。很多人在做SARIMA和Prophet时只用发电量本身这一个单变量这是可以用的但也浪费了最有价值的辅助信息。我建议至少引入两个外生变量水平面总辐照度GHI和环境温度。这两个变量对发电量的解释力最强。Prophet里可以直接通过add_regressor方法加入SARIMA则可以用arima的exogenous参数传入。不过要注意一点预测未来时外生变量也需要预测值所以实际操作中要么用数值天气预报数据要么用历史同期均值做回退方案。- 数值归一化。SARIMA和Prophet本身对量纲不敏感因为它们是线性模型的扩展不是梯度下降驱动的。但如果你后面要在这个基础上接其他模型做融合比如LSTM或XGBoost那归一化就很有必要了。我做项目时习惯统一在预处理阶段就把数据归一化到0到1之间这样后面换模型、融合、评估指标计算都不用回头改。清洗完的数据我最后会先画一张时序图确认形态再画一张月度热力图看季节规律是否清晰。这一步很便宜却非常必要数据形态不对后续全部白做。3. SARIMA模型把季节性和自回归吃透3.1 先搞清楚SARIMA(p,d,q)(P,D,Q)s的每一层含义SARIMA的全称是季节性自回归移动平均模型它本质上是把ARIMA对非平稳序列的处理能力加上一个针对固定周期周期的季节差分和季节自回归、季节移动平均项。很多人一看到(p,d,q)(P,D,Q)s这七个参数就头大我换一种方式讲。你把发电量序列想象成一条水流AR项(p)就是“看前几个时刻的水位来推测当前水位”MA项(q)就是“看前几个时刻的冲击误差来修正当前判断”差分阶数(d)就是“通过做几次减法让水流从有趋势变成平稳”。季节部分同理(P,D,Q)是对“每年的同一天”这一层格局做的同样操作s就是周期的长度。放在光伏场景里如果你用15分钟粒度数据日内有96个点那s96如果用小时粒度s24。这个s非常关键很多人在做光伏预测时把s设成7因为看到有星期周期性但那是把光伏发电量当成了“居民用电量”来建模光伏靠天吃饭没有周末不周末的区别唯一的周期就是昼夜和季节。当然有些地区的电价政策会对光伏并网产生一周内的规律性影响比如某个工业园区的限电措施在工作日和周末不同这种情况下s24之外可以再考虑一个周周期项但这是少数情况默认先做日内周期。3.2 定参流程与代码实现我实际项目里的定参流程是这样的第一步用ADF检验确认序列平稳性。如果p值大于0.05就对序列做一阶差分再检验这个确定d。光伏数据因为有极强的日内季节性直接看原始数据的话ADF结果通常会很不稳定我的建议是先做季节差分也就是每条数据减去前s条数据再做普通差分。第二步用ACF和PACF图来初定p、q、P、Q。ACF图在季节延迟处有明显截尾时说明MA类项起作用PACF在季节延迟处有截尾时说明AR类项起作用。这里我给个快速参考光伏发电量序列的ACF图通常会在s,2s,3s的位置呈现出拖尾衰减的形状这就是季节MA项的典型表现也意味着Q通常取1或者2就够了。第三步用AIC或BIC做网格搜索把候选参数组合跑一遍。我通常搜的范围是p和q在0到5之间P和Q在0到2之间d取0或1D取0或1。这个网格规模大概几百个组合SARIMA拟合一次从几十毫秒到几秒不等全部跑完也就几分钟完全可接受。第四步回测验证。拿最近14天的数据做滚动预测每天预测未来24小时算RMSE和MAE。这一步效果比任何一项检验指标都直观。下面是一段我从实际项目中抽出来的核心代码用的是statsmodels库import pandas as pd import numpy as np from statsmodels.tsa.statespace.sarimax import SARIMAX from statsmodels.graphics.tsaplots import plot_acf, plot_pacf from pmdarima import auto_arima from sklearn.metrics import mean_absolute_error, mean_squared_error # 数据预处理假设df已经清洗完毕索引为15分钟粒度DatetimeIndex # df[power] 是归一化后的发电量 train df.loc[:2024-06-30, power] test df.loc[2024-07-01:, power] # 先用auto_arima粗筛限定季节性周期为9615分钟粒度 auto_model auto_arima( train, seasonalTrue, m96, start_p0, max_p5, start_q0, max_q5, d0, max_d2, D0, max_D1, traceFalse, error_actionignore, suppress_warningsTrue, stepwiseTrue, n_fits50 ) print(auto_model.order, auto_model.seasonal_order) # 手动拟合精细模型 order auto_model.order seasonal_order auto_model.seasonal_order model SARIMAX( train, orderorder, seasonal_orderseasonal_order, enforce_stationarityFalse, enforce_invertibilityFalse ) result model.fit(dispFalse) print(result.summary())这里有一个我在项目里反复踩过的坑statsmodels的SARIMAX在enforce_stationarity和enforce_invertibility同时开启时很多参数组合会直接报错或者收敛失败。尤其当季节周期s比较大时模型的参数矩阵维度会比较高默认的约束条件过于严格。所以我在拟合前都会把这两项关掉拟合完再通过result.check_innovations去查看残差是否满足白噪声假设。再看预测部分# 滚动预测未来24小时96个点 forecast_steps 96 pred result.get_forecast(stepsforecast_steps) pred_mean pred.predicted_mean pred_ci pred.conf_int(alpha0.05) # 反归一化 pred_mean_original pred_mean * (power_max - power_min) power_minSARIMA的输出是带置信区间的这个很有用。实际业务中电力交易申报需要的是一个确定值但并网调度希望你给出区间SARIMA天然支持这个。我在给电站做方案时一般把置信区间的上下界直接映射成“乐观场景”和“悲观场景”这比单一预测值有说服力得多。4. Prophet模型让趋势和节假日自己说话4.1 Prophet的核心机制与适用边界Prophet是Meta开源的时间序列预测工具它在工业界的口碑是“开箱即用参数少出图漂亮”。但说句公道话Prophet不是万能的它更适合那些有强趋势、有周期性并且有少量异常值的业务型时间序列比如电商销量、网站流量。光伏发电量和这类序列正好有几分类似但也有本质不同。Prophet的原理是把你看到的时间序列拆成四个部分趋势项、季节项、节假日项、误差项。趋势项默认是分段线性趋势也可以切换成logistic增长趋势季节项用傅里叶级数来拟合所以你实际上是在用一组正弦和余弦的组合去逼近季节形态节假日项是一个稀疏脉冲矩阵的回归。在光伏场景里Prophet能做因为季节项能够刻画“白天高、夜晚低”的形态傅里叶级数的阶数越高对曲线细节的拟合能力越强。但这里有个关键点Prophet的默认季节项是年周期和周周期没有日内周期。如果直接用日粒度数据还好但如果你用15分钟粒度数据就需要自己配置日内周期。我的建议是Prophet在光伏预测里最适合的是“日粒度或小时粒度的发电量总量预测”特别是用来做未来3到7天的日发电量趋势预判。而SARIMA更适合做日内逐点的精细曲线拟合。两者在这个层面的分工刚好互补。让我给一个直观对比维度SARIMAProphet日内周期精细拟合强s参数直接控制弱需要手动加周期项趋势突变适应中差分结构对突变滞后强变点检测自动识别缺失值处理差需要提前清洗插值好自带容错机制外生变量支持支持但需要额外处理支持add_regressor方便可解释性中参数含义专业强plot组件直观计算速度慢网格搜索耗时长快参数少4.2 参数配置与代码实现我用Prophet时最关注的是三组参数growth、seasonality、changepoint。growth我用linear。光伏发电量在没有新增装机容量变化时不呈现饱和增长形态所以logistic不太合适。如果你的电站中间有扩建发电量序列会有一个突越这时你需要手动传入capacity的上下限用logistic也能处理但复杂度上来了我这里不做展开。seasonality方面我搭配了三个周期项。第一个是日内周期周期长度为1天傅里叶阶数设为6到10之间阶数太低拟合不了光伏的尖峰形态太高会过拟合到云层的细节抖动。第二个是年周期傅里叶阶数设为10用来捕捉不同季节日照时长的差异。第三个是周周期我默认不开除非实际数据显示出明显的周期性并网限制。changepoint_prior_scale这个参数值得单独说一下。它控制的趋势变点的灵敏度默认是0.05。光伏电站的发电量趋势在一年内相对稳定不像电商大促前后那样剧烈波动所以我一般调大到0.1到0.2之间让模型能捕捉到因为设备更换、组件衰减导致的长周期趋势漂移。相反如果调得太小比如0.01以下模型的趋势项基本就是一条直线失去了变点检测的意义。核心代码如下from prophet import Prophet import pandas as pd # 准备Prophet格式的数据ds是时间戳y是归一化发电量 df_prophet df.reset_index()[[ds, power]].rename(columns{power: y}) model_p Prophet( growthlinear, seasonality_modemultiplicative, yearly_seasonality10, weekly_seasonalityFalse, daily_seasonalityFalse, changepoint_prior_scale0.15, seasonality_prior_scale10.0 ) # 添加日内周期周期为一天 model_p.add_seasonality(namedaily_custom, period1, fourier_order8) # 添加外生回归变量辐照度和温度 model_p.add_regressor(ghi) model_p.add_regressor(temp) train_prophet df_prophet[[ds, y, ghi, temp]].iloc[:len(train)] model_p.fit(train_prophet) future model_p.make_future_dataframe(periodsforecast_steps, freq15min) # 未来外生变量用实况预报或者历史均值填充 future[ghi] ghi_forecast future[temp] temp_forecast forecast model_p.predict(future)这里需要提醒一点Prophet的multiplicative seasonality通常用来处理“季节波动的幅度随趋势水平变化”的序列。光伏发电量正好符合这个特点夏天的峰值不仅平均出力高而且峰值的日波动也更剧烈。如果用additive模式模型会假定每天的偏差幅度是恒定的这对光伏来说太强了我用multiplicative模式的效果明显好一截。预测结果出来之后我会把prophet输出的yhat、yhat_lower、yhat_upper都留下来这三个值分别对应预测均值、下限和上限。配合绘图组件plot_components你可以直接看到日内周期项、年周期项和趋势项各自的形状这在实际项目汇报里是非常好的可视化素材比直接甩一个RMSE数字更有说服力。5. 两个模型怎么结合加权融合与兜底策略5.1 融合权重怎么定模型结合这件事最大的误区是拿两个模型的预测结果直接取平均。取平均在大多数情况下都有用因为它天然让系统的方差降低了但这不是最优策略。SARIMA和Prophet的结构差异决定了它们在同一个数据集上的表现分布也是不同的更好的做法是动态权重。我用的方法是用过去N天比如14天的滚动回测来评估两个模型各自在“当前近期段”的表现然后按误差倒数分配权重。核心逻辑很直接谁过去表现好谁在未来贡献更多。权重计算公式我简化成这样w_sarima (1 / rmse_sarima) / (1 / rmse_sarima 1 / rmse_prophet) w_prophet 1 - w_sarima在代码里的实现如下from sklearn.metrics import root_mean_squared_error # backtest_results 里存放两个模型在近14天的每日误差 sarima_rmse root_mean_squared_error(backtest_sarima[y_true], backtest_sarima[y_pred]) prophet_rmse root_mean_squared_error(backtest_prophet[y_true], backtest_prophet[y_pred]) w_sarima (1 / sarima_rmse) / (1 / sarima_rmse 1 / prophet_rmse) w_prophet 1 - w_sarima final_pred w_sarima * sarima_forecast w_prophet * prophet_forecast这段逻辑看起来简单但我实际项目中还有几个细节要注意第一回测窗口的长度不能太长也不能太短。太短比如3天权重会跟着某一次天气过程剧烈震荡太长比如60天又对最近的气象形势变化反应太慢。14天是我试下来比较稳的选择。第二权重计算的基准应该用“归一化后的误差”因为SARIMA和Prophet如果分别在原始量纲上预测数值尺度不同会导致误差倒数的尺度过分偏向某一个模型。把所有预测都映射到0到1区间之后再算RMSE这样权重才有可比性。第三融合不只是加权平均。我在业务落地时还加了一层“悬崖式兜底”如果SARIMA的预测值和Prophet的预测值在某一时刻的偏差超过总装机容量的15%说明两个模型对当前天气形势出现了根本性分歧这个时候不急着融合而是触发异常检测逻辑检查输入数据是不是有误、外生变量是不是有一方填充异常。等排查清楚再继续往下走。5.2 完整预测流程与代码骨架整套流程我总结成五个阶段数据加载与清洗、特征构建、SARIMA训练与预测、Prophet训练与预测、融合输出与评估。下面给一个完整骨架方便你直接抄def predict_solar_fleet(df, steps96): # 阶段1数据预处理 df preprocess(df) # 阶段2特征构建 df build_features(df) # 阶段3SARIMA sarima_model train_sarima(df) sarima_forecast, sarima_ci sarima_predict(sarima_model, steps) # 阶段4Prophet prophet_model train_prophet(df) prophet_forecast prophet_predict(prophet_model, steps) # 阶段5动态权重融合 weights compute_weights(backtest_records) final weights[0] * sarima_forecast weights[1] * prophet_forecast return final, sarima_forecast, prophet_forecast从这个骨架可以看到整个模块的核心只暴露了一个入口函数。在项目里我是把它封装成定时任务的每天早上6点自动拉取最近30天的历史数据和当天的天气预报数据重新训练一次两个模型然后输出未来24小时的预测结果到数据库。这里说一下为什么每天重训而不是每周重训光伏发电有很强的季节变化设备效率也会因为清洗和老化缓慢漂移模型如果每天都跟着最新数据微调一次就能始终贴合当前的电站状态。实测对比下来日更训练比周更训练的RMSE能降低3%到6%看上去不多但在电力交易场景里3%的预测偏差可能就是真金白银的偏差考核费用。6. 常见问题与排查实录6.1 SARIMA调参踩坑SARIMA最让人头疼的两个问题一个是拟合警告频发一个是预测结果发散。拟合警告方面最常见的提示是“ConvergenceWarning: Maximum likelihood estimation failed to converge”。这个在多数情况下不是模型有问题而是参数搜索空间太大或者初值设置不合理。我的处理办法是先把auto_arima的结果作为初值再用statsmodels手动拟合并且把maxiter从默认50提高到200置信度会好很多。另外一个细节是seasonal_order里的s越大模型的状态空间维度就越高收敛难度越大。所以如果你用15分钟粒度数据s96拟合时间会比小时粒度s24慢很多。预测结果发散这个坑更隐蔽。有时候训练集拟合得很好但预测未来24小时时曲线后半段突然振荡幅值巨大这就多半是参数的AR或MA多项式的根靠近单位圆了模型处于临界稳定状态。解决办法是回到enforce_stationarity和enforce_invertibility这两个参数把它们打开重新拟合虽然可能牺牲一部分拟合精度但预测稳定性会大幅提升。我一般这样处理先用宽松约束找到候选参数组合再用严格约束做最终模型。还有一个我在做差分时经常遇到的问题多差分会把序列里的真实信号也削弱。光伏发电量在晴天时特别平滑做一次普通差分和一次季节差分之后剩下的部分噪声占比会很大这时候模型很容易把噪声当成信号来学。所以d和D的取值并不是越大越好我见过不少案例是d0、D1就已经能通过检验了硬要加d1反而让AIC变差。6.2 Prophet与SARIMA结果差异过大的处理当你把两个模型融合在一起最直观的问题是如果某一个时段的预测差值超过阈值说明什么我的经验是这大概率不是模型本身的错而是输入数据有问题。举个例子有一次我发现某个电站的SARIMA预测值比Prophet高出将近20%查了半天发现是那几天的辐照度数据出现了连续缺测我用的回退策略是用历史同期均值填充而SARIMA没有用到辐照度作为外生变量它对这组缺失值毫无感知自然就只按历史发电量的季节规律往前走Prophet用了辐照度回归项看到辐照度均值偏低就自动向下修正了。这两个模型的预测方向完全相反最终融合值被拉到了一个中间位置反而成了一个不好不坏的预测。这种场景下我的建议是融合之前先做一个一致性检查如果偏差超限输出预测的同时把自己标记为“低置信度”并附带原因代码。这样下游业务系统就可以根据置信度决定是否启用手动决策流程。6.3 天气突变等特殊情况的兜底SARIMA和Prophet本质上是基于历史统计规律的模型它们对“历史中没见过的天气过程”无能为力。比如一次突如其来的强对流天气温度骤降10度辐照度在半小时内从800W/m²跌到100W/m²这时候任何统计模型输出都会失准。兜底方案有三层。第一层是外生变量修正把数值天气预报的辐照度转换成一个“天气折算系数”在融合预测结果上直接乘这个系数。这个系数的计算方式可以简单理解成预测时段的辐照度预报值除以历史同期的气候平均辐照度结果直接缩放发电量预测。第二层是分时段置信度调整如果天气预报显示未来24小时有降雨云团活动就把预测区间的宽度扩大宁可牺牲一点精确度也要覆盖住风险。第三层是短期滚动修正当实际发电量开始偏离预测时用最近2到3小时的实际出力斜率来动态调整接下来几小时的输出这相当于一个简单的人工PID虽然不优雅但在突发天气时比任何模型都好用。我在项目验收报告里额外提到过一个经验短期预测系统的核心不在于单点误差有多小而在于误差是否可控、是否可解释。SARIMA加Prophet这套组合的意义也在这里——模型的任何一个输出异常都能被追溯到具体原因这是黑盒模型很难做到的。7. 一点个人体会做光伏发电量预测这几年我最大的体会是模型是配角数据和场景的理解才是主角。SARIMA和Prophet都是很成熟的工具但把它们用对地方需要对电站的物理特性、数据采集的细节、甚至当地天气系统的脾气有足够了解。如果你想在现有项目里切入这套方案我给你一个最小可行路径先用小时粒度、只做未来24小时预测、只跑不融合的两模型对比把预测误差和业务容忍度对上号再迭代加入外生变量、动态权重和异常兜底。一上来就追求全功能往往会把问题复杂度推高到没法调试。最后说个小细节预测结果输出时建议同时保存模型版本号、数据版本号和预测时间戳。有一次我们排查一个突然变差的预测最后定位到是前一天的清洗脚本改了填充策略导致输入数据均值整体偏移了。如果没有数据版本记录这种问题排查起来基本靠猜。预测系统能不能长期稳定运行很多时候就靠这些不起眼的工程细节。
返回列表