聚类概念
聚类分析是在对象数据中发现对象之间关系。一般来说,组内相似性越高,组间相似性越大,则聚类的效果越好。
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算法
# 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()
下面多次随机化初始化类中心 可以观察到结果也会有很大的差异:
直观上很容易观察到 第二幅图 聚类分析效果并不好。
也可以通过观察clustAssing第二列数据值来验证效果,误差在1.8左右时效果最好。