K-means算法作为无监督学习中最经典的聚类方法之一,在数据挖掘、图像分割、市场分析等领域有着广泛应用。MATLAB凭借其强大的矩阵运算能力和丰富的可视化工具,成为算法快速实现和验证的首选平台。这个项目实现了K-means算法从理论到实践的完整闭环,特别针对聚类效果评价这一常被忽视的环节,提供了完整的指标计算方案。
在实际工程应用中,我发现很多开发者只关注聚类过程本身,却忽略了结果验证的重要性。这就像厨师只注重烹饪过程却不尝味道一样危险。本项目特别整合了轮廓系数、Davies-Bouldin指数等5种主流评价指标,帮助开发者从不同维度评估聚类质量。我曾在一个工业设备故障检测项目中,通过系统化的指标对比,发现了传统欧氏距离度量不适用的问题,最终通过马氏距离改进使准确率提升了23%。
K-means本质是通过迭代寻找最优的簇中心划分,其数学本质是最小化平方误差函数:
code复制J = ΣΣ ||x - μ_i||²
其中μ_i表示第i个簇的质心。在MATLAB中实现时,需要特别注意三个关键环节:
matlab复制[IDX, C] = kmeans(data, k, 'Start', 'plus');
matlab复制opts = statset('Display','final');
[IDX, C] = kmeans(data, k, 'Distance', 'cosine', 'Options', opts);
一个健壮的实现应该包含以下模块:
matlab复制function [clusters, centroids, metrics] = myKmeans(data, k)
% 初始化模块
centroids = initCentroids(data, k);
% 迭代模块
max_iter = 100;
for iter = 1:max_iter
% 分配阶段
clusters = assignPoints(data, centroids);
% 更新阶段
new_centroids = updateCentroids(data, clusters);
% 收敛判断
if norm(new_centroids - centroids) < 1e-6
break;
end
centroids = new_centroids;
end
% 评价模块
metrics = evaluateClusters(data, clusters, centroids);
end
关键提示:在分配阶段使用矩阵运算而非循环,能显著提升速度。例如计算所有点到质心的距离时:
matlab复制distances = sqrt(sum((reshape(data,[],1,size(data,2)) - reshape(centroids,1,[],size(data,2))).^2, 3));
衡量样本与同簇和其他簇的紧密程度,范围在[-1,1]之间。MATLAB实现时要注意避免重复计算距离矩阵:
matlab复制function s = silhouetteScore(data, labels)
k = max(labels);
s = zeros(size(data,1),1);
% 预计算距离矩阵
D = pdist2(data, data);
for i = 1:length(labels)
a = mean(D(i, labels == labels(i) & (1:end)' ~= i));
b = inf;
for j = 1:k
if j == labels(i), continue; end
b = min(b, mean(D(i, labels == j)));
end
s(i) = (b - a) / max(a,b);
end
end
衡量簇间分离度与簇内紧密度之比,值越小越好。计算时需处理可能出现的除零错误:
matlab复制function db = daviesBouldin(data, labels, centroids)
k = size(centroids,1);
S = zeros(k,1);
for i = 1:k
S(i) = mean(sqrt(sum((data(labels==i,:) - centroids(i,:)).^2, 2)));
end
R = zeros(k);
for i = 1:k
for j = i+1:k
Mij = norm(centroids(i,:) - centroids(j,:));
R(i,j) = (S(i) + S(j)) / Mij;
end
end
db = mean(max(R,[],2));
end
matlab复制data = zscore(data); % 比 (data - min(data)) / (max(data)-min(data)) 更稳健
matlab复制[coeff, score] = pca(data);
scatter(score(:,1), score(:,2), 15, labels, 'filled');
肘部法则的改进实现:通过二阶差分自动检测拐点
matlab复制ks = 2:10;
inertias = zeros(size(ks));
for i = 1:length(ks)
[~, ~, sumd] = kmeans(data, ks(i));
inertias(i) = sum(sumd);
end
% 计算曲率变化率
diff2 = diff(diff(inertias));
[~, optimal_k] = max(diff2);
optimal_k = ks(optimal_k + 1);
在轴承故障诊断项目中,我们处理的是12维的振动信号数据。传统阈值法误报率达18%,而通过以下流程实现了7%的误报率:
关键代码片段:
matlab复制% 特征提取
features = [max(data)-min(data); kurtosis(data); waveletEnergy(data)]';
% 聚类优化
k_range = 3:6;
for k = k_range
[labels, centroids] = myKmeans(features, k);
silh(k-min(k_range)+1) = mean(silhouetteScore(features, labels));
end
[~, best_k] = max(silh);
matlab复制opts = statset('UseParallel',true);
[IDX, C] = kmeans(data, k, 'Options', opts, 'MaxIter', 100, 'OnlinePhase','on');
matlab复制dataGPU = gpuArray(single(data));
[IDX, C] = kmeans(dataGPU, k);
matlab复制function dist = fastCosineDist(a,b)
an = a ./ sqrt(sum(a.^2,2));
bn = b ./ sqrt(sum(b.^2,2));
dist = 1 - an * bn';
end
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 聚类结果不稳定 | 随机初始化导致 | 使用kmeans++初始化,固定随机种子 |
| 轮廓系数出现NaN | 存在单样本簇 | 设置min_cluster_size参数合并小簇 |
| DBI值异常大 | 存在重叠簇 | 尝试不同的距离度量或数据标准化 |
| 迭代不收敛 | 数据存在大量重复点 | 预处理时合并重复样本 |
| 内存不足 | 样本量过大 | 改用batch处理或降维 |
在最近的一个电商用户分群项目中,遇到DBI值突增的情况。最终发现是某个用户群体的消费金额特征存在极端值,通过Winsorize缩尾处理后问题解决:
matlab复制data(:,1) = winsorize(data(:,1), [5 95]); % 处理前后5%的极端值