聚类算法介绍、K-means聚类的实现与衍生算法、密度聚类

Kmeans算法

介绍

  • 给定一个有M个对象的数据集,构建一个具有K个簇的模型,其中k<=M。满足:
    • 每个簇至少包含一个对象
    • 每个对象属于且仅属于一个簇
    • 将满足上述条件的K个簇称为一个合理的聚类划分
  • 基本思想:对于给定的类别数目k,首先给定初始划分,通过迭代改变样本和簇的隶属关系,使得每次处理后得到的划分方式比上一次的好(总的数据集之间的距离和变小了)

算法理论

假设输入样本为 T = x 1 , x 2 , . . . , x m T=x_1,x_2,...,x_m T=x1,x2,...,xm;则算法步骤为(使用欧氏距离):

  • 随机选择样本初始化的k个聚类中心, a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak
  • 对于每个样本 x i x_{i} xi,将其标记为距离类别中心 a j a_{j} aj最近的类别 j j j
  • l a b e l i = arg ⁡ min ⁡ 1 < j < k { ∑ i = 1 n ( x i − a j ) 2 } l a b e l_{i}=\underset{1<j<k}{\arg \min }\{\sqrt{\sum_{i=1}^{n}\left(x_{i}-a_{j}\right)^{2}}\} labeli=1<j<kargmin{i=1n(xiaj)2 }
  • 更新每个类别的中心点 a j a_{j} aj为隶属该类别的所有样本的均值
    μ i = 1 ∣ C i ∣ ∑ x ∈ C i x \mu_{i}=\frac{1}{|C_i|} \sum_{x \in C_i} x μi=Ci1xCix
  • 重复上述两步操作,知道达到某个终止条件
  • 终止条件:
    • 迭代次数
    • 最小平方误差MSE
    • 簇中心点变化率

例子

对数据集 { ( 1 , 2 ) , ( 2 , 2 ) , ( 6 , 8 ) , ( 7 , 8 ) } \{(1,2),(2,2),(6,8),(7,8) \} {(1,2),(2,2),(6,8),(7,8)}聚成2类

  1. 随机选取聚类中心 C = { ( 1 , 2 ) , ( 2 , 2 ) } C=\{(1,2),(2,2)\} C={(1,2),(2,2)}
  2. 分别计算样本点到每个中心点的欧式距离
  3. 对距离从小到大进行排序,将样本点进行归类
  4. 各类使用样本均值更新样本中心点
  5. 重复2、3、4
  6. 当聚类中心不再变化时,停止迭代,完成聚类
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import copy
import sklearn.datasets as ds
import matplotlib.colors
from sklearn.cluster import KMeans,DBSCAN,AgglomerativeClustering
import scipy.cluster.hierarchy as sch
# v测量,轮廓系数
from sklearn.metrics import v_measure_score, silhouette_score

plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = 'False'

def example1():
    X = np.array([[1, 2], [2, 2], [6, 8], [7, 8]])
    # 初始聚类中心
    C = np.array([[1.0, 2.0], [2.0, 2.0]])
    plt.scatter(X[:, 0], X[:, 1])
    plt.show()

    '''
    1、计算所有样本点到每个中心的距离
    2、比较样本点与哪个中心点距离最近,记为该簇
    3、采用均值方式,更新中心位置
    '''
    iters = 5
    x = 0
    while x < 5:
        iters -= 1
        x += 1
        # 保存每个样本点到中心的距离
        B = []
        C_ = copy.deepcopy(C)
        for c in C:
            # 计算欧氏距离
            dis = np.sqrt((X - c) ** 2).sum(axis=1)
            B.append(dis)
        # 求样本点属于哪一个类别
        min_idx = np.argmin(np.array(B), axis=0)
        for i in range(len(C)):
            # 更新每个中心的位置
            C[i] = np.mean(X[min_idx == i], axis=0)
        if np.sum(C - C_) == 0:
            break
        plt.scatter(X[:,0].X[:,1],c=min_idx)
        plt.scatter(C[:,0].C[:,1],c='red',marker='*')
        plt.legend()
        plt.show()

K-means衍生算法

K-means缺点

  • 缺点一:聚类中心的个数K需要事先给定,但在实际中K值的选定是非常困难的,很多时候我们并不 知道给定的数据集应该聚成多少个类别才最合适
  • 缺点二:k-means算法需要随机地确定初始聚类中心,不同的初始聚类中心可能导致完全不同的聚 类结果,有可能导致算法收敛很慢甚至出现聚类出错的情况
  • 针对第一个缺点:很难在k-means算法以及其改进算法中解决,一般来说,我们会根据对数据的先 验经验选择一个合适的k值,如果没有什么先验知识,则可以通过“肘方法”选择一个合适的k值
  • 针对第二个缺点:可以通过k-means++算法来解决

