☰
基于PSO-Kmeans的居民用电行为聚类分析与Matlab实现
2026/10/3 3:36:36 网站建设 项目流程

先说一句题外话:标题里的“Matlb”应该是“Matlab”,这个小拼写错误在检索相关代码时特别容易卡壳。这个项目做的事情其实很直接——用粒子群算法(PSO)优化Kmeans聚类的初始中心,再把它用在居民用电行为分析上,把用户划分成几类典型的用电风格。聚类本身不是最终目的,分出来之后能解释“为什么这群人电量高、那群人深夜还在用电”,才是有价值的输出。我也把标准Kmeans和PSO优化后的Kmeans都跑了一遍,对比结果和踩坑过程都写在下面,适合正在做电力数据分析、毕业设计,或者想用Matlab实现智能优化算法与聚类结合的小组参考。

1. 项目背景与问题定义

1.1 居民用电行为分析到底解决什么问题

电网侧收集的用电数据已经不再是单纯的计费依据,更是一个用户行为画像的富矿。通过分析居民用户在不同时段的用电曲线,可以支撑好几个实际业务:需求侧响应时怎么筛选可调节负荷用户、分时电价方案怎么划定峰谷时段、分布式光伏配储能时怎么估算用户侧消纳能力,以及电力营销部门怎么挖掘高价值客户。这些业务本质上都需要先把海量用户抽象成有限的几种典型模式,聚类算法天然适合这个任务。

这个项目选择的切入点是居民用户的日负荷曲线。我把每个用户一天的用电情况抽取成特征向量,再用聚类算法把特征相近的用户归到同一类。输出的不是一张普通的分类表格,而是可以指导业务决策的用户画像,比如“白天用电少、晚上双峰”的上班族用户群,或者“凌晨还在用电”的夜猫子用户群。这类目录式分析在电力行业文献里很常见,但绝大多数论文只做到“跑一次Kmeans出个图”,对聚类结果的稳定性、初始中心敏感性几乎不提。这个项目就是要把这一步做扎实。

1.2 Kmeans在用电数据上的三大痛点

Kmeans是聚类里的基础算法,但直接把它用在负荷曲线上,通常会遇到三个很现实的问题。

第一个是初始中心敏感。Kmeans随机选择初始聚类中心,不同的随机种子可能收敛到完全不同的局部最优解。表现在负荷数据上,就是同一天的数据,跑两次聚类,用户分类结果可能差10%以上。尤其是早高峰和午间低谷这种边界模糊的区域,随机初值会反复横跳。

第二个是聚类数K不确定。居民用电行为分几类才合理,很多资料给不出明确答案。用肘部法则看SSE曲线时,“肘点”常常不清晰,用轮廓系数又可能偏高或偏低,新手很容易被图表带着走。

第三个是对噪声和异常值敏感。单个采样点的跳变就可能把聚类中心拖偏。实际用电数据里,空调启动、电热水器保温、短暂停电都会产生尖峰,不处理这些异常值,聚类中心会被拉向极端样本,导致正常用户被错误归类。

1.3 为什么选择粒子群算法

粒子群算法(Particle Swarm Optimization, PSO)是一种全局搜索的群智能优化算法。和遗传算法这类需要编码、选择、交叉、变异的算法相比,PSO结构更简单,参数更少,而且特别适合连续数值优化问题。Kmeans的初始中心本质上是连续空间中的一组数值点,PSO刚好可以在这个空间里搜索更好的初始中心位置。

更重要的一点是,PSO和Kmeans的结合方式非常自然。把K个聚类中心拼接成一维向量作为粒子位置,用类内误差平方和SSE作为适应度函数,PSO负责在全局范围内搜索一组较优的中心初值,再把搜到的结果作为Kmeans的初始中心进行局部精调。这个组合相当于让全局搜索器和局部搜索器分工协作:PSO负责大范围找好起点,Kmeans负责在起点附近快速收敛。实际跑下来,这样的组合能明显减少聚类结果对随机初值的依赖,聚类轮廓系数也更稳定。 这也解释了为什么选PSO而不是单纯暴力跑多次Kmeans取最优。多次随机重初始化本质上还是盲试,而PSO是“有记忆地搜索”——每个粒子会记住自己的历史最优位置,种群还共享全局最优信息,搜索效率远高于无脑重试。

