咱们目的是将样本分红k个类,其实说白了就是求每一个样例x的隐含类别y,而后利用隐含类别将x归类。因为咱们事先不知道类别y,那么咱们首先能够对每一个样例假定一个y吧,可是怎么知道假定的对不对呢?怎么评价假定的好很差呢?咱们使用样本的极大似然估计来度量,这里是就是x和y的联合分布P(x,y)了。若是找到的y可以使P(x,y)最大,那么咱们找到的y就是样例x的最佳类别了,x顺手就聚类了。可是咱们第一次指定的y不必定会让P(x,y)最大,并且P(x,y)还依赖于其余未知参数,固然在给定y的状况下,咱们能够调整其余参数让P(x,y)最大。可是调整完参数后,咱们发现有更好的y能够指定,那么咱们从新指定y,而后再计算P(x,y)最大时的参数,反复迭代直至没有更好的y能够指定。css
聚类的目标:python
K-means算法是将样本聚类成k个簇(cluster),具体算法描述以下算法
复杂度:n*k*tapp
优势:快dom
缺点:ide
K-means面对的第一个问题是如何保证收敛,前面的算法中强调结束条件就是收敛,能够证实的是K-means彻底能够保证收敛性。下面咱们定性的描述一下收敛性,咱们定义畸变函数distortion function
以下:函数
J函数表示每一个样本点到其质心的距离平方和。K-means是要将J调整到最小。假设当前J没有达到最小值,那么首先能够固定每一个类的质心μ,调整每一个样例的所属的类别c^(i)^来让J函数减小,一样,固定c^(i)^,调整每一个类的质心μ也可使J减少。这两个过程就是内循环中使J单调递减的过程。当J递减到最小时,μ和c也同时收敛。(在理论上,能够有多组不一样的μ和c值可以使得J取得最小值,但这种现象实际上不多见)。学习
因为畸变函数J是非凸函数,意味着咱们不能保证取得的最小值是全局最小值,也就是说k-means对质心初始位置的选取比较感冒,但通常状况下k-means达到的局部最优已经知足需求。但若是你怕陷入局部最优,那么能够选取不一样的初始值跑多遍k-means,而后取其中最小的J对应的μ和c输出。3d
def ReAssignClass(): did = 0 totalDis = 0.0 # 全部的距离 for doc in DocList: min = ComputeDis(doc, ClassCenterList[0]) minIndex = 0 for i in range(1, K): dis = ComputeDis(doc, ClassCenterList[i]) if dis < min: min = dis minIndex = i ClassList[did] = minIndex did += 1 totalDis += min return totalDis
注意到,K是咱们事先给定的聚类数,ComputeDis将会计算出样例i与k个类中距离最近的那个类,其的值是1到k中的一个。质心表明咱们对属于同一个类的样本中心点的猜想(即μ,其实也就是咱们随机初始化一个点),如若拿星团模型来解释就是要将全部的星星聚成k个星团,首先随机选取k个宇宙中的点(或者k个星星)做为k个星团的质心,而后第一步对于每个星星计算其到k个质心中每个的距离,而后选取距离最近的那个星团记为min
,这样通过第一步每个星星都有了所属的星团。code
def ComputeDis(doc1, doc2): sum = 0.0 for (wid, freq) in doc1.items(): if wid in doc2: d = freq - doc2[wid] sum += d * d else: sum += freq * freq for (wid, freq) in doc2.items(): if wid not in doc1: sum += freq * freq sum = math.sqrt(sum) return sum
第二步对于每个星团,从新计算它的质心ReComputeCentroids
(对里面全部的星星坐标求平均)。重复迭代第一步和第二步直到质心不变或者变化很小。
def ReComputeCentroids(): for i in range(K): ClassSizeList[i] = 0 ClassCenterList[i] = {} for i in range(len(DocList)): classid = ClassList[i] ClassSizeList[classid] += 1 AddDoc(ClassCenterList[classid], DocList[i]) for i in range(K): ClassCenterList[i] = Average(i)
基于Kmeans的文章聚类(把相同的文章聚合)
1000份已经打好标签的文本,而且由标签分类如bussiness
,it
,yule
...
抽取其中一分内容已经通过文本预处理,每一个词汇经过
分隔开
数据预处理以后经过idf
为这些词打分
def AddIDF(DocList): wordDic = {} for doc in DocList: for word in doc.keys(): if word in wordDic: wordDic[word] += 1 else: wordDic[word] = 1 N = len(DocList) for doc in DocList: for word in doc.keys(): doc[word] *= math.log(N + 1 / (float)(wordDic[word])) Normalize(doc)
初始化K个类中心,也就是Kmeans的核心节点
def Init(): templist = random.sample(DocList, K) for i in range(K): ClassSizeList.append(0) ClassCenterList.append(templist[i])
计算各个点到(类)中心的距离,固然了,这个距离是尽量的越小越好,输出的结果就是wcss
,其计算方式以下:
def ComputeDis(doc1, doc2): sum = 0.0 for (wid, freq) in doc1.items(): if wid in doc2: d = freq - doc2[wid] sum += d * d else: sum += freq * freq for (wid, freq) in doc2.items(): if wid not in doc1: sum += freq * freq sum = math.sqrt(sum) return sum
本质上就是计算同一个簇内的距离的平方和后开方,
while math.fabs(oldWCSS - WCSS) > Threshold: oldWCSS = WCSS print "Iteration", i, "WCSS:", WCSS ReComputeCentroids() WCSS = ReAssignClass() i += 1 ReComputeCentroids() print "Final iteration WCSS:", WCSS
重复迭代计算,直到wcss是最小,我用oldWCSS - WCSS
作差,而threshold
我自定义为1,固然了能够更加精确的,好比0.0001??
运行结果如图所示
有5种分类方式,其中第一种的分类方式准确率是0.6,第二种是0.9,。。。。。。