K-means++算法

K-Means++算法和K-Means算法的区别主要在于 初始的K个中心点的选择方面,K-Means算法使用随机给定的方式,K-Means++算法采用下列步 骤给定K个初始质点:

  • 从数据集中任选一个节点作为第一个聚类中心

  • 对数据集中的每个点x,计算x到所有已有聚类中心点的距离和 D ( X ) D(X) D(X),基于 D ( X ) D(X) D(X)采用线性概率选择出下一个 聚类中心点(距离较远的一个点成为新增的一个聚类中心点)

  • 重复步骤2直到找到k个聚类中心点

  • 缺点:
    由于聚类中心点选择过程中的内在有序性,在扩展方面存在着性能方面的问题(第k个聚类中 心点的选择依赖前k-1个聚类中心点的值) k-means++ 最主要的缺点在于其内在的顺序执行特性, 得到 k 个聚类中心必须遍历数据集 k 次,并且当前聚类中心的计算依赖于前面得到的所有聚类中心, 这使得算法无法并行扩展,极大地限制了算法在大规模数据集上的应用。因此有了K-means||算法

K-means与K-means++

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import copy
import sklearn.datasets as ds
import matplotlib.colors
from sklearn.cluster import KMeans,DBSCAN,AgglomerativeClustering
import scipy.cluster.hierarchy as sch
# v测量,轮廓系数
from sklearn.metrics import v_measure_score, silhouette_score

plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = 'False'

#指定输出边界
def expand(a, b):
    return a-1, b+1#最小值-1,最大值+1

def K_means_plus():
    N = 400  # 400个样本点
    centers = 4  # 4个中心
    # 样本点数量,特征数,中心数,随机种子
    data, y = ds.make_blobs(N, n_features=2, centers=centers, random_state=6)
    # 指定四个中心的颜色,红绿蓝,粉
    cm = matplotlib.colors.ListedColormap(list('rgbm'))
    plt.title(u'原始数据')
    plt.scatter(data[:, 0], data[:, 1], c=y, s=30, cmap=cm)
    x1_min, x2_min = np.min(data, axis=0)
    x1_max, x2_max = np.max(data, axis=0)
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid()
    plt.show()
    # Kmeans的调用, 默认形式是采用K-means++进行处理的,如果使用K-means操作,就需要init='random'
    cls = KMeans(n_clusters=4, init='random')
    y_hat = cls.fit_predict(data)
    plt.title(u'KMeans聚类')
    plt.scatter(data[:, 0], data[:, 1], c=y_hat, s=30, cmap=cm)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid()
    plt.show()
    #K-means++
    cls = KMeans(n_clusters=4, init='k-means++')
    y_hat = cls.fit_predict(data)
    plt.title(u'KMeans++聚类')
    plt.scatter(data[:, 0], data[:, 1], c=y_hat, s=30, cmap=cm)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid()
    plt.show()

K-means||

  • 主要思路是改变每次遍历时候的取样规则,并非按 照K-Means++算法每次遍历只获取一个样本,而是每次获取K个样本,重复该取样操作O(logn)次, 然后再将这些抽样出来的样本聚类出K个点,最后使用这K个点作为K-Means算法的初始聚簇中心 点。
  • k-means||是k-means++的变体,k-means||在初始化中心点时对kmeans++的缺点做了规避,主 要体现在不需要根据k的个数严格地寻找k个点,突破了算法在大规模数据集上的应用瓶颈

聚类算法评估

均一性、完整性

  • 均一性:一个簇中只包含一个类别的样本,则满足均一性;其实也可以认为就是正确率(每个聚簇中 正确分类的样本数占该聚簇总样本数的比例和)
    p = 1 k ∑ i = 1 k N ( C i = K i ) N ( K i ) \mathrm{p}=\frac{1}{k} \sum_{i=1}^{k} \frac{N\left(C_{i}=K_{i}\right)}{N\left(K_{i}\right)} p=k1i=1kN(Ki)N(Ci=Ki)
  • 完整性:同类别样本被归类到相同簇中,则满足完整性;每个聚簇中正确分类的样本数占该类型的 总样本数比例的和
    r = 1 k ∑ i = 1 k N ( C i = K i ) N ( C i ) r=\frac{1}{k} \sum_{i=1}^{k} \frac{N\left(C_{i}=K_{i}\right)}{N\left(C_{i}\right)} r=k1i=1kN(Ci)N(Ci=Ki)
  • V-measure:均一性和完整性的加权平均
    v β = ( 1 + β 2 ) ⋅ p r β 2 ⋅ p + r v_{\beta}=\frac{\left(1+\beta^{2}\right) \cdot \mathrm{p} r}{\beta^{2} \cdot p+r} vβ=β2p+r(1+β2)pr