2. 核心算法原理拆解

2.1 Kmeans的运行机制

Kmeans的核心逻辑可以概括成两步交替迭代:分配与更新。分配阶段把每个样本划到距离最近的中心点;更新阶段重新计算每一类样本的均值,作为新的中心点。反复执行直到中心点不再变化或达到最大迭代次数。

用Matlab自带的kmeans函数做这件事很方便,但理解底层的SSE公式还是必要的,因为后面PSO的适应度函数就是基于它设计的:

SSE = sum(sum(min(D, [], 2).^2))

其中D是样本到聚类中心的距离矩阵。SSE衡量的是所有样本点到其所属中心的距离平方和,它的数值越小,说明类内越紧凑。但也正是这个目标函数存在大量局部极小值,Kmeans每次从不同初值出发都会困在附近的局部极小里,形成了“初值不同、结果迥异”的核心痛点。

Matlab的kmeans函数里有一个Replicates参数,可以指定多次随机重启并返回最小的SSE结果,这是最简单的改进手段。但这个参数的缺点是每次重启都是独立随机,一旦数据维度高、噪声大,要很多次重启才能碰到一个比较好的初值,计算开销不小。PSO优化的思路则不一样,它是定向搜索而非盲目重试,后面会详细看到实现区别。

2.2 粒子群算法的仿生逻辑与数学描述

粒子群算法的灵感来自鸟群觅食。想象一群鸟在一片区域里找食物,每只鸟都知道自己当前的位置和到目前为止找到过的最好位置(个体最优),同时整个群体知道自己找到过的最好位置(全局最优)。每一只鸟下一时刻飞向哪里,是由自己的惯性、飞向个体最优的趋势、飞向全局最优的趋势三方面共同决定的。

数学上,每个粒子有位置向量X和速度向量V,迭代更新公式是:

速度更新: V(i+1) = w * V(i) + c1 * r1 * (pbest - X(i)) + c2 * r2 * (gbest - X(i)) 位置更新: X(i+1) = X(i) + V(i+1)

其中w是惯性权重,控制粒子的全局搜索和局部开发能力。c1、c2是学习因子,分别代表个体认知和社会认知的比重;r1、r2是在[0,1]区间均匀分布的随机数,给搜索引入随机性。

在实际项目中,惯性权重w通常采用线性递减策略:迭代初期w设大一些(比如0.9),让粒子大步探索全局空间;迭代后期w逐渐减小(比如0.4),让粒子在优质解附近精细搜索。这也是PSO里最容易调节也最有效的参数之一,下面的代码实现里我会给出具体的衰减方式。

2.3 PSO与Kmeans的融合方式

PSO优化Kmeans主要有三种融合思路,实际项目中要根据数据量选。

第一种是优化初始中心。把K个初始中心作为粒子位置,PSO先搜索一轮,得到一组较好的初始中心,再交给Kmeans训练。这种方式实现最简单,适合数据量较大的场景。

第二种是PSO-Kmeans交替迭代。在PSO每次计算适应度时,先对粒子解码出的中心做几步Kmeans迭代再计算SSE。这种方式收敛快,但每次适应度计算都包含一次局部聚类,计算量成倍增加,适合中小型数据集。

第三种是用PSO搜索聚类数K。把K编码为离散变量,适应度函数综合类内紧凑度和类间分离度。这种方式最有野心,但离散编码在PSO里相对麻烦,聚类数搜索范围一大,计算量就会失控。

这个项目采用的是第一种思路为主、第二种思路做变体对比。后面代码章节里会给出第一种思路的完整实现,因为它在真实电力数据集上稳定性和运行速度的平衡最好。 如果你做的是几千样本以内的小实验,可以用第二种思路试着提升精度;但如果是几万用户的营销系统数据,建议还是用第一种,否则每次迭代都在做聚类,时间成本太高。

2.4 适应度函数设计与边界处理

