手搓人工智能—聚类分析(下)谱系聚类与K-mean聚类

 “无论结果如何,至少我们存在过”

——《无人深空》

前言

        除了上一篇手搓人工智能-聚类分析(上)中提到的两种简单聚类方式,还有一些更为常用、更复杂的聚类方式:谱系聚类,K-均值聚类。

谱系聚类

        谱系聚类又被称为是系统聚类,层次聚类,它的思想主要来源于社会科学和生物分类学,目标不仅是要产生样本的不同聚类,而且要生成一个完整的样本分类谱系。

谱系聚类合并算法

        先给出一般谱系聚类算法:


  • 初始化:每个样本作为一个单独的聚类,C_i = \{x_i\},i=1,...,n
  • 循环,直到所有样本属于一个聚类为止:
    • 寻找当前聚类中最相近的两个聚类:d(C_i,C_j)=\underset{r,s}{min} \ d(C_i,C_j)
    • 删除聚类 C_i  和 C_j,增加新的聚类 C_q = C_i \bigcup C_j
  • 输出:样本的合并过程,形成层次化谱系

        但是,对于一个聚类问题来说,将所有样本合并为一个聚类的结果显然是毫无意义的。我们可以设置一个终止条件,当满足该条件时,输出当前的聚类。常用的终止条件包括

  • 预定类别数:预先设定一个目标聚类数,当合并过程中剩余的聚类数量达到预定的目标聚类数时,停止合并。
  • 距离阈值:预先设定一个距离阈值,当最近的两个聚类之间的距离大于阈值时,停止合并过程,输出当前的聚类。
  • “最优”聚类数:在合并过程中按照某种准则判断当前的聚类结果是否达到“最优”的聚类数,以“最优”聚类数作为终止合并的条件。依据什么样的准则确定一个给定样本集的“最优”聚类是大多聚类分析方法需要面对的问题。根据样本间距离准则,有
    • 最大距离法:以两个聚类 C_i 和 C_j 中相距最远的两个样本间距离度量聚类之间的相似程度 d(C_i,C_j)=\underset{x \in C_i,y \in C_j}{max} d(x,y)
    • 最小距离法:以两个聚类 C_i 和 C_j 中相距最近的两个样本间距离度量聚类之间的相似程度 d(C_i,C_j)=\underset{x \in C_i,y \in C_j}{min} d(x,y)
    • 平均距离法:计算两个集合中任意一对样本的距离,以所有的距离平均值来度量集合之间的相似程度 d(C_i,C_j)=\frac{1}{n_in_j}\sum_{x\in C_i,y\in C_j} d(x,y)
    • 平均样本法:以两类样本均值之间的距离度量集合之间的相似程度 d(C_i,C_j)=d(m_i,m_j)

        算法在第 K 轮合并之前需要计算 n-k+1 个聚类之间的距离,生成整个谱系需要 n 轮合并,因此总的距离计算次数是:

\sum_{k=1}^{n}\binom{n-k+1}{2}=\sum_{k=1}^{n}\frac{(n-k+1)(n-k)}{2}=\frac{n^3}{6}-\frac{n}{6}

        在具体实现过程中,可以利用距离矩阵 D 来记录当前各个聚类之间的距离,通过寻找其中的最小元素来确定下一步进行合并的两个聚类;合并聚类后更新距离矩阵 D,根据所用不同距离度量方式计算新生成聚类与其他聚类之间的距离

        优化后的算法流程如下:


  • 初始化:每个样本作为一个单独的聚类,C_i={x_i},i=1,2,\cdots,n,每个聚类的样本数:n_i=1,计算每个样本间的距离,构成距离矩阵 D=(D_{ij}=d(x_i,x_j))_{n*n},聚类数 l=n
  • 循环,直到满足聚类的终止条件为止:
    • 寻找距离矩阵 D 中上三角矩阵元素的最小值 D_{ij}
    • 删除聚类 C_i,C_j,增加新的聚类 C_q=C_i \bigcup C_j,n_q=n_i+n_j,l=l-1
    • 更新距离矩阵
  • 输出:聚类\{C_1,\cdots,C_l\},聚类数 l

示例代码(C++)

采用距离度量为欧式距离,取最小距离

void Clustering::Lineageclustering(std::vector<std::vector<double>> sample, double theta, const char* type, int p)
{Distance distance;std::vector<std::vector<double>> Distancemartix;std::vector<std::vector<double>> centers;//初始化for (int i = 0; i < sample.size(); i++) {clusters.push_back(std::vector<std::vector<double>>());clusters[i].push_back(sample[i]);}Distancemartix = distance.DistanceMartix(sample, type, -1);//遍历每个样本double min = 0;while (min < theta) {//更新聚类中心for (int m = 0; m < clusters.size(); m++) {centers.push_back(GetCenter(clusters[m]));}//找到最近的两个聚类,并合并聚类min = DBL_MAX;int select_index1 = 0;int select_index2 = 0;for (int i = 0; i < centers.size(); i++) {for (int j = i; j < centers.size(); j++) {if (i == j) continue;min = (min < distance.SelectDistance(centers[i], centers[j], type, p)) ? min : distance.SelectDistance(centers[i], centers[j], type, p);select_index1 = (min < distance.SelectDistance(centers[i], centers[j], type, p)) ? select_index1 : i;select_index2 = (min < distance.SelectDistance(centers[i], centers[j], type, p)) ? select_index2 : j;}}for (int i = 0; i < clusters[select_index2].size(); i++) {clusters[select_index1].push_back(clusters[select_index2][i]);}clusters.erase(clusters.begin() + select_index2);if (centers.size() < 2) {break;}}
}

输入测试样本 ,阈值设为2

data = { {1, 1}, {1, 3}, {3, 5}, {3, 3}, {3, 7}, {3, 9} };

 分类结果

Cluster 0: (1, 1)
Cluster 1: (1, 3)
Cluster 2: (3, 5)
Cluster 3: (3, 3)
Cluster 4: (3, 7) (3, 9)

K-mean 聚类

        K-均值算法的想法最早由 Hugo Steinhaus 于 1957 年提出,Stuart Lloyd 于 1957 年在 Bell 实验室给出了标准K-均值聚类算法。由于算法实现简单,计算复杂度和存储复杂度低,对很多简单的聚类问题可以得到令人满意的结果,K-均值算法已经成为最著名和常用的聚类算法之一

K-mean 算法的目标是将 n 个样本依据最小化类内距离的准则分到 K 个聚类之中

\underset{C_1,\cdots,C_K}{min}J_w(C_1,\cdots,C_K)=\frac{1}{n}\sum_{j=1}^{K}\sum_{x\in C_j} ||x-m_j||^2

m_j=\frac{1}{n_j}\sum_{x \in C_j}x

直接对上述类内距离准则优化存在一定困难,不妨换一个思路:

首先假设每个聚类的均值 m_1,\cdots,m_K 是固定已知的,那么这个优化问题就很容易求解了

现在这个问题变成了:

每一个样本 x 选择加入一个聚类 C_j,使得类内距离准则最小

很显然,如果 j=\underset{1\leq i \leq K}{argmin} \ ||x-m_i||^2,应该将样本 x 放入聚类 C_j,这样可以使得 J_w最小。

然而,已知每个聚类的均值 m_1,\cdots,m_K 的假设是不成立的,因为在知道每个聚类包含哪个样本之前是无法求得样本均值的,聚类的均值只能根据这个聚类中所有样本求得。

K-均值的思想是首先对每类均值做出假设 m_1,\cdots,m_k ,得到对聚类结果的猜想 C_1,\cdots,C_K,将样本分类后更新均值,得到一个迭代的过程

m_1,\cdots,m_K\rightarrow C_1,\cdots,C_K\rightarrow m_1,\cdots,m_K\rightarrow \cdots

经过多轮迭代,分类结果不再发生变化,可以认为算法收敛到了一个对 J_w 的优化结果

 K-mean 聚类算法

K-mean算法流程


  • 初始化:随机选择K个聚类均值 m_j,j=1,\cdots,K
  • 循环,直到K个均值不在变化为止
    • C_j=\varnothing ,j=1,\cdots,K
    • for i = 1 to n
      • k=\underset{1\leq j\leq K}{argmin}\ ||x_i-m_j||,C_k=C_k\bigcup\{x_i\}
    • 更新 K 个聚类的均值:
      • m_j=\frac{1}{n_j}\sum_{x\in C_j},j=1,\cdots,K
  • 输出:聚类\{C_1,\cdots,C_K\}

示例代码(C++) 

struct Point {std::vector<double> coordinates;
};// 计算两个数据点之间的欧氏距离
double distance(const Point& p1, const Point& p2) {double sum = 0.0;for (size_t i = 0; i < p1.coordinates.size(); ++i) {sum += std::pow(p1.coordinates[i] - p2.coordinates[i], 2);}return std::sqrt(sum);
}// K-means聚类算法
std::vector<int> kMeans(const std::vector<Point>& data, int k, int maxIterations) {std::vector<int> labels(data.size(), 0); // 存储每个数据点的簇标签std::vector<Point> centroids(k); // 存储簇中心// 随机初始化簇中心std::srand(std::time(0));for (int i = 0; i < k; ++i) {int randomIndex = std::rand() % data.size();centroids[i] = data[randomIndex];}for (int iteration = 0; iteration < maxIterations; ++iteration) {// 分配数据点到最近的簇中心for (size_t i = 0; i < data.size(); ++i) {double minDist = std::numeric_limits<double>::max();int closestCentroid = 0;for (int j = 0; j < k; ++j) {double dist = distance(data[i], centroids[j]);if (dist < minDist) {minDist = dist;closestCentroid = j;}}labels[i] = closestCentroid;}// 更新簇中心std::vector<int> count(k, 0);std::vector<Point> newCentroids(k, {std::vector<double>(data[0].coordinates.size(), 0.0)});for (size_t i = 0; i < data.size(); ++i) {int clusterIndex = labels[i];count[clusterIndex]++;for (size_t j = 0; j < data[i].coordinates.size(); ++j) {newCentroids[clusterIndex].coordinates[j] += data[i].coordinates[j];}}for (int i = 0; i < k; ++i) {for (size_t j = 0; j < data[0].coordinates.size(); ++j) {centroids[i].coordinates[j] = newCentroids[i].coordinates[j] / count[i];}}}return labels;
}

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

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

相关文章

文件内容扫描工具

简介 文件扫描助手是一款基于Vite Vue 3 Electron技术栈开发的跨平台桌面应用程序。它提供了强大的文件内容搜索功能&#xff0c;支持Word、Excel、PDF、PPT等常见办公文档格式。用户可以通过关键词快速定位到包含特定内容的文件&#xff0c;极大地提高了文件管理和查找效率…

函数类型注释和Union联合类型注释

函数类型注释格式&#xff08;调用时提示输入参数的类型&#xff09;: )def 函数名(形参名:类型&#xff0c;形参名:类型&#xff09;->函数返回值类型: 函数体 Union联合类型注释&#xff08;可注释多种类型混合的变量&#xff09;格式: #先导入模块 from typing import…

AIGC--AIGC与人机协作:新的创作模式

AIGC与人机协作&#xff1a;新的创作模式 引言 人工智能生成内容&#xff08;AIGC&#xff09;正在以惊人的速度渗透到创作的各个领域。从生成文本、音乐、到图像和视频&#xff0c;AIGC使得创作过程变得更加快捷和高效。然而&#xff0c;AIGC并非完全取代了人类的创作角色&am…

【ue5】UE5运行时下载视频/UE5 runtime download video(MP4)

插件还是老朋友。 节点的content type要打对。 &#xff08;参照表&#xff1a;MIME 类型&#xff08;MIME Type&#xff09;完整对照表 - 免费在线工具&#xff09; 结果展示&#xff1a;

STM32F103外部中断配置

一、外部中断 在上一节我们介绍了STM32f103的嵌套向量中断控制器&#xff0c;其中包括中断的使能、失能、中断优先级分组以及中断优先级配置等内容。 1.1 外部中断/事件控制器 在STM32f103支持的60个可屏蔽中断中&#xff0c;有一些比较特殊的中断&#xff1a; 中断编号13 EXTI…

C嘎嘎探索篇:栈与队列的交响:C++中的结构艺术

C嘎嘎探索篇&#xff1a;栈与队列的交响&#xff1a;C中的结构艺术 前言&#xff1a; 小编在之前刚完成了C中栈和队列&#xff08;stack和queue&#xff09;的讲解&#xff0c;忘记的小伙伴可以去我上一篇文章看一眼的&#xff0c;今天小编将会带领大家吹奏栈和队列的交响&am…

【c语言】文件操作详解 - 从打开到关闭

文章目录 1. 为什么使用文件&#xff1f;2. 什么是文件&#xff1f;3. 如何标识文件&#xff1f;4. 二进制文件和文本文件&#xff1f;5. 文件的打开和关闭5.1 流和标准流5.1.1 流5.1.2 标准流 5.2 文件指针5.3 文件的打开和关闭 6. 文件的读写顺序6.1 顺序读写函数6.2 对比一组…

从 0 到 1 掌握部署第一个 Web 应用到 Kubernetes 中

文章目录 前言构建一个 hello world web 应用项目结构项目核心文件启动项目 检查项目是否构建成功 容器化我们的应用编写 Dockerfile构建 docker 镜像推送 docker 镜像仓库 使用 labs.play-with-k8s.com 构建 Kubernetes 集群并部署应用构建 Kubernetes 集群环境编写部署文件 总…

Matlab以一个图像分类例子总结分类学习的使用方法

目录 前言 导入数据 训练学习 导出训练模型 仿真测试 总结 前言 最近在尝试一些基于Simulink的边沿AI部署,通过这个案例总结Matlab 分类学习功能的使用。本案例通过输入3000张28*28的灰度图像,训练分类学习模型。并验证训练好的模型最后部署到MCU。 导入数据 如下图是…

