From scratch build KMeans

原理

机器学习可以大致分为 无监督学习和有监督学习 两大类别(半监督学习在此不予讨论)。在无监督学习中,数据不具有预先定义的标签信息。

聚类算法是无监督学习中常用的方法之一,用于将数据样本按照相似性或距离度量分成不同的群组。其中,一些常见的聚类算法包括 K -means、层次聚类算法和 DBSCAN 等。本文主要介绍 K -means 算法。

聚类算法在许多应用场景中得到广泛应用,例如客户群体分析、社交网络分析以及数据预处理和半监督学习等间接应用。

K-means 算法的具体流程如下:

  1. 初始化 k 个聚类中心点(通常随机选择)。
  2. 计算每个样本与 k 个聚类中心的距离,并将每个样本分配到距离最近的聚类中心所属的簇中。
  3. 更新 k 个聚类的中心,即计算每个簇中数据点的均值作为新的聚类中心。
  4. 重复步骤 2 和步骤 3,直到 k 个聚类中心不再发生移动或达到预定的停止条件。

1. 分析 iris 数据

接下来我们使用 iris dataset 来探索 KMeans 作用和从零构建 KMeans。

from sklearn import datasets
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans

iris = datasets.load_iris()
==========================
iris.feature_names
['sepal length (cm)',
 'sepal width (cm)',
 'petal length (cm)',
 'petal width (cm)']

对前 2 个特征 花瓣长度和宽度 可视化如下图

#'sepal length (cm)',
# 'sepal width (cm)',
#前两列数据代表花瓣长度和宽度
X = iris.data[:, :2]
y = iris.target

# 将数据集中所有数据进行二维可视化展示
plt.scatter(X[:,0], X[:,1], c=y, cmap='rainbow')
plt.xlabel('Spea1 Length', fontsize=8)
plt.ylabel('Sepal Width', fontsize=8)
plt.title('Iris 2 features 2D visualization', fontsize=12)
plt.show()
From scratch build KMeans

PCA 降维后 3D 可视化如下:

# 为了更好理解数据集,使用 PCA 降维,观察 3D 模式下数据的分布
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.decomposition import PCA

fig = plt.figure(1, figsize=(8, 8))

ax = fig.add_subplot(111, projection='3d')
# ax = Axes3D(fig, elev=-150, azim=110)
#将数据降到 3 维,接下来方便 3 维展示
X_reduced = PCA(n_components=3).fit_transform(iris.data)

ax.scatter(X_reduced[:, 0], X_reduced[:, 1], X_reduced[:, 2], c=y,
cmap='rainbow', edgecolor='k', s=40)

ax.set_title("PCA Modeling")
ax.set_xlabel("1st eigenvector")
ax.set_ylabel("2nd eigenvector")
ax.set_zlabel("3rd eigenvector")
ax.view_init(elev=-150, azim=110) 
plt.show()
From scratch build KMeans

iris dataset 本身 label 就是 3 类,PCA 降维 3 维后,可以发现也差不多分为 3 簇。

2.sklearn KMeans

使用 sklearn 聚类包中的 KMeans, 得到 3 个聚类中心和

km = KMeans(n_clusters=3,  n_init='auto')
km.fit(X)

# 打印聚类后各个簇的中心点
centers = km.cluster_centers_
print(centers)
======================================
[[6.82391304 3.07826087]
 [5.8        2.7]
 [5.00392157 3.40980392]]

3. 从零构建 KMeans

class KMeans:
    def __init__(self, n_clusters, max_iter=100):
        self.n_clusters = n_clusters
        self.max_iter = max_iter

    def fit(self, X):
        # 随机初始化 K 个中心点
        self.centroids = X[np.random.choice(X.shape[0], self.n_clusters, replace=False)]

        for _ in range(self.max_iter):
            # 分配样本到最近的中心点
            labels = self._assign_clusters(X)

            # 更新中心点位置
            self._update_centroids(X, labels)

    def _assign_clusters(self, X):
        # 计算样本到中心点的距离
        distances = np.sqrt(((X - self.centroids[:, np.newaxis]) ** 2).sum(axis=2))

        # 分配样本到最近的中心点
        labels = np.argmin(distances, axis=0)

        return labels

    def _update_centroids(self, X, labels):
        # 更新中心点位置为所属簇的均值
        for i in range(self.n_clusters):
            self.centroids[i] = X[labels == i].mean(axis=0)

    def predict(self, X):
        # 分配样本到最近的中心点
        labels = self._assign_clusters(X)
        return labels

kmeans = KMeans(n_clusters=3)
kmeans.fit(X)
labels = kmeans.predict(X)
print(kmeans.centroids)

其 3 个聚类中心坐标为:

#kmeans.centroids
[[5.77358491, 2.69245283],
[6.81276596, 3.07446809],
[5.006     , 3.428]]

#centers
[[6.82391304 3.07826087]
 [5.8        2.7]
 [5.00392157 3.40980392]]

跟 2 中的 centers 差距不大。那么我们可视化结果看看。

fig, axes = plt.subplots(1, 2, figsize=(16,8))
axes[0].scatter(X[:, 0], X[:, 1], c=labels, cmap='rainbow', 
                edgecolor='k', s=150)
axes[1].scatter(X[:, 0], X[:, 1], c=predicted_labels, cmap='rainbow',
                edgecolor='k', s=150)
axes[0].set_xlabel('Sepal length', fontsize=16)
axes[0].set_ylabel('Sepal width', fontsize=16)
axes[1].set_xlabel('Sepal length', fontsize=16)
axes[1].set_ylabel('Sepal width', fontsize=16)
axes[0].tick_params(direction='in', length=10, width=5, colors='k', labelsize=20)
axes[1].tick_params(direction='in', length=10, width=5, colors='k', labelsize=20)
axes[0].set_title('Custom kmeans predicted', fontsize=18)
axes[1].set_title('Sklearn Kmeans Predicted', fontsize=18)
From scratch build KMeans

自己构建的 Kmeans 和 Sklearn 非常接近。说明我们搭建的 KMeans 是有效的,当然你也可以打印 labels 和 predicted_labels 来比较

Inference

[1] kmeans-clustering-from-scratch

 
正文完
 
admin
版权声明:本站原创文章,由 admin 2023-11-26发表,共计3536字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请联系tensortimes@gmail.com。
评论(没有评论)
验证码