MATLAB可复现k-means聚类:随机种子、K值选择与工程化脚本

发布时间:2026/11/6 2:54:41
MATLAB可复现k-means聚类:随机种子、K值选择与工程化脚本 简介这份《k-means数据聚类实验报告MATLAB详细代码》文档面向学习聚类分析、数据挖掘课程实验及需要MATLAB实现参考的学生与初级算法实践者系统梳理k-means的无监督划分思路与实验过程。文档围绕算法原理、流程步骤、Iris数据集聚类实验展开给出k3与k4的聚类效果对比并讨论初始类中心、数据输入顺序、k值选择对迭代次数和结果稳定性的影响。包内共1个doc文件约126KB以实验报告正文和MATLAB代码附录为主便于直接对照阅读算法描述与实现细节。已有2743人学习适合用于课程实验报告撰写、算法复现、参数敏感性分析与聚类方法入门。读者可从中获取完整的实验目的、步骤、结果分析、结论心得及可参考的MATLAB主程序代码快速理解k-means的收敛条件、平方误差准则和实际应用局限。1. 一份 k-means 实验报告被退回能跑通和能复现差在哪某高校实验室交上去的一份 k-means 数据聚类实验报告被打回来两次理由不在结果对错而在导师的三个追问换台机器重跑散点图的颜色还会一样吗K 取 4除了肘部图上的那个拐点还有没有别的依据表里那一列质心坐标能不能指着代码说清它是哪一步算出来的。这三个问题指向同一件事k-means 在 MATLAB 里跑通只要十几行但要让别人照着复现、让结论经得起问得把随机种子、初始化方式、K 的选择准则、结果回读四件事写进代码而不是留在口头。下面的顺序是先把目标函数写成看得懂的矩阵运算再把数据管线搭起来然后讨论 K 怎么定最后把踩过的坑和固化脚本的做法摆出来。适合正在写课程实验报告、要给业务方出一版分群结果或者打算把 MATLAB 原型交付出去的人。2. 把 k-means 的目标函数写进 MATLAB向量化距离、质心更新与空簇处理k-means 的全部内容就一句话反复做“把样本分给最近的质心”和“把质心挪到簇内均值”两件事直到质心不再动。最小化的目标是簇内平方误差和 $J\sum_{i}|x_i-c_{l_i}|^2$。它收敛是因为每一步都不会让 $J$ 变大但它只保证收敛到局部最优——这一点决定了后面所有关于初始化和多次重启的讨论。要写成能交差的实验报告光调kmeans函数不够。工具箱函数把初始化、重启、空簇处理都收进了黑匣子报告里说不清哪一步影响了结果也没法在缺工具箱的机器上跑。自己写一份核心循环二十来行可控性完全不一样。2.1 距离矩阵用展开式算别拼三维数组新手最容易写成三层循环或者n×k×d的三维数组样本上到五万行、特征几十维内存直接顶到天花板。平方欧氏距离有现成的展开式$|x-c|^2|x|^2-2x^\top c|c|^2$右边三项都是矩阵乘法交给 BLAS 处理。function [labels, centroids, Jhist] kmeans_core(X, k, seed, maxIter, tol) % 输入 % X n x d 数值特征矩阵一行一个样本不能含 NaN % k 簇数 % seed 随机种子实验报告里必须写死否则每次图都不一样 % maxIter 最大迭代轮数默认 300 % tol 质心最大位移容差小于它就认为收敛 % 输出 % labels n x 1 簇编号 % centroids k x d 质心坐标 % Jhist 每轮的簇内平方和用来画收敛曲线 if nargin 4 || isempty(maxIter), maxIter 300; end if nargin 5 || isempty(tol), tol 1e-6; end rng(seed); % 固定随机流复现的第一步 n size(X, 1); if k n, error(簇数不能大于样本数); end centroids X(randperm(n, k), :); % 从真实样本里挑 k 个做初值 labels zeros(n, 1); Jhist zeros(maxIter, 1); for it 1:maxIter % 平方欧氏距离展开n x k一次矩阵乘法出结果 D sum(X.^2, 2) sum(centroids.^2, 2) - 2 * (X * centroids); D max(D, 0); % 浮点误差可能给出 -1e-14截掉 [~, labels] min(D, [], 2); % 第二个参数 2 表示沿列方向取最小 counts accumarray(labels, 1, [k 1]); % 每簇样本数 sums accumarray(labels, X, [k size(X, 2)]); % 每簇特征求和 newC sums ./ max(counts, 1); % 空簇先占位下面补 % 空簇兜底把离自己质心最远的样本提上来当新质心 for e find(counts 0) own D(sub2ind(size(D), (1:n), labels)); [~, farIdx] max(own); newC(e, :) X(farIdx, :); labels(farIdx) e; end Jhist(it) sum(min(D, [], 2)); % 当前分配的 J shift max(sqrt(sum((newC - centroids).^2, 2))); % 质心最大位移 centroids newC; if shift tol, break; end % 位移够小就停 end Jhist Jhist(1:it); endX必须是double整型矩阵做.^2会溢出D max(D,0)这行看着多余但在维度高、数值量级大的数据上不加会出负距离紧接着min选出的归属就会莫名其妙。min(D,[],2)里的2是维度参数漏了它会把整个矩阵压成一个标量症状是所有样本被分到同一簇。这里没用pdist2因为它是统计工具箱的函数换个机房、换台笔记本就可能报未定义。展开式只依赖基础矩阵运算代价是数值稳定性略差靠max(D,0)兜住就够了。距离度量也不一定非欧氏不可特征稀疏、维度高比如文本向量时余弦距离更合适把D换成1 - X*centroids ./ (vecnorm(X,2,2) * vecnorm(centroids,2,2))即可特征是有量纲的计数且差异体现在绝对偏移上时曼哈顿距离对异常值更稳。选哪种要在报告里写一句理由别默认欧氏。2.2 质心更新、空簇与收敛判据的三个细节accumarray是这段代码里最值得记住的函数第一个参数是分组下标正整数第二个是待累加的值第三个是输出尺寸。accumarray(labels, X, [k size(X,2)])直接得到 k 行、每行是该簇所有样本的特征和比for j 1:k, sum(X(labelsj,:))快一个量级因为后者每轮都要做 k 次全表比较。max(counts,1)是为了防零除——如果某簇在这一轮变空sums那一行是全零除以 0 会得到NaN然后NaN会沿着后面的距离计算污染整个矩阵最终所有标签都变成 1。空簇在真实数据上并不罕见尤其是初始质心挨得太近、或者某一簇本身只有几个离群点。两种处理方式一是代码里的重新播种把离自己质心最远的样本点拉过来当新质心好处是几乎不增加计算量二是合并到最近的簇然后 k 减一。前者适合报告里演示后者适合 k 有业务含义、不允许变的场景。我一般用前者但在日志里打一行警告因为空簇频繁出现说明 k 给大了或者数据本身有高度重复的点。tol控制的是质心最大欧氏位移不是 J 的相对变化。1e-6 在标准化后的数据上通常 10 到 30 轮收敛如果特征量级在 1e6 以上这个阈值等于没有得按特征尺度调到 1e-3 之类。Jhist别看它只是个数列画出来能立刻分辨两种失败一种是曲线一路平到底、第 2 轮就停——初始质心选得太好或者 k 太小另一种是曲线反复小幅抖动几十轮——数据里有大量近似重复点标签在两组等距质心之间来回跳。2.3 先拿合成数据验一遍再碰真实数据真实数据出问题时你分不清是代码错还是数据怪。先用两组结构已知的合成数据把管线跑通是省时间的做法。rng(7); % 场景一三个各向同性高斯簇k-means 的舒适区 mu [0 0; 6 0; 3 6]; Xg []; for i 1:3 Xg [Xg; mu(i,:) 0.9 * randn(150, 2)]; end trueG repelem((1:3), 150); % 真实标签用于算纯度 % 场景二两个同心半圆非凸结构用来暴露 k-means 的失效边界 t linspace(0, pi, 200); Xm [[cos(t), sin(t)]; [2*cos(t), 2*sin(t)]] 0.05 * randn(400, 2); trueM repelem((1:2), 200); [Lg, Cg, Jg] kmeans_core(Xg, 3, 1); [Lm, Cm, Jm] kmeans_core(Xm, 2, 1); fprintf(高斯数据 纯度 %.3f迭代 %d 轮J %.2f\n, ... purity(trueG, Lg), numel(Jg), Jg(end)); fprintf(半圆数据 纯度 %.3f迭代 %d 轮J %.2f\n, ... purity(trueM, Lm), numel(Jm), Jm(end)); function p purity(trueLab, predLab) % 纯度每个预测簇取占比最高的真实类别按簇大小加权 u unique(predLab); hit 0; for i 1:numel(u) hit hit max(accumarray(trueLab(predLab u(i)), 1)); end p hit / numel(trueLab); end跑完你会看到高斯数据上纯度接近 1.0半圆数据上纯度只有 0.5 出头两个半圆各被劈成一半再拼起来。这不是代码写错了是 k-means 的目标函数本身就假设簇是凸的、各向同性的、大小相近的——它只会用直线高维下是超平面去切空间。报告里把这两组对照放进去比只贴一张漂亮的三色散点图有说服力得多因为它同时说明了方法的适用边界。纯度只看“每个簇里最多的那类占多少”簇数给多了它会虚高所以还得配一个对簇数不敏感的指标这点放到第 4 章讲。3. 数据管线决定上限缺失值、量纲与 PCA 降维前后的聚类差异聚类结果不好八成问题出在进kmeans_core之前。算法本身没有“理解”能力它只认数值和距离字段里有一个量级差三位的金额列整个空间就被这一维主宰了。3.1 缺失值、异常值与量纲的处理顺序顺序很重要先删列再补缺再压异常值最后标准化。反过来做先标准化会把缺失值算成NaN传染整列先补缺再删列等于白补。情况处理方式判断依据单列缺失率 30%整列剔除补出来的值占比太高等于造数据缺失率 5%~30%中位数或线性插值补齐分布偏斜时中位数比均值稳数值列量级差 100 倍z-score 标准化欧氏距离对量级敏感存在明显离群点MAD 稳健 z 分数|z|3.5 视为异常均值和标准差本身会被离群点带偏ID、时间、文本列单独存一份不进特征矩阵参与距离计算没有意义T readtable(features.csv, VariableNamingRule, preserve); % 只把数值列拿进特征矩阵ID 和文本列留档 isNum varfun(isnumeric, T, OutputFormat, uniform); ids T(:, ~isNum); Xraw table2array(T(:, isNum)); % 一、按缺失率删列 missRate mean(ismissing(Xraw), 1); Xraw Xraw(:, missRate 0.30); % 二、补齐列内线性插值两端用最近值兜底 Xraw fillmissing(Xraw, linear, EndValues, nearest); % 三、MAD 稳健 z 分数压离群点先标 NaN 再补 med median(Xraw, 1); madz 1.4826 * median(abs(Xraw - med), 1); % 1.4826 让 MAD 对齐正态标准差 Z (Xraw - med) ./ max(madz, eps); % eps 防常数列为 0 Xraw(abs(Z) 3.5) NaN; Xraw fillmissing(Xraw, nearest); % 四、标准化让每一维在距离里权重相同 X zscore(Xraw);1.4826这个常数不是拍脑袋来的正态分布下 MAD 乘上它才等于标准差所以 3.5 这个阈值和常见的 3σ 大致可比。max(madz, eps)是给常数列留活路——某一列全表一个值MAD 是 0不兜底会出Inf后面全变NaN。fillmissing的EndValues,nearest只在linear方法下有效管首尾两端的NaN。数据量大、时间有序时插值比填均值合理如果行与行之间没有顺序关系比如问卷直接填中位数反而更老实。3.2 标准化与 PCA 之后聚类结果差多少很多人以为 PCA 只是降维提速实际上它会改变簇的形状。标准化把每一维拉到同一尺度PCA 再旋转坐标系、按方差排序前几个主成分往往集中了绝大部分方差——这时候欧氏距离又被前两三个主成分主导了等于换了个方式重复量纲问题。所以 PCA 之后要不要再对主成分做白化是个得实测的选择。% 标准化后做 PCA看累计方差 [coeff, score, latent] pca(X); cumVar cumsum(latent) / sum(latent); p find(cumVar 0.85, 1); % 取到 85% 方差所需的主成分数 Xp score(:, 1:p); % 三种输入下的聚类结果对比 [L1, ~] kmeans_core(X, 3, 1); % 标准化原空间 [L2, ~] kmeans_core(Xp, 3, 1); % PCA 前 p 维 Xw zscore(score(:, 1:p)); % 主成分白化 [L3, ~] kmeans_core(Xw, 3, 1); fprintf(原空间 vs PCA ARI %.3f\n, ari(L1, L2)); fprintf(原空间 vs 白化PCA ARI %.3f\n, ari(L1, L3)); fprintf(前三主成分解释 %.1f%% 方差\n, 100 * cumVar(p)); function a ari(l1, l2) % 调整兰德指数对标签编号置换不敏感衡量两次划分的一致程度 n numel(l1); [~, ~, i1] unique(l1); [~, ~, i2] unique(l2); M accumarray([i1 i2], 1); nij sum(M(:) .* (M(:) - 1) / 2); ai sum(sum(M,2) .* (sum(M,2) - 1) / 2); bj sum(sum(M,1) .* (sum(M,1) - 1) / 2); tot n * (n - 1) / 2; expct ai * bj / tot; mx (ai bj) / 2; a (nij - expct) / (mx - expct eps); endARI 取值大致落在 0 到 1 之间1 表示两次划分完全一致标签编号不同也算一致0 附近表示跟随机分组差不多。这个指标的好处是不用真实标签也能用特别适合写报告时比较“不同预处理方案下结论稳不稳”。如果你的报告里要对比三种方案一张 ARI 矩阵比三张散点图直观。白化这一步值得单独说zscore(score(:,1:p))把每个主成分也拉到单位方差等于告诉算法“第 1 主成分和第 5 主成分一样重要”。原始特征维度之间本来没有可比性这么做通常合理但如果某个主成分确实承载了核心业务信号白化会把它稀释掉。我的习惯是两种都跑ARI 差距小于 0.1 就用白化版因为它的结果对随机种子更不敏感。3.3 结果回写与出图报告里该出现的三样东西散点图只画前两个特征但聚类用的是全部维度图上看不出的东西得靠数字补。报告里至少要有带质心的散点图、轮廓值分布图、以及一张把标签写回原始表的结果表。% 距离矩阵用于取每个样本到自身质心的距离 D sum(X.^2, 2) sum(C1.^2, 2) - 2 * (X * C1); D max(D, 0); [~, L1] min(D, [], 2); figure(Position, [100 100 960 400]); subplot(1,2,1); gscatter(X(:,1), X(:,2), L1, lines(3), ., 8); hold on; plot(C1(:,1), C1(:,2), kp, MarkerSize, 14, MarkerFaceColor, y); title(前两维散点与质心); hold off; subplot(1,2,2); silhouette(X, L1, Euclidean); % 每个点的轮廓值越接近 1 越贴合本簇 title(轮廓值分布); % 标签和“到质心距离”一起写回方便后续按簇下钻 T.cluster L1; T.dist2centroid sqrt(min(D, [], 2)); writetable(T, clustered_result.csv);dist2centroid这一列常被忽略实际很有用同一个簇里距离 0.1 的点和距离 4.0 的点业务含义完全不同。把它一起导出后续做分群画像时可以直接取每簇最核心的样本看典型特征也可以把距离最大的那批单独拉出来看是不是异常样本。subplot里两张图并排是因为散点图会让你误判——看起来分得很开的两个簇在完整维度上可能轮廓值只有 0.2。轮廓值分布图才是判断这次聚类到底靠不靠谱的那张。4. 簇数 K 怎么定肘部法、轮廓系数与多次重启的实测对照K 是 k-means 唯一的外生参数也是最容易在报告里被追问的地方。常见的三种做法——肘部法、轮廓系数、Gap 统计量——各有各的失效场景指望某一个自动给出正确答案基本是玄学。稳妥的做法是三种都算看它们是否指向同一个区间。4.1 肘部法的读数陷阱与二阶差分肘部法的逻辑是K 增大时簇内平方和 J 单调下降但下降速度会在真实簇数附近突然放缓画出来像个手肘。问题在于“突然”这个判断太主观曲线平滑的时候十个人能看出五个肘部。ks 2:10; sse zeros(size(ks)); for i 1:numel(ks) [~, ~, Jh] kmeans_core(X, ks(i), 42); % 固定种子保证可比较 sse(i) Jh(end); end d1 diff(sse); % 一阶差分J 的下降量 d2 diff(sse, 2); % 二阶差分下降量的变化率 [~, idx] max(d2); % 曲率最大处就是肘部 kElbow ks(idx 1); fprintf(肘部法给出 K %d\n, kElbow); plot(ks, sse, -o); xlabel(K); ylabel(簇内平方和 J);diff(sse,2)里第二个参数是差分阶数意思是“下降速度本身的变化”。曲线上肘部明显时二阶差分会在那里出现一个尖峰曲线整体平滑时这个尖峰只有零点几个单位和噪声没区别这时max挑出来的位置基本随机。所以我会在报告里同时贴原始曲线和二阶差分如果尖峰不突出就明说“肘部法在这里不可靠改用轮廓系数”。另外两个坑一是ks从 2 开始而不是 1K1 时 J 必然最大放进去会把整个曲线的纵向尺度拉爆后面的变化被压平二是每轮必须用同一个种子否则 J 的抖动来自随机初始化而不是 K 的变化曲线会呈锯齿状看着像有一堆肘部。4.2 轮廓系数能给出绝对好坏但对非凸簇天生苛刻轮廓系数衡量的是“点离自己簇有多近、离最近的其他簇有多远”单点取值在 -1 到 1 之间取所有点的均值作为该 K 的得分。相比肘部法它有个明显优势给的是绝对评价0.6 就是比 0.3 好不依赖眼睛。ks 2:10; s zeros(size(ks)); for i 1:numel(ks) [L, ~] kmeans_core(X, ks(i), 42); if numel(X) 20000 % 大数据抽样silhouette 是 O(n^2) idx randperm(numel(L), 20000); s(i) mean(silhouette(X(idx,:), L(idx))); else s(i) mean(silhouette(X, L)); end end [bestS, bi] max(s); fprintf(轮廓系数最优 K %d得分 %.3f\n, ks(bi), bestS);silhouette内部要算点对之间的距离样本量上万时内存和时间都会吃紧抽样是常规处理但要在报告里写清抽了多少。得分解读上0.5 以上说明结构清晰0.25 到 0.5 之间算“有结构但不强”0.25 以下基本可以认为这组特征上不存在明显的簇结构——这时候不要硬凑 K先回头检查特征选得对不对。前面半圆数据那个例子轮廓系数会在 K2 时给出一个相当不错的分数但聚类结果肉眼可见是错的。原因是轮廓系数同样基于欧氏距离它衡量的是“球形簇”的紧致程度。遇到这种矛盾轮廓系数高但业务上看不合理说明数据分布本身不满足 k-means 的假设该换密度聚类或者谱聚类而不是继续调 K。4.3 多次重启加 k-means把随机性压到可接受范围同一个 K、不同的随机初值J 能差出十几个百分点这是 k-means 最让人不放心的地方。两个手段组合起来能把它压住。function C kmeanspp_init(X, k, seed) % k-means第一个质心随机取之后每个质心按 D^2 的概率挑 rng(seed); n size(X, 1); C zeros(k, size(X, 2)); C(1, :) X(randi(n), :); d2 sum((X - C(1,:)).^2, 2); for j 2:k p d2 / sum(d2); idx find(rand cumsum(p), 1, first); % 不用 randsample免工具箱 C(j, :) X(idx, :); d2 min(d2, sum((X - C(j,:)).^2, 2)); % 只保留更近的距离 end end % 多次重启留最优 bestJ inf; bestL []; bestC []; for r 1:20 C0 kmeanspp_init(X, 4, r); % 用 k-means 播种 [L, C, Jh] kmeans_core_from(X, 4, C0, 300, 1e-6); if Jh(end) bestJ bestJ Jh(end); bestL L; bestC C; end end fprintf(20 次重启最优 J %.2f\n, bestJ);kmeanspp_init里的关键在于p d2 / sum(d2)已经被选中质心覆盖过的区域距离小、被选中的概率也小新质心倾向于落在还没被代表的地方。这比纯随机播种好多少在合成高斯数据上纯随机 20 次重启的 J 极差通常在 5% 上下k-means 能压到 1% 以内迭代轮数也少一半。kmeans_core_from是我把kmeans_core拆出来的一个变体——接受外部传入的初始质心其余逻辑一致代码这里省略。报告里值得记录的一个数字是20 次重启中 J 的最大值、最小值和标准差。标准差大说明这次聚类本身不稳定即使拿到最优解结论也要打折扣。还有一点K 的扫描和重启次数是乘法关系。K 从 2 扫到 10、每次重启 20 遍就是 180 次完整聚类。小数据没问题几十万行时建议先抽 10% 做 K 的粗扫选定 K 之后再用全量数据跑最后一次并把这次的种子写进报告。5. 避坑与排查五条让 k-means 结果翻车的记录下面这些是真实踩过、并且在别人的报告里反复见到的。每条按“现象、原因、解决”写症状描述尽量具体到能对上号。5.1 结果层面的三个坑坑一同一份数据跑两次簇的颜色全换了。现象是散点图的形状一模一样但簇 1 变成了簇 2质心坐标表对不上上一版。原因有两层一是没调rng固定种子初始化不同导致收敛到不同局部最优二是即便 J 一样k-means 输出的标签编号本身没有语义第一次的第 1 簇可能就是第二次的第 3 簇。解决上种子必须写进代码且记进报告标签不要直接比对编号用质心坐标排序后再对齐——按第一主成分或者按簇大小从大到小重排或者在报告里直接贴质心坐标表别人一看就懂。真要严谨用第 3 章的 ARI 比对两次划分。坑二K 从 2 加到 8J 一路下降报告里写“K8 最优”。这是最典型的误读。J 对 K 单调不增K 等于样本数时 J 恰好是 0。光看 J 挑 K永远会挑到最大的那个。解决是加约束轮廓系数取最大、肘部法看拐点、或者直接按业务可解释性定上限。我一般把 K 的搜索范围限制在 2 到 $\sqrt{n/2}$ 之间超过这个量级簇的平均样本数已经少到做画像没有统计意义了。坑三某一列金额把距离全吃掉了结果只按金额分群。现象是分出来的簇在其他特征上几乎没差别唯独金额从低到高排得整整齐齐。原因是该列方差比其他列大三个数量级平方之后在距离里占了 99% 的权重。解决就是标准化但要注意标准化必须用同一套均值方差——训练集算出来的均值方差应用到新数据上不能每次重算。如果该列本身是长尾分布多数人在低位、极少数极高zscore之后仍会被极值拉扯先取对数再标准化更稳。5.2 实现层面的两个坑坑四样本量一大就内存溢出任务被系统杀掉。现象是十万行、五十维的数据跑到距离计算那一步 MATLAB 直接卡死或者报内存不足。原因是有人为了“算清楚”先构造了n×n的距离矩阵十万乘十万就是八十亿个 double。k-means 全程只需要n×k的距离k 通常不超过 20差了五个数量级。解决是坚持用第 2 章的展开式任何时候都不要在 k-means 里出现pdist生成的全距离矩阵如果确实要用pdist2算到质心的距离也要显式传质心矩阵而不是整个数据集。坑五结果表写回之后行序错乱标签和样本对不上。现象是导出 CSV 打开一看某个 ID 对应的簇号跟图上的位置明显不符。原因是在预处理阶段用Xraw(completeIdx,:)删过缺失行但写回时用的是原始表T两者行数不同MATLAB 按位置赋值错位就发生了。解决的铁律是全程保留一个唯一 ID 列所有过滤、抽样都只操作索引最后用 ID 做join合并标签永远不要依赖行号对齐。这条在跨步骤的脚本里尤其重要一个中间步骤改了行序后面所有分析都是错的。6. 把实验报告固化成脚本种子、日志与一键复现的验证清单到这一步代码能跑、图能出、K 有依据了但离“交出去别人能复现”还差一层。我的习惯是把整份实验拆成四个文件用主脚本串起来任何一个环节改了都能重跑。文件职责不许做的事prep_data.m读表、删列、补缺、标准化输出X.mat和ids.mat不做聚类不画图select_k.mK 扫描、肘部与轮廓系数、输出 K 的选择依据不写最终标签run_cluster.m用选定 K 和固定种子跑 20 次重启输出最优标签与质心不改预处理参数report_fig.m只读X.mat和结果文件出图与导出 CSV不做任何计算决策分开的好处很直接导师问“换个标准化方式会怎样”你只改prep_data.m重跑其余三个文件一行不动对比实验十分钟出结果。全塞在一个脚本里改一处就得从头跑跑到一半忘了哪步改过。验证清单我固定查四项。第一连跑两次用ari(L_first, L_second)比对结果必须是 1.000不是就说明某个环节的种子没固定住。第二把select_k.m的输出存成k_selection.mat里面除了 K 还要存扫描曲线和轮廓得分重跑时先比对这两条曲线是否逐点相同。第三日志里记下数据文件的行数、列名、每列的缺失率和标准化后的均值方差这些数字比“数据已预处理”这句话有用得多。第四最终结果表的行数必须等于原始表的行数用height(T) height(Tout)断言一次能挡住第 5 章坑五那类错位。顺带说一个提速的技巧K 扫描阶段用抽样但不要把抽样的结论直接当最终结果。我的做法是先抽 10% 跑一遍拿到 K 的候选区间再在全量数据上只跑区间内的两三个 K最后只对选定的 K 做完整的 20 次重启。这样总耗时约为全量扫描的三分之一结论基本一致。血泪经验是报告里最值钱的不是那张漂亮的散点图而是能让人把你的脚本下载下来、改个数据路径、跑出同样数字的那套东西。我早期交报告只贴图和结论被问过一次“你这个 K 是试出来的还是算出来的”答不上来后来才养成把种子、日志、断言都写进代码的习惯。这份后悔药不难吃只是要在一开始就想到。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询