Kmeans算法简介
作为无监督学习领域的一种简单的算法,Kmeans在实际应用中却是相当广泛的。其过程是通过不断交替迭代求得最优的类中心以及每个样本所属类别,具体步骤如下:
- 确定类别个数k
- 随机初始化k个类的中心,分别为(\mu_1, \mu_2, …, \mu_k )
- 确定每个样本类别,原则为样本与类中心距离最小,即
\begin{aligned} c^{(i)}=\underset{j}{arg min}Dist(x^{(i)}, \mu_j) \end{aligned}- 更新每个类的中心
\begin{aligned} \mu_j = \frac{\sum_{i=1}^m I(c{(i)}=j)x{(i)}} {\sum_{i=1}^m I(c^{(i)}=j)} \end{aligned}
若已收敛,则结束迭代,否则转到3。迭代是否收敛可以跟据本次与前一次每个类的中心的变化来确定。
算法实现
其实matlab本身已经有kmeans的函数,这里实现主要是熟悉算法过程。首先生成训练样本,这里生成3类高斯分布的样本,代码如下:
N = 100;
%生成第0类数据
mu = [2 2];
Sigma = [1 .5; .5 2]; R = chol(Sigma);
x = repmat(mu,N,1) + randn(N,2)*R;
y = zeros(N,1);
Sample = [x y];
figure, plot(x(:,1),x(:,2),'k.','LineWidth',3); hold on
%生成第1类数据
mu = [8 6];
Sigma = [1.0 0.5; 1.5 1.7]; R = chol(Sigma);
x = repmat(mu,N,1) + randn(N,2)*R;
y = ones(N,1);
Sample = [Sample;x y];
plot(x(:,1),x(:,2),'g.','LineWidth',3);
%生成第2类数据
mu = [5 -6];
Sigma = [2 0.3; 0.3 1.4]; R = chol(Sigma);
x = repmat(mu,N,1) + randn(N,2)*R;
y = 2 * ones(N,1);
Sample = [Sample;x y];
plot(x(:,1),x(:,2),'b.','LineWidth',3);
save('data.mat', 'Sample')
生成的样本数据如下图:
kmeans算法的实现代码如下:
function [Label C IterInfo] = MyKmeans(Samples, k)
[m n] = size(Samples); %m个n维样本
C = Samples(1:k, :);
C1 = C;
MaxIterTime = 1000;
i = 0;
Label = zeros(m, 1);
IterInfo.C = {C};
while(i < MaxIterTime)
C1 = C;
%calc label
for j = 1:m
x = Samples(j,:)';
Label(j) = GetLabel(x, C1);
end
%calc center
for j = 1:k
idx = find(Label == j);
C(j, :) = mean(Samples(idx, :));
end
IterInfo.C = [IterInfo.C, {C}];
i = i + 1;
%end iter
if max(abs(C-C1)) < 1e-3
break;
end
end
IterInfo.IterTime = i+1;
end
function d = Dist(x, y)
d = (x - y)' * (x - y);
end
function L = GetLabel(x, C)
[k, n] = size(C);
min_d = inf;
L = 0;
for i = 1:k
d = Dist(x, C(i, :)');
if d < min_d
min_d = d;
L = i;
end
end
end
对于上面的一组样本,迭代了5次就已经收敛,可见速度相当快,每次迭代结果如下图所示,红色方块是3个类的中心。
算法分析
- 关于算法的收敛性。算法的代价函数为每个样本距离其所属类别的中心距离和,即
\begin{aligned} J=\sum_{i=1}mD(x{(i)}, \mu_{c(i)}) \end{aligned}
可以比较容易分析出,每一次迭代,此函数J是在递减的。根据课件中所讲,该函数为非凸函数,迭代可能陷入局部最优解,因此可以多次随机初始化类中心来比较结果(这一点我暂时还没法从理论上证明)。
然而某些特殊情况又可能导致不同的初始化类心会计算出不同的结果,比如3个样本点,两两距离相等(即等边三角形的3个顶点)的情况。 - 算法需要输入类别数k,这一点在很多实际问题中是比较困难的。当然目前有一些文章给出了自动确定k的方法,我还未详细了解,后续找时间补上。但基本的思想是通过检验不同k的条件下分类结果的聚合性来选择最优k。
- kmeans中使用的距离度量可以有很多种,如欧式距离,街区距离,余弦距离等等,具体使用哪种需要根据实际情况来定。上面代码中使用的是欧式距离。
- kmeans算法不是万能的,它只能出来类别呈中心聚集的情况。比如下面左图的情况kmeans的处理结果就很差。但是通过极坐标变换后(见右图),两类点就都分布呈中心聚集的状态,再使用kmeans就OK了。