
做风电预测的人都知道数据比模型难搞。我最近在处理一个西班牙风场数据项目核心任务是先对风场风速做短中期预测再基于风速结果做功率预测最后把整套流程整理成一套能接入任意风场历史数据的分析管线。目前这个方案在验证集上的风速RMSE能稳定在0.6 m/s附近功率NMAE约7%踩过的坑比想象中多。这篇文章我会把从数据清洗、特征构造、模型选型到评估部署的完整过程写下来不管你是刚接触风电数据分析还是想把自己的数据迁移进现有模型都可以直接参考。1. 项目背景与任务拆解1.1 这次需求到底是什么从项目标题看这次要做的事情有两件一是基于风场历史观测数据做风速预测二是把风速预测结果转换成功率预测同时还要保留“用户自带数据也能跑”的入口。实际项目中风速预测和功率预测经常被放在同一个任务里但它们的建模逻辑并不完全一样。风速是连续变化的天气变量受大气过程影响功率则是风速经过风机功率曲线和运行控制策略映射后的结果。如果直接把功率当回归目标模型可能会学到一些奇怪的偏置比如低风速下风机为了调频而保持出力的模式这在某些风电考核场景里会带来很大误差。1.2 西班牙风场数据有什么特点这次拿到的数据来自西班牙沿海某风场时间粒度是10分钟包含机舱风速计、测风塔、功率、温度、气压等字段。西班牙的风资源在欧洲范围里属于比较优质的夏季白天受海风影响风况相对稳定冬季则容易出现大西洋锋面过境带来的风速快速抬升。和国内很多内陆风场相比这类沿海风场的风切变和阵风强度更大风向变化也更频繁。这些气象特征会直接影响预测难度如果只用历史风速做时间序列外推在天气过程转换的时段很容易出现连续低估或高估。所以在模型设计时我特意把风向变化率和湍流强度这两个特征放了进去后面会详细讲。1.3 为什么先风速再功率我见过不少项目直接用一个模型从历史功率预测未来功率这种做法在数据平稳时也能跑出不错的结果但有一个致命问题功率序列是非平稳的受风况和机组启停影响直接建模往往会把“上一时刻功率”当作最强特征导致预测曲线滞后。先预测风速再用风速-功率关系映射到功率等于把物理机制显式地放进了模型。这样即使风速预测有偏差我们也能在功率环节做残差修正而不是让模型黑盒地自己找规律。对于需要向调度部门解释预测结果的风场来说两阶段方案的可解释性也更好。2. 数据准备与预处理2.1 先认识手里的数据开始前先盘点字段。典型一条10分钟记录包括时间戳、机舱风速、测风塔风速、风向、有功功率、无功功率、发电机转速、桨距角、环境温度、气压。实际项目里可能还有来自数值天气预报NWP的预报风速但这次西班牙风场数据没有公共NWP所以我主要用的是历史观测序列做滚动外推。如果你自己带的数据也没有NWP别急着上复杂模型先把历史数据整理成时间序列样本即可。建议把原始数据统一成CSV每一行是一个时间点缺失值先保留不要提前填充方便后面核对。2.2 清洗的三个关键坑第一坑是机舱风速计受叶轮扰动。机舱后面的风速计测到的风速在风机正常发电时会偏小在停机时会突然变大如果直接用这个字段做训练模型会被搞糊涂。我的做法是把机舱风速和测风塔风速做对比偏差超过合理范围的样本直接剔除。第二坑是限电和停机记录。有些时段功率恒定在额定值以下不是真实气象出力而是被调度限了负荷这类点如果不删掉模型会学到“风速高但功率低”的错误映射。第三坑是传感器冻结或漂移冬季风速计结冰会造成连续0值需要结合温度和时间连续性判断。这些坑虽然基础但处理不好后面模型调参全是白费。2.3 特征工程别只把历史风速堆进模型时间序列预测最简单的特征就是滞后风速比如过去6个时刻1小时的风速、风向和功率。但只做滞后会带来“镜子效应”模型倾向于输出近似上一时刻的值。我额外加了四类特征第一类是时间特征包括小时、月份、一年中的天数用正弦余弦编码避免0点和24点的跳变第二类是风况特征包括风向正弦/余弦、风速一阶差分、风向变化率、近1小时风速最大值第三类是湍流强度用10分钟风速标准差除以平均风速这一步能捕捉阵风影响第四类是功率曲线相关的非线性特征比如风速的三次方、切入风速以上的有效风速。特征不是越多越好我用LightGBM的feature importance筛过一轮最终保留了18个特征。2.4 数据划分要守住时间顺序标准机器学习里的随机K折交叉验证在时间序列场景下是错的因为相邻时刻的样本高度相关随机拆分会让模型偷看未来信息。我采用的方案是按时间顺序切分前70%做训练中间15%做验证最后15%做测试。验证集用于早停和超参选择测试集只评估一次。另外预测一条完整序列时输入窗口里不能混入待预测时段之后的数据。具体做法是把时序数据转换成监督学习格式用前N个时刻的特征预测后M个时刻的目标窗口滑过整个数据集时就形成了独立样本但样本之间仍然有重叠所以交叉验证时必须按块切分不能用shuffle。这一步是很多人踩漏的地方。3. 风速预测建模3.1 先跑一个持续法当基线持续法在风电领域被称为persistence baseline含义是“现在风速就是未来风速”尤其对15分钟到1小时的短临预测持续法效果出奇地好。我通常用它做下限如果某个复杂模型连持续法都打不过那就说明特征或训练过程有问题。针对24步4小时预测持续法的RMSE大约在0.9 m/s左右LightGBM能降到0.7LSTM能到0.65。需要说明的是这个数值只对这次西班牙沿海数据有效换成复杂地形风场可能完全不同。但流程是通用的先搭基线再迭代。模型1小时RMSE (m/s)4小时RMSE (m/s)持续法0.620.91线性回归0.580.81LightGBM0.440.67LSTM0.420.643.2 LightGBM的窗口构造与训练对多步预测我选择直接多步策略而不是递归多步。直接多步就是为每一个预测步数训练一个模型比如预测未来24个10分钟点就训练24个LightGBM模型。这样做的好处是误差不会随步数累积缺点是训练时间增加但风速预测的特征维度不大24个模型也很快。输入样本结构为特征矩阵X包含当前时刻往前12个时间点的风速、风向、温差等标签y是未来第k个时刻的风速。窗口长度12对应过去2小时经过对比这比用6或24的效果更稳。训练时用验证集早停learning_rate0.05num_leaves31min_child_samples20。import lightgbm as lgb def build_samples(df, features, window12, horizon12): X, y [], [] data df[features].values for i in range(window, len(data) - horizon): X.append(data[i - window:i].reshape(-1)) y.append(data[i horizon - 1][0]) # wind_speed 排在 features 第一位 return np.array(X), np.array(y) X_train, y_train build_samples(df_train, feature_cols, window12, horizon12) X_val, y_val build_samples(df_val, feature_cols, window12, horizon12) model lgb.LGBMRegressor( learning_rate0.05, num_leaves31, max_depth6, min_child_samples20, n_estimators2000, ) model.fit( X_train, y_train, eval_set[(X_val, y_val)], callbacks[lgb.early_stopping(50), lgb.log_evaluation(100)], )3.3 LSTM/TCN到底有没有必要很多团队一上来就上LSTM但我的经验是在10分钟粒度的短临风速预测上LightGBM往往就能达到和LSTM差不多的精度LSTM的优势更多体现在长序列和捕捉复杂时间依赖上。如果你用LSTM需要把样本整理成三维张量(样本数, 时间步长, 特征数)。这里时间步长我选24也就是过去4小时特征只有风速、风向正弦、风向余弦、温度。网络结构不要堆太深单层LSTM 64或两层32就已经足够过深的网络在小样本下容易过拟合。训练时要注意输入特征和目标都要做归一化我的做法是用训练集的均值和标准差做标准化保存这两个统计量预测时用同样的统计量还原。3.4 超参调整与实战心得LightGBM的关键超参其实不多num_leaves控制模型复杂度min_child_samples控制过拟合learning_rate和n_estimators配合早停。LSTM里更关键的是batch size和learning rate我一般用Adam初始学习率0.001每隔10个epoch乘0.5batch size取256。另一个容易忽略的点是随机种子像LSTM这类模型在不同种子下结果波动能达到0.03 m/s所以在对比模型时一定要固定种子否则你以为是模型差异其实是随机性。我还试过把持续法和LightGBM做简单集成即0.3持续法0.7模型输出短临预测的综合误差反而更低算是一个性价比很高的小技巧。4. 功率预测建模4.1 风速到功率先做曲线查表标准风功率曲线给出的关系是一条S形曲线在切入风速3 m/s附近功率开始爬升在额定风速附近达到额定值。拿到风速预测后最简单的方式是用风机厂商的标准化功率曲线查表得到功率预测。问题在于厂商曲线是在理想条件下测的实际风场有空气密度、地形、尾流、控制策略等影响直接查表会有系统性偏差。所以我先对历史数据做了bin法经验功率曲线把风速按0.5 m/s间隔分箱每个箱内取功率中位数再用插值生成连续曲线。这样处理后的经验曲线比厂商曲线更贴近这个风场的真实出力特性。4.2 直接预测功率和两阶段预测怎么选我最后采用两阶段第一阶段预测风速第二阶段把风速带入经验曲线得到基础功率再训练一个残差模型修正偏差。为什么不直接训练功率回归模型因为直接模型在低风速段容易把功率预测成负值或超过额定值而且遇到新场数据时需要重新拟合大量参数。经过残差修正后预测功率的NMAE比直接用厂商曲线低了约2个百分点。残差模型的特征包括预测风速、当前实际风速、风向、温度、湍流强度、所在风速区间的虚拟变量。目标变量是实际功率与经验曲线功率之差。训练好残差模型后最终预测功率 经验曲线功率 残差预测。4.3 特殊工况夜间、切变、尾流夜间大气层结稳定风速垂直梯度变大机舱风速和轮毂高度风速的差距会更明显如果只用测风塔数据建模夜间容易整体偏低。我的处理是在特征里加入小时和温度让模型自己学习夜间的偏移。切变问题主要是风向变化过快时风机偏航跟不上实际功率会比稳态偏低这靠的是把风向变化率作为残差模型特征。尾流效应在大型风场里很显著上游风机遮挡会让下游风速降低、湍流增加如果数据里有每个机组的独立发电量可以按机群分组建模但本次只给了场级数据所以我在特征里加入了风向扇区与风速的交互项作为尾流影响的间接代理。5. 模型评估与结果分析5.1 指标选择风速预测常用RMSE、MAE、R2功率预测常用RMSE、MAE和NMAE。NMAE是归一化平均绝对误差分母通常用装机容量这样不同风场之间可以比较。我这次用5.2 MW的场级装机容量测试集整体风速MAE为0.43 m/sRMSE为0.61 m/s功率MAE为0.35 MWNMAE约为6.8%如果只看功率RMSE则约0.52 MW。这些数值只能代表该西班牙沿海风场的短临预测水平。不同时段、不同季节差异很大评估时一定要分层看。我每次汇报结果都会同时给出平均指标和分箱指标否则很容易被一个好数字蒙混过去。5.2 分风速区间和分时段看误差把测试集按预测风速分成4、4-8、8-12、12 m/s四个区间结果很有意思低风速段风速RMSE只有0.3 m/s但功率NMAE反而最高因为功率曲线在切入风速附近很陡风速只差0.5 m/s功率可能差出几十千瓦高风速段风速RMSE较大但功率已经在额定值附近功率误差反而小。按时段拆分夜间误差比白天高15%左右原因是夜间热力湍流减弱、风切变增强。这张表也验证了一个道理用同一个模型处理所有工况必然有偏分层评估能帮你找到下一步优化方向。工况风速RMSE (m/s)功率NMAE (%)全天0.616.8白天(8-20时)0.575.9夜间(20-8时)0.667.9低风速(4 m/s)0.3110.2中低风速(4-8 m/s)0.488.3高风速(12 m/s)0.794.65.3 一次24小时滚动预测复盘为了验证模型在真实离线预测中的表现我选了冬季某天做了一次24小时滚动预测每10分钟更新一次输入预测未来2小时风速和功率。结果上午10点左右有一次锋面过境实测风速从7 m/s快速升到13 m/sLightGBM预测有明显滞后2小时误差达到1.8 m/s但因为有滚动更新实际输出曲线只晚了20分钟后面快速修正。功率预测相应偏低NMAE一度到14%。这说明短临预测对天气过程突变天然不敏感后续可以加入NWP或者气象自动识别来改善。这次复盘让我认识到任何离线指标都不能完全代表紧急天气过程的真实表现。6. 如何用自己的数据跑这套模型6.1 代码目录怎么组织为了让项目能被其他人复用我把整个流程按五个目录组织config存放参数文件data放原始数据和清洗后数据features放特征工程脚本models放训练好的模型和评估脚本output放预测结果和图表。配置文件用一个yaml文件描述列名、采样频率、预测步数和模型超参这样换一个新风场时只需要改配置不用改代码。如果你只打算复现某一部分建议也从配置化开始哪怕只有一个脚本也把数据路径和列名抽出来不要写死。我自己的经验是刚开始不觉得有必要一旦数据源换了写死列名的代码会让你改到怀疑人生。6.2 数据格式统一与重采样想带入自己的数据最少需要四列时间戳、风速、功率、风向。如果还有温度、气压和湍流强度模型效果会更好。原始数据采样频率不一样也没关系统一重采样成10分钟平均这一步用pandas的resample(10min, labelright, closedright).mean()即可。列名映射最好写一个rename字典兼容中文列名和英文列名。重采样后的缺失值不要立刻填0先看缺失比例小于3%可以线性插值或前向填充超过30%的字段干脆删掉。尤其注意如果风场停机检修时间长功率会有大段零值这些要单独打标不能简单当成数据缺失。6.3 直接重新训练还是微调如果你带来的数据和西班牙风场数据差异较大建议直接用你的数据重新训练。模型结构不用变但标准化参数、特征均值、风速区间都需要重新统计。如果数据量特别少比如只有两个月的记录可以考虑用预训练模型的部分权重做迁移学习但这种做法在表格型时间序列上效果有限我更推荐用小数据集训练一个简单LightGBM往往比强行用LSTM迁移更稳。我的项目里预留了一个train_from_scratch开关设为true时会自动重算特征和模型false时则加载已有模型预测。对于大多数场景重训比微调更省心。6.4 结果输出和可视化模型跑完至少要输出三个东西预测结果CSV、误差统计表、预测曲线对比图。CSV字段建议包括时间戳、实际风速、预测风速、实际功率、预测功率、风速误差、功率误差。误差统计可以按日汇总便于看出哪几天模型表现差。画图时用两条线分别画实际和预测再加一条灰色条带表示置信区间如果只是散点图很难看出滞后问题。我强烈建议把预测结果按时间顺序画一条长曲线而不是只画测试集前100个点否则你很可能会漏掉拐点处的滞后。这一步做好之后整个项目才算闭环。7. 常见问题及排查技巧7.1 训练误差低但测试误差高典型症状是训练集R2很高验证集和测试集断崖下降。原因多半不是过拟合而是未来信息泄漏。常见泄漏点有三个一是特征工程时用了整个数据集计算均值或方差做标准化二是划分样本前用了shuffle三是在构造滑动窗口时特征中包含了预测时段之后的数据。排查方法是随机抽取验证集一个批次手动检查特征矩阵里的时间戳和标签对应的时刻是否有重叠。另外如果用标准化必须只对训练集fit再transform验证集和测试集。7.2 预测曲线整体滞后如果你看到预测曲线比实测曲线晚半个到一个采样周期说明模型把上一时刻值当作最大特征了这在风速和功率预测里都很常见。持续法本身就是一个极端滞后的模型。想降低滞后需要引入外部预报变量或者使用损失函数对变化方向加权。我在LightGBM里加入了一阶差分特征后滞后缓解了不少。还有一个办法是直接对风速变化量建模预测出delta后再加到当前风速上相当于让模型更关注趋势而不是数值本身。这个方法我测试过多步预测时RMSE大约能再降0.03 m/s。7.3 低风速段功率误差特别大风速在切入风速附近时功率曲线斜率最大同样的0.3 m/s误差会造成几十千瓦偏差。解决方案不是换模型而是对功率目标做非线性变换或者分桶模型。我试过在低风速段单独训练一个残差模型并把风向扇区作为强特征效果明显。更深层的问题是切入风速附近可能出现风机启停的滞后不同机组的切入策略不一样单一场级数据很难完全刻画这种情况下你只能接受一部分不可约误差不要过分追求指标。7.4 极端天气时预测完全失真一次寒潮过去风速从5 m/s飙到18 m/s我的模型预测只有8 m/s这在训练集里是极小概率事件模型没有被强制学出来。解决思路是用数据重加权把训练集中风速大于15 m/s的样本权重提高另一个更实际的办法是建立异常天气识别器当风向变化率和风速变化率同时超过阈值时自动切换到持续法或人工修正。神经网络还可以在训练时做数据增强给特征加噪声模拟极端样本但效果取决于模型容量。不要期待模型能完美外推工程上保证恶劣工况下不太离谱就够了。7.5 上线后指标和离线不一致这是最让人头疼的问题。原因通常是线上数据质量与离线不同缺失值填充方式不一致、实时值没有经过同样的重采样、某些字段在线上延迟到达导致特征为空。我的处理是在预测服务入口增加数据校验不满足要求的样本打上低置信标记不参与上报。模型保存时也把特征列名顺序固定避免上线后列顺序错乱导致预测结果完全随机。另外每周用新到的数据重训一次模型防止风场特性随季节漂移。这些都是很小的事情但每一个都能毁掉整体精度。最后想分享一个真实的感受这个西班牙风场项目里最后提升误差最明显的不是LSTM也不是调参而是花了一天时间把清洗逻辑从“看着没问题”改成“每个异常点都有原因”。第一次跑出结果时功率NMAE是9%怎么调都降不下来后来发现是限电时段没删干净删掉那3%的样本后指标直接降到7%。如果你在做类似预测我建议至少留出20%的时间去检查数据标签的质量而不是急着让模型跑起来。数据里藏着的每一个“为什么”最后都会变成模型输出里的一个误差。