news 2026/9/12 13:51:57

LSTM短期光伏功率预测实战:从数据清洗到模型部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LSTM短期光伏功率预测实战:从数据清洗到模型部署

简介:一份基于Python与LSTM的短期光伏预测算法实战资源,适合计算机、人工智能、电气及自动化方向的学生用于毕设、课设或项目初探。项目源自个人毕业设计,代码经完整测试,可结合园区数据完成光伏与负荷预测建模,覆盖单变量和多变量LSTM、规则集对比、数据获取与预处理脚本、可视化图表及README说明,帮助快速理解从数据清洗、特征构造到模型训练与效果评估的完整流程。压缩包共11个文件,以6个Jupyter Notebook为核心演示代码,辅以Python脚本、Excel示例数据、图片和Markdown文档,整体仅3.89MB,轻量易部署。已有199人学习,适合需要快速复现LSTM时序预测流程、或在此基础上修改扩展的读者。资源还包含模拟程序与储能框架参考,可支撑答辩演示和后续功能拓展,是短期光伏预测项目起步的实用参考。

1. 短期光伏功率预测用LSTM,不只是因为它能记住昨天

并网光伏电站要提前一天申报发电计划,电网侧则要按小时级甚至分钟级去调整备用容量。晴天还好,真正让调度头疼的是云团过境:前一分钟出力还在额定值的八成,后一分钟掉到两成,等云过去了又瞬间拉满。这种短时波动靠物理辐照模型很难追,因为云的运动本身就不是一个确定性问题。这也是为什么近几年的短期和超短期光伏功率预测方案里,数据驱动方法逐渐成了主流,而 LSTM 又是其中被用得最多的一种。

LSTM 能在光伏预测里站住脚,原因是它的细胞状态可以跨时间步传递信息,既保留了昼夜、季节这种长周期规律,又能对分钟级的功率突变做响应。配合 Python 生态里的 TensorFlow/Keras,特征工程、训练、部署可以用一套代码串起来。这篇文章就从选型逻辑开始,把数据清洗、特征构造、模型搭建、参数设置和评估验证完整走一遍,结尾再讲几个工程落地上最容易踩的坑。

2. LSTM 时间序列预测的建模逻辑:为什么光伏功率适合用门控结构

2.1 时序模型选型:RNN、LSTM、GRU 在光伏场景下的取舍

光伏功率序列本质上是一个带强周期性的非平稳时间序列。拿一个 15 分钟粒度的数据集来说,一天 96 个点,夜里长时间贴着零,白天则是一条随辐照度起伏的曲线。用普通 RNN 去拟合这种序列,反向传播时梯度经过多个时间步后会迅速衰减,前几小时的云况信息到下午基本就传不过来了,预测后段误差会明显变大。

LSTM 通过遗忘门、输入门、输出门和贯穿序列的细胞状态解决了梯度传播问题。相比之下,GRU 把三个门简化成两个,参数量更少,在小数据集上训练更快,但在光伏功率这种同时存在强周期和随机突变的序列上,LSTM 对极端云况的记忆保持能力通常更稳。Transformer 模型也在时序预测里流行,但需要的数据量和调参成本都更高,做短期光伏预测时,LSTM 仍然是最容易跑通且效果有保障的起点。

模型结构长期依赖能力参数量光伏功率场景适配度
RNN弱,梯度易消失最少适合小时级短窗,遇到功率骤降骤升误差放大
LSTM强,细胞状态贯穿全程中等能同时捕捉昼夜周期和分钟级波动,短期预测首选
GRU较强,结构更简中等偏少小数据量下收敛快,极端天气下记忆能力略弱

选择 LSTM 还有一层工程原因:Keras 里 LSTM 层只需要指定单元数和输入形状,不需要像 Transformer 那样额外处理位置编码和注意力掩码。对于光伏预测这种输入特征维度不高、序列长度不太长的任务,LSTM 的实现成本最低。

2.2 遗忘门、输入门、输出门分别对应光伏数据的哪些变化

