发布时间:2023-12-08 17:00
前言
在上篇《 Python 机器学习实战 —— 无监督学习(上)》介绍了数据集变换中最常见的 PCA 主成分分析、NMF 非负矩阵分解等无监督模型,举例说明使用使用非监督模型对多维度特征数据集进行降维的意义及实用方法。对 MDS 多维标度法、LLE 局部线性嵌入法、Isomap 保距映射法、t-SNE 分布邻域嵌入算法等 ML 流形学习模型的基础使用方法进行讲解。
本文将对聚类算法进行讲解,聚类算法就是将数据集划分成组的任务,这些组叫成簇,同一个簇内的数据点特征非常相似,不同簇内的数据点特征区别很大,这点与监督学习中的分类算法很类似,运行完成后系统会为同一簇内的数据分配同一个数字,不同簇的数字都不一样。常见的聚类模型有 KMeans、DBSCAN、GMM 、Agglomerative 等,下面将一一介绍。
目录
一、PCA 主成分分析
二、NMF 非负矩阵分解
三、ML 流形学习
四、KMeans 均值聚类
五、GMM 高斯混合模型
六、Agglomerative 凝聚聚类
七、DBSCAN 密度聚类
4.1 KMeans 的基本原理
KMeans 均值聚类是最简单最常用的聚类算法之一,它会尝试找到代表数据区域的簇中心,并保存在 cluster_centers 属性中。再把每个数据点分配给最接近的簇中心,并把每个簇中心设置为所分配数据点的平均值。当簇中心的值根据模型运算设置不再发生变化时,算法结束。
构造函数
1 class KMeans(TransformerMixin, ClusterMixin, BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, n_clusters=8, *, init='k-means++', n_init=10, 4 max_iter=300, tol=1e-4, precompute_distances='deprecated', 5 verbose=0, random_state=None, copy_x=True, 6 n_jobs='deprecated', algorithm='auto'):
常用参数
4.2 KMeans 的应用场景
下面先用简单的 make_blobs 数据集了解 KMeans 的基础使用方式,KMeans 模型在分别把簇中心设置为 3 和 5,观察一下数据的变化。注意测试数据集中默认为 3 类,然而 KMeans 是无监督学习模型,不会使用数据集给出的结果进行分类,而是按照 n_clusters 的设置进行分类。这是 KMeans 模型的优点也可以说是缺点,虽然没有受数据集类型的限制,然而在运行前必须先设置簇的数量。实事上在现实场景中,很多数据事先是无法确定簇数量的。
1 def kmean_test(n): 2 #生成数据集 3 X, y = datasets.make_blobs(n_samples=100, n_features=2, random_state=1) 4 #使用KMeans模型,3个簇中心 5 kmean=KMeans(n_clusters=n) 6 model=kmean.fit_predict(X) 7 #显示运算结果 8 plt.scatter(X[:,0],X[:,1],s=50,c=model,marker='^') 9 plt.xlabel('feature0') 10 plt.ylabel('feature1') 11 #显示簇中心 12 center=kmean.cluster_centers_ 13 plt.scatter(center[:,0],center[:,1],s=200,color='red',marker='.') 14 #显示簇边界 15 radii=[cdist(X[model==i],[center]).max() for i,center in enumerate(center)] 16 for c,r in zip(center,radii): 17 axes.add_patch(plt.Circle(c,r,alpha=0.3,zorder=1)) 18 plt.show() 19 20 if __name__=='__main__': 21 kmean_test(3) 22 kmean_test(5)
3 簇中心运行结果
5 簇中心运行结果
下面例子尝试利用 digits 数据集,通过 KMeans 训练,再查看一下分辨手写数字的准确率,可见单凭 KMeans 模型已经可以分辨手写数字的准确率到达将近80% 。
1 def kmean_test(): 2 # 输入测试数据 3 digits=datasets.load_digits() 4 #使用KMeans模型,10个簇中心 5 kmean=KMeans(10) 6 #训练数据 7 model=kmean.fit_predict(digits.data) 8 #计算匹配度 9 labels=np.zeros_like(model) 10 for i in range(10): 11 mask=(model==i) 12 labels[mask]=mode(digits.target[mask])[0] 13 #计算准确率 14 acc=accuracy_score(digits.target,labels) 15 print(acc)
运行结果
还记得在上一篇文章《 Python 机器学习实战 —— 无监督学习(上)》介绍 ML 流形学习中曾经提过, t-SNE 模型是一个非线性嵌入算法,特别擅长保留簇中的数据点。在此尝试把 KMeans 与 t-SNE 相结合一起使用,有意想不到的效果。
运行后可以发现末经过调试的算法,准确率已经可以达到 94% 以上。可见,只要适当地运用无监督学习,也能够精准地实现数据的分类。
1 def kmean_test(): 2 # 输入测试数据 3 digits=datasets.load_digits() 4 #使用t-SNE 模型进行训练 5 tsne=TSNE() 6 model0=tsne.fit_transform(digits.data) 7 #使用KMeans模型进行训练 8 kmean=KMeans(10) 9 model1=kmean.fit_predict(model0) 10 #计算匹配度 11 labels=np.zeros_like(model1) 12 for i in range(10): 13 mask=(model1==i) 14 labels[mask]=mode(digits.target[mask])[0] 15 #计算准确率 16 acc=accuracy_score(digits.target,labels) 17 print(acc)
运行结果
4.3 通过核转换解决 KMeans 的非线性数据问题
虽然 KMeans 模型适用的场景很多,然而 KMeans 也有一个问题就是它只能确定线性聚类边界,当簇中心点呈现非线性的复杂形状时,此算法是不起作用的。例如以 make_moons 数据集为例,把 noise 参数设置为 0.04,用 KMeans 模型进行测试。分别把 n_clusters 设置为 2 和 10,运行一下程序,可见单凭 KMeans 模型是无法分开复杂形状的非线性数据的。
1 def kmean_test(n): 2 #生成数据集 3 X, y = datasets.make_moons(n_samples=300, noise=0.04,random_state=1) 4 #使用KMeans模型,n个簇中心 5 kmean=KMeans(n_clusters=n) 6 model=kmean.fit_predict(X) 7 #显示运算结果 8 plt.scatter(X[:,0],X[:,1],s=50,c=model,marker='^') 9 plt.xlabel('feature0') 10 plt.ylabel('feature1') 11 #显示簇中心 12 center=kmean.cluster_centers_ 13 plt.scatter(center[:,0],center[:,1],s=200,color='red',marker='.') 14 plt.show() 15 16 if __name__=='__main__': 17 kmean_test(2) 18 kmean_test(10)
n_clusters 为 2 时
n_clusters 为 10 时
此时,大家可能会想起在上一章《 Python 机器学习实战 —— 监督学习(下)》中提到的核函数技巧,类似地 skLearn 也提供了 SpectralClustering 评估器来完成 KMeans 非线性边界的问题。
构造函数
1 class SpectralClustering(ClusterMixin, BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, n_clusters=8, *, eigen_solver=None, n_components=None, 4 random_state=None, n_init=10, gamma=1., affinity='rbf', 5 n_neighbors=10, eigen_tol=0.0, assign_labels='kmeans', 6 degree=3, coef0=1, kernel_params=None, n_jobs=None, 7 verbose=False):
尝试 SpectralClustering 评估器来完成非线性数据的分类问题,把 affinity 设置 'nearest_neighbors‘ 使用 k 近邻算法内核,assign_labels 设置为 'kmeans' 策略。从运行结果可以看到,SpectralClustering 使用 k 近邻内核完美地实现了数据边界的分离。
1 def kmean_test(): 2 #生成数据集 3 X, y = datasets.make_moons(n_samples=300, noise=0.04,random_state=1) 4 #使用SpectralClustering评估器 5 spectral=SpectralClustering(n_clusters=2,affinity='nearest_neighbors',assign_labels='kmeans') 6 model=spectral.fit_predict(X) 7 #显示运算结果 8 plt.scatter(X[:,0],X[:,1],s=50,c=model,marker='^') 9 plt.xlabel('feature0') 10 plt.ylabel('feature1') 11 plt.show()
运行结果
回到目录
5.1 GMM 的基本原理
由于 KMeans 模型是围绕着簇中心而计算的,造成当中存在短板,数据点分配是依赖簇中心到数据点的平均值的,因此簇的模型必须是圆形的(在上节的例子中可以看出)。当簇中心比较接近时,数据点分配就会发生重叠而引起混乱。
为解决 KMeans 模型的问题,GMM 高斯混合模型应运而生,它会改 KMeans 模型簇边界的计算方式,把圆形改成椭圆形,让数据边界更明显。
构造函数
1 class GaussianMixture(BaseMixture): 2 @_deprecate_positional_args 3 def __init__(self, n_components=1, *, covariance_type='full', tol=1e-3, 4 reg_covar=1e-6, max_iter=100, n_init=1, init_params='kmeans', 5 weights_init=None, means_init=None, precisions_init=None, 6 random_state=None, warm_start=False, 7 verbose=0, verbose_interval=10):
参数说明
方法说明
继续使用 make_blobs 数据集进行测试,适当修改数据点之间的间距,使用 GMM 模型进行计算,将为每个数据点找到对应每个簇的概率作为权重,然后更新每个簇的位置,将其标准化,最后把所有数据点的权重来确定形状。如此一来,数据点跟预期一样更为集中,数据边界会根据数据点的分布形成椭圆形。
1 def draw_ellipse(position, covariance, ax=None, **kwargs): 2 # 计算图形边界 3 if covariance.shape == (2, 2): 4 U, s, Vt = np.linalg.svd(covariance) 5 angle = np.degrees(np.arctan2(U[1, 0], U[0, 0])) 6 width, height = 2 * np.sqrt(s) 7 else: 8 angle = 0 9 width, height = 2 * np.sqrt(covariance) 10 # 画图 11 for nsig in range(1, 4): 12 ax.add_patch(Ellipse(position, nsig * width, nsig * height, 13 angle, **kwargs)) 14 15 def gmm_test(): 16 #测试数据集 17 X, y = datasets.make_blobs(n_samples=100,centers=4,n_features=2,random_state=1) 18 #修改数据点间距 19 rng=np.random.RandomState(12) 20 X1=np.dot(X,rng.randn(2,2)) 21 fig,axes=plt.subplots(1,1) 22 #使用GMM模型 23 gmm=GaussianMixture(n_components=4,random_state=28) 24 model=gmm.fit_predict(X1) 25 #显示运算结果 26 plt.scatter(X1[:,0],X1[:,1],s=50,c=model,marker='^') 27 plt.xlabel('feature0') 28 plt.ylabel('feature1') 29 #显示簇边界 30 n=0.2/gmm.weights_.max() 31 for mean,covar,weight in zip(gmm.means_,gmm.covariances_,gmm.weights_): 32 draw_ellipse(mean,covar,ax=axes,alpha=weight*n) 33 plt.show() 34 35 if __name__=='__main__': 36 gmm_test()
运行结果
回到目录
Agglomerative 凝聚聚类算法首先会声明每个点都是一个簇,然后合并两个最相似的簇,直到满足设定条件准则为止。这个准则可以通过 linkage 参数确定,linkage 为 { 'ward', 'complete', 'average', 'single' } 之一,下面将会详细说明。
观察下面系统自带的合并过程,当运行到第4步后,两点最相近的簇完成合并,到第5步一个两点簇增加到三个点,当三个点的簇完成合并后,到第8步,五点的簇开始合并,如此类推,到最后完成合并任务。
构造函数
1 class AgglomerativeClustering(ClusterMixin, BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, n_clusters=2, *, affinity="euclidean", 4 memory=None, 5 connectivity=None, compute_full_tree='auto', 6 linkage='ward', distance_threshold=None, 7 compute_distances=False):
使用 make_blobs 数据集作测试,把 n_cluster3 设置为 3,由于 AgglomerativeClustering 没有 predict 方法,可以直接使用 fit_predict 方法进行计算。完成计算后,可以利用 SciPy 的 dendrogram 函数绘制树状图,以观察簇的构建过程。
1 def agglomerative_test(): 2 # 测试数据集 3 X,y=make_blobs(random_state=1) 4 # 使用 AgglomerativeClustering模型 5 agglomerative=AgglomerativeClustering(n_clusters=3) 6 # 开始运算 7 model=agglomerative.fit_predict(X) 8 # 显示簇合并结果 9 fig, axes = plt.subplots(1, 2, figsize=(10,5)) 10 ax0=axes[0] 11 ax0.scatter(X[:,0],X[:,1],s=50,c=model,marker='^') 12 # 显示树状图 13 ax1=axes[1] 14 shc.dendrogram(shc.ward(X[::5])) 15 plt.show()
运行结果
回到目录
DBSCAN 密度聚类是最常用的模型之一,一般用于分析形状比较复杂的簇,还可找到不属于任何簇的数据点。上面介绍到的 KMeans 模型、SpectralClustering 评估器、Agglomerative 模型都需要在建立模型前通过 n_clusters 参数预先设置簇的个数,然而在复杂的数据中,单凭简单的数据分析去正确评估簇数据是很困难的。 DBSCAN 的优点在于它不需要预先设置簇的个数,而是通过特征空间的数据点密度来区分簇。DBSCAN 最常用到的参数是 eps 和 min_samples,eps 是用于设定距离,DBSCAN 将彼此距离小于 eps 设置值的核心样本放在同一个簇。min_samples 则用于设置同一簇内数据点的最少数量,如果数据点数量少于此设置则把这此点默认为噪点 noise,数据点数量大于等于此数值时则默认一个簇。
1 class DBSCAN(ClusterMixin, BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, eps=0.5, *, min_samples=5, metric='euclidean', 4 metric_params=None, algorithm='auto', leaf_size=30, p=None, 5 n_jobs=None):
使用 make_blobs 数据集,分别把 eps 设置为 0.5,1.0,1.5,观察一下测试结果,紫蓝色的是噪点。随着 eps 的增大,噪点越来越少,测试结果也越来越接近真实数据。然而 eps 对结果的影响也很大,如果 eps 设置得过大,可能会导致所有数据点形成同一个簇,如果 eps 太小,可能会导致所有数据点都是噪点。
1 def dbscan_test(eps,title): 2 # 测试数据集 3 X,y=datasets.make_blobs(random_state=1) 4 # 使用DBSCAN,输入eps参数 5 dbscan=DBSCAN(eps=eps) 6 model=dbscan.fit_predict(X) 7 # 显示测试后结果 8 plt.scatter(X[:,0],X[:,1],c=model,s=50,marker='^') 9 plt.xlabel('feature0') 10 plt.ylabel('feature1') 11 plt.title(title) 12 plt.show() 13 14 if __name__=='__main__': 15 dbscan_test(0.5,'EPS:0.5 ') 16 dbscan_test(1,'EPS 1') 17 dbscan_test(1.5,'EPS 1.5')
运行结果
eps = 0.5
eps = 1.0
eps = 1.5
使用相同数据集,把 eps 设置为1.0,尝试修改 min_samples 参数,看看参数对模型的影响,紫蓝色为噪点。随着 min_samples 的减少,测试结果也越来越接近真实数据。
1 def dbscan_test(min,title): 2 # 测试数据集 3 X,y=datasets.make_blobs(random_state=1) 4 # 使用DBSCAN,输入eps,min_samples参数 5 dbscan=DBSCAN(eps=1.0,min_samples=min) 6 model=dbscan.fit_predict(X) 7 # 显示测试后结果 8 plt.scatter(X[:,0],X[:,1],c=model,s=50,marker='^') 9 plt.xlabel('feature0') 10 plt.ylabel('feature1') 11 plt.title(title) 12 plt.show() 13 14 if __name__=='__main__': 15 dbscan_test(20,'MIN SAMPLES 20') 16 dbscan_test(10,'MIN SAMPLES 10') 17 dbscan_test(5,'MIN SAMPLES 5')
运行结果
min_samples = 20
min_samples = 10
min_samples = 5
第四节介绍 KMeans 模型时曾经介绍到 KMeans 模型是无法分开复杂形状的非线性数据的,只能用到 SpectralClustering 评估器来解决此类问题。然而 DBSCAN 模型的密度分布算法侧可以轻松地解决此类问题,同样使用 make_moons 数据集,使用 DBSCAN 模型时把 eps 设置为0.4,min_samples 设置为30。通过测试结果可以看出,只需要利用 DBSCAN 模型最常用的参数配置就可以解决复杂的非线性数据问题。
1 def dbscan_test(title): 2 # 测试数据集 3 X, y = datasets.make_moons(n_samples=300, noise=0.04,random_state=1) 4 # 使用DBSCAN,输入eps,min_samples参数 5 dbscan=DBSCAN(eps=0.4,min_samples=30) 6 model=dbscan.fit_predict(X) 7 # 显示测试后结果 8 plt.scatter(X[:,0],X[:,1],c=model,s=50,marker='^') 9 plt.xlabel('feature0') 10 plt.ylabel('feature1') 11 plt.title(title) 12 plt.show() 13 14 if __name__=='__main__': 15 dbscan_test('DBSCAN')
运行结果
在前一章《 Python 机器学习实战 —— 无监督学习(上)》中介绍到的 PCA 模型, 下面这个例子就是先利用PCA模型对数据特征进行降维处理,然后再使用 DBSCAN 找出噪点,观察一下噪点与簇数据的区别出自哪里。使用 fetch_lfw_person 数据集,利用 PCA 模型把主要成分保持在 95%,完成训练后再使用 DBSCAN 模型,把 eps 设置为 23,min_samples 设置 3 ,再进行训练。
最后把噪点通过主要成分还原数据进行显示,从运行结果可以看到,噪点主要是因为人物有带帽子,张开嘴巴,用手捂嘴等动作造成的。 通过噪点分析,能找出很多有趣的与别不同的特征。
1 def dbscan_test(): 2 # 测试数据集 3 person = datasets.fetch_lfw_people() 4 # 使用PCA模型把特征降至60 5 pca=PCA(0.95,whiten=True,random_state=1) 6 pca_model=pca.fit_transform(person.data) 7 # 使用DBSCAN,输入eps,min_samples参数 8 dbscan=DBSCAN(eps=23,min_samples=3) 9 model=dbscan.fit_predict(pca_model) 10 # 显示测试后结果 11 fig,axes=plt.subplots(5, 5, figsize=(25,25)) 12 # 获取噪点的特征成分图 13 noise=pca_model[model==-1] 14 # 输出噪点数量 15 print(str.format('noise len is {0}'.format(len(noise)))) 16 for componemt,ax in zip(noise,axes.ravel()): 17 # 获取噪点提取成分后还原图 18 image=pca.inverse_transform(componemt) 19 ax.imshow(image.reshape(62, 47), cmap = 'viridis') 20 plt.show()
运行结果
回到目录
本章总结
本章主要介绍了 KMeans、GMM 、Agglomerative 、DBSCAN 等模型的使用,KMeans 是最常用最简单的模型,它尝试根据 n_clusters 设置找到代表数据区域的簇中心。而 GMM 可以看成是升级版的 KMeans ,它会改 KMeans 模型簇边界的计算方式,把圆形改成椭圆形,让数据边界更明显。Agglomerative 则更类似于树模型,使用近邻合并的模型,把相近的数据点合并为簇。DBSCAN 是更智能化的模型,通过数据点的聚集程度判断簇中心,在没有设置固定 n_clusters 的情况下分配出符合实际情况的簇。
对机器学习的原理及应用场景介绍到这里结束,后面将开始讲述深度学习的相关内容,敬请留意。
希望本篇文章对相关的开发人员有所帮助,由于时间仓促,错漏之处敬请点评。
对 .Python 开发有兴趣的朋友欢迎加入QQ群:790518786 共同探讨 !
对 JAVA 开发有兴趣的朋友欢迎加入QQ群:174850571 共同探讨!
对 .NET 开发有兴趣的朋友欢迎加入QQ群:162338858 共同探讨 !
AI 人工智能相关文章
Python 机器学习实战 —— 监督学习(上)
Python 机器学习实战 —— 监督学习(下)
Python 机器学习实战 —— 无监督学习(上)
Python 机器学习实战 —— 无监督学习(下)
Tensorflow 2.0 深度学习实战——介绍损失函数、优化器、激活函数、多层感知机的实现原理
TensorFlow 2.0 深度学习实战 —— 浅谈卷积神经网络 CNN
作者:风尘浪子
Python 机器学习实战 —— 无监督学习(下) - 风尘浪子 - 博客园
原创作品,转载时请注明作者及出处