八 机器学习之K均值聚类

229 阅读5分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

一、聚类分析概述

聚类分析是无监督类机器学习算法中最常用的一类,其目的是将数据划分成有意义或有用的组(也被称为簇)。组内的对象相互之间是相似的(相关的),而不同组中的对象是不同的(不相关的)。组内的相似性(同质性)越大,组间差别越大,聚类就越好。

1.簇的定义

简单来说,簇就是分类结果中的类,但实际上簇并没有明确的定义,并且簇的划分没有客观标准,我们可以利用下图来理解什么是簇。该图显示了20个点和将它们划分成簇的3种不同方法。标记的形状指示簇的隶属关系。下图分别将数据划分成两部分、四部分和六部分。将2个较大的簇每一个都划分成3个子簇可能是人的视觉系统造成的假象。此外,说这些点形成4个簇可能也不无道理。该图表明簇的定义是不精确的,而最好的定义依赖于数据的特性和期望的结果。

2常用的聚类算法

最常用的聚类算法有以下三种: ·K-means聚类:也称为K均值聚类,它试图发现k(用户指定个数)个不同的簇,并且每个簇的中心采用簇中所含值的均值计算而成。 ·层次聚类:层次聚类(hierarchical clustering)试图在不同层次对数据集进行划分,从而形成树形的聚类结构。.DBSCAN:这是一种基于密度的聚类算法,簇的个数由算法自动地确定。低密度区域中的点被视为噪声而忽略,因此DBSCAN不产生完全聚类。

二、K-均值聚类算法

K均值是发现给定数据集的k个簇的算法。簇个数k是用户给定的,每一个簇通过其质心(centroid)来描述。 K均值工作流程是这样的:首先,随机选择K个初始质心,其中K是用户指定的参数,即所期望的簇的个数。然后将数据集中每个点指派到最近的质心,而指派到一个质心的点即为一个簇。然后,根据指派到簇的点,将每个簇的质心更新为该簇所有点的平均值。重复指派和更新步骤,直到簇不发生变化,或等价地,直到质心不发生变化。 1.K-均值算法的python实现 根据k-均值算法的工作流程,我们可以写出伪代码: 创建k个点作为初始质心(通常是随机选择) 当任意一个点的簇分配结果发生改变时: 对数据集中的每个点: 对每个质心: 计算质心与数据点之间的距离将数据点分配到据其最近的簇 对每个簇,计算簇中所有点的均值并将均值作为新的质心直到簇不再发生变化或者达到最大迭代次数 在伪代码中,有提到"”最近"的说法,那就意味着要进行某种距离的计算这里我们使用的是最简单的欧式距离。

三 导入数据集

此处先以经典的鸢尾花数据集为例,来帮助我们建模,数据存放在iris.txt中

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
#导入数据集
iris = pd.read_csv('iris.txt',header = None)

四 距离计算函数

我们需要定义一个两个长度相等的数组之间欧式距离计算函数,在不直接应用计算距离计算结果,只比较距离远近的情况下,我们可以用距离平方和代替距离进行比较,化简开平方运算,从而减少函数计算量。 此外需要说明的是,涉及到距离计算的,一定要注意量纲的统一。如果量纲不统一的话,模型极易偏向量纲大的那一方。此处选用鸢尾花数据集,基本不需要考虑量纲问题。

def distEclud(arrA, arrB):
    d = arrA - arrB
    dist = np.sum(np.power(d, 2), axis=1)
    return dist

五 自动生成随机质心的函数

在定义随机质心生成函数时,首先需要计算每列数值的范围,然后从该范围中随机生成指定个数的质心。此处我们使用numpy.random.uniform()函数生成随机质心。

def randCent(dataSet, k): 
    n = dataSet.shape[1]
    data_min = dataSet.iloc[:, :n-1].min()
    data_max = dataSet.iloc[:, :n-1].max()
    data_cent = np.random.uniform(data_min,data_max,(k, n-1))
    return data_cent

六 k-eans聚类函数

在执行K-Means的时候,需要不断的迭代质心,因此我们需要两个可迭代容器来完成该目标: 第一个容器用于存放和更新质心,该容器可考虑使用list来执行,list不仅是可迭代对象,同时list内不同元素索引位置也可用于标记和区分各质心,即各簇的编号; 第二个容器则需要记录、保存和更新各点到质心之间的距离,并能够方便对其进行比较,该容器考虑使用一个三列的数组来执行,其中第一列用于存放最近一次计算完成后某点到各质心的最短距离,第二列用于记录最近一次计算完成后根据最短距离得到的代表对应质心的数值索引,即所属簇,即质心的编号,第三列用于存放上一次某点所对应质心编号(某点所属簇),后两列用于比较质心发生变化后某点所属簇的情况是否发生变化。

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m,n = dataSet.shape
    centroids = createCent(dataSet, k)
    clusterAssment = np.zeros((m,3))
    clusterAssment[:, 0] = np.inf
    clusterAssment[:, 1: 3] = -1
    result_set = pd.concat([dataSet, pd.DataFrame(clusterAssment)], axis=1, ignore_index = True)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            dist = distMeas(dataSet.iloc[i, :n-1].values, centroids)
            result_set.iloc[i, n] = dist.min()
            result_set.iloc[i, n+1] = np.where(dist == dist.min())[0]
        clusterChanged = not (result_set.iloc[:, -1] == result_set.iloc[:, -2]).all()
        if clusterChanged:
            cent_df = result_set.groupby(n+1).mean()
            centroids = cent_df.iloc[:,:n-1].values
            result_set.iloc[:, -1] = result_set.iloc[:, -2]
    return centroids, result_set