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