關於譜聚類的ng算法的實現


      廣義上講,任何在學習過程中應用到矩陣特征值分解的方法均叫做譜學習方法,比如主成分分析(PCA),線性判別成分分析(LDA),流形學習中的譜嵌入方法,譜聚類等等。

     由於科苑向世明老師課件上面關於ng的譜聚類算法里面與ng大神的論文中寫到的算法中有所出入,導致昨天晚上調了一晚上的算法並沒有調出滿意的結果,今天在網上找到了ng大神的原始paper閱讀一遍,雖然還是有很多不理解的地方,還是有了自己的見解。下面是ng算法的流程。

      

      算法第一步先通過高斯函數計算出每個點與其他點的親和度,與自己的親和度為0,對於每一個點保留前k_nearest個最近鄰的點的親和度(即k_nearest個親和度最大的點),其他的親和度置為0,這里的k作為算法的參數傳入

     算法第二步先計算度矩陣D,D矩陣為一個對角矩陣,將親和度矩陣的每一行元素求和放在度矩陣的相應對角元素上面即可,算出對角矩陣后再構造歸一化的拉普拉斯矩陣,由於D是對角矩陣,所以D^(-0.5)可以直接理解為對角元素的倒數開根號,然后得到拉普拉斯矩陣

     算法第三步求解拉普拉斯矩陣的特征值和特征向量,並按照特征值大小選取對應最大的K個特征值對應的特征向量(這里的K為將數據點聚成的類的個數),每個特征向量按列排列組成K維空間的n個向量即X屬於Rn*k

    算法第四步將得到的X矩陣按行歸一化長度得到矩陣Yn*k

    算法第五步將得到的歸一化長度的矩陣數據送入k_means算法進行聚類,得到的類別標簽即為原始數據點的類別標簽

 

    個人對算法的理解是將原始數據點映射到K維數據空間便於更好的將數據分類出來。

   

    下面是算法實現的matlab代碼,python代碼等有時間寫完再貼上來。

function [after_class_data,class_label,acc] = spectral_clustering(dataSet,class_num,sigma,k_near)
Affinity_mat = creat_D(dataSet,k_near,sigma);     %創建親和度矩陣
laplas = get_norm_laplas(Affinity_mat);           %得到歸一化的拉普拉斯矩陣
[eig_val,eig_vec] = get_special_vector(laplas,class_num);    %得到拉普拉斯矩陣的前K大的特征值對應的特征向量
[label,after_center] = k_means(eig_vec,class_num);           %將特征向量送入k_means進行聚類
after_class_data = after_center;
class_label = label;
%{
n11 = size(find(label(1:100)==1),2);   
n12 = size(find(label(1:100)==2),2);
n21 = size(find(label(1:100)==1),2);
n22 = size(find(label(1:100)==2),2);
n_1 = max(n11,n12);
n_2 = max(n21,n22);
acc = (n_1 + n_2)/size(label,2);       %計算分類正確率
%}


%計算親和度矩陣,用K近鄰求取,其余的賦值為0,每個點與本身的親和度為0
function Affinity_mat = creat_D(dataSet,k,sigma)
[row,col] = size(dataSet);
dis_mat = zeros(row,row);
index_all = zeros(row,row);
%index_all = [];
for i = 1:row
    for j=1:row
        if i ~= j
            dis_mat(i,j) = exp((-sum((dataSet(i,:)-dataSet(j,:)).^2))/(2*sigma.^2));
        end
    end
end

Affinity = dis_mat;

for t =1:row
    [sort_dis,index] = sort(Affinity(t,:),'descend');
    index_all(t,:) = index;
end

for ii = 1:row
    temp_index = index_all(ii,:);
    temp_clear = temp_index(k+1:row);
    %temp_one = temp_index(1:k);
    Affinity(ii,temp_clear) = 0;
    %Affinity(ii,temp_one) = 1;
    Affinity(ii,ii) = 0;
end
Affinity_mat = Affinity;
        
function laplas = get_norm_laplas(Affinity_mat)
row = size(Affinity_mat,1);
du = zeros(row,row);
%col_sum = sum(Affinity_mat);
for i=1:row
    du(i,i) = sum(Affinity_mat(i,:));   %求度矩陣
end
dn = du^(-0.5);
laplas = dn*Affinity_mat*dn;        %歸一化的laplas

%求k個最小的特征值和對應的特征向量
function [eig_val,special_vector] = get_special_vector(laplas,k)
eig_con = eig(laplas);
[vector,x] = eig(laplas);
[sort_vec,index] = sort(eig_con,'descend');
eig_val = eig_con(index(1:k));
temp_vector = vector(:,index(1:k));
[row,col] = size(temp_vector);
y = zeros(row,col);
for i=1:row
    s = (sum(temp_vector(i,:).^2)).^(0.5);    %特征向量的歸一化
    for j=1:col
        y(i,j) = temp_vector(i,j)/s;
    end
end
special_vector = y;

 k_means聚類算法代碼如下:

function [class_type,after_center] = k_means(dataSet,class_number)
[data_row,data_col] = size(dataSet);
%last_label = zeros(1,data_row);
label = ones(1,data_row);
ini_center = randn(class_number,data_col)
%ini_center = [1.1437 0.9726;-0.5316 -0.5223];
new_center = zeros(class_number,data_col);

while (sum(sum(abs(ini_center - new_center) > 1e-5)) > 0)
    new_center = ini_center;
    for i=1:data_row
        min_dis = Inf;
        belong_class = 1;             %用該變量來存儲屬於的類別,如為1,則歸屬為第一類。。。
        for j=1:class_number
            cur_dis = sum((ini_center(j,:)-dataSet(i,:)).^2);
            if cur_dis < min_dis
                min_dis = cur_dis;
                belong_class = j;
            end
        end
        label(i) = belong_class;
    end        %找到每一個類的歸屬標簽
    %重新計算歸屬類的中心點
    for k=1:class_number
        class_index = find(label==k);
        n = size(class_index,2);
        %{
        sum_x = sum(dataSet(class_index,1));   %算出類別k中的x的和
        sum_y = sum(dataSet(class_index,2));
        ini_center(k,1) = sum_x/n;
        ini_center(k,2) = sum_y/n;   %更新歸屬類的中心
        %}
        ini_center(k,:) = sum(dataSet(class_index,:))./n;
    end
end

class_type = label;
after_center = new_center;

 用月牙型數據進行測試分類情況,設定sigma參數為35,當取k_nearest=10,20,30時,對應的分類圖如下

    k_nearest = 10

k_nearest = 20

k_nearest = 30

隨着最近鄰參數k_nearest的增大,分類的正確逐漸下降。由於此k_means算法的初始點使用randn生成,而初始點的選取對結果是有影響的,故在跑程序途中會出現結果不理想出現Nan的情況,這些比較正常,多試幾次就能得到比較穩定的分類結果了。以后會逐漸的用python來實現算法,后續會上傳python代碼。

 

感慨:特征值分解,特征向量映射原始點到K維空間,大神們是怎么想到了呢,太神奇了,個人的數學思維和素養需要進一步提升,雖然算法里面的一些數學原理上的東西還不是很理解,但是我會加油的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM