在数据分析和机器学习中,聚类是一种常用的无监督学习方法,它可以将数据点按照某种相似度标准进行分组,从而发现数据中的结构和模式。聚类算法有很多种,其中一种比较经典的是AGNES算法,它是一种基于层次的聚类算法,它的全称是Agglomerative Nesting,即凝聚式嵌套。在这篇博客中,我将介绍AGNES算法的原理和意义,并给出一个用Matlab实现的代码示例。
目录
2.Matlab实现AGNES算法,对照每行代码标注详细注解
一、什么是AGNES算法
AGNES算法的原理是将数据集中的对象看作是一个图(graph)中的节点(node),然后根据节点之间的距离或者相似度来构建一个邻接矩阵(adjacency matrix)。邻接矩阵表示了图中节点之间是否相连以及相连程度。然后通过使用不同的连接方式(linkage)来计算不同簇之间的距离或者相似度,并且将最近或者最相似的两个簇合并成一个新的簇。这个过程重复进行,直到所有的节点都合并成一个大的簇为止。最后,根据需要划分的簇的个数,从合并过程中产生的树形结构(dendrogram)中切断一条水平线,得到最终的簇划分。
二、AGNES算法的意义
1.AGNES算法的优点
- 不需要预先指定簇的个数,可以自动发现合适的簇的个数。
- 可以生成一个树形结构,表示数据点之间的层次关系,方便进行可视化和分析。
- 可以使用不同的距离度量和链接方法,适应不同的数据特征和需求。
例如,如果我们想要对一些文本数据进行聚类,我们可以使用余弦相似度作为距离度量,并使用平均链接作为链接方法。
2.AGNES算法的应用场景
- 数据挖掘:可以用于对大量数据进行分组和分类,发现数据中的潜在模式和规律。
- 信息检索:可以用于对文档或网页进行聚类,提高检索效率和质量。
- 生物信息学:可以用于对基因或蛋白质进行聚类,揭示生物系统的结构和功能。
- 图像处理:可以用于对图像进行分割或压缩,提高图像质量和效果。
三、如何实现AGNES算法
1.AGNES算法大致思路
(1)构建邻接矩阵:给定一个数据集X,其中包含n个对象,每个对象有p个属性。首先计算每两个对象之间的距离或者相似度,然后根据一定的规则来构建一个n乘n的邻接矩阵A。常用的规则有以下几种:
- K近邻法(KNN):对于每个对象,只与其最近的K个对象相连,即A[i,j]=1当且仅当对象i和j是彼此最近的K个对象之一,否则A[i,j]=0。
- ε-邻域法(ε-neighborhood):对于每个对象,只与其距离小于ε的对象相连,即A[i,j]=1当且仅当对象i和j之间的距离小于ε,否则A[i,j]=0。
- 全连接法(fully connected):对于每个对象,与所有其他对象相连,即A[i,j]等于对象i和j之间的距离或者相似度。
(2)选择连接方式:根据不同的连接方式来计算不同簇之间的距离或者相似度。常用的连接方式有以下几种:
- 最小距离法(single linkage):使用两个簇中最近的两个对象之间的距离作为簇之间的距离。
- 最大距离法(complete linkage):使用两个簇中最远的两个对象之间的距离作为簇之间的距离。
- 平均距离法(average linkage):使用两个簇中所有对象之间的平均距离作为簇之间的距离。
- 中心距离法(centroid linkage):使用两个簇中各自计算出来的中心点之间的距离作为簇之间的距离。
(3)合并最近或者最相似的两个簇:根据所选用的连接方式,找出邻接矩阵中最小或者最大的元素,并且将对应的两个簇合并成一个新的簇。同时更新邻接矩阵,删除被合并掉的两个簇的行和列,增加新的簇的行和列,计算新的簇与其他簇之间的距离或者相似度。
(4)重复上一步,直到所有的节点都合并成一个大的簇为止:每次合并后,邻接矩阵的维度减一,直到只剩下一个元素为止。在合并的过程中,记录下每次合并的两个簇的编号和距离或者相似度,以及新产生的簇的编号。这些信息可以用来绘制树形结构(dendrogram)。
(5)根据需要划分的簇的个数,从树形结构中切断一条水平线,得到最终的簇划分:根据树形结构中不同高度的水平线,可以得到不同个数的簇划分。一般来说,水平线越高,划分出来的簇越少,水平线越低,划分出来的簇越多。可以根据实际问题或者数据特点来选择合适的水平线位置。
2.Matlab实现AGNES算法,对照每行代码标注详细注解
% 生成一个包含两个月牙形簇的数据集,共有300个样本,每个样本有两个属性
X = [moon(150,10,50,10);moon(150,10,-50,-10)]; % 使用自定义函数moon生成月牙形数据
y = [ones(150,1);2*ones(150,1)]; % 生成真实的簇标签
scatter(X(:,1),X(:,2),[],y) % 绘制数据集的散点图,不同颜色表示真实的簇标签
title('True labels') % 添加标题
% 构建邻接矩阵,使用欧氏距离计算距离
n = size(X,1); % 获取样本数
A = zeros(n,n); % 初始化邻接矩阵
for i = 1:n % 遍历每一个样本
for j = i+1:n % 遍历其他样本
A(i,j) = norm(X(i,:)-X(j,:)); % 计算欧氏距离
A(j,i) = A(i,j); % 由于距离是对称的,所以复制到对角位置
end
end
% 选择连接方式,这里使用最小距离法(single linkage)
method = 'single';
% 初始化合并记录矩阵Z,它有n-1行和3列,每一行记录了一次合并操作
% 第一列和第二列是被合并掉的两个簇的编号,第三列是合并后产生的新簇的编号
Z = zeros(n-1,3);
% 初始化当前存在的簇编号向量C,它有n个元素,每个元素表示对应样本所属的簇编号
C = (1:n)';
% 初始化当前存在的簇数k,它等于样本数n
k = n;
% 合并最近或者最相似的两个簇,重复进行n-1次
for t = 1:n-1 % 遍历每一次合并操作
% 根据所选用的连接方式,找出邻接矩阵中最小或者最大的元素,并且获取对应的两个簇编号i和j
if strcmp(method,'single') % 如果是最小距离法
[min_val,min_idx] = min(A(:)); % 找出邻接矩阵中的最小值和其索引
[i,j] = ind2sub(size(A),min_idx); % 将索引转换为行列坐标,即两个簇编号
elseif strcmp(method,'complete') % 如果是最大距离法
[max_val,max_idx] = max(A(:)); % 找出邻接矩阵中的最大值和其索引
[i,j] = ind2sub(size(A),max_idx); % 将索引转换为行列坐标,即两个簇编号
elseif strcmp(method,'average') % 如果是平均距离法
A_mean = mean(A,2); % 计算邻接矩阵每一行的平均值
[min_val,min_idx] = min(A_mean); % 找出平均值中的最小值和其索引,即一个簇编号
i = min_idx; % 将索引赋值给i
A_row = A(i,:); % 获取邻接矩阵第i行
A_row(i) = inf; % 将第i行第i列的元素设为无穷大,避免干扰
[min_val,min_idx] = min(A_row); % 找出第i行中的最小值和其索引,即另一个簇编号
j = min_idx; % 将索引赋值给j
elseif strcmp(method,'centroid') % 如果是中心距离法
X_mean = mean(X,1); % 计算数据集每一列的平均值,即所有对象的中心点
X_dist = pdist2(X,X_mean); % 计算每个对象与中心点的距离
X_dist = X_dist ./ sum(X_dist); % 归一化距离,使其和为1
A_centroid = A .* X_dist; % 计算邻接矩阵与距离的加权乘积,即每个元素乘以对应对象与中心点的距离比例
A_centroid_mean = mean(A_centroid,2); % 计算加权邻接矩阵每一行的平均值
[min_val,min_idx] = min(A_centroid_mean); % 找出平均值中的最小值和其索引,即一个簇编号
i = min_idx; % 将索引赋值给i
A_centroid_row = A_centroid(i,:); % 获取加权邻接矩阵第i行
A_centroid_row(i) = inf; % 将第i行第i列的元素设为无穷大,避免干扰
[min_val,min_idx] = min(A_centroid_row); % 找出第i行中的最小值和其索引,即另一个簇编号
j = min_idx; % 将索引赋值给j
end
% 将对应的两个簇合并成一个新的簇,并且更新邻接矩阵,删除被合并掉的两个簇的行和列,增加新的簇的行和列,计算新的簇与其他簇之间的距离或者相似度
C(C==j) = i; % 将编号为j的簇的所有对象的簇编号改为i,即将簇j合并到簇i中
A(i,:) = min(A(i,:),A(j,:)); % 将邻接矩阵第i行和第j行取最小值,即使用最小距离法更新簇i与其他簇之间的距离
A(:,i) = A(i,:)'; % 将邻接矩阵第i列设为第i行的转置,即保持对称性
A(j,:) = []; % 删除邻接矩阵第j行
A(:,j) = []; % 删除邻接矩阵第j列
% 记录合并操作的信息,更新合并记录矩阵Z
Z(t,1) = i; % 记录被合并掉的第一个簇的编号
Z(t,2) = j; % 记录被合并掉的第二个簇的编号
Z(t,3) = k+1; % 记录合并后产生的新簇的编号,从n+1开始递增
k = k+1; % 更新当前存在的簇数,每次合并后加一
end
% 根据需要划分的簇的个数,从树形结构中切断一条水平线,得到最终的簇划分
n_clusters = 2; % 指定需要划分的簇的个数
T = clusterdata(X,'maxclust',n_clusters,'linkage',method); % 使用matlab自带的clusterdata函数进行层次聚类,指定最大簇数和连接方式,得到每个对象所属的簇编号
scatter(X(:,1),X(:,2),[],T) % 绘制聚类结果的散点图,不同颜色表示预测的簇标签
title('AGNES clustering') % 添加标题
% 计算AGNES聚类的调整兰德指数(Adjusted Rand Index),用于评估聚类效果,取值范围是[-1,1],越接近1表示越好
ari_agnes = adjusted_rand_score(y, T);
fprintf('ARI of AGNES: %.4f\n',ari_agnes);
补充:自定义函数moon
function data = moon(num, radius, offset, noise)
% MOON 生成一个月牙形状的数据簇
% data = moon(num, radius, offset, noise) 返回一个N乘2的矩阵,其中
% N是样本数,每一行表示一个点的x坐标和y坐标。这些点是从一个月牙形状的分布中生成的,
% 根据给定的半径、偏移量和噪声水平。
% 从0到pi均匀地生成点的角度
theta = pi * rand(num, 1);
% 从(radius - 0.5)到(radius + 0.5)均匀地生成点的半径
r = radius + 0.5 * (rand(num, 1) - 0.5);
% 计算点的x坐标和y坐标
x = r .* cos(theta);
y = r .* sin(theta);
% 给坐标加上一些随机噪声
x = x + noise * randn(num, 1);
y = y + noise * randn(num, 1);
% 根据偏移量平移点的位置
x = x + offset;
y = y - offset;
% 将坐标拼接成一个矩阵
data = [x y];
end
四、AGNES算法的总结和建议
AGNES算法是一种自底向上的层次聚类方法,它可以将数据集中的对象按照相似度逐步合并成一个大的簇。在使用AGNES算法时,我们需要注意以下几点:
- 选择合适的距离或者相似度度量,根据数据的特点和需求,构建一个反映数据结构的邻接矩阵。
- 选择合适的连接方式,根据不同的连接方式,计算不同簇之间的距离或者相似度,并且影响合并过程中产生的树形结构。
- 选择合适的水平线位置,根据实际问题或者数据特点,从树形结构中切断一条水平线,得到最终的簇划分。
以上就是AGNES算法的全部内容啦~有帮助点个小赞收藏起来,会持续更新其他算法的实现