当前位置:   article > 正文

无监督学习 k-means算法_def load_data_set():

def load_data_set():

一、无监督学习

无监督学习是机器学习算法中的一种。监督学习的目的主要是对数据进行分类和回归预测,它主要是通过已知推测未知,大部分监督学习算法有一个训练模型的过程;相对于监督学习,无监督学习则是主要着重于数据的分布特点,与有监督学习不同,无监督学习并没有训练的过程。

二、 聚类

针对给定的样本数据,聚类算法会根据它们的特征相似度或距离,把相似的数据划分为若干个簇中。相似的样本划分到相同的簇中, 不相似的样本划分到不同的簇。聚类处理数据的过程也是从未知到已知的过程,因为聚类算法在事先并不知道聚类得到的簇的特点和样本的分布。

k-means属于无监督学习中聚类算法的一种。

三、 k均值聚类算法

3.1 k-means

k均值聚类算法将样本集划分为k个簇,把每个样本数据划分到距离最近的簇中,且每个样本仅属于一个类,此为k均值聚类算法。k-mean算法属于硬聚类,即每个样本只能属于一个簇。

聚合聚类需要预先考虑以下几个要素:

  1. 距离或相似度;
  2. 合并规则;
  3. 停止条件。

3.2 步骤

  1. 随机选择k个簇作为质心(需要保证这k个簇的特征值在样本集特征值的范围之内);
  2. 把n个样本点划分到距离最近的簇中;
  3. 计算每个簇的平均值。
  4. 判断是否收敛,如果没有收敛,则从步骤2开始。

 

图1 k-means流程图

 

图2 k均值聚类算法 迭代过程

通过图2可以了解到,由于第三张图和第四张图未发生变化,则表示此时的簇已经收敛。

3.3 实现

  1. from numpy import *
  2. from matplotlib import pyplot as plt
  3. import matplotlib
  4. def load_data_set(filename):
  5. data_matrix = []
  6. with open(filename) as fp:
  7. for line in fp.readlines():
  8. # 多个浮点型的列
  9. data = line.strip().split('\t')
  10. float_data = [float(datum) for datum in data]
  11. data_matrix.append(float_data)
  12. return data_matrix

 load_data_set函数用于将文本文件导入成一个列表中。

  1. def euclidean_distance(vec_a, vec_b):
  2. """计算两个向量的欧式距离"""
  3. return sqrt(sum(power(vec_a - vec_b, 2)))

 距离的计算有很多种选择,比如闵可夫斯基距离、马氏距离等,不同的距离选择会使得计算页会有所不同。这里选择的是欧氏距离,即各个特征差值的平方再开方(勾股定理的计算就用到了欧式距离),马氏距离是欧式距离的推广

  1. def random_centroids(data_set, k):
  2. """
  3. 返回k个随机的质心 保证这些质心随机并且不超过整个数据集的边界
  4. :param data_set: 数据集
  5. :param k: 质心的数量
  6. :return: k个随机的质心
  7. """
  8. # 创建一个k行n列的矩阵,用于保存随机质心
  9. n = shape(data_set)[1]
  10. centroids = mat(zeros((k, n)))
  11. # 对n个特征进行遍历
  12. for j in range(n):
  13. minJ = min(data_set[:, j])
  14. rangeJ = float(max(data_set[:, j]) - minJ)
  15. centroids[:, j] = minJ + rangeJ * random.rand(k, 1)
  16. return centroids

 random_centroids的作用就是随机创建k个质心。data_set[:, j]获取的是第j列的所有元素,这种用法是numpy对__getitem__()即切片方法的重写。

  1. def kMeans(data_set, k, distance_measure=euclidean_distance, create_centroids=random_centroids):
  2. """
  3. k means 算法
  4. :param data_set: 数据集
  5. :param k: k means算法的k 即要生成几个簇
  6. :param distance_measure: 计算距离函数
  7. :param create_centroids: 创建质心的函数
  8. :return:
  9. """
  10. m = shape(data_set)[0]
  11. # 向量分配到某一个(簇索引值,误差)
  12. cluster_assment = mat(zeros((m, 2)))
  13. # 创建k个质心
  14. centroids = create_centroids(data_set, k)
  15. cluster_changed = True
  16. while cluster_changed:
  17. cluster_changed = False
  18. # 计算该点离哪个质心最近
  19. for i in range(m):
  20. min_index, min_dist = -1, inf
  21. # 遍历k个质心 获取一个最近的质心
  22. for j in range(k):
  23. # 计算该点和质心j的距离
  24. distJI = distance_measure(centroids[j, :], data_set[i, :])
  25. if distJI < min_dist:
  26. min_dist, min_index = distJI, j
  27. # 分配质心索引发生了变化 则仍然需要迭代
  28. if cluster_assment[i, 0] != min_index:
  29. cluster_changed = True
  30. # 不断更新最小值
  31. cluster_assment[i, :] = min_index, min_dist ** 2
  32. print(centroids)
  33. # 更新质心
  34. for cent in range(k):
  35. # 获取属于该簇的所有点
  36. ptsInClust = data_set[nonzero(cluster_assment[:, 0].A==cent)[0]]
  37. # 按矩阵的列进行均值计算
  38. centroids[cent, :] = mean(ptsInClust, axis=0)
  39. # 显示每一次迭代后的簇的情况
  40. # show_image(data_set, centroids, cluster_assment)
  41. return centroids, cluster_assment

 kMeans函数就是之前流程图的实现,它会迭代到簇收敛为止。

  1. def show_image(data_set, centroids, clustAssing):
  2. colors = 'bgrcmykb'
  3. markers = 'osDv^p*+'
  4. for index in range(len(clustAssing)):
  5. datum = data_set[index]
  6. j = int(clustAssing[index, 0])
  7. flag = markers[j] + colors[j]
  8. plt.plot(datum[:, 0], datum[:, 1], flag)
  9. # 质心
  10. plt.plot(centroids[:, 0], centroids[:, 1], '+k')

 show_image函数用于点的分类的显示,它目前最多显示4个簇,过多的时候需要再sign和color上添加;除此之外,这个函数目前只能显示两个特征值,当有多个特征值的时候,可以考虑进行降维处理。