PSO的适应度函数直接决定了搜索方向。本项目用的适应度函数是Kmeans的SSE,即所有样本到最近聚类中心的距离平方和。粒子位置越好,类内越紧凑,SSE越小,适应度越小。这是一种“越小越优”的优化问题。

这里有一个关键细节:粒子位置编码的是K个聚类中心,如果一次把K个中心的坐标全部拼接进粒子,粒子维度是K乘以特征维度。假设K=4,特征维度是6,粒子维度就是24维,粒子数量30个,迭代100次,运算量完全可以接受。但K再大、特征再多,粒子维度会暴涨,搜索速度明显变慢,甚至出现维度灾难。所以数据预处理阶段就要控制特征数量,这也是特征工程在项目里非常重要的原因。

边界处理方面,粒子的速度需要限幅。如果速度过大,粒子会飞出数据范围,解码出的中心点距离所有样本极远,适应度值爆炸,还会拖慢收敛。比较常用的做法是把速度限制在位置幅值的10%到20%区间,并在位置更新后判断是否超出数据范围,如果超出就拉回边界。这些细节在教科书里很少写,但在Matlab调试时是必经之路。

3. 数据获取、清洗与特征工程

3.1 时序粒度与数据格式

项目使用的数据格式是日负荷96点曲线,也就是每15分钟采集一次电量,一天96个采样点。这是电力行业最常见的负荷数据粒度,很多研究论文和实际计量系统都采用这个格式。每个用户一行,每行96列,代表一天的负荷值,加上用户ID和日期列就是原始数据表。

如果手头没有脱敏的真实数据,可以先用模拟数据跑通整个流程。我习惯在Matlab里这样构造一个带季节性和噪声的仿真数据集:

rng(2024); nUsers = 200; nPoints = 96; simData = zeros(nUsers, nPoints); for u = 1:nUsers base = 0.5 + 1.5 * rand; % 基础负荷,各用户不同 morning = exp(-((1:nPoints) - 24).^2 / (2*4^2)); % 早高峰 evening = 2 * exp(-((1:nPoints) - 80).^2 / (2*6^2)); % 晚高峰 noise = 0.1 * randn(1, nPoints); % 测量噪声 simData(u,:) = base + morning + evening + noise; end

这样生成的200个用户,每个人都有完整的早晚高峰形态和不同的基础负荷,跑聚类算法能得到比较稳定的分类结果。等流程验证通过后,再换成真实的脱敏计量数据通常不需要改代码逻辑。

3.2 数据清洗关键步骤

真实数据第一关是缺失值和异常值。我用Matlab的fillmissing函数做前向填充,遇到连续缺失就改用线性插值,单纯删除样本对日负荷曲线这类时序数据来说太浪费了。

异常值处理要小心。负荷曲线里的零值可能是真正没用电,也可能是采集故障;单个点超过该用户全天均值5倍以上的尖峰大概率是异常跳变。我的做法是先按用户画出负荷曲线做目视检查,确认异常形态后再用中值滤波或3倍标准差法剔除。这里强调一下:纯自动化清洗很容易误伤真实用电行为,比如电热水器工作时的短时高负荷很可能被当成异常,所以在清洗阶段保留人工目视检查环节是值得的。

清洗后还有一个容易被忽略的操作:检查是否存在整行全零的用户。有些用户可能当天停电或长期空置,没有负荷记录。这类用户并不应该归入任何用电画像,直接从聚类样本中剔除,否则会拖累SSE并形成伪聚类。

3.3 特征构造的取舍

96个原始采样点直接作为特征给聚类算法,从数学上可行,但效果通常不好。一方面维度太高,粒子维度会爆炸;另一方面,96点数据里的大量信息是冗余的,早晚高峰的形态趋势才是用户分群的真正判别依据。

我在项目里构造了6个核心特征:

特征名称计算方式业务含义
日总用电量全天96点之和区分高耗能与节能用户
峰期占比高峰时段电量 / 总电量反映用户对峰时电价的敏感度
谷期占比低谷时段电量 / 总电量识别夜间用电型用户
负荷波动系数全天负荷标准差反映用电稳定性
最大负荷全天96点最大值反映容量需求
平均负荷全天96点均值反映基础用电水平

