1. 程式人生 > >K-means聚類分析-互動式GUI演示(Matlab)

K-means聚類分析-互動式GUI演示(Matlab)

K-means聚類分析-互動式GUI演示(Matlab)


學習K-means的時候總是想,這應該是一個很酷的演算法,那麼酷的演算法,就應該有比較酷的demo來演示它,於是我寫了這個程式就是為了能裝逼,哦不,可以更直觀的和K-means演算法進行互動。原創程式,希望大家可以喜歡,多多指教。

目錄


原理簡介


  • K-means是無監督學習的聚類演算法,是機器學習中最重要的演算法之一,他的目的是通過機器學習自動探索一批未知的資料,並將其分類。
  • 具體的演算法流程如下:
圖1 K-means演算法流程圖

實現效果和功能展示


  1. 參考圖2,使用 Matlab 編寫 GUI 介面程式,完成對 K-means
    演算法基本原理的演示,程式介面如圖 2所示。點選“樣本點數”的 edit 框,可以修改初始樣本點個數,“中心點”下拉框可以選擇投放中心點的方式(如圖3所示,若選擇“自定義”,則可以使用游標在 axes 上自定義選擇任意位置任意個數中心點如,若選擇“隨機2點”,則隨機產生2箇中心點,“隨機3點”,“隨機4點”同理。),“迭代”進度條,可以拉動以實現迭代,右邊的文字框可以實時顯示迭代的次數,迭代過程中中心點的運動軌跡將被繪出(如圖5所示為隨機3箇中心點進行迭代分類,不同類用不同的顏色區分)。需要注意的是,該程式可以迴圈演示,無需退出,重新選擇中心點後將會重新初始化整個過程。
圖3 “中心點”下拉框選擇落點方式
圖2 程式介面
圖4 自定義落點
圖5 隨機3箇中心點進行迭代
圖6 自定義7箇中心點進行分類
圖7 自定義4點,500個樣本

一點體會


  • 內嵌式 Matlab 程式設計的時候,主函式裡面的變數都會被視為global,為所有子函式可見。在宣告回撥函式的時候,’callback’,{ @classify,gca},的意思是宣告該控制元件的回撥函式為classify,同時傳入引數gca,需要注意的是,在定義classify函式的時候,預設是有兩個控制元件引數hObj(當前控制元件控制代碼),和event(當前控制元件事件結構體)的,傳入的引數必須放在這兩個引數後面,才能實現正常的引數傳遞,如function classify(hObj,event,ax),傳入的引數gca會賦值給臨時變數ax。

  • 產生多維正態隨機變數的時候使用mvnrnd(Multivariate normal random numbers),語法為R = mvnrnd(MU,SIGMA),其中SIGMA為協方差矩陣而不是方差,應該注意的是多維隨機變數(隨機向量)要表示其內部隨機變數的關係,使用的是協方差,而不是方差,當然,內部隨機變數的方差位於這個協方差矩陣的對角。

  • 若要在axes中繪點,需要加上點標誌,如‘*’或者’.’,才會在plot中繪點,不然其預設為繪製線。

程式碼


KmeansMain.m

