机器学习-一篇入门

【代码】层次聚类-自实现代码(ward方差法)

作者 : 老饼 发表日期 : 2022-06-26 03:10:02 更新日期 : 2024-11-17 12:25:53
本站原创文章,转载请说明来自《老饼讲解-机器学习》www.bbbdata.com



层次聚类算法是一个基本的聚类算法,它一般以ward方差作为类别距离

本文讲解层次聚类算法以及ward方差的思想、计算公式,并展示如何自实现一个层次聚类算法

通过本文可以了解什么是层次聚类算法、什么是ward方差,以及如何自实现ward方差层次聚类算法





    01. 什么是层次聚类ward算法    



本节讲解什么是层次聚类算法以及ward方差




     层次聚类算法与ward方差      


什么是层次聚类算法
层次聚类算法(Hierarchical)是一种最基本的聚类算法,用于对样本进行聚类
它先将每个样本作为一类,然后逐步合并,是一种自下而上的聚类算法,如下:
 什么是层次聚类法 
如图所示,从下往上,12个样本先各自作为一个类别,然后逐步合并为一个类别
层次聚类算法流程
 层次聚类算法的流程如下:
1. 将每个样本初始化为一类,有多少个样本就有多少个类别               
2. 计算类别两两之间的距离                                                              
3. 将距离最近的两个类别合并为一类                                                
4. 计算新类别与其它类别的距离                                                       
重复3,4,直到只剩1个类别
而当层次聚类算法使用ward方差作为距离时,则称为层次聚类ward算法





     Ward方差是什么      


Ward方差法以类别u、v合并时,类内方差SSE的增加量作为u、v的距离d
 即ward方差法的距离定义为:
 

 其中,类别K的类内方差SSE定义为:

   
其中,  :x的维度                      
 :类别K的样本个数
 :类别K的样本集合
 即类别K的样本到类别K中心的方差总和







     02. ward方差法的原理     




本节讲解以ward方差进行层次聚类时的算法流程以及相关计算公式




     ward方差法的距离计算公式     


ward方差法的初始距离
初始状态,每个样本独自作为一个类别,因此,此时所有类别的SSE都为0
 则类别u与类别v之间的距离为:
   
 作为简化,可以取为,即欧几里得距离的平方
ward方差法的距离更新
每次选择两个最小距离的类别进行合并后,需要计算新类别与其余类别的距离
    如果每次都按上述定义公式计算,计算量会较大,所以一般使用如下迭代公式:
 
                  其中,u由s、t两个类别合并而成                    
 
代表的样本个数       
 
          
   备注:笔者也未验证上述迭代公式的推导过程,有兴趣的可按定义进行推导一下





      层次聚类(ward连接)算法流程     


层次聚类算法使用ward连接时,算法流程如下:
 一、 初始化                                                                                      
      (1) 每个样本作为一个类别                                   
(2)计算每个类别的距离d                                 
 
 ,dist是欧几里得距离
 二、循环合并类别                                                                             
 循环合并类别,直到只剩一个类别:                                  
 (1) 将距离最近的两个类别s、t合并,得到新类别u        
 (2) 计算u与其它类别的距离                                         
        对于类别v,新类别u(由s,t合并而成)与v的距离为: 
                          
             其中,代表的样本个数,
        
(3)记录本次合并的两个类别,以及它们的距离到"合并过程表"Z 
 三、输出                                                                                         
输出合并过程表Z                                                               








    03. 层次聚类ward代码实现    




本节不借用软件包,展示如何自行实现层次聚类算法(ward方差法)




     层次聚类ward算法-代码自实现     


下面不借用软件包,自行实现层次聚类ward算法
 具体代码如下:
 
 备注:本代码主要扒自matlab的linkage函数,运行结果与matlab、scipy一致
x = [1 2 ; 3 4; 5 6 ;1 8;2 2];                                          % x
Y = pdist(x);                                                           % 计算距离,存放(1,2)(1,3),..,(1,n),(2,3),(2,4)..,(n-1,n)的距离
[k, n] = size(Y);                                                       % 距离向量的行与列
m = (1+sqrt(1+8*n))/2;                                                  % 当前类别个数
Z = zeros(m-1,3);                                                       % 初始化输出矩阵
N = zeros(1,2*m-1);                                                     % 类别样本个数
N(1:m) = 1;                                                             % 当前每个类别都是一个样本
n = m;                                                                  % 总类别个数
R = 1:n;                                                                % 当前类别的编号
Y = Y .* Y;                                                             % 对距离Y平方
for s = 1:(n-1)
    X = Y;                                                              % 将Y赋给X
    [v, k] = min(X);                                                    % 找出最小距离
    i = floor(m+1/2-sqrt(m^2-m+1/4-2*(k-1)));                           % 找出最小距离对应的i是当前第几个类别
    j = k - (i-1)*(m-i/2)+i;                                            % 找出最小距离对应的j是当前第几个类别
    Z(s,:) = [R(i) R(j) v];                                             % 记录本次合并类别,R中存放的是类别编号
    
    I1 = 1:(i-1); I2 = (i+1):(j-1); I3 = (j+1):m;                       % 临时变量
    U = [I1 I2 I3];                                                     % 剩余类别
    I = [I1.*(m-(I1+1)/2)-m+i i*(m-(i+1)/2)-m+I2 i*(m-(i+1)/2)-m+I3];   % 剩余类别与i的距离索引(不包括j)
    J = [I1.*(m-(I1+1)/2)-m+j I2.*(m-(I2+1)/2)-m+j j*(m-(j+1)/2)-m+I3]; % 剩余类别与j的距离索引 (不包括i)
    Y(I) = ((N(R(U))+N(R(i))).*Y(I) + (N(R(U))+N(R(j))).*Y(J) - ...
        N(R(U))*v)./(N(R(i))+N(R(j))+N(R(U)));                          % 更新距离 
    % ((剩余类别样本数+i的样本数)*剩余类别与i的距离
    % +(剩余类别样本数+j的样本数)*剩余类别与j的距离
    % -(剩余类别样本数)*ij距离)/(剩余类别样本数+i类别样本数+j类别样本数)
    J = [J i*(m-(i+1)/2)-m+j];                                          % 与j连接的类别的距离索引 (包括i)
    Y(J) = [];                                                          % 删除与j连接的类别的距离
    m = m-1;                                                            % 更新类别个数
    N(n+s) = N(R(i)) + N(R(j));                                         % 更新类别编号的样本个数
    R(i)   = n+s;                                                       % 将原存放i类别编号的位置改为新类别的编号 
    R(j:(n-1))=R((j+1):n);                                              % 移动编号,覆盖掉j
end
Z(:,3) = sqrt(Z(:,3))                                                   % 修改回欧几里得距离,就是最终聚类过程结果 
运行结果如下:
 
层次聚类的代码运行结果 
Z记录了合并过程,三列各代表:类别1,类别2,距离
 以第一行为例,即类别1与类别5的距离最小,距离为1,本次进行合并








  End  






联系老饼