无监督学习:
【机器学习】使用scikitLearn对数据进行聚类:Kmeans聚类算法的应用及密度聚类DBSCAN
【机器学习】使用scikitLearn对数据进行聚类:高斯聚类GaussianMixture
【机器学习】使用scikitLearn对数据进行聚类:7种异常和新颖性检测方式
聚类是典型的无监督学习的一种,它将相似的元素聚集在一起。
聚类的应用有很多,比如降维,将一群实例点集聚成K类,每个实例点求对k类的相似度,组成一个k维的向量,如果k小于原来实例的维度,则实现了降维。
当用于异常检测时,也叫离群值检测,请注意,异常值检测时,训练集中会包含异常值,而新颖性检测时,默认训练集都是正常值。
如果使用有标签的算法进行异常值检测,由于异常值极少,类别不平衡,算法很难学习到异常值特征,故一般使用聚类算法,其具体思路是:
轮廓系数较小的实例点或者距离聚类中心最远的点,将其作为离群值点进行关注。
新颖性检测,常用SVM算法,其默认训练数据中没有新颖点。
半监督学习,使用聚类技术,使已有的少数标签数据,在实例中进行扩散。
搜索引擎,先聚类,构造聚类模型,再将原始图片进行聚类操作,聚集出k类,新图片到来时,再用聚类模型对图片进行分类,返回最近类别的全部图片。
KMean算法:
算法的特点是简单,并且高效。
其基本代码为:
from sklearn.cluster import KMeans
k = 5
kmeans = KMeans(n_clusters=k, random_state=42)
y_pred = kmeans.fit_predict(X)
下面查看聚类中心:
kmeans.cluster_centers_
预测新类别:
X_new = np.array([[0, 2], [3, 2], [-3, 3], [-3, 2.5]])
kmeans.predict(X_new)
也可通过计算实例与各个计算中心间距的方法,实现“软投票”。
代码如下:
kmeans.transform(X_new)
通过初始化中心、计算每个集群实例的平均坐标值确定新的中心点,再次将每个实例分配给最近的中心点,算法受随机初始化的中心点影响较大,故多次初始化中心点运行算法吗,取最优值:
#5个聚类中心,运行算法10次,取最优值。
kmeans_rnd_10_inits = KMeans(n_clusters=5, init="random", n_init=10,
algorithm="full", random_state=2)
kmeans_rnd_10_inits.fit(X)
上述代码中, n_init=10为默认值,init="random"为随机选定的聚类中心,其不是默认值,默认情况下,kmeans对聚类中心的选择,是按某种概率分布,使初始聚类中心之间的间距尽可能远。algorithm=“full”,为原始算法,elkan K-Means改进算法可以减少原始算法的计算量,默认的auto则会根据数据值是否是稀疏的,来决定如何选择full和elkan。
最优的评价标准是惯性,即每个实例距离其最近的聚类中心的均方距离之和,该值越小越好:
kmeans.inertia_
如果有大致的聚类中心,也可以直接给定:
good_init = np.array([[-3, 3], [-3, 2], [-3, 1], [-1, 2], [0, 2]])
kmeans = KMeans(n_clusters=5, init=good_init, n_init=1, random_state=42)
kmeans.fit(X)
kmeans.inertia_
小批量kmeans的出现降低了内存的占用,使得同一模型可以分批次进行数据训练。
from sklearn.cluster import MiniBatchKMeans
minibatch_kmeans = MiniBatchKMeans(n_clusters=5, random_state=42)
#小批量,每次训练时不使用全部数据,是内部将X拆开了,如果调用partial_fit,是将传递给
#partial_fit的全部X(其实是整体的一部分)进行训练
minibatch_kmeans.fit(X)
同样的,如果使用memmap进行内存映射,如同在PCA降维中介绍过的,需要指定batchsize,直接使用fit()进行操作:
filename = "my_mnist.data"
X_mm = np.memmap(filename, dtype='float32', mode='write', shape=X_train.shape)
#其实也需要将X_train的数据一次性加载进内存,只不过持久化后训练时分次使用时不再占内存
X_mm[:] = X_train
minibatch_kmeans = MiniBatchKMeans(n_clusters=10, batch_size=10, random_state=42)
minibatch_kmeans.fit(X_mm)
最佳聚类中心数确定:
由于聚类算法的特殊性,惯性会随聚类数的增加持续下降,直到聚类数与实例数相同。
常采用确定聚类中心数目的方法有两个,肘部法及轮廓法:
肘部法,通过作图,找出惯性系数减小趋向平缓的点:
kmeans_per_k = [KMeans(n_clusters=k, random_state=42).fit(X)
for k in range(1, 10)]
inertias = [model.inertia_ for model in kmeans_per_k]
plt.figure(figsize=(8, 3.5))
plt.plot(range(1, 10), inertias, "bo-")
plt.axis([1, 8.5, 0, 1300])
plt.show()
看起来k为4时,能取得一个较优的聚类中心数目。
轮廓系数法:
实例的轮廓系数等于(b-a)/max(a,b),其中a是与同一集群中其他实例的平均距离(即集群内平均距离),b是平均最近集群距离(即到下一个最近集群实例的平均距离)。轮廓系数可以在-1和+1之间变化,极端值在a取0及b取0时取得,轮廓系数越接近于1越好。
计算代码如下:
from sklearn.metrics import silhouette_score
kmeans_per_k = [KMeans(n_clusters=k, random_state=42).fit(X)
for k in range(1, 10)]
#kmeans.labels_为所属索引,不是标签
silhouette_score(X, kmeans.labels_)
silhouette_scores = [silhouette_score(X, model.labels_)
for model in kmeans_per_k[1:]]
#绘图
plt.figure(figsize=(8, 3))
plt.plot(range(2, 10), silhouette_scores, "bo-")
plt.xlabel("$k$", fontsize=14)
plt.ylabel("Silhouette score", fontsize=14)
plt.axis([1.8, 8.5, 0.55, 0.7])
plt.show()
可以看出4是比较好的选择。
进一步,结合每类实例数,以及总体轮廓系数,可以绘制轮廓图:
#silhouette_samples返回所有样本的轮廓系数,silhouette_score只返回自己的
from sklearn.metrics import silhouette_samples
from matplotlib.ticker import FixedLocator, FixedFormatter
plt.figure(figsize=(11, 9))
#可能的轮廓系数取值:
for k in (3, 4, 5, 6):
plt.subplot(2, 2, k - 2)
#kmeans算法运行后提供的标签号
y_pred = kmeans_per_k[k - 1].labels_
silhouette_coefficients = silhouette_samples(X, y_pred)
padding = len(X) // 30
pos = padding
ticks = []
for i in range(k):
coeffs = silhouette_coefficients[y_pred == i]
coeffs.sort()
color = mpl.cm.Spectral(i / k)
plt.fill_betweenx(np.arange(pos, pos + len(coeffs)), 0, coeffs,
facecolor=color, edgecolor=color, alpha=0.7)
ticks.append(pos + len(coeffs) // 2)
pos += len(coeffs) + padding
plt.gca().yaxis.set_major_locator(FixedLocator(ticks))
plt.gca().yaxis.set_major_formatter(FixedFormatter(range(k)))
if k in (3, 5):
plt.ylabel("Cluster")
if k in (5, 6):
plt.gca().set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
plt.xlabel("Silhouette Coefficient")
else:
plt.tick_params(labelbottom=False)
plt.axvline(x=silhouette_scores[k - 2], color="red", linestyle="--")
plt.title("$k={}$".format(k), fontsize=16)
plt.show()
运行后会得到如下图形:
红色虚线为群体平均轮廓系数,最好的状态为k=5时,所有刀形超过红色虚线。
局限性:
当集群具有不同的大小、不同的密度或非球形时,K-Means的表现较差,K-Means算法重点考虑聚类半径,当聚类半径不同时,可以考虑高斯聚类,也因此在聚类前先要进行数据标准化或归一化。