西班牙电价预测实战:基于TCN与SHAP的深度学习完整链路
1. 为什么偏偏是西班牙电力市场可再生能源高渗透下的价格困局西班牙电力市场这几年几乎是全球电价预测研究者的“天然实验室”。原因不复杂这个市场的可再生能源装机比例非常高风电和光伏出力在总发电量中经常能占到一半以上而这两类电源又天然具有极强的间歇性——晴天、大风天和阴天、静风天的发电曲线完全是两回事。电价在这种供需结构里变得极其敏感波动幅度远超传统火电主导的市场。我最早接触这个课题时印象最深的是2022年到2023年那段时间的极端行情。批发电价一度冲到每兆瓦时200欧元以上而某些光伏大发的中午时段又会瞬间跌到接近地板价甚至偶尔出现负电价。这种“一天之内冰火两重天”的走势给传统统计模型ARIMA、GARCH这类带来了近乎毁灭性的打击——它们假设价格序列具有某种平稳性或至少可预测的波动结构但西班牙市场的数据告诉我这个假设十次有八次不成立。那深度学习模型为什么能扛住这种波动核心在于它不依赖对数据生成过程的强假设。LSTM、TCN这类模型只需要大量的历史样本就能自动学习到“光照强→光伏出力大→午间电价被打压”这类隐含的因果关系哪怕是滞后若干小时、嵌套了复杂的天气耦合效应也能通过多层非线性变换逼近。加上电价预测本质上是一个时间序列回归问题输入特征可以做得非常丰富——历史负荷、历史电价、新能源预测出力、日历变量、燃料价格甚至相邻法国市场的电价——这对数据驱动模型是天然的利好。这篇文章要分享的就是我在“西班牙电价预测”这个研究方向上完整的实操链路从数据获取与清洗、特征工程、模型选型与训练到SHAP可解释性分析的落地写法。目标读者是那些已经跑过几个简单回归模型、想进一步把深度学习真正用在实际电力市场数据上的朋友。我会把踩过的坑、试错过但最终放弃的方案、以及“为什么要这么做”的底层逻辑一起讲清楚不绕弯子直接上干货。2. 数据准备环节ENTSO-E透明平台与特征工程中的隐藏陷阱2.1 用公开API拿到靠谱的市场数据做电价预测第一步最难的不是建模而是拿数据。西班牙电力市场的数据源主要分两类一类是西班牙电网运营商的公开平台直接提供日前市场Day-Ahead Market的清算价、系统负荷、新能源实际出力等字段另一类是欧洲范围的电力数据透明平台ENTSO-E覆盖面更全还能同时拉取邻近市场的价格。我的建议是两个源都接以ENTSO-E为主以本土平台作交叉校验。从实操角度ENTSO-E的API走的是RESTful接口需要先注册申请API Key申请理由填学术研究或市场分析一般都能过审。拉数据有一个很烦的机制大部分数据不是实时开放而是延迟三天到一周才完整归档所以要做历史回测或者训练集构建需要耐心把时间窗口拉开。我当时写了一个封装脚本用requests逐月拉取遇到限流就sleep几秒再继续整个历史数据集大概花了一晚上跑完。字段选择上我做了这样的裁剪变量类别具体字段时间粒度目标变量日前市场清算价€/MWh1小时负荷类系统总负荷、可控负荷1小时新能源类风电实测出力、光伏实测出力1小时机组组合类燃煤/燃气/核电出力1小时市场关联类葡萄牙市场电价、法国市场电价1小时日历类星期、节假日标记、小时序数1小时这里有个很典型的坑ENSTO-E返回的新能源出力数据是“实测值”而电价预测真正想要的是“预测值”——因为预测时点上未来几个小时的风光出力我们是不知道的只能用天气预报或新能源功率预测结果作为输入。如果直接用实测值训练模型无意中“偷看”了未来信息回测分数会非常漂亮但上线表现直接崩塌。这个错误我在早期版本中踩过后来解决方案是用滞后几小时的新能源出力作为特征模拟预测输出的时间滞后性或者干脆引入公开的气象再分析数据比如ERA5自己算理论出力。2.2 时间对齐与缺失值处理看似简单实则致命电力数据天然带着时区和夏令时的坑。西班牙使用欧洲中部时间CET/CEST每年三月和十月要切换夏令时切换当天会多一小时或少一小时。很多公开数据源在夏令时切换日的索引是错位的如果直接pandas对齐会出现“同一时刻两条记录”或者“某时刻为空”。我当时用的方法是统一转成UTC时间戳存储建模时再映射回本地时间并把小时序号单独做成特征。缺失值方面电网数据整体质量不错但还是会有小块的空洞——比如某天的某个采集通道故障或者极端天气导致上报链路中断。一般两小时以内的空洞我用线性插值补超过两小时的直接剔除样本不硬填。尤其注意不要在目标变量电价上做暴力插值因为电价序列的尖峰和跳跃本身就是模型要学习的信号插值会把这些信号抹平。特征工程里还有一个小细节值得展开说一下。除了原始的历史负荷和新能源出力我加了几组构造特征滚动统计特征过去24小时的峰值、谷值、均值、标准差捕捉近期价格波动的状态滞后差分类特征比如“当前时刻负荷与昨日同刻负荷之差”能反映日内变化趋势日历编码把星期、小时用正弦余弦编码避免星期几或小时序号被模型当成连续值处理差分电价特征过去1小时、3小时、6小时的电价变动量。为什么这些手工特征在深度学习里还有必要虽然LSTM理论上可以自己学时序依赖但给模型提供显式的变化率特征能显著降低学习难度尤其在训练数据量有限我用了三年逐小时数据约两万多条样本的情况下特征质量直接决定过拟合程度。实测中加入滚动统计和差分特征后验证集MAPE下降了大约1.5个百分点这个收益在电价预测里已经非常可观了。3. 深度学习模型选型与训练细节从LSTM到TCN的摸索过程3.1 为什么最终选了TCN而不是更火的Transformer我一开始是按照惯性直接上了LSTM毕竟处理序列问题它是老牌选手。但在实际调参过程中我逐渐发现LSTM的两个痛点第一训练速度确实慢尤其在序列长度拉长到96步四天窗口之后batch内的时间步展开非常吃显存第二梯度消失问题虽然通过门控机制缓解了但深层堆叠时训练稳定性仍然不够理想偶尔会遇到损失不下降或剧烈震荡的情况。后来我换成了时序卷积网络TCN。TCN的核心思想是用因果膨胀卷积替代循环结构。因果卷积保证了每个时刻的输出只依赖当前时刻及之前的输入不会“偷看未来”膨胀卷积则通过指数级增大感受野的方式使模型可以用很浅的层数覆盖很长的历史窗口。相比LSTMTCN的训练是并行化的速度至少快三到四倍而且没有循环结构带来的BPTT梯度问题训练曲线平稳得多。至于Transformer我也试过。在序列长度不算特别长96步以内的设定下Transformer的注意力机制和绝对位置编码并没有发挥出碾压性优势反而因为参数量大、数据量不够而更容易过拟合训练时间也更长。电力市场数据的样本量与NLP领域的海量语料完全不是一个量级模型复杂度必须克制。所以最终落地的是TCN 双向LSTM的混合结构先用TCN提取局部时序模式再喂给一个只堆两层的双向LSTM捕捉更长的依赖关系。这个组合在验证集上比单用LSTM降低大约0.8%的MAPE比单用TCN也略有提升。3.2 训练配置与超参数给一个能直接复现的基线如果直接抄我的最终配置大致是这样的输入特征维度27维历史电价、负荷、新能源出力、日历编码、滚动统计、差分特征等时间窗口7天×24小时 168步预测未来24小时即输出24个连续小时的价格TCN层4层初始通道数64每层翻倍到512卷积核大小5膨胀系数[1, 2, 4, 8]BiLSTM层2层隐藏单元128dropout 0.2输出层全连接 线性激活映射到24个值损失函数HuberLossδ1.0对尖峰不那么敏感比MSE稳优化器AdamW初始学习率3e-4配合CosineAnnealingLR调度器衰减Batch大小128早停轮数15最大训练轮数80训练集、验证集、测试集按时间顺序切分前两年半训练接下来三个月验证最后三个月测试。切分时千万不能随机打乱时间序列必须保持顺序否则会造成数据泄漏——模型见过“未来的数据”再来预测“过去的时刻”评估结果没有任何参考意义。Loss曲线在HuberLoss下比较平稳不像MSE那样被个别极端尖峰带着剧烈跳变。我截图跑完的best epoch大概在第47轮验证Loss稳定在0.32左右。测试集上的具体指标是MAE约4.36 €/MWhMAPE约9.8%。考虑到测试集中包含了几次价格大幅波动的事件段这个表现我认为是比较扎实的。3.3 训练过程中踩过的两个ESL坑第一个坑是归一化方式。电价序列的分布是典型的重尾分布——大部分时间价格在30到80欧元之间徘徊但偶尔会冲到150甚至300。如果直接做MinMax归一化异常尖峰会把正常区间的数值压缩到很小的范围模型几乎是在用放大镜看“正常情况”。我最后用的是RobustScaler基于中位数和四分位距对极端值有很强的鲁棒性。训练时目标变量也做了同样的缩放预测输出再反变换回去。第二个坑是训练-预测分布漂移。西班牙市场在2022年经历了能源价格飙升批发电价均值从2021年的60多欧涨到130多欧。拿2021年数据训练的模型直接去预测2022年的电价MAPE会恶化到20%以上。解决办法是滚动再训练每两周用最新一个月的真实数据微调一次模型权重把学习率降到原学习的十分之一只训练5轮。这个策略跟金融里处理波动率聚集的思路如出一辙——不可能有一个静态的“万能模型”能应对持续变化的市场结构动态更新才是唯一的出路。4. SHAP可解释性分析让黑盒模型开口说话4.1 SHAP原理速通从Shapley值到实际的Python实现深度学习模型的准确性有了但研究项目停在“预测得准”是不够的尤其是电力市场这种对决策敏感的场景“为什么预测出这个价”和“预测出什么价”同等重要。评审和业务方都会追问你模型觉得这个价格由什么驱动这就要引入可解释性分析。SHAPSHapley Additive exPlanations是目前最常用的模型解释工具理论基础是合作博弈论中的Shapley值。它的思路是这样假设有一个包含N个特征的合作博弈模型的预测值是这些特征共同作用的结果。要计算每个特征“公平”的贡献值需要考察在所有可能特征集合中加入该特征前后预测值变化的加权平均。直观讲就是把所有特征按不同顺序排列组合逐个“加入游戏”看每个特征在每种排列下带来的边际贡献然后对这些边际贡献做加权平均权重与子集大小有关。从实现角度看SHAP库对深度学习模型的支持已经相当成熟。对于PyTorch模型常用的有两种explainer一种是DeepExplainer专门针对神经网络利用链式法则和误差反向传播思想计算近似SHAP值速度快但精确度略低另一种是GradientExplainer同样适用于深度学习模型基于期望梯度方式。如果模型本身是梯度提升树XGBoost、LightGBM那就直接用TreeExplainer那是精确计算性能极好。我在这篇研究里用的是DeepExplainer因为模型规模不大计算成本完全可以接受。4.2 SHAP可视化的四种核心图以及我实际解读出来的结论SHAP的输出不是一张图就完事它是一整套分析体系。我实际使用过程中最有价值的四种可视化方式做个梳理全局特征重要性Bar Plot把所有样本各特征的|SHAP值|取平均后排序反映“平均而言谁对预测值的绝对影响最大”。特征影响方向Bee Swarm Plot即蜂群图每个样本是蜂群图上的一个点横轴是SHAP值正负代表推高/拉低预测电价颜色代表特征值高低能直观看到“特征值越大越推高价格”还是“特征值越极端影响越强”这类非线性关系。单样本解释Waterfall Plot瀑布图针对某一个具体预测时刻从上到下展示各特征如何从基准预测值一步步加加减减得到最终预测值。交互作用分析Interaction Values两个特征的交互贡献例如“光伏出力”和“时刻”之间的相互作用——相同光伏出力水平在午间和傍晚对电价的影响方向截然不同。我跑完整个测试集后全局特征重要性的排序结果非常有说服力。不出意外历史同小时电价前一天同一时刻的清算价和系统负荷排在前两位两者合计贡献了超过50%的全局影响。这符合电力市场的基本规律电价具有很强的自相关性和负荷驱动性。真正让我意外的是光伏出力的SHAP模式——它在午间时段是负贡献拉低价格但在晚上7点到9点的峰荷时段光伏出力特征的SHAP值反而出现了正向偏移。原因其实不复杂晚高峰时光伏几乎不发电所以当模型检测到那一刻的光伏出力“异常地高于凌晨水平”比如夏季长日照导致傍晚还有残余出力它会推断电力供需紧张程度比往年同期要低反而降低了价格预测。这个“特征值与影响方向不一致”的现象恰恰是线性模型完全无法表达、而SHAP能拆解出来的非线性关系。负荷类特征的表现也很有意思。系统总负荷的SHAP值呈现明显的U型关系——低负荷时段凌晨三四点和高负荷时段晚上八九点都会强烈影响最终预测值但方向相反。区别在于凌晨低负荷往往对应着风电大发夜间风大是欧洲常见的天气特征电价被压到很低的水平这里“负荷低”本身就是一个强信号而晚间高负荷时即使价格冲刺到高峰模型给出的预测不确定性也更大因为这时候天然气价格、碳排放成本的传导效应更强这些外部因素在我的特征集里没有直接覆盖模型只能通过历史同期的价格模式间接推断。4.3 用SHAP反哺特征工程的几个实例可解释性分析不只是用来“画几张图放报告里”它的更实际价值是反哺特征工程和模型迭代。我举两个例子说明怎么用SHAP做特征筛选和矛盾识别。第一我在初始特征集合里放了一个“节假日标记”的0/1变量。SHAP的全局重要性排序里它的|SHAP|均值排在倒数几位几乎不起作用。进一步看它的蜂群图发现节假日和非节假日的SHAP分布几乎没有重叠说明模型确实没有从该特征学到差异化信息。分析原因后发现西班牙的节假日分布与北欧国家不同很多假日是宗教节日并不一定伴随工商业用电的显著下降比如圣诞节期间虽然工商业放假但家庭取暖用电反而上升。所以我后来把这个布尔标记改成“非工作日连续天数”和“假期前/后一天”两个连续特征再跑一遍SHAP重要性明显上升验证集MAPE降了0.6个百分点。第二利用SHAP交互值发现了“风电出力”和“葡萄牙电价”之间的冗余关系。初始特征里我同时放了西班牙本土的风电出力和葡萄牙市场的日前电价。交互分析显示这两个特征的联合SHAP值分布几乎与“仅使用葡萄牙电价”时的效果重叠说明葡萄牙电价已经隐式包含了西班牙风电大量出力时向葡萄牙输电导致的互联价格走低信息。结论是把风电出力从特征集里移除模型验证精度不变但特征维度从27降到26训练时间缩短了8%。这种“特征去冗余”单靠相关性分析看不出来只有SHAP交互分析能把信息重叠暴露得这么清楚。5. 结果分析、误差透视与业务层面的落地建议5.1 误差不是均匀分布的分时段误差分析只看平均指标会给决策者造成错觉。我把测试集按小时拆分逐一计算每个预测时刻的MAE发现了非常明显的时段差异预测时刻当地MAE€/MWhMAPE%典型场景0:00 - 4:002.816.1低负荷风电主导价格平稳4:00 - 8:003.979.0负荷爬坡启动燃气机组开始竞价8:00 - 12:005.1211.3光伏爬坡价格被压缩但波动加大12:00 - 16:003.669.5光伏大发午间低谷价格16:00 - 20:004.8410.2光伏衰减价格抬升启动晚高峰20:00 - 24:006.7315.8晚高峰不确定性最大晚间时段的误差显著偏高原因不难理解这个时段的价格不仅受西班牙国内供需影响还深受法国市场的跨区输电态势左右——法国核电晚高峰的可用性、跨境联络线容量约束都可能成为价格跳变的触发器。但这些信息在我当前的特征集里仅有一小部分通过“法国日前电价”体现出来远远不够。如果后续要提升精度需要引入联络线可用容量、法国负荷高峰预测、以及更细粒度的天气变量风速预报的空间分布而非全国平均。从误差分布形态看模型在价格尖峰时刻的系统性低估现象也比较明显。测试集里出现了两天价格超过180 €/MWh模型在这两天的预测值分别只有131和117低估幅度超过35%。归根结底极端尖峰往往由突发事件驱动比利时核电机组跳闸、法国核电站检修叠加寒潮、天然气供应扰动而这些事件在训练历史中的出现频率极低任何数据驱动模型都难以提前捕捉。这是模型天花板的问题不是调参能解决的。5.2 落地场景差异交易策略、电网运营和风险管理的不同用法同一个预测模型不同角色的用法完全不同。对于能源交易团队核心关心的是方向性准确率和价格区间边界。我的实测经验是模型在“判断明天午间价格低于清晨价格”这类相对排序问题上准确率超过92%这比绝对价格预测的精度更有交易价值。交易员完全可以根据预测价格曲线设计日前市场的购电策略——在预测低价时段购入高价时段卖出或减少购电配合储能充放电策略能有效规避尖峰时段的购电成本。对于电网调度部门预测的价值更多体现在场景多样性上。一个点预测不够用他们更需要的是分位数预测或区间预测。我在后期测试中把模型的输出层改成24个分位数头用分位数损失函数训练生成10%、50%、90%三个分位数区间用于评估“价格走势是否存在极端尾部风险”。比如在预测到晚间负荷压力较大的日子90%分位数价格比中位数高40%以上这就是一个强烈的风险提示信号调度可以提前预留更多的储备容量。站在研究复现的角度我的建议是不要一上来就追求“全网最精准”的SOTA模型先把完整的“数据整理→特征工程→深度模型→SHAP解释”的闭环跑通。这个闭环里最值钱的能力不是某个特定模型结构或某层参数而是对数据特征与市场价格形成机制之间关系的理解能力——理解为什么光伏负贡献、为什么晚间误差大、为什么节假日特征无效这些能力可以迁移到任何其他电力市场比如法国市场、北欧市场或者国内某些省份的现货试点市场。根据我个人在这些踩坑过程中的体会SHAP分析不仅在论文和报告中好看最大的作用是逼着我去重新审视那些“想当然合理”的特征。没有SHAP我的光伏出力特征和节假日标记很可能就一直糊涂地放在训练集里模型也能收敛指标也不难看但你知道这里面有多少是碰运气。模型精度重要但只有配合可靠的归因逻辑才能让研究结论经得住推敲也才能在真实业务场景里形成有效的决策支撑。