各类聚类算法整理

各类聚类算法整理

  • 0. 先验的基础知识
  • 1. K-Means
  • 2. GMM
  • 3. EM算法
  • 4.Spectral Clustering
  • 5. Mean Shift
  • 6. DBSCAN

本篇将介绍整理各种聚类算法,包括k-means,GMM(Guassian Mixture Models, 高斯混合),EM(Expectation Maximization,期望最大法),Spectral Clustering(谱聚类),Mean Shift(均值偏移)和DBSCAN(Density-Based Spatial Clustering of Applications with Noise)

0. 先验的基础知识

文章: 聚类算法的先验基础知识

1. K-Means

K-means算法是一种常用的聚类算法,用于将数据集中的样本分成 K 个不同的类别或簇。

  • 原理介绍:

    • 初始化: 随机选择 K 个初始聚类中心。
    • 分配样本: 将每个样本分配到距离最近的聚类中心所在的簇。
    • 更新中心: 对每个簇,计算其所有样本的均值,将该均值作为新的聚类中心。
    • 重复步骤2和步骤3,直到收敛: 当聚类中心不再变化或变化很小时,算法收敛,得到最终的聚类结果。
  • 优缺点:

    • 优点:
      • 简单直观,易于理解和实现。
      • 可以处理大规模数据集。
      • 对处理球形簇形状的数据效果较好。
    • 缺点:
      • 需要预先指定聚类个数 K。
      • 对异常值敏感,可能导致聚类结果不佳。
      • 结果可能收敛到局部最优解,因此多次运行可以获得更好的结果。
    • 应用:
      • 数据分析:对数据集进行聚类分析,发现数据集中的内在结构。
      • 图像处理:对图像像素进行聚类,实现图像分割和压缩。
      • 推荐系统:对用户进行聚类,提供个性化推荐。
  • C++代码:

#include <iostream>
#include <vector>
#include <cmath>
#include <limits>

using namespace std;

// 计算两个点之间的欧氏距离
double distance(vector<double>& point1, vector<double>& point2) {
    double sum = 0.0;
    for (int i = 0; i < point1.size(); ++i) {
        sum += pow(point1[i] - point2[i], 2);
    }
    return sqrt(sum);
}

// 更新簇的中心
void updateCenters(vector<vector<double>>& points, vector<vector<double>>& centers, vector<int>& clusterAssignments) {
    vector<int> clusterCounts(centers.size(), 0);
    vector<vector<double>> newCenters(centers.size(), vector<double>(centers[0].size(), 0.0));

    for (int i = 0; i < points.size(); ++i) {
        int cluster = clusterAssignments[i];
        for (int j = 0; j < points[i].size(); ++j) {
            newCenters[cluster][j] += points[i][j];
        }
        clusterCounts[cluster]++;
    }

    for (int i = 0; i < newCenters.size(); ++i) {
        if (clusterCounts[i] > 0) {
            for (int j = 0; j < newCenters[i].size(); ++j) {
                newCenters[i][j] /= clusterCounts[i];
            }
        }
    }

    centers = newCenters;
}

// K-means算法
void kMeans(vector<vector<double>>& points, int k, double threshold) {
    vector<vector<double>> centers(k);
    vector<int> clusterAssignments(points.size(), -1);

    // 随机初始化聚类中心
    for (int i = 0; i < k; ++i) {
        int randIndex = rand() % points.size();
        centers[i] = points[randIndex];
    }

    bool converged = false;
    while (!converged) {
        converged = true;

        // 分配样本到最近的聚类中心
        for (int i = 0; i < points.size(); ++i) {
            double minDist = numeric_limits<double>::max();
            int minIndex = -1;
            for (int j = 0; j < centers.size(); ++j) {
                double dist = distance(points[i], centers[j]);
                if (dist < minDist) {
                    minDist = dist;
                    minIndex = j;
                }
            }
            if (clusterAssignments[i] != minIndex) {
                converged = false;
                clusterAssignments[i] = minIndex;
            }
        }

        // 更新聚类中心
        if (!converged) {
            vector<vector<double>> oldCenters = centers;
            updateCenters(points, centers, clusterAssignments);

            // 判断中心点变化是否小于阈值
            double maxCenterShift = 0.0;
            for (int i = 0; i < centers.size(); ++i) {
                double shift = distance(oldCenters[i], centers[i]);
                if (shift > maxCenterShift) {
                    maxCenterShift = shift;
                }
            }

            if (maxCenterShift < threshold) {
                converged = true; // 达到收敛条件
            }
        }
    }

    // 打印聚类结果
    for (int i = 0; i < points.size(); ++i) {
        cout << "Point " << i << " belongs to cluster " << clusterAssignments[i] << endl;
    }
}

