聚类分析(自学使用,侵权即删)

引子

聚类(Clustering)是最常见的无监督学习算法,它指的是按照某个特定标准(如距离)把一个数据集分割成不同的类或簇,使得同一个簇内的数据对象的相似性尽可能大,同时不在同一个簇中的数据对象的差异性也尽可能地大。也即聚类后同一类的数据尽可能聚集到一起,不同类数据尽量分离。

主流聚类算法


我们先对聚类算法做个了解,主流的聚类算法可以分成两类:划分聚类(Partitioning Clustering)层次聚类(Hierarchical Clustering)。他们的主要区别如图中所示:

划分聚类算法会给出一系列扁平结构的簇(分开的几个类),它们之间没有任何显式的结构来表明彼此的关联性。

常见算法有 K-Means/K-Medoids、Gaussian Mixture Model (高斯混合模型)、Spectral Clustering(谱聚类)、Centroid-based Clustering等。


层次聚类算法会输出一个具有层次结构的簇集合,因此能够比划分聚类输出的无结构簇集合提供更丰富的信息。层次聚类可以认为是是嵌套的划分聚类。

常见算法有 Single-linkage、Complete-linkage、Connectivity-based Clustering等。


这两类算法在聚类过程中用到的具体算法不一样,后文我们会重点展开讲一下K-Means算法、DB-SCAN算法、GMM算法、Single-linkage算法和Complete-linkage算法。

划分聚类算法

1)K-Means算法 

K-Means算法核心概念:我们提到了聚类算法要把n个数据点按照分布分成k类(很多算法的k是人为提前设定的)。我们希望通过聚类算法得到k个中心点,以及每个数据点属于哪个中心点的划分。

中心点可以通过迭代算法来找到,满足条件:所有的数据点到聚类中心的距离之和(利用欧氏距离)是最小的。中心点确定后,每个数据点属于离它最近的中心点。

        1.首先,我们选择一些类/组来使用并随机地初始化它们各自的中心点。要想知道要使用的类的数量,最好快速地查看一下数据,并尝试识别任何不同的分组。中心点是与每个数据点向量相同长度的向量,在上面的图形中是“X”。

        2.每个数据点通过计算点和每个组中心之间的距离进行分类,然后将这个点分类为最接近它的组。

        3.基于这些分类点,我们通过取组中所有向量的均值来重新计算组中心。

        4.对一组迭代重复这些步骤。你还可以选择随机初始化组中心几次,然后选择那些看起来对它提供了最好结果的来运行。

K-Means聚类算法的优势在于它的速度非常快,因为我们所做的只是计算点和群中心之间的距离;它有一个线性复杂度O(n)。

另一方面,K-Means也有几个缺点。首先,你必须选择有多少组/类。这并不是不重要的事,理想情况下,我们希望它能帮我们解决这些问题,因为它的关键在于从数据中获得一些启示。K-Means算法过于依赖初始点的选择,从随机选择的聚类中心开始,因此在不同的算法运行中可能产生不同的聚类结果,且收敛很慢;同时使用欧式距离不可避免地会使结果受到异常值的较大干扰。

针对上述问题,我们首先看一下如何实现合理分组

常见确定簇数K的方法:
1.肘部法则 (The elbow method)
        肘部法则,我们主要通过计算在不同情况下的K值的平均cost function(损失函数)变化从而得出明显变化拐点(inertia点)。cost function的大小主要与簇数K有关,不同的K之间,inertia曲线的斜率是不同的。具体如图所示。但拐点寻找较为主观。因此肘部法则只能解决部分的确定最有的簇类数目。

2.轮廓系数 (the optimization of the silhouette coefficient)

        使用轮廓系数(Silhouette Coefficient)来确定聚类算法中最优的K值是一种评估聚类性能的方法。轮廓系数结合了聚类的密集程度和分离程度,为每个样本提供了一个度量值,范围从-1到1。轮廓系数越高,表明样本更适合其自身的聚类且不适合相邻的聚类,这意味着聚类效果更好。

# 导入所需的库
import numpy as np  # NumPy用于数值运算
import matplotlib.pyplot as plt  # Matplotlib用于绘图
from sklearn.cluster import KMeans  # 从sklearn库导入KMeans用于聚类
from sklearn.datasets import make_blobs  # make_blobs用于生成合成数据集
from sklearn.metrics import silhouette_score  # silhouette_score用于计算轮廓分数
from scipy.spatial.distance import cdist  # cdist用于计算点之间的距离

# 生成合成数据集,可以用自己的数据集替换这部分
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=1.0, random_state=42)

# 初始化一个列表用于存储不同k值的SSE(误差平方和)值
sse = []

# 初始化一个列表用于存储不同k值的轮廓分数
silhouette_scores = []

# 定义一个k值的范围进行尝试
k_values = range(2, 10)