肘部法

  • 核心指标是SSE(sum of the squared errors,误差平方和)
    S S E = ∑ i = 1 k ∑ x ∈ C i ∥ x − μ i ∥ 2 SSE=\sum_{i=1}^{k}{\sum_{x \in C_{i}}}\left\|x-\mu_{i}\right\|^{2} SSE=i=1kxCixμi2
    其中, C i C_i Ci是第 i i i个簇, x x x C i C_i Ci中的样本点, μ i μ_i μi是Ci的质心(KaTeX parse error: Expected group after '_' at position 3: Ci_̲中所有样本的均值),SSE是所有样本的 聚类误差,代表了聚类效果的好坏
    在这里插入图片描述
  • 手肘法的核心思想是:
  1. 随着聚类数k的增大,样本划分会更加精细,每个簇的聚合程度会逐渐提高, 那么误差平方和SSE自然会逐渐变小。
  2. 当k小于真实聚类数时,由于k的增大会大幅增加每个 簇的聚合程度,故SSE的下降幅度会很大
  3. 当k到达真实聚类数时,再增加k所得到的聚合程度回 报会迅速变小,所以SSE的下降幅度会骤减,然后随着k值的继续增大而趋于平缓,也就是说SSE和 k的关系图是一个手肘的形状,而这个肘部对应的k值就是数据的真实聚类数。