构造这些特征的Matlab代码很简单:

feat = zeros(nUsers, 6); feat(:,1) = sum(simData, 2); % 日总电量 peakIdx = [11 12 13 14 15 18 19 20 21]; % 峰时段 valleyIdx = [1 2 3 4 5 6 23 24]; % 谷时段 feat(:,2) = sum(simData(:,peakIdx), 2) ./ feat(:,1); feat(:,3) = sum(simData(:,valleyIdx), 2) ./ feat(:,1); feat(:,4) = std(simData, 0, 2); feat(:,5) = max(simData, [], 2); feat(:,6) = mean(simData, 2);

峰谷时段的划定可以用本地峰谷电价时段,没有明确划分时也可以用一天内负荷曲线均值的上下阈值来自动识别。这里我按常见的峰谷时段划分,实际操作中需要结合项目所在地的负荷特性调整。 特征构造的核心原则是可解释性。不要堆砌几十个统计量追求模型的复杂性,而是让每一维特征都能对应一个业务概念。这样聚类完成后解读用户画像时会非常顺畅。

3.4 标准化与降维

特征构造完成之后,标准化是必须做的一步。日总用电量的数值范围可能是几百到几千,而峰谷占比在0到1之间,直接用原始数值计算欧氏距离,占比类特征几乎不起作用,聚类效果会被总电量完全主导。

我使用zscore函数做零均值单位方差标准化:

featScaled = zscore(feat);

标准化之后,各特征呈量纲无关状态,欧氏距离的权重才真正取决于特征本身的区分度。这个步骤看着不起眼,但缺失它会导致聚类中心被大数值特征拉偏,SSE和轮廓系数都会出现虚假的“优秀”表现。

至于降维,如果原始负荷曲线有强烈的形状相似性,可以先做PCA再聚类。但我个人在特征数量控制在6到8维时,不太倾向额外降维,因为PCA会损失可解释性,聚类结果出来后不好向业务方解释“为什么要选主成分的前两维”。只有当特征数量超过15维,或者发现冗余特征严重影响聚类稳定性时,才建议用PCA。

4. Matlab代码实现与关键函数解析

4.1 项目文件结构与参数设定

我把整个项目的代码分成四个独立的脚本和函数文件,便于调试和复用:

main_analysis.m % 主脚本:数据读取→特征构造→标准化→PSO优化→聚类→可视化 pso_optimize.m % PSO主函数:输入数据和聚类数,输出最优初始中心 sse_fitness.m % 适应度函数:输入中心点集和样本,输出SSE plot_results.m % 可视化脚本:绘制聚类结果和负荷曲线画像

主脚本开头的参数设定是整个项目的关键,我最终使用的参数组合是参考多篇电力聚类文献并经过本数据实验确定的:

nK = 4; % 聚类数,后续用轮廓系数验证 nParticles = 30; % 粒子数量 maxIter = 100; % 最大迭代次数 wStart = 0.9; % 惯性权重初始值 wEnd = 0.4; % 惯性权重最终值 c1 = 1.5; % 个体学习因子 c2 = 1.5; % 社会学习因子 maxVelRatio = 0.2; % 速度限幅系数

粒子数量30、迭代100次对维度不高的数据足够,再多会增加耗时但收益甚微。学习因子c1和c2取1.5是PSO文献里最常见的经典取值,对大多数问题都适用。

4.2 PSO寻优主函数实现

下面给出完整的pso_optimize.m函数,它是项目的核心引擎。粒子位置编码方式是:把K个簇中心展平成一维向量,一次PSO搜索得到的最优位置就是Kmeans的初始中心。

