K-Means算法的并行和分布式写法

article/2025/8/31 14:04:59

  前段时间学习了并行与分布式技术,为了写了篇关于KMeans算法的并行和分布式的编程写法,上网找了挺久,没想到网上并没有很多资料,那今天就来说一下我是怎么写的吧。

首先来讲一下K-Means的思想原理吧!

K-Means算法思想原理

   K-means算法是根据给定 n n n个对象的数据集,构建 K K K个划分聚类方法,每个划分聚类称为簇。在这K个簇中,每个簇至少有一个数据对象,且每个数据对象有且只可属于一个簇。簇中的数据还有一个必须遵守的法则:同一个簇内的数据对象相似度高,不同簇的数据对象相似度低。这里的相似度将采用距离来衡量。

   根据下图,可以看到K-means算法包括初始中心点的选择,对剩余数据对象遍历,进行相似度(距离)的计算,将相似度最高的数据点划分至该簇,重新计算中心点,再次相似度计算,直至代价函数达到最小值,即数据中心不再移动为止。
kmeans图片

算法步骤总结

step1: 随机初始化 K K K个聚类中心点,循环Z次, ε = 1 0 − 5 \varepsilon=10^{-5} ε=105

step2: 遍历其余样本点计算与各中心点的距离,选择相似度最高的聚为一类。

\step3: 计算代价函数 J J J,若 ∣ J ∣ ≤ ε |J|\leq\varepsilon Jε或所有的观测值不再被分配或 k k k大于指定循环次数则退出循环, k = k + 1 k = k + 1 k=k+1

step4: 重新计算新聚类中心点,返回step2。

接下来就是代码实现啦!

首先来看看只是用Numpy是怎么写的吧!

使用Numpy实现

为了方便理解,附上流程图如下:
numpy
Numpy的实现其实就是把所有数据丢进一个矩阵,然后算算算就好了😀

import numpy as np
import pandas as pd# 找出最优簇选择(初始)
def initial_value(n, k):minJ = np.min(data, axis=0)maxJ = np.max(data, axis=0)rangeJ = maxJ - minJcentroids = minJ + rangeJ * np.random.rand(k, 1)return centroids# 算每个的距离,,取最小距离的索引
def distance(data, centroids):d = np.sqrt(((data-centroids[:, np.newaxis])**2).sum(axis=2))return np.argmin(d, axis=0)# 更新簇中心
def update(res, K):return np.array([data[res==k].mean(axis=0) for k in range(K)])def main(k, data, iters):m, n = np.shape(data)centroids = initial_value(n, k)  # 初始点for i in range(iters):res = distance(data, centroids)  # 最小距离索引new_centroids = update(res, k)  # 更新簇if (new_centroids == centroids).all():  # 若更新前后中心相同,跳出循环breakcentroids = new_centroidsreturn resif __name__ == '__main__':data = pd.read_csv('data.csv').valuesk, iters = 2, 100result = main(k, data, iters)print(result)

  这种方式在数据量不大的时候其实还是很好用的,但如果数据量特大,和并行和分布式比起来就不是很占优势了。

多进程并行实现

  老规矩,附上流程图:
在这里插入图片描述
  在并行中,你会发现欸,怎么这流程不太一样了。。。
  其实为了加快计算速度,我把数据分为8份丢到8个矩阵中同时计算,是不是听着就觉得快很多了呢!然后根据计算结果分别求平均值,最后找到新的中心点,循环到满意就ok啦。
  至于我为什么是分8个矩阵,进程分配主要看自己的电脑,我的电脑是8核的。

import numpy as np
import pandas as pd
import multiprocessing# 找出最优簇选择(初始)
def initial_value(n, k):minJ = np.min(data, axis=0)maxJ = np.max(data, axis=0)rangeJ = maxJ - minJcentroids = minJ + rangeJ * np.random.rand(k, 1)return centroids# 算每个的距离,,取最小距离的索引
def distance(data, centroids):d = np.sqrt(((data-centroids[:, np.newaxis])**2).sum(axis=2))return np.argmin(d, axis=0)# 每个进程平均值
def avg(K, centroids, data):res = distance(data, centroids)return np.array([data[res==k].mean(axis=0) for k in range(K)]), res    def job(z):return avg(z[0], z[1], z[2])def main(k, data, iters):m, n = np.shape(data)centroids = initial_value(n, k)  # 初始点pool = multiprocessing.Pool(processes=8)data_lst = [(k, centroids, data)]for i in range(iters):update = pool.map(job, data_lst)  # 求得每个进程平均part_centroids, res = update[0][0], update[0][1]new_centroids = part_centroids.mean(axis=0)if (centroids == new_centroids).all():  # 若更新前后中心相同,跳出循环breakcentroids = new_centroidsreturn resif __name__ == '__main__':data = pd.read_csv('data.csv').valuesk, iters = 2, 100result = main(k, data, iters)print(result)

