
简介在新能源并网规模持续扩大的背景下光伏发电功率预测是电力调度与电站考核的关键技术。其核心难点在于出力序列受云层、温度等因素影响呈现强非线性与非平稳特性。以长短时记忆网络LSTM为代表的深度时序模型配合注意力机制能有效捕捉多变量历史状态与未来功率间的映射关系。围绕完整技术闭环系统阐释了从数据清洗、特征工程到模型训练、后端接口及可视化大屏的工程实现并剖析归一化泄漏、时区错位、滚动预测误差累积等实际坑点。借助FastAPI与ECharts实现了从气象预报到预测曲线展示的全链路服务为新能源预测与电力数字化转型提供了可直接参考的实战方案。 做过光伏预测的人都知道这不是一个“调个网络就能跑出漂亮曲线”的轻松任务。我前年接手一个 4.8MW 分布式光伏电站的功率预测项目调度侧要求未来 24 小时逐 15 分钟输出发电功率预测曲线用来做发电计划和日内考核。一开始我也试过直接套用论文里的 LSTM 模型结果晴天效果还行一到多云天预测曲线和实际功率就像两根对不上的琴弦误差大得没法看。后来我把整个项目从数据清洗、特征工程到模型结构逐步重构再补齐了前端展示和后端服务才真正把一套可以落地使用的“基于深度学习的光伏发电功率预测系统源码”跑通。这篇博客就是完整的项目说明包括核心技术点、前后端实现思路、关键代码片段以及我摔过的坑希望对做新能源预测、电力数字化或者想入门时序深度学习实战的同行有帮助。1. 光伏功率预测的痛点与这套系统的整体定位1.1 光伏出力波动的底层来源光伏发电的核心物理过程是光生伏特效应组件输出的直流功率由光照强度、组件温度、光谱分布共同决定。实际电站运行中功率曲线最剧烈的扰动来自云层遮挡。一片云飘过辐照度可以在几分钟内从 900W/m² 掉到 200W/m²组串输出随之暴跌整站出力可能瞬间下降 60% 以上。温度同样关键。晶硅组件普遍是负温度系数大约 -0.35%/°C 到 -0.45%/°C也就是说组件温度每升高 10°C发电效率下降约 3.5% 到 4.5%。夏天的暴晒日组件表面温度超过 60°C 很正常这就解释了为什么中午辐射最强的时候功率曲线反而会出现一个“高温凹陷”。风速、湿度、空气污染都会间接影响出力。风速帮助组件散热湿度大容易让表面形成水膜或积尘结垢雾霾天散射辐照占比高整体辐照强度衰减。还有积雪、落叶、鸟类遮挡这些随机因素都会平白增加预测难度。这些波动源叠加起来就是一个典型的强非线性、非平稳时间序列建模问题。1.2 不同预测尺度对应的业务价值光伏功率预测按照时间尺度大致可以分成三类每类对应的业务场景完全不同。超短期预测未来 0~4 小时分钟级分辨率主要用于实时调度、储能充放电策略和调频辅助服务。时间粒度细要求响应快对突变天气非常敏感调度员需要知道下一个小时功率会不会骤降是否需要提前调用火电或储能顶上去。短期预测未来 1~3 天小时级或 15 分钟级分辨率用于日前发电计划申报、电力交易和区域内功率平衡。电网公司考核光伏电站的日前预测准确率这个指标直接影响电站的考核费用甚至影响并网资格。中期预测周级别以上主要服务于电站检修安排、组件清洗计划、储能电量规划。这类需求对单点精度要求不高看趋势即可。我们这个项目把重点放在短期超短期两个尺度上输出未来 24 小时逐 15 分钟的预测曲线同时支持未来 1 小时内的快速滚动预测。这个定位最贴近电站实际考核需求源码结构上也没有为中期预测做过度设计。1.3 源码包的交付范围与系统架构整个项目采用前后端分离架构技术栈上后端负责模型推理和业务数据接口前端负责可视化展示和交互操作。完整的交付内容如下后端服务FastAPI 框架 PyTorch 模型推理 APScheduler 定时任务 MySQL 数据存储前端应用Vue 3 Vite ECharts Axios模型部分数据预处理模块、LSTMAttention 模型定义、训练脚本、评估脚本项目说明环境配置文档、数据格式说明、API 接口文档、训练与部署步骤数据流向是NWP 气象预报数据 电站 SCADA 历史功率数据 → 数据清洗和特征工程 → LSTMAttention 模型 → 预测结果写入 MySQL → 后端 API 输出 → 前端 ECharts 展示曲线。这个架构不算复杂但是每一步都有值得展开的细节下面从最容易被忽略的数据工程开始讲。2. 数据工程决定预测精度的 80% 环节我在这个项目里踩过最深的坑就是花大量时间调模型结构提升效果还不如老老实实把数据处理干净。说数据工程决定 80% 的精度一点不夸张。很多同学直接拿公开数据集或者电站导出的原始数据就跑模型结果训练集和测试集的时间顺序是乱的、夜间数据没处理、NWP 预报字段对不齐这些都会让模型效果虚高或者完全不可用。2.1 数据源选择与字段设计光伏功率预测模型需要两类数据历史实测数据和未来气象预报数据。历史实测数据来自电站 SCADA 系统建议至少收集一整年的数据时间分辨率 15 分钟。如果条件允许两年以上更好这样能覆盖春、夏、秋、冬完整的季节变化。SCADA 导出的核心字段如下时间戳统一为东八区时间精度到分钟并网点有功功率单位 kW这是模型要预测的目标变量逆变器状态/可用率用于判断数据是否在正常发电状态组件温度、环境温度与效率衰减直接相关内部辐照度如果电站装有辐照仪这是最强预测特征NWP 数值天气预报数据来自气象服务商或开源气象模型主要字段包括总辐照度GHI、直接辐照度DNI、散射辐照度DHI、2 米温度、2 米相对湿度、10 米风速和风向。NWP 数据的时间分辨率通常是 1 小时或 3 小时需要插值到和 SCADA 数据一致的 15 分钟间隔。一个容易忽略的点NWP 数据是“预报数据”不是实测数据。它和真实气象之间存在系统误差比如某些模型总是高估多云天的辐照度。在训练阶段不要直接用 NWP 数据作为特征去拟合真实功率而应该尽量把 NWP、真实气象实测、历史功率三者放在一起让模型自己去学习 NWP 修正量。这里建议的字段对齐方式如下数据源关键字段时间分辨率用途SCADA 历史实测并网功率、组件温度、环境温度、内部辐照度15 分钟训练标签 滞后特征NWP 气象预报GHI、DNI、DHI、温度、湿度、风速、风向1 小时/3 小时未来预测特征真实气象站辐照度、温度、风速15 分钟校正 NWP 系统误差我自己用的版本里还加了一个很重要的字段太阳高度角。这个不用从气象站拿根据经纬度和时间戳就能算出来。加入太阳高度角之后模型能更快学习到“日出日落”的规律晴天的拟合误差明显下降。2.2 异常值清洗与夜间数据规范化原始数据的脏程度往往超过想象。常见的几类异常包括夜间数据功率不为零。待机状态的逆变器会有很小的自耗电读数可能是 -2kW 或者 1kW这类数据必须处理。逻辑上太阳高度角小于 0 时理论发电功率应该为 0直接把该时段功率和辐照度特征全部置零。辐照度大于 0 但功率为 0。这种情况通常对应电站停机维护、逆变器故障、限电、或者通讯中断。如果样本量不大直接删除如果样本量大建议打一个独立的“停机标记”特征让模型学习到停机状况而不是无脑地把功率和辐照关联起来。功率小于 0 或者超过装机容量。功率小于 0 通常是计量误差直接置零。超过装机容量的情况在分布式电站偶尔出现比如夜间用电导致功率计量方向反转需要结合计量点位置判断一般按上限截断。连续缺数超过一定比例。比如通讯故障导致连续缺失 3 小时以上这一段直接整体剔除。短时间缺失可以用前后插值配合 NWP 温度变化率来补。清洗这一步做完还要做归一化。我这里是分两个维度处理的对功率用装机容量做除法转成标幺值对辐照度按各自量程做 MinMax 归一化。归一化的统计量只能用训练集计算保存为 json 文件推理阶段加载后在服务端重放。这个问题很多人踩坑后面会专门讲。2.3 特征工程把天气趋势变成模型能读的信号原始字段不能直接全丢给深度学习模型。虽然理论上模型能自动提取特征但实际工程中手工特征能显著降低数据拟合难度尤其在训练数据量有限的情况下。我最终使用的特征分为四组第一组历史延迟特征。取目标时刻前 15 分钟、30 分钟、45 分钟、60 分钟的历史功率值用于捕捉功率变化的惯性。这组特征对超短期预测效果极其显著因为天气系统通常有连续性未来 15 分钟的功率和最近一小时的功率高度相关。第二组滚动统计量。计算过去 1 小时、2 小时、6 小时窗口的历史功率均值、最大值、标准差。标准差能反映近期功率波动水平波动大的时候模型应该更保守降低预测置信度。第三组气象趋势特征。包括 NWP 辐照度在未来 1 小时、2 小时内的变化率NWP 温度在未来 6 小时内的变化趋势。这些特征能告诉模型“接下来天气是转好还是转坏”对多云天特别有效。第四组时间编码。小时0~23和太阳高度角度做周期编码传入模型。星期信息对光伏影响不大主要影响用电负荷这里忽略了。特征一共 32 个维度。做特征重要性分析时发现辐照度变化率和历史功率标准差排在最前面单纯依赖历史功率均值反而不够。这说明“趋势”比“绝对值”对预测更重要。2.4 时间序列训练集划分避免数据泄漏这是整个项目里最容易翻车的地方。光伏数据的季节性很强不同季节、不同天气类型下功率曲线差异巨大。如果随机打乱数据划分训练集和验证集模型很可能“见过”验证集的数据因为同一时刻前后的数据都被丢进了训练集这就是典型的数据泄漏。正确做法是严格按时间顺序划分取前 70% 作为训练集中间 10% 作为验证集最后 20% 作为测试集。而且要注意验证集和测试集不应该随机抽一天插在训练集中间而是连续时间段。还有一个更隐蔽的问题归一化统计量的泄漏。如果先对全部数据做 MinMax 归一化再切分数据集相当于使用训练集和测试集的全局最大值、最小值来缩放数据测试集的信息已经偷偷流入训练过程。正确的流程是先切分数据集再用训练集的最小值和最大值做归一化验证集和测试集沿用同一套统计量。我项目里的实际做法是写了一个独立的make_dataset.py脚本按时间顺序切分原始数据然后只从训练集分支计算 scaler 参数保存成一个 pickle 文件。之后训练、验证、推理全部加载这个 pickle保证一致性。3. 模型设计从 LSTM 基线到注意力机制3.1 物理模型的局限与深度学习的优势传统光伏功率预测方法主要是物理模型和统计模型两大类。物理模型通过建立辐照度到组件输出的数学方程来预测功率。需要精确的组件安装倾角、方位角、串联电阻、温度系数、逆变器效率曲线等几十个参数。真实电站里很多参数是出厂铭牌值但运行一段时间后组件衰减、积尘、局部遮挡实际参数已经变了。物理模型对参数误差极其敏感调参成本高而且对多云天边界层的模拟效果差。统计模型以 ARIMA、回归分析、支持向量机为主它们对线性趋势和简单周期建模有效但光伏功率和气象因素之间的关系是强非线性、强耦合的统计模型的表达力明显不够。深度学习的优势在于可以直接从大量历史数据中学习“气象输入 → 功率输出”的非线性映射不用关心电站内部参数细节对组件衰减、积尘这些隐性因素也有自适应性。只要数据量够模型可以自动发现辐照度、温度、风速在不同季节不同时段下的复杂交互关系。3.2 网络结构细节两层 LSTM 注意力加权我最终采用的模型结构是输入层 → 两层 LSTM → 注意力机制 → 全连接输出。整体结构不复杂但每个组件的选择都有原因。第一层 LSTM 的隐藏单元数设为 128负责提取短期的局部时序模式比如功率的惯性延续、辐照度的短时波动。第二层 LSTM 的隐藏单元数设为 64在上面叠加更抽象的时序依赖。注意力机制是这套模型提升效果的核心。普通 LSTM 只取最后一个时间步的输出或者取所有时间步输出后做一个简单平均。这两种做法要么损失中间信息要么把所有历史时刻等同对待。实际上对功率预测来说当前时刻和未来 1 小时内的输入特征远比清晨时段重要。注意力机制让模型自动学习每个时间步的权重把和预测目标最相关的历史时刻放大弱化无关时刻的影响。核心代码结构如下import torch import torch.nn as nn import torch.nn.functional as F class LSTMAttention(nn.Module): def __init__(self, input_size, hidden_size128, num_layers2, output_size4): super(LSTMAttention, self).__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue, dropout0.2) self.attention nn.Linear(hidden_size, 1) self.fc nn.Sequential( nn.Linear(hidden_size, 64), nn.ReLU(), nn.Dropout(0.2), nn.Linear(64, output_size) ) def forward(self, x): out, _ self.lstm(x) # out: [batch, seq_len, hidden] attn_weights F.softmax(self.attention(out).squeeze(-1), dim1) attn_weights attn_weights.unsqueeze(1) context torch.bmm(attn_weights, out).squeeze(1) return self.fc(context)这里output_size4表示一次预测未来 4 个时间点也就是未来 1 小时15 分钟间隔配合滑动窗口可以实现多步滚动预测。如果预测未来 24 小时一般不需要纯递归而是直接让输出维度变成 96即未来 96 个 15 分钟点。但输出维度太大的时候模型训练难度显著增加序列末尾的预测误差会非常大。我实际对比过输出维度 96 的模型后期误差比滚动预测法大 20% 以上。所以最终方案固定为“预测 4 步 滚动”用上一次预测结果作为下一次输入的一部分。3.3 训练策略与超参数训练过程中我踩过几个关键的超参数坑列出来给大家参考。输入序列长度lookback选择的是 24也就是过去 6 小时15 分钟间隔的数据。实验结果显示lookback24 比 lookback12 提升了大约 12% 的 RMSE但继续增加到 48 以后提升微弱训练时间却翻倍。对光伏这种强周期性数据来说6 小时的历史足够覆盖大部分天气过程的“前兆”了。损失函数用的是 Huber Loss。相比 MSEHuber Loss 对异常值不那么敏感不会因为个别极端天气样本破坏整体梯度相比 MAE它在误差较小时梯度更平滑收敛更稳。评估指标用 RMSE 和归一化 RMSE。Batch size 设为 64学习率初始值 3e-4使用 AdamW 优化器配合 CosineAnnealingLR 学习率调度。训练最多 200 个 epoch但配合早停一般在 60~80 个 epoch 就能收敛。Dropout 设为 0.2 防止过拟合。训练集有两万多条样本放到单个 3060 显卡上十分钟内跑完。操作上还有一个重要细节对功率为 0 的夜间样本做权重衰减。因为夜间样本占全天的一半如果不降低它们的影响模型会偏向学习“输出 0”导致白天预测值偏低。在损失函数里加了一个 mask 权重白天样本权重是 1.0夜间样本权重是 0.3训练后晴天的峰值预测误差降低了约 6%。3.4 评价指标不要只看 MAPE很多人汇报预测精度时喜欢用 MAPE平均绝对百分比误差这个指标对光伏功率预测非常不友好。原因是夜间和日出日落时刻功率接近 0误差百分比趋近无穷大直接拉爆整体指标。更坑的是同样的模型不同季节的 MAPE 差异极大冬天地面辐照弱、日发电量小MAPE 数字会特别难看。我的建议是用归一化均方根误差nRMSE RMSE / 装机容量 × 100%这个指标可以跨电站对比。同时统计“误差超过 20% 装机容量的样本占比”这个指标更贴近调度考核的实际感受。按天气类型分别统计。晴天、多云、雨天的模型表现差异巨大混在一起看会掩盖真实问题。下表是项目里一次典型的测试集表现未来 24 小时滚动预测天气类型nRMSEMAE误差20%装机占比晴天4.3%3.1%1.2%多云12.6%9.2%18.4%雨天17.8%12.5%31.5%看着很稳定的表现如果没有按天气拆分统计我根本发现不了多云天的预测风险有多大。这件事直接影响系统设计——后面讲后端和前端时你会看到我特意加了“预测可信度”标识。4. 后端工程化把模型变成稳定可用的服务4.1 FastAPI 与模型部署的选型理由模型训练完只是第一步真正要落地使用需要把它封装成稳定的后端服务。框架选择上我对比过 Flask、FastAPI 和 Django REST Framework。Django REST Framework 确实功能全面自带 ORM 和后台管理但对一个轻量预测服务来说太重了项目结构里有一堆用不到的配置。Flask 轻量但异步支持弱模型推理是 CPU 密集I/O 混合任务多个请求同时进来时Flask 的并发处理能力有点捉襟见肘。FastAPI 基于 Starlette 实现异步支持性能好自带 OpenAPI 文档页面调试接口很方便。所以我选了 FastAPI。模型推理这块用 PyTorch 的 TorchScript 方式导出模型避免部署环境需要完整安装 PyTorch 和模型定义代码。如果直接把 Python 训练脚本塞到生产环境每次启动都要重新加载模型代码容易因为版本不一致出问题。4.2 模型加载、预处理与预测结果反归一化的完整链路模型导入和预处理是整个后端最容易出错的地方。我踩过一次很大的坑训练好的模型在本地测试效果不错部署到服务器后预测结果完全错乱最后发现是服务器的时间格式和训练机不一致导致时间特征算出来的太阳高度角差了 90 度。所以后来我写了一个独立的预测服务类把数据预处理、模型推理、结果反归一化全部封装起来。核心逻辑如下import torch import numpy as np import pickle class SolarPredictor: def __init__(self, model_path, scaler_path, devicecpu): self.model torch.jit.load(model_path, map_locationdevice) self.model.eval() with open(scaler_path, rb) as f: self.scaler pickle.load(f) def predict(self, features): features self.scaler.transform(features) features torch.FloatTensor(features).unsqueeze(0) with torch.no_grad(): pred_scaled self.model(features) # 反归一化转为实际功率 power_array self.scaler.inverse_transform( np.zeros((1, pred_scaled.shape[-1])) ) real_scale power_array[0] pred_real pred_scaled.numpy() * real_scale return pred_real.tolist()这里要注意inverse_transform用在全零数组上只是为了取出功率维度的缩放系数。如果原始样本里噪声太多scaler 里的 max-min 可能包含异常极值所以训练阶段要做一次截断才能保证反归一化合理。4.3 API 接口设计与定时预测任务后端对外提供三个核心接口POST /api/predict接收最近 6 小时的实测特征序列返回未来 1 小时逐 15 分钟预测功率。GET /api/forecast?dateYYYY-MM-DD返回某天的 96 点预测曲线数据从 MySQL 读取。GET /api/history?start...end...返回历史实际功率曲线用于和预测对比。预测结果不会实时去调用模型而是由定时任务每天在固定时间点提前把未来 24 小时的预测算好写入 MySQL。这样做的目的是模型推理是同步操作耗时不可控如果每次浏览器请求都触发推理高峰期容易被拖死。定时任务把预测结果缓存下来前端请求时只查库响应速度在毫秒级。from apscheduler.schedulers.blocking import BlockingScheduler def scheduled_predict(): features load_latest_weather_and_power() predictor get_predictor() preds predictor.predict(features) save_to_database(preds) scheduler BlockingScheduler(timezoneAsia/Shanghai) scheduler.add_job(scheduled_predict, cron, hour5, minute30) scheduler.add_job(scheduled_predict, cron, hour11, minute30) scheduler.start()每天跑两次早上 5:30 根据最新 NWP 数据出白天的预测曲线中午 11:30 再修正一次兼顾日前和日内两个业务场景。4.4 模型更新与版本管理模型不是训练一次就一劳永逸的。光伏组件会逐年衰减电站周围可能有新建建筑物遮挡NWP 数据源本身也可能更换版本这些都会让旧模型的预测精度下降。所以我留了一个模型训练模块可以随时用最近 3 个月的新数据增量训练。部署层面采用最朴素的版本管理方式每个模型文件带训练日期例如model_20250601.pt后端配置文件中指定当前要加载的模型版本。切换模型只需要改配置文件重启服务不涉及代码改动。新模型上线前会先在离线测试集上跑一遍 nRMSE 对比连续三天的预测误差比旧模型低 5% 以上才允许切换。5. 前端可视化让预测结果真正被“看见”5.1 Vue 3 ECharts 的选型前端部分我用的是 Vue 3 Vite ECharts。选 Vue 而不是 React主要是考虑到电力行业信息化系统里 Vue 的生态更成熟上手快团队里其他同事有经验。ECharts 的图表生态非常丰富折线图、堆叠面积图、仪表盘都能直接配置交互效果也够用。项目里前端不是重头戏但它是让预测结果真正被电站运维人员“看到”的关键。没有可视化再准的模型也很难被用户认可。5.2 核心页面与图表设计页面结构分三个模块首页大屏、预测对比、历史查询。首页大屏是整个系统的门面。中间放一张 24 小时功率预测曲线纵轴是功率MW横轴是时间。实际功率用实线绘制预测功率用虚线绘制预测区域用渐变填充。大屏只有这一个核心图表周围配上当前实时功率、今日发电量、预测可信度三个数字卡片。预测对比页是运维和分析人员使用最多的页面。这里提供一个时间范围选择器默认展示最近一周的逐 15 分钟数据。ECharts 配置上关键是让预测功率和实际功率使用同一个 y 轴量纲同时用一条竖线标出“当前时刻”划分左边的历史区间和右边的预测区间。这个视觉提示对用户理解预测语义特别重要。历史查询页会比较简洁提供一个表格展示历史预测误差排行按误差从大到小排序。这个页面直接暴露模型的弱点时段方便定位数据问题。ECharts 核心配置思路如下option { xAxis: { type: time }, yAxis: { type: value, name: 功率/MW }, series: [ { name: 实际功率, type: line, data: actualData, smooth: true, lineStyle: { width: 3 } }, { name: 预测功率, type: line, data: forecastData, smooth: true, lineStyle: { type: dashed } }, { name: 预测区间, type: line, data: confidenceBand, areaStyle: { opacity: 0.15 } } ] };预测可信度也做成一个简单的计算规则如果目标时段云量预报值高于 60%可信度标记为“低”否则标记为“高”。这个规则虽然有粗糙之嫌但比把数字直接扔给用户更直观。5.3 前后端联调中的三个典型问题前后端联调阶段我遇到三个问题值得单独说。第一个是跨域问题。前端跑在 localhost:5173后端跑在 localhost:8000端口不同直接触发 CORS 拦截。后端的 FastAPI 里配置了 CORS 中间件允许本地开发环境的 origin生产环境则改为 Nginx 反向代理同一个域名挂前端静态资源和后端 API彻底规避跨域。第二个是时间格式不一致。前端传给后端的日期参数是2025-06-01后端返回的时间戳是字符串2025-06-01T05:30:00Z前端拿到后直接用 ECharts 的type: time解析结果发现所有数据点都比本地时间少了 8 小时。后来统一规定后端返回带时区的 ISO 字符串前端在代码里显式转换成Asia/Shanghai时区再喂给图表。第三个是空数据处理。如果某一天电站断网数据库里没有数据后端接口会返回空数组。前端图表不能直接render空数组需要做空状态判断否则页面白屏。这个细节虽然小但运维人员半夜看数据的时候碰到一次就够难受了。6. 实测效果、踩坑记录与后续优化6.1 不同天气条件下的预测表现系统上线运行三个月整体 nRMSE 稳定在 8%~10% 之间在同类光伏电站里属于能打的数据。分天气类型看晴天的预测效果最好nRMSE 在 4% 左右。这符合预期因为晴天的辐射模型很稳定每天的曲线基本呈一个光滑钟形模型学到的规律可以直接外推。多云天是最大的痛点nRMSE 约 12%。云层遮挡引起的功率骤降几乎是随机事件NWP 数据只能预报大尺度云量变化无法精确到具体哪一块云什么时候飘过电站上空。这是当前光伏预测领域的物理上限不是单靠模型结构就能解决的。雨天表现反而不算太差nRMSE 在 16% 左右。因为雨天整体出力低绝对值误差小虽然百分比看起来高但实际对电网的影响有限。这个结果也验证了一个重要结论预测误差的来源结构和天气类型强相关在做误差分析时一定要按天气分层看不要只看整体数字。6.2 代码跑通后最容易踩到的坑我把这个项目从零到一跑通之后总结出一批非常折磨人的坑。每一个都是真实发生过的希望大家能用得上。第一个坑是归一化统计量不一致。训练时用了全量数据的最大值和最小值测试时却用测试集自己的统计量重新归一化结果测试误差看起来非常小实际部署后效果暴跌。这个问题的本质是数据泄漏我在 2.4 小节已经强调了这里再说一遍无论是归一化还是特征工程统计量都只能从训练集学习验证集和测试集只做变换不参与计算。第二个坑是 LSTM 的输入形状。PyTorch 的 LSTM 默认接受[batch, seq_len, feature]形状但batch_firstFalse时默认是[seq_len, batch, feature]。我在第一次写训练脚本时就栽在这里调试了一下午才发现是形状不对。建议创建 LSTM 时直接把batch_firstTrue写上之后所有处理都不容易混淆。第三个坑是时区问题。前端和后端训练机和服务器任何一处时区不一致都会导致时间特征错位。尤其当时间特征包含太阳高度角时一旦服务器用的 UTC 时间而训练数据是东八区时间模型预测出来的曲线会整体错位几个小时。上生产环境前一定要在部署文档里明确写明“统一使用 Asia/Shanghai 时区”并且在后端启动时强制设置。第四个坑是推理环境缺失训练时用的库版本。我用torch.jit.save导出模型后部署时发现服务器上没有安装对应的 TorchVision虽然模型推理本身用不到但导入时依然会报错。后来改成在 docker 镜像里固定所有依赖版本再也没出过这种问题。建议项目里直接提供 requirements.txt 和 Dockerfile。第五个坑是滚动预测的误差累积。模型一次预测 4 步未来 1 小时如果要做未来 24 小时的预测原始的滚动方法会把前一步的预测输出当作下一步的输入。但预测值本身有误差误差会逐步累积24 小时后期末误差会显著放大。我最终的做法是未来 4 小时用滚动预测之后每个小时直接用 NWP 气象特征重新做一次独立预测避免误差叠加。6.3 后续可以扩展的方向这套系统目前已经能稳定运行但要进一步提升我还有几个明确的方向想补。第一个方向是针对多云天做多模型集成。太阳能辐射物理模型在晴天表现好统计模型在稳定的多云天表现好深度学习模型在复杂天气下的泛化能力最强。把这些模型的预测结果做加权融合根据云量预报动态调整权重理论上可以进一步压低多云天的误差。第二个方向是引入卫星云图数据。卫星云图能提供 15 分钟粒度的云层运动趋势把云层特征作为额外输入让模型提前感知云层即将遮挡电站。这个方向需要额外的数据源和图像处理模块改造量不小但可能从根上解决最头疼的突变云问题。第三个方向是多站点联合训练。目前模型是单电站独立训练如果手上有同区域多个电站的数据可以用联邦学习或者简单的多任务学习框架让模型共享区域气象规律每个电站再保留自己的私有特征。这类方法在小规模数据集上的提升很明显。第四个方向是把预测结果接入储能调度。光伏加储能是大势所趋预测准确率直接决定储能充放电策略的好坏。把预测输出从单纯的功率曲线扩展成概率分布让储能调度算法有置信区间可用这一步从算法到运维都能获得很大收益。这个项目做到现在给我最深的感受是预测问题归根结底不是模型问题而是数据问题。模型框架都是公开的真正决定系统可用性的是数据处理是否严谨、部署链路是否完整、界面是否真的帮用户看懂结果。对我个人来说另一个很有价值的收获是对“预测可信度”这个概念的重新理解——不是所有时刻都值得给出高置信度的预测结果把不确定性明确告诉系统使用者反而是提升信任度的最好方式。最后分享一个小技巧在预测对比页面里我把最近 14 天的误差按 24 小时时段画成一张热力图运维人员可以一眼看到模型在傍晚时分误差最大。这个 3 行代码就实现的功能是整个系统里被电站运维人员点赞最多的功能。数据可视化很多时候不是把信息堆得越多越好而是把模型真实的不确定区域清晰呈现出来这才是预测系统真正该做的事情。本文还有配套的精品资源点击获取