kmeans 是一个简单实用的机器学习算法,今天专门介绍一下。这篇文章主要关注以下几点:
k-means算法的步骤:
k-means算法存在的问题:
我们可以先自己手工构造一个一个的簇,作为聚类的数据集。 这里讲一下生成数据的方法。
要点如下:
import numpy as np
import os
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
# 这是中心点
blob_centers = np.array(
[
[2.2,2.3],
[-1.5, 2.3],
[-2.8, 1.8],
[-2.8, 2.8],
[-2.8, 1.3]
]
)
# 这是离散程度, 越大则越离散。
blob_std = np.array([1, 0.3, 0.1, 0.1, 0.1])
X,y = make_blobs(n_samples=5000, centers = blob_centers, cluster_std = blob_std, random_state=7)
print(X, y)
这里有个小思考: 例子中基本都是使用的二维点来进行展示的, 如果是多维的数据,如果可以图形化展示聚类算法的效果呢?
# kmeans算法
from sklearn.cluster import KMeans
# 需要人为指定簇的个数
k = 5
# random_stat 指定可以保证出来的结果都是一样的
kmeans = KMeans(n_clusters = k, random_state=42)
# 输入的X是一个二维的数组,每个元素都是一个点。
y_pred= kmeans.fit_predict(X)
# 得到的是每个点的簇的标签
print(y_pred)
print(kmeans.labels_)
# 可以得出簇的中心点
kmeans.cluster_centers_
等高线可以看出每个簇所处的区域是啥,具体的函数代码如下:
def plot_data(X):
plt.plot(X[:, 0], X[:, 1], 'k.', markersize=2)
# 画中心点
def plot_centroids(centroids, weights=None, circle_color='w', cross_color='k'):
if weights is not None:
centroids = cnetroids[weights > weights.max() / 10]
plt.scatter(centroids[:, 0], centroids[:, 1], marker= 'o', s = 30, linewidths=8, color=circle_color, zorder=10, alpha=0.9)
plt.scatter(centroids[:, 0], centroids[:, 1],
marker='x', s=50, linewidths=1,
color=cross_color, zorder=11, alpha=1)
def plot_decision_boundaries(clusterer, X, resolution=1000, show_centroids=True, show_xlabels=True, show_ylabels=True):
# 这个是为了可以显示所有的点
mins = X.min(axis=0)
maxs = X.max(axis=0)
print(mins)
print(maxs)
# 下面画一个网格
xx, yy = np.meshgrid(np.linspace(mins[0], maxs[0], resolution),
np.linspace(mins[1], maxs[1], resolution)
)
# 这个ravel函数可以把二维数组拍平
Z = clusterer.predict(np.c_[xx.ravel(), yy.ravel()])
print(Z)
Z = Z.reshape(xx.shape)
plt.contourf(Z, extent=(mins[0], maxs[0], mins[1], maxs[1]), cmap="Pastel2")
plt.contour(Z, extent=(mins[0], maxs[0], mins[1], maxs[1]), linewidths=1, colors='k')
plot_data(X)
if show_centroids:
plot_centroids(clusterer.cluster_centers_)
if show_xlabels:
plt.xlabel("$x_1$", fontsize=14)
else:
plt.tick_params(labelbottom='off')
if show_ylabels:
plt.ylabel("$x_2$", fontsize=14, rotation=0)
else:
plt.tick_params(labelleft='off')
plt.figure(figsize=(8,4))
plot_decision_boundaries(kmeans, X)
def plot_color(clusterer, X):
# 预测一下聚类效果
y = clusterer.fit_predict(X)
# 这里的c 表示颜色序列
plt.scatter(X[:, 0], X[:, 1], c=y,s=2, cmap=plt.cm.Spectral)
plot_color(kmeans, X)
我们知道, kmeans聚类算法的结果,会受到初始值中心点的影响,我们怎么保证最后聚类出来的结果是比较合适的呢?
kmeans算法里有几个参数, 需要注意一下。
机器学习库中, 有个inertia_属性, 这个就是每个样本与对应质心的距离的平方和的累加值。当设定n_init参数的时候, 会选取多次聚类, inertia_值最小的聚类结果。
首先,选择最小inertia值对应的k值不可靠,原因是,随着k的增加, 聚类的簇就会越来越多, 数据点到质心的距离也会很小, 最后的inertia的值也会变得非常小, 但是聚类效果并不是越来越好的。
推荐方案一:画一个图,表示出k值与对应的inertia值的对应关系曲线,在曲线的下降率发生剧烈变化的时候对应的k值,可以认为是比较合适的k值,不过, 这个点只能作为参考,有可能不是最优的选择。
推荐方案二:使用轮廓系数来选取k值。
定义两个概念:
s ( i ) = b ( i ) − a ( i ) m a x ( 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 ) s(i) s(i)的均值,作为评价指标。
sklearn机器学习库中有一个专门计算函数。具体使用如下:
# kmeans_per_k表示聚类实例的列表。
# 注意实例取值要从1开始, 否则会出错的。
silhouette_scores = [silhouette_score(X,model.labels_) for model in kmeans_per_k[1:]]
#绘制图表
plt.figure(figsize=(8,4))
plt.plot(range(2,10),silhouette_scores,'bo-')
plt.show()
这个指标也只能是作为一个参考,最合适的k值还是需要根据自己的需求来决定的。
kmeans 是一个简单实用的算法, 不过缺点也是比较明显的, 对于形状不规则的点状图,kmeans 的聚类算法效果可能会不好,而且对于kmeans 的模型评估比较难, 不容易找到合适的k值。