function KmeansMain
    close all;clear;clc;
    %隨機生成隨機數
    mu = [0 0];
    %協方差矩陣,對角為方差值0.3,0.35
    var = [0.3 0; 0 0.35];
    samNum = 200;
    data = mvnrnd(mu, var, samNum);
    a = figure;
    plot(gca, data(:,1), data(:,2), '*', 'color', 'k');hold on;
    classNum = [];%類數
    iterNum = 0;%迭代次數
    x = [];
    centerPoint = [];
    centerPointPathAarry = [];
    h_plotCenterPoint = [];%中心點繪製handle
    h_plotPath = [];%中心點路徑繪製handle
    %centerPointPathAarry結構
    %第1次迭代|中心點1(x,y)|中心點2(x,y)|中心點3(x,y)|中心點n(x,y)
    %第2次迭代|中心點1(x,y)|中心點2(x,y)|中心點3(x,y)|中心點n(x,y)
    h_slider = uicontrol(a,'Style', 'slider',...
             'SliderStep',[0.02 0.02],...
            'Min',0,'Max',50,'Value',0,...
            'Position', [400 20 100 20],...
            'Callback', {@classify,gca});   
    h_edit = uicontrol(a,'Style', 'edit',...  
           'String', '200',...
            'Position', [80 20 40 20],...
            'Callback', {@paintRandomPoint,gca});   
    uicontrol('Style', 'popup',...
               'String', '自定義|隨機2點|隨機3點|隨機4點',...
               'Position', [200 22 120 20],...
               'Callback', {@SpsfPoit,gca});   
    h_t1 = uicontrol('Style','text','String','迭代', ...
                       'Position', [355 20 40 20]);
    h_textClassNum = uicontrol('Style','text','String','中心點', ...
                       'Position', [140 20 55 20]);
     uicontrol('Style','text','String','樣本點數:', ...
                       'Position', [25 20 50 20]);
    h_textshow = uicontrol('Style','text','String','0','Position', [500 20 20 20]);                         
    set(gca,'xtick',[],'ytick',[],...
        'title',text('string','Kmeans演示指令碼','color','k'));
    xlim([-1.5 1.5]);ylim([-1.5 1.5]);
    %%%%%%%%%%%%%%%%%%%%
    function SpsfPoit(hObj,event,ax)
    set(h_slider,'value',0);    %清零滑動條,以實現從0迭代
    cla;%清空axes
    set(h_textshow,'string',0);%介面顯示的迭代次數清零
    %控制代碼賦值為空
    h_plotCenterPoint = [];
    h_plotPath = [];
    centerPointPathAarry = [];%軌跡歸零
    plot(gca, data(:,1), data(:,2), '*', 'color', 'k');%樣本點顏色初始化
    val = get(hObj, 'Value');%獲得popup menu的值
    if val == 1   
        %選擇任意若干點作為中心點
         [x, y] = ginput;
         centerPoint = [x y];
        [classNum, ~] = size(centerPoint);
       repaintBeginPoint(h_plotCenterPoint, classNum, centerPoint);
    elseif val == 2
        %選擇任意2點作為中心點
       centerPoint = rand(2, 2)*2-0.5;
       [classNum, ~] = size(centerPoint);
       repaintBeginPoint(h_plotCenterPoint, classNum, centerPoint);
    elseif val == 3
       %選擇任意3點作為中心點
       centerPoint = rand(3, 2)*2-0.5;
        [classNum, ~] = size(centerPoint); 
        repaintBeginPoint(h_plotCenterPoint, classNum, centerPoint);
    elseif val == 4
        %選擇任意4點作為中心點
        centerPoint = rand(4,2)*2-0.5;
        [classNum,~] = size(centerPoint);
        repaintBeginPoint(h_plotCenterPoint, classNum, centerPoint);
    end
      [labelSample] = classifyAndShowAndLabel(classNum, centerPoint, data, samNum, gca);
      centerPointPathAarry = [centerPointPathAarry; reshape(centerPoint', 1, classNum*2)];
      set(h_textClassNum, 'string', [num2str(classNum) '箇中心點']);
    end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%迭代分類函式%%%%%%%%%%%%%%%%%%%%
    function classify(hObj,event,ax)
        iterNum = round(get(hObj, 'value')); 
         set(h_textshow, 'string', iterNum);
           %根據起始點分類,並且為不同的類標記不同顏色,返回帶標籤樣本資料
           [labelSample] = classifyAndShowAndLabel(classNum, centerPoint, data, samNum, gca);
          %重新獲得起始點矩陣centerPoint(x|y)
          [centerPoint] = recalClassCenter(labelSample, classNum);
         centerPointPathAarry = [centerPointPathAarry; reshape(centerPoint', 1, classNum*2)];
         %重新繪製起始點centerPoint(x|y)到axes上
         repaintBeginPoint(h_plotCenterPoint, classNum, centerPoint);
        disp('path:');
        disp(centerPointPathAarry);%將中心點的軌跡顯示出來
        for i = 1:classNum
             [selected_color] = colorMap(i, classNum);
            h_plotPath(i)=plot(centerPointPathAarry(:, (i*2)-1), centerPointPathAarry(:,i*2), 'color',  selected_color);
        end
    end

    %%%%%%%%%%%%%%%函式部分%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%重新繪製起始點函式%%%%%%%%%
        function repaintBeginPoint(handle_plo,classnum,R)
           delete(h_plotCenterPoint);%清除繪製的中心點,並將控制代碼賦值為空
            h_plotCenterPoint=[];
            %重新繪製起始點,每個起始點的顏色不同
           for i = 1:classnum
            [selected_color] = colorMap(i, classnum);
            h_plotCenterPoint(i) = plot(R(i,1), R(i,2), 'o', 'MarkerSize', 7, 'MarkerFaceColor', selected_color); 
        end
        end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%重新計算類重心%%%%%%%%%%%%%%%%%%%%%%
        function [newCenterPoint]=recalClassCenter(labelSample,classNum)
            %R為重新被計算的類中心
            newCenterPoint=[];
           %分類並且計算每個類的重心
          for i=1:classNum      
               %取出所有標籤為i類的所有行,即第i類的所有點
               classs=labelSample(labelSample(:,3)==i,:);
                 %有用的只有第一列和第二列,去除標籤列
               classs=[classs(:,1),classs(:,2)];
              %重新計算重心
               classs_repoint=mean(classs);
               newCenterPoint=[newCenterPoint;classs_repoint];
          end
        end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%根據起始點分類,並且為不同的類標記不同顏色,返回帶標籤樣本資料%%%%%%%%%
       function [labelSample]=classifyAndShowAndLabel(classNum,centerPoint,data1,samNum,gca)  
         disArray=[];
        for i=1:classNum
            calproA=[centerPoint(i,:);data1(:,1),data1(:,2)];
            Adist=pdist(calproA,'euclidean');
            Adist=Adist(1:samNum)';
            disArray=[disArray,Adist];
        end
      %拼接,得到距離矩陣,一列代表一個點到所有樣本點的距離
      %disArray=[Adist Bdist];
      %disp(disArray);
      %獲取每一行最小值所在距離矩陣的列
      %並和原樣本矩陣拼接為labelSample
      %labelSample 表示被標記的原始樣本,每一行為一個樣本
      %每一行的最後一列為標記值,在這裡標記是距離哪個樣本點最近。
       minn=min(disArray');
       cols=[];
      for i=1:length(minn)
          [row,col] = find(disArray==minn(i));
          cols(i)=col;
      end
      cols=cols';
       labelSample=[data1(:,1),data1(:,2),cols];
       %將不同類的點標上不同的顏色
       for i=1:samNum
            [selected_color]=colorMap(labelSample(i,3),classNum);
           plot(gca,data1(i,1),data1(i,2),'*','color',selected_color);
       end
       end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%樣本點選擇並繪製函式%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function paintRandomPoint(hObj,event,ax)
            textt = get(hObj, 'string');
            samNum = str2num(textt);
            data = mvnrnd(mu, var, samNum);
            cla;
            set(h_slider,'value',0);    %清零滑動條,以實現從0迭代
            plot(ax,data(:,1), data(:,2), '*', 'color', 'k');hold on;
        end
end

colorMap.m

function [selected_color] = colorMap(num, max_color_value)
    %顏色對映函式,輸入一個數值範圍0-max_clor_value,將
    %顏色空間對映到0-max_clor_value的數值中去,輸入一個
    % 這個範圍裡面的數num,可以返回一個顏色值selected_color   
    % jet_color = colormap(hsv(max_color_value));
    % jet_color = colormap(cool(max_color_value));
    % jet_color = colormap(hot(max_color_value));
    % jet_color = colormap(pink(max_color_value));
    % jet_color = colormap(gray(max_color_value));
    % jet_color = colormap(pink(max_color_value));
    % jet_color = colormap(bone(max_color_value));
    jet_color = colormap(jet(max_color_value));
    % jet_color = colormap(copper(max_color_value));
    % jet_color = colormap(prim(max_color_value));
    % jet_color = colormap(flag(max_color_value));

    selected_color = jet_color(num,:);

可以複製上面的程式碼,或者可以到以下連結下載:https://download.csdn.net/download/qq_33826564/10574319
GitHub: https://github.com/swq123459/Kmeans-GUI