【机器学习-无监督学习】聚类

news2024/11/27 8:03:30

在这里插入图片描述

【作者主页】Francek Chen
【专栏介绍】 ⌈ ⌈ Python机器学习 ⌋ ⌋ 机器学习是一门人工智能的分支学科,通过算法和模型让计算机从数据中学习,进行模型训练和优化,做出预测、分类和决策支持。Python成为机器学习的首选语言,依赖于强大的开源库如Scikit-learn、TensorFlow和PyTorch。本专栏介绍机器学习的相关算法以及基于Python的算法实现。
【GitCode】专栏资源保存在我的GitCode仓库:https://gitcode.com/Morse_Chen/Python_machine_learning。

文章目录

    • 一、k均值聚类的原理
    • 二、动手实现k均值算法
    • 三、k-means++算法
    • 四、Sklearn中的k-means算法
    • 五、拓展:Sklearn中的层次聚类和密度聚类
      • (一)层次聚类
      • (二)密度聚类之DBSCAN算法


  本文开始我们讲解无监督学习算法。在之前的文章中,我们给模型的任务通常是找到样本 x \boldsymbol x x与标签 y y y之间的对应关系。在训练时,模型对样本 x \boldsymbol x x给出预测 y ^ \hat y y^,再通过损失函数计算 y ^ \hat y y^ y y y之间的偏差,从而优化模型。在这一过程中,真实标签 y y y保证我们可以根据模型的预测 y ^ \hat y y^来调整模型,起到了“监督”模型输出的作用。因此,这样的学习过程称为监督学习(supervised learning)。还有一类任务中,我们只有样本 x \boldsymbol x x,却没有其标签 y y y。这类任务也不再是通过 x \boldsymbol x x去预测 y y y,而是通过样本的特征找到样本之间的关联。由于没有标签作为监督信号,这一过程被称为无监督学习(unsupervised learning)。监督学习和无监督学习在某些情况下可以互相转化。例如在卷积神经网络一文中,我们通过训练集中的图像与其类别得到模型,在测试集上完成了图像分类任务,这是有监督学习的过程。我们最后给图像分类的依据是由模型提取出的图像的特征。然而,即使我们一开始就不知道图像的真实类别,也可以通过图像特征之间的相似程度判断出来哪些图像属于同一类。也就是说,我们可以在仅有图像的情况下把猫和狗的图像分为两类,而类别无非是告诉我们这两类分别叫“猫”和“狗”而已。

  本文我们将要讲解的k均值(k-means)聚类算法就是一个无监督学习算法。它的目标是将数据集中的样本根据其特征分为几个类,使得每一类内部样本的特征都尽可能相近,这样的任务通常称为聚类任务。作为最简单的聚类算法,k均值算法在现实中有广泛的应用。下面,我们就来详细讲解k均值算法的原理,然后动手实现该算法,最后我们将介绍改进的k-means++算法。

