机器学习实战笔记-第十章 利用K-均值聚类算法对未标记数据分组

聚类与分类的最大不同在于,分类的目标事先已知,而聚类则不一样。因为其产生的结果与分类相同,而只是类别没有预定义,聚类有时也称为无监督分类(unsupervised classification)
 
 
K-means算法
优点:容易实现
缺点:可能收敛到局部最小值,在大规模数据集上收敛缓慢
适用数据类型:数值型数据。
 
K-均值是发现给定数据集的k个簇的算法。簇个数k是用户给定的,每个簇通过其质心(centroid),即簇中所有点的中心来描述。
 
工作流程如下:
(1) 随机确定k个初始点作为质心。
(2) 将数据集中的每个点分配到一个簇中,具体讲为每个点找距其最近的质心,并将其分配给该质心所对应的簇。
(3) 每个簇的质心更新为该簇所有点的平均值。


伪码如下:
创建k个点作为起始质心(随机选择)
当任意一个点的簇分配结果发生改变时
对数据集中的每个数据点
对每个质心
计算质心与数据点之间的距离
将数据点分配到距其最近的簇
对每一个簇,计算簇中所有点的均值并将均值作为质心
 

代码如下:

from numpy import *


def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        from numpy import *


def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = list(map(float,curLine))
        dataMat.append(fltLine)
    return dataMat


def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA - vecB, 2)))


def randCent(dataSet, k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k,n)))
    for j in range(n):
        minJ = min(dataSet[:,j]) 
        rangeJ = float(max(dataSet[:,j]) - minJ)
        centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1))
    return centroids
    
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))
                                     
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDist = inf; minIndex = -1
            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
        for cent in range(k):
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]
            centroids[cent,:] = mean(ptsInClust, axis=0)
    return centroids, clusterAssment




datMat = loadDataSet('testSet.txt')
myCentroids, clusterAssing = kMeans(datMat, 4)
        dataMat.append(fltLine)
    return dataMat


def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA - vecB, 2)))


def randCent(dataSet, k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k,n)))
    for j in range(n):
        minJ = min(dataSet[:,j]) 
        rangeJ = float(max(dataSet[:,j]) - minJ)
        centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1))
    return centroids
    
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))
                                     
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDist = inf; minIndex = -1
            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
        for cent in range(k):
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]
            centroids[cent,:] = mean(ptsInClust, axis=0)
    return centroids, clusterAssment




datMat = loadDataSet('testSet.txt')
myCentroids, clusterAssing = kMeans(datMat, 4) 

 代码是从源码中拮取下来的, 修改了一个地方,见17行处,该处不用list()处理的话会有问题,代码比较简单。不过有些numpy的数据处理比较生疏了,自己写代码理解下:
clusterAssment = mat(zeros((10,2)))
print(clusterAssment[:,0].A)


clusterAssment[0,:] = 1, 0
clusterAssment[1,:] = 1, 10
clusterAssment[2,:] = 2, 20
clusterAssment[3,:] = 3, 30


print(clusterAssment[:,0].A)


data = []
for i in range(10):
    data.append([i])


data = mat(data)


for i in range(4):
    print(clusterAssment[:,0].A==i)
    print(nonzero(clusterAssment[:,0].A==i)[0])
    ptsInClust = data[nonzero(clusterAssment[:,0].A==i)[0]]
    print(ptsInClust)
    print()


主要是使用mat()矩阵化,易于处理数据。
clusterAssment[:,0].A==i该语句返回一个矩阵,如下:
[[False]
 [False]
 [False]
 [False]
 [ True]
 [ True]
 [ True]
 [ True]
 [ True]
 [ True]]
而nonzero()返回true对应的下标组成的单行矩阵,如下:
[4 5 6 7 8 9]
data[nonzero(clusterAssment[:,0].A==i)[0]]获取到筛选后的矩阵
[[4]
 [5]
 [6]
 [7]
 [8]
 [9]]
用于后续求平均值,更新质心。
二分K-均值算法
目的:解决k-均值算法收敛于局部最小值的问题
 
算法流程:首先将所有点作为一个簇,然后将簇一分为二,之后选择其中一个簇继续进行划分,选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE的值。上述基于SSE的划分过程不断重复,直到得到用户指定的簇数目为止。
 
 
伪代码如下:
 
将所有点看成一个簇
当簇数目小于k时
对于每一个簇
计算总误差
在给定的簇上面进行K-均值聚类(k=2)
计算将该簇一分为二之后的总误差
选择使得误差最小的那个簇进行划分操作

猜你喜欢

转载自blog.csdn.net/zzb5233/article/details/80987928