[聚類算法]聚類算法中的K-Means實現以及驗證

K-Means聚類算法,感覺是接觸到目前爲止,距離程序員思維最近的算法,應該也是使用到的數理知識最簡單的算法。
所以在記筆記的時候,忍不住,又去實現了一把,但是根據吳大大(吳恩達)的介紹來看,的確是不需要每個算法都自己去寫的,而是需要了解的算法的本質、知道算法適用的場景,多加練習,才能達到熟能生巧的程度。
在這裏插入圖片描述
符號“o”表示數據點。符號“x”表示我們需要尋找的中心點。
主要思路描述:
1、在所有的點中隨機選擇K點(也就是中心點),詳見函數中的getRandom(我在函數中增加了K點不重複的邏輯,如果重複的話,結果就變成K-1個點了);
2、計算每個x點到第K個點的距離,假設我們x(i),x有i個點;K(k),K中心點有k個點;我們將x(i)分析給到第K個點距離最短的那個K點。也就是歸屬到哪個K點的勢力範圍;
3、我們計算每個K點的勢力範圍下面的x(i)的點,並計算在該勢力範圍的中位點。並將K點移動到對位的中位點。
然後再重複2、3步驟,直到最終K點不再移動。
在這裏插入圖片描述
注:圖片借用網友的圖(原文章地址:https://blog.csdn.net/Katherine_hsr/article/details/79382249)
是一篇比較綜合來講聚類算法的文檔。

根據上述的分析來看代碼的實現:

%分類器,聚類算法(K-Means)
%入參:k,隨機點K的個數,K=2,表示將所有的點分成兩個簇cluster;
%入參:x,表示欲分類的記錄集(x1,x2,x3...,xj)表示j個維度或者特徵
%出參:K,表示每次分類選擇的K點,形式如下:[k01,k02,..k0k;k11,k12,...kik],k表示族數,i表示循環選點的次數
function K = CluseringKmeans(k,x)
  %iCount代表樣本數;
  %jCount代表特徵數;
  iCount=size(x)(1);
  jCount=size(x)(2);
  %隨機取點
  RandList = getRandom(iCount,k)
  K(:,:)=x(RandList,:)
  LastKean=mean(mean(K));
  KMeanErr = LastKean - 0;
  while abs(KMeanErr)>0,
    LastKean=mean(mean(K));
    %求所有x的點到L點的距離,然後選擇最小的,放到C中去
    for iIndex = 1 : iCount,
      %用來記錄最小的點
      minMixValue=0;
      %需要對每個點的到每個K值點的距離進行求解
      for kIndex = 1 : k,
        %如果K(i)與當前取值點是相同的,則距離等於0,這種情況下,不需要對計算
        if iIndex == RandList(kIndex,1),
          C(iIndex,1)=kIndex;
          break;
         elseif,
           tmpMix=x(iIndex,:)-K(kIndex,:);
           MixErr=tmpMix'*tmpMix;
           %如果minMixValue=0代表還沒有賦值,直接使用當前點
           if minMixValue==0,
             minMixValue=MixErr;
           endif
           if MixErr<=minMixValue,
             C(iIndex,1)=kIndex;
           endif
        endif
      endfor    
    endfor
    %重新計算K點的均值
    
    for kIndex =1 :k,
     K(kIndex,:) = mean(x(find(C==kIndex),:),1);
    endfor
    %如果兩點的K點已經固定,則退出循環
    KMeanErr=mean(mean(K))-LastKean;
  endwhile; 
endfunction

%返回隨機數列表,列表中的隨機數不重複
%入參:itop,表示隨機數從1到iTop之間;
%入參:iNumberCount表示要生成多少個隨機數
function randList = getRandom(itop,iNumberCount)
  randList = zeros(iNumberCount,1);
  iCernLen = 1 / itop;
  for iIndex = 1 : iNumberCount,
  %在iTop範圍內,找一個不重複的Index
    bRepeat=false;
    while bRepeat == false,
      iTmpNum = floor(rand(1)/iCernLen);
      if iTmpNum==0,
        break;
      endif
      bRepeat=true;
      for rIndex = 1 : size(randList)(1),
        if iTmpNum == randList(rIndex,1),
            bRepeat = false;
            break;
        endif
      endfor      
     endwhile
     randList(iIndex,1)=iTmpNum;    
  endfor
endfunction

對上述代碼進行驗證:

x=[0.5,0.4;0.6,0.2;1.2,1.8;1.9,2.0;2.5,2.2;2.8,2.9;2.8,3.2;3.2,4.0;0.8,1.2;1.0,1.1;0.8,1.2;1.8,2.0;1.9,2.2;2.0,2.1]
plot(x(:,1),x(:,2),'o')
hold on
k=CluseringKmeans(1,3,x)
plot(k(:,1),k(:,2),'x')

得到的結果如下圖所示:
在這裏插入圖片描述
從上圖來看,憑我們的直覺也會認爲這並不是一個小的分類。
那麼我們需要多次執行,綜合去尋找一個比較好的結果。再多試幾次試一下:
在這裏插入圖片描述
看淡橙的點,這個結果就比較符合我們的期望了。聚類算法的結果,目前還是需要多次執行來尋找一個最優解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章