一、k均值聚类的原理

  假设空间中有一些点,聚类问题的目标就是将这些点按距离分成数类。设数据集 D = { x 1 , ⋯   , x M } \mathcal D=\{\boldsymbol x_1,\cdots,\boldsymbol x_M\} D={x1,,xM},其中每个样本 x i ∈ R n \boldsymbol x_i\in \mathbb R^n xiRn 的特征维数都是 n n n。最终聚簇的个数 K K K由我们提前指定。直观上来说,同一类的点之间距离应该比不同类的点之间的距离远。但是,由于我们没有任何点的真实标签,所以也无法在最开始确定每一类的中心(centroid),以其为基准计算距离并分类。针对这一问题,k均值算法提出了一个非常简单的解决方案:在初始时,随机选取数据集中的 K K K个样本 μ 1 , ⋯   , μ K \boldsymbol\mu_1,\cdots,\boldsymbol\mu_K μ1,,μK,将 μ i \boldsymbol\mu_i μi作为第 i i i类的中心。选取中心后,我们用最简单的方式,把数据集中的点归到最近的中心点所代表的类中。记第 i i i类包含样本的集合为 C i \mathcal C_i Ci,两点之间的距离函数为 d d d,那么 C i \mathcal C_i Ci可以写为 C i = { x j ∈ D ∣ ∀ l ≠ i , d ( x j , μ i ) ≤ d ( x j , μ l ) } \mathcal C_i=\{\boldsymbol x_j\in\mathcal D|\forall l\ne i,d(\boldsymbol x_j,\boldsymbol\mu_i)\le d(\boldsymbol x_j,\boldsymbol\mu_l)\} Ci={xjD∣∀l=i,d(xj,μi)d(xj,μl)}

  当然,仅仅随机选取中心点还不够,我们还要继续进行优化,尽可能减小类内的点到中心点距离。将数据集中所有点到其对应中心距离之和作为损失函数,得到 L ( C 1 , ⋯   , C K ) = ∑ i = 1 K ∑ x ∈ C i d ( x , μ i ) = ∑ i = 1 K ∑ j = 1 M I ( x j ∈ C i ) d ( x j , μ i ) \mathcal L(\mathcal C_1,\cdots,\mathcal C_K)=\sum_{i=1}^K\sum_{\boldsymbol x\in\mathcal C_i}d(\boldsymbol x,\boldsymbol\mu_i)=\sum_{i=1}^K\sum_{j=1}^M\mathbb I(\boldsymbol x_j\in\mathcal C_i)d(\boldsymbol x_j,\boldsymbol\mu_i) L(C1,,CK)=i=1KxCid(x,μi)=i=1Kj=1MI(xjCi)d(xj,μi)

  既然在初始时,各个类的中心点 μ i \boldsymbol\mu_i μi是随机选取的,那么我们应当再选取新的中心点,使得损失函数的值最小。将上式对 μ i \boldsymbol\mu_i μi求偏导,得 ∂ L ∂ μ i = ∑ x ∈ C i ∂ d ( x , μ i ) ∂ μ i \frac{\partial\mathcal L}{\partial\boldsymbol\mu_i}=\sum_{\boldsymbol x\in\mathcal C_i}\frac{\partial d(\boldsymbol x,\boldsymbol\mu_i)}{\partial\boldsymbol\mu_i} μiL=xCiμid(x,μi)

  如果我们用欧氏距离的平方作为度量标准,即 d ( x , μ ) = ∥ x − μ ∥ 2 d(\boldsymbol x,\boldsymbol\mu)=\Vert\boldsymbol x-\boldsymbol\mu\Vert^2 d(x,μ)=xμ2,上式可以进一步计算为 ∂ L ∂ μ i = ∑ x ∈ C i ∂ ∥ x − μ i ∥ 2 ∂ μ i = 2 ∑ x ∈ C i ( x − μ i ) = 2 ∑ x ∈ C i x − 2 ∣ C i ∣ μ i \frac{\partial\mathcal L}{\partial\boldsymbol\mu_i}=\sum_{\boldsymbol x\in\mathcal C_i}\frac{\partial\Vert\boldsymbol x-\boldsymbol\mu_i\Vert^2}{\partial\boldsymbol\mu_i}=2\sum_{\boldsymbol x\in\mathcal C_i}(\boldsymbol x-\boldsymbol\mu_i)=2\sum_{\boldsymbol x\in\mathcal C_i}\boldsymbol x-2|\mathcal C_i|\boldsymbol\mu_i μiL=xCiμixμi2=2xCi(xμi)=2xCix2∣Ciμi

  令该偏导数为零,就得到最优的中心点为: μ i = 1 ∣ C i ∣ ∑ x ∈ C i x \boldsymbol\mu_i=\frac{1}{|\mathcal C_i|}\sum_{\boldsymbol x\in\mathcal C_i}\boldsymbol x μi=Ci1xCix

  上式表明,最优中心点就是 C i \mathcal C_i Ci中所有点的质心。但是,当中心点更新后,每个样本距离最近的中心点可能也会发生变化。因此,我们重新计算每个样本点到中心点的距离,对它们重新分类,再计算新的质心。如此反复迭代,直到各个点的分类几乎不再变化或者达到预设的迭代次数位置。需要注意,如果采用其他的距离函数作为度量,那么最优的中心点就不再是集合的质心。

二、动手实现k均值算法

  下面,我们用一个简单的平面点集kmeans_data.csv来展示k均值聚类算法的效果。首先,我们加载数据集并可视化。数据集中每行包含两个值 x 1 x_1 x1 x 2 x_2 x2,表示平面上坐标为 ( x 1 , x 2 ) (x_1,x_2) (x1,x2)的点。考虑到我们还希望绘制迭代的中间步骤,这里将绘图部分写成一个函数。

import numpy as np
import matplotlib.pyplot as plt

dataset = np.loadtxt('kmeans_data.csv', delimiter=',')
print('数据集大小:', len(dataset))

在这里插入图片描述

# 绘图函数
def show_cluster(dataset, cluster, centroids=None):  
    # dataset:数据
    # centroids:聚类中心点的坐标
    # cluster:每个样本所属聚类
    # 不同种类的颜色,用以区分划分的数据的类别
    colors = ['blue', 'red', 'green', 'purple']
    markers = ['o', '^', 's', 'd']
    # 画出所有样例
    K = len(np.unique(cluster))
    for i in range(K):
        plt.scatter(dataset[cluster == i, 0], dataset[cluster == i, 1], color=colors[i], marker=markers[i])

    # 画出中心点
    if centroids is not None:
        plt.scatter(centroids[:, 0], centroids[:, 1], 
            color=colors[:K], marker='+', s=150)  
        
    plt.show()

