咱們目的是將樣本分紅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,。。。。。。