Menu
CoddyTech

手动实现 K-Means

本课属于 Coddy Machine Learning 学习旅程的“无监督学习”单元。第 3 课,共 51 课。

K-means 重复两步组成的一轮,以找到 k 个簇。分配:每个点都加入离它最近的中心。移动:每个中心移动到其所对应点的均值处。三个冰淇淋推车:

海滩上有 150 条毛巾,分成左、中、右三群。三辆推车一开始彼此靠近,位于中间附近;黑线逐轮描绘每辆推车的路径,终点是各自所在人群中间的一颗星星

NumPy 中的一轮操作,其中 dists 是距离表,每个中心对应一列:

labels = dists.argmin(axis=1)  # 分配
for c in range(k):
    centers[c] = X[labels == c].mean(axis=0)  # 移动

argmin(axis=1) 会给出每一行中最小值对应的列,也就是离该行最近的中心。一轮又一轮之后,中心会逐渐稳定;当没有中心再移动时,k-means 就完成了。

challenge icon

挑战

简单

一家 T 恤制造商根据今天的顾客重新调整 S、M 和 L 码的尺寸。旧尺码表用来设置初始中心,distances(people, centers) 会生成距离表,程序会读取要运行的轮数。

  1. 每一轮:为每个人分配尺码,然后移动每个中心。
  2. 打印每种尺码的中心,以及最后一轮分配到该尺码的人数,如下所示。

对于另一家制造商:

Rounds run: 2
S: chest 87.3 cm, length 65.1 cm, 14 people
M: chest 95.0 cm, length 68.9 cm, 31 people
L: chest 104.8 cm, length 72.6 cm, 45 people

亲手试试

import numpy as np

# 不要修改下面的代码行
rng = np.random.default_rng(12)
chest = np.concatenate([rng.normal(88, 3, 30), rng.normal(98, 3.5, 35), rng.normal(110, 4, 25)])
length = np.concatenate([rng.normal(66, 1.8, 30), rng.normal(70, 2, 35), rng.normal(75, 2.2, 25)])
# 90 位顾客,每人一行:胸围和衣长,单位为 cm
people = np.round(np.column_stack([chest, length]), 1)

# 旧尺码表给出了 S、M、L 的初始中心点
sizes = ["S", "M", "L"]
centers = np.array([[85.0, 64.0], [90.0, 67.0], [96.0, 69.0]])
rounds = int(input())


# 距离表:每人一行,每个中心点一列
def distances(X, centers):
    table = np.zeros((len(X), len(centers)))
    for c in range(len(centers)):
        table[:, c] = np.sqrt(((X - centers[c]) ** 2).sum(axis=1))
    return table


# 在下面编写你的代码
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

无监督学习中的所有课

自己练习: Python 在线编译器