# 初始时不区分类别
show_cluster(dataset, np.zeros(len(dataset), dtype=int))

在这里插入图片描述

  对于简单的k均值算法,初始的中心点是从现有样本中随机选取的,我们将其实现如下。

def random_init(dataset, K):
    # 随机选取是不重复的
    idx = np.random.choice(np.arange(len(dataset)), size=K, replace=False)
    return dataset[idx]

  接下来,我们用欧氏距离作为标准,实现上面描述的迭代过程。由于数据集比较简单,我们将迭代的终止条件设置为所有点的分类都不再变化。对于更复杂的数据集,这一条件很可能无法使迭代终止,从而需要我们控制最大迭代次数,或者设置允许类别变动的点的比例等等。

def Kmeans(dataset, K, init_cent):
    # dataset:数据集
    # K:目标聚类数
    # init_cent:初始化中心点的函数
    centroids = init_cent(dataset, K)
    cluster = np.zeros(len(dataset), dtype=int)
    changed = True
    # 开始迭代
    itr = 0
    while changed:
        changed = False
        loss = 0
        for i, data in enumerate(dataset):
            # 寻找最近的中心点
            dis = np.sum((centroids - data) ** 2, axis=-1)
            k = np.argmin(dis)
            # 更新当前样本所属的聚类
            if cluster[i] != k:
                cluster[i] = k
                changed = True
            # 计算损失函数
            loss += np.sum((data - centroids[k]) ** 2)
        # 绘图
        print(f'Iteration {itr}, Loss {loss:.3f}')
        show_cluster(dataset, cluster, centroids)
        # 更新中心点
        for i in range(K):
            centroids[i] = np.mean(dataset[cluster == i], axis=0)
        itr += 1

    return centroids, cluster

  最后,我们观察k均值算法在上面的数据集上聚类的过程。根据上面的可视化结果,我们大概可以看出有4个聚类,因此设定 K = 4 K=4 K=4

np.random.seed(0)
cent, cluster = Kmeans(dataset, 4, random_init)

在这里插入图片描述

三、k-means++算法

  上面的分类结果与我们的主观感受区别不大。但是,k均值算法对初始选择的聚类中心非常敏感,且极易收敛到局部最小值,因此不同的中心选择可能导致完全不同的划分。通常来说,我们可以用不同的随机种子选择多组初值,最终挑出划分最好的那一个。但是,当聚类个数和数据量较大时,k均值算法运行需要的时间很长,反复调整随机种子也很不方便。因此,改进的k均值算法——k-means++算法提出了一种新的初始中心选择方法,使算法整体对随机种子的依赖大大减小。

  首先,k-means++算法从所有样本中随机选取一个点当作第一个聚类的中心点。直观上来讲,我们希望初始的中心点尽可能散开。因此在选择接下来的中心点时,该算法会将样本到当前中心点的距离也纳入考量。设目前已有 k k k个中心点,分别是 μ 1 , ⋯   , μ k \boldsymbol\mu_1,\cdots,\boldsymbol\mu_k μ1,,μk,对于样本 x \boldsymbol x x,其他与最近的中心点的距离为 D ( x ) D(\boldsymbol x) D(x)。为了使各个中心点之间的距离尽可能大,令 x \boldsymbol x x被选为第 k + 1 k+1 k+1 个中心点的概率为 P ( μ k + 1 = x ) = D 2 ( x ) ∑ x D 2 ( x ) P(\boldsymbol\mu_{k+1}=\boldsymbol x)=\frac{D^2(\boldsymbol x)}{\sum\limits_{\boldsymbol x}D^2(\boldsymbol x)} P(μk+1=x)=xD2(x)D2(x)

  上式的分母是在整个数据集上进行的求和,使所有样本被选为中心点的概率值和为1。上式的含义是,样本 x \boldsymbol x x被选为中心点的概率与其到当前中心点距离的平方成正比。我们重复这一过程,直到 K K K个聚类中心都被选出为止。

  下面,我们来实现k-means++的初始化函数。

