聚类算法是无监督学习算法,对于没有打上标签的数据,可以采用聚类算法。下面介绍两种常用的算法KMeans和谱聚类。
1 Kmeans
假设有数据集合
{xi}
,
1≤i≤N
,
xi∈Rn
,如果想把该数据集合分成k个类,应该如何划分?这就是聚类问题。例如有如下二维数据(以二维为例方便展示)
图1.1 三类数据
对于图1.1所示的数据可以使用KMeans方法对其进行聚类,KMeans算法是最简单高效稳健的算法,而且很容易并行化,但是其有个问题就是无法处理非凸集问题,处理非凸集问题的算法,将在下一节中介绍谱聚类将解决非凸集问题。
KMeans算法的流程如下:
(1)适当选择
k
个类的初始中心,可以随机初始化;
(2)在第
(3)利用均值等方法更新该类的中心值;
(4)对于所有的
对于以上的流程实现的代码如下
def kmeans(data, k, tol = 1e-6):
data = np.array(data)
import random
index = np.arange(0, len(data))
random.shuffle(index)
index = index[0: k]
#(1)随机初始化初始的聚类中心
init_center = data[index, :]
labels = np.zeros((data.shape[0],),dtype=np.int)
iter_ = 0
while iter_ < 100 :
err = 0.0
#(2)在k次迭代中将样本分配到最近的聚类中心中
for i in xrange(data.shape[0]):
di = data[i, :]
max_dist = 1e100
tmp = []
for center in init_center:
dis = pdist([di, center])
tmp.append(dis[0])
labels[i] = np.argmin(tmp)
#(3)计算新类的聚类中心
new_center = []
for label in set(labels):
label_data = data[labels == label, :]
new_center.append(np.sum(label_data, axis=0) / len(label_data))
#(4)计算新的聚类中心和旧的聚类中心的差异
for i in range(0, k):
err = err + norm(new_center[i] - init_center[i])
init_center = new_center
iter_ = iter_ + 1
#画出每次迭代后的类别
observer(iter_, data, labels, new_center)
#如果新的聚类中和旧的聚类中心的误差小于阈值停止迭代
if err < tol:
break
return labels
图1.2是图1.1经过十次迭代后的结果
2 谱聚类
图2.1 谱聚类的原始数据
对于图2.1所示的数据KMeans就无能为力了,用KMeans对图2.1聚类的结果如下
图2.2 KMeans对图2.1的聚类结果
下面看下谱聚类对图2.1的聚类结果
图2.2 谱聚类对图2.1的聚类结果
谱聚类的计算流程如下:
假设存在数据点集
(0)对S进行中性化
(1)构造相似矩阵 A∈R{n×n} , Ai,j=exp(−d2(si,sj)σ2) , i≠j , Ai,i=0 , −d2(si,sj) 为距离函数,通常为 si 和 sj 的欧拉距离, σ 为一重要的标量参数,下面会有详细介绍。
(2)构造对角矩阵
D
,
(3)选择一个期望聚类的数目C。
(4)找出L中最大的C个最大的特征值向量
x1,⋯,xc
,构成矩阵
X=[x1,⋯,xc]∈Rn×c
。
(5)将矩阵
X
的行向量单位化,得到矩阵
(6)将
Y
的每行看作属于
(7)对Y进行聚类,聚类的结果就代表了原始数据点 si 的聚类结果。
谱聚类之所以能处理非凸集数据问题就是因为其对数据首先进行了变换,变换流程如流程中的(1)-(4),下面看下图2.1所示的数据经过(1)-(4)变换后的结果图
图2.3 经过(1)(4)变换后的数据
程序解析
第一步:加载数据
def readData(path):
data = []
for line in open(path, 'r'):
ele = line.split('\t')
tmp = []
for e in ele:
tmp.append(float(e))
data.append(tmp)
return data
第二步:对数据进行中性化,对应与流程中的(0)
def normalize(data):
mean = np.mean(data, axis = 0)
data = (data - mean)
max_ = np.max(np.abs(data))
data = data / max_
return data
第三步:计算相似矩阵,相当于(1)
def affinity(data, knn):
data = np.array(data)
d = cdist(data,data,'sqeuclidean')
sigma_list = np.zeros(data.shape[0])
dis_matrix = np.zeros((data.shape[0], data.shape[0]))
i = 0
for e in d:
ec = copy.deepcopy(e)
ec.sort()
index = ec > 0.0
tmp = ec[index]
sigma_list[i] = tmp[knn]
i = i + 1
dis_matrix = updataAffinity(d, sigma_list)
return dis_matrix
:
第四步:计算拉普拉斯矩阵,相当于(2)
def lMatrix(dis_matrix):
d = dMatrix(dis_matrix)
if (len(d.shape) == 1):
d = np.array([d])
if(d.shape[0] == 1):
d_row = copy.deepcopy(d.transpose())
d_col = copy.deepcopy(d)
else:
d_row = copy.deepcopy(d)
d_col = copy.deepcopy(d.transpose())
tmp = d_col * dis_matrix
l = tmp * d_row
return l
第五步:计算拉普拉斯矩阵的奇异值,相当于(3)(4)
def svd(l):
u,s,v = svds(l,2)
return u
第六步:对奇异值分解的结果进行单位化,相当于(5)
def unit(data):
norm2 = np.array([[norm(di) for di in data]]).transpose()
u = data / norm2
return u
第七步:调用第一节中的KMeans算法对结果进行聚类,相当于(6)
KMeans算法见上一节