2025蓝桥杯(单片机)备赛--扩展外设之UART1的原理与应用(十二)

一、串口1的实现原理 a.查看STC15F2K60S2数据手册: 串口一在590页&#xff0c;此款单片机有两个串口。 串口1相关寄存器&#xff1a; SCON:串行控制寄存器&#xff08;可位寻址&#xff09; SCON寄存器说明&#xff1a; 需要PCON寄存器的SMOD0/PCON.6为0&#xff0c;使SM0和SM…

Reactor 模式的理论与实践

1. 引言 1.1 什么是 Reactor 模式&#xff1f; Reactor 模式是一种用于处理高性能 I/O 的设计模式&#xff0c;专注于通过非阻塞 I/O 和事件驱动机制实现高并发性能。它的核心思想是将 I/O 操作的事件分离出来&#xff0c;通过事件分发器&#xff08;Reactor&#xff09;将事…

【Android+多线程】IntentService 知识总结:应用场景 / 使用步骤 / 源码分析

定义 IntentService 是 Android中的一个封装类&#xff0c;继承自四大组件之一的Service 功能 处理异步请求 & 实现多线程 应用场景 线程任务 需 按顺序、在后台执行 最常见的场景&#xff1a;离线下载不符合多个数据同时请求的场景&#xff1a;所有的任务都在同一个T…

Easy Excel 通过【自定义批注拦截器】实现导出的【批注】功能

目录 Easy Excel 通过 【自定义批注拦截器】实现导出的【批注】功能需求原型&#xff1a;相关数据&#xff1a;要导出的对象字段postman 格式导出对象VO 自定义批注拦截器业务代码&#xff1a; 拦截器代码解释&#xff1a;详细解释&#xff1a;格式优化&#xff1a; Easy Excel…

Spring Boot 的 WebClient 实践教程

什么是 WebClient&#xff1f; 在 Spring Boot 中&#xff0c;WebClient 是 Spring WebFlux 提供的一个非阻塞、响应式的 HTTP 客户端&#xff0c;用于与 RESTful 服务或其他 HTTP 服务交互。相比于传统的 RestTemplate&#xff0c;WebClient 更加现代化&#xff0c;具有异步和…

QML学习 —— 29、3种不同使用动画的方式(附源码)

效果 说明 第一种:属性动画 - 当启动软件时候自动执行动画。      第二种:行为动画 - 当属性发生变化则自动执行动画。      第三种:目标动画 - 将动画变为对象,指定对象的目标进行执行动画。 代码 import QtQuick 2.12 import QtQuick.Window 2.12 import QtQu…

Redis缓存穿透及常见的解决方案

一.什么是缓存穿透&#xff1f; 缓存穿透是指当客户端请求的数据在缓存&#xff08;如 Redis&#xff09;中不存在&#xff0c;并且在数据库中也不存在时&#xff0c;直接绕过缓存去请求数据库。这种情况会导致&#xff1a; 缓存系统无法发挥作用&#xff0c;数据每次都会直接…

(原创)Android Studio新老界面UI切换及老版本下载地址

前言 这两天下载了一个新版的Android Studio&#xff0c;发现整个界面都发生了很大改动&#xff1a; 新的界面的一些设置可参考一些博客&#xff1a; Android Studio新版UI常用设置 但是对于一些急着开发的小伙伴来说&#xff0c;没有时间去适应&#xff0c;那么怎么办呢&am…

windows下安装wsl的ubuntu,同时配置深度学习环境

写在前面&#xff0c;本次文章只是个人学习记录&#xff0c;不具备教程的作用。个别信息是网上的&#xff0c;我会标注&#xff0c;个人是gpt生成的 安装wsl 直接看这个就行&#xff1b;可以不用备份软件源。 https://blog.csdn.net/weixin_44301630/article/details/1223900…

Node.js的http模块:创建HTTP服务器、客户端示例

新书速览|Vue.jsNode.js全栈开发实战-CSDN博客 《Vue.jsNode.js全栈开发实战&#xff08;第2版&#xff09;&#xff08;Web前端技术丛书&#xff09;》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com) 要使用http模块&#xff0c;只需要在文件中通过require(http)引入即可。…

AI赋能电商:构建高效、智能化的新零售生态

随着人工智能&#xff08;AI&#xff09;技术的不断进步&#xff0c;其在电商领域的应用日益广泛&#xff0c;从购物推荐到供应链管理&#xff0c;再到商品定价&#xff0c;AI正在全面改变传统电商的运营模式&#xff0c;并推动行业向智能化和精细化方向发展。本文将探讨如何利…