接着是主函数和数据:

  1. if __name__ == '__main__':
  2. data_mat = mat(load_data_set('testSet.txt'))
  3. centroids, clustAssing = kMeans(data_mat, 4)
  4. print('----')
  5. # print(clustAssing)
  6. show_image(data_mat, centroids, clustAssing)
  7. plt.show()

 数据集:dataSet.txt

把上述数据保存为dataSet.txt和之前的kMeans.py放入同一文件夹下运行即可得到图 2所示的收敛图。

3.4 sklearn实现

python有着完整的机器学习的库,通过调用这些库,可以在很大程度上减少代码的编写和错误率。

  1. """
  2. 使用sklearn提供的K均值聚类算法
  3. """
  4. import numpy
  5. from sklearn.cluster import KMeans
  6. import matplotlib.pyplot as plt
  7. from kMeans import load_data_set
  8. def show_image(data_set, centroids, cluster_centers):
  9. colors = 'bgrcmykb'
  10. markers = 'osDv^p*+'
  11. for index in range(len(cluster_centers)):
  12. datum = data_set[index]
  13. j = int(cluster_centers[index])
  14. flag = markers[j] + colors[j]
  15. plt.plot(datum[:, 0], datum[:, 1], flag)
  16. # 质心
  17. plt.plot(centroids[:, 0], centroids[:, 1], '+k')
  18. if __name__ == '__main__':
  19. data_set = numpy.mat(load_data_set('testSet.txt'))
  20. kmeans_model = KMeans(n_clusters=4).fit(data_set)
  21. # 显示模型
  22. show_image(data_set, kmeans_model.cluster_centers_, kmeans_model.labels_)
  23. plt.show()

运行结果如下:

图3 K均值聚类算法运行结果

 

四、二分K-均值算法

把数据集切换为dataSet2.txt后,再次运行之前的kMeans算法可能会得到下图的结果:

图4 质心的随机性导致了k-均值算法效果不好

k均值聚类算法能够保证收敛局部最优性,它的聚类效果在很大程度上依赖于随机质心的选择,这会造成算法收敛但聚类效果却较为一般。二分K-均值算法可以避免此类问题。