def kmeanspp_init(dataset, K):
    # 随机第一个中心点
    idx = np.random.choice(np.arange(len(dataset)))
    centroids = dataset[idx][None]
    for k in range(1, K):
        d = []
        # 计算每个点到当前中心点的距离
        for data in dataset:
            dis = np.sum((centroids - data) ** 2, axis=-1)
            # 取最短距离的平方
            d.append(np.min(dis) ** 2)
        # 归一化
        d = np.array(d)
        d /= np.sum(d)
        # 按概率选取下一个中心点
        cent_id = np.random.choice(np.arange(len(dataset)), p=d)
        cent = dataset[cent_id]
        centroids = np.concatenate([centroids, cent[None]], axis=0)

    return centroids

  我们已经预留了初始化函数的接口,只需要将参数从random_init替换为kmeanspp_init就可以测试k-means++算法的表现了。从绘制的迭代中间结果可以明显看出,用k-means++算法选择的初始中心点互相之间的距离非常远,从而收敛速度也要快很多。大家可以修改随机种子,观察随机初始化和k-means++初始化对随机种子的敏感程度。

cent, cluster = Kmeans(dataset, 4, kmeanspp_init)

在这里插入图片描述

四、Sklearn中的k-means算法

  使用scikit-learn库中cluster模块的KMeans类可以实现KMeans聚类算法对数据进行聚类,KMeans类的基本使用格式和常用参数说明如下:

class sklearn.cluster.KMeans(n_clusters=8, *, init=‘k-means++’, n_init=10, max_iter=300, tol=0.0001)

参数名称参数说明
n_clusters接收int。表示要形成的簇数以及生成的质心数。默认为8。
init接收方法名。表示所选择的初始化方法,可选’k-means ++‘,‘random’,ndarray,callable。默认为’k-means ++’。
n_init接收int。表示K均值算法将在不同质心下运行的次数。默认为10。
max_iter接收int。表达单次运行的K均值算法的最大迭代次数。默认为300。
tol接收float。表示两个连续迭代的聚类中心的差异,以声明收敛。默认为1e-4。

  选择K个算法的流程如下:首先先选择K个聚类个数.直接生成K个中心作为均值定量,或者随机选择K个均值定量,然后作为聚类中心。对每个点确定其聚类中心点。再计算其聚类新中心。重复以上步骤直到满足收敛要求。(通常就是确定的中心点不再改变)。

  Kmeans的流程可以用以下一组图表示。

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
blobs = make_blobs(random_state=7,centers=3)
X_blobs = blobs[0]
plt.scatter(X_blobs[:,0],X_blobs[:,1],c='r',edgecolors='k')
plt.show()

在这里插入图片描述

from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=3)
kmeans.fit(X_blobs)
y_kmeans = kmeans.predict(X_blobs)
plt.scatter(X_blobs[:, 0], X_blobs[:, 1], c=y_kmeans, s=50)
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1],marker='x',c='r', s=200, alpha=0.5)
plt.show()

在这里插入图片描述

  我们先设置了一个K值为3,然后我们先随机从数据中找出三点作为三个均值定量,然后在计算所有数据各点到质心的距离,然后将是数据分配给距离最近的一类,用不同的颜色表示数据所属各类,然后经过第一轮的迭代后从各类中可以计算新的均值定量,然后计算每个数据点到个类之间的最近距离分到该类里面,重复迭代上述步骤。最终得到了一个合适的均值定量来表示我们的类别。当然在实际运用该算法时,一般会多以迭代,才能得到理想的结果。

  通过下列代码观察Kmeans算法的聚点,并观察决策边界。

# 导入必要的库
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans

# 生成带有3个中心的随机数据点
blobs = make_blobs(random_state=7, centers=3)
X_blobs = blobs[0]

# 绘制原始数据点
plt.scatter(X_blobs[:, 0], X_blobs[:, 1], c='r', edgecolors='k')

# 创建KMeans聚类模型,设置聚类数为3
kmeans = KMeans(n_clusters=3)
kmeans.fit(X_blobs)

# 定义图形的边界
X_min, X_max = X_blobs[:, 0].min() - 0.5, X_blobs[:, 0].max() + 0.5
y_min, y_max = X_blobs[:, 1].min() - 0.5, X_blobs[:, 1].max() + 0.5
# 创建网格用于绘制决策边界
xx, yy = np.meshgrid(np.arange(X_min, X_max, .02), np.arange(y_min, y_max, .02))

# 使用KMeans模型预测网格上的每个点的聚类
Z = kmeans.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# 创建新的图形并清空之前的内容
plt.figure(1)
plt.clf()
# 绘制聚类决策边界
plt.imshow(Z, interpolation='nearest', extent=(xx.min(), xx.max(), yy.min(), yy.max()), cmap=plt.cm.summer, aspect='auto', origin='lower')
# 绘制原始数据点
plt.plot(X_blobs[:, 0], X_blobs[:, 1], 'r.', markersize=5)
# 绘制聚类中心
centroids = kmeans.cluster_centers_
plt.scatter(centroids[:, 0], centroids[:, 1], marker='x', s=150, linewidths=3, color='b', zorder=10)
# 设置图形的显示范围
plt.xlim(X_min, X_max)
plt.ylim(y_min, y_max)
# 隐藏坐标轴刻度
plt.xticks(())
plt.yticks(())
# 显示图形
plt.show()