int main() {
    // 示例数据
    vector<vector<double>> points = {{1, 2}, {5, 6}, {3, 4}, {8, 9}, {7, 8}};
    int k = 2; // 聚类个数
    double threshold = 0.001; // 中心点位置变化阈值

    // 调用 K-means 算法
    kMeans(points, k, threshold);

    return 0;
}
  • 技巧:
    因为在更新中心点之前需要得到所有点所属的中心点,然后才能计算更新后的中心点。这样效率比较低,可以每次迭代只选择其中的一部分点来完成中心点的更新,这种做法叫做mini batch k_means。

2. GMM

高斯混合模型(Gaussian Mixture Model,GMM)是一种常用的概率模型,用于对数据进行聚类或密度估计。原理是基于假设:数据是由多个高斯分布组成的混合物。每个高斯分布代表一个聚类簇,而混合模型则将多个高斯分布加权组合起来,以适应数据中的不同分布情况。简单来说就是集合P中包含的所有点是来自多个分布的,但是现在想用所有分布的加权平均来表达集合P中所有点的分布。但是因为单一的高斯分布不明确,且加权平均权重也不明确,所以通过这组数据得到 K K K个不同的高斯分布以及 K K K个权重就是GMM聚类算法的核心。最后的聚类结果就是得到每个点属于每个高斯分布的后验概率,就完成了聚类。

说人话:有一组2维点,表示为 X = [ x 1 , x 2 , . . . , x n ] , x i ∈ R 2 X=[\mathbf{x}_1, \mathbf{x}_2, ...,\mathbf{x}_n],\mathbf{x}_i \in R_2 X=[x1,x2,...,xn]xiR2 x i \mathbf{x}_i xi表示第 i i i个点的坐标。这 n n n个点来自于 K K K个不同的高斯分布(也就是说这 n n n个点可以分成 K K K组,每组点都是从同一个分布中采样得到),但是我们得到的信息只有这组点的坐标,并不知道这K个高斯分布参数是多少。另外,K个单一的高斯分布对每个点的概率贡献权重也不知道。所以GMM的算法核心就是通过这组点的坐标信息得到 π , μ , Σ , π ∈ R 2 × k , μ ∈ R 2 × k , k = 1 , 2 , 3 , . . . , K \mathbf{\pi},\mu,\Sigma,\pi\in R_{2\times k}, \mu\in R_{2\times k} ,k=1,2,3,...,K π,μ,Σ,πR2×k,μR2×k,k=1,2,3,...,K Σ = [ Σ 1 , Σ 2 , . . . , Σ k ] , Σ i = d i a g ( σ i 1 2 , σ i 2 2 ) ; \Sigma = [\Sigma_1,\Sigma_2,...,\Sigma_k], \Sigma_i = diag(\sigma_{i1}^2,\sigma_{i2}^2); Σ=[Σ1,Σ2,...,Σk],Σi=diag(σi12,σi22);这里高斯分布的参数之所以都是2维的,是因为我们的点坐标是2维的,这里代表不同维度有不同的高斯分布参数,比如说颜色属性和坐标属性。

单维度变量 x x x的高斯分布,描述为:
N ( x ∣ μ , σ 2 ) = 1 ( 2 π σ 2 ) 1 2 e x p { − 1 2 σ 2 ( x − μ ) 2 } \mathcal{N}(x|\mu,\sigma^2) = {1 \over (2\pi\sigma^2)^{1\over2}} exp \{{-1\over2\sigma^2}(x-\mu)^2\} N(xμ,σ2)=(2πσ2)211exp{2σ21(xμ)2}

