matlab编程实现基于密度的聚类(DBSCAN)

1. DBSCAN聚类的基本原理

详细原理可以参考链接:
https://www.cnblogs.com/pinard/p/6208966.html
这是找到的相对很详细的介绍了,此链接基本仍是周志华《机器学习》中的内容,不过这个链接更通俗一点,且算法流程感觉比《机器学习》书中的伪代码形式算法流程更清晰。

2. matlab实现DBSCAN聚类

参考《机器学习》中的伪代码编写的算法,具体代码如下:

function [clusterIndex, dataType] = DBSCAN(data, r, minPts)
% 本函数用于基于密度的聚类(DBSCAN)
% 输入data为聚类数据,每行一个数据点
% 输入r为密度聚类的滑动窗口半径参数
% 输入minPts为滑动窗口内数据量阈值参数
% 输出clusterIndex为数据对应的类别号组成的序列
% 输出objType为数据样本类型,0表示噪声样本,1表示边缘对象,2表示核心对象

% 数据量大小
dataSize = length(data(:, 1));
% 预分配输出的内存
clusterIndex = zeros(dataSize, 1);
dataType = zeros(dataSize, 1);
% 获得距离方阵
distanceMatrix = squareform(pdist(data));

% 标记核心对象集合
% 这里直接用距离矩阵判断,相当于把自己到自己的距离0也算进去
for i = 1 : dataSize
    if sum(distanceMatrix(:, i) < r) >= minPts
        dataType(i) = 2;
    end
end
% 保存核心对象
coreObj = data(dataType == 2, :);

% 初始化聚类簇的标记
k = 0;
% 初始化未被访问的样本集合
noData = data;

% 进入迭代,如果核心对象集合非空,则进入迭代过程
while ~isempty(coreObj)
    % 记录当前未被访问样本集合,noData数据每次迭代会更新
    noDataOld = noData;
    % 随机选择一个核心对象作为初始迭代对象
    randCoreIndex = randperm(length(coreObj(:, 1)), 1);
    initCoreObj = coreObj(randCoreIndex, :);
    % 初始化队列,最初Q只有一个初始核心对象
    Q = initCoreObj;
    % 注:集合运算后默认是将集合排序过的,需要加stable参数来保持原来的顺序
    % 从未访问样本中删去随机选择的这个核心对象,只删除这一个,不删除它密度直达的对象
    noData = setdiff(noData, initCoreObj, 'stable', 'rows');
    
    % 进入循环,找到Q的所有密度可达对象,并放入队列Q中
    while ~isempty(Q)
        % 取出Q中的第一个元素
        q = Q(1, :);
        Q(1, :) = [];
        % 再次距离计算,找到点q邻域内的数据个数qSize
        qIndex = ismember(data, q, 'rows') == 1;
        qSize = sum(distanceMatrix(qIndex, :) <= r);
        % q邻域内的数据
        qAeraData = data(distanceMatrix(qIndex, :) <= r, :);
        % 如果数量超过阈值,则将邻域内的的未访问的数据赋给队列Q        
        if qSize >= minPts
            % 求交集D = qAereData∩noData
            D = intersect(qAeraData, noData, 'stable', 'rows');
            % 将D中的元素加入队列Q
            Q = union(Q, D, 'stable', 'rows');
            % 再将这些数据从未访问数据noData中删除掉
            noData = setdiff(noData, D, 'stable', 'rows'); 
        end
    end
    k = k + 1;
    % 生成聚类簇
    kCluster = setdiff(noDataOld, noData, 'stable', 'rows');
    coreObj = setdiff(coreObj, kCluster, 'stable', 'rows');
    % 记录类编号
    clusterIndex(ismember(data, kCluster, 'rows')) = k;
end

% 遍历得到边缘对象(非核心对象,但在类中)
for i = 1 : dataSize
    if (dataType(i) ~= 2) && (clusterIndex(i) ~= 0)
        dataType(i) = 1;
    end