Dask分布式实现

  嗯,上图!
分布式
  其实并行和分布式感觉上的流程是差不多的,浅显的理解一下,分布式就是利用电脑上的多个部件疯狂肝,分布式是通过局域网在多台电脑上疯狂肝。

import numpy as np
import dask.array as da
import dask.dataframe as dd
import pandas as pd
from dask.distributed import Client# 找出最优簇选择(初始)
def initial_value(n, k):minJ = da.min(data, axis=0)maxJ = da.max(data, axis=0)rangeJ = maxJ - minJcentroids = minJ + rangeJ * np.random.rand(k, 1)return centroids.compute()# 算每个的距离,,取最小距离的索引
def distance(data, centroids):d = da.sqrt(((data-centroids[:, np.newaxis])**2).sum(axis=2))return np.argmin(d, axis=0)# 每个进程平均值
def avg(K, centroids, data):res = distance(data, centroids)return da.array([data[res==k].mean(axis=0) for k in range(K)])   def job(z):return avg(z[0], z[1], z[2])def main(k, data, iters):m, n = da.shape(data)centroids = initial_value(n, k)  # 初始点data_lst = [(k, centroids, data)]for i in range(iters):update = client.map(job, data_lst)  # 求得每个进程平均new_centroids_sum = client.submit(sum, update)new_centroids = new_centroids_sum.result()/len(update)if (centroids == new_centroids.compute()).all():  # 若更新前后中心相同,跳出循环breakcentroids = new_centroidsres = distance(data, centroids)return res.compute()if __name__ == '__main__':client = Client('192.168.0.106:8786')data = pd.read_csv('data.csv').valuesk, iters = 2, 100result = main(k, data, iters)print(result)

结果对比

从下表中可以看到,速度从快到慢为:并行>Numpy>分布式>for循环。

方法耗时 ( s ) (s) (s)
for循环5296.72
Numpy383.95
并行36.26
分布式450.92

  是不是很震惊,并行和分布式的速度居然差这么多!在我的理解上,分布式的速度快慢更多的是与网络好坏有关,可能小编的网真的很差吧😔!

  这次的分享就到这啦,有什么意见或者建议可以在评论区里说说哟!


http://chatgpt.dhexx.cn/article/Kh8garM6.shtml

相关文章

k-means算法、性能及优化

k-means算法、性能及优化 一、k-means算法简介 k-means是用来解决著名的聚类问题的最简单的非监督学习算法之一。 该过程遵循一个简易的方式,将一组数据划分为预先设定好的k个簇。其主要思想是为每个簇定义一个质心。设置这些质心需要一些技巧,因为不同的…

k均值聚类算法(K Means)及其实战案例

算法说明 K均值聚类算法其实就是根据距离来看属性,近朱者赤近墨者黑。其中K表示要聚类的数量,就是说样本要被划分成几个类别。而均值则是因为需要求得每个类别的中心点,比如一维样本的中心点一般就是求这些样本的算术平均数。 这里存在一个…

详解K-Means算法

一、引言 K-Means算法是机器学习中最简单、最常见的一种聚类算法。 1.什么是聚类? 通俗来讲,聚类就是将一堆没有标签的原始样本数据,按照某个标准让其中特征一致的样本自动聚成一堆儿,最后原始样本数据聚成了一堆儿一堆儿的。 …

weighted Kernel k-means 加权核k均值算法理解及其实现(一)

那就从k-means开始吧 对于机器学习的新手小白来说,k-means算法应该都会接触到吧。传统的k-means算法是一个硬聚类(因为要指定k这个参数啦)算法。这里利用百度的解释 它是数据点到原型的某种距离作为优化的目标函数,利用函数求极值的方法得到迭代运算的调整规则。K-means算…

K-均值算法的原理与实战

K-均值(K-means)算法是一种常用的聚类算法。 当我们需要对未标记的数据划分类别时,往往用到的算法是聚类(clustering)。聚类是一种无监督的学习,它试图将数据集中的样本划分为若干个通常是不相交的子集&…

十分钟快速讲明白K均值聚类算法

K均值聚类算法 无监督学习 在一个典型的监督学习中,我们有一个有标签的训练集,我们的目标是找到能够区分正样本和负样本的决策边界,在这里的监督学习中,我们有一系列标签,我们需要据此拟合一个假设函数。 与此不同的…

深入浅出聚类算法之k-means算法

k-means是一个十分简单的聚类算法,它的思路非常简明清晰,所以经常拿来当做教学。下面就来讲述一下这个模型的细节操作。 内容 模型原理模型收敛过程模型聚类个数模型局限 1. 模型原理 将某一些数据分为不同的类别,在相同的类别中数据之间的…

