改进的 K-Means 聚类方法介绍

引言

数据科学的一个中心假设是,紧密度表明相关性。彼此“接近”的数据点是相似的。如果将年龄、头发数量和体重绘制在空间中,很可能许多人会聚集在一起。这就是 k 均值聚类背后的直觉。

我们随机生成 K 个质心,每个簇一个,并将每个数据点分配给与该数据点最近的质心对应的簇。然后,我们生成新的质心,每个质心都是属于该簇的所有点的平均值。然后重复这个过程直到收敛。

我们可以使用欧几里德距离作为距离度量并计算每个数据点与质心之间的距离。

def cross_euclidean_distance(x, y=None):y = x if y is None else y assert len(x.shape) >= 2assert len(y.shape) >= 2return euclidean_distance(x[..., :, None, :], y[..., None, :, :])

这样我们就可以执行 K 均值聚类算法的核心了。

def fit(self, X):cluster_ind = np.repeat(0, X.shape[0])idx = np.random.randint(X.shape[0], size=self.m_clusters)self.centroids = X.loc[idx].to_numpy()cross_dist = cross_euclidean_distance(X.to_numpy(), self.centroids)cluster_ind = np.argmin(cross_dist, axis = 1)for _ in range(self.max_iter):#Calculating new centroidsfor i in range(self.m_clusters):X_i = X[cluster_ind == i]self.centroids[i] = X_i.mean(axis = 0).to_numpy()#Assigne data points to new cluster and check if cluster assignment chengescross_dist = cross_euclidean_distance(X.to_numpy(), self.centroids)cluster_ind_new = np.argmin(cross_dist, axis = 1)if not (cluster_ind_new == cluster_ind).any():breakcluster_ind = cluster_ind_new

K-means算法保证收敛,但可能不会收敛到全局最优。相反,它经常陷入局部最优。本文的其余部分将致力于解决这个问题。

根据种子的不同,算法可能会陷入局部最优

关注公众号 [小Z的科研日常] ,查看最新技术分享。

简单的解决方案

最简单的解决方案就是使用不同的起始质心多次运行算法,然后选择最佳的聚类分配。

为了有效地做到这一点,我们需要一种衡量方法来量化集群分配的好坏。其中一种衡量标准是欧几里得畸变。每个点到质心的平均距离对应于它们分配到的簇。运行该算法几次并保存欧几里得失真最低的算法,并希望它是全局最优的。

def euclidean_distortion(X, z):X, z = np.asarray(X), np.asarray(z)assert len(X.shape) == 2assert len(z.shape) == 1assert X.shape[0] == z.shape[0]distortion = 0.0for c in np.unique(z):Xc = X[z == c]mu = Xc.mean(axis=0)distortion += ((Xc - mu) ** 2).sum()return distortion

更智能的采样

幸运的是,我们可以做得更好。这种方法的主要问题是计算成本较高。为了缓解这个问题,我们可以调整起始条件,以便我们更有可能找到全局最优值。为了了解如何实现,让我们看一个玩具问题。

我们希望我们的算法找到总共 10 个数据组。看起来有 10 个不同的簇。当我们运行算法时,我们发现我们可能会得到一个或两个集群错误。

使用 K = 10 运行 K 均值聚类后的聚类分配。我们看到分配并不完美。紫色簇太大,而黄色和青绿色簇争夺同一组。

两个组成为一个超级集群,而另一个组则由两个集群共享。发生这种情况可能是因为两个初始质心靠得太近。为了减少这种情况发生的可能性,我们可以在如何对初始质心进行采样方面变得更加智能。

我们可以不从数据中均匀采样质心,而是从加权分布中顺序采样质心,其中每个权重由到已采样质心的距离决定。这确保了我们不太可能对接近已采样质心的质心进行采样。其算法很简单。在这里,我简单地将概率视为每个数据点到最近质心的归一化距离。这确保我们不会对同一个数据点进行两次采样。

idx = np.random.choice(range(X.shape[0]), size=1)
centroids = X.loc[idx].to_numpy()
while len(centroids)<self.m_clusters:distances = cross_euclidean_distance(centroids, X.to_numpy())prob_vec = distances.min(axis = 0)prob_vec = prob_vec**2/np.sum(prob_vec**2)#Note: zero proability that new centorid is allready a centroididx = np.append(idx, np.random.choice(X.shape[0], size=1, p = prob_vec)) centroids = X.loc[idx].to_numpy()self.centroids = centroids

智能分配

然而,上述方法可能仍然有些不足。我们只是增加了不出现错误初始配置的机会。为了确保我们得到全局最大值的分配,我们仍然需要运行算法多次。这样做的问题是,每次重新启动算法时,我们都会丢弃进度。更好的方法是找出我们当前任务的问题所在以及如何改进。考虑这个集群分配。

这个簇分配非常糟糕,有两个超级簇和两对质心,彼此距离太近。

黄色和青绿色的簇占据了太多的空间,而棕色、紫色、粉色和橙色的簇则占据了很少的空间。通过智能分配算法,我们可以识别两个最近的质心,并将其中一个移动到最需要的位置。

