k_means是一种聚类算法,也是无监督学习算法。
在一个典型的监督学习中,我们有一个有标签的训练集,我们的目标是找到能够区分正
样本和负样本的决策边界,在这里的监督学习中,我们有一系列标签,我们需要据此拟合一
个假设函数。与此不同的是,在非监督学习中,我们的数据没有附带任何标签。
数据看起来可以分成几个点集(簇),一个能够找到圈出这些点集的算法被称为聚类算法。
K-均值是一个迭代算法,
主要操作是簇分配和移动聚类中心。
假设我们想要将数据聚类成 n 个组,其方法为:
首先选择𝐾个随机的点,称为聚类中心(cluster centroids);
对于数据集中的每一个数据,按照距离𝐾个中心点的距离,将其与距离最近的中心点关
联起来,与同一个中心点关联的所有点聚成一类。
计算每一个组的平均值,将该组所关联的中心点移动到平均值的位置。
重复步骤 2-4 直至中心点不再变化。
通俗讲就是遍历每个样本,根据每一个点与那选择的k个中心的距离远近,把点与距离最近的中心关联起来。
再移动聚类中心,就是计算分成的每个簇求平均值,平均值即为新的聚类中心点。
然后不断重复以上两个步骤进行迭代。直到中心质点不再发生变化。
k_means算法输入的是数据集和k(簇的个数),数据集是没有标签的向量,并且规定是n维,不是n+1维。
k_means算法第一步是随机选取k个中心,并且循环遍历所有数据,计算和每个中心的距离,并将其归到哪一个簇。就是找到能够使点到中心点最小的k值,然后归为一簇,计算距离一般用平方,这是惯例。这就是簇分配步骤。
第二步移动中心聚点,重新计算每个簇的均值,重新规定新的质点。如果遇到没有聚点的中心,一般选择删除。
k_means算法的优化目标
优化目标是使每个点到聚类中心距离的平方和最小,这也是k_means的代价函数。也叫失真代价函数和k_means算法的失真。
他要找到每个点所属的簇和中心质点来使代价函数取得最小值。
簇分配的步骤就是在保持聚类中心的位置不变来最小化代价函数。
移动中心质点使通过改变u的值来最小化代价函数。所以k_means算法的实质是分别通过找到每个点距离最近的簇和移动中心质点来最小化代价函数。然后保持迭代,最后达到聚类目标。
再来说一下,如何选择k值,一般通过观察就可以确定k的值,但是由于无监督学习的特性,使得k没有确定的值,我们只能通过一些规定来确定一些比较合适的k值。
比如“肘部法则”,就是把k值当作横坐标,代价函数的值作为纵坐标。画出函数图像,如果有明显的拐点,我们就选择拐点作为k值。
不过一般图像都是平滑的曲线,不容易看出拐点。那么可以根据实际情况,按照实际需求来决定k值。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
class kmeans():
def __init__(self, X, K, max_iters = 10):
'''max_iters:最大迭代次数
X:数据集
K:分成的群数'''
self.X = X
self.K = K
self.max_iters = max_iters
#返回x的每行所属的中心点索引
def findClosestCentroids(self, centroids):
# 总共有len(X)行数据,所以idx是一个 len(X) * 1的矩阵
idx = np.zeros(len(self.X )).reshape( self.X .shape[0],-1)
for i in range(len(self.X )):
minDistance = float('inf');#初始化无限大
index = 0
for k in range(len(centroids)):
#计算每个点到相应质心的距离
distance = np.sum(np.power(self.X [i]-centroids[k],2))
#不断更新最近的质心信息
if(distance<minDistance):
minDistance = distance
index = k
idx[i]=index
return idx
#idx:X每行所属的中心点索引
#K:聚类个数
def computeCentroids(self , idx):
#idx: 每行所对应的质心的索引
#K: 聚类的个数
k = set(np.ravel(idx).tolist()) #找到所有聚类中心索引
#取出Cluster的编号
k = list(k)
centroids = np.ndarray((len(k),self.X .shape[1]))
for i in range(len(k)):
#得出每个质心对应的Cluster的数据
data = self.X [np.where(idx==k[i])[0]]
#重新计算质心
centroids[i] = np.sum(data,axis=0)/len(data)
return centroids
def predict(self):
#计算出初始化的质心
initial_centroids = self.InitCentroids()
#进行分Cluster
idx = self.findClosestCentroids(initial_centroids)
#重新计算质心
centroids = self.computeCentroids(idx)
#接下来就是不断迭代的过程
for i in range(self.max_iters):
idx = self.findClosestCentroids(centroids)
centroids = self.computeCentroids(idx)
#画图
ax = plt.subplot(self.max_iters / 3+1, 3,i+2)
ax.scatter(self.X[:, 0], self.X[:, 1], c=np.ravel(idx), s=20)
ax.scatter(centroids[:, 0], centroids[:, 1], marker='*', s=500) # 质心标记为星号
plt.show()
return idx,centroids
#初始化质心,从原有的数据中选取K个座位质心
def InitCentroids(self):
index = np.random.randint(0,len(self.X )-1,self.K)
return self.X[index]
首先先随机找出k个质心。然后进行簇分类和移动质心并且不断迭代。