在这里插入图片描述

  看过此算法后,我们很容易与KNN算法搞混,两者都有一个相似的过程,就是都需要找到某一个点最近的点,二者都采用了最近邻的思想。其实两者的差别还是挺大的,Kmeans算法是无监督学习的聚类算法,而KNN算法是监督学习的分类算法。KNN算法基本不需要训练,只要从测试集里面根据距离计算公式找到K个点,用这K个点表示测试机集的类别即可,而Kmeans算法有明显的训练过程,需要反复迭代找到K个类别的最佳质心来决定数据的类别。

Kmeans算法的优点

  1. 原理简单、实现容易,是解决聚类问题的一种经典算法,保持可伸缩性和高效率,当数据集是密集的,它的效果较好。
  2. 算法的可解释性较强,只要需要调参的的参数只有分类数K。

Kmeans算法的缺点

  1. 必须事先给出K值,而且对初值敏感,对于不同的初始值,可能会导致不同结果。
  2. 对躁声和孤立点数据敏感。
  3. 采用迭代方法,得到的结果只是局部最优。

五、拓展:Sklearn中的层次聚类和密度聚类

(一)层次聚类

  使用scikit-learn库中cluster模块的AgglomerativeClustering类可以实现层次聚类算法对数据进行聚类,AgglomerativeClustering类的基本使用格式和常用参数说明如下:

class sklearn.cluster.AgglomerativeClustering(n_clusters=2, *, affinity='euclidean', memory=None, connectivity=None, compute_full_tree='auto', linkage='ward', distance_threshold=None)

参数名称参数说明
n_clusters接收int or None。表示要查找的集群数。默认为2。
affinity接收str or callable。表示用于计算链接的度量。默认为’euclidean’。
memory接收具有joblib的内存str或对象。表示用于缓存树计算的输出。默认为None。
connectivity接收数组或可调用的连接对象。表示连接的矩阵。默认为None。
compute_full_tree是否计算完整的聚类树,默认是 ‘auto’。
linkage连接策略,默认是 ‘ward’,可选择 ‘average’, ‘complete’, ‘single’。
distance_threshold接收float。表示链接距离的阈值。默认为None。

  层次聚类算法类,是许多基于相同原则构建的聚类算法,算法的原理如下:所谓层次聚类就是让算法在一开始的时候,将每一个点作为一个类别,每一次进行算法合并两个最近的两类,直到满足某个条件为止。sklearn中实现该算法的满足条件是类别的个数,当剩下指定个数的类时,算法自动停止。

  这里的参数重要的是链接准则,他的意思是如何寻找最相近的点。sklearn库中包含三种链接方式:ward:是默认选项,通过挑选两个类来合并,类中的方差增加最小通过这中链接方式通常得到大小差不多相等的簇。average链接:将簇中所有点之间平均距离最小的两个类合并。complete链接:也称为最大链接,将簇中点之间最大距离最小的两个类合并。ward适用于大多数数据集。如果类中的成员个数非常不同,那么average或complete可能效果更好。single连接法(单链接):是指使用最小距离来合并簇。这意味着在两个簇之间,距离最近的两个点的距离被用作簇间距离。这种方法倾向于形成长而狭的簇,可能导致链状结构的形成。适用于处理形状不规则的数据集,但也可能对噪声和离群点敏感。

  下面是对数据集采用三种链接准则得到的结果。程序如下:

# 导入必要的库
from sklearn.datasets import make_blobs  # 生成随机数据的模块
from sklearn.cluster import AgglomerativeClustering  # 导入层次聚类模型
import numpy as np  # 数值计算库
import matplotlib.pyplot as plt  # 绘图库
from itertools import cycle  # 用于循环颜色的迭代器模块

# 定义数据中心
centers = [[1, 1], [-1, -1], [1, -1]]
# 生成随机数据,n_samples是样本数量,centers是中心位置,cluster_std是簇的标准差
X, labels_true = make_blobs(n_samples=2000, centers=centers, cluster_std=0.5, random_state=22)
# 创建层次聚类模型,使用ward链接方法,期望聚类数为4
ac = AgglomerativeClustering(linkage='ward', n_clusters=4)
# ac = AgglomerativeClustering(linkage='average', n_clusters=4)
# ac = AgglomerativeClustering(linkage='complete', n_clusters=4)

# 训练模型并对数据进行聚类
ac.fit(X)

