kmeans手写实现与sklearn接口
kmeans简介
K 均值聚类是最基础的一种聚类方法。它是一种迭代求解的聚类分析算法。
kmeans的迭代步骤
-
给各个簇中心 μ1,…,μc\mu_1,\dots,\mu_cμ1,…,μc 以适当的初值;
-
更新样本 x1,…,xnx_1,\dots,x_nx1,…,xn 对应的簇标签 y1,…,yny_1,\dots,y_ny1,…,yn
yi←argminy∈{1,…,c}∣∣xi−μy∣∣2,i=1,…,ny_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←1ny∑i:yi=yxi,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
其中,nyn_yny 为属于簇 yyy 的类别总数 -
重复上述步骤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 npdef kmeans(data, n_clusters, tolerence):n_samples = data.shape[0]sample_asign = np.zeros((n_samples, 2))cluster_centers = data[: n_clusters, :]isChanged = Trueepoch_cnt = 0while isChanged:epoch_cnt += 1isChanged = False# 更新每个样本点所属于的类for sample_index in range(n_samples):min_dist = np.infmin_index = 0for cluster_index in range(n_clusters):dist = np.linalg.norm(data[sample_index, :] - cluster_centers[cluster_index, :])if dist < min_dist:min_dist = distmin_index = cluster_indexsample_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_centerif cluster_center_diff > tolerence:isChanged = Trueprint(f"epoch count: {epoch_cnt}")return cluster_centersif __name__ == '__main__':n_samples = 512data_dim = 3n_clusters = 4data = np.random.randn(n_samples, data_dim)tol = 1e-12centers = 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 joblibx, 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原理与实现