end

% 函数结束
end

主函数调用:

clear all; close all;
model_class = 3;
dim = 2;
% 期望值
m = [0, 0;
    2, 2;
    -2, -2];
% 协方差阵
s(:, :, 1) = [0.1, 0;
              0, 0.1];
s(:, :, 2) = [0.5, 0.3;
              0.3, 0.5];
s(:, :, 3) = [1, -0.5;
              -0.5, 1];
          
num = [500, 500, 500];
data = generate_data_GMM(dim, model_class, m, s, num);
data = data(:, (1:2));

[T, N] = DBSCAN(data, 0.27, 9);

figure(1)
for i = 1 : length(T)
    if N(i) == 0
        plot(data(i, 1), data(i, 2), '*k');
        hold on;
    % 边缘对象
    elseif N(i) == 1
        if T(i) == 1
            plot(data(i, 1), data(i, 2), 'or');
            hold on;
        elseif T(i) == 2
            plot(data(i, 1), data(i, 2), 'og');
            hold on;
        elseif T(i) == 3
            plot(data(i, 1), data(i, 2), 'ob');
            hold on;
        else 
            plot(data(i, 1), data(i, 2), 'oy');
            hold on;
        end
    else % 核心对象
        if T(i) == 1
            plot(data(i, 1), data(i, 2), '.r');
            hold on;
        elseif T(i) == 2
            plot(data(i, 1), data(i, 2), '.g');
            hold on;
        elseif T(i) == 3
            plot(data(i, 1), data(i, 2), '.b');
            hold on;
        else 
            plot(data(i, 1), data(i, 2), '.y');
            hold on;
        end
    end
    grid on;
end

% watermelon4_0 = load('watermelon4.0.txt');
% data = watermelon4_0;

聚类结果如下:
matlab编程实现基于密度的聚类(DBSCAN)_第1张图片
注:主函数给了两个例子,一个是基于高斯分布数据,一个是基于《机器学习》书籍上的西瓜数据集4.0。这里高斯分布数据用到了笔者自编写的generate_data_GMM函数,这个函数详细说明及代码请查看:
https://blog.csdn.net/sangnanpo/article/details/104222339

3. 其他说明

DBSCAN的主要优点有:

  1. 不需要输入簇的数量
  2. 可以对任意形状的稠密数据集进行聚类,相对的,K-Means之类的聚类算法一般只适用于凸数据集。
  3. 可以在聚类的同时发现噪声点,对数据集中的噪声点不敏感。
  4. 聚类结果没有偏倚,相对的,K-Means之类的聚类算法初始值对聚类结果有很大影响。

DBSCAN的主要缺点有:

  1. 如果样本集的密度不均匀、聚类间距差相差很大时,聚类质量较差,这时用DBSCAN聚类一般不适合。
  2. 如果样本集较大时,聚类收敛时间较长,此时可以对搜索最近邻时建立的KD树或者球树进行规模限制来改进。
  3. 需要调参数,调参相对于传统的K-Means之类的聚类算法稍复杂,主要需要对距离阈值ϵ,邻域样本数阈值MinPts联合调参,不同的参数组合对最后的聚类效果有较大影响。这一点在具体实现时即可发现。

附:西瓜数据集4.0
0.697 0.460
0.774 0.376
0.634 0.264
0.608 0.318
0.556 0.215
0.403 0.237
0.481 0.149
0.437 0.211
0.666 0.091
0.243 0.267
0.245 0.057
0.343 0.099
0.639 0.161
0.657 0.198
0.360 0.370
0.593 0.042
0.719 0.103
0.359 0.188
0.339 0.241
0.282 0.257
0.748 0.232
0.714 0.346
0.483 0.312
0.478 0.437
0.525 0.369
0.751 0.489
0.532 0.472
0.473 0.376
0.725 0.445
0.446 0.459

你可能感兴趣的:(聚类,算法,机器学习)