# 获取每个数据点的分类标签
labels = ac.labels_
# 绘图
plt.figure(1)  # 创建一个新图形
plt.clf()  # 清空当前图形
# 定义颜色循环,使用'r','g','c','y'四种颜色
colors = cycle('rgcy')
# 遍历每个聚类标签(0到3),并绘制相应的数据点
for k, col in zip(range(4), colors):
    # 创建一个布尔数组,标识哪些点属于当前聚类
    my_members = labels == k
    # 绘制属于当前聚类的点,X[my_members,0]为横坐标,X[my_members,1]为纵坐标
    plt.plot(X[my_members, 0], X[my_members, 1], col + '.')
# 显示绘制的图形
plt.show()

在这里插入图片描述

层次聚类算法的优点

  1. 距离和规则的相似度容易定义,限制少
  2. 不需要预先制定聚类数
  3. 可以发现类的层次关系

层次聚类算法的缺点

  1. 计算复杂度太高
  2. 奇异值也能产生很大影响
  3. 算法很可能聚类成链状

(二)密度聚类之DBSCAN算法

  使用scikit-learn库中cluster模块的DBSCAN类可以实现密度聚类算法对数据进行聚类,DBSCAN类的基本使用格式和常用参数说明如下:

class sklearn.cluster.DBSCAN(eps=0.5, *, min_samples=5, metric='euclidean', metric_params=None, algorithm='auto', leaf_size=30, p=None, n_jobs=None)

参数名称参数说明
eps接收float。表示邻域半径。默认为0.5。
min_samples接收int。表示一个点附近的样品数量(或总重量)被视为核心点。默认为5。
metric接收string or callable。表示计算要素阵列中实例之间的距离时使用的度量。默认为’euclidean’。
metric_params接收dict。表示度量功能的其他关键字参数。默认为None。
algorithm接收算法名称。表示NearestNeighbors模块将使用该算法来计算逐点距离并查找最近的邻居。默认为’auto’。
leaf_size接收int。影响ball_tree和kd_tree的构建和查询速度的参数。较大的值可能会导致更快的查询,但更慢的构建时间。默认为30。
p接收float。在使用Minkowski距离时的参数,可以设置为1(曼哈顿距离)或2(欧氏距离)。默认为None。
n_jobs接收int。表示要运行的并行作业数。默认为None。

  DBSCAN聚类算法是一种基于密度的算法,这一类算法通常通过根据数据的紧密程度进行分类,如果一个数据属于该类,则在其附近一定存在属于该类的数据点。通过紧密分布的数据集分为一类,就得到了一个聚类类别。通过将所有数据划分为不同类别,就得到了最终的聚类类别结果。该算法将具有足够密度的区域划分为一类,并在具有噪声的数据集中能划分出不同形状的类别,它将类定义为密度相连的点的最大集合。那么它是怎么基于密度工作的呢?

  在一个数据集中,我们的目标是把数据中密度相近的聚为一类。我们先从数据中选择一点,然后按照一定规则在这里寻找密度相近的点组成一类。其他的数据点也是如此。这个规则就是根据这个随即被选中的数据点画一个圆圈,规定这个圆的半径以及圆内最少包含的数据数,然后在包含在内的数据中转移中心点,那么这个圆圈的圆心就转移到这个内部样本点,继续去圈附近其它的数据点,然后一直重复上述过程,继续增加数据点直到没有符合条件的数据为止。

  基于密度这点有什么好处呢,我们知道k-means聚类算法只能根据距离进行计算,而现实中还会有各种形状的图,比如环形图,这个时候,k-means算法就不适用。于是就想到根据数据的密度进行分类。

  上述基本是DBSCAN算法的主要内容了,是不是很简单,但是我们还有三个问题没有考虑。第一个是一些数据远离于其他数据点,这些点不在任何一类的周围,在DBSCAN中,我们一般将这些样本点标记为噪音点。第二个是距离的度量问题,即如何计算某样本和核心对象样本的距离。在DBSCAN中,一般采用最近邻思想,采用某一种距离度量来衡量样本距离,比如欧式距离。这和KNN分类算法的最近邻思想完全相同。

  DBSCAN这个算法包括了重要的两个参数,这两个参数比较难指定,公认的指定方法是以下两个:

  • 半径:半径是比较重要的一个点,如果选择过大了,圈住点就多了,类别的个数就少了,反之圈住的点少,类别就增加,这对最后生成的结果非常重要。
  • MinPts:这个参数就是圈住的点的个数,也相当于是一个密度,一般我们先让这个值尽可能地小,然后进行多次尝试算法。

  下面我们对DBSCAN算法进行应用。首先我们生成一个环形数据来测试算法。

from sklearn.datasets.samples_generator import make_circles
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
X, y_true = make_circles(n_samples=2000, factor=0.5,noise=0.1)  # 环状数据

# DBSCAN算法
dbscan = DBSCAN(eps=.1, min_samples=10)
dbscan.fit(X)  # 该算法对应的两个参数
plt.scatter(X[:, 0], X[:, 1], c=dbscan.labels_)
plt.show()