多维度变量 x , x ∈ R D \mathbf{x} ,\mathbf{x} \in R_D x,xRD的高斯公式,描述为:
N ( x ∣ μ , Σ ) = 1 ( 2 π ) D / 2 1 ∣ Σ ∣ 1 / 2 exp ⁡ { − 1 2 ( x − μ ) T Σ − 1 ( x − μ ) } \mathcal{N}(\mathbf{x} \mid \boldsymbol{\mu}, \boldsymbol{\Sigma})=\frac{1}{(2 \pi)^{D / 2}} \frac{1}{|\boldsymbol{\Sigma}|^{1 / 2}} \exp \left\{-\frac{1}{2}(\mathbf{x}-\boldsymbol{\mu})^{\mathrm{T}} \boldsymbol{\Sigma}^{-1}(\mathbf{x}-\boldsymbol{\mu})\right\} N(xμ,Σ)=(2π)D/21Σ1/21exp{21(xμ)TΣ1(xμ)}

高斯混合分布,公式表达为:
p ( x ) = ∑ k = = 1 K π k N ( x ∣ μ k , Σ k ) p(x) = \sum_{k==1}^K\pi_kN(x|\mu_k,\Sigma_k) p(x)=k==1KπkN(xμk,Σk)
公式表示了变量 x x x在高混合分布中的概率值。

  • 算法步骤

    • 初始化:选择初始均值 μ \mu μ、协方差矩阵 Σ \Sigma Σ和混合系数 π , μ = [ μ 1 , μ 2 , . . . , μ k ] , Σ = [ Σ 1 , Σ 2 , . . . , Σ k ] , π = [ π 1 , π 2 , . . . , π k ] , k = 1 , 2 , . . . , K \pi,\mu=[\mu_1,\mu_2,...,\mu_k],\Sigma=[\Sigma_1,\Sigma_2,...,\Sigma_k],\pi = [\pi_1,\pi_2,...,\pi_k],k=1,2,...,K π,μ=[μ1,μ2,...,μk],Σ=[Σ1,Σ2,...,Σk],π=[π1,π2,...,πk],k=1,2,...,K
    • EM算法:迭代进行E步(Expectation)和M步(Maximization)。在E步中,计算每个数据点 x \mathbf{x} x属于每个高斯分布的后验概率 P ( z k ∣ x ) , z k P(z_k|\mathbf{x}),z_k P(zkx),zk表示第 k k k个高斯分布。在M步中,利用这些后验概率更新高斯分布的参数 μ \mu μ Σ \Sigma Σ π \pi π
    • 收敛:当参数变化小于设定阈值或达到最大迭代次数时停止迭代。
  • E-Step,计算每个数据点 x x x属于每个高斯分布的后验概率 P ( z k ∣ x ) P(z_k|\mathbf{x}) P(zkx)
    γ n k ≡ p ( z k ∣ x n ) = π k p ( x n ∣ z k ) ∑ j = 1 K π j p ( x n ∣ z j ) = π k N ( x n ∣ μ k , Σ k ) ∑ j = 1 K π j N ( x n ∣ μ j , Σ j ) \begin{aligned} \gamma_{nk} \equiv p\left(z_k \mid \mathbf{x}_n\right) & =\frac{\pi_kp\left(\mathbf{x}_n \mid z_{k}\right)}{\sum_{j=1}^{K} \pi_j p\left(\mathbf{x}_n \mid z_{j}\right)} \\ & =\frac{\pi_{k} \mathcal{N}\left(\mathbf{x}_{n} \mid \mu_{k}, \Sigma_{k}\right)}{\sum_{j=1}^{K} \pi_{j} \mathcal{N}\left(\mathbf{x}_{n} \mid \mu_{j}, \Sigma_{j}\right)} \end{aligned} γnkp(zkxn)=j=1Kπjp(xnzj)πkp(xnzk)=j=1KπjN(xnμj,Σj)πkN(xnμk,Σk)
    r n k r_{nk} rnk表示第n个点属于第k个高斯分布的概率。

  • M-Step,使用E步求出的后验概率更新高斯分布的参数 μ \mu μ Σ \Sigma Σ π \pi π
    首先,需要明确的一点是,更新是需要等式的,那么 r n k r_{nk} rnk与三者的联系是什么,就需要进一步的求解推导。这里借助极大似然估计的思想,拉格朗日算子法的手段得到三者的联系。
    核心思想是寻找使得观测到的数据在给定模型下出现的可能性最大的参数值。即,选择使似然度函数 L ( μ , Σ , π ∣ x ) L(\mu,\Sigma,\pi|x) L(μ,Σ,πx)最大化的参数值 μ , Σ , π \mu,\Sigma,\pi μ,Σ,π。公式表达为:
    π , μ , Σ = arg ⁡ max ⁡ π , μ , Σ ln ⁡ p ( X ∣ π , μ , Σ ) = arg ⁡ max ⁡ π , μ , Σ ∑ n = 1 N ln ⁡ { ∑ k = 1 K π k N ( x n ∣ μ k , Σ k ) } , s . t . = ∑ k = 1 K π k = 1 \pi, \mu, \boldsymbol{\Sigma}=\underset{\pi, \mu, \boldsymbol{\Sigma}}{\arg \max } \ln p(\mathbf{X} \mid \pi, \mu, \boldsymbol{\Sigma})=\underset{\pi, \mu, \boldsymbol{\Sigma}}{\arg \max } \sum_{n=1}^{N} \ln \left\{\sum_{k=1}^{K} \pi_{k} \mathcal{N}\left(\mathbf{x}_{n} \mid \mu_{k}, \boldsymbol{\Sigma}_{k}\right)\right\},s.t.=\sum_{k=1}^{K} \pi_{k}=1 π,μ,Σ=π,μ,Σargmaxlnp(Xπ,μ,Σ)=π,μ,Σargmaxn=1Nln{k=1KπkN(xnμk,Σk)},s.t.=k=1Kπk=1

    • μ k \mu_k μk求偏导得,
      0 = − ∑ n = = 1 N γ n k Σ k ( x n − μ k ) μ k = 1 N k ∑ n = 1 N γ n k x n 0 = -\sum_{n==1}^N\gamma_{nk}\Sigma_k(\mathbf{x}_n-\mu_k) \\ \mu_k = {1\over N_k}\sum_{n=1}^N\gamma_{nk}\mathbf{x}_n 0=n==1NγnkΣk(xnμk)μk=Nk1n=1Nγnkxn
      其中, N k N_k Nk是后验概率之和,物理意义是表示有多少点是属于第k个高斯分布的, μ k \mu_k μk物理意义是属于第k个高斯分布的所有点的加权平均,不是平均值,还有权重

    • Σ k \Sigma_k Σk求偏导得,
      Σ k = 1 N k ∑ n − 1 N γ ( z n k ) ( x n − μ k ) ( x n − μ k ) T \Sigma_{k}=\frac{1}{N_{k}} \sum_{n-1}^{N} \gamma\left(z_{n k}\right)\left(\mathbf{x}_{n}-\mu_{k}\right)\left(\mathbf{x}_{n}-\mu_{k}\right)^{T} Σk=Nk1n1Nγ(znk)(xnμk)(xnμk)T

    • 对拉格朗日参数 λ \lambda λ求偏导得,这里结合拉格朗日算子法,目标函数为:
      ln ⁡ p ( X ∣ π , μ , Σ ) + λ ( ∑ k = 1 K π k − 1 ) ln ⁡ p ( X ∣ π , μ , Σ ) = ∑ n = 1 N ln ⁡ { π k N ( x n ∣ μ k , Σ k ) } \ln p(\mathbf{X} \mid \pi, \mu, \Sigma)+\lambda\left(\sum_{k=1}^{K} \pi_{k}-1\right) \\ \ln p(\mathbf{X} \mid \pi, \mu, \Sigma)=\sum_{n=1}^{N} \ln \left\{\pi_{k} \mathcal{N}\left(\mathbf{x}_{\mathbf{n}} \mid \mu_{k}, \Sigma_{k}\right)\right\} lnp(Xπ,μ,Σ)+λ(k=1Kπk1)lnp(Xπ,μ,Σ)=n=1Nln{πkN(xnμk,Σk)}
      得,
      λ = − N \lambda = -N λ=N

    • π k \pi_k πk求偏导得,
      π k = N k N \pi_k = {N_k \over N} πk=NNk
      其中N为所有点的数量

  • 代码部分