# 对于k值范围内的每一个k,执行以下操作
for k in k_values:
    # 使用k个聚类中心初始化KMeans模型
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(X)  # 对数据集X进行拟合
    
    # 计算并添加当前k值的SSE
    sse.append(kmeans.inertia_)
    
    # 计算并添加当前k值的轮廓分数
    silhouette_avg = silhouette_score(X, kmeans.labels_)
    silhouette_scores.append(silhouette_avg)

# 绘制不同k值的SSE值图
plt.figure(figsize=(12, 6))  # 设置图形大小
plt.subplot(1, 2, 1)  # 定义子图布局
plt.plot(k_values, sse, 'bo-')  # 绘制SSE值曲线图
plt.title('Elbow Method For Optimal k')  # 添加标题
plt.xlabel('Number of clusters (k)')  # x轴标签
plt.ylabel('SSE')  # y轴标签

# 绘制不同k值的轮廓分数图
plt.subplot(1, 2, 2)  # 定义第二个子图布局
plt.plot(k_values, silhouette_scores, 'go-')  # 绘制轮廓分数曲线图
plt.title('Silhouette Method For Optimal k')  # 添加标题
plt.xlabel('Number of clusters (k)')  # x轴标签
plt.ylabel('Silhouette Score')  # y轴标签

plt.tight_layout()  # 调整子图布局
plt.show()  # 展示图形

改进:

1)距离的改进

K-Medians是另一种与K-Means有关的聚类算法,除了使用均值的中间值来重新计算组中心点以外,这种方法对离群值的敏感度较低(因为使用中值),但对于较大的数据集来说,它要慢得多,因为在计算中值向量时,每次迭代都需要进行排序。

K-Medoids也是对K-Means算法的进一步优化:1)限制聚类中心点必须来自数据点。求中心点的计算方法,由原来的直接计算重心,变成计算完重心后,在重心附近找一个数据点作为新的中心点。K-Medoids重拟合步骤比直接求平均的K-Means要复杂一些。2)为避免平方计算对离群点的敏感,把平方变成绝对值。

2) 初始点的改进

K-means++算法,主要解决了K-means算法在初始中心点选择上的问题。K-means++算法在选取初始中心点时采用了更合理的方法,其基本思想是:

  • 随机选择一个数据点作为第一个中心点。
  • 对于后续的中心点,选择的原则是距离已选中心点越远的数据点被选为下一个中心点的概率越高。
  • 重复上述过程,直到选出K个中心点。

2)  DB-SCAN算法

DB-SCAN是一个基于密度的聚类。如下图中这样不规则形态的点,如果用K-Means,效果不会很好。而通过DB-SCAN就可以很好地把在同一密度区域的点聚在一类中

聚类步骤

        1.DBSCAN以一个从未访问过的任意起始数据点开始。这个点的邻域是用距离ε(所有在ε距离的点都是邻点)来提取的。

        2.如果在这个邻域中有足够数量的点(根据 minPoints),那么聚类过程就开始了,并且当前的数据点成为新聚类中的第一个点。否则,该点将被标记为噪声(稍后这个噪声点可能会成为聚类的一部分)。在这两种情况下,这一点都被标记为“访问(visited)”。

        3.对于新聚类中的第一个点,其ε距离附近的点也会成为同一聚类的一部分。这一过程使在ε邻近的所有点都属于同一个聚类,然后重复所有刚刚添加到聚类组的新点。

        4.步骤2和步骤3的过程将重复,直到聚类中的所有点都被确定,就是说在聚类附近的所有点都已被访问和标记。

        5.一旦我们完成了当前的聚类,就会检索并处理一个新的未访问点,这将导致进一步的聚类或噪声的发现。这个过程不断地重复,直到所有的点被标记为访问。因为在所有的点都被访问过之后,每一个点都被标记为属于一个聚类或者是噪音。

        DBSCAN比其他聚类算法有一些优势。首先,它不需要一个预设定的聚类数量。它还将异常值识别为噪声,而不像均值偏移聚类算法,即使数据点非常不同,它也会将它们放入一个聚类中。此外,它还能很好地找到任意大小和任意形状的聚类。

        DBSCAN的主要缺点是,当聚类具有不同的密度时,它的性能不像其他聚类算法那样好。这是因为当密度变化时,距离阈值ε和识别邻近点的minPoints的设置会随着聚类的不同而变化。这种缺点也会出现在非常高维的数据中,因为距离阈值ε变得难以估计。

3)高斯混合模型

高斯混合模型(Gaussian Mixture Model,简称GMM)是一种统计学中的概率模型,用于表示由多个高斯分布(正态分布)混合组成的数据集合。其核心原理基于假设数据集中的每个数据点都是由多个潜在的高斯分布之一生成的,这些高斯分布的参数(如均值和方差)以及它们的权重(每个分布的贡献程度)是需要通过模型学习和估计的

