k-means聚类算法总结

聚类概念

聚类分析是在对象数据中发现对象之间关系。一般来说,组内相似性越高,组间相似性越大,则聚类的效果越好。

k-means概念

k-means是一种无监督学习,它会将相似的对象归到同一类中。

k-means聚类的优缺点

优点:容易实现。

缺点:可能会收敛到局部最小值, 当应用到大规模数据集时会收敛较慢。

适用于:数值型数据。

k-means聚类的算法思想

1.随机计算k个类中心作为起始点。

2. 将数据点分配到理其最近的类中心。

3.移动类中心。

4.重复2,3直至类中心不再改变或者达到限定迭代次数。

具体的伪代码实现:

创建k个点作为起始质心(多是随机选择)
repeat
    对数据集中的每个数据点
        repeat
            对每个质心
                计算质心与数据点之间的距离
        将数据点分配到距离其最近的类(或簇)
    对每一个类(簇),计算类(簇)中所有点的均值并将其作为质心。

k-means算法实现 参照《机器学习实战》

from numpy import *

# k-均值支持函数

def loadDataSet(fileName):
    # 用于读取文本数据
	dataMat = []
	fr = open(fileName)
	for line in fr.readlines():
		curLine = line.strip().split('\t')   # 清除文本格式 转换成数组
		fltLine = map(float, curLine)   # map知识
        # map(function, data) 将函数function作用于数据data (类似用法:reduce) 
		dataMat.append(fltLine)     # 收集数据
	return dataMat

def  distEclud(vecA, vecB):
    # 用于计算两个向量之间的欧氏距离 
	return sqrt(sum(power(vecA - vecB, 2 )))

def randCent(dataSet, k):
    # 构建类(簇)质心 
    # n 获取dataSet的列数 也可以写作 dataSet.shape[1]
	n = shape(dataSet)[1]
    # 然后构建一个(k,n)初始0矩阵 用于后续保存质心
	centroids = mat(zeros((k, n)))
    # 开始初始类质心 需要注意的是 要保证初始质心是在所允许的范围内 
	for j in range(n):
		minJ = min(dataSet[:,j])   # 找到数据集所有列的最小值
		rangeJ = float(max(dataSet[:,j]) - minJ)  # 列最大值-列最小值 得出取值范围
        # 需要注意的是 这是从第一列开始 对每列进行确定取值范围 然后进行在该取值范围内随机取值
		centroids[:, j] = minJ + rangeJ * random.rand(k, 1)  
        # 以最小值为下限 随机在(0,1)内进行取值 通过取值范围rangeJ和最小值minJ进行
        # 随机点都落在 数据边界之内
	return centroids

需要注意的是:

             在随机构建类质心时确保所有数据点都落在范围边界内。

关于欧式距离:

             《k-means聚类算法总结》   

此处求各数据点到类质心的距离使用欧式距离公式完成距离计算。其他距离计算公式暂不讨论。

k-means聚类算法

# 随机构建完成类中心后 开始应用下面k-means算法 
# dataSet:数据集   k:k个类中心 distMeas:欧氏距离公式求距离 createCent:随机初始化类中心


def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    # m: 获取 数据集行数 用于 后边初始化全0矩阵 clusterAssment 
    m = shape(dataSet)[0]
    # clusterAssment:用于保存结果矩阵
    # 其中第一列保存 索引值  第二列保存 误差
    clusterAssment = mat(zeros((m,2)))#create mat to assign data points 
                                      #to a centroid, also holds SE of each point
    # 初始创建类中心
    centroids = createCent(dataSet, k)
    # 默认类中心不再改变 退出算法
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):#for each data point assign it to the closest centroid
        # 开始 循环 为每一个数据点 分配到距离其最近的类中心
            minDist = inf; minIndex = -1     # 初始化最小距离为inf(无穷大) 索引为负
            # 开始针对当前数据点 计算其到每个类质心最短的距离 保存最小的距离
            for j in range(k):
                # 注意以行为开头 即 行向量 即对每个数据点计算其到每个类中心点的欧氏距离 
                distJI = distMeas(centroids[j,:],dataSet[i,:])
                # 每次进行判断是否比上次距离更小 进行存储更小的距离
                # 直至比较到最后取到最小距离 【不保存所有距离,只保存最小距离】
                if distJI < minDist:
                    minDist = distJI; minIndex = j
            # 如果索引即 该数据点的归属类(簇)发生了改变 就继续进行循环
            if clusterAssment[i,0] != minIndex: clusterChanged = True
            # 此时再进行更新该数据点的(索引)归属类,和距离该归属类质(或中)心最小距离。
            clusterAssment[i,:] = minIndex,minDist**2
        print centroids
        # 最后重新移动类质心 (如果需要)
        for cent in range(k):  # recalculate centroids
            # 下面用于找到 当前类质心 下的所有数据点
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster
            # 利用numpy.mean 求得每列的平均值 得到一个数据点 即是该类的新的类质心
            centroids[cent,:] = mean(ptsInClust, axis=0) #assign centroid to mean 
    return centroids, clusterAssment