#include <iostream>
#include <vector>
#include <Eigen/Dense> // 使用Eigen库进行矩阵运算

using namespace std;
using namespace Eigen;

// 定义GMM模型参数
struct GMMParameters {
    vector<double> weights; // 每个高斯分布的权重
    vector<VectorXd> means; // 每个高斯分布的均值向量
    vector<MatrixXd> covariances; // 每个高斯分布的协方差矩阵
};

// 计算高斯分布的概率密度
double gaussianPDF(VectorXd x, VectorXd mean, MatrixXd covariance) {
    int dim = x.size();
    double det = covariance.determinant();
    MatrixXd invCov = covariance.inverse();
    VectorXd diff = x - mean;
    double exponent = -0.5 * diff.transpose() * invCov * diff;
    double normalizer = pow(2 * M_PI, -dim / 2) * sqrt(det);
    return exp(exponent) / normalizer;
}

// 对每个数据点计算属于每个高斯分布的后验概率
MatrixXd computePosterior(vector<VectorXd>& data, GMMParameters& params) {
    int numData = data.size();
    int numComponents = params.weights.size();
    MatrixXd posterior(numData, numComponents);

    for (int i = 0; i < numData; ++i) {
        VectorXd x = data[i];
        for (int j = 0; j < numComponents; ++j) {
            double likelihood = gaussianPDF(x, params.means[j], params.covariances[j]);
            posterior(i, j) = params.weights[j] * likelihood;
        }
        posterior.row(i) /= posterior.row(i).sum(); // 归一化后验概率
    }

    return posterior;
}

