Kmeans聚类算法python sklearn用户画像教程
作者:圆觉_
1、基本概念
聚类分析简称聚类(clustering),是一个把数据集划分成子集的过程,每一个子集是一个簇(cluster),使得簇中的样本彼此相似,但与其他簇中的样本不相似。
聚类分析不需要事先知道样本的类别,甚至不用知道类别个数,因此它是一种典型的无监督学习算法,一般用于数据探索,比如群组发现和离群点检测,还可以作为其他算法的预处理步骤。
在工作中遇到用户画像、群组划分问题,而kmeans聚类这一无监督学习算法,可以在无数据标注训练情况下,基于距离按将群组划分不同的簇。
主要的聚类算法一般可以划分为以下几类:
方法 | 一般特点 |
---|---|
划分方法 | 1.发现球形互斥的簇 2.基于距离 3.可用均值或中心点代表簇中心 4.对中小规模数据有效 |
层次方法 | 1.聚类是一个层次分解 2.不能纠正错误的合并或划分 3.可以集成其他技术 |
基于密度的方法 | 1.可以发现任意形状的簇 2.簇是对象空间中被低密度区域分隔的稠密区域 3.簇密度 4.可能过滤离群点 |
基于网格的方法 | 1.使用一种多分辨率网格数据结构 2.快速处理 |
2、Kmeans算法
Kmeans属于划分方法的经典聚类方法。
算法步骤如下:
选择K个点作为初始质心(随机产生或者从D中选取)
repeat
:将每个点分配到最近的质心,形成K个簇; 重新计算每个簇的质心until
:簇不发生变化或达到最大迭代次数
2.1 k值选取
k的值是用户指定的,表示需要得到的簇的数目。
在运用Kmeans算法时,我们一般不知道数据的分布情况,不可能知道数据的集群数目,所以一般通过枚举来确定k的值。
另外,在实际应用中,由于Kmean一般作为数据预处理,或者用于辅助分类贴标签,所以k一般不会设置很大。
2.2 初始质心的选取
Kmeans算法对初始质心的选取比较敏感,选取不同的质心,往往会得到不同的结果。
初始质心的选取方法,常用以下两种的简单方法:一种是随机选取,一种是用户指定。
需要注意的是,无论是随机选取还是用户指定,质心都尽量不要超过原始数据的边界,即质心每一维度上的值要落在原始数据集每一维度的最小与最大值之间。
2.3 距离度量方法
距离度量方法(或者说相似性度量方法)有很多种,常用的有欧氏距离,余弦相似度,街区距离,汉明距离等等。
在Kmeans算法中,一般采用欧氏距离计算两个点的距离,欧氏距离如下:
distEclud(X,Y)=∑i=1n(Xi−Yi)2‾‾‾‾‾‾‾‾‾‾‾‾⎷
举个例子,X=(1000,0.1),Y=(900,0.2),那么它们的欧氏距离就是:
(1000−900)2+(0.1−0.2)2‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√≈100 (1000−900)2+(0.1−0.2)2≈100
举这个例子是为了说明,当原始数据中各个维度的数量级不同时,它们对结果的影响也随之不同,那些数量级太小的维度,对于结果几乎没产生任何影响。
比如所举的例子中的第二个维度的0.1,0.2,与第一个维度1000的数量级相差了一万倍。
为了赋予数据每个维度同等的重要性,我们在运用欧氏距离时,必须先对数据进行规范化,比如将每个维度都缩放到[0,1]之间。
2.4 质心的计算
在Kmeans算法中,将簇中所有样本的均值作为该簇的质心。这也是Kmeans名字的由来吧。
2.5 算法停止条件
在两种情况下算法应该停止:一种是达到了指定的最大迭代次数,一种是算法已经收敛,即各个簇的质心不再发生变化。关于算法的收敛,在2.5部分讨论。
2.6 代价函数与算法收敛
Kmeans算法的代价函数比较简单,就是每个样本点与其所属质心的距离的平方和(误差平方和,Sum of Squared Error,简称SSE):
J(c,u)=∑i=1k||X(i)−uc(i)||2
与其他机器学习算法一样,我们要最小化这个代价函数,但这个函数没有解析解,所以只能通过迭代求解的方法来逼近最优解(这一点也和众多机器学习算法一样吧)。
所以你再看看算法步骤,其实就是一个迭代过程。
由于代价函数(SSE)是非凸函数,所以在运用Kmeans算法时,不能保证收敛到一个全局的最优解,我们得到的一般是一个局部的最优解。
因此,为了取得比较好的效果,一般会多跑几次算法(用不同的初始质心),得到多个局部最优解,比较它们的SSE,选取SSE最小的那个。
方法优点:
- k-平均算法是解决聚类问题的一种经典算法,算法简单、快速。
- 对处理大数据集,该算法是相对可伸缩的和高效率的,因为它的复杂度大约是O(nkt),其中n是所有对象的数目,k是簇的数目,t是迭代的次数。通常k<<n。这个算法经常以局部最优结束。
- 算法尝试找出使平方误差函数值最小的k个划分。当簇是密集的、球状或团状的,而簇与簇之间区别明显时,它的聚类效果很好。
缺点:
- K 是事先给定的,这个 K 值的选定是非常难以估计的;
- 对初值敏感,对于不同的初始值,可能会导致不同的聚类结果。一旦初始值选择的不好,可能无法得到有效的聚类结果;
- 该算法需要不断地进行样本分类调整,不断地计算调整后的新的聚类中心,因此当数据量非常大时,算法的时间开销是非常大的。
- 不适合于发现非凸面形状的簇,或者大小差别很大的簇;
- 对于”噪声”和孤立点数据敏感,少量的该类数据能够对平均值产生极大影响。
3、Kmeans算法实现
采用python实现,基于numpy、sklearn库,其中从sklearn.cluster中import KMeans。
为了可视化聚类效果,对二维数据进行聚类并用matplot画出数据不同簇的划分。
#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ @author: liuweima """ from sklearn.cluster import KMeans from sklearn.externals import joblib import numpy import time import matplotlib.pyplot as plt import sys reload(sys) sys.setdefaultencoding('utf8') if __name__ == '__main__': ## step 1: 加载数据 print "step 1: load data..." dataSet = [] loss = [] fileIn = open('path') for line in fileIn.readlines(): lineArr = line.strip('\xef\xbb\xbf') # '\xef\xbb\xbf'是BOM,标识读入的文件是UTF-8编码,需strip()切掉 lineArr = lineArr.strip().split('\t') #注意自己文件中每行数据中是用什么对列数据做分割 建议先用Word 规范一下要打开的文件 dataSet.append([float(lineArr[0])/1.99759326,(float(lineArr[1])-100)/192230]) #数据规范化【0,1】 print dataSet #设定不同k值以运算 for k in range(2,10): clf = KMeans(n_clusters=k) #设定k !!!!!!!!!!这里就是调用KMeans算法 s = clf.fit(dataSet) #加载数据集合 numSamples = len(dataSet) centroids = clf.labels_ print centroids,type(centroids) #显示中心点 print clf.inertia_ #显示聚类效果 mark1 = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr'] #画出所有样例点 属于同一分类的绘制同样的颜色 for i in xrange(numSamples): #markIndex = int(clusterAssment[i, 0]) plt.plot(dataSet[i][0], dataSet[i][1], mark1[clf.labels_[i]]) #mark[markIndex]) mark2 = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', '<b', 'pb'] # 画出质点,用特殊图型 centroids = clf.cluster_centers_ for i in range(k): plt.plot(centroids[i][0], centroids[i][1], mark2[i], markersize = 12) #print centroids[i, 0], centroids[i, 1] plt.show() loss.append(clf.inertia_) for m in range(8): #因为k 取值是2-9 (!不包括10) m取值是0-7 plt.plot(m,loss[m],'bo') plt.show()
质心的初始化、最大迭代次数都为默认值。
其中读取文件一些坑已经做了备注。整个kmeans算法不是很耗时,主要在画图上花费时间。以我跑十万级别的数据来看,画图很费时间。
聚类结果图 ,涉及业务,就不贴出了。
贴出,k取range(2,30)时,kmeans的误差平方和(SSE)曲线图。
为了弥补经典kmeans聚类算法的不足,出现了一些改进型kmeans
比如二分Kmeans算法(bisectingKmeans)是为了克服Kmeans算法收敛于局部最小值的问题而提出的。
该算法首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续划分,选择哪一个簇进行划分
取决于对其划分是否可以最大程度降低SSE的值,上述过程不断迭代,直到得到用户指定的簇数目为止。
或者先使用MeanShift算法自动生成k值删除游离点。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。