文章程式碼顯示

2017年12月13日 星期三

《機器學習筆記》淺談 K-means K均值聚類與實現

聚類是什麼?

聚類分析(cluster analysis)是一種無監督的學習(不依賴預先定義的標記),試圖將相似對象歸入同一簇,將不相似對象歸到不同簇,並且不同簇之間有很大的相異性。

監督學習 (supervised learning):是對具有概念標記(分類)的訓練樣本進行學習,以儘可能對訓練樣本集外的數據進行標記(分類)預測。【神經網絡和決策樹】

無監督學習 (unsupervised learning):是對沒有概念標記(分類)的訓練樣本進行學習,以發現訓練樣本集中的結構性知識。【聚類】


從機器學習的角度講,聚類與分類的最大不同在於,分類(監督)學習的數據對象有標記;而聚類(無監督學習)是一種探索性的分析,在過程中人們不必事先給出一個分類的標準,需要由聚類學習算法自動確定標記。因為其產生的結果與分類相同,但類別沒有被預先定義,所以聚類也被稱為無監督分類(unsupervised classification )。

從實際應用的角度看,聚類分析是數據挖掘的主要任務之一。而且聚類能夠作為一個獨立的工具獲得數據的分布狀況,觀察每一簇數據的特徵,集中對特定的聚簇集合作進一步地分析。

聚類算法通常按照中心點或者分層的方式對輸入數據進行歸類,試圖找到數據的內在結構,以便按照最大的共同點將數據進行歸類。



K-menas

KMeans 算法是最常用的聚類算法,主要思想是:在給定K值和K個初始類簇中心點的情況下,把每個點(亦即數據記錄)分到離其最近的類簇中心點所代表的 類簇中,所有點分配完畢之後,根據一個類簇內的所有點重新計算該類簇的中心點(取平均值),然後再疊代的進行分配點和更新類簇中心點的步驟,直至類簇中心點的變化很小,或者達到指定的疊代次數。

K-Means是聚類算法中的一種,其中K表示類別數,Means表示均值。顧名思義K-Means是一種通過均值對數據點進行聚類的算法。K-Means算法通過預先設定的 K 值及每個類別的初始質心對相似的數據點進行劃分。並通過劃分後的均值疊代優化獲得最優的聚類結果。

K值是聚類結果中類別的數量。簡單的說就是我們希望將數據劃分的類別數。K值決定了初始質心的數量。K值為幾,就要有幾個質心。

更詳盡的解釋在該網頁中

算法過程

1. Load datas

2. 隨機選用 k 個點作為初始質心

3. 計算第一個數據點至每個初始質心的距離

4. 取最小距離,並把第一個數據點歸類至該初始質心所代表的簇

5. 重複步驟 3,4 直到所有數據點都被歸類完成

6. 更新每個簇的初始質心為第一次疊代質心(該簇所有數據點的平均值)

7. 依照步驟 3~6 的概念找出第二次疊代的質心,並不斷反覆疊代直到質心不再移動或變化很小

K如何確定?

K是使用者事先給定的聚類數,即所期望的簇的個數。K個簇點的選取有幾種方法:

一、選擇彼此距離儘可能遠的K個點;
  首先隨機選擇一個點作為第一個初始類簇中心點,然後選擇距離該點最遠的那個點作為第二個初始類簇中心點,然後再選擇距離前兩個點的最近距離最大的點作為第三個初始類簇的中心點,以此類推,直至選出 K 個初始類簇中心點。

二、取所有樣本的質心作為第一個點。然後,對於每個後繼初始質心,選擇離已經選取過的初始質心最遠的點。使用這種方法,確保了選擇的初始質心不僅是隨機的,而且是散開的。但是,這種方法可能選中離群點。

K-menas 算法缺點

一、事先需要給定的這個 K 值非常難以估計,合理的確定 K 值和 K 個初始類簇中心點對於聚類效果的好壞有很大的影響

二、對初值敏感,對於不同的初始值,可能會導致不同的聚類結果

三、對於「噪聲」和離群點數據敏感。

SSE 量化聚類結果