// 更新GMM参数
void updateGMMParameters(vector<VectorXd>& data, MatrixXd& posterior, GMMParameters& params) {
    int numData = data.size();
    int numComponents = params.weights.size();
    VectorXd weights(numComponents);
    vector<VectorXd> means(numComponents);
    vector<MatrixXd> covariances(numComponents);

    // 更新每个高斯分布的权重、均值和协方差矩阵
    for (int j = 0; j < numComponents; ++j) {
        weights[j] = posterior.col(j).sum() / numData; // 更新权重

        VectorXd mean = VectorXd::Zero(data[0].size());
        for (int i = 0; i < numData; ++i) {
            mean += posterior(i, j) * data[i];
        }
        mean /= posterior.col(j).sum(); // 更新均值
        means[j] = mean;

        MatrixXd covariance = MatrixXd::Zero(data[0].size(), data[0].size());
        for (int i = 0; i < numData; ++i) {
            VectorXd diff = data[i] - mean;
            covariance += posterior(i, j) * (diff * diff.transpose());
        }
        covariance /= posterior.col(j).sum(); // 更新协方差矩阵
        covariances[j] = covariance;
    }

    // 更新参数
    params.weights = weights;
    params.means = means;
    params.covariances = covariances;
}

int main() {
    // 生成示例数据
    vector<VectorXd> data;
    data.push_back(VectorXd(2));
    data[0] << 1, 2;
    data.push_back(VectorXd(2));
    data[1] << 3, 4;

    // 初始化GMM参数
    GMMParameters params;
    params.weights = {0.5, 0.5};
    params.means.push_back(VectorXd(2));
    params.means[0] << 1, 2;
    params.means.push_back(VectorXd(2));
    params.means[1] << 3, 4;
    params.covariances.push_back(MatrixXd::Identity(2, 2));
    params.covariances.push_back(MatrixXd::Identity(2, 2));

    // EM算法迭代更新参数
    for (int iter = 0; iter < 10; ++iter) {
        MatrixXd posterior = computePosterior(data, params);
        updateGMMParameters(data, posterior, params);
    }

    // 输出更新后的GMM参数
    cout << "Updated GMM Parameters:" << endl;
    cout << "Weights: " << params.weights[0] << ", " << params.weights[1] << endl;
    cout << "Mean 1: " << params.means[0].transpose() << endl;
    cout << "Mean 2: " << params.means[1].transpose() << endl;
    cout << "Covariance 1: " << endl << params.covariances[0] << endl;
    cout << "Covariance 2: " << endl << params.covariances[1] << endl;

    return 0;
}

3. EM算法

K-means和GMM算法都属于EM算法,EM算法的通用推导比较麻烦,就不再推导了。
实在是写不动了,先贴图吧,以后有空了编辑

在这里插入图片描述
在这里插入图片描述

4.Spectral Clustering

