机器学习-K均值聚类(python3代码实现)
2018-02-27 16:00
609 查看
K均值聚类
哈尔滨工程大学-537算法原理:
K均值是发现给定数据集的kk个簇的算法。簇个数kk是用户给定的,每一个簇通过其质心(centroid),即簇中所有点的中心来描述。K均值算法的工作流程是:首先随机确定kk个初始点作为质心。然后将数据集中的每个点分配到一个簇,具体来讲,为每个点找距其最近的质心,并将其分配给该之心所对应的簇。这一步完成后,每个簇的质心更新为该簇所有点的平均值。
代码实现 :
一、基础实现
1)首先导入所需要的各个库%matplotlib inline是为了使接下来的绘图能够显示在Jupyter notebook的浏览器上。
import numpy as np import math import matplotlib.pyplot as plt %matplotlib inline
2)定义加载样本点数据的函数load_dataset,输入参数为文件名;
将文件按行读取,每一行为一个列表,依次追加到空列表data_mat中,最后以矩阵的形式返回所得。
def load_dataset(file_name): data_mat = [] with open(file_name) as fr: lines = fr.readlines() for line in lines: cur_line = line.strip().split("\t") flt_line = list(map(lambda x:float(x), cur_line)) data_mat.append(flt_line) return np.array(data_mat)
3)调用load_dataset函数,参数为将要打开的文件的路径和文件名,得到样本点的特征矩阵矩阵data_set。
data_set = load_dataset(r"D:\python_data\MachineLearningInaction\machinelearninginaction\Ch10\testSet.txt") print(data_set)
将其打印出来,得到一个80行,2列的矩阵,其中前9行如下图:
4)利用python的绘图库绘制样本点的散点图。其中横坐标为”factor1”,纵坐标为”factor2”。
point_x = data_set[:,0] point_y = data_set[:,1] fig, ax = plt.subplots(figsize=(10,5)) ax.scatter(point_x, point_y, s=30, c="r", marker="o", label="sample point") ax.legend() ax.set_xlabel("factor1") ax.set_ylabel("factor2")
绘制出的散点图如下图所示:
5)接下来定义一个计算两个向量之间欧氏距离的函数dist_eclud,已知向量X=(x1,x2...xn)X=(x1,x2...xn)和Y=(y1,y2...yn)Y=(y1,y2...yn)之间的欧氏距离为
(x1−y1)2+(x2−y2)2+...+(xn−yn)2−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−√(x1−y1)2+(x2−y2)2+...+(xn−yn)2
def dist_eclud(vecA, vecB): vec_square = [] for element in vecA - vecB: element = element ** 2 vec_square.append(element) return sum(vec_square) ** 0.5
6)构建kk个随机质心。输入参数为数据集data_set和想要构建的质心个数k。随机质心必须要在整个数据集的边界之内。首先可以找到数据集每一维的最小和最大值。然后得到每一维的取值范围。用0到1之间的随机数和取值范围相乘,再用最小值加上该乘积,就可以得到在每一维取值范围内的随机数。
# 构建k个随机质心 def rand_cent(data_set, k): n = data_set.shape[1] centroids = np.zeros((k, n)) for j in range(n): min_j = float(min(data_set[:,j])) range_j = float(max(data_set[:,j])) - min_j centroids[:,j] = (min_j + range_j * np.random.rand(k, 1))[:,0] return centroids
7)算法的主函数,输入参数为数据集和簇的个数。
首先初始化样本点的簇分配矩阵,有80行2列,第一列为该样本点的簇分配索引,第二列为该样本点到该簇质心的欧氏距离。
当任意一个点的簇分配发生变化时,迭代执行以下操作:遍历每个样本点,计算样本点i到各个质心的距离,找到最小距离,将该质心所在簇编号分配给该样本点。遍历完所有样本点后,重新计算每个簇的质心。直到所有样本点的簇分配都不再发生变化时迭代停止。
最后返回质心和样本点的簇分配矩阵
def Kmeans(data_set, k): m = data_set.shape[0] cluster_assment = np.zeros((m, 2)) centroids = rand_cent(data_set, k) cluster_changed = True while cluster_changed: cluster_changed = False for i in range(m): min_dist = np.inf; min_index = -1 for j in range(k): dist_ji = dist_eclud(centroids[j,:], data_set[i,:]) if dist_ji < min_dist: min_dist = dist_ji; min_index = j if cluster_assment[i,0] != min_index: cluster_changed = True cluster_assment[i,:] = min_index, min_dist**2 for cent in range(k): pts_inclust = data_set[np.nonzero(list(map(lambda x:x==cent, cluster_assment[:,0])))] centroids[cent,:] = np.mean(pts_inclust, axis=0) return centroids, cluster_assment
8)调用函数Kmean,输入数据集为data_set,簇数量为4个,得到收敛后的质心和簇分配矩阵(簇分配矩阵略)。
my_centroids, my_cluster_assment = Kmeans(data_set, 4) print(my_centroids) # print(my_cluster_assment)
得到的4个质心为:
9)为了更加清晰直观,用绘图库绘制散点图,其中,四个质心用黑色▲标出。
point_x = data_set[:,0] point_y = data_set[:,1] cent_x = my_centroids[:,0] cent_y = my_centroids[:,1] fig, ax = plt.subplots(figsize=(10,5)) ax.scatter(point_x, point_y, s=30, c="r", marker="o", label="sample point") ax.scatter(cent_x, cent_y, s=100, c="black", marker="v", label="centroids") ax.legend() ax.set_xlabel("factor1") ax.set_ylabel("factor2")
得到的散点图如下图所示:
二、二分K-均值聚类
上述算法的聚类簇的数目kk是用户自定义的,那么用户如何才能知道kk的选择是否正确?如何才能知道生成的簇比较好呢?在包含簇分配结果的矩阵中保存着每个点的误差,即该点到簇质心的距离平方值。一种用于度量聚类效果的指标使SSE(Sum of Squared Error,误差平方和),SSE值越小表示数据点越接近它们的质心,聚类效果也就越好。
于是就可以采用二分K-聚类的方法,具体代码实现如下:
1)定义一个bi_Kmeans函数,两个输入参数分别为数据集和聚类簇数量。
该函数首先创建一个矩阵cluster_assment来存储数据集中每个点的簇分配结果及平方误差,然后计算整个数据集的质心,并使用一个列表cent_list来保留所有的质心。得到上述质心之后,可以遍历数据集中所有点来计算每个点到质心的误差值。这些误差值将会在后面用到。
接下来程序进入while循环,该循环会不停对簇进行划分,直到得到想要的簇数目为止。可以通过考察簇列表中的值来获得当前簇的数目。然后遍历所有簇来决定最佳的簇进行划分。
为此需要比较划分前后的SSE。一开始将最小SSE设置为无穷大,然后遍历簇列表cent_list中的每一个簇。对每个簇,将该簇中的所有点堪称一个小的数据集pts_incurr_cluster。将pts_incurr_cluster输入到函数Kmeans()中进行处理(k=2)。K-均值算法会生成两个质心(簇),同时给出每个簇的误差值。这些误差与剩余数据集的误差之和作为本次划分的误差。如果该划分的SSE值最小,则保存本次划分。
一旦决定了要划分的簇,接下来就可以实际执行划分操作。划分操作很容易,只需要将划分的簇中所有点的簇分配结果进行修改即可。当使用Kmeans()函数并且制定簇数目为2时,会得到两个编号分别为0和1的结果簇。需要将这些簇编号修改为被划分簇以及新加簇的编号,该过程可以通过两个数组过滤器来完成。
最后,新的簇分配结果被更新,新的质心会被添加到cent_list中。
当while循环结束时,同Kmeans()函数一样,函数返回之心列表与簇分配结果。
# 二分K均值聚类的主函数,输入参数为数据集和要分的簇的个数 def bi_Kmeans(data_set, k): m = data_set.shape[0] # 得到data_set的行数,即数据集的个数 cluster_assment = np.zeros((m,2)) # 初始化样本点的簇分配矩阵,第一列为簇分配索引,第二列为欧氏距离平方 centroid0 = np.mean(data_set, axis=0) # 按列计算均值,即找到初始质心 cent_list = [centroid0] for j in range(m): # 对于每个样本点 cluster_assment[j,1] = dist_eclud(centroid0, data_set[j,:])**2 # 计算该样本点的误差平方 while (len(cent_list) < k): # 当已有的簇个数小于k时,迭代执行以下代码 lowestSSE = np.inf # 初始化误差平方和SSE的最小值 # 找到对哪个簇进行划分可以最大程度降低SSE值 for i in range(len(cent_list)): # 遍历每个已有的簇 # 得到属于该簇的所有样本点 pts_incurr_cluster = \ data_set[np.nonzero(list(map(lambda x:x==i, cluster_assment[:,0])))] # 将该簇的所有样本点通过函数Kmean进行划分(k=2),得到划分后的质心和簇分配矩阵 centroid_mat, split_clust_ass = Kmeans(pts_incurr_cluster, 2) sse_split = np.sum(split_clust_ass[:,1]) # 得到划分后的误差平方和 # 得到其他样本点的误差平方和 sse_not_split = \ np.sum(cluster_assment[np.nonzero(list(map(lambda x:x!=i, cluster_assment[:,0]))),1]) if (sse_split + sse_not_split) < lowestSSE: # 如果总的误差平方和小于lowestSSE best_cent_to_split = i # 则保存本次划分 best_new_cents = centroid_mat best_clust_ass = split_clust_ass lowestSSE = sse_split + sse_not_split # 对最大程度降低SSE值的簇进行划分 # 将划分后得到的编号为0的结果簇的编号修改为原最大簇编号+1,即len(cent_list) best_clust_ass[np.nonzero(list(map(lambda x:x==1, best_clust_ass[:,0]))), 0] = len(cent_list) # 将划分后得到的编号为1的结果簇的编号修改为被划分的簇的编号 best_clust_ass[np.nonzero(list(map(lambda x:x==0, best_clust_ass[:,0]))), 0] = best_cent_to_split cent_list[best_cent_to_split] = best_new_cents[0,:] # 更新被划分的簇的质心 cent_list.append(best_new_cents[1,:]) # 添加一个新的簇的质心 cluster_assment[np.nonzero(list(map(lambda x:x==best_cent_to_split, cluster_assment[:,0]))),:] = \ best_clust_ass # 将簇分配矩阵中属于被划分的簇的样本点的簇分配进行更新 return np.array(cent_list), cluster_assment
2)接下来调用bi_Kmeans()函数,得到三个质心和簇分配结果(簇分配结果略),在调用bi_Kmeans()函数前,先执行load_dataset()函数载入数据。
data_set2 = load_dataset(r"D:\python_data\MachineLearningInaction\machinelearninginaction\Ch10\testSet2.txt")
cent_list, cluster_assment = bi_Kmeans(data_set2, 3) print(cent_list)
3)最后,用绘图库绘制散点图,横坐标为”factor1”,纵坐标为”factor2”,其中三个质心用黑色X标出。
point_x = data_set2[:,0] point_y = data_set2[:,1] cent_x = cent_list[:,0] cent_y = cent_list[:,1] fig, ax = plt.subplots(figsize=(10,5)) ax.scatter(point_x, point_y, s=30, c="r", marker="o", label="sample point") ax.scatter(cent_x, cent_y, s=100, c="black", marker="x", label="centroids") ax.legend() ax.set_xlabel("factor1") ax.set_ylabel("factor2")
所绘制的散点图如下:
相关文章推荐
- 机器学习经典算法详解及Python实现--聚类及K均值、二分K-均值聚类算法
- k-均值聚类Python代码实现
- 【机器学习】聚类结果评价指标及python3代码实现
- [置顶] 【二分-kMeans算法】二分K均值聚类分析与Python代码实现
- 机器学习经典算法详解及Python实现--聚类及K均值、二分K-均值聚类算法
- 【Python数据挖掘课程】三.Kmeans聚类代码实现、作业及优化
- 【Python数据挖掘课程】三.Kmeans聚类代码实现、作业及优化
- 【机器学习】高斯混合聚类python实现
- 机器学习-支持向量机(python3代码实现)
- 用Python Scikit-learn 实现机器学习十大算法--朴素贝叶斯算法(文末有代码)
- 机器学习:交叉验证和模型选择与Python代码实现
- 机器学习实战 - 第十章 K-均值聚类 代码报错整理
- # 机器学习数据挖掘关联规则挖掘Apriori算法python代码实现
- 机器学习:神经网络、正则化、多分类问题与Python代码实现
- Python数据挖掘课程 三.Kmeans聚类代码实现、作业及优化
- 数学模型 机器学习 系统聚类(system clustering) Python实现
- 原型聚类之K均值算法及Python实现
- python实现之K-均值聚类
- 机器学习与神经网络(三):自适应线性神经元的介绍和Python代码实现
- [python] 基于k-means和tfidf的文本聚类代码简单实现