机器学习第九章K-均值聚类算法

利用K-均值聚类算法对未标注数据分组

文章目录

  • 利用K-均值聚类算法对未标注数据分组
    • 基本术语
    • K-均值聚类算法实现
    • 对地理坐标做聚类
    • 小结

基本术语

  • 聚类:一种无监督学习方式,相较于有监督学习方式需要使用带有标签的数据来指导模型进行学习,聚类不要求数据必须带标签,它要求模型自行发现数据中的结构、关系,比如对一群消费者做聚类,模型会根据购买记录,把消费者分成不同的类群,比如买了一堆柴米油盐的人可能被归到为1类(家庭主妇)、买了一堆零食的被归类为2类(大学生)。
  • K-均值聚类:聚类的一种,也是无监督算法,它把样本分成k个不同的簇,目的是:最小化簇内样本之间的平方距离之和,最大化簇间的样本距离。
  • 二分K-均值聚类:K-均值聚类的一种改进,以递归的方式对簇做二分化(每次化一个簇为两个子簇)。具体做法为:初始化,把整个数据集作为一个簇;使用某种策略,循环对一个簇进行划分,比如选择具有最大误差的簇,再对选择好的簇做K-均值聚类,从而化成两个子簇;计算划分好的子簇的误差,然后更新簇集合;循环除了初始化以外的步骤,直到达到预定的簇数量或设好的迭代次数。

K-均值聚类算法实现

伪代码

随机创建K个点作为起始质心
当任意一个点的簇分配结果发生变动
    对数据集中的每个点
        对每个质心
            计算质心和数据点之间的距离
        把当前数据点分配到最近的簇
    对每个簇,计算簇里所有点的均值,以此作为质心

运行代码

  • loadDataSet()函数从txt文本中加载数据集,返回一个矩阵
  • distEclud(vecA, vecB)函数会被多次调用,负责计算a,b两点的距离
  • randCent(dataSet, k)函数用于初始化聚类中心,先确定数据维度n,创建一个大小为(k,n)的矩阵centroids,初始化为零矩阵;对于每个维度j,找到数据集在当前维度上的最小值minJ和rangeJ,随机生成一个(k,1)的随机矩阵,用该随机矩阵乘以rangeJ,再加上minJ得到每个聚类中心在该维度上的坐标,循环结束后返回centroids
  • kMeans(dataSet, k, distMeas=distEclud, createCent=randCent)函数接收前面几个函数的结果作为参数。先获取数据集的行数m,创建(m,2)维度的零矩阵clusterAssment以存储每个点所属的类别、距离;然后用createCent函数初始化聚类中芯centroids,设置一个clusterChanged记录当前循环中是否有样本类的类别被改变。在while里,默认样本类别没被改变,对每个样本点计算其与聚类中心的距离,找到最近的中心的索引minIndex,如果点的索引和当前类别的索引不同,说明点的类别变了。最后计算每个聚类中心cent,找出所有属于这个类别的样本点,计算平均值并以此值为新的聚类中心。
    有点像kruskal和prim算法,复杂度很高
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):
    disTance = sqrt(sum(power(vecA-vecB,2)))
    return disTance

# 用于初始化聚类中心的函数
def randCent(dataSet,k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k,n)))  # 每一行都是一个质心的坐标,有n维度
    for j in range(n):
        minJ = min(dataSet[:,j])
        rangeJ = float(max(dataSet[:,j]) - minJ)
        centroids[:,j] = minJ + rangeJ * random.rand(k,1)
        # 随机构建k个聚类中心,每一个中心的每一个坐标都不会超过已有样本的范围
    return centroids

def kMeans(dataSet, k, distMeas = distEclud, createCent = randCent):
    # 与之前的类似,后两个参数为函数的引用,当想用另外的方式计算距离或者初始化
    # 就改这两个参数即可
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))  # 用来存储样本点所属的类和距离
    centroids = createCent(dataSet,k)  # 初始化k个类的质心
    clusterChanged = True  # 用来记录当前循环是否有样本点改变了类别
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDist = inf  # 初始化
            minIndex = -1
            for j in range(k):
                distIJ = distMeas(centroids[j,:],dataSet[i,:])  # 计算距离
                if distIJ < minDist:  # 更新距离
                    minDist = distIJ
                    minIndex = j
            if clusterAssment[i,0] != minIndex:  # 如果不相等则说明发生了更新
                clusterChanged = True
                clusterAssment[i,:] = minIndex,minDist**2
        print(centroids)
        for cent in range(k):  # 更新质心的位置
            # 先找出属于当前类的样本点
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A == cent)[0]]
            centroids[cent,:] = mean(ptsInClust,axis = 0)  # 按照平均值来更新
    return centroids,clusterAssment

if __name__ == '__main__':
    datMat=mat(loadDataSet('./8/testSet.txt'))
    rand_centroids=randCent(datMat, 2)
    print('Random centroids:',rand_centroids)
    myCentroids,clustAssing=kMeans(datMat,4)
    print('myCentroids:',myCentroids)
    

随机得到四个簇的质心
机器学习第九章K-均值聚类算法_第1张图片

对地理坐标做聚类

原理和上个实验差不多,但是要提取信息有点麻烦,比如txt文本中有一段的文本需要提取出以下内容:
名称:Dolphin II
地址:10860 SW Beaverton-Hillsdale Hwy
城市和州:Beaverton, OR
纬度:45.486502
经度:-122.788346

执行的代码:
scatter_markers表示用多种图形代表一类地点

def cluster_clubs(num_clust=5):
    dat_list = []
    with open("8/places.txt", 'r') as fr:
        for line in fr.readlines():
            line_arr = line.split('\t')
            dat_list.append([float(line_arr[4]), float(line_arr[3])])

    dat_mat = mat(dat_list)
    my_centroids, clust_assing = bi_k_means(dat_mat, num_clust, dist_meas=dist_slc)

    fig = plt.figure()
    rect = [0.1, 0.1, 0.8, 0.8]
    scatter_markers = ['s', 'o', '^', '8', 'p', 'd', 'v', 'h', '>', '<']
    axprops = dict(xticks=[], yticks=[])
    ax0 = fig.add_axes(rect, label='ax0', **axprops)
    img_p = plt.imread("8/Portland.png")
    ax0.imshow(img_p)
    ax1 = fig.add_axes(rect, label='ax1', frameon=False)

    for i in range(num_clust):
        pts_in_curr_cluster = dat_mat[nonzero(clust_assing[:, 0].A == i)[0], :]
        marker_style = scatter_markers[i % len(scatter_markers)]
        ax1.scatter(pts_in_curr_cluster[:, 0].flatten().A[0], pts_in_curr_cluster[:, 1].flatten().A[0],
                    marker=marker_style, s=90)
    ax1.scatter(my_centroids[:, 0].flatten().A[0], my_centroids[:, 1].flatten().A[0], marker='+', s=300)
    plt.show()
    plt.savefig('hh.png')

效果:机器学习第九章K-均值聚类算法_第2张图片

小结

K-均值聚类算法是无监督学习的一种,随机选择一个点作为一个簇的质心,新加入的质点可能影响质心的选择,重复这个过程直到质心稳定。这个算法感觉比较直观,但是迭代次数很大,并且如果有几个点比较离谱,可能会影响结果的准确性,比如地理聚类实验左上角、右下角的点。

你可能感兴趣的:(机器学习,算法,均值算法)