Spectral Clustering(谱聚类)是一种基于图论和谱理论的聚类算法,其原理、应用场景以及优缺点如下:

  • 原理步骤

    • 构建相似度图: 首先根据数据集中样本点之间的相似度构建一个相似度图(通常是无向加权图),其中节点表示样本点,边的权重表示样本点之间的相似度。
    • 计算拉普拉斯矩阵: 根据相似度图计算拉普拉斯矩阵(Laplacian Matrix)。常用的有度数矩阵(Degree Matrix)和邻接矩阵(Adjacency Matrix)构建拉普拉斯矩阵。
    • 特征值分解: 对拉普拉斯矩阵进行特征值分解,得到特征值和对应的特征向量。
    • 选取特征向量: 根据特征值和特征向量,选取其中特征值较小的前 k 个特征向量(k 为聚类簇的数量)。
    • 聚类: 利用选取的特征向量进行聚类,常用的方法包括 K-means 等聚类算法。
  • 应用场景

    • 图像分割: 在图像处理中,谱聚类可以用于图像分割,将图像中相似的像素点聚类到同一个分割区域。
    • 社交网络分析: 在社交网络分析中,可以利用谱聚类对用户进行聚类,发现社区结构或用户群体。
    • 模式识别: 在模式识别领域,谱聚类可用于将相似模式或特征聚类到一起,用于数据降维或分类。
    • 推荐系统: 在推荐系统中,可以利用谱聚类对用户或物品进行聚类,实现个性化推荐。
  • 优缺点

    • 优点:
  1. 对非线性、非凸分布的数据具有较好的适应性。
  2. 能够发现不规则形状的聚类簇。
  3. 对噪声数据较为鲁棒。
  • 缺点:
  1. 对于大规模数据集计算复杂度较高。
  2. 需要事先确定聚类簇的数量。
  3. 对于高维稀疏数据表现不佳。
  4. 总体来说,谱聚类算法在处理非线性、非凸分布的数据,发现不规则形状的聚类簇方面具有一定优势,但在大规模数据和
    高维稀疏数据上可能存在一些挑战。因此,在选择聚类算法时需要根据具体应用场景和数据特点进行综合考虑。

谱聚类关注的是连接性,并不是工作在欧式空间上
在这里插入图片描述

5. Mean Shift

一种基于密度的聚类算法

  1. 初始化
    从数据集中随机选择一个数据点作为初始的圆心。
    设定初始的圆半径 r r r
  2. 移动圆
    将圆移动到窗口内的数据点的中心位置。
    计算窗口内所有数据点的平均位置,将圆心移动到该位置。
  3. 收敛判断
    检查圆心移动的距离是否小于设定的阈值 ϵ ϵ ϵ,如果是则认为已经收敛,停止迭代。
    否则,继续迭代执行步骤2。
  4. 移除重叠圆
    如果存在重叠的圆,则保留包含数据点数量最多的圆,移除其他重叠的圆。
    对于重叠的圆,可以比较它们所包含的数据点数量来决定保留哪一个圆。
  5. 确定聚类中心
    使用保留的圆的圆心作为聚类中心。
    可以根据圆的圆心与数据点的距离来确定数据点所属的聚类。
  6. 重复步骤 1-5
    继续选择新的圆心并执行移动、收敛判断、移除重叠圆、确定聚类中心的步骤,直到所有圆都收敛且没有重叠。
    这种方法结合了Mean Shift的密度估计和圆的移动过程,通过移除重叠圆和确定聚类中心的方式来处理重叠的情况,并最终确定聚类结果。这样的方法可以更好地处理重叠的情况,并在一定程度上提高聚类的准确性。

