1. k-means算法思想
k-means算法中文名叫做k均值。它是一种非监督聚类算法,如有一堆数据,但是知道这些数据有k个类,具体每一个数据点所属分类不知道。此时就需要用k-means聚类算法,它可以把原先的数据分成k个部分,相似的数据就聚在一起。
2. k-means算法步骤
共有3个步骤:
- 初始化–随机生成K个初始“均值”(质心);
- 分配–通过将每个观测值与最近的质心相关联来创建K个聚类,遍历所有点计算该点到那k个聚类中心点的距离。此时有k个距离,哪个距离最短,就认为当前这个点是属于这个聚类;
- 更新–群集的质心成为新的均值,得到了k个聚类。到这一步需要重新计算聚类中心。此时聚类中心就是当前这个聚类的包含的点的平均值。也就是说各个点加起来取平均作为当前聚类的中心.
- 迭代地重复分配和更新,直到收敛为止,最终结果是,点与它们各自的质心之间的平方误差之和最小。
3 使用Python实现k-means算法
3.1 数据初始化:生成随机样本数据,并生成3个初始"质心"
import pandas as pdimport numpy as npimport matplotlib.pyplot as plt%matplotlib inlinedf = pd.DataFrame({ 'x': [12, 20, 28, 18, 29, 33, 24, 45, 45, 52, 51, 52, 55, 53, 55, 61, 64, 69, 72], 'y': [39, 36, 30, 52, 54, 46, 55, 59, 63, 70, 66, 63, 58, 23, 14, 8, 19, 7, 24]})np.random.seed(200) #可以使多次生成的随机数相同k = 3# centroids[i] = [x, y]centroids = { i+1: [np.random.randint(0, 80), np.random.randint(0, 80)] for i in range(k)} print(centroids) fig = plt.figure(figsize=(5, 5))#【绘图】plt.figure()的使用plt.scatter(df['x'], df['y'], color='k')colmap = {1: 'r', 2: 'g', 3: 'b'}for i in centroids.keys(): plt.scatter(*centroids[i], color=colmap[i])plt.xlim(0, 80)plt.ylim(0, 80)plt.show()
3.2 分配数据:通过将每个观测值与最近的质心相关联来创建K个聚类
def assignment(df, centroids): for i in centroids.keys(): # sqrt((x1 - x2)^2 - (y1 - y2)^2) 欧式距离 df['distance_from_{}'.format(i)] = ( np.sqrt( (df['x'] - centroids[i][0]) ** 2 + (df['y'] - centroids[i][1]) ** 2 ) ) centroid_distance_cols = ['distance_from_{}'.format(i) for i in centroids.keys()] df['closest'] = df.loc[:, centroid_distance_cols].idxmin(axis=1) df['closest'] = df['closest'].map(lambda x: int(x.lstrip('distance_from_'))) df['color'] = df['closest'].map(lambda x: colmap[x]) return dfprint(centroids)df = assignment(df, centroids)print(df.head())fig = plt.figure(figsize=(5, 5))plt.scatter(df['x'], df['y'], color=df['color'], alpha=0.5, edgecolor='k')for i in centroids.keys(): plt.scatter(*centroids[i], color=colmap[i])plt.xlim(0, 80)plt.ylim(0, 80)plt.show()
3.3 更新–群集的质心成为新的均值
import copyold_centroids = copy.deepcopy(centroids) #【绘图】plt.figure()的使用print(old_centroids)def update(k): for i in centroids.keys(): centroids[i][0] = np.mean(df[df['closest'] == i]['x']) centroids[i][1] = np.mean(df[df['closest'] == i]['y']) return kcentroids = update(centroids)print(centroids) fig = plt.figure(figsize=(5, 5))ax = plt.axes() #设置你的轴域 ax1 = plt.Axes(fig1,[0.2, 0.2, 0.4, 0.4]) 将轴域添加至figurefig1.add_axes(ax1)plt.scatter(df['x'], df['y'], color=df['color'], alpha=0.5, edgecolor='k')for i in centroids.keys(): plt.scatter(*centroids[i], color=colmap[i])plt.xlim(0, 80)plt.ylim(0, 80)for i in old_centroids.keys(): old_x = old_centroids[i][0] old_y = old_centroids[i][1] dx = (centroids[i][0] - old_centroids[i][0]) * 0.75 print(dx) dy = (centroids[i][1] - old_centroids[i][1]) * 0.75 print(dy) ax.arrow(old_x, old_y, dx, dy, head_width=2, head_length=3, fc=colmap[i], ec=colmap[i]) #matplotlib 画箭头的plt.show()
3.4 重复分配阶段:重复分配和更新
df = assignment(df, centroids)# Plot resultsfig = plt.figure(figsize=(5, 5))plt.scatter(df['x'], df['y'], color=df['color'], alpha=0.5, edgecolor='k')for i in centroids.keys(): plt.scatter(*centroids[i], color=colmap[i])plt.xlim(0, 80)plt.ylim(0, 80)plt.show()
3.5 重复进行直到所有集群都没有变化为止
n = 0while True: n += 1 print(f'第{n}次迭代.............') closest_centroids = df['closest'].copy(deep=True) centroids = update(centroids) df = assignment(df, centroids) if closest_centroids.equals(df['closest']): breakfig = plt.figure(figsize=(5, 5))plt.scatter(df['x'], df['y'], color=df['color'], alpha=0.5, edgecolor='k')for i in centroids.keys(): plt.scatter(*centroids[i], color=colmap[i])plt.xlim(0, 80)plt.ylim(0, 80)plt.show()
到此可以看到有3个清晰的群集,在这些群集的中心有3个均值
4 使用scikit-learn进行k-means算法
scikit-learn封装有k-means算法,可以直接调用实现k-means算法功能。
import numpy as npimport pandas as pd import matplotlib.pyplot as pltfrom sklearn.cluster import KMeansdf = pd.DataFrame({ 'x': [12, 20, 28, 18, 29, 33, 24, 45, 45, 52, 51, 52, 55, 53, 55, 61, 64, 69, 72], 'y': [39, 36, 30, 52, 54, 46, 55, 59, 63, 70, 66, 63, 58, 23, 14, 8, 19, 7, 24]})kmeans = KMeans(n_clusters=3)kmeans.fit(df)labels = kmeans.predict(df) # 使用这个数据进行训练print(type(labels))d = kmeans.labels_ print(d) # 我们可以看到每个样本点的聚类标签centroids = kmeans.cluster_centers_ ## 我们可以看到每个样本点的聚类标签fig = plt.figure(figsize=(5, 5))print(labels)colmap = {1: 'r', 2: 'g', 3: 'b'}colors = list(map(lambda x:colmap[x+1], labels)) #map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 使用 lambda 匿名函数 [1, 4, 9, 16, 25]print(colors)plt.scatter(df['x'], df['y'], color=colors, alpha=0.5, edgecolor='k')for idx, centroid in enumerate(centroids): plt.scatter(*centroid, color=colmap[idx+1])plt.xlim(0, 80)plt.ylim(0, 80)plt.show()
使用scikit-learn封装的API得到的结果与按算法步骤执行的结果完全相同。
5 小结
今天总结了机器学习的k-means算法的思想原理和实现方法:
- k-means算法属于应用广泛的聚类算法,聚类属于无监督算法(Unsupervied learning),即没有明确的类别,没有明确的label,通过算法把相似的东西分到一个组,来寻找其中的规律;
- k-means算法优点在于原理简单,容易实现,聚类效果好;
- k-means算法缺点:K值、初始点的选取不好确定,得到的结果只是局部最优,受离群值影响大。