先交代一下背景我之前一直在做电力客户侧的的数据分析项目其中一块内容就是对居民用电行为做聚类。起初我图省事直接用Matlab自带kmeans函数跑聚类数设为4结果两次运行的结果差异大得离谱同一批用户经常被分到不同类别拿去给业务同事看人家直接问这个结果能复现吗。后来我把粒子群算法PSO和Kmeans组合起来用PSO先搜索一组高质量的初始聚类中心再交给Kmeans做局部精调才把结果稳定下来。这套方法在居民用电行为分析场景里特别实用下面我把完整的思路、原理和Matlab代码实现都梳理出来。1. 为什么居民用电聚类最终选了PSOKmeans这套组合1.1 先搞清楚我们要分析什么样的居民用电行为居民用电行为分析通俗讲就是把一群用户的用电习惯分门别类。比如有人是典型的上班族双峰型早晚各有一个用电高峰有人白天家里也有人曲线平缓属于全天平稳型还有人半夜用电量特别大大概率是给电动车充电再有就是夏天猛开空调的季节性高耗型。这些画像对供电公司做需求侧响应、分时电价测算、错峰调度、精准营销都有直接价值对普通家庭来说也能通过对比知道自己是不是耗电大户。做聚类之前需要先确定用什么特征表征一个用户的用电行为。智能电表采集的数据通常以15分钟或1小时为间隔一个用户一天就有96点或24点的负荷曲线。直接用整条曲线做聚类也不是不行但有几个问题一是维度高96维的数据距离计算量大而且聚类结果不好解释二是曲线形态的差异很容易被用电绝对数值的大小掩盖。所以我习惯先把原始曲线转成一组低维特征比如日平均负荷、日最大负荷、峰谷差、夜间用电占比、负荷率等。这样既保留了行为特征又大幅压缩了维度Kmeans聚类的稳定性和可解释性都会好很多。1.2 标准Kmeans的聚合结果为什么让我不敢直接用标准Kmeans的核心逻辑很简洁随机选K个点作为初始聚类中心然后把每个样本分到离它最近的中心再重新计算每个簇的均值作为新中心反复迭代直到中心不再明显变化。这套流程跑起来非常快但它的短板也很致命极度依赖初始中心的选取随机初始化很容易让算法陷入局部最优解。我实际测试的时候遇到过一种典型情况用户数据有3000多户K取4第一次运行某个用户簇的轮廓系数是0.42第二次直接变成0.31而且同一个用户在两轮结果里被分到了不同簇。对业务方来说聚类结果每次跑都不一样根本没法往下做套餐设计或负荷预测。还有一个问题Kmeans默认每个簇是凸的、球形分布但真实用电数据经常是长尾的比如少数高耗能用户的曲线跟普通用户差异极大会让聚类中心偏移。除了初始中心敏感Kmeans还要求先定好K值。实际业务中K怎么选本身就挺头疼很多时候只能用肘部法则或者轮廓系数去试。这些痛点叠加在一起让我意识到单纯靠Kmeans直接出结果在居民用电场景里并不靠谱需要一个更稳的寻优机制来辅助它。1.3 粒子群优化的切入点不是替代聚类而是给Kmeans选初始中心粒子群算法模拟的是鸟群觅食行为每个粒子代表解空间中的一个候选解粒子根据个体历史最优和全局历史最优不断调整自己的速度和位置。它是一个连续优化算法擅长在连续空间里搜索高质量解但它的搜索过程不像Kmeans那样有逐步求精的迭代逻辑直接在原空间里搜索聚类结果往往收敛较慢。所以我的做法不是用PSO完全替代Kmeans而是做两阶段配合第一阶段用PSO在连续空间里搜索一组好的初始聚类中心第二阶段把这组中心作为Kmeans的起点让Kmeans做局部精细收敛。这样既利用了PSO的全局搜索能力保留了Kmeans的快速收敛特性又绕开了标准Kmeans随机初始化带来的不稳定性。实测下来两阶段配合比纯PSO搜索快比纯Kmeans稳定后面实验结果部分会详细对比。2. 粒子群优化Kmeans的原理拆解编码、适应度与迭代流程2.1 粒子群算法两个核心公式先过一遍PSO的实现基础是速度和位置迭代更新公式。第i个粒子在第t轮迭代后它的速度(v_i)和位置(x_i)按下面的方式更新[ v_i(t1) w \cdot v_i(t) c_1 \cdot r_1 \cdot (pbest_i - x_i(t)) c_2 \cdot r_2 \cdot (gbest - x_i(t)) ][ x_i(t1) x_i(t) v_i(t1) ]参数w是惯性权重控制粒子对上一轮速度的继承程度c1和c2分别是个体学习因子和社会学习因子r1和r2是[0,1]之间的均匀随机数。pbest_i是个体历史最优位置gbest是整个粒子群的历史最优位置。这几个参数对结果影响最直观w太大粒子飞得太野容易震荡w太小粒子很快就集中在局部区域全局搜索能力退化。我一般把w设为从0.9线性衰减到0.4让迭代前期多探索、后期多收敛。c1和c2通常都取1.5到2之间两者接近能让粒子兼顾自身经验和群体经验。2.2 把聚类中心塞进粒子的编码方案在Kmeans优化场景中粒子就是一组候选的聚类中心。假设数据集有d维特征聚类数设为K那么每个粒子编码成一个长度为(K \times d)的向量前d个元素是第一个聚类中心的坐标接着d个元素是第二个中心以此类推。比如K4、d6时每个粒子的长度就是24。这个编码方式简单直接Matlab里用reshape就能转换粒子向量为(K,d)的中心矩阵。边界处理也很关键数据已经归一化到[0,1]区间聚类中心是整个空间中的点理论上也在[0,1]范围内所以我把粒子的位置范围限制在[0,1]速度限制在[-0.2,0.2]超出就做边界修正避免粒子飞到无意义的区域。粒子群里每个粒子的初始位置就是在[0,1]空间里随机生成的K个中心初始速度也是随机小量。这样一群候选中心组合就生成好了接下来就是迭代寻优。2.3 适应度函数设计与空簇处理适应度函数是PSO的指挥棒它决定哪些粒子能被保留下来。居民用电聚类中我的适应度函数用的是Kmeans的目标函数所有样本到其所属聚类中心的距离平方和SSE。SSE越小说明簇内样本越紧凑聚类效果越好。公式如下[ SSE \sum_{i1}^{n} | x_i - \mu_{c_i} |^2 ]其中(\mu_{c_i})是样本(x_i)所属簇的中心。初看这个适应度函数好像和Kmeans的目标函数一样但关键区别在于这里PSO是在全局解空间里搜索一组中心每轮迭代不是简单地基于当前划分去更新中心而是通过群体协作搜索去逼近全局最优因此能跳出局部极小点。空簇问题是必须处理的。某个粒子代表的K个中心里如果有一个中心没有任何样本被分配给它这个粒子的实际有效簇数就小于KSSE计算也会失真。我的处理办法是先给空中心重置为当前数据集中随机抽取的一个样本点再计算适应度如果重置后仍然为空就把这个粒子的适应度值设为一个很大的惩罚值迫使它飞离无效区域。2.4 整个流程怎么串起来两阶段策略完整的优化流程分两阶段。第一阶段是PSO全局搜索阶段流程大致如下读入归一化后的特征数据设置PSO参数粒子数N、最大迭代次数maxgen、w、c1、c2、边界范围。初始化N个粒子每个粒子是K×d维的位置向量随机赋值到[0,1]速度随机初始化。对每一个粒子解析出K个聚类中心用Matlab的pdist2计算样本到各中心的距离把样本划分到最近中心再更新中心为簇内均值这里可以执行1次或多次Kmeans迭代也可以不执行直接按当前粒子位置算SSE。计算每个粒子的SSE适应度值更新个体最优pbest和全局最优gbest。按速度位置公式更新粒子速度和位置检查边界条件。重复3-5直到达到maxgen。第二阶段是局部精调阶段把PSO搜索到的gbest解析成K个聚类中心作为kmeans函数的初始中心调用Matlab自带的kmeans再跑一轮直到收敛。第一阶段PSO负责跳出局部最优、找到好的中心位置第二阶段Kmeans负责快速收敛到局部精确解。这套组合拳在速度和效果上都有明显改善。3. 居民用电数据准备这一块比算法本身还容易翻车3.1 原始负荷数据到底怎么变成特征矩阵我用的数据是智能电表采集的居民用户日负荷记录按小时一个点一天就是24个点原始数据大概长这样每一行是一个用户在某一天的24小时负荷值第一列是用户ID后面24列是0点到23点的功率值单位是kW。拿原始曲线直接聚类的做法我前期试过结果并不好因为不同用户表计容量不一样有的用户最大负荷能到10kW以上有的只有2kW欧氏距离算出来的差异主要来自负荷大小而不是用电形态。所以后来我改成特征提取对每一个用户按整月日数据做统计提取周平均日负荷曲线然后在这个平均曲线上计算特征。我常用的特征集包括日平均负荷、日最大负荷、日最小负荷、峰谷差、负荷率平均负荷/最大负荷、夜间用电占比22点到次日6点的用电量占总用电量的比例、用电量标准差。这些特征既覆盖了用电幅度也覆盖了用电时间分布维度却只有7个聚类计算压力小很多。3.2 缺失值、异常值和归一化的处理细节智能电表数据从来不是干净的。采集终端的通信故障、停电、表计异常都会造成数据缺失或突刺。缺失率不高的用户我用fillmissing函数配合线性插值直接填充缺失率超过20%的用户直接剔除因为强行补全会引入大量虚假信息反而干扰聚类。异常值也需要单独处理。居民负荷数据里偶尔会出现某个点突然拉到几十千瓦的脉冲这通常是漏电或者表计故障导致的不代表真实用电行为。我用滑动窗口的3σ原则去过滤对每个用户每天的负荷序列计算每个点的窗口内均值和标准差超过均值3倍标准差的点判断为异常然后用窗口内前后两点的均值替代。归一化在聚类前是必须的我习惯把所有特征缩放到[0,1]区间。Matlab里直接用normalize(data, range)就能做到。如果不做归一化像日最大负荷这种数值范围大的特征会在距离计算中占据主导地位夜间用电占比这种小数值特征基本就没话语权了。3.3 特征要不要降维我用过的两种做法特征维度降到7个之后是不是还需要再做PCA我的经验是看聚类结果的可解释性要求。如果做PCA把7个特征降到2-3个主成分聚类速度会快不少可视化也方便但主成分是原始特征的线性组合给业务方解释起来比较费劲比如这个簇的第三主成分得分高远不如这个簇的夜间用电占比高来得直观。因此我实际项目里默认用原始特征直接聚类只在两种情况下才会做PCA一是聚类特征需要画二维散点图看分布时把数据降到2维用于展示二是特征维度超过20出现明显的特征冗余时用PCA在保留90%以上方差的前提下压缩维度。对于居民用电场景7维特征不需要强制降维直接进聚类即可。4. Matlab代码实现从主程序到核心函数的完整拆解4.1 主程序骨架数据加载、参数设置与全局流程整个程序的框架我用脚本加函数的方式组织主脚本负责读数据、设置参数、调用各函数、输出结果。以下是一个可以直接运行的主程序骨架Matlab R2021b及以上测试通过% 主脚本PSO优化Kmeans的居民用电行为分析 clear; clc; close all; rng(42); % 固定随机种子保证结果可复现 %% 1. 加载数据 % 假设数据文件包含变量 featMat: n x d 的特征矩阵 % 以及 userIDs: n x 1 的用户ID load(household_feature.mat, featMat, userIDs); [n, d] size(featMat); %% 2. 归一化 X normalize(featMat, range); %% 3. PSO参数设置 K 4; % 聚类数可由肘部法则确定 N 30; % 粒子数 maxgen 80; % 最大迭代次数 w 0.9; % 惯性权重起始值 w_end 0.4; % 惯性权重终止值 c1 1.5; % 个体学习因子 c2 1.5; % 社会学习因子 dim K * d; % 每个粒子的维度 lb zeros(1, dim); % 位置下界 ub ones(1, dim); % 位置上界 vmax 0.2; % 速度限幅 %% 4. 调用PSO优化函数 [gbest, gbest_fitness, fitness_hist] pso_kmeans(X, K, N, maxgen, w, w_end, c1, c2, lb, ub, vmax); %% 5. 用PSO结果初始化Kmeans并做局部精调 initCenters reshape(gbest, K, d); [clusterIdx, finalCenters, sumd] kmeans(X, K, Start, initCenters, MaxIter, 500, Replicates, 1); %% 6. 评价指标 SSE sum(sumd); sil silhouette(X, clusterIdx); DBI davies_bouldin(X, clusterIdx, finalCenters); %% 7. 结果输出与可视化 figure; plot(1:maxgen, fitness_hist, LineWidth, 1.5); xlabel(迭代次数); ylabel(SSE); title(PSO迭代收敛曲线); % 更多可视化代码见4.4这里固定了随机种子rng(42)目的是让实验结果可复现。实际工程中如果想让结果更客观可以去掉这一行多跑几次取平均。4.2 PSO优化部分代码详细注释pso_kmeans函数是整个程序的核心。我把它单独放在一个m文件里函数的入参是特征数据、聚类数、粒子数等出参是全局最优粒子位置一组聚类中心和收敛曲线。以下是完整实现function [gbest, gbest_fitness, fitness_hist] pso_kmeans(X, K, N, maxgen, w_start, w_end, c1, c2, lb, ub, vmax) % PSO优化Kmeans初始中心 % 输入 % X: n*d 归一化后的特征矩阵 % K: 聚类数 % N: 粒子群规模 % maxgen: 最大迭代次数 % 输出 % gbest: 1*(K*d) 最优粒子位置 % gbest_fitness: 最优适应度值 % fitness_hist: 每代的最优适应度值 [n, d] size(X); dim K * d; % 初始化粒子群 popx rand(N, dim) .* (ub - lb) lb; % 位置矩阵 popv -vmax 2 * vmax * rand(N, dim); % 速度矩阵 pbest popx; % 个体最优位置 pbest_fitness inf(N, 1); % 个体最优适应度 gbest zeros(1, dim); % 全局最优位置 gbest_fitness inf; % 全局最优适应度 fitness_hist zeros(maxgen, 1); for t 1:maxgen % 线性递减惯性权重 w w_start - (w_start - w_end) * (t / maxgen); % 对每个粒子计算适应度 for i 1:N centers reshape(popx(i, :), K, d); % 计算样本到各中心的距离矩阵 distMat pdist2(X, centers); % n*K [~, idx] min(distMat, [], 2); % 每个样本最近的簇编号 % 空簇处理重置空中心为该簇最近点 for k 1:K if sum(idx k) 0 centers(k, :) X(randi(n), :); end end % 重新计算距离和SSE distMat2 pdist2(X, centers); [~, idx] min(distMat2, [], 2); sse 0; for k 1:K if sum(idx k) 0 sse sse sum(distMat2(idx k, k).^2); end end % 更新个体最优 if sse pbest_fitness(i) pbest_fitness(i) sse; pbest(i, :) popx(i, :); end % 更新全局最优 if sse gbest_fitness gbest_fitness sse; gbest popx(i, :); end end % 更新速度和位置 for i 1:N r1 rand(1, dim); r2 rand(1, dim); popv(i, :) w * popv(i, :) c1 * r1 .* (pbest(i, :) - popx(i, :)) c2 * r2 .* (gbest - popx(i, :)); % 速度限幅 popv(i, :) max(min(popv(i, :), vmax), -vmax); % 位置更新 popx(i, :) popx(i, :) popv(i, :); % 边界修正 popx(i, :) max(min(popx(i, :), ub), lb); end fitness_hist(t) gbest_fitness; fprintf(迭代次数 %d/%d, 最优SSE%.4f\n, t, maxgen, gbest_fitness); end end这段代码有几个细节值得说明。一是每次更新完空簇中心后要重新计算一次距离矩阵和簇分配不能沿用旧的idx二是粒子群迭代时没有在每次循环内执行完整的Kmeans收敛过程只是按粒子位置直接算SSE这样计算量小粒子寻优速度快三是边界修正用的是直接截断法把超出[0,1]的坐标拉回到边界。截断法虽然简单但容易让大量粒子聚集在边界附近所以速度限幅vmax一定不能太大0.2是我反复试下来比较合适的值。4.3 用最优粒子初始化Kmeans的收尾步骤PSO迭代结束后gbest就是一组质量较高的初始中心。下一步就是把这组中心给到kmeans函数做局部精调这部分在主脚本里已经写过。需要注意一个细节kmeans函数的Replicates参数要设为1因为我们已经有确定的初始中心不需要再随机多次尝试否则反而会引入随机性破坏PSO寻优的意义。%% 用PSO结果初始化Kmeans initCenters reshape(gbest, K, d); [clusterIdx, finalCenters, sumd] kmeans(X, K, ... Start, initCenters, ... MaxIter, 500, ... Replicates, 1, ... Display, final);为什么PSO搜索完之后还要再跑一次Kmeans因为PSO粒子群的搜索粒度相对粗糙每个粒子在迭代中扮演的是候选中心组合的角色它并没有保证最后的中心处在局部最优位置。而Kmeans在给定起点之后会沿着梯度方向做精确收敛两者的配合是取长补短。实测中同一份数据上这一步通常能让SSE再下降5%到10%。4.4 结果可视化和用户画像输出聚类之后光有簇标签是不够的业务方要看每个簇对应的用户长什么样。首先是迭代收敛曲线用来判断PSO是否充分收敛。然后是聚类结果的可视化我通常分成两部分一是把特征数据用tsne降到二维按簇标签着色看簇之间的分离情况二是按簇输出用户的平均日负荷曲线这样可以直接看到不同簇的用电形态差异。%% t-SNE降维可视化 rng(88); Y tsne(X, NumDimensions, 2, Perplexity, 30); figure; gscatter(Y(:,1), Y(:,2), clusterIdx, rbgm, ., 12); title(PSO-Kmeans聚类结果二维分布); legend(簇1-双峰上班型, 簇2-全天平稳型, 簇3-夜间充电型, 簇4-季节高耗型); %% 各簇平均日负荷曲线 % 假设rawCurve是 n*24 的原始日平均负荷矩阵用户数*24小时 figure; hold on; colors lines(K); for k 1:K members (clusterIdx k); meanCurve mean(rawCurve(members, :), 1); plot(0:23, meanCurve, Color, colors(k, :), LineWidth, 2); end xlabel(时刻(h)); ylabel(平均负荷(kW)); title(各簇用户平均日负荷曲线对比); legend(簇1, 簇2, 簇3, 簇4); grid on;每个簇的标签名称我是在得到聚类结果后根据这个簇的平均曲线形态和特征均值来命名的。比如夜间用电占比这个特征在某个簇里显著偏高平均曲线在22点到6点明显凸起那基本就是夜间充电型。这种命名方式在给业务方汇报时非常有用。5. 聚类效果怎么评价不是肉眼看图就够的5.1 三个内部指标SSE、轮廓系数与DBI聚类效果好坏的评判不能停留在二维散点图和平均曲线上需要量化指标。我常用的三个指标是SSE、轮廓系数和Davies-Bouldin指数DBI。SSE是簇内误差平方和前面已经定义过它衡量的是簇内紧密度越小越好。但它有一个问题SSE会随K增大而单调递减所以不能单独用来比较不同K的聚类效果只能用于同K下不同算法的对比。轮廓系数综合了簇内紧密度和簇间分离度每个样本的轮廓系数计算公式是[ s(i) \frac{b(i) - a(i)}{\max(a(i), b(i))} ]其中a(i)是样本i到同簇其他样本的平均距离b(i)是样本i到最近其他簇样本的平均距离。轮廓系数的取值在[-1,1]之间越接近1说明聚类效果越好。全体样本的轮廓系数平均值是我最看重的指标之一。DBI是另一种评价聚类效果的指标它计算每个簇的最大相似度与簇间中心距离的比值值越小说明簇内越紧凑、簇间越分散。Matlab里没有直接提供DBI函数我一般自己写一个简单的版本代码很短function dbi davies_bouldin(X, idx, centers) K max(idx); nClust K; % 计算每个簇的簇内散度 S zeros(nClust, 1); for k 1:nClust members X(idx k, :); if size(members, 1) 1 S(k) mean(pdist2(members, centers(k, :))); else S(k) 0; end end % 计算每对簇之间的中心距离 M pdist2(centers, centers); % 计算DBI dbi 0; for i 1:nClust max_ratio 0; for j 1:nClust if i ~ j ratio (S(i) S(j)) / M(i, j); if ratio max_ratio max_ratio ratio; end end end dbi dbi max_ratio; end dbi dbi / nClust; end在居民用电项目里我用这三个指标做算法对比时发现PSO-Kmeans的SSE通常比标准Kmeans低8%到12%轮廓系数平均高约0.05到0.1DBI也稳定下降。更重要的是多次运行标准偏差明显缩小这正是业务上最看重的可复现性。5.2 聚类中心和用户画像的对应关系评估聚类的业务价值还要看每个簇的特征均值和业务含义对得上。我拿某次实际项目的结果举例K4时四个簇的特征均值大概如下簇编号日平均负荷(kW)夜间用电占比负荷率用电形态描述簇10.420.220.31早晚双峰白天低谷典型上班族簇20.850.480.63全天平稳夜间不低老人家庭/地暖簇30.530.650.28深夜明显凸起电动车充电用户簇41.720.310.36整体负荷高夏冬季节性波动大对照表格会发现簇3的夜间用电占比0.65明显高于其他簇簇4的平均负荷是簇1的4倍这些数字和曲线的形态相互印证。聚类分析报告里我会把这些表格和曲线图一起给出业务方看完可以直接判断出每个簇代表哪类居民后续就能针对性地设计分时电价套餐或需求响应策略。6. 跑通之后才遇到的坑参数调优与实践经验6.1 粒子数和迭代次数到底怎么选粒子数N和迭代次数maxgen是PSO最关键的调参项。我的经验是N取值在20到50之间就够用了N越大搜索覆盖越全面但每一代的适应度计算量也线性上升。居民用电数据通常是几千到几万户特征维度7个左右N取30、maxgen取80整个优化过程在普通笔记本上大约一两分钟完全可以接受。迭代次数怎么判断够不够看收敛曲线。如果最优SSE在最后20代还在明显下降说明迭代不够需要增大maxgen如果曲线在30代之前就基本走平那说明还有很多算力被浪费了可以适当减小maxgen。另外w从0.9线性降到0.4配合maxgen来调如果maxgen改了w的衰减节奏也会变需要一起调整。6.2 K怎么定肘部法则与业务约束结合K值的选择直接影响聚类结果的可解释性。纯数据驱动的方法是用肘部法则跑K2到K8的聚类画出SSE随K变化的折线找到拐点作为最优K。但实际业务中K并不是越大越好因为每个簇都要能对应一种可描述的用电行为。我一般先跑肘部法则看数学上建议的K再结合业务方的标签需求做取舍。在我的项目里肘部法则给出的最优K通常是3或4但业务方希望区分出电动车用户所以K4最合适——四个簇分别对应上班族双峰型、全天平稳型、夜间充电型和季节高耗型。如果K取5多出来的那个簇往往是某两个簇的边缘碎片业务上不好解释所以我通常建议宁少勿多。6.3 随机性与稳定性问题PSO本身有随机性粒子初始位置和速度都是随机的所以即使算法逻辑完全一样两次运行的结果也可能有细微差别。为了让结果可复现我在主脚本里加了rng(42)。但我测试时也发现只要数据量不是特别小、PSO迭代次数充足PSO-Kmeans两次运行的轮廓系数差异能控制在0.01以内远好于标准Kmeans。如果你发现自己的PSO-Kmeans结果反复横跳先别怀疑算法大概率是两个原因一是粒子数太少或者迭代次数不足粒子群还没收敛就提前结束了二是初始中心落在边界被截断的位置导致粒子群的搜索空间受限。检查办法很简单把收敛曲线画出来看如果迭代后期的gbest还在大幅变动就是要加大迭代次数的信号。6.4 大数据量下的运行速度优化当用户数量到了几万户、特征维度也上去之后PSO每一代都要计算N个粒子对应的距离矩阵计算量还是不小的。这时候有几个优化手段可以用。第一是向量化计算。用pdist2一次算完所有样本到所有中心的距离避免循环里一层层算。第二是Mini-Batch策略每代随机采样一部分样本来计算适应度比如从3万户里抽5000户算SSE粒子寻优速度能快好几倍最后再用全部数据做一次Kmeans精调。第三是在保证效果的前提下降低N和maxgen实测N20、maxgen50时结果和N30、maxgen80差别很小但时间能省一半。我用过最大的数据集是8万用户、7维特征PSO-Kmeans跑完大约需要6到8分钟加Mini-Batch后压缩到两分钟以内。对于离线分析任务这个效率完全可以接受。最后分享一个我踩过几次坑之后的个人习惯无论用哪种方法跑聚类最终交付给业务方之前我都会固定随机种子完整复跑一遍记录SSE、轮廓系数、DBI三个指标同时画好收敛曲线和各簇平均负荷曲线。理由很简单业务方下次让你重新跑的时候你得能拿出完全一致的复现版本这些记录能让你从容应对每一次再跑一遍看看。
阅读完成 · 觉得有帮助?