把 LSTM 的三个门对应到光伏功率的物理过程,选型理由会清楚很多。

遗忘门决定从细胞状态里丢掉多少旧信息。对光伏数据来说,这意味着模型要学会在日出后逐步弱化前一夜的功率记忆,在持续的阴天里淡出几天前的晴空出力水平。输入门决定当前时间步有多少新信息被写入状态。当辐照度骤降时,当前时刻的功率变化和气象特征会通过输入门进入细胞状态,成为后续预测的重要依据。输出门则控制当前状态有多少被用于生成这一时刻的输出值。

理解了这三个门的含义,就能明白为什么 LSTM 对光伏预测是合适的:它不需要人工告诉模型“今天是什么天气类型”,模型会在训练中自己学会何时保留长周期规律、何时切换到短时突变模式。这也是为什么在特征工程里加入辐照度、温度、湿度等气象变量后,预测效果往往比单纯用功率历史值自回归更好。

2.3 把算法流程图落到张量形状:输入和输出对齐是关键

看 LSTM 算法流程图的时候,重点是理解数据从输入到输出的形状变化,而不是死记神经网络结构图。光伏预测里输入样本的常见组织方式是三维张量:[样本数, 时间步数, 特征数]。时间步数是滑动窗口长度,特征数包含历史功率、辐照度、温度等变量。

一个常见错误是,把 LSTM 的输出直接理解成“一个数”,忽略了 Keras 中return_sequences参数的影响。只取最后一步输出用于回归时,return_sequences=False,输出形状是[样本数, 单元数];如果需要每个时间步都有输出,就必须设成True。在建短期预测模型时,一般只在最后一层 LSTM 前使用完整序列输出,最终接全连接层得到预测值。

3. 光伏数据清洗与特征构造:决定预测上限的一步

3.1 数据质量检查:先看缺测率,再看分布是否合理

光伏预测项目里最花时间的往往不是模型调参,而是数据清洗。电站采集系统传回来的原始数据,常见问题包括通讯中断导致的整段缺测、辐照度仪表漂移导致的负值、夜间功率传感器残留的非零读数,以及重复时间戳。

拿到数据后先做一个快速统计:按天统计功率最大值是否超过装机容量、夜间时段是否有持续非零值、缺测占比是否超过 10%。超过 10% 且缺测集中在白天时段时,线性插值的可信度会明显下降,建议直接考虑删除该时间段或补充气象卫星数据。15 分钟粒度下,短时间缺测用前后值线性插值即可,连续超过 2 小时的大段缺测不建议强行填充。

字段单位常见异常处理方式
有功功率 PkW夜间非零、超过装机容量、死值夜间按当地时间置零;超容量值剔除;死值用邻域中值替换
水平辐照度 GHIW/m²负值、缺测、高值异常负值置零;缺测线性插值;与同时刻功率做散点图检查相关性
温度传感器漂移、极端值与正常气候范围比对,超出 3 倍标准差时标记剔除

处理完异常值后,用重采样统一时间戳:DataFrame.resample('15min').mean()是常见做法,重复时间戳取均值而不是直接删除,可以尽可能保留有效信息。

3.2 归一化与时间编码:把物理量转成模型友好输入

LSTM 对输入特征的尺度敏感,归一化是必须做的。功率和辐照度这类有明确上下界的物理量,用 MinMaxScaler 缩放到 0 到 1 之间足够;如果数据里存在极端突刺,RobustScaler 按分位数缩放会更稳,不容易被离群点带偏。

import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler df = pd.read_csv('pv_power.csv', parse_dates=['dt']) df = df.set_index('dt').resample('15min').mean() # 基础清洗:夜间功率归零,超过装机容量1.2倍的置为NaN后插值 df.loc[df.index.hour < 5, 'P'] = 0 df['P'] = df['P'].mask(df['P'] > 1200).interpolate() # 时间特征:用正余弦编码保留周期连续性,避免把23点和0点切成两个孤立的类别 hour = df.index.hour df['hour_sin'] = np.sin(2 * np.pi * hour / 24) df['hour_cos'] = np.cos(2 * np.pi * hour / 24) # 归一化:先fit再transform,训练后要保存scaler供预测时复用 feat_cols = ['P', 'GHI', 'T', 'RH', 'hour_sin', 'hour_cos'] scaler = MinMaxScaler() df[feat_cols] = scaler.fit_transform(df[feat_cols])