c++函数调用

一个函数就是完成一项任务的独立代码块,它以名字作为整个代码块的代表 函数完成任务后,如果有数值需要返回,则在声明函数时,在函数名前写好返回值类型 如果不需要返回任何值,则在函数名前写 void 有时,一个…

c语言中函数调用的过程

一.程序在内存中的占用。 要学习C语言中函数调用的过程,必须要知道程序在内存中各个区域的分布。 C语言的函数调用的过程主要分布在栈中,所以我们今天主要研究栈。 二.几个基本的汇编指令。 call:1.将当前指令的下一条指令的地址保存到栈中…

函数定义和调用

<1>定义函数 定义函数的格式如下&#xff1a; def 函数名(): 代码 demo: 定义一个函数&#xff0c;能够完成打印信息的功能 def printInfo(): print(’------------------------------------’) print(’ 人生苦短&#xff0c;我用Python’) print(’----------------…

Shell函数调用

文章目录 一.函数基本格式二.函数调用2.1函数中调用2.2函数调用函数2.3外部调用2.4案例 三.总结 在shell脚本中&#xff0c;有些命令或者某些操作需要频繁的使用时&#xff0c;每次都重新写太过繁琐&#xff0c;这时我们就可以使用函数&#xff0c;当需要使用时&#xff0c;直接…

类的函数调用

父类和子类的函数调用 1.用指针&#xff08;引用&#xff09;调用函数的时候&#xff0c;被调用的函数取决于指针&#xff08;引用&#xff09;的类型&#xff1b; 2.涉及多态性时&#xff0c;采用虚函数和动态绑定&#xff0c;函数调用在运行时绑定&#xff0c;而非在编译时…

函数调用栈分析

进程在虚拟地址空间的布局 操作系统把磁盘上可执行文件加载到内存运行之前, 需要做很多工作, 其中很重要的一件事就是把可执行文件中的代码, 数据存放到内存 中合适的位置, 并分配和初始化程序运行过程中必须的堆栈, 所有准备工作完成之后操作系统才会调度程序起来运行. 进程…

C语言的函数调用过程

C语言的函数调用过程 先上一段代码 #include<stdio.h> int Add(int x, int y) {int z 0;z x y;return z; } #include <stdio.h> int main() {int a 10;int b 20;int c Add(a, b);return 0; } 123456789101112131415 这个函数计算两个数的值。接下来我们通…

C--函数调用

C函数的调用约定 编译器实现函数调用时所遵循的一系列规则称为函数的“调用约定&#xff08;Calling Convention&#xff09;” 对于C语言来说&#xff0c;运行在X86-64平台上的编译器基本都会根据操作系统的不同选择使用几种常见的调用约定。例如&#xff0c;在wiindows下通常…

C语言 函数调用的过程

例题&#xff1a;求两个整数中的较小者&#xff0c;用函数调用实现。 【代码实现】 int Min(int x, int y) {if (x < y){return x;}elsereturn y; } int main() {int Min(int x, int y);int a, b, c;printf("输入两个要比较的整数&#xff1a;\n");scanf("%…

函数调用的过程分析

一、函数调用机制 局部变量占用的内存是在程序执行过程中”动态”地建立和释放的。这种”动态”是通过栈由系统自动管理进行的。当任何一个函数调用发生时,系统都要作以下工作: 1)建立栈帧空间;2)保护现场: 主调函数运行状态和返回地址入栈&#xff1b;3)为被调函数传递数据(…

C/C++ 函数调用是如何实现的?

一、写在前面的话 C/C 函数调用方式与栈原理是 C/C 开发必须要掌握的基础知识&#xff0c;也是高级技术岗位面试中高频题。我真的真的真的建议无论是使用 C/C 的学生还是广大 C/C 开发者&#xff0c;都该掌握此回答中所介绍的知识。 如果你看不懂接下来第二部分在说什么&#…

函数调用过程

今天突然看到有人私信我说一直没写函数调用过程&#xff08;栈帧的形成和销毁过程&#xff09;这篇博文&#xff0c;赶紧补上。 刚看的栈帧内容时&#xff0c;我很迷惑&#xff0c;我觉得栈帧创建和销毁很麻烦&#xff0c;几句话根本说不完&#xff0c;而且我好像描述不清楚他…

浅谈函数调用!

导语 | 在任意一门编程语言中&#xff0c;函数调用基本上都是非常常见的操作&#xff1b;我们都知道&#xff0c;函数是由调用栈实现的&#xff0c;不同的函数调用会切换上下文&#xff1b;但是&#xff0c;你是否好奇&#xff0c;对于一个函数调用而言&#xff0c;其底层到底…