也就是说,到距质心最远的点。如果我们进行了太多的跳跃,我们只需保存最佳的收敛统计数据,就像之前的重复过程一样。这个的实现是在下面的代码中完成的。

n_worst = int(round(X.shape[0]*self.worst_prec))
best_reroll_centroids = self.centroids
best_reroll_distortion = euclidean_distortion(X, cluster_ind)
for i in range(self.n_rerolls):# Caculate new centroidsfor i in range(self.m_clusters):X_i = X[cluster_ind == i]self.centroids[i] = X_i.mean(axis = 0).to_numpy()# Find the two centroids that are closest and pick the centoid with the lowest average distance to other centroidscentroid_dist = cross_euclidean_distance(self.centroids)cetorid_dist_inf = centroid_dist + np.diag(np.repeat(np.inf, centroid_dist.shape[0])) # Add inf to diagworst_pair = np.unravel_index((cetorid_dist_inf).argmin(), cetorid_dist_inf.shape) # Find indexes of worst pairworst_ind = worst_pair[0] if (np.mean(centroid_dist[worst_pair[0]])<np.mean(centroid_dist[worst_pair[1]])) else worst_pair[1]# Assign the old centroid to be the one closest to the poinst that are furthest away from the current centroidsmin_dists = np.min(cross_dist, axis = 1)high_dists_ind = np.argpartition(min_dists, -n_worst)[-n_worst:]X_high = X.loc[high_dists_ind]self.centroids[worst_ind] = X_high.mean(axis = 0).to_numpy()# Itterate until convergencefor _ in range(self.max_iter):#Calculating new centroidsfor i in range(self.m_clusters):X_i = X[cluster_ind == i]self.centroids[i] = X_i.mean(axis = 0).to_numpy()#Assigne data points to new cluster and check if cluster assignment chengescross_dist = cross_euclidean_distance(X.to_numpy(), self.centroids)cluster_ind_new = np.argmin(cross_dist, axis = 1)if not (cluster_ind_new == cluster_ind).any():breakcluster_ind = cluster_ind_newdistortion = euclidean_distortion(X, cluster_ind)if distortion<best_reroll_distortion:best_reroll_distortion = distortionbest_reroll_centroids = self.centroidsself.centroids = best_reroll_centroids 

下图显示了正在运行的算法。首先,粉红色的簇被移动。

粉红色的质心已经跳到绿松石色的星团上

我们看到粉红色的质心已经跳到绿松石色的星团上。此配置仍远未达到最佳状态。紫色和粉红色的簇所占的量太少,而黄色和绿松石色的簇所占的量太多。这可以通过重复该过程几次来解决。

    可以观察到紫色质心跳到粉色簇 

可以观察到紫色质心跳转到黄色质心

通过最后的跳跃,我们可以简单地运行直到收敛,最终获得接近最佳的集群分配。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/663860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

webstorm、vscode、HBuilder配置eslint检查

你们好&#xff0c;我是金金金。 场景 每个人写的代码都有自己所属的风格&#xff0c;所以项目中统一代码风格特别重要&#xff0c;新开的项目中如何快速配置ESLint呢&#xff1f; 安装 npm install --save-dev eslint ----安装eslintnpm install --save-dev eslint-plugin-vu…

事件分发机制:从OnTouchListener开始,按钮变色的Demo

要彻底弄清楚事件分发机制&#xff0c;先要明白OnTouchListener的作用。 我们看下Android 1.6上&#xff0c;OnTouchListener的代码定义&#xff0c;源码在线地址&#xff1a;Android 1.6 sdk4 View.java 可以看到&#xff0c;OnTouchListener就是View类中的一个public接口&am…

django+flask警务案件信息管理系统python-5dg53-vue

1&#xff09;用户在后台页面各种操作可及时得到反馈。 &#xff08;2&#xff09;该平台是提供给多个用户使用的平台&#xff0c;警员使用之前需要注册登录。登录验证后&#xff0c;警员才可进行各种操作[10]。 &#xff08;3&#xff09;管理员用户拥有信息新增&#xff0c;修…

Git 实战场景过程(工作总结篇)

目录 前言1. Git远程仓库建立分支&#xff0c;本地未显示1.1 问题所示1.2 知识补充 2. Git暂存内容切换分支2.1 问题所示2.2 知识补充 3. Git放弃修改数据3.1 问题所示3.2 知识补充 4. git merge合并查看差异 前言 主要总结工作中的疑惑点&#xff0c;如果你也有相应的场景&am…

跟着cherno手搓游戏引擎【19】抽象纹理

引入&#xff1a; 导入stb_image: GitHub - nothings/stb: stb single-file public domain libraries for C/C 下载复制stb_image.h的内容&#xff08;8000多行&#xff09;&#xff0c;然后粘到如图位置 stb_image.cpp: #include"ytpch.h" #define STB_IMAGE_IM…

Android进阶之路 - 通过业务(Activity)栈管理业务流程

