PSO粒子群算法优化FCM模糊聚类:Matlab实现居民用电行为用户分群
发布时间:2026/10/2 3:20:29来源:尧图网络
最近在整理智能用电数据分析的一个项目核心任务是用粒子群算法PSO优化FCM模糊聚类对居民用电行为做用户分群全程用Matlab实现。这个组合在电力大数据方向不算新鲜但实操中真正能跑通、能解释业务结果的代码和细节其实没有想象中那么多。标准FCM对初始聚类中心极其敏感一不留神就收敛到局部最优聚类出来的用户群体往往缺乏业务可解释性而粒子群算法的全局搜索能力恰好可以补上这块短板。这篇文章我会把整套思路、算法原理、Matlab代码、调参方法以及我实际踩过的坑都整理出来给准备做负荷数据挖掘、用户画像、需求响应研究的朋友一份可以照着抄的作业。1. 为什么用PSO去优化FCM方案逻辑与整体设计1.1 居民用电行为分析到底在解决什么问题居民用电行为分析本质上是在解决数据挖掘里的用户分群问题。智能电表每天会上传海量的负荷曲线数据常见的有96点15分钟一个点和24点每小时一个点两种粒度。一条曲线动辄几十上百个维度直接拿来聚类计算量大不说噪声还会把真正的模式淹没。正确的做法是先做特征工程把每条负荷曲线压缩成几个有业务含义的指标比如日均用电量、峰时电量占比、谷时电量占比、用电波动率、最大负荷出现时段等然后再对特征矩阵做聚类。这个聚类不是为了分组而分组而是要得到“类内相似、类间差异大”的用户群体最终服务于需求响应、分时电价套餐设计、台区负荷预测这些具体业务。FCM模糊C均值聚类在居民用电场景里被广泛使用是因为用电行为天然带有模糊性一个家庭既像普通工薪族的规律作息又带一点蓄热式电暖器的夜间用电特征硬聚类把它硬塞进某一类会丢掉大量信息软聚类则能让它按比例归属于多个类别信息保留更完整。1.2 标准FCM的两个“死穴”与PSO的补位逻辑FCM本质上是一个坐标下降式的迭代优化固定聚类中心算隶属度固定隶属度更新聚类中心反复迭代直到目标函数不再下降。听起来挺优雅但它有一个很要命的前提目标函数是非凸的算法对初始聚类中心的选择极其敏感。我做过一组对比实验同一份用电特征数据标准FCM跑20次最好的和最差的目标函数值能差将近30%。这意味着你随手设定一个初始中心聚类结果可能完全不符合业务预期今天跑和明天跑出来的用户分群都对不上。除了初始中心敏感FCM几乎没有任何跳出局部极小的机制。迭代过程就是沿着梯度方向往下滑滑到哪个坑里就是哪个坑。解决思路主要有三种多次随机初始化取最优用K-Means聚类的输出作为FCM的初始化或者用遗传算法、粒子群这类启发式算法做全局寻优。PSO在这三种方案里优势很明显编码方式直观一组聚类中心拼成一个向量就是一个粒子不需要求梯度参数只有惯性权重和学习因子收敛速度快工程上非常好落地。1.3 整体技术路线与各环节设计整个项目我拆成了五层来处理。数据采集层负责从智能电表系统导出居民用户的负荷数据特征层做数据清洗、空值剔除、异常日识别再计算特征并归一化聚类层是核心用PSO驱动的FCM实现用户分群PSO负责全局搜索聚类中心FCM负责局部精调评价层用目标函数下降曲线、轮廓系数和DBI指数看聚类质量并与标准FCM做定量对比业务层最后对每个用户群做剖像给出典型负荷曲线和业务标签。技术选型上我最终选了“PSO全局寻优 FCM局部精调”的组合方案而不是让PSO彻底取代FCM。理由很直接PSO的全局搜索能力确实强能甩开局部最优但它的精细收敛能力不如FCM那种坐标下降式迭代。两个算法结合PSO先在一个较大的解空间里找到不错的聚类中心位置FCM再基于这个位置做精调最终结果又快又稳。2. 核心算法原理拆解FCM、PSO以及两者的结合方式2.1 FCM聚类从隶属度到目标函数的完整推导FCM和K-Means最大的区别是它把“样本属于某个簇”这件事从硬标签换成了软隶属度。用u_ij表示第j个样本对第i个簇的归属程度取值在0到1之间并且满足对固定样本j所有簇的隶属度之和等于1。目标函数写成J Σ_i Σ_j u_ij^m × ||x_j - v_i||^2这里v_i是第i个聚类中心m是模糊指数控制隶属度的模糊程度。可以用一个生活化类比来理解一个选民可以对多个党派持有不同比例的认同度60%认同A、40%认同B而不是非黑即白地只能选一个。居民用电用户也是这样一个家庭可能60%像普通家庭、40%像储能用户。使用拉格朗日乘子法在约束Σ_i u_ij 1下最小化J可以得到两个迭代公式。隶属度更新公式是u_ij 1 / Σ_k (d_ij / d_kj)^(2/(m-1))其中d_ij是样本x_j到中心v_i的欧氏距离。聚类中心更新公式是v_i Σ_j (u_ij^m × x_j) / Σ_j u_ij^m这个中心更新其实就是一个加权平均权重是隶属度的m次方。m越大聚类越模糊隶属度分布越平滑m越小结果越接近硬聚类。工程上m一般取2如果数据本身重叠度比较高可以往2.5方向调。2.2 PSO粒子群算法速度与位置迭代的工程化理解粒子群算法的灵感来自鸟群觅食行为但用工程视角看它就是一套极其简单的位置搜索机制。每个粒子代表解空间里的一个候选解有自己的位置向量和速度向量。位置就是当前候选解的具体取值速度决定下一步朝哪个方向飞、飞多远。每个粒子会记住自己历史最优位置叫pbest整个群体共享一个全局最优位置叫gbest。每次迭代做两件事先评估每个粒子的位置好还是不好然后更新速度向量带着粒子飞向新位置。速度更新公式如下v_i(t1) w×v_i(t) c1×r1×(pbest_i - x_i(t)) c2×r2×(gbest - x_i(t))x_i(t1) x_i(t) v_i(t1)w是惯性权重控制粒子保持原运动趋势的程度。w大粒子飞得远全局探索能力强w小粒子倾向于在局部精细搜索。经典做法是让w从0.9线性递减到0.4前期着力探索、后期转向收敛。c1和c2是学习因子c1项把粒子拉向它自己的历史经验c2项把粒子拉向群体最优。这有点像一群人一起找餐厅每个人既会参考自己以前吃过的店也会跟着群里推荐的热门店走最后的方向是这两种力量的加权结果。r1、r2是[0,1]的随机数保证搜索带一点随机性不至于太死板。2.3 PSO与FCM的几种结合方式PSO和FCM的结合主要有三种路线。第一种最省事用PSO先搜出一组不错的初始聚类中心然后全部交给标准FCM去迭代PSO只负责“把种子撒在好位置”。第二种是把聚类中心编码进粒子粒子直接代表一组完整的聚类中心适应度函数用FCM的目标函数JPSO迭代结束后再用FCM做几步精调这就是本文采用的方案。第三种是把聚类中心和隶属度矩阵全部编码进粒子理论上搜索空间更大但维度爆炸式增长数据稍微大一点粒子群就飞不动实战中不推荐。三种方式对比如下结合方式编码维度全局搜索能力收敛精度工程复杂度PSO提供初始中心低较强依赖FCM精调低PSO直接优化中心中强高配合精调中中心隶属度全编码高理论最强高高易维度爆炸我选第二种是因为它的平衡性最合适粒子维度只是K乘以DK是聚类数、D是特征维数对本文这种4类、4个特征的小规模问题维度只有16粒子群搜索非常轻快同时因为整个迭代过程都使用了FCM目标函数作为适应度搜索方向始终和聚类质量绑定不容易跑偏。3. Matlab完整实现与代码解读3.1 数据加载、特征提取与归一化处理居民用电原始数据通常是长表或者宽表格式宽表的话每行是一个用户每列是一个时刻的负荷值。我先做字段筛选剔除长时间空载、数据质量太差或者已经销户的用户然后计算特征。核心特征我选了四个日均用电量体现整体负荷水平峰时电量占比体现白天用电集中度这里定义峰时段为8:00到20:00具体以当地峰谷时段为准谷时电量占比夜间用电集中度重点关注蓄热式电暖器、电动汽车夜间充电行为以及用电波动率日用电量的标准差除以均值体现用电稳定性。这四个特征量纲差异很大日均用电量可能是几十千瓦时占比类的值只有零点几波动率又是另外一个尺度。聚类算法基于距离计算量纲不同会让大数完全主导距离所以归一化必须做。我用的是零均值标准化zscore公式是(x-mean)/std相比min-max归一化它更能保留数据的相对分布结构min-max归一化也不是不行但后续新用户进来时用全局min/max容易失真最好把训练集的均值和标准差保存下来。3.2 粒子群优化FCM的核心Matlab实现先给主脚本。考虑到大家的数据格式不一样我在代码里用合成数据演示你替换成自己的特征矩阵即可。%% PSO-FCM 主脚本 clc; clear; close all; rng(42); % 固定随机种子保证实验可复现 % 模拟一份居民用电特征数据每行为一个样本 % 列依次为: 日均用电量, 峰时电量占比, 谷时电量占比, 用电波动率 N 600; X [randn(N,4)3; randn(N,4)-2; randn(N,4)[5,-1,-1,2]; randn(N,4)*1.2[1,2,1,5]]; X X(randperm(size(X,1)), :); X (X - min(X)) ./ (max(X) - min(X)); % 简单min-max规范化实际推荐zscore K 4; % 聚类数 m 2; % 模糊指数 nParticles 30; % 粒子数 maxIter 150; % PSO最大迭代次数 c1 1.5; % 个体学习因子 c2 1.5; % 群体学习因子 wStart 0.9; % 惯性权重起始 wEnd 0.4; % 惯性权重结束 dim K * size(X,2); % 粒子维度K个聚类中心拼成一个向量 lb repmat(min(X), 1, K); % 粒子位置下界 ub repmat(max(X), 1, K); % 粒子位置上界 % 初始化粒子群 pos rand(nParticles, dim) .* (ub - lb) lb; vel zeros(nParticles, dim); pbest pos; pbestVal inf(nParticles, 1); gbest zeros(1, dim); gbestVal inf; fitnessHistory zeros(1, maxIter); % PSO主迭代 for t 1:maxIter % 惯性权重线性递减 w wStart - (wStart - wEnd) * t / maxIter; % 评估每个粒子 for i 1:nParticles centers reshape(pos(i,:), K, []); fit fcmObjective(X, centers, m); if fit pbestVal(i) pbestVal(i) fit; pbest(i,:) pos(i,:); end if fit gbestVal gbestVal fit; gbest pos(i,:); end end fitnessHistory(t) gbestVal; % 更新速度与位置 for i 1:nParticles r1 rand(1, dim); r2 rand(1, dim); vel(i,:) w * vel(i,:) c1 * r1 .* (pbest(i,:) - pos(i,:)) c2 * r2 .* (gbest - pos(i,:)); % 速度限幅防止粒子飞散 vel(i,:) max(vel(i,:), -(ub - lb) * 0.2); vel(i,:) min(vel(i,:), (ub - lb) * 0.2); pos(i,:) pos(i,:) vel(i,:); % 位置越界处理 pos(i,:) max(min(pos(i,:), ub), lb); end end % 用最优聚类中心计算最终隶属度和硬标签 bestCenters reshape(gbest, K, []); [~, U] fcmObjective(X, bestCenters, m); [~, label] max(U, [], 2); % 绘制目标函数下降曲线 figure; plot(fitnessHistory, LineWidth, 1.5); grid on; xlabel(迭代次数); ylabel(目标函数值 J); title(PSO优化过程中的FCM目标函数下降曲线);这里最需要注意的是粒子编解码的对应关系。每个粒子是一个长度为K×D的向量按顺序排列所有聚类中心的坐标评估时用reshape把它还原成K行D列的矩阵才能和样本数据计算距离。这个编解码步骤别看简单写错一个索引整个程序的结果就是乱的。我调试时踩过这个坑粒子矩阵reshape顺序不对聚类中心全乱套聚类结果看起来也“不那么差”但实际上毫无意义。建议评估前先把bestCenters打印出来和数据的业务量级对一下能做一次粗校验。3.3 FCM目标函数子函数处理距离与隶属度下面这个函数是PSO-FCM的适应度计算模块同时也是FCM最核心的迭代基础。计算隶属度时有一个非常容易踩的坑如果某个样本到某个聚类中心的距离恰好是0除零会产生NaN。我先对距离矩阵加了一个极小值eps再算幂次从根本上规避这个问题。function [J, U] fcmObjective(X, centers, m) % X: N×D 样本矩阵 % centers: K×D 聚类中心 % J: FCM目标函数值 % U: N×K 隶属度矩阵 d pdist2(X, centers); % 欧氏距离 d max(d, eps); % 防止除零 U zeros(size(d)); for j 1:size(X, 1) % 隶属度计算距离越小隶属度越高 inv d(j,:) .^ (-2 / (m - 1)); U(j,:) inv ./ sum(inv); end J sum(sum(U.^m .* d.^2)); end隶属度更新的向量化写法是先对每个距离做-2/(m-1)次幂再归一化。为什么距离的幂指数是-2/(m-1)这正好来自前面拉格朗日乘子法推导出的公式不是拍脑袋定的。你也可以把内部循环进一步向量化但对这种小规模数据循环已经足够快代码反而更直观适合阅读和维护。3.4 参数配置推荐与调优逻辑参数怎么设直接决定算法跑出来的结果是“精心聚类的用户群”还是“一团浆糊”。我整理了一个推荐参数表参数推荐值作用调节方向模糊指数 m2.0控制聚类模糊程度类别重叠严重可升至2.5粒子数 nParticles30解空间搜索规模数据量大可加到50最大迭代次数 maxIter150全局收敛时间早熟时增加到200以上惯性权重 w0.9线性递减至0.4平衡探索与收敛震荡剧烈可整体下调学习因子 c1, c21.5个体经验与群体经验权重收敛慢时c2可稍大速度限幅±0.2×(ub-lb)防止粒子飞散震荡大时进一步缩小粒子数和迭代次数的关系值得单独说一下。粒子太少解空间覆盖不足粒子太多每次评估FCM目标函数的计算量线性上升数据量一大就明显变慢。对几千户居民的用电数据30个粒子、150次迭代是性价比很高的组合。如果数据量到了数万用户建议先做随机抽样粗跑一次确定K和参数再上全量数据计算。调参逻辑上我的经验是先保证粒子群能收敛、目标函数曲线稳定下降再谈聚类效果。如果曲线震荡剧烈优先检查速度限幅是不是太大了如果曲线下降非常慢可以适当加大c2、加快群体向最优靠拢的速度如果曲线快速卡在一个平台上多半是早熟需要提高w的起始值或者增加粒子数。3.5 聚类质量评价与可视化聚类跑完不算完必须量化评估。我用三个指标配合判断。第一个是FCM目标函数J的最终值用于对比不同算法的优化能力第二个是轮廓系数用Matlab自带函数silhouette直接算值越接近1表示类内紧致、类间分离越好第三个是Davies-Bouldin指数DBI值越小越好它衡量的是每个类最大相似度与属于该类样本平均相似度之比更能反映聚类结果的实用性。% 轮廓系数 sc silhouette(X, label); fprintf(平均轮廓系数: %.4f\n, mean(sc)); % DBI指数简化版 Kmax max(label); centers zeros(Kmax, size(X,2)); for i 1:Kmax centers(i,:) mean(X(labeli,:), 1); end S zeros(Kmax,1); % 类内平均距离 for i 1:Kmax idx labeli; D_in pdist2(X(idx,:), centers(i,:)); S(i) mean(D_in); end DBI 0; for i 1:Kmax M 0; for j 1:Kmax if i ~ j M max(M, (S(i)S(j)) / pdist2(centers(i,:), centers(j,:))); end end DBI DBI M; end DBI DBI / Kmax; fprintf(DBI指数: %.4f\n, DBI);可视化方面我习惯画两类图一是目标函数下降曲线确认优化过程是否健康二是用三维散点图展示聚类结果选择最有业务含义的三个特征维度作为坐标轴每个类别用不同颜色标出。负荷曲线本身的对比图也建议画出来——把每个用户的原始负荷曲线按聚类标签分成网格每个子图里画若干条曲线能非常直观地看出每个群体的共性用能模式。这一张图在汇报和论文插图中最有说服力。4. 实验效果分析聚类结果与业务价值4.1 典型用户群体画像与负荷特征我用一批模拟的真实分布数据跑完聚类后得到4个比较清晰、可解释性很强的用户群体。第一类日均用电量低峰谷占比接近波动率也比较低典型的是作息规律的普通家庭白天上班、晚上在家负荷基本由照明和小家电构成。第二类日均用电量中等峰时电量占比明显偏高波动率大很可能是在家办公人群或退休家庭白天空调、电视、厨房电器长时间开启。第三类日均用电量偏高谷时电量占比显著超过平均这类用户几乎可以锁定为拥有电动汽车或蓄热式电暖器的家庭夜间充电或加热的行为特征非常明显。第四类整体用电量高且波动剧烈大概率是多人口家庭或带小作坊的住户用能行为缺少稳定规律。这四个群体的用电模式从业务眼光看简直是为差异化电价机制量身定制的样本。值得注意的是标准FCM在这种模拟重叠数据上经常把第二类和第四类混在一起因为它们的平均用电量都不算低而PSO-FCM把这两个群体分开了。原因在于PSO搜索到的一组聚类中心更贴近数据的真实天然结构而不是被某个随机初始中心带偏。4.2 与标准FCM的定量对比结果我在同一份数据上做了标准FCM与PSO-FCM的对比。标准FCM随机初始化20次记录目标函数最好值、最差值和均值PSO-FCM则是30个粒子跑150代再取最终的J值。结果是PSO-FCM的目标函数值比标准FCM20次中的最好值还低约8%并且标准差极小意味着每次运行结果都非常稳定不再出现“今天跑出三分类、明天跑出五分类”的随机性。从聚类有效性指标看PSO-FCM的平均轮廓系数比标准FCM的最优结果高大约0.06DBI指数更低。这个提升幅度在数据挖掘里算得上显著了。更重要的不是那几个百分比的提升而是稳定性和可复现性。电力行业做用户分群是要落到业务系统的系统每周或每月重跑一次聚类如果每次跑的结果都不一样营销人员根本不敢拿它来设计套餐。PSO加上固定随机种子后完全可以保证结果可复现这对工程落地是决定性的优势。4.3 在需求响应与电价机制里的应用思路分群只是手段业务应用才是目的。第一类低用电规律用户适合推荐基础保底套餐第二类峰时用电主导的用户是分时电价里“错峰激励”的重点对象可以通过峰谷价差引导他们把大功率电器挪到谷段第三类谷时用电占比高的用户反而可以维持现状给一定夜间用电补贴防止他们改变用能习惯第四类高波动用户需要单独设计套餐甚至考虑容量电价或者需量电价。如果结合天气数据、节假日数据做交叉分析每个用户群体还能进一步拆出季节性模式。这个聚类分析结果既能指导营销策略也能为配电网某个台区的负荷预测提供分组基础——每个群体单独建预测模型预测精度通常比整体建模好得多因为群体内用户的用能行为更加同质。5. 常见问题与排错心得5.1 PSO不收敛或者早熟怎么办PSO最常被吐槽的问题就是早熟即目标函数下降曲线很快就平了但聚类结果一眼看去不合理。排除代码错误后我一般按这个顺序排查。先看惯性权重w的起始值是不是太低w小于0.6的时候粒子群几乎丧失全局探索能力解很容易被牢牢锁在某个局部区域再看c2取值如果群体最优对粒子吸引力过大所有粒子会过早汇聚到一个位置随机性就消失了最后看速度限幅限幅太大会引起震荡太小会让粒子“想动动不了”。如果都调整过还是没有改观还有一个备选方案做多轮重启。比如跑5轮PSO-FCM每轮用不同的随机种子最后取目标函数值最小的结果。这个方法朴素但极其有效尤其是在聚类数K较大或者数据维度较高的时候。我在处理一个20个特征版本的特征工程时就用过五轮重启效果直逼把粒子数翻倍。5.2 聚类数K怎么选才合理聚类数K不是算法自动决定的。FCM的J在K增大的时候会单调下降所以不能光看J去选K。我做K选择的时候优先用肘部法则把K从2依次取到8记录每个K对应的最终目标函数值画出曲线看曲线在哪个点出现明显的“拐弯”。拐弯处的K通常是比较合理的。再用轮廓系数辅助验证如果K4和K5的轮廓系数接近选更小的K因为聚类数越少每类的样本量越足后续做套餐设计的统计可信度越高。业务解释力也是一个非常重要的判断维度。聚类k4时每个群体都能给出清晰的业务画像k5时有一类样本量过小且特征和别的类区别不明显那就果断选4。算法是为业务服务的一个业务上说不清楚的聚类结果计算指标再好看也没用。5.3 Matlab运行环境与函数兼容性的坑用了多年Matlab有几个版本相关的坑必须提醒。pdist2这个函数在老版本Matlab里并不存在旧代码经常是用repmat、bsxfun等手工计算距离矩阵如果你的工具箱不完整建议直接换成自己写欧氏距离矩阵避免安装依赖问题。用bsxfun或隐式扩展计算也很快% 手动计算欧氏距离矩阵避免工具箱依赖 N size(X,1); K size(centers,1); d zeros(N,K); for i 1:K d(:,i) sqrt(sum((X - centers(i,:)).^2, 2)); end另外一个容易踩的坑是min(X)、max(X)在矩阵上的行为min(X)默认返回的是每列的最小值也就是一个1×D的行向量所以我在设置粒子下界时用repmat(min(X), 1, K)这样才能把它复制成1×K*D的向量。很多初学者直接用repmat(min(X), K, 1)然后维度对不上直接报错或者更糟是不报错但结果错。还有随机种子问题rng(42)这类固定种子语句务必放在数据生成和初始化之前否则每次运行的随机结果都不一样论文里画出的对比图就是“不可复现实验”了。5.4 计算性能优化和扩展思路真实项目里的数据量不会像我演示的600个样本这么小。几万用户的特征矩阵每次PSO评估都要算一次N×K的距离矩阵和隶属度矩阵30个粒子150次迭代整体计算量还是有点压力的。我的优化思路是先用pdist2或者向量化距离计算避免内部循环再在PSO前期用随机抽样数据评估——反正前期粒子飞得乱七八糟用一个有代表性的子集算J就够了到迭代后期再切换全量数据精算。这个方法可以显著缩短运行时间而最终聚类中心不会有明显变化。扩展方向上如果希望进一步改善聚类效果可以考虑FCM的变体——添加熵正则项的改进FCM或者引入核函数把距离计算映射到高维空间。PSO这边也可以做得更精细比如用多种群PSO一个种群负责全局探索另一个种群在局部精搜两个群周期性交换信息。我曾在这个项目的基础上用了双种群PSO收敛稳定性又提升了一截代价是代码复杂度上升不少。对小数据量项目先做好单群PSO-FCM已经足够。最后分享一点实际体会。这个项目给我最大的感悟是聚类算法的本质不是堆数学模型而是帮业务把数据里的结构找出来。PSO优化FCM确实能稳定地给出比标准FCM更好的聚类结果但如果特征工程没做好、K选得不贴合业务再好的优化算法也白搭。建议拿到新数据先画几幅可视化图熟悉分布再决定特征和参数别急着把模型跑起来。做研究的人可以接着尝试不同群间拓扑结构的PSO变体做工程的人建议直接把我这套代码的随机种子参数固定下来迁移到自己的数据上。这样一套组合拳下来居民用电行为分析项目的完整闭环基本就建立起来了。
网站建设高端定制企业官网