在这里插入图片描述

  从图中可以看出,算法划分的方法符合我们的思维,并且基本做到了完美划分。接下来试验一下对于双半月数据,算法能不能做到好的预测。

from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
X, y_true = make_moons(n_samples=2000, noise=0.1)    # 双半月数据

# DBSCAN算法
dbscan = DBSCAN(eps=.1, min_samples=10)
dbscan.fit(X)  # 该算法对应的两个参数
plt.scatter(X[:, 0], X[:, 1], c=dbscan.labels_)
plt.show()

在这里插入图片描述

  从图中看出,依旧能建立较好的模型。测试完之后大家可能感觉没有那么神奇,因为这种方法符合我们的思维,但是这是由计算机划分出来的。与Kmeans算法和层次聚类算法进行对比,我们就能知道这个算法的优越之处了。

from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
X, y_true = make_moons(n_samples=2000,noise=0.1)
# KMeans算法
kmeans = KMeans(n_clusters=2)
kmeans.fit(X)  # 该算法对应的两个参数
plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels_)
plt.show()

在这里插入图片描述

# 层次聚类
ac = AgglomerativeClustering(n_clusters=2)
ac.fit(X)
plt.scatter(X[:, 0], X[:, 1], c=ac.labels_)
plt.show()

在这里插入图片描述

  可以看到如果分为两类,这种划分方式不符合我们的思维。所以对于不同的问题要选择合适的算法才能解决。

DBSCAN算法的优点

  1. 速度快,不需要提前设定K值大小,DBSCAN不需要事先知道要形成种类的数量。
  2. 对噪声不敏感。可以在聚类的同时发现异常点,对数据集中的异常点不敏感,能发现任意形状的簇。这是因为该算法能够较好地判断离群点,并且即使错判离群点,对最终的聚类结果也没什么影响。
  3. 聚类生成的种类没有偏倚,而Kmeans之类的聚类算法初始值对聚类结果有很大影响。

DBSCAN算法的缺点

  1. 对参数的设置敏感,当半径和密度变化时会很大程度影响结果。
  2. 当数据量较大时,要求的内存也大。
  3. 当空间密度不均匀、聚类间距差相差很大时,聚类质量较差。

:以上文中的数据集及相关资源下载地址:
链接:https://pan.quark.cn/s/bbdafa452d3e
提取码:3bBk

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安卓系统升级后,关于Fiddler工具不能抓取https接口问题

问题原因? 目前安卓手机可以抓取的https接口都在安卓7.0版本以下,有时候抓取Android7.0版本或以上的接口抓取不到 因为Android7.0之后常规手段不能抓Https的包,应用会默认不信任用户安装的证书(手机里自己安装的证书),只信任系统…

最新版FaceFusion3.0.0,最强AI换脸,表情修改,视频换脸,年龄修改,多人换脸,面部遮挡换脸,参数调优

主要修改:表情修改,视频换脸,年龄修改,多人换脸,面部遮挡换脸,参数调优 变更日志 改造一切皆工作的建筑介绍pixel boost换脸者为面部检测器添加多角度处理引入年龄修正处理器推出 Live Portrait 表情恢复处理器推出由 Live Portrait 提供支持的脸部编辑处理器用res…

视频制作软件哪个好?前十名推荐!

在视频制作领域,选择合适的软件是提升创作效率和作品质量的关键。本文将根据软件的适用人群:新手入门和专业领域,以及推荐的书籍,为您详细介绍视频制作软件的前十名。 新手入门级别: 1.影忆 功能特点:新手入…

浙大数据结构:05-树9 Huffman Codes

这道题难度挺大,写起来较为费劲,这里我依然使用了STL库,使得代码量大幅减少不过百行,便于大家理解。 机翻: 1、条件准备 数组存储字符对应频率,n,student存储输入多少字符,有多少学生测试。 …

【Transformers基础入门篇2】基础组件之Pipeline

文章目录 一、什么是Pipeline二、查看PipeLine支持的任务类型三、Pipeline的创建和使用3.1 根据任务类型,直接创建Pipeline,默认是英文模型3.2 指定任务类型,再指定模型,创建基于指定模型的Pipeline3.3 预先加载模型,再…

用二维码收集信息时,在后台可以查看、统计哪些数据?

大家都知道,在二维码上关联表单,就可以扫码填写信息了。那么,收集到的数据在哪里查看?具体可以查看到哪些数据呢? 如果是用草料二维码平台搭建的二维码,前往后台,在表单列表中找到对应的表单&a…

智能Ai语音机器人的应用价值有哪些?