一种用于度量聚类效果的指标是SSE(Sum of Square Error, 误差平方和),它等于所有的样本点到对应的簇的距离的平方的和,也就是上面的kMeans函数内部cluster_assment的第一列的和(用代码实现就是sum(cluster_assment[:, 1]))。SSE越小表示数据点越接近于它们的质心,聚类效果也就越好。

4.1 思路

该算法首先将所有的样本点作为一个簇,然后将该簇一分为二;之后选择其中一个可以在最大程度上降低SSE值的簇进行划分,直到满足停止条件为止。这里的停止条件是划分的簇的数量达到了k后则停止划分。

  1. 把所有的点作为一个簇;
  2. 判断当前簇的数量是否大于等于k,如果是则直接退出,否则向下执行;
  3. 对于每一个簇,在给定的簇上进行K-均值聚类(k=2),并计算将该簇一分为二之后的总误差;
  4. 选择使得误差最小的那个簇进行划分操作;跳转到步骤2。
图5 二分K-均值算法

4.2 代码实现

  1. def binary_kmeans(data_set, k, distance_measure=euclidean_distance):
  2. """
  3. 二分 K-均值算法
  4. :param data_set: 样本集
  5. :param k: 要划分的簇的数量
  6. :param distance_measure: 距离计算函数
  7. :return: 返回同kMeans()函数
  8. """
  9. m = shape(data_set)[0]
  10. cluster_assment = mat(zeros((m, 2)))
  11. # 按照列求平均数并转换成列表
  12. centroid0 = mean(data_set, axis=0).tolist()[0]
  13. # 划分的簇
  14. cent_list = [centroid0]
  15. # 计算点当前簇的误差
  16. for j in range(m):
  17. cluster_assment[j, 1] = distance_measure(mat(centroid0), data_set[j, :]) ** 2
  18. # 划分的簇小于选定值时 继续划分
  19. while len(cent_list) < k:
  20. lowestSSE = inf
  21. # 找到一个划分后SSE最小的簇
  22. for i in range(len(cent_list)):
  23. # 获取该簇的所有数据
  24. ptsInCurrCluster = data_set[nonzero(cluster_assment[:, 0].A == i)[0], :]
  25. # 经过划分 一个簇得到编号分别为0和1的两个簇
  26. centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distance_measure)
  27. # 计算分簇之后的Sum of Square Error
  28. sseSplit = sum(splitClustAss[:, 1])
  29. sseNotSplit = sum(cluster_assment[nonzero(cluster_assment[:, 0].A != i)[0], 1])
  30. print('sseSplit, and not split', sseSplit, sseNotSplit)
  31. if sseSplit + sseNotSplit < lowestSSE:
  32. bestCentToSplit = i
  33. bestNewCents = centroidMat
  34. bestClustAss = splitClustAss.copy()
  35. lowestSSE = sseSplit + sseNotSplit
  36. # 重新编排编号
  37. bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(cent_list)
  38. bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
  39. print('the bestCentToSplit is:', bestCentToSplit)
  40. print('the len of bestClustAss is', len(bestClustAss))
  41. cent_list[bestCentToSplit] = bestNewCents.tolist()[0]
  42. cent_list.append(bestNewCents.tolist()[1])
  43. cluster_assment[nonzero(cluster_assment[:, 0].A == bestCentToSplit)[0], :] = bestClustAss
  44. # 显示每一次迭代后的簇的情况
  45. show_image(data_set, mat(cent_list), cluster_assment)
  46. return mat(cent_list), cluster_assment

在二分K-均值算法中主要有两个循环,第一重循环用于确定簇的个数,第二重循环用于选定一个能使得SSE降到最低的划分。在划分完成后,一个簇被划分成了两个簇,需要对这个簇进行一些后续的操作:编排序号(序号是由k均值聚类算法给定的)和添加到簇列表中。

 二分K-均值算法可以很好地解决k均值聚类算法的问题,不过目前的k值仍然需要预先给定。

 

参考:

  • 《机器学习实战》
  • 《统计学习方法》
  • 《Python 机器学习及实践》
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/235478
推荐阅读
相关标签
  

闽ICP备14008679号