乐趣区

关于机器学习:机器学习Kmeans

前言

K-means 是一种经典的无监督学习算法,用于对数据进行聚类。K-means 算法将数据集视为具备 n 个特色的 n 维空间,并尝试通过最小化簇内平方误差的总和来将数据点划分为簇。本文将介绍 K -means 算法的原理、实现和利用。

定义
  • K-means 是一种无监督学习算法,
  • 用于对数据进行聚类。该算法将数据集分为 K 个簇,每个簇蕴含最靠近其质心的数据点。K-means 算法将数据集视为具备 n 个特色的 n 维空间,并尝试通过最小化簇内平方误差的总和来将数据点划分为簇。
  • 它是一种迭代算法,通过将每个数据点调配到最近的质心并计算新的质心来迭代地改良簇的品质,直到质心不再变动或达到最大迭代次数为止。
实现流程(k-means算法原理)

K-means 算法是一种迭代算法,其根本思维是通过将每个数据点调配到最近的质心,并计算新的质心来迭代地改良簇的品质,直到质心不再变动或达到最大迭代次数为止。具体步骤如下:

  1. 随机抉择 K 个点作为初始质心;
  2. 计算每个数据点与 K 个质心的间隔;
  3. 将数据点划分到间隔最近的质心所在的簇;
  4. 对于每个簇,从新计算该簇内所有数据点的均值,将该均值作为新的质心;
  5. 如果质心没有变动,则进行迭代。

K-means 算法的外围是将数据点调配到最近的质心所在的簇,这是通过计算每个数据点与 K 个质心的间隔来实现的。一般而言,间隔能够应用欧氏间隔、曼哈顿间隔等来计算。而每个簇的质心则是该簇内所有数据点的均值,用于示意该簇的核心地位。

K 值的抉择

在 K -means 算法中,簇的数量 k 是须要当时指定的。抉择适合的簇的数量十分重要。

  1. 手肘法:在不同的 k 值下运行 K -means 算法,计算每个簇的误差平方和(SSE),并将其绘制成折线图。通常会发现,随着 k 值的减少,SSE 会逐步减小。然而,当 k 值减少到某个值时,SSE 的降落速度会变得更加迟缓,造成一个相似手肘的拐点。该拐点所对应的 k 值就是比拟适合的簇的数量。
  2. 轮廓系数法:轮廓系数是一种用于评估聚类后果品质的指标,其取值范畴在 [-1, 1] 之间。对于每个数据点,轮廓系数是其与同簇其余数据点间隔的平均值和与最近的不同簇的所有数据点的间隔的平均值之差除以两者中的较大值。聚类后果的整体轮廓系数是所有数据点的轮廓系数的平均值。较高的轮廓系数示意聚类成果较好。能够在不同的 k 值下运行 K -means 算法,计算聚类后果的整体轮廓系数,抉择轮廓系数最大的 k 值。
  3. 教训法令:在理论利用中,能够依据数据的特点、利用场景等因素,采纳教训法令来抉择 k 值。例如,对于图像宰割等利用,通常抉择 k 值为 2,对于客户分类等利用,通常抉择 k 值为 3 或 4。

须要留神的是,K-means 算法可能陷入部分最优解,因而,抉择 k 值须要屡次运行算法,比拟不同的聚类后果。

创立数据
  • 该代码生成了一个蕴含 1000 个数据点的随机数据集,其中有 4 个簇,每个簇的中心点别离位于 (0,0)、(3,3)、(0,3) 和(3,0)。每个簇的数据点遵从正态分布。最初,用散点图可视化了数据集。能够依据须要调整数据集的大小、簇的数量、中心点的地位等参数。
import numpy as np
import matplotlib.pyplot as plt