这段代码里有几个值得留意的点。重采样把原始数据统一到 15 分钟频率,缺测的时段会产生 NaN,后续插值把它们补上。时间特征的正余弦编码比直接用 hour 数字更合理,因为小时 23 和 0 在数值上差距很大,但在物理含义上是连续的。归一化必须在划分训练集和测试集之前先 fit 一次,之后用同一个 scaler 去 transform 测试集,测试时输入的真实物理值也要经过完全相同的变换。

3.3 滑动窗口构造样本对:seq_len、label_len 和验证集切分

超短期光伏预测的目标是未来 0 到 4 小时,也就是 16 个 15 分钟点。实践中并不总是直接预测全部 16 步,更常见的做法是用过去 24 步(6 小时)预测未来 4 步(1 小时),再通过滚动方式延伸预测时长。

def make_sequences(data, seq_len, label_len, feat_cols, target_idx=0): X, y = [], [] for i in range(len(data) - seq_len - label_len): X.append(data[i:i+seq_len]) y.append(data[i+seq_len:i+seq_len+label_len, target_idx]) return np.array(X), np.array(y) SEQ_LEN = 24 LABEL_LEN = 4 data_arr = df[feat_cols].values X, y = make_sequences(data_arr, SEQ_LEN, LABEL_LEN, feat_cols) # 按时间顺序切分,不能随机打乱 n_train = int(len(X) * 0.7) n_val = int(len(X) * 0.15) X_train, y_train = X[:n_train], y[:n_train] X_val, y_val = X[n_train:n_train+n_val], y[n_train:n_train+n_val] X_test, y_test = X[n_train+n_val:], y[n_train+n_val:]

label_len取 4 而不是 16,是短期预测任务里常见的选择:预测步数越少,误差积累越小,模型的输出也更容易收敛。如果想直接预测更长时间,可以把 LSTM 的输出维度加大,但误差会随预测步长迅速增长,不如滚动预测稳定。

验证集切分必须按时间顺序完成,随机打乱会把未来信息泄漏进训练集,导致验证指标虚高,模型上线后表现断崖式下跌。

4. 用 Python 构建 LSTM 光伏预测模型:源代码组织与关键参数

4.1 模型定义:一个可以直接运行的最小网络

有了处理好的样本对,模型部分用 Keras Sequential 接口就够了。常见结构是两层 LSTM 加 Dropout,再接全连接层输出。

import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout def build_lstm_model(seq_len, n_features, label_len): model = Sequential([ # return_sequences=True 时输出每个时间步的隐状态,供第二层LSTM继续处理 LSTM(64, input_shape=(seq_len, n_features), return_sequences=True), Dropout(0.2), LSTM(32, return_sequences=False), Dropout(0.2), # 输出维度与预测步数一致,单元数越大越容易拟合复杂曲线,但也更容易过拟合 Dense(label_len) ]) model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss='huber' ) return model model = build_lstm_model(SEQ_LEN, len(feat_cols), LABEL_LEN) model.summary()

return_sequences=True让第一层 LSTM 输出所有时间步的隐状态,第二层 LSTM 才能继续处理完整的序列信息。Dropout 放在 LSTM 层之间,训练时随机丢弃一部分神经元,减少对训练集噪声的拟合。损失函数选了 huber,它对功率骤升骤降造成的离群点不像 MSE 那么敏感,在光伏数据上通常比 MSE 稳。

4.2 训练参数怎么设:学习率、批量大小、损失函数和早停

LSTM 训练参数的选择虽然有一定经验成分,但逻辑是清楚的。Adam 优化器下学习率从 0.001 起步,如果验证集 loss 在前几个 epoch 震荡剧烈,降一个数量级到 0.0001。批量大小在数据量不大时取 32 即可,批量太大模型收敛快但容易收敛到平坦的次优解,批量太小训练时间会成倍增加。

参数建议起始值调整逻辑
learning_rate0.001验证集 loss 震荡则降到 0.0003 或 0.0001
batch_size32序列样本数过万时可调到 64,加速训练
losshuber功率毛刺多的电站用 huber,数据干净可用 MSE
epochs100配合早停,不设上限,以验证集 loss 为准
patience10验证集 loss 连续 10 个 epoch 不降则停止训练

早停是防止过拟合最直接的手段。光伏数据里晴天和阴天的样本分布极不均衡,训练到后期模型容易把晴天特征背下来,在验证集上表现反而变差。

from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True) reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5) history = model.fit( X_train, y_train, validation_data=(X_val, y_val), epochs=100, batch_size=32, callbacks=[early_stop, reduce_lr], verbose=1 )