关于业务栈的管理方式&#xff0c;我在去年刚接触当前项目的时候就想记录一下&#xff0c;但是一直晃晃悠悠拖到了现在&#xff0c;索性在春节前以其收尾也是不错。其实这篇内容在项目中肯定经常用得到&#xff0c;但是关于标题命名我却不知道如何描述… 在实际业务中为了形成业…

leetcode-用栈实现队列

232. 用栈实现队列 前面有道题是用队列实现栈 &#xff0c;本题反过来了&#xff0c;是使用两个栈来模拟队列。 题解&#xff1a; 我们定义两个栈一个进栈in_stack&#xff0c;一个出栈out_stack&#xff0c;在push数据的时候将数据放入进栈就好&#xff0c;但在pop的时候&a…

【Java】MybatisPlus入门

学习目标 能够基于MyBatisPlus完成标准Dao开发 能够掌握MyBatisPlus的条件查询 能够掌握MyBatisPlus的字段映射与表名映射 能够掌握id生成策略控制 能够理解代码生成器的相关配置 一、MyBatisPlus简介 1. 入门案例 问题导入 MyBatisPlus环境搭建的步骤&#xff1f; 1.1 Sp…

部署实战--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…

Go语言中...(三个点)的使用几个常见情况

①可变参数函数&#xff08;Variadic Functions&#xff09;&#xff1a; 在函数定义中&#xff0c;“…” 可以用于表示可变参数。这使得函数可以接受任意数量的参数。例如&#xff1a; func sum(nums ...int) int {total : 0for _, num : range nums {total num}return tot…

DPVS 多活部署架构部署

一、目标 利用DPVS部署一个基于OSPF/ECMP的提供HTTP服务的多活高可用的测试环境。 本次部署仅用于验证功能&#xff0c;不提供性能验证。 配置两台DPVS组成集群、两台REAL SERVER提供实际HTTP服务。 注&#xff1a;在虚拟环境里面&#xff0c;通过在一台虚拟服务器上面安装FR…

mybatis查询修改mysql的json字段

前言&#xff1a; mysql5.7版本之后支持json字段类型&#xff0c;推荐mysql8版本&#xff0c;适用于属性不确定的个性化字段&#xff0c;比如: 身份信息{“职业”,“学生”,“兴趣”:“打乒乓球”,“特长”:“跳高&#xff0c;书法”}; 图片信息{“日期”:“2023-12-12 22:12”…

flinkjar开发 自定义函数

编写自定义加密函数&#xff0c;继承ScalarFunction类&#xff0c;实现eval方法&#xff0c;参数个数类型和返回值根据业务来自定义。 import org.apache.flink.table.functions.ScalarFunction; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax…

ELK集群搭建(基础教程)

ELK集群搭建(基础教程) 目录&#xff1a; 机器准备 集群内各台机器安装Elasticsearch 安装部署Kafka&#xff08;注&#xff1a;每个节点都配置&#xff0c;注意ip不同&#xff09; 安装logstash工具 安装filebeat ELK收集Nginx的json日志 ELK收集Nginx正常日志和错误日…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之TimePicker组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之TimePicker组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 编辑 二、TimePicker组件 TextClock组件通过文本将当前系统时间显示在设备上。…

Java数组的静态初始化、动态初始化和默认初始化

以下是Java数组的静态初始化、动态初始化和默认初始化的示例&#xff1a; 静态初始化&#xff1a; 静态初始化是在声明数组时直接赋值&#xff0c;不需要使用new关键字。例如&#xff1a; int[] staticArray {10, 20, 30, 40};这里&#xff0c;staticArray是一个静态初始化…

华为配置使用SNMPv1与网管通信示例

配置使用SNMPv1与网管通信示例 组网图形 图1 配置使用SNMPv1与网管通信组网图 SNMP简介配置注意事项组网需求配置思路操作步骤配置文件 SNMP简介 简单网络管理协议SNMP&#xff08;Simple Network Management Protocol&#xff09;是广泛应用于TCP/IP网络的网络管理标准协议。S…

AJAX-接口文档

接口文档&#xff1a;由后端提供的描述接口的文章 接口&#xff1a;使用AJAX和服务器通讯时&#xff0c;使用的URL&#xff0c;请求方法&#xff0c;以及参数 1.请求参数的位置为query&#xff08;查询&#xff09;的时候&#xff0c;就说明要使用params写为查询参数 2.请求参…

神经网络 | 基于多种神经网络模型的轴承故障检测

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要源自《第二届全国技能大赛智能制造工程技术项目比赛试题&#xff08;样题&#xff09; 模块 E 工业大数据与人工智能应用》&#xff0c;基于给出的已知轴承状态的振动信号样本&#xff0c;对数据进行分析&#xff0c;建…

PDF中公式转word

效果&#xff1a;实现pdf中公式免编辑 step1: 截图CtrlAltA&#xff0c;复制 step2: SimpleTex - Snip & Get 网页或客户端均可&#xff0c;无次数限制&#xff0c;效果还不错。还支持手写、文字识别 单张图片&#xff1a;选 手写板 step3: 导出结果选择 注&#xff1a;…