6. DBSCAN

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,它可以有效地处理具有任意形状和大小的聚类,并且能够识别和排除噪声数据点。

  • DBSCAN算法基于以下两个重要概念:

    • 核心点(Core Point): 在给定的半径 ϵ 内至少包含指定数量 MinPts 个数据点的点被认为是核心点。
    • 密度可达(Density Reachable): 如果点 P 在点 Q 的 ϵ 邻域内,并且点 Q 是核心点,则点 P 是密度可达于点 Q。
  • 详细步骤

    • 初始化: 设置参数 ϵ 和MinPts,以及数据集。
    • 核心点标记: 对于每个数据点,计算其 ϵ 邻域内的数据点数量,如果大于等于 MinPts,则将该点标记为核心点。
    • 密度可达性: 对于每个核心点,通过递归地检查其 ϵ 邻域内的点是否密度可达,将其加入到同一个簇中。
    • 噪声点处理: 将未被标记为核心点且不属于任何簇的点标记为噪声点。
    • 聚类结果: 最终,所有密度可达的点被划分为不同的簇,噪声点则被排除。
  • 优缺点

    • 优点:
      • 能够处理任意形状的聚类,对于密度不均匀的数据效果较好。
      • 能够识别和排除噪声点。
      • 不需要预先指定聚类数量。
    • 缺点:
      • 对参数 ϵ 和 MinPts 比较敏感,需要调优。
      • 在高维数据集上可能会失效,因为密度的定义在高维空间中变得模糊。
      • 对于密度差异较大的数据集,可能会产生较多的噪声点或者无法正确识别聚类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/523934.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高新技术企业发展的重要性

高新技术企业发展的重要性及其挑战 随着科技的不断进步&#xff0c;高新技术企业正逐渐成为推动经济发展的重要力量。这些企业以高科技含量、高附加值和高成长性为主要特征&#xff0c;对于提升国家整体科技水平、优化产业结构、促进就业等方面都具有重要意义。 高新技术企业…

hydra九头蛇

一、hydra简介 Hydra是一款非常强大的暴力破解工具&#xff0c;它是由著名的黑客组织THC开发的一款开源暴力破解工具。Hydra是一个验证性质的工具&#xff0c;主要目的是&#xff1a;展示安全研究人员从远程获取一个系统认证权限。 目前该工具支持以下协议的爆破&#xff1a; A…

头盔检测 | 基于Caffe-SSD目标检测算法实现的建筑工地头盔检测

项目应用场景 面向建筑工地头盔检测场景&#xff0c;使用深度学习 Caffe SSD 目标检测算法&#xff0c;基于 C 实现。 项目效果 项目细节 > 具体参见项目 README.md (1) 安装 Caffe SSD(2) 执行训练 sh examples/Hardhat/SSD300/train_SSD300.sh (3) 部署算法 项目获取 h…

远程过程调用(远程调用)

远程过程调用&#xff08;远程调用&#xff09; 1、什么是分布式计算 在计算机科学中&#xff0c;分布式计算&#xff08;英语&#xff1a;Distributed computing&#xff09;&#xff0c;又译为分散式运算。这个研究领域&#xff0c;主要研究分布式系统&#xff08;Distribu…

STL之string模拟实现

面试题&#xff1a;简易版string(深拷贝与浅拷贝的问题) 如果要实现简易版的string 无需涉及增容问题&#xff0c;成员变量可以不用存储容量和元素个数 构造函数 错误示范 class string {string(): _str(nullptr){}string(const char* str): _str(str){}char& operator[](s…

HBase详解(2)

HBase 结构 HRegion 概述 在HBase中&#xff0c;会从行键方向上对表来进行切分&#xff0c;切分出来的每一个结构称之为是一个HRegion 切分之后&#xff0c;每一个HRegion会交给某一个HRegionServer来进行管理。HRegionServer是HBase的从节点&#xff0c;每一个HRegionServ…

鸿蒙内核源码分析 (双向链表篇) | 谁是内核最重要结构体

双向链表是什么&#xff1f; 谁是鸿蒙内核最重要的结构体 &#xff1f; 一定是: LOS_DL_LIST(双向链表)&#xff0c; 它长这样。 typedef struct LOS_DL_LIST {struct LOS_DL_LIST *pstPrev; /**< Current nodes pointer to the previous node | 前驱节点(左手)*/struct L…

【开发环境搭建篇】安装Anaconda

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;介绍Python编程入门相关的内容&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、下载三、安装四、配置环境变量五、创建虚拟环境六、总结 一、前言 学习Python编程&#xff0c;…

什么是MQ ?为什么用MQ?

什么是MQ&#xff1f; MQ(message queue)&#xff08;消息队列&#xff09;&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO先入先出&#xff0c;只不过队列中存放的内容是message而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息…

C++分析程序各模块耗时-perf火焰图

