简介
KMeans:无监督学习、用于分类处理。在数据中寻找一种特定类型的结构。查看大量数据点,自动找到彼此相关或相似的数据点。
K-Means算法的思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本集划分为K个簇。让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。
应用
- 新闻分类:大熊猫一类的文章
- DNA数据分析:例如查看来自不同个体的基因表达数据并尝试将他们分组为表现相似特征的人
- 天文数据分析:空间中的物质组合在一起,确定哪些物体形成一个星系或哪些物体形成空间中的连贯结构。
思想流程
- 随机选择集群中心
- 每个点距离哪个簇质心最近(根据欧氏距离进行计算)
- 对于分类后的产生的k个簇,分别计算到簇内其他点距离均值最小的点最为质心(对于拥有坐标的簇可以计算没个簇坐标的均值作为质心)更新簇心位置
- 更新各点所在的簇
- 直至簇质心不再改变,说明KMeans算法已收敛。
- 对停止KMeans算法解释
当质心不再改变,或给定loop(运行次数)最大次数loopLimit
当每个簇的质心,不再改变时就可以停止k-menas
当loop次数超过looLimit时,停止k-means
只需要满足两者的其中一个条件,就可以停止k-means
如果Step4没有结束k-means,就再执行step2-step3-step4
如果Step4结束了k-means,则就打印(或绘制)簇以及质心
# -*- codeing = utf-8 -*-
# @Time : 2022/1/14 18:10
# @Author : lcl
# @File : K-means.py
# @Software : PyCharm
import random
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 计算欧拉距离
def calcDis(dataSet, centroids, k):
clalist = []
for data in dataSet:
diff = np.tile(data, (k,
1)) - centroids # 相减 (np.tile(a,(2,1))就是把a先沿x轴复制1倍,即没有复制,仍然是 [0,1,2]。 再把结果沿y方向复制2倍得到array([[0,1,2],[0,1,2]]))
squaredDiff = diff ** 2 # 平方
squaredDist = np.sum(squaredDiff, axis=1) # 和 (axis=1表示行)
distance = squaredDist ** 0.5 # 开根号
clalist.append(distance)
clalist = np.array(clalist) # 返回一个每个点到质点的距离len(dateSet)*k的数组
return clalist
# 计算质心
def classify(dataSet, centroids, k):
# 计算样本到质心的距离
clalist = calcDis(dataSet, centroids, k)
# 分组并计算新的质心
minDistIndices = np.argmin(clalist, axis=1) # axis=1 表示求出每行的最小值的下标
newCentroids = pd.DataFrame(dataSet).groupby(
minDistIndices).mean() # DataFramte(dataSet)对DataSet分组,groupby(min)按照min进行统计分类,mean()对分类结果求均值
newCentroids = newCentroids.values
# 计算变化量
changed = newCentroids - centroids
return changed, newCentroids
# 使用k-means分类
def kmeans(dataSet, k):
# 随机取质心
centroids = random.sample(dataSet, k)
# 更新质心 直到变化量全为0
changed, newCentroids = classify(dataSet, centroids, k)
while np.any(changed != 0):
changed, newCentroids = classify(dataSet, newCentroids, k)
centroids = sorted(newCentroids.tolist()) # tolist()将矩阵转换成列表 sorted()排序
# 根据质心计算每个集群
cluster = []
clalist = calcDis(dataSet, centroids, k) # 调用欧拉距离
minDistIndices = np.argmin(clalist, axis=1)
for i in range(k):
cluster.append([])
for i, j in enumerate(minDistIndices): # enymerate()可同时遍历索引和遍历元素
cluster[j].append(dataSet[i])
return centroids, cluster
# 创建数据集
def createDataSet():
return [[1, 1], [1, 2], [2, 1], [6, 4], [6, 3], [5, 4]]
if __name__ == '__main__':
dataset = createDataSet()
centroids, cluster = kmeans(dataset, 2)
print('质心为:%s' % centroids)
print('集群为:%s' % cluster)
for i in range(len(dataset)):
plt.scatter(dataset[i][0], dataset[i][1], marker='o', color='green', s=40, label='原始点')
# 记号形状 颜色 点的大小 设置标签
for j in range(len(centroids)):
plt.scatter(centroids[j][0], centroids[j][1], marker='x', color='red', s=50, label='质心')
plt.show
评价优缺点
K-Means的主要优点有:
1)原理比较简单,实现也是很容易,收敛速度快。
2)聚类效果较优。
3)算法的可解释度比较强。
4)主要需要调参的参数仅仅是簇数k。
K-Means的主要缺点有:
1)K值的选取不好把握
2)对于不是凸的数据集比较难收敛
3)如果各隐含类别的数据不平衡,比如各隐含类别的数据量严重失衡,或者各隐含类别的方差不同,则聚类效果不佳。
4) 采用迭代方法,得到的结果只是局部最优。
5) 对噪音和异常点比较的敏感。
如何确定分类数量
手肘法
使用各种k值运行KMeans算法,并将J函数(失真函数所有簇差异的平方的平均值)绘制成簇数量的函数,随着k增加J会下降。
下降过程:速度很快->突然变慢,我们找的就是这个转折点,即k值。
算法流程
- 选择聚类的个数k.
- 任意产生k个聚类,然后确定聚类中心,或者直接生成k个中心。
- 对每个点确定其聚类中心点。
- 再计算其聚类新中心。
- 重复以上步骤直到满足收敛要求。(通常就是确定的中心点不再改变。)
例子代码
其中使用了plt、sklearn等包。
tips:make_blobs更换了母包,
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import calinski_harabasz_score
#创建数据集
#参数random——state:相当于随机种子,设置了随机种子的值后,那么当别人重新运行你的代码的时候就能得到完全一样的结果,复现和你一样的过程。
#1、训练集测试集的划分 2、构建机器学习模型 3、生成数据集
# 随机数种子控制每次划分训练集和测试集的模式,其取值不变时划分得到的结果一模一样,其值改变时,划分得到的结果不同。若不设置此参数,则函数会自动选择一种随机模式,得到的结果也就不同。```c
# 其取值不变时,用相同的训练集建树得到的结果一模一样,对测试集的预测结果也是一样的;其值改变时,得到的结果不同;若不设置此参数,则函数会自动选择一种随机模式,每次得到的结果也就不同。
X,y=make_blobs(n_samples=1000,n_features=4,centers=[[-1,-1],[0,0],[1,1],[2,2]],cluster_std=[0.4,0.2,0.2,0.2],random_state=9)
#以上参数对应样本集总数、每个样本特征数、簇数、簇中心、遍历次数。
#原始图
plt.scatter(X[:,0],X[:,1],marker="o")
plt.show()
#归为两类
def KMeans_2():
y_pre=KMeans(n_clusters=2,random_state=9).fit_predict(X)
plt.scatter(X[:,0],X[:,1],c=y_pre)
plt.show()
print(calinski_harabasz_score(X,y_pre))
#kmeans训练且可视化,聚类为2,后用ch_scole查看最后评价结果
KMeans_2()
补充:对random_state的介绍。
一、设置随机种子作用
random_state 相当于随机数种子random.seed() 。random_state 与 random seed 作用是相同的。
设置了随机种子的值后,那么当别人重新运行你的代码的时候就能得到完全一样的结果,复现和你一样的过程。
random_state可以用于很多函数,了解到的常用于以下三个地方:
1、训练集测试集的划分
随机数种子控制每次划分训练集和测试集的模式,其取值不变时划分得到的结果一模一样,其值改变时,划分得到的结果不同。若不设置此参数,则函数会自动选择一种随机模式,得到的结果也就不同。
2、构建机器学习模型
其取值不变时,用相同的训练集建树得到的结果一模一样,对测试集的预测结果也是一样的;
其值改变时,得到的结果不同;
若不设置此参数,则函数会自动选择一种随机模式,每次得到的结果也就不同。
3、生成数据集
未设置random_state随机种子值的数据集,同一段代码连续执行两次结果如下图所示,可以看到两次生成不同的数据集。
设置了random_state随机种子值的数据集,同一段代码连续执行两次结果如下图所示,可以看到两次生成相同的数据集。
总结
在需要设置random_state的地方给其赋一个值,当多次运行此段代码能够得到完全一样的结果,别人运行此代码也可以复现你的过程。若不设置此参数则会随机选择一个种子,执行结果也会因此而不同了。虽然可以对random_state进行调参,但是调参后在训练集上表现好的模型未必在陌生训练集上表现好,所以一般会随便选取一个random_state的值作为参数。
详情见:原文链接:https://blog.csdn.net/xiaotan0814/article/details/127961403;
具体KMeans思想算法,参考:K-Means聚类算法_kmeans聚类算法_骄阳少年爱算法的博客-CSDN博客