基于DBSCAN的风电-负荷场景削减方法详解

📅 发布时间:2026/10/22 5:22:09
基于DBSCAN的风电-负荷场景削减方法详解
做风电并网分析的时候我一度被场景库的数量折磨得不轻。蒙特卡洛随手抽出上千个出力场景并不难难的是这些场景丢进机组组合或经济调度模型之后求解器直接被卡到怀疑人生。后来想明白一件事随机规划要的不是场景多而是场景能代表不确定性的真实分布。用聚类把相似的场景合并只留下最典型的那一个整个过程就叫场景削减。我试过K-means也试过层次聚类直到上手DBSCAN密度聚类才发现它对风电-负荷这种“分布形态不规则、离群点又不少”的场景做削减反而比传统方法更顺手。这篇文章就围绕基于DBSCAN的风电-负荷场景削减方法把思路、数据准备、参数整定、操作步骤和评估手段完整拆开讲一遍给准备做随机规划建模的同事留一份可抄的作业。1. 场景削减不是预处理是随机规划里抽筋拔骨的那一刀1.1 随机场景为什么要同时覆盖风电和负荷风电出力与负荷的不确定性本质上是一个联合概率空间。如果我单独对风电出力做削减再单独对负荷做削减然后交叉组合场景数量会爆炸性增长而且风电与负荷之间的相关性会被彻底丢失。举个例子冬季晚高峰负荷很高但风电出力可大可小午间光伏强、负荷也高风电可能处于反调峰状态。如果独立削减很容易生成“风电出力大同时负荷也大”这种叠加极端场景并且在概率上被高估。实际上这种组合也许只存在于某个特殊天气过程里出现的概率远低于独立削减模型暗示的值。因此场景削减必须把同一个随机时刻的风电功率负荷功率当成一个联合样本或者把一天24小时的出力曲线拼成一个向量再做聚类和削减。只做单侧削减再组合的办法在规划层面会产生偏差甚至会让后续的机组组合结果过于乐观或过于保守。项目里只要涉及到风电和负荷同时随机我都建议直接处理联合场景集不要图省事分开削。1.2 传统K-means削减为什么不够用K-means在做场景削减时有个先天问题你得先告诉它聚成多少类也就是K值。在场景集误差可控的前提下最优K并不知道通常要试好多个K然后看轮廓系数或肘部图。可试出来的K只是“平均意义上”的簇数它不一定能覆盖那些小概率的极端场景。更麻烦的是K-means以欧氏距离平方和最小化为目标偏好大小相近的球形簇而风电场景经常是“一大坨常规场景 零星几条极端场景”的形态K-means会把离群点强行并进某个簇极端场景就这么被抹平了。对安全校核来说极端场景恰恰是最不能丢的。DBSCAN不需要指定簇数还能把低于密度阈值的样本直接标为噪声这正是场景削减最需要的弹性。它给你保留极端场景的主动权而不是像K-means那样强制“所有点都要有归属”。从这一步开始我就彻底转向了密度聚类。对比项K-meansDBSCAN簇数是否需预设必须指定K自动发现离群点处理强并入最近簇标记为噪声可保留或剔除簇形状假设偏向球形任意形状对极端场景的覆盖差可通过保留噪声点来覆盖参数整定K值eps、min_samples这个表不是唱衰K-means它仍然是场景削减的常用基线但在风电-负荷场景这种“正常工况相对聚集、极端工况零散分布”的数据集上DBSCAN的密度判定逻辑显然更占优势。2. DBSCAN的密度聚类逻辑怎么跟场景削减一拍即合2.1 从密度的角度理解“相似场景”和“离群场景”DBSCAN的核心是密度连通性。在一个半径eps内至少包含min_samples个点这个点就是核心点核心点之间可以通过密度可达关系连成一片形成簇不满足任何簇密度条件的点就是噪声点。对应到场景削减里“密度高”意味着这些场景代表的出力-负荷组合频繁出现值得用一根代表性场景去替代“密度低”意味着这个组合是个小概率事件可能是极端天气或特殊运行工况不能随手丢掉。我最早接触密度聚类时觉得这个概念不像K-means那样直接。后来用生活类比才顺过来你在城市里找吃饭的地方热门商圈人流密集走几步就有一堆餐厅这是“核心区”从核心区向外散步连锁小吃店一家接一家慢慢稀落这是“边界”最后走到郊区周围啥也没有那就是“噪声点”。场景削减就是把这套地理思维搬到多维空间里密度高的区域用一个代表场景代替密度低的点单拎出来特殊处理。2.2 自动定簇数与噪声识别正是削减最需要的弹性实际削减操作中最爽的一点就是不用提前定簇数。我把1000个场景丢进去DBSCAN会根据数据天然分布给出簇数比如30几个簇加上10来个噪声点。如果想让削减后的场景覆盖更多极端工况就把噪声点原样保留每个噪声点作为一个小概率场景权重设为1/N如果只是为了降低调度模型计算量可以直接剔除噪声点让代表场景更干净。这种“低于密度阈值就挂出来晾着”的特性比K-means“无论离多远都必须分给一个簇”的强扭式做法灵活得多。当然密度阈值eps、min_samples需要整定这是实际工程中最花时间的环节。也正因为有这个灵活性我们可以在削减后的场景集里既保留高频典型场景又不丢失低频极端场景兼顾经济性和安全性。这条在后面的算例里会体现得很明显。3. 联合场景怎么构造数据准备里最容易翻车的地方3.1 场景向量设计24小时序列拼接还是特征化最常见的做法是把一天的风电出力和负荷出力曲线首尾相接拼成一个48维向量。这样保留了时间相关性比如风电的夜间高发时段和负荷的晨峰晚峰形态都完整保留。缺点也很直接维度高欧氏距离在高维空间里的区分度会下降两个场景之间的差异会被淹没在大量维度里。另一种做法是先做特征提取把24小时的风电和负荷各自压缩成几个统计量比如均值、标准差、峰值出现的时刻、峰谷差等然后用这些低维特征去聚类。特征化之后聚类速度很快也有利于参数整定但会丢失曲线形状信息——两条一天的均值同为200MW的风电曲线一条从早到晚平缓另一条中午飙到600MW然后又跌回零在特征空间里可能被当成一个场景。对需要做时序出力模拟的场景削减来说丢失形状无法接受。我的折中做法是如果削减后的代表场景要直接回填到机组组合或生产模拟模型就老老实实用完整的24小时序列拼接如果只是快速想看模式、做初步分类再考虑用特征向量。第三种折中是用PCA把48维降到10~20维兼顾速度和形状信息这个后面细说。3.2 标准化和降维量纲不一致会让DBSCAN直接失效风电功率的单位是MW负荷的单位也是MW可量级可能差很多。比如某风电场容量100MW区域负荷峰值3000MW如果不做标准化两个场景的差异会被负荷主导风电出力的波动对距离的贡献微乎其微聚类结果基本等于按负荷分类那等于没做风电侧的分析。所以每一维都要做z-score标准化也就是减掉该维均值再除以标准差或者做Min-Max归一化。我习惯用z-score它对离群值不太敏感。标准化之后还有一层问题48维向量中很多维度高度相关比如相邻小时的负荷本来就差不多直接用欧氏距离会受到冗余维度的影响导致eps半径变得很难选。我通常先用PCA把48维降到10~20维保留90%以上方差再做DBSCAN。降维后的数据密度结构更清晰eps选取稳定很多还能顺带滤掉一部分测量噪声。这里必须提醒一句标准化用的均值和方差要在全部初始场景样本上计算而不是在削减后的代表场景上计算否则会造成信息泄漏评估结果虚高。这是我最早踩过的一个坑。4. 用DBSCAN做削减的完整流程从距离矩阵到代表性场景4.1 距离矩阵计算欧氏距离和它的加速方式DBSCAN算法内部需要频繁做邻域查询。如果距离度量为欧氏距离可以直接用scikit-learn里的DBSCAN它底层用KD树或Ball树做最近邻搜索不用显式构造完整距离矩阵速度很快。但如果距离度量不是欧氏的比如用动态时间弯曲DTW就得自己算距离矩阵再扔给fit_predict。DTW能处理曲线相移的问题比如两段风电曲线形状相似但峰值出现时间偏差了两小时欧氏距离会认为它们差很多DTW则认为它们足够接近。不过DTW的计算复杂度很高1000个24维场景还能忍5000个以上就非常吃力。我的实际建议是先用PCA降维到10维左右然后用欧氏距离速度和效果都比较平衡。只有测试发现场景集存在明显的错峰模式时再考虑用DTW配合自己的场景削减流程这时候最好控制场景总量在2000以内。4.2 eps与min_samples的整定k-distance曲线实操这是整个流程里最讲究的一步。min_samples按经验通常取2倍维度到3倍维度但我们的场景向量降维后还有10维以上取5到10比较合理。eps的取值我用k-distance曲线辅助判断对每个点计算到它的第min_samples个最近邻居的距离把所有点的这个距离升序排列画成曲线选择曲线从“平缓”变“陡峭”的地方对应的距离作为eps。这个拐点大致就是密度变化的阈值。我第一次实际操作时把eps设小了结果算法识别出200多个微型簇每个簇就一两个点等于没削减把eps调大后又发现全并成一个大簇极端场景全被吞掉。后来我在拐点附近取了好几个候选值分别跑一轮削减再用下一节的评估指标挑最合适的。这里不要指望一次找到完美参数把“参数整定”当做一个小的网格搜索来做输出场景数、噪声比例、分布距离这几个指标综合判断。4.3 从簇到代表场景最近中心点和概率加权聚类完成后每个簇包含若干场景样本。我的做法是对每个簇内部所有样本逐维求平均得到虚拟中心然后从簇内找出离这个虚拟中心最近的真实场景作为代表场景。这里刻意不用虚拟中心本身因为虚拟中心可能落在现实世界不存在的组合上比如某维平均功率是42.67MW直接当场景喂给优化模型会违反复核逻辑。用真实场景能保证物理可行性也方便工程部门理解“这个是实际发生过的某一天”。权重则设为该簇的样本数除以总样本数。对于噪声点如果决定保留每个噪声点单独作为一个代表场景权重为1/N。这样会在削减后的场景集中额外增加几条极端场景但代价换来的是对极端工况的覆盖在安全校核中很值。当噪声点太多导致场景削减率下降时我倾向于把噪声点二次分配计算噪声点到所有簇中心的距离分配给距离最近的簇而不是一刀切全部剔除。下面这段Python代码展示了用scikit-learn跑DBSCAN并提取代表场景的核心逻辑实际项目里我会把这段封装成函数import numpy as np from sklearn.cluster import DBSCAN from sklearn.decomposition import PCA # scenarios: shape (N, 48)已按全部样本做标准化 # 1. 降维 pca PCA(n_components12) scenarios_pca pca.fit_transform(scenarios) # 2. DBSCAN聚类 labels DBSCAN(eps0.35, min_samples6).fit_predict(scenarios_pca) # 3. 提取代表场景和权重 N len(scenarios) representatives [] weights [] for lab in np.unique(labels): idx np.where(labels lab)[0] if lab -1: # 噪声点 for i in idx: representatives.append(scenarios[i]) weights.append(1 / N) else: center np.mean(scenarios[idx], axis0) dist_to_center np.linalg.norm(scenarios[idx] - center, axis1) rep_idx idx[np.argmin(dist_to_center)] representatives.append(scenarios[rep_idx]) weights.append(len(idx) / N) # 输出削减后的场景数 print(f簇数: {len(np.unique(labels)) - (1 if -1 in labels else 0)}) print(f噪声点数: {np.sum(labels -1)}) print(f代表场景总数: {len(representatives)})这里有一个工程细节sklearn中的DBSCAN用的是fit_predict它的输入可以是特征矩阵也可以直接用预计算的pairwise距离矩阵。如果数据量大且特征维数高建议把metriceuclidean以及算法参数明确写出来。如果用了预计算距离矩阵记得把metricprecomputed。5. 削减完拿什么证明它好评估指标与一组模拟算例5.1 分布距离评估为什么Wasserstein距离比平均误差更靠谱场景削减本质上是用少数场景去逼近原始场景集的概率分布所以评价指标应该刻画两个分布之间的“距离”。最常用的是Wasserstein距离也就是推土机距离它考虑的是把一个概率分布搬运成另一个分布的最小代价。这个指标的好处是能同时反映概率差异和数值差异对少量极端分布不敏感非常适合评价削减前后是否保持了整体分布的形态。有些论文喜欢用平均绝对误差比如计算原始场景集中每个样本到最近代表场景的平均距离。这个指标只能说明代表场景在数值上靠近原始场景却不能证明概率覆盖是否完整因为你完全可以只保留一个场景让平均绝对误差保持很小但分布信息丢得一干二净。另外也可以看置信区间覆盖率削减后的场景集能不能覆盖原始场景集的90%分位数范围。这一点对后续做风险约束特别重要。5.2 算例对比1000场景压到50后决策结果还不跑偏我构造过一个模拟案例基于某风电场历史出力曲线叠加正态扰动生成1000个日场景负荷曲线参考典型区域负荷特性也叠加扰动两者合成1000个联合场景每个场景48维。用DBSCAN先做PCA降到10维然后取eps0.4、min_samples8削减后得到37个常规簇和5个噪声点把噪声点保留后共42个代表场景。作为对比用K-meansK42做同样规模的削减。结果上DBSCAN削减集与原始场景集的Wasserstein距离是0.048K-means是0.067说明DBSCAN保留的分布信息更完整。更直观的差异在极端工况覆盖上K-means削减后的场景集里没有任何“重载且风电出力很高”的极端工况而DBSCAN把其中一条孤立极端场景完整保留了下来。后续把这些场景代入一个简单的机组组合模型用原始1000场景求得期望运行成本用DBSCAN削减后的42场景重新求两者偏差小于0.3%K-means的偏差约0.8%。这个算例不是要给出普适结论——具体偏差一定与数据分布和模型结构有关。但它说明了一个经常被忽略的点场景削减的评估应该结合下游决策结果不能只看聚类指标。如果你削减完的场景集连期望成本都算不准分布距离再好看也没有实际意义。6. 落地时绕不开的坑以及我试过的几种改进6.1 eps参数对结果的影响有夸张到什么程度我在这个项目里踩过最大的坑就是eps。固定min_samples5把eps从0.2调到0.5簇数从120多一路降到2。在0.3附近稍微波动一下代表场景集就完全不同。这说明DBSCAN对密度阈值异常敏感尤其在高维空间邻域内的点数随着半径增长是指数级增加。所以与其手动猜一个eps不如做一次小网格搜索在k-distance拐点附近选5个候选值分别运行DBSCAN和代表场景提取用Wasserstein距离和场景数画一条平衡曲线挑“场景数尽量少、距离增加不大”的那个点。min_samples也不是越大越好。太大容易把正常密集区边缘的样本标成噪声太小又会把噪声拾进簇里。我在实践中一般让min_samples在5~10之间选数据规模越大可以适当取大一点。如果你发现噪声比例超过20%先别急着改聚类参数回到数据预处理去看是不是标准化出了问题或者有没有个别样本存在坏数据。6.2 高维场景的替代思路降维之后再做密度聚类对于更高维或者曲线形态更复杂的场景集我尝试过在DBSCAN之前引入非线性降维比如自编码器或UMAP。一次尝试里我用自编码器把48维压缩到6维再跑DBSCANeps0.28min_samples5得到的簇数比PCA降维后稳定得多因为非线性降维能更好地保留曲线的形变结构。代价是要多训练一个网络隐层维度这种超参数本身就是新的坑调试成本明显上升。如果不想引入深度学习用UMAP降维再跑DBSCAN也是一个有效组合。UMAP对局部结构保留得很好聚类轮廓比PCA更清晰但它内部有随机采样过程每次运行结果可能略有差异工程上需要固定随机种子。这里我更推荐在规划项目里优先走PCA因为它简单透明、可解释性强只有当结果精度不能满足要求时再把自编码器或UMAP拿出来做精度提升。如果你也打算在项目里用DBSCAN做场景削减我建议先拿一个600~1000规模的小场景集跑通完整流程把标准化、降维、k-distance、代表场景提取这些步骤中的细节体会一遍再放量到几千个场景。这样踩坑成本会小很多。最后说个实在的别迷信算法默认参数文档里的eps示例只是示意真实数据永远要自己调第一个能用的参数组合往往是试出来的而不是看出来的。