随着时间的推移,人工智能的发展越来越成熟,智能时代也离人们越来越近,近几年人工智能越来越火爆,人工智能的应用已经开始渗透到各行各业,与生活交融,成为人们无法拒绝,无法失去的一个重要存在。…

【解密 Kotlin 扩展函数】命名参数和默认值(十三)

导读大纲 1.0.1 命名参数1.0.2 默认参数值 上一节讲述如何自定义 joinToString 函数来代替集合的默认字符串表示 文末遗留下几个待优化问题–传送门 1.0.1 命名参数 我们要解决的第一个问题涉及函数调用的可读性 例如,请看下面的joinToString调用: joinToString(collection,&…

MyBatis深度剖析:从入门到精通的实践指南

前言 什么是mybatis? MyBatis是一款优秀的持久层框架,用于简化Java应用程序与数据库之间的交互 什么是框架,为什么需要框架技术? 框架技术 是一个应用程序的半成品提供可重用的公共结构按一定规则组织的一组组件框架优势&#x…

【自动化测试】Appium 生态工具以及Appium Desktop如何安装和使用

引言 Appium 是一个开源的自动化测试框架,用于测试原生、移动 Web 和混合应用程序。它支持 iOS、Android 和 Windows 平台。Appium 生态系统包含多个工具和库,这些工具和库可以与 Appium 一起使用,以提高移动应用的自动化测试效率 文章目录 引…

Java面试指南(基础篇)

文章目录 前言01 Java语言的特点02 JVM、JRE及JDK的关系03 Java和C的区别04 基本数据类型05 类型转换06 自动装箱与拆箱07 String的不可变性08 字符常量和字符串常量的区别09 字符串常量池10 String 类的常用方法11 String和StringBuffer、StringBuilder的区别12 switch 是否能…

舒服了!学大模型必看的学习书籍来了

最近整理了日前市面上一大波大模型的书,已经打包成pdf了,大家有需要的,可以自行添加获取,纯福利,无套路,添加后说明是哪本书,会直接给大家!(文末获取) 部分书…

IO 多路转接之 epoll

文章目录 IO 多路转接之 epoll1、IO 多路转接之 poll1.1、poll 函数1.2、poll 函数返回值1.3、Socket 就绪条件1.3.1、读就绪1.3.2、写就绪1.3.3、异常就绪 1.4、poll 的优点1.5、poll 的缺点1.6、poll 改写 select 2、IO 多路转接之 epoll2.1、epoll 函数2.2、epoll_create2.3…

Leetcode 反转链表

使用递归 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ class S…

超低排放燃气锅炉

在全球环保浪潮的推动下,超低排放燃气锅炉以其卓越的环保性能和高效能源利用,正逐渐成为现代热能供应的主力军。作为传统锅炉的升级版,超低排放燃气锅炉不仅在技术上实现了质的飞跃,更在环保和节能方面树立了新的标杆。朗观视觉小…

linux入门到实操-10 控制台显示和输出重定向、监控文件变化、软连接

教程来源:B站视频BV1WY4y1H7d3 3天搞定Linux,1天搞定Shell,清华学神带你通关_哔哩哔哩_bilibili 整理汇总的课程内容笔记和课程资料(包含课程同版本linux系统文件等内容),供大家学习交流下载:…

【Delphi】扩展现有组件创建新的 FireMonkey 组件(步骤一)

本例中演示将TLabel控件扩展成TClockLabel新控件。具体如下: 步骤 1 - 使用新建组件向导创建组件 1. 菜单选择 Component -> New Component。 2. 在新建组件向导的第一页,选择 FireMonkey for Delphi : 3. 在 “Ancestor Component ”页…

【最新华为OD机试E卷-支持在线评测】爱吃蟠桃的孙悟空(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 💻 ACM金牌🏅️团队 | 大厂实习经历 | 多年算法竞赛经历 ✨ 本系列打算持续跟新华为OD-E/D卷的多语言AC题解 🧩 大部分包含 Python / C / Javascript / Java / Cpp 多语言代码 👏 感谢大家的订阅➕ 和 喜欢�…

解决windows上VMware的ubuntu虚拟机不能拷贝和共享

困扰多时的VMware虚拟机不能复制拷贝和不能看到共享文件夹的问题,终于解决了~ 首先确定你已经开启了复制拷贝和共享文件夹,并且发现不好用。。。 按照下面方式解决这个问题。 1,删除当前的vmware tools。 sudo apt-get remove --purge ope…

【Redis技术进阶之路】「原理分析系列开篇」揭秘分析客户端和服务端网络通信交互实现(客户端篇)

揭秘高效存储模型与数据结构底层实现 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 客户端和服务器Redis服务器IO多路复用RedisClient结构 客户端属性分析套接字描述符客户端的分类伪客户端…