聚类算法
聚类算法又叫做“无监督分类”,其目的是将数据划分成有意义或有用的组(或簇)。这种划分可以基于我们的业务需求或建模需求来完成,也可以单纯地帮助我们探索数据的自然结构和分布。
无监督学习:在进行模型训练的时候,只需要特征矩阵 X,不需要真实标签 Y
K-Means算法
算法过程
定义: 簇中所有数据的均值μ(j),通常被称为这个簇的质心(centroids),j表示第j个簇 在一个二维平面中,一簇数据点的质心的横坐标就是这一簇数据点的横坐标的均值,质心的纵坐标就是这一簇数据点的纵坐标的均值
算法过程: K-Means算法中,,簇的个数K是一个需要我们人为确定的参数 K-Means的核心任务就是根据我们设定好的K,找出K个最优的质心,并将离这些质心最近的数据分别分配到这些质心代表的簇中去 其中,距离的度量:欧氏距离或余弦相似度(需要标准化)
1
2
3
4
51. 随机添加K个样本作为最初的质心
2. 开始循环:
2.1 计算每个样本点到每个质心的距离,将他们分配到最近的质心,生成K个簇
2.2 在每个簇中,计算所有被分配到该簇的样本点的平均值并作为新的质心
3. 当质心的位置不再发生变化,循环结束,聚类完成
程序实现
详细文档:https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html#sklearn.cluster.KMeans
创建数据集
1 | from sklearn.datasets import make_blobs |
make_blobs
方法有两个返回值,第一个值表示生成的数据点,第二个值表示每个数据点的标签(或类别)(但是这在无监督学习中并没有用处)
K-Means聚类
- 重要参数
n_clusters
:要生成的簇数和要生成的质心数,即上文的K,默认值为81
2
3
4from sklearn.cluster import KMeans
# 训练x之后,完成循环,聚类结束
cluster = KMeans(n_clusters=4, random_state=0).fit(x)
注意:另外两个APIfit_predict
和predict
常用于大数据集的形式,假设1000万条数据,全部用KMeans().fit()
去一轮一轮的查找,效率极低。此时可以对数据集进行切片,切取小部分数据进行fit得到准确的质心,再将整个数据集放到predict
中进行预测
重要属性
labels_
:指聚类完成后,每个样本所对应的簇数1
2labels = cluster.labels_
print(labels)重要属性
cluster_centers_
:查看质心1
2centroid = cluster.cluster_centers_
print(centroid)重要属性
inertia_
:查看总距离平方和,聚类结果的一个评判依据,越小越好1
2inertia = cluster.inertia_
print(inertia)
通过inertia_
查看总距离平方和(total
inertia)。对于一个簇来说,所有的样本点到质心的距离之和越小,那么簇中的样本越相似,簇内的差异就越小。
但是当更改n_clusters
的时候,inertia会越来越小。实际上K-Means算法过程就是求解能够让
inertia
最小化的质心,而这样一来,改变了n_clusters
,同时会对聚类结果有影响。所以综上,inertia不是对聚类结果很好的一个评判依据。
- API
transform
:计算每个样本点到质心的距离。它将会返回一个矩阵,矩阵的每一行代表一个样本点到每个质心的距离。这个距离信息可以用来进行各种任务,例如降维、特征工程等。通常,在聚类任务中,1
distances = kmeans.transform(x)
transform
方法的输出可以作为新的特征,用于构建监督学习模型,或者用于其他机器学习任务。
- 数据可视化:
1
2
3
4plt.figure()
plt.scatter(x[:, 0], x[:, 1], c=labels)
plt.scatter(centroid[:, 0], centroid[:, 1], marker='x', s=100, c='black')
plt.show()
轮廓系数
由于是无监督学习,所以我们是对没有真实标签的数据进行探索,需要完全依赖于评价簇内的稠密程度(簇内差异小)和簇间的离散程度(簇外差异大)来评估聚类的效果,所以引入了轮廓系数这个评判依据
某样本的轮廓系数计算公式:
其中,a是该样本点与其簇内其他所有样本点的平均距离,b是该样本点与另一个最近的簇内所有样本点的平均距离。轮廓系数的计算结果是(-1,1)
,其中:
-
值越接近1,表示样本与自己所在的簇中的样本很相似,并且与其他簇中的样本不相似。
-
当轮廓系数为0时,则代表两个簇中的样本相似度一致,两个簇本应该是一个簇。
-
如果许多样本点具有低轮廓系数甚至负值,则聚类是不合适的聚类的超参数K可能设定得太大或者太小。
在sklearn中,使用silhouette_score
计算所有样本的轮廓系数的均值,使用silhouette_sample
计算每个样本自己的轮廓系数
1
2
3
4
5
6
7
8
9
10from sklearn.metrics import silhouette_score
from sklearn.metrics import silhouette_samples
# silhouette_score
result_score = silhouette_score(x,labels)
print(result_score)
# silhouette_samples
result_samples = silhouette_samples(x,labels)
print(result_samples)
因此我们可以通过循环K值的方式找到最合适的簇数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 利用轮廓系数检验簇数(或质心数)
# 方式一:使用silhouette_score
inertia_list = []
for i in range(3,10):
cluster = KMeans(n_clusters=i,random_state=0).fit(x)
y_pred = cluster.labels_
result_score = silhouette_score(x, y_pred)
inertia_list.append(result_score)
print(inertia_list)
# 方式二:使用silhouette_samples
for i in range(3,10):
cluster = KMeans(n_clusters=i,random_state=0).fit(x)
y_pred = cluster.labels_
result_samples = silhouette_samples(x, y_pred)
print(result_samples)使用可视化工具可以更直观地找到拐点
1 | plt.figure() |
可见,当K=4的时候,轮廓系数接近1,聚类结果最好
DBSCAN算法
算法过程
- 定义: DBSCAN 是一种基于密度的聚类算法,这类密度聚类算法一般假定类别可以通过样本分布的紧密程度决定。通过将紧密相连的样本划为一类,这样就得到了一个聚类类别。通过将所有各组紧密相连的样本划为各个不同的类别,则我们就得到了最终的所有聚类类别结果。
以下是需要了解的基本概念,在DBSCAN中需要人为指定的参数有半径ϵ和阈值MinPts: 1. ϵ-邻域:样本x以ϵ为半径的范围内包含的所有样本的集合,这个集合称为x的ϵ-邻域。 2. 核心对象:样本x的ϵ-领域内样本数大于阈值MinPts,则称x为核心对象。
对于样本点,我们可以分为3类: 1. 核心点:核心对象内部的点 2. 边缘点:核心对象内部但没有后继的点 3. 噪音点:不在任何核心对象内部的点
- 算法过程:
1
2
3
4
5
6
7
8
9
10
11将所有的样本标记为未访问状态
loop
随机选择一个未访问对象p,将其标记为已访问
如果p为核心对象,创建一个新簇C,将p的ϵ-邻域添加到C中
对于C中的每一个样本q,如果q为核心对象,遍历其ϵ-邻域内的所有样本x
loop
if x未被访问,则标记为已访问,将x添加到C中
else x已被访问但是不属于任何一个簇,将x添加到C中
end
end
输出所有簇
程序实现
详细文档:https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html#sklearn.cluster.DBSCAN
创建数据集
我们可以通过make_circles
或者make_moons
生成有特殊形状的数据集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20from sklearn.datasets import make_circles
from sklearn.datasets import make_moons
import matplotlib.pylab as plt
x1, y1 = make_circles(
n_samples=3000, # 样本数
factor=0.3, # 内环和外环的比例因子,越小越分离
noise=0.05, # 噪声值,越小越向心
random_state=666) # 随机数种子,保证每次创建的数据集一样
x2, y2 = make_moons(
n_samples=3000,
noise=0.05, # 噪声值,越小则两条线越清晰
random_state=666)
plt.subplot(1, 2, 1)
plt.title('make_circles')
plt.scatter(x1[:, 0], x1[:, 1], s=8)
plt.subplot(1, 2, 2)
plt.title('make_moons')
plt.scatter(x2[:, 0], x2[:, 1], s=8)
plt.show()
DBSCAN聚类
DBSCAN
有两个重要参数:eps
:邻域半径;min_samples
:一个核心对象应该拥有的最少样本数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21from sklearn.cluster import DBSCAN
from sklearn.cluster import KMeans
# 使用DBSCAN聚类
y_pred1 = DBSCAN(eps=0.2, min_samples=5).fit_predict(x1)
# 使用KMeans聚类
cluster = KMeans(n_clusters=2)
y_pred2 = cluster.fit_predict(x1)
centroid = cluster.cluster_centers_
# 数据可视化
plt.figure()
plt.subplot(1, 2, 1)
plt.title('DBSCAN')
plt.scatter(x1[:, 0], x1[:, 1], c=y_pred1)
plt.subplot(1, 2, 2)
plt.title('K-Means')
plt.scatter(x1[:, 0], x1[:, 1], c=y_pred2)
plt.scatter(centroid[:, 0], centroid[:, 1], marker='x', s=100, c='black')
plt.show()
其余参数详见文档
可见,K-Means与DBSCAN的区别:K-Means只能用于具有明确定义的质心(比如均值或中位数)的数据;DBSCAN要求密度定义(基于传统的欧几里得密度概念)对于数据是有意义的。
- 重要属性
labels_
:指聚类完成后,每个样本所对应的簇数其中,-1代表该点属于噪音点1
2db_labels_ = DBSCAN(eps=0.3, min_samples=10).fit(x).labels_
print(db_labels_)
聚类练习:鸢尾花
- 数据集探索
- 导包
1
2
3
4
5
6
7import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler - 加载数据集
iris也称鸢尾花卉数据集,是一类多重变量分析的数据集,常用于分类实验。数据集包含150个数据样本,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。
1
2
3
4
5
6
7
8
9
10
11
12
13# 加载数据集
iris_data = load_iris()
# load_iris()返回的是一个 Bunch 对象,与字典非常相似,里面包含键和值
print(iris_data.keys())
# 鸢尾花的三个品种:setosa,versicolor,virginica
target_names = iris_data.target_names
print(target_names)
# 鸢尾花的四个属性:花萼长度,花萼宽度,花瓣长度,花瓣宽度
feature_names = iris_data.feature_names
print(feature_names)1
2
3# 获取每朵花的属性数据
x = iris_data.data
print(x)
不同特征之间往往具有不同的量纲,由此所造成的数值间的差异可能很大,在涉及空间距离计算或梯度下降法等情况的时候不对其进行处理会影响到数据分析结果的准确性。为了消除特征之间的量纲和取值范围差异可能会造成的影响,需对数据进行标准化处理,也可以称为规范化处理。
1
2
3# 数据预处理:标准差标准化
MMS = MinMaxScaler().fit(x)
data = MMS.transform(x)1
2
3
4
5
6
7
8# 3.构建KMeans模型训练数据
cluster = KMeans(n_clusters=3,random_state=10,n_init=10).fit(data)
# 3.1 获取聚类结果
y_pred = cluster.labels_
# 3.2 获取质心
centers = cluster.cluster_centers_
# 3.3 查看簇内平方和
inertia = cluster.inertia_
由于数据集是四维的(即四个特征),我们需要对其进行降维处理,目标是降到二维平面使用散点图进行数据可视化。这里我们使用TSNE降维法:
1
2
3
4
5
6# 4.聚类结果可视化
from sklearn.manifold import TSNE
# 进行数据降维处理
tsne = TSNE(n_components=2,init='random',random_state=0).fit(data)
df = pd.DataFrame(tsne.embedding_)
print(df)1
2
3
4# 绘制散点图
plt.figure(figsize=(8,6))
plt.scatter(df[0],df[1],c=y_pred,s=8)
plt.show()
- 聚类模型评估
- 轮廓系数
1
2
3
4
5
6
7
8
9
10
11
12# 5.聚类模型评估
# 5.1轮廓系数
from sklearn.metrics import silhouette_score
silhouetteScore = []
for i in range(2,10):
# 构建并训练模型
kmeans = KMeans(n_clusters=i,random_state=0,n_init=10).fit(data)
score = silhouette_score(data,kmeans.labels_)
silhouetteScore.append(score)
plt.figure(figsize=(8,6))
plt.plot(range(2,10),silhouetteScore,linewidth=1.5,linestyle='-')
plt.show()
- 卡林斯基 - 哈拉巴斯指数
1
2
3
4
5
6
7
8
9
10
11# 5.2 卡林斯基-哈拉巴斯指数
from sklearn.metrics import calinski_harabasz_score
chs = []
for i in range(2,10):
# 构建聚类模型
kmeans = KMeans(n_clusters=i,random_state=0,n_init=10).fit(data)
chsScore = calinski_harabasz_score(data,kmeans.labels_)
chs.append(chsScore)
plt.figure(figsize=(10, 8))
plt.plot(range(2, 10), chs, linewidth=1.5, linestyle='-')
plt.show()
- FMI评价法
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 5.3 FMI评价法
# 注意:需要有真实标签
y = iris_data.target
from sklearn.metrics import fowlkes_mallows_score
fms = []
for i in range(2,10):
# 构建聚类模型
kmeans = KMeans(n_clusters=i,random_state=0,n_init=10).fit(data)
fmsScore = fowlkes_mallows_score(y,kmeans.labels_)
fms.append(fmsScore)
plt.figure(figsize=(10, 8))
plt.plot(range(2, 10), fms, linewidth=1.5, linestyle='-')
plt.show()
- 完整代码