训练过程中重点观察验证集 loss 和训练集 loss 的差距。训练集 loss 持续下降而验证集 loss 连续多个 epoch 不降反升,说明模型开始记忆训练集的噪声,早停会自动恢复到最佳权重。如果验证集 loss 从一开始就不下降,优先检查归一化是否用了同一个 scaler、样本序列是否构造正确。

4.3 调参经验:先固定网络结构,再动数据

一个常见的调参误区是上来就堆参数量。光伏功率序列的复杂度并没有高到需要超大网络,训练数据只有几千个样本时,单层 LSTM 配 32 或 64 个单元往往就够用了。参数调优的顺序应该是:先把滑窗长度和预测步数定下来,用一组默认参数跑通全流程,确认验证集指标合理后,再逐个调整 LSTM 单元数和 Dropout 比例。

调参时还要注意shuffle的问题。Keras 的model.fit默认会打乱训练样本的顺序,这在一般任务里没问题,但时间序列任务里如果样本顺序被打乱,模型就失去了利用相邻样本连续性学习的能力。可以在fit里设shuffle=False,或者保持默认,但验证集必须严格按时间顺序切片。

5. 预测效果验证与工程化落地:从评估指标到源码交付

5.1 按装机容量归一化的误差指标才有可比性

不同光伏电站的装机容量差异很大,直接用 RMSE 或 MAE 做横向对比没有意义。工程上更常用的是按装机容量归一的 NMAE 和 NRMSE。

from sklearn.metrics import mean_squared_error, mean_absolute_error capacity_kw = 1000 # 电站装机容量,单位kW y_pred = model.predict(X_test) y_true = y_test # 预测输出是归一化后的值,要反归一化回真实功率才能算误差 y_pred_real = scaler.inverse_transform( np.concatenate([y_pred, np.zeros((len(y_pred), len(feat_cols)-1))], axis=1) )[:, 0] y_true_real = scaler.inverse_transform( np.concatenate([y_true, np.zeros((len(y_true), len(feat_cols)-1))], axis=1) )[:, 0] nmae = mean_absolute_error(y_true_real, y_pred_real) / capacity_kw * 100 rmse = np.sqrt(mean_squared_error(y_true_real, y_pred_real)) print(f'NMAE: {nmae:.2f}% RMSE: {rmse:.2f} kW')

反归一化是这里最容易出错的环节。MinMaxScaler是在整个特征矩阵上训练的,预测的输出只有功率一列,其他特征位置要补零才能调用inverse_transform。如果直接把scaler单独 fit 在功率序列上,再对多步预测输出做反归一化,就会报维度错误或者得到错位的数值。更稳妥的做法是在训练前就为功率列单独建一个 scaler,预测时直接用它变换。

5.2 模型持久化与滚动预测:预测值要能接回滑窗