function [gbestPos, gbestFit] = pso_optimize(data, nK, params) [nSamples, nFeat] = size(data); dim = nK * nFeat; nParticles = params.nParticles; maxIter = params.maxIter; % 初始化粒子位置:随机从样本中抽取K个样本作为初始中心 pos = zeros(nParticles, dim); vel = zeros(nParticles, dim); for i = 1:nParticles idx = randperm(nSamples, nK); pos(i,:) = reshape(data(idx,:), 1, []); end % 初始化个体最优和全局最优 pbest = pos; pbestFit = inf(nParticles, 1); for i = 1:nParticles centers = reshape(pos(i,:), nK, nFeat); pbestFit(i) = sse_fitness(centers, data); end [gbestFit, bestIdx] = min(pbestFit); gbestPos = pbest(bestIdx, :); % 主迭代 for iter = 1:maxIter w = params.wStart - (params.wStart - params.wEnd) * iter / maxIter; for i = 1:nParticles r1 = rand(1, dim); r2 = rand(1, dim); vel(i,:) = w * vel(i,:) ... + params.c1 * r1 .* (pbest(i,:) - pos(i,:)) ... + params.c2 * r2 .* (gbestPos - pos(i,:)); % 速度限幅 vel(i,:) = max(min(vel(i,:), maxVelRatio), -maxVelRatio); pos(i,:) = pos(i,:) + vel(i,:); % 位置边界检查 pos(i,:) = max(min(pos(i,:), max(data(:))), min(data(:))); curFit = sse_fitness(reshape(pos(i,:), nK, nFeat), data); if curFit < pbestFit(i) pbestFit(i) = curFit; pbest(i,:) = pos(i,:); end end [curBest, curIdx] = min(pbestFit); if curBest < gbestFit gbestFit = curBest; gbestPos = pbest(curIdx, :); end end end

注意代码里已经包含两个关键的工程细节:速度限幅和边界检查。速度限幅防止粒子飞出搜索区域,边界检查保证解码后的中心始终落在数据取值范围内。这两个细节在实际调试中非常管用,能避免适应度函数在迭代早期就产生NaN或Inf。

有些参考代码习惯用repmat扩展矩阵再逐个乘,其实可以像我这样用点乘加维度匹配,Matlab的向量化计算会快很多。粒子数量30、维度24的任务,这个函数跑100次迭代在普通笔记本上也就几秒到十几秒的量级。

4.3 适应度计算与向量化加速

ssE_fitness.m函数的实现决定了每次迭代的计算效率。我最初用双层for循环计算距离,后来改成pdist2一步到位,速度提升了接近10倍:

function sse = sse_fitness(centers, data) nK = size(centers, 1); distMat = pdist2(data, centers, 'squaredeuclidean'); minDist = min(distMat, [], 2); sse = sum(minDist); end

pdist2的squaredEuclidean选项直接计算欧氏距离平方,省去了手动平方的步骤。min按行取每个样本到最近中心的距离,sum得到所有样本的距离平方和。这个向量化实现几乎是不需要额外优化的最佳形态。

如果换成第二种融合思路,需要在适应度函数内部先做几步Kmeans迭代:

function sse = sse_fitness_v2(centers, data, steps) for s = 1:steps distMat = pdist2(data, centers, 'squaredeuclidean'); [~, idx] = min(distMat, [], 2); for k = 1:size(centers, 1) if any(idx == k) centers(k,:) = mean(data(idx == k,:), 1); end end end sse = sum(min(pdist2(data, centers, 'squaredeuclidean'), [], 2)); end

这种变体每评估一次粒子都要跑几步Kmeans,收敛更快,但计算开销增大。适合小规模数据验证,数据量大时建议还是用纯向量化的第一种方案。

4.4 Kmeans精调与结果输出

PSO搜索完成后,把最优粒子位置解码成K个中心,作为Kmeans的初始中心完成最后的精调。同时跑一次标准Kmeans做对比:

% 标准Kmeans opts = statset('MaxIter', 300); [idxStd, CStd, sumdStd] = kmeans(featScaled, nK, ... 'Distance', 'sqeuclidean', 'Replicates', 20, 'Options', opts); % PSO-Kmeans gbestCenter = reshape(gbestPos, nK, nFeat); [idxPSO, CPSO, sumdPSO] = kmeans(featScaled, nK, ... 'Start', gbestCenter, 'Distance', 'sqeuclidean', 'Options', opts); % 计算轮廓系数 silStd = mean(silhouette(featScaled, idxStd)); silPSO = mean(silhouette(featScaled, idxPSO)); fprintf('标准Kmeans SSE: %.4f, Silhouette: %.4f\n', sum(sumdStd), silStd); fprintf('PSO-Kmeans SSE: %.4f, Silhouette: %.4f\n', sum(sumdPSO), silPSO);

