机器学习算法原理实现——kmeans聚类算法
kmeans算法原理和步骤
K-means是一种常用的聚类方法,它将数据划分为K个相似的簇,其中每个簇的中心为该簇内所有数据点的均值。以下是K-means的基本原理和步骤:
原理: K-means基于一个简单的想法:相似的数据点应该在空间中彼此靠近,并且可以通过计算每个点到各个簇中心的距离来找到这些点的簇标签。
步骤:
-
初始化:首先选择K个数据点作为初始的簇中心。这可以是随机选择,也可以是使用某种启发式方法。
-
分配数据点:对于数据集中的每一个数据点,计算其到K个中心的距离,并将其分配到距离最近的中心所在的簇。
-
更新簇中心:对于每一个簇,计算簇中所有数据点的均值,将均值作为新的簇中心。
-
收敛判断:比较新的簇中心与上一次迭代的簇中心,如果簇中心没有(或只有微小的)变化,算法结束。否则,返回第2步。
-
结束:当簇中心不再变化或达到预定的迭代次数时,算法结束。
需要注意的是,K-means的结果可能会受到初始中心的影响,导致局部最优。为了获得更好的聚类结果,通常会多次运行算法,每次使用不同的初始中心,然后选择最好的结果。
优点:
- 实现简单
- 对于大数据集,算法是高效的
缺点:
- 结果可能会受到初始中心选择的影响,导致局部最优
- 对于簇的形状和大小敏感(例如,对于非凸形状的簇,K-means可能不会很好地工作)
- 需要预先指定K值,这在实际应用中可能不容易确定
为了解决部分缺点,有很多变种和改进的方法,例如K-means++(用于更好的初始化中心)、二分K-means、DBSCAN(不需要预先指定簇的数量,并且可以发现任意形状的簇)等。
因为这个算法比较直观,所以代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | # 导入numpy库 import numpy as np ### 定义欧式距离 def euclidean_distance(x, y): ''' 输入: x:向量x y:向量y 输出: np.sqrt(distance):欧式距离 ''' # 初始化距离 distance = 0 # 遍历并对距离的平方进行累加 for i in range ( len (x)): distance + = pow ((x[i] - y[i]), 2 ) return np.sqrt(distance) ### 定义质心初始化函数 def centroids_init(X, k): ''' 输入: X:训练样本,NumPy数组 k:质心个数,也是聚类个数 输出: centroids:质心矩阵 ''' # 样本数和特征数 m, n = X.shape # 初始化质心矩阵,大小为质心个数×特征数 centroids = np.zeros((k, n)) # 遍历 for i in range (k): # 每一次循环随机选择一个类中心作为质心向量 centroid = X[np.random.choice( range (m))] # 将质心向量分配给质心矩阵 centroids[i] = centroid return centroids ### 定义样本所属最近质心的索引 def closest_centroid(x, centroids): ''' 输入: x:单个样本实例 centroids:质心矩阵 输出: closest_i: ''' # 初始化最近索引和最近距离 closest_i, closest_dist = 0 , float ( 'inf' ) # 遍历质心矩阵 for i, centroid in enumerate (centroids): # 计算欧式距离 distance = euclidean_distance(x, centroid) # 根据欧式距离判断并选择最近质心的索引 if distance < closest_dist: closest_i = i closest_dist = distance return closest_i ### 分配样本与构建簇 def build_clusters(centroids, k, X): ''' 输入: centroids:质心矩阵 k:质心个数,也是聚类个数 X:训练样本,NumPy数组 输出: clusters:聚类簇 ''' # 初始化簇列表 clusters = [[] for _ in range (k)] # 遍历训练样本 for x_i, x in enumerate (X): # 获取样本所属最近质心的索引 centroid_i = closest_centroid(x, centroids) # 将当前样本添加到所属类簇中 clusters[centroid_i].append(x_i) return clusters ### 计算质心 def calculate_centroids(clusters, k, X): ''' 输入: clusters:上一步的聚类簇 k:质心个数,也是聚类个数 X:训练样本,NumPy数组 输出: centroids:更新后的质心矩阵 ''' # 特征数 n = X.shape[ 1 ] # 初始化质心矩阵,大小为质心个数×特征数 centroids = np.zeros((k, n)) # 遍历当前簇 for i, cluster in enumerate (clusters): # 计算每个簇的均值作为新的质心 centroid = np.mean(X[cluster], axis = 0 ) # 将质心向量分配给质心矩阵 centroids[i] = centroid return centroids ### 获取每个样本所属的聚类类别 def get_cluster_labels(clusters, X): ''' 输入: clusters:当前的聚类簇 X:训练样本,NumPy数组 输出: y_pred:预测类别 ''' # 预测结果初始化 y_pred = np.zeros(X.shape[ 0 ]) # 遍历聚类簇 for cluster_i, cluster in enumerate (clusters): # 遍历当前簇 for sample_i in cluster: # 为每个样本分配类别簇 y_pred[sample_i] = cluster_i return y_pred ### k均值聚类算法流程封装 def kmeans(X, k, max_iterations): ''' 输入: X:训练样本,NumPy数组 k:质心个数,也是聚类个数 max_iterations:最大迭代次数 输出: 预测类别列表 ''' # 1.初始化质心 centroids = centroids_init(X, k) # 遍历迭代求解 for _ in range (max_iterations): # 2.根据当前质心进行聚类 clusters = build_clusters(centroids, k, X) # 保存当前质心 cur_centroids = centroids # 3.根据聚类结果计算新的质心 centroids = calculate_centroids(clusters, k, X) # 4.设定收敛条件为质心是否发生变化 diff = centroids - cur_centroids # 检查差异diff中是否有任何非零元素。如果所有元素都是0,那么diff.any()将返回False if not diff. any (): break # 返回最终的聚类标签 return get_cluster_labels(clusters, X) # 创建测试数据 # X = np.array([[0,2],[0,0],[1,0],[5,0],[5,2]]) from sklearn.datasets import make_blobs # 生成3类数据,每类50个样本,每个样本2个特征 X, y = make_blobs(n_samples = 150 , centers = 3 , n_features = 2 , random_state = 0 ) # 设定聚类类别为2个,最大迭代次数为10 labels = kmeans(X, 3 , 10 ) # 打印每个样本所属的类别标签 print (labels) import matplotlib.pyplot as plt # 使用k-means聚类的结果绘制散点图 plt.subplot( 131 ) plt.scatter(X[:, 0 ], X[:, 1 ], c = labels) plt.title( 'K-means Clustering Result' ) # 使用真实的类别标签绘制散点图 plt.subplot( 132 ) plt.scatter(X[:, 0 ], X[:, 1 ], c = y) plt.title( 'True Labels' ) # 导入KMeans模块 from sklearn.cluster import KMeans # 创建k均值聚类实例并进行数据拟合 kmeans = KMeans(n_clusters = 3 , random_state = 0 ).fit(X) # 打印拟合标签 print (kmeans.labels_) plt.subplot( 133 ) plt.scatter(X[:, 0 ], X[:, 1 ], c = kmeans.labels_) plt.title( 'Sklearn kmeans Labels' ) plt.show() |
最后绘图如下:
可以看到和sklearn的结果几乎没区别!
还可以优化下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | ### 定义欧式距离,不使用numpy def euclidean_distance2(x, y): ''' 输入: x:向量x y:向量y 输出: np.sqrt(distance):欧式距离 ''' # 初始化距离 distance = 0 # 遍历并对距离的平方进行累加 for i in range ( len (x)): distance + = pow ((x[i] - y[i]), 2 ) return np.sqrt(distance) ### 定义欧式距离,使用numpy def euclidean_distance(x, y): return np.sqrt(np. sum (np.square(x - y))) ### 定义质心初始化函数 def centroids_init(X, k): ''' 输入: X:训练样本,NumPy数组 k:质心个数,也是聚类个数 输出: centroids:质心矩阵 ''' # 样本数和特征数 m, n = X.shape # 初始化质心矩阵,大小为质心个数×特征数 centroids = np.zeros((k, n)) # 遍历 for i in range (k): # 每一次循环随机选择一个类中心作为质心向量 centroid = X[np.random.randint(m)] # 将质心向量分配给质心矩阵 centroids[i] = centroid return centroids ### 定义样本所属最近质心的索引,不用numpy def closest_centroid2(x, centroids): ''' 输入: x:单个样本实例 centroids:质心矩阵 输出: closest_i: ''' # 初始化最近索引和最近距离 closest_i, closest_dist = 0 , float ( 'inf' ) # 遍历质心矩阵 for i, centroid in enumerate (centroids): # 计算欧式距离 distance = euclidean_distance(x, centroid) # 根据欧式距离判断并选择最近质心的索引 if distance < closest_dist: closest_i = i closest_dist = distance return closest_i ### 定义样本所属最近质心的索引,使用numpy def closest_centroid(x, centroids): ''' 输入: x:单个样本实例 centroids:质心矩阵 输出: closest_i: ''' # 计算x与所有质心的欧式距离, axis=1 计算每一行 distances = np.sqrt(np. sum ((centroids - x) * * 2 , axis = 1 )) # 返回最近质心的索引 return np.argmin(distances) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2022-09-11 vmvare windows 7 虚拟机无法ping通宿主机解决方法
2021-09-11 利用PowerUpSQL攻击SQL Server实例——本质上就是利用弱密码登录SQL server
2019-09-11 python代码安全扫描工具
2018-09-11 TCP报文格式和三次握手——三次握手三个tcp包(header+data),此外,TCP 报文段中的数据部分是可选的,在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。
2018-09-11 https ddos检测——研究现状
2018-09-11 https ddos攻击——由于有了认证和加解密 后果更严重 看绿盟的产品目前对于https的ddos cc攻击需要基于内容做检测