训练完成后的模型要保存下来,供定时预测任务加载使用。model.save('lstm_pv.h5')之后,用tf.keras.models.load_model加载即可。真正的难点在滚动预测:做未来 4 小时预测时,如果一次性输出,误差会随时间步增大,工程上更常见的做法是每次预测 4 步,把得到的预测功率补充到滑窗末尾,再滑动窗口继续预测。

def rolling_predict(model, last_seq, n_steps, step_len=4): predictions = [] current_seq = last_seq.copy() for _ in range(n_steps // step_len): y_step = model.predict(current_seq[np.newaxis, :, :], verbose=0) predictions.append(y_step[0]) # 构造新窗口:丢掉最旧的step_len步,把预测值拼接到末尾 next_seq = np.vstack([current_seq[step_len:], np.zeros((step_len, current_seq.shape[1]))]) next_seq[-step_len:, 0] = y_step[0] next_seq[-step_len:, 1:] = 0 # 缺失的气象特征用最近值或0填充 current_seq = next_seq return np.concatenate(predictions)

滚动预测的关键假设是未来时段的气象特征未知,只能用最近观测值或预报值填充。如果项目里能拿到数值天气预报数据,把辐照度、温度的预报值填进去,预测精度会明显提升。这也解释了为什么单纯依赖功率历史值的自回归模型,预测时长超过 1 小时后误差会快速变大。

5.3 文档说明里必须写清楚的三个运行细节

标题里强调了“源代码+文档说明”,源码交付时最容易让接手人头疼的往往不是网络结构,而是数据流动的细节。第一个要写清楚的是数据格式:原始 CSV 的时间列格式、时区是 UTC 还是北京时间、功率单位是 kW 还是 MW,这些不写明白,换一份数据就跑不通。第二个是归一化和反归一化的完整流程,scaler 文件要和模型文件一起交付,否则加载模型后无法对新的输入做变换。第三个是天气突变时的评估方式,建议在测试集里单独挑多云天和晴天各统计一次 NMAE,因为晴天的误差可能只有 5%,多云天可能冲到 20%,只看总体平均值会掩盖模型在复杂天气下的真实表现。

跑通基础模型后,可以先集中精力把这几处补齐:给训练脚本加一个--input --model_dir参数,让代码能接受任意路径的数据文件;把预测结果画成曲线图,和真实功率叠加对比,人工看一眼就能发现预测是否出现了相位偏移或整体偏低。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 13:49:14

Raspberry Pi Pico入门:MicroPython控制GPIO与PWM实战

1. 项目概述&#xff1a;为什么从 Pico 和 MicroPython 开始学硬件开发&#xff1f;如果你最近翻过电子爱好者论坛、刷过 B 站硬件区&#xff0c;或者在淘宝搜过“入门单片机”&#xff0c;大概率会撞见 Raspberry Pi Pico 这块巴掌大的小板子——它不靠性能堆料&#xff0c;不…

作者头像 李华
网站建设 2026/9/12 13:48:11

LLM推理优化实战:从KV缓存到FlashAttention的硬核调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 13:48:05

5分钟跑通数学可视化:用MathViz把抽象数学变成可拖拽的3D画面

5分钟跑通数学可视化&#xff1a;用MathViz把抽象数学变成可拖拽的3D画面 【免费下载链接】AnimateAnyone Animate Anyone: Consistent and Controllable Image-to-Video Synthesis for Character Animation 项目地址: https://gitcode.com/GitHub_Trending/an/AnimateAnyone…

作者头像 李华
网站建设 2026/9/12 13:47:52

openpi 环境搭建30分钟指南:Docker 3 步从裸机到跑通 VLA 示例

openpi 环境搭建30分钟指南&#xff1a;Docker 3 步从裸机到跑通 VLA 示例 【免费下载链接】openpi 项目地址: https://gitcode.com/GitHub_Trending/op/openpi openpi 是 Physical Intelligence 团队开源的机器人 VLA 模型仓库&#xff0c;提供 π₀、π₀-FAST、π₀…

作者头像 李华