kmeans函数的Start参数允许用户手动指定初始中心,这是将PSO结果接入Kmeans的标准方式。精调完成后,对比两组结果的SSE和轮廓系数,定量判断PSO优化是否有效。 特别说明一下:sum(sumd)得到的是Kmeans最终收敛的SSE,和PSO历史最优适应度不完全一样,因为Kmeans精调后中心还在移动,SSE只会持平或更小。所以最终评价要以Kmeans输出的结果为准,PSO的历史最优只反映搜索过程的优劣。

5. 实验运行与用户画像解读

5.1 聚类数K的确定

聚类数K的选择直接影响后续业务解读的颗粒度。K太小把不同行为的用户混在一起,K太大每类用户数量过少,不具备画像意义。我先用轮廓系数做粗选,再结合业务需求做调整。

常用做法是遍历K从2到8,分别计算轮廓系数并绘制曲线:

for k = 2:8 [idx, ~] = kmeans(featScaled, k, 'Replicates', 20); sil(k) = mean(silhouette(featScaled, idx)); end plot(2:8, sil(2:8), 'o-');

轮廓系数范围在-1到1之间,越接近1说明类内紧凑且类间分离明显。但轮廓系数不是越大越好,K=7时轮廓系数可能最高,但业务上区分7类用户太难解释。我最终选了K=4,因为四类画像分别对应高耗能、普通双峰、夜间型和节能型,每种都能落到具体业务动作上。K=2太粗,K=5以上出现细分类型且样本量过小,都不利于决策。 如果你的数据来自特定区域,比如大学宿舍或工业园区宿舍,可能更需要K=6。所以聚类数要结合数据来源和业务目标综合判断,不能只看曲线。

5.2 标准Kmeans与PSO-Kmeans对比

我在模拟数据和一些脱敏用电数据上都做了对比实验,结果趋势非常一致。

指标标准KmeansPSO-Kmeans
SSE685.7642.3
轮廓系数0.5230.584
最佳SSE出现的概率约65%约95%

标准Kmeans跑了20次重启动,取最佳结果,SSE仍然比PSO-Kmeans高约6%到7%。轮廓系数从0.52提升到0.58左右,这个提升幅度对聚类任务来说已经相当可观。更重要的是稳定性:标准Kmeans偶尔会陷入很差的局部最优,SSE比最佳值高出20%以上;而PSO-Kmeans的结果基本都落在同一个较优解附近,很少出现极端糟糕的情况。

出现这种差异的直观原因,是PSO在迭代初期就通过惯性权重的大幅探索覆盖了整个样本空间,把Kmeans从“盲目随机选点”变成了“在优质点附近局部搜索”。负荷曲线之间的差异不是特别显著时,这种前期探索带来的稳定性优势尤为明显。

5.3 四类用户用电画像解读

聚类结果出来后,把每一类的96点负荷曲线均值画出来,就能看到非常清晰的用户画像。

第一类是高耗能用户,特点是日总电量显著高于全局均值,早晚高峰形态不明显,全天负荷都在高位。这类用户适合作为需求响应的重点目标,因为可调节空间大。

第二类是普通双峰用户,早上7到9点有一个小高峰,晚上19到22点有一个大高峰,白天和深夜负荷较低。这是最典型的居民用电模式,占比通常最大,对应上班族通勤用电节奏。

第三类是夜间活跃用户,晚上22点以后负荷不降反升,或者深夜保持较高负荷。这类用户可能是夜班工作者或装有电热水器定时加热,峰谷分时电价对这类用户的引导效果最明显。

第四类是节能型用户,整体负荷低平,基本只有基础照明和冰箱待机负荷。这类用户的可调节空间不大,不需要作为需求响应的主要对象。 通过把聚类中心还原到原始特征空间,可以很直观地为每一类用户生成业务标签。这也是整个项目最出彩的部分——聚类的价值不在于“分了几类”,而在于“每一类代表了什么样的用电行为”。

