ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

粒子群算法优化FCM聚类在居民用电行为分析中的实践

粒子群算法优化FCM聚类在居民用电行为分析中的实践 我一个做了快十年算法落地的人看到“粒子群算法优化FCM聚类”这种标题第一反应不是“高大上”而是“当年我调参调到头秃的那段日子”。但确实这个组合在居民用电行为分析里是一个特别典型、特别能出成果的研究方向——非侵入式负荷识别、用户画像、需求侧响应策略制定全都离不开这一步。简单说它解决了这样一个问题居民用电数据本身噪声大、波动剧烈、还有明显的季节和作息规律直接用传统FCM硬聚类结果经常被初始聚类中心带偏迭代几次就陷入局部最优而粒子群搜索的全局性刚好补上这块短板。这篇内容适合正在做电力数据分析的研究生、刚接触Matlab聚类实现的工程师以及准备用负荷曲线做用户分群的业务侧同学参考。1. 为什么要用PSO去优化FCM居民用电行为分析的本质是个“聚类烂摊子”先说结论居民用电行为分析的核心不是分类而是聚类——我们根本不知道用户有哪几类只能从历史负荷曲线里“无监督”地找出群体模式。这一步做得稳不稳直接决定了后续需求响应、电价套餐设计、窃电检测这些业务环节的准确性。1.1 居民负荷数据的三个“反人类”特征如果你拿一份真实的居民用电数据打开看第一眼一定很崩溃。我的感受可以总结成三条特征一强随机性。今天空调开不开、电磁炉用不用、电动车充不充电全看用户心情同一户居民的工作日负荷曲线昨天和今天可能相关系数只有0.4。特征二多重周期性叠加。日周期、周周期、季节周期混在一起夏天晚高峰在21点冬天午高峰在12点周末的曲线形状完全换了一个人。特征三量纲和幅值差异极大。一户单身公寓的月均负荷可能只有0.3kW一户有三台空调的大家庭可以到5kW。如果不做归一化聚类算法学到的全是幅值差异而非形态差异这会让聚类结果失真严重。1.2 FCM聚类的基本原理与其“娇气”所在FCM模糊C均值聚类属于软聚类它不硬性规定某个样本属于哪个簇而是给每个样本计算一组在[0,1]之间的隶属度。对负荷曲线这种边界模糊的数据来说这比K-means合理得多——一个家庭可能就是早晚两个峰并存你硬把它归到“夜峰型”本身就是在丢失信息。FCM把聚类问题转化为最小化目标函数[ J \sum_{i1}^{n} \sum_{j1}^{k} u_{ij}^m | x_i - c_j |^2 ]其中u_{ij}是样本i对簇j的隶属度m是模糊指数通常取2c_j是第j个簇中心。问题在于FCM的迭代公式是从初始聚类中心出发做坐标下降式更新目标函数是非凸的初始值稍微离谱一点算法就收敛到某个局部极值。我在跑模拟数据时做过统计同样的数据、同样的k值换三组不同的初始中心聚类准确率能差出20个百分点。这还只是仿真真实负荷数据的凹凸性更严重。1.3 粒子群优化PSO为什么能救场粒子群算法的思路有点像一个“无领导”的搜索团队每个粒子是一组候选解在搜索空间里飞靠个体历史最优pbest和全局历史最优gbest两个牵引力不断修正飞行方向。它不依赖目标函数的梯度信息黑盒式搜索天然适合给FCM这种“迭代法”找好的出发位置。把PSO和FCM结合业界有两条路线串行热身式先用PSO全局搜聚类中心搜出来一组不错的结果再喂给FCM精修。内嵌交替式PSO每迭代一次就以FCM的一轮迭代做局部增强两者交替前进。我做下来感觉中文论文里常说的“PSO-FCM”大多指第一种——把PSO当作初始中心生成器因为它逻辑清晰、代码易实现、跑起来也快。第二种的收敛曲线更漂亮但调参复杂度高出一截不是所有场景都值得。2. 算法融合的关键细节粒子编码、适应度函数与FCM迭代的咬合这个融合算法听上去简单但落地时很多细节容易踩坑。下面几个点是我反复对比后确定的方案。2.1 粒子怎么编码中心矩阵拉平成一维向量一个粒子代表一套完整的“聚类中心方案”。如果k个簇、每个样本维度为d比如一条日负荷曲线有24个采样点那么一个粒子的长度就是k×d。用Matlab代码表示假设我们准备把n个用户各自24小时的负荷曲线聚成k类每个粒子的位置向量就是dim k * d; % d24dim就是24*k % 粒子位置边界所有元素在[0, 1]之间数据归一化后 lb zeros(1, dim); ub ones(1, dim);这里要注意粒子位置的有效范围必须和负荷数据的归一化范围保持一致。我见过有人把数据归一化到[0,1]而粒子边界设成[-1,1]结果PSO在大量无效区域搜索收敛慢了一半不止。2.2 适应度函数FCM的目标函数J就是天然适应度PSO要“优化”某个东西得有个目标。对我们来说最好的选择就是FCM的目标函数J——某套聚类中心方案算出的J越小说明类内紧凑、类间分离效果越好。具体实现是给一个粒子解码得到k个聚类中心然后用一步FCM计算隶属度矩阵和J值。这个J值就是该粒子的适应度。注意这里不需要把FCM迭代到收敛只需要算一步甚至半部只更新隶属度矩阵就能反映该中心方案的优劣。这样做能大幅降计算量我实测能省60%以上的运行时间。2.3 PSO速度更新公式标准版就够了粒子群的速度-位置更新采用标准形式[ v_{i}^{t1} w \cdot v_{i}^{t} c_1 r_1 (pbest_i - x_i^t) c_2 r_2 (gbest - x_i^t) ][ x_{i}^{t1} x_{i}^{t} v_{i}^{t1} ]参数上我的经验值惯性权重w线性递减从0.9到0.4前期全局搜索后期局部精修。学习因子c1c22经典取值多数场景下稳定。粒子数30~50足够。再多收益很小只增加运算时间。最大速度vmax设为边界范围的10%~20%即可。这里有个让我印象很深的教训PSO的收敛速度虽然快但它不像单纯形法那样保证每一步都在下降中间会震荡。所以别让PSO自己跑完就算完最后一定要用FCM把得到的最优中心再精迭代几十轮才能拿到平滑的收敛结果。3. Matlab代码实现全流程从数据预处理到画像输出的完整方案下面我给出一个可复现的Matlab实现骨架。数据是我模拟生成的300户居民日负荷曲线每户24点。实际项目中换成你手上的营销系统数据即可。3.1 数据生成与预处理真实数据拿不到手的时候先用模拟数据验证算法逻辑是对的。我这里的模拟逻辑混合了“单峰型”“双峰型”“平缓型”“晚峰型”四种曲线形状再叠加高斯噪声和幅值缩放尽量贴近真实居民用电的复杂情况。rng(2025); n 300; % 模拟300户 d 24; % 24小时采样点 trueK 4; loadData zeros(n, d); % 生成四种模板曲线 t 1:d; p1 exp(-(t-13).^2/8) 0.1; % 午峰型 p2 exp(-(t-8).^2/6) 1.5*exp(-(t-20).^2/4) 0.15; % 早晚双峰型 p3 0.5 0.1*rand(1,d); % 平缓型 p4 2*exp(-(t-21).^2/6) 0.15; % 晚峰型 types randi(trueK, n, 1); for i 1:n switch types(i) case 1, base p1; case 2, base p2; case 3, base p3; case 4, base p4; end amp 0.8 0.4*rand; % 家庭用电规模差异 loadData(i,:) amp * base 0.05*randn(1,d); end loadData max(loadData, 0.05); % 避免负功率 % 归一化到[0,1]按曲线最大值归一保留形态特征 normData loadData ./ max(loadData, [], 2);这里我特别建议按每户的最大值做归一化而不是全局最大值。按全局最大值归一大负荷用户会把小负荷用户压成一个接近0的“电老虎形状”形态细节全丢了。我踩过这个坑换完之后聚类轮廓系数明显提升。3.2 PSO初始化与主循环k 4; % 聚类数 m 2; % 模糊指数 maxIterPso 60; nParticles 40; w 0.9; wEnd 0.4; c1 2; c2 2; dim k * d; lb zeros(1, dim); ub ones(1, dim); vmax 0.15 * (ub - lb); % 粒子群初始化 pos rand(nParticles, dim) .* (ub - lb) lb; vel -vmax 2*vmax.*rand(nParticles, dim); pbest pos; gbest zeros(1, dim); pbestVal inf(nParticles, 1); % 初始适应度评估 for i 1:nParticles pbestVal(i) fcmFitness(pos(i,:), normData, k, m); end [gbestVal, idx] min(pbestVal); gbest pos(idx, :); for iter 1:maxIterPso wCur w - (w - wEnd) * iter / maxIterPso; for i 1:nParticles r1 rand(1, dim); r2 rand(1, dim); vel(i,:) wCur*vel(i,:) c1*r1.*(pbest(i,:)-pos(i,:)) c2*r2.*(gbest-pos(i,:)); vel(i,:) max(min(vel(i,:), vmax), -vmax); pos(i,:) pos(i,:) vel(i,:); pos(i,:) max(min(pos(i,:), ub), lb); val fcmFitness(pos(i,:), normData, k, m); if val pbestVal(i) pbestVal(i) val; pbest(i,:) pos(i,:); end if val gbestVal gbestVal val; gbest pos(i,:); end end end3.3 适应度函数只算“半步FCM”这个子函数是整个融合算法的核心很多实现版本把它写成了完整FCM运行慢得离谱。我的做法是从粒子解码出中心只更新一次隶属度矩阵然后算J值直接交给PSO做适应度评估。function J fcmFitness(x, data, k, m) d size(data, 2); centers reshape(x, k, d); % 粒子解码成k个中心 n size(data, 1); distMat zeros(n, k); for j 1:k diffMat data - centers(j,:); distMat(:, j) sum(diffMat.^2, 2); end distMat max(distMat, 1e-10); % 防止除零 invDist 1 ./ distMat; u invDist ./ (sum(invDist, 2) .^ (1/(m-1))); % 隶属度矩阵 J sum(sum((u.^m) .* distMat, 2)); end写到这里要强调一个细节上面代码里隶属度矩阵更新公式里的指数1/(m-1)一定不能写错。我见过有代码写成2/(m-1)结果迭代出来的聚类中心全堆在一起形状完全错误。3.4 用FCM做最终精修和隶属度输出得到PSO给出的最优中心后把它作为FCM的初始中心迭代精修到收敛[finalCenter, U, objHist] fcm(normData, k, ... Options, [2, 100, 1e-5, 1], ... Init, reshape(gbest, [k, d]));这里用的是Matlab模糊逻辑工具箱的fcm函数。如果不想依赖工具箱也可以自己写完整FCM迭代循环30行以内就能搞定。用时注意Init这个属性的传入格式必须是[k, d]的矩阵我第一次传成了行向量报了半天错才反应过来。4. 常见问题与排查技巧实录这部分内容是我在实际调试中遇到最多的情况惯例以问题清单形式整理出来供大家直接查阅。4.1 聚类数K到底选几个K是“无监督”里绕不过的问题。我的建议不要只靠一个指标要“指标业务”双轨判断。指标侧算不同K值下轮廓系数Silhouette或DBI指数选指标拐点。注意DBI下降曲线通常没有明显的“肘部”拐点比较平缓这时候结合下面业务侧一起定。业务侧聚类结果要让营销人员看得懂、说得出策略。我曾遇到技术评分最好的K7但业务人员看了一眼就摇头因为有两类曲线只差在幅值上形态几乎一样根本没法分别设计运营活动。后来把它们合并成K5效果反而好很多。对于居民用电K取3~6比较常见。真要钻牛角尖的话还可以跑一遍“聚类数扫描多次重复取最稳值”避免一次运气好选了个偶然最优。4.2 PSO参数怎么调才不失控粒子群算法本身对参数不算敏感但有几个坑惯性权重递减速率宁慢勿快。我曾经把w从0.9直接降到0.2结果前10次迭代粒子全收缩到gbest附近搜索彻底丧失多样性后面再怎么迭代都是原地打转。线性递减时迭代次数不足别把w拉得太低。粒子数不是越多越好。40个粒子跑60次迭代处理300×24这种规模的数据几秒钟就出结果了。你摆到200个粒子精度提升微乎其微时间却花了5倍完全没必要。vmax设太小粒子飞不动。如果发现gbestVal前几次迭代下降正常后面曲线变成一条平稳直线先检查是不是vmax太小把粒子困在了局部邻域。4.3 FCM相关报错与数据坑我汇总一下最常见的三个问题1聚类中心NaN。通常是因为数据里有NaN或Inf值。负荷数据经常因为采集终端离线产生空缺直接往前端导数据进Matlab必炸。解决方式检测并处理normData(all(isnan(normData), 2), :) []; % 删除全空行 normData(isnan(normData)) 0; % 单点空值补0或插值问题2部分聚类中心重合。FCM的模糊特性允许中心靠近但完全重合说明模糊指数m可能过大或者初始中心给得太集中。我在实验里试过m3中心重合的概率显著上升居民负荷这种平滑曲线尤其明显。保持m2同时给PSO的初始位置加一点均匀随机扰动可以有效避免。问题3同一套参数每次跑出来的结果不一样。这是PSO的随机性带来的正常现象。想要稳定复现代码开头固定随机种子rng(2025);如果是写论文做对比实验务必固定随机种子并且多次重复取平均结果否则审稿人让你复现实验会很难看。5. 结果解读从聚类中心到用户用电行为画像算法跑完了矩阵输出了但这只是完成了一半。真正见功力的是把聚类数字翻译成业务语言。5.1 四种典型负荷曲线形态的判断方法聚类中心向量就是k条24点的曲线拿到手之后我一般按这三个维度判读峰值出现时段峰值落在上午是“午峰主导”落在晚上是“晚峰主导”早晚双峰是典型的上班族规律。峰谷差峰谷差大说明用户调峰潜力大峰谷差小说明用电平稳对电价敏感度可能也低。夜间最低负荷夜间负荷若一直维持较高水平很可能存在保温设备电热水器、鱼缸加热棒或数据中心级设备这类负荷和新能源协同改造潜力高。用我自己跑的模拟数据来展示一下得到了4条中心曲线第1类午峰型、第2类双峰型、第3类平缓型、第4类晚峰型。双峰型用户的两峰时段分别为8点和20点明显是“早出门前用电晚上回家后用电”的上班族晚峰型用户在21点达到最高家里可能有集中用水的电热水器这类群体很适合推分时电价下的夜间蓄热策略。5.2 把隶属度矩阵变成用户分群标签FCM输出的不是硬标签而是隶属度矩阵U。实际落地时我习惯这样处理[~, hardLabel] max(U, [], 2); % 每个用户归到隶属度最大的类对300户用户的模拟案例我最终把用户划分成了四类。看一眼每类的人数占比平缓型大约是33%双峰型28%晚峰型22%午峰型17%。这个比例结构合理没有出现某一类占掉七成的情况说明聚类有效性还不错。5.3 聚类结果的验证不只盯着目标函数有一种自欺欺人的情况目标函数J下降了但聚类结果在业务上没意义。为了避免这种问题我习惯加做两件事轮廓系数验证用Matlab的silhouette(normData, hardLabel)直接出图数值在0.25以上算能接受0.4以上算优秀。居民用电数据噪声大通常跑到0.3左右就算不错别拿它和Iris数据集的标准死磕。可视化复核把每类用户原始曲线画在一个图里看类内曲线是否贴合、类间形态是否可区分。这一步虽然“不够量化”但很多时候比一堆指标都直观也方便拿给业务同事快速理解。6. 一点个人心得和后续扩展思路这套PSO-FCM模型单独看并不复杂真正花时间的是三件事一是把PSO的适应度函数和FCM目标函数对齐二是忍受真实负荷数据的各种脏点三是把聚类结果讲成一个业务部门听得懂的故事。我在实际项目中反复实验得出的体会是“先用PSO搜全局、再用FCM精局部”的组合在居民用电场景下比单纯FCM平均能把目标函数J降低8%~15%但更关键的是把结果稳定性提上来了——同一份数据不会因为换了一组随机种子就变一套用户画像。最后分享一个可以在现有代码上快速扩展的方向把某用户的日负荷曲线改成“工作日平均曲线周末平均曲线”的双通道形式粒子编码长度翻倍其他逻辑基本不动就能同时识别工作日与周末行为差异。这个小改动在很多省级电网用采数据分析项目里很受欢迎值得一试。
返回列表