需要注意的点:

一些函数的使用:

    nonzero(dataSet) : 返回数据集中非零(false)的数据。

    dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]     

                 clusterAssment[:,0].A==cent : 比较第一列索引 也即是数据集中数据点的归属类,进行比较看哪些属于当前类质心。                   

matrix.A

         Return self as an ndarray object.

         Equivalent to np.asarray(self).           # 以ndarray对象返回

另外只去[0]第一列进行判断。

mean(daMmat, axis=0)  

axis = 0 对每一列进行求平均值         

datMat=numpy.array([

[1,2,3],

[4,5,6]

])

mean(datMat, axis=0)

>>>> array([2.5, 3.5, 4.5])

axis=1 是对每一行进行求平均值

mean(datMat, axis=1)

>>>>array([2., 5.])

 

数据可视化

数据可视化能够让我们更直观的看到算法的效果

可视化程序只是针对4分类的可视化 可以自己更改成自己需要的。

# -*- coding:utf-8 -*-
from numpy import *
import matplotlib.pyplot as plt
import kMeans

def make_result(fileName='testSet.txt',k=4):
	datMat = mat(kMeans.loadDataSet(fileName))
	myCentroids, clustAssing = kMeans.kMeans(datMat,k)       # k = 4
	# 将matrix 数据转换成 array
	datMatA = array(datMat)
	# 获取的聚类结果
	clustAssingA = array(clustAssing)
	clustAssingy = clustAssingA[:,0]  # 只要第一列的分到各簇的结果


	plt.scatter(datMatA[clustAssingy==0, 0],
	datMatA[clustAssingy==0, 1],
	c='red',
	marker ='s',
	label = 'cluster 1')


	plt.scatter(datMatA[clustAssingy==1, 0],
	datMatA[clustAssingy==1, 1],
	c='green',
	marker ='o',
	label = 'cluster 2')

	plt.scatter(datMatA[clustAssingy==2, 0],
	datMatA[clustAssingy==2, 1],
	c='orange',
	marker ='v',
	label = 'cluster 3')

	plt.scatter(datMatA[clustAssingy==3, 0],
	datMatA[clustAssingy==3, 1],
	c='blue',
	marker ='x',
	label = 'cluster 4')

	'''
	myCenteroids
	matrix([[ 2.80293085, -2.7315146 ],
		[ 2.6265299 ,  3.10868015],
		[-2.46154315,  2.78737555],
		[-3.38237045, -2.9473363 ]])
	>>> centerA = array(myCenteroids)
	'''
    # 根据结果集 勾绘类中心 
	centerA = array(myCentroids)
	plt.scatter(centerA[:,0],
	centerA[:,1],
	marker='*',
	c = 'purple',
	label = 'centroids')
	
	plt.legend(loc='upper left')  # 将指示label放到左上角
	plt.grid(True)
	plt.show()

 

  下面多次随机化初始化类中心 可以观察到结果也会有很大的差异:

 

                                        《k-means聚类算法总结》

 

                                       《k-means聚类算法总结》

                                    《k-means聚类算法总结》

                                       《k-means聚类算法总结》

直观上很容易观察到 第二幅图 聚类分析效果并不好。

也可以通过观察clustAssing第二列数据值来验证效果,误差在1.8左右时效果最好。

 

 

 

    原文作者:聚类算法
    原文地址: https://blog.csdn.net/liuhehe123/article/details/82492047
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