# 创立数据集
np.random.seed(0)
n_samples = 1000
centers = np.array([[0, 0], [3, 3], [0, 3], [3, 0]])
X = np.zeros((n_samples, 2))
for i in range(len(centers)):
    X[i * (n_samples // len(centers)): (i + 1) * (n_samples // len(centers)), :] = \
        centers[i] + np.random.randn(n_samples // len(centers), 2)

# 可视化数据集
plt.scatter(X[:, 0], X[:, 1], s=10)
plt.show()
实现 k -means
  • 该代码承受一个数据矩阵 X、簇的数量 k 和最大迭代次数 max_iter 作为输出,返回每个数据点的簇标签和最终的质心坐标。在每次迭代中,先计算每个数据点与 k 个质心的间隔,而后将数据点划分到间隔最近的质心所在的簇。接着,对于每个簇,从新计算该簇内所有数据点的均值,将该均值作为新的质心。最初,如果质心没有变动,则进行迭代。
import numpy as np

def k_means(X, k, max_iter=100):
    # 随机抉择 k 个点作为初始质心
    centroids = X[np.random.choice(len(X), k, replace=False)]

    for i in range(max_iter):
        # 计算每个数据点与 k 个质心的间隔
        distances = np.linalg.norm(X[:, np.newaxis, :] - centroids, axis=-1)

        # 将数据点划分到间隔最近的质心所在的簇
        labels = np.argmin(distances, axis=1)

        # 对于每个簇,从新计算该簇内所有数据点的均值,将该均值作为新的质心
        new_centroids = np.array([X[labels == j].mean(axis=0) for j in range(k)])

        # 如果质心没有变动,则进行迭代
        if np.allclose(new_centroids, centroids):
            break

        centroids = new_centroids

    return labels, centroids
if __name__ == '__main__':
    
    labels, centroids = k_means(X=X, k=4)
    
    plt.scatter(X[labels==0, 0], X[labels==0, 1], color='r')
    plt.scatter(X[labels==1, 0], X[labels==1, 1], color='g')
    plt.scatter(X[labels==2, 0], X[labels==2, 1], color='b')
    plt.scatter(X[labels==3, 0], X[labels==3, 1])

    plt.show()
优化后代码
  • 在此优化后的代码中,咱们应用了两个新函数
  • plot_data 函数用于绘制划分后的数据集
  • plot_centers 函数用于绘制中心点
  • 在主函数中,咱们先调用 k_means 函数失去簇标签和中心点,而后调用 plot_data 函数绘制划分后的数据集,最初调用 plot_centers 函数绘制中心点。这样的代码构造使得代码更易读,更易于调试和保护。
import numpy as np
import matplotlib.pyplot as plt


# 创立数据集
np.random.seed(0)
n_samples = 1000
centers = np.array([[0, 0], [3, 3], [0, 3], [3, 0]])
X = np.zeros((n_samples, 2))
for i in range(len(centers)):
    X[i * (n_samples // len(centers)): (i + 1) * (n_samples // len(centers)), :] = \
        centers[i] + np.random.randn(n_samples // len(centers), 2)

# 可视化数据集
def plot_data(X, labels):
    plt.scatter(X[labels==0, 0], X[labels==0, 1], color='r')
    plt.scatter(X[labels==1, 0], X[labels==1, 1], color='g')
    plt.scatter(X[labels==2, 0], X[labels==2, 1], color='b')
    plt.scatter(X[labels==3, 0], X[labels==3, 1], color='m')
    plt.show()

def plot_centers(centroids):
    plt.scatter(centroids[:, 0], centroids[:, 1], s=200, marker='*', color='k')
    plt.show()

def k_means(X, k, max_iter=100):
    # 随机抉择 k 个点作为初始质心
    centroids = X[np.random.choice(len(X), k, replace=False)]

    for i in range(max_iter):
        # 计算每个数据点与 k 个质心的间隔
        distances = np.linalg.norm(X[:, np.newaxis, :] - centroids, axis=-1)

        # 将数据点划分到间隔最近的质心所在的簇
        labels = np.argmin(distances, axis=1)

        # 对于每个簇,从新计算该簇内所有数据点的均值,将该均值作为新的质心
        new_centroids = np.array([X[labels == j].mean(axis=0) for j in range(k)])

        # 如果质心没有变动,则进行迭代
        if np.allclose(new_centroids, centroids):
            break

        centroids = new_centroids

    return labels, centroids

if __name__ == '__main__':
    labels, centroids = k_means(X=X, k=4)
    plot_data(X, labels)
    plot_centers(centroids)
退出移动版