高斯混合模型(GMM)在以下情况下通常效果更好:

  1. 簇的形状不是球形:GMM能够识别出椭圆形或者更复杂形状的簇,因为它考虑了数据的协方差结构。如果数据簇呈现出非球形的分布,GMM比K-means等基于距离的聚类方法更有效。

  2. 簇之间有重叠:当簇之间存在重叠时,GMM能够通过软聚类的特性为每个数据点分配属于每个簇的概率,从而更好地处理重叠区域的数据点。

  3. 簇的密度不同:GMM能够处理簇内数据点密度不同的情况,因为它不是简单地基于距离来聚类,而是基于概率密度函数。

  4. 需要概率解释:GMM提供了每个数据点属于每个簇的概率,这在某些应用中非常有用,例如在分类任务中作为特征,或者在需要量化不确定性的场景中。

算法步骤

1. 随机选择或基于某种启发式方法(如K-means聚类结果)初始化每个高斯分布的均值、方差和混合权重。

2. 根据当前的高斯分布参数,计算每个数据点属于每个高斯分布的后验概率(也称为责任或归属概率),即数据点由某个高斯分布生成的概率。

3. 使用E-step计算得到的后验概率来更新每个高斯分布的均值、方差和混合权重,使得数据的似然函数最大化。

4. 重复执行E-step和M-step,直到模型参数的变化达到预设的收敛条件(如对数似然函数的变化小于某个阈值)或达到预设的迭代次数。 

import numpy as np
import matplotlib.pyplot as plt
from sklearn.mixture import GaussianMixture
from sklearn.datasets import make_blobs

# 生成一些模拟数据
X, y = make_blobs(n_samples=400, centers=4, cluster_std=0.60, random_state=0)

# 绘制原始数据
plt.scatter(X[:, 0], X[:, 1], s=50)
plt.title("Original Data")
plt.show()

# 使用GMM模型拟合数据
gmm = GaussianMixture(n_components=4, random_state=0).fit(X)

# 预测每个点的簇标签
labels = gmm.predict(X)

# 绘制GMM的结果
plt.scatter(X[:, 0], X[:, 1], c=labels, s=50, cmap='viridis')
plt.title("Gaussian Mixture Model")
plt.show()

# 如果查看每个簇的均值和协方差,可以这样做:
print("Means:")
print(gmm.means_)
print("\nCovariances:")
print(gmm.covariances_)

# 预测新数据的簇标签
new_data = np.array([[0, 0], [4, 4], [-2, 2]])
new_labels = gmm.predict(new_data)
print("New data labels:", new_labels)

层次聚类算法

层次法(Hierarchicalmethods):先计算样本之间的距离。每次将距离最近的点合并到同一个类。然后,再计算类与类之间的距离,将距离最近的类合并为一个大类。不停的合并,直到合成了一个类。其中类与类的距离的计算方法有:最短距离法,最长距离法,中间距离法,类平均法等。比如最短距离法,将类与类的距离定义为类与类之间样本的最短距离。

层次聚类算法根据层次分解的顺序分为:自下向上和自上向下,即凝聚的层次聚类算法和分裂的层次聚类算法(agglomerative和divisive),也可以理解为自下而上法(bottom-up)和自上而下法(top-down)

层次式聚类方法的基本步骤如下:
1.     将每个对象归为一类, 共得到N类, 每类仅包含一个对象. 类与类之间的距离就是它们所包含的对象之间的距离.

2.     找到最接近的两个类并合并成一类, 于是总的类数少了一个.
3.     重新计算新的类与所有旧类之间的距离.
4.     重复第2步和第3步, 直到最后合并成一个类为止(此类包含了N个对象).
根据步骤3的不同, 可将层次式聚类方法分为几类: single-linkage, complete-linkage 以及 average-linkage 聚类方法等

Agglomerative Clutsering 是一种自下而上的层次聚类方法,它能够根据指定的相似度或距离定义计算出类之间的距离。(Hierarchical clustering两种方式的其中一种,另一种是divisive,自顶而下)依据对相似度(距离)的不同定义,将Agglomerative Clustering的聚类方法分为三种:Single-linkage,Complete-linkage和Group average.
- Single-linkage:要比较的距离为元素对之间的最小距离
- Complete-linkage:要比较的距离为元素对之间的最大距离
- Group average:要比较的距离为类之间的平均距离(平均距离的定义与计算:假设有A,B两个类,A中有n个元素,B中有m个元素。在A与B中各取一个元素,可得到他们之间的距离。将nm个这样的距离相加,得到距离和。最后距离和除以nm得到A,B两个类的平均距离。)

from sklearn.preprocessing import normalize
data_scaled = normalize(data)
data_scaled = pd.DataFrame(data_scaled, columns=data.columns)

import scipy.cluster.hierarchy as shc
plt.figure(figsize=(10, 7))  
plt.title("Dendrograms")  
dend = shc.dendrogram(shc.linkage(data_scaled, method='ward'))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值