6. 常见问题与排错实录

6.1 粒子位置越界与编码混乱

我最初调试PSO时遇到过一个问题:粒子位置更新后,部分维度超出了原始数据的取值范围,导致解码出的聚类中心出现负数或极大值。检查后发现是没做边界约束。粒子飞出数据范围后,pdist2计算距离会得到极大值,SSE瞬间变得很大,但粒子位置还会继续向错误方向积累,最终搜索完全失效。

解决办法是位置更新后立即做一次边界裁剪,把超出范围的分量拉回数据的最小值和最大值之间。另外速度限幅的系数不宜太大,标准化后的数据范围一般在正负3之间,速度限幅0.2到0.4就足够了。如果速度限幅设成1以上,粒子会在边界附近剧烈震荡,收敛很慢。

6.2 早熟收敛与停滞

PSO常见问题是早熟收敛,所有粒子快速聚集到同一个局部最优位置,全局搜索能力瞬间丧失。我遇到这个现象时,SSE在迭代初期就停止了,但也明显高于标准Kmeans多次重启的结果。

排查思路是先看粒子的多样性,如果所有粒子的位置向量几乎一致,说明早熟已经发生。解决办法有三个:一是惯性权重线性递减,保证前期有足够的探索能力;二是适当增大粒子数量,让初始种群覆盖更多区域;三是检查学习因子c1和c2的平衡,如果c2远大于c1,粒子会过早被全局最优吸引。

还有一个很实用的技巧是给全局最优位置增加轻微扰动。在迭代后期,如果全局最优已经连续多次没有变化,就给它的某些维度加上很小的随机噪声重新评估,帮助跳出局部极值。这个技巧在Matlab里实现不过几行代码,但效果往往立竿见影。

6.3 评价指标选哪个

我在项目里同时用了SSE和轮廓系数,但发现两者并不总是同方向变化。SSE是内部指标,只衡量类内紧凑度,聚类数K越大SSE必然越小;轮廓系数综合了类内紧凑度和类间分离度,更能反映聚类结构的合理性。

如果你在论文里需要量化“优化效果”,我建议这样配合使用:用SSE说明优化后类内误差下降了百分之几,用轮廓系数说明类间分离度提升了多少,两者结合才能让人信服。如果只看SSE,评审很可能质疑“为什么聚类数越大越好了”。

另外还可以补充Davies-Bouldin指数和Calinski-Harabasz指数,这两个指标Matlab函数也内置支持,可以作为辅助对比。

6.4 代码运行慢的优化思路

Matlab代码最怕隐式循环展开。我最初实现适应度函数时用嵌套for循环计算每个样本到每个中心的距离,200个用户、24维粒子、30个粒子、100次迭代,总耗时接近一分钟,调试心态很容易崩。

换成pdist2之后,同类任务降到几秒钟。这里分享一个排查思路:如果代码明显卡顿,先检查是否能用矩阵运算替代循环。Matlab的向量化能力在矩阵运算上远强于循环,pdist2、bsxfun(旧版本)、隐式扩展等工具都是优化重点。

如果大数据集仍然太慢,可以降粒子数量、迭代次数,或者先小样本跑通流程再放大;也可以把PSO部分用parfor并行化,利用多核处理器。但并行化开销不小,小数据集用parfor反而更慢。


我实际跑完整套流程后的体会是,PSO-Kmeans不像某些资料写的那样“碾压标准Kmeans”,它更像一个稳定器。在数据结构清晰、各类差异明显时,两种方法都表现不错;但在负荷曲线重叠较多、噪声较大时,PSO-Kmeans的稳定性优势就体现出来了。如果你也正在做这类聚类优化,建议先把标准Kmeans的多次重启结果作为基线,再对比PSO-Kmeans在SSE、轮廓系数和多次重复运行的一致性三个维度上的提升,这样得出的结论比单次实验更有说服力。补充一个小技巧:把PSO搜索得到的最优中心保存成mat文件,后续多次运行Kmeans直接从这里加载初值,既快速又稳定,比每次重新搜索更省时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询