C分析程序各模块耗时-perf火焰图 1. 简介2. 安装3. 测试示例4. 从火焰图可以获得的信息5. 生成火焰图常见问题 Reference: Perf Wiki【性能】perf 火焰图分析软件性能瓶颈【火焰图&#x1f525;】Linux C/C性能优化分析工具Perf使用教程 perf: Linux profiling with perform…

如何用Java后端处理JS.XHR请求

Touching searching engine destroies dream to utilize php in tomcat vector.The brave isn’t knocked down&#xff0c;turn its path to java back-end. Java Servlet Bible schematic of interaction between JS front-end and Java back-end Question 如何利用Java…

SKF的便携式分析系统简介

1.系统简介 SKF是知名的轴承供应商。它的行业知识文档非常丰富。这里摘录一下它的当前的振动分析系统。可以在构建自己的振动分析系统时参考。它的手机应用不知道是否与传感器绑定。国内下载不方便&#xff0c;我回头找找上传后把App链接留在这里。 SKF的振动分析系统&#x…

JAVA—抽象—定义抽象类Converter及其子类WeightConverter

同样&#xff0c;我们由这道题引出抽象类&#xff0c;抽象方法这个概念。 按下面要求定义类Converter及其子类WeightConverter 定义抽象类&#xff1a;Converter&#xff1a; 定义一个抽象类Converter&#xff0c;表示换算器&#xff0c;其定义的如下&#xff1a; 一个私有…

SV学习笔记(五)

文章目录 线程的使用程序和模块什么是线程线程的概念澄清 线程的控制fork并行线程语句块fork…joinfork…join_any等待所有衍生线程停止单个线程停止多个线程停止被多次调用的任务 线程的通信写在前面event事件通知的需求semaphore旗语mailbox信箱三种通信的比较和应用 参考资料…

免疫检查点信号转导和癌症免疫治疗(文献)

目录 基础 介绍 免疫检查点的表面调控&#xff08;细胞膜层面&#xff09; ​编辑 PD-1调节 PD-L1调节 CTLA-4 调节 检查点信号通路 关于靶点研究 展望 Immune checkpoint signaling and cancer immunotherapy - PubMed (nih.gov) 基础 【中英字幕】肿瘤免疫疗法之免…

Java开发测试(第一篇):Java测试框架JUnit5

目录 1.基本介绍 2.maven中安装JUnit5 3.使用 4.JUnit5命名规则 5.JUnit5常用注解 6.JUnit5断言 7.JUnit5多个类之间的继承关系 8.JUnit5参数化 &#xff08;1&#xff09;使用场景&#xff1a; &#xff08;2&#xff09;使用前需在pom.xml文件中导入依赖 &#xff…

蓝桥杯刷题-13-子矩阵-二维滑动窗口 ಥ_ಥ

给定一个 n m &#xff08;n 行 m 列&#xff09;的矩阵。 设一个矩阵的价值为其所有数中的最大值和最小值的乘积。求给定矩阵的所有大小为 a b &#xff08;a 行 b 列&#xff09;的子矩阵的价值的和。 答案可能很大&#xff0c;你只需要输出答案对 998244353 取模后的结果。…

电力行业智能升级:IEC104网关在电网中的作用

IEC104是国际电工委员会&#xff08;IEC&#xff09;制定的一套用于电力自动化的通信协议。通过IEC104规约可以实现实时监测电力系统的状态、采集各种数据、控制设备的运行和保护等功能&#xff0c;为电力系统的安全稳定运行提供了重要的支持。 钡铼技术IEC104网关可实现对IEC-…

Java零基础入门-综合案例(File类+递归)

一、概述 java零基础教学也讲了一阵子了&#xff0c;从jdk安装到第一个java程序再到如今的java File类&#xff0c;递归思想等&#xff0c;不知道你们对于此教学有没有啥建议&#xff0c;毕竟看着浏览量不是很可人&#xff0c;所以在开启此篇前&#xff0c;我想统计一下&#x…

MyBatis操作数据库(1)

前言 在应用分层的学习时, 我们了解到web应用程序一般分为三层,即Controller, Service, Dao. 之前的案例中, 请求流程如下: 浏览器发起请求, 先请求Controller, Controller接受到请求后,调用Service进行业务逻辑处理, Service再调用Dao, 但是Dao层的数据是Mock的, 真实的数据…