kmeans手写实现与sklearn接口
kmeans简介
K 均值聚类是最基础的一种聚类方法。它是一种迭代求解的聚类分析算法。
kmeans的迭代步骤
-
给各个簇中心 μ 1 , … , μ c \mu_1,\dots,\mu_c μ1,…,μc 以适当的初值;
-
更新样本 x 1 , … , x n x_1,\dots,x_n x1,…,xn 对应的簇标签 y 1 , … , y n y_1,\dots,y_n y1,…,yn
y i ← a r g m i n y ∈ { 1 , … , c } ∣ ∣ x i − μ y ∣ ∣ 2 , i = 1 , … , n y_i\leftarrow argmin_{y\in \{1,\dots,c\}}||x_i-\mu_y||^2,\ \ \ i=1,\dots,n yi←argminy∈{1,…,c}∣∣xi−μy∣∣2, i=1,…,n -
更新各个簇中心 μ 1 , … , μ c \mu_1,\dots,\mu_c μ1,…,μc
μ y ← 1 n y ∑ i : y i = y x i , y = 1 , … , c \mu_y\leftarrow \frac{1}{n_y}\sum_{i:y_i=y}x_i,\ \ \ y=1,\dots,c μy←ny1i:yi=y∑xi, y=1,…,c
其中, n y n_y ny 为属于簇 y y y 的类别总数 -
重复上述步骤2, 3,直到簇标签不再变化(变化足够小),达到收敛精度为止
优缺点
优点
- 算法快速、简单;
- 对大数据集有较高的效率并且是可伸缩性的;
- 时间复杂度近于线性,而且适合挖掘大规模数据集。K-Means聚类算法的时间复杂度是O(n×k×t) ,其中n代表数据集中对象的数量,t代表着算法迭代的次数,k代表着簇的数目
缺点
- 在k-measn算法中K是事先给定的,但是K值的选定是非常难以估计的。
- 在 K-means 算法中,首先需要根据初始聚类中心来确定一个初始划分,然后对初始划分进行优化。这个初始聚类中心的选择对聚类结果有较大的影响,一旦初始值选择的不好,可能无法得到有效的聚类结果,这也成为 K-means算法的一个主要问题。
- 当数据量很大时,算法的开销是非常大的。
kmeans算法的改进
- kmeans++
- 二分Kmeans
- 分解最大 SSE (误差平方和)的簇
- 合并距离最小的簇 或者 合并SSE增幅最小的两个簇。
手写实现
伪代码:
创建 k 个点作为起始质心 (随机选择):
当任意一个点的簇分配结果发生改变的时候:
对数据集中的每个数据点:
对每个质心:
计算质心与数据点之间的距离
将数据点分配到距其最近的簇
对每一个簇:
求出均值并将其更新为质心
Python实现:
import numpy as np
def kmeans(data, n_clusters, tolerence):
n_samples = data.shape[0]
sample_asign = np.zeros((n_samples, 2))
cluster_centers = data[: n_clusters, :]
isChanged = True
epoch_cnt = 0
while isChanged:
epoch_cnt += 1
isChanged = False
# 更新每个样本点所属于的类
for sample_index in range(n_samples):
min_dist = np.inf
min_index = 0
for cluster_index in range(n_clusters):
dist = np.linalg.norm(data[sample_index, :] - cluster_centers[cluster_index, :])
if dist < min_dist:
min_dist = dist
min_index = cluster_index
sample_asign[sample_index, :] = min_dist, min_index
# 更新每个聚类中心
for cluster_index in range(n_clusters):
new_cluster_samples = data[ sample_asign[:, 1] == cluster_index ]
new_center = np.mean(new_cluster_samples, axis=0)
cluster_center_diff = np.linalg.norm( new_center - cluster_centers[cluster_index, :] )
cluster_centers[cluster_index, :] = new_center
if cluster_center_diff > tolerence:
isChanged = True
print(f"epoch count: {epoch_cnt}")
return cluster_centers
if __name__ == '__main__':
n_samples = 512
data_dim = 3
n_clusters = 4
data = np.random.randn(n_samples, data_dim)
tol = 1e-12
centers = kmeans(data, n_clusters, tol)
print(centers)
sklearn接口
kmeans 手写实现主要还是为了理解算法,在实际应用中,我们一般调 sklearn 的包就好了。
class sklearn.cluster.KMeans(n_clusters=8, init='k-means++', n_init=10, max_iter=300, tol=0.0001, verbose=0, random_state=None, copy_x=True, algorithm='lloyd')
类初始化参数
- init:指定初始化聚类中心的方法。可以是 kmeans++ (默认),random 随机选取,或者是一个形状为 (n_clusters, n_features) 的数组,直接将该数组作为初始的聚类中心。
- n_init:使用不同质心 (centeroid) 种子运行k -means 算法的次数。最终结果将是 inertia 最佳时的输出。整型,默认为 10。
- max_iter:kmeans 算法单词运行的最大迭代数。整型,默认 300。
- tol:关于两次连续迭代的聚类中心差异的 Frobenius 范数的相对容忍度,小于该值认为收敛。浮点数。
- verbose:是否打印迭代过程中的输出。整型,默认为 0。
- random_state:决定初始聚类中心的随机数种子。整型。
- copy_x:在预先计算距离时,首先将数据居中在数值上更准确。 如果 copy_x 为 True(默认),则不修改原始数据。 如果为 False,则修改原始数据,并在函数返回之前放回,但通过减去再添加数据均值可能会引入小的数值差异。 请注意,如果原始数据不是 C 连续的,即使 copy_x 为 False,也会进行复制。 如果原始数据是稀疏的,但不是 CSR 格式,即使 copy_x 为 False,也会进行复制。
- algorithm:要使用的 K-means 算法。 默认是经典的 EM 风格算法 “lloyd”。可选项有:“lloyd”, “elkan”, “auto”, “full”。
类属性
- cluster_centers_:聚类中心的坐标,是一个形状为 (n_clusters, n_features) 的数组。如果算法完全收敛,则与 labels_ 一致。
- labels_:每个点的标签,形状为 (n_clusters, ) 。
- inertia_:样本到其最近聚类中心的平方距离总和,如果给了权重的话会计算加权和。浮点数。
- n_features_in_:在运行 fit 方法时见到的特征数,整型、
- feature_names_in_:在运行 fit 方法时见到到的特征名称。只有当 X 的要素名称均为字符串时才定义。形状为 (n_features_in_, ) 的数组。
类方法
函数签名 | 说明 |
---|---|
fit(X[, y, sample_weight]) | 计算 kmeans 聚类 |
fit_predict(X[, y, sample_weight]) | 计算 kmeans 聚类并预测每个样本的簇序号 |
fit_transform(X[, y, sample_weight]) | 计算 kmeans 聚类并将 X 转换到聚类距离空间 |
get_feature_names_out([input_features]) | 获取转换的输出特征名称 |
get_params([deep]) | 获取 estimator 的参数 |
predict(X[, sample_weight]) | 预测 X 中每个样本所属于的簇 |
score(X[, y, sample_weight]) | (看起来是评估模型的准确率) |
set_params(**params) | 设置 estimator 的参数 |
transform(X) | 将 X 转换到聚类距离空间 |
模型保存与加载
需要通过 joblib 来保存,安装:
pip install joblib
保存/加载模型
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
import joblib
x, y = make_blobs(n_samples=1000, n_features=4, centers=[[-1, -1], [0, 0], [1, 1], [2, 2]], cluster_std=[0.4, 0.2, 0.2, 0.4], random_state=42)
model = KMeans(n_clusters=6)
model.fit(x, y)
print(model.cluster_centers_)
# 保存模型
joblib.dump(model, 'kmeans_model.pkl')
# 加载模型
model = joblib.load('kmeans_model.pkl')
print(model.cluster_centers_)
Ref
- 图解机器学习——杉山将
- sklearn官方文档
- k-means原理与实现