轮廓系数

  • 簇内不相似度:计算样本i到同簇其它样本的平均距离为ai,ai越小,表示样本i越应 该被聚类到该簇,簇C中的所有样本的ai的均值被称为簇C的凝聚度。
  • 簇间不相似度:计算样本i到其它簇C_{j}的所有样本的平均距离 b i j , b i = m i n b i 1 , b i 2 , . . . , b i k ; b i b_{ij},b_{i}=min{b_{i1},b_{i2},...,b_{ik}};b_{i} bijbi=minbi1,bi2,...,bikbi越大,表示样本i越不属于其它簇。
  • 轮廓系数: s i s_{i} si值取值范围为[-1,1],越接近1表示样本i聚类越合理,越接近-1,表示样 本i应该分类到另外的簇中,近似为0,表示样本i应该在边界上;所有样本的si的均 值被称为聚类结果的轮廓系数。
  • s ( i ) = b ( i ) − a ( i ) max ⁡ { a ( i ) , b ( i ) } s(i)=\frac{b(i)-a(i)}{\max \{a(i), b(i)\}} s(i)=max{a(i),b(i)}b(i)a(i)
    s ( i ) = { 1 − a ( i ) b ( i ) , a ( i ) < b ( i ) 0 , a ( i ) = b ( i ) b ( i ) a ( i ) − 1 , a ( i ) > b ( i ) s(i)=\left\{\begin{array}{l}{1-\frac{a(i)}{b(i)}, \quad a(i)<b(i)} \\ {0, \quad a(i)=b(i)} \\ {\frac{b(i)}{a(i)}-1, \quad a(i)>b(i)}\end{array}\right. s(i)=1b(i)a(i),a(i)<b(i)0,a(i)=b(i)a(i)b(i)1,a(i)>b(i)

    算法评估指标的sklearn使用

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import copy
import sklearn.datasets as ds
import matplotlib.colors
from sklearn.cluster import KMeans,DBSCAN,AgglomerativeClustering
import scipy.cluster.hierarchy as sch
# v测量,轮廓系数
from sklearn.metrics import v_measure_score, silhouette_score

def Evaluation():
    X = np.array([[1, 2, 1], [2, 2, 1], [6, 8, 0], [7, 8, 0]])  # 第三个数值是实际标签
    C = np.array([[1, 1], [2, 1]])
    x = X[:, :-1]  # 前两个数值作为特征
    label = X[:, -1]  # 最后一个数值作为标签
    # 生成2个聚类
    model = KMeans(n_clusters=2)
    model.fit(X, C)
    # 质心位置
    print(model.cluster_centers_)
    labels = model.labels_
    # 分类结果
    print(labels)
    print(silhouette_score(X, labels, metric='euclidean'))  # 轮廓得分(轮廓系数法,后面会提到)
    print(model.inertia_)  # 最小化平方误差E SSE
    print(v_measure_score(label, labels))  # 均一性和完整性的加权平均

def Elbow():
    data = pd.read_csv('../datas/drink.txt')
    record = []  # 记录每一次生成SSE
    for i in range(2, 10):  # k值范围2-10
        kmeans = KMeans(i)
        kmeans.fit(data)
        record.append(kmeans.inertia_)  # inertia簇内误差平方和  SSE
    plt.plot(range(2, 10), record, range(2, 10), record, marker='o', c='b')
    plt.show()  # k=3是最优k值点

DBSCAN密度聚类

  • K-means算法原理简单,容易实现,但是对噪音和异常点比较的敏感
  • 密度聚类: 只要样本点的密度大于某个阈值,则将该样本添加到最近的簇中。这类算法可以克服基于距离的算法只能发现凸聚类的缺点,可以发现任意形状的聚类,而且对噪声数据不敏感。但是计算复杂度高,计算量大

算法理论

  • ε邻域(ε neighborhood,也称为Eps):给定对象在半径ε内的区域
    N c ( x ) = { y ∈ X : d i s t ( x , y ) ≤ ε } N_{c}(x)=\{y \in X: d i s t(x, y) \leq \varepsilon\} Nc(x)={yX:dist(x,y)ε}
  • 密度(density):ε邻域中x的密度,是一个整数值,依赖于半径ε
    p ( x ) = ∣ N ε ( x ) ∣ p(x)=\left|N_{\varepsilon}(x)\right| p(x)=Nε(x)
  • MinPts定义核心点时的阈值,也简记为M
  • 核心点(core point):如果p(x)>=M,那么称x为X的核心点;记由X中所有核心点构成的集合为 X c X_c Xc, 并记 X n c = { X ∣ x ̸ X c } X_{nc}=\{X|x \not X_{c}\} Xnc={XxXc}表示由X中所有非核心点构成的集合。直白来讲,核心点对应于稠密区域内部的点。
  • 边界点(border point): 如果非核心点x的ε邻域中存在核心点,那么认为x为X的边界点。由X中所有 的边界点构成的集合为Xbd。直白来将,边界点对应稠密区域边缘的点。
    x ∈ X n c ∗ ∃ y ∈ X ; y ∈ N ε ( x ) ∩ X c x \in X_{n c^{*}} \exists y \in X ; \quad y \in N_{\varepsilon}(x) \cap X_{c} xXncyX;yNε(x)Xc
  • 噪音点(noise point):集合中除了边界点和核心点之外的点都是噪音点,所有噪音点组成的集合叫 做Xnoi;直白来讲,噪音点对应稀疏区域的点。
    X n o l = X \ ( X c ∪ X n t ) X_{n o l}=X \backslash\left(X_{c} \cup X_{n t}\right) Xnol=X\(XcXnt)
  • 簇(cluster):一个基于密度的簇是最大的密度相连对象的集合C;满足以下两个条件:
    • Maximality:若x属于C,而且y是从x密度可达的,那么y也属于C
    • Connectivity:若x属于C,y也属于C,则x和y是密度相连的

在这里插入图片描述

  • 算法流程:
    • 如果一个点x的ε邻域包含超过m个对象,则创建一个x作为核心对象的新簇;
    • 寻找并合并与核心对象直接密度可达的对象;
    • 没有新点可以更新簇的时候,算法结束
  • 算法特征描述:
    • 每个簇至少包含一个核心对象
    • 非核心对象可以是簇的一部分,构成簇的边缘
    • 包含过少对象的簇被认为是噪声

− − − − − − − − − − ----------
简单的说就是

  1. 以A点位圆心,以ε为半径画圆,如果这个圆包含的样本数大于阈值m,那么A就称为核心点。
  2. 对圆内的点进行循环画圆。以 A 1 A_1 A1为圆心,ε为半径画圆,如果圆内样本数大于m,则 A 1 A_1 A1也是核心点,以此类推
  3. 当画到B或者C点,圆内个数不足m个,那么B点和C点称为边界点
  4. 当边界点画完后,没接触到的点,即N点,称为噪音点
    − − − − − − − − − − ----------

优缺点

  • 优点:
    • 不需要事先给定cluster的数目
    • 可以发现任意形状的cluster
    • 能够找出数据中的噪音,且对噪音不敏感
    • 算法只需要两个输入参数
    • 聚类结果几乎不依赖节点的遍历顺
  • 缺点:
    • DBSCAN算法聚类效果依赖距离公式的选取,最常用的距离公式为欧几里得距离。但是对于高维数据,由于维数太多,距离的度量已变得不是那么重要
    • DBSCAN算法不适合数据集中密度差异很小的情况

sklearn实现,K-means与DBSCAN对比

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import copy
import sklearn.datasets as ds
import matplotlib.colors
from sklearn.cluster import KMeans,DBSCAN,AgglomerativeClustering
import scipy.cluster.hierarchy as sch
# v测量,轮廓系数
from sklearn.metrics import v_measure_score, silhouette_score

plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = 'False'

def Dbscan():
    X1, y1 = ds.make_circles(n_samples=5000, factor=.6, noise=.05)
    X2, y2 = ds.make_blobs(n_samples=1000, n_features=2, centers=[[1.2, 1.2]],
                                 cluster_std=[[.1]], random_state=9)
    X = np.concatenate((X1, X2))
    plt.scatter(X[:, 0], X[:, 1], marker='o')
    plt.title('原始数据样本点分布')
    plt.show()

    y_pred = KMeans(n_clusters=3, random_state=9).fit_predict(X)
    plt.scatter(X[:, 0], X[:, 1], c=y_pred)
    plt.title('K-means算法进行聚类处理')
    plt.show()

    y_pred = DBSCAN().fit_predict(X)
    plt.scatter(X[:, 0], X[:, 1], c=y_pred)
    plt.title('默认参数密度聚类')
    plt.show()

    y_pred = DBSCAN(eps=0.08, min_samples=3).fit_predict(X)  # eps = 0.08 半径, min_samples=3样本数量
    plt.scatter(X[:, 0], X[:, 1], c=y_pred)
    plt.title('设置参数的密度聚类')
    plt.show()

层次聚类简介

概述

  • 层次聚类方法对给定的数据集进行层次的分解,直到满足某种条件为止,传统的层次聚类算法主要 分为两大类算法:
    • 凝聚的层次聚类:AGNES算法(AGglomerative NESting)==>采用自底向上的策略。最初将每个对象作 为一个簇,然后这些簇根据某些准则被一步一步合并,两个簇间的距离可以由这两个不同簇中距离最近的 数据点的相似度来确定;聚类的合并过程反复进行直到所有的对象满足簇数目。
    • 分裂的层次聚类:DIANA算法(DIvisive ANALysis)==>采用自顶向下的策略。首先将所有对象置于一个 簇中,然后按照某种既定的规则逐渐细分为越来越小的簇(比如最大的欧式距离),直到达到某个终结条件 (簇数目或者簇距离达到阈值)。

优缺点

简单,理解容易
合并点/分裂点选择不太容易
合并/分类的操作不能进行撤销
大数据集不太适合
执行效率较低O(t*n2),t为迭代次数,n为样本点数

优化算法

  • BIRCH算法(平衡迭代削减聚类法):聚类特征使用3元组进行一个簇的相关信息,通过构建满足分枝 因子和簇直径限制的聚类特征树来求聚类,聚类特征树其实是一个具有两个参数分枝因子和类直径 的高度平衡树;分枝因子规定了树的每个节点的子女的最多个数,而类直径体现了对这一类点的距 离范围;非叶子节点为它子女的最大特征值;聚类特征树的构建可以是动态过程的,可以随时根据 数据对模型进行更新操作。
    • 适合大规模数据集,线性效率;
    • 只适合分布呈凸形或者球形的数据集、需要给定聚类个数和簇之间的相关参数;
  • CURE算法(使用代表点的聚类法):该算法先把每个数据点看成一类,然后合并距离最近的类直至类 个数为所要求的个数为止。但是和AGNES算法的区别是:取消了使用所有点或用中心点+距离来表 示一个类,而是从每个类中抽取固定数量、分布较好的点作为此类的代表点,并将这些代表点乘以 一个适当的收缩因子,使它们更加靠近类中心点。代表点的收缩特性可以调整模型可以匹配那些非 球形的场景,而且收缩因子的使用可以减少噪音对聚类的影响
    • 能够处理非球形分布的应用场景
    • 采用随机抽样和分区的方式可以提高算法的执行效率
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梚枫_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值