使各個簇(共 k 個)中的數據點與所在簇質心的誤差平方和 SSE(Sum of Squared Error) 達到最小,這是評價 K-means 算法最後聚類效果的評價標準。

Cost function 為



各簇內的樣本越相似,其與該簇質心的誤差平方越小,計算所有簇的誤差平方之和,即可驗證分為 k 個簇時的聚類是否是最優的。SSE 值越小表示數據點越接近於它們的質心,聚類效果也越好。因為對誤差取了平方,因此更加重視那些遠離中心的點。

% k-means
clc
clear
close all

% main variables
dim = 2; % 樣本維度
k = 2;   % 設有 k 類

%% create samples:
for i=1:100  
    x1(i) = rand()*5;
    y1(i) = rand()*5;    
    x2(i) = rand()*5 + 3;
    y2(i) = rand()*5 + 3;
end  
x = [x1,x2];  
y = [y1,y2];  
data = [x;y];
datas = data';
N = size(datas,1);

% 繪出原始 data
figure;
for i = 1:N
   plot(datas(i,1),datas(i,2), '*k'); 
   hold on
end
xlabel('X-axis');
ylabel('Y-axis');
title('Original datas');

% 初始化
CC = zeros(k,dim); % 聚類中心矩陣
D = zeros(N,k); % 樣本與各聚類中心的距離

C = cell(1,k); %% 聚類矩陣
for i = 1:k-1
    C{i} = [i];
end
C{k} = k:N;

B = 1:N; % 上次疊代結果中樣本屬於哪一個聚類
B(k:N) = k;

for i = 1:k
    CC(i,:) = datas(i,:);%假設初始質心位在前 k 個樣本
end

while(1)
    change = 0; %標記分類結果是否有異動
    
    
    %% ===== 對每一個樣本 i ,計算其到第 k 個聚類中心的距離 =====
    for i = 1:N %樣本
        for j = 1:k %質心
              D(i,j) = sqrt( (datas(i,1) - CC(j,1))^2 + (datas(i,2) - CC(j,2))^2 );
        end
        
        t = find( D(i,:) == min(D(i,:)) ); % 找出第 i 個樣本其 D 矩陣中的最小值,得到 i 屬於第 t 類
        
        if B(i) ~= t % 上次疊代結果中第 i 個樣本不屬於第 t 類
            change = 1; % 結果有異動
            
            % 將 i 從第 B(i) 類中移除
            t1 = C{B(i)};
            t2 = find( t1==i );            
            t1(t2) = t1(1);
            t1 = t1(2:length(t1)); 
            C{B(i)} = t1;
            % 將 i 加入第 t 類
            C{t} = [C{t},i]; 
            B(i) = t;
        end %end if     
    end 
       %% ===== 對每一個樣本 i ,計算其到第 k 個聚類中心的距離 end =====
       
    if change == 0 % 分類結果無變化,疊代停止(跳出迴圈)
        break;
    end

%% 重新計算聚類中心矩陣
    for i = 1:k
        CC(i,:) = 0;
        iclu = C{i};
        for j = 1:length(iclu)
            CC(i,:) = datas( iclu(j),: )+CC(i,:);
        end
        CC(i,:) = CC(i,:)/length(iclu); %取樣本平均        
    end
end

% 繪出結果
figure
plot(CC(:,1),CC(:,2),'kd')%繪出 k 個類的質心
hold on
for i=1:N
 if(B(1,i)==1)
        plot(datas(i,1),datas(i,2),'*b'); % 繪出第一類的樣本
        hold on
    elseif(B(1,i)==2)
        plot(datas(i,1),datas(i,2), '*r'); % 繪出第二類的樣本
        hold on
    elseif(B(1,i)==3)
        plot(datas(i,1),datas(i,2),'*g'); % 繪出第三類的樣本
        hold on
 else
        plot(datas(i,1),datas(i,2), '*m'); % 繪出第四類的樣本
        hold on
    end
end
xlabel('X-axis');
ylabel('Y-axis');
title('k-means 聚類結果');

for i = 1:k
    str=['第' num2str(i) '類包含樣本點(index)為:  ' num2str(C{i})];
    disp(str);
end









↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引