聚类 之 MeanShift

article/2025/11/6 16:46:31

文章目录

      • Meanshift 聚类基本原理
      • Meanshift 聚类流程简述
      • 实例演示
      • MeanShift聚类简易应用示例
      • 总结
      • 拓展阅读

上篇博客介绍了基于距离的K-Means聚类,这次给大家推荐一个基于密度的聚类算法:Meanshift(均值漂移)。

Meanshift 聚类基本原理

Meanshift 聚类的主要思路是:计算某一点A与其半径R内的点之间向量距离的平均值M,得到该点下一步的漂移(移动)方向(A=M+A)和距离(||M||)。当该点不再移动时,计算这个点与历史簇中心的距离,满足小于阈值D即合并为同一个类簇,不满足则自身形成一个类簇。直到所有的数据点选取完毕。

Meanshift 聚类流程简述

相比 K-Means 聚类,Meanshift 最大的优势是不需要人为指定分成几类。该算法会根据分布密度自动将数据归到适合的类中。

Meanshift 聚类算法的大致思想就是 “哪里人多哪里跑” :

首先,将所有数据点设置为未标价状态。

  1. 未被标记的数据中随机选取一个点作为初始大佬(质心);
    由于事先并不知道会聚成几类,所以只能一类一类的聚,最后聚成几类就几类了╮(╯▽╰)╭。
  2. 以当前大佬为圆心,半径为 R R R(超参1) 画个圆,圆内的点记做集合 M M M,里面为该位大佬的小弟;为了方便日后相认,大佬决定给小弟们一张带来自己标识的保命卡进行标记
  3. 由于是随机选择的大佬,难以服众。这位大佬的小弟们提出要召开选举大会,选出新的大佬(即通过算法选出新的质心);
  • 投票依据:这一届的小弟都很喜欢热闹,哪里人多就往哪边投。依次计算各位小弟到该大佬的向量距离(黑色向量)的平均值 s h i f t shift shift(绿色向量),如下图所示。
  • 更新方式:旧大佬(蓝色圆圈)沿着投票方向即shift向量的方向移动,移动距离是||shift||,即为新大佬的位置(橙色圆圈)。也就是说这个大佬是“虚拟的”。
  1. 迭代2~3步骤,直到新大佬和旧大佬之间的偏移量小于某一个设置的阈值 δ \delta δ (超参2),即表示重新计算的质心的位置变化不大,趋于稳定,或者说收敛。从而得到一个候选大佬(还不是正式的哈,晋级之路漫漫其修远兮)。
  2. 比较这位候选大佬跟正式大佬的距离,若距离小于指定的阈值 ω \omega ω(超参3),则合并这两个大佬。否则,将该候选大佬提升为正式大佬,类别数加1;
  3. 迭代上述1~5步骤,直到所有数据点都被标记;
  4. 正式大佬全部确定下来后,小弟们就要选择投靠哪位大佬了。根据大佬在晋级路上发给小弟们的保命卡数量(即每个正式大佬对每个小弟的访问频数),小弟们选择投靠给自己保命卡最多的大佬(即对自己访问频数最大的那个大佬)。看样子小弟们还是很念旧情的哈。
  • 注意:上面涉及的三个超参是需要用户人为指定的哈。

在这里插入图片描述

实例演示

看了上面的原理简述,估计还是有点糊涂,下面举一个非常形象简单的例子。

如下图所示,有6个点,从图上看应该可以分成两堆,前三个点一堆,后三个点另一堆。现在我手工地把MeanShift 算法的计算过程演示一下,同时检验是不是和预期一致:
在这里插入图片描述

  1. 随机选择一个初始大佬(就选 P2),开始它的晋级之路;

  2. R = 5 R=5 R=5 为半径,圈定大佬的势力范围。在范围内的点 P1、P3 即为该大佬的小弟。大佬为了笼络小弟们的心,给范围内的小弟人手一张保命卡。此时所有数据点的保命符情况:

保命符P1P2P3P4P5P6
大佬1101000
  1. 召开选举大会,重新选大佬。

    首先,计算各位小弟到该大佬的向量距离(黑色向量)的平均值 s h i f t shift shift

P1P3
距离向量 ( − 1 , − 2 ) (-1,-2) (1,2) ( 2 , − 1 ) (2,-1) (2,1)

则, s h i f t = ( − 1 + 2 2 , − 2 − 1 2 ) = ( 0.5 , − 1.5 ) shift=(\dfrac{-1+2}{2}, \dfrac{-2-1}{2})=(0.5,-1.5) shift=(21+2221)=0.51.5

此时,旧大佬沿着shift向量的方向进行漂移,漂移长度为||shift||,即为新大佬 P 哥的位置: ( 1 , 2 ) + ( 0.5 , − 1.5 ) = ( 1.5 , 0.5 ) (1,2)+(0.5, -1.5) = (1.5, 0.5) (1,2)+(0.5,1.5)=(1.5,0.5)

  1. 令超参 δ = 1 \delta=1 δ=1,由于上一步偏移距离 ∣ ∣ s h i f t ∣ ∣ = ( 0. 5 2 + ( − 1.5 ) 2 ) > δ ||shift||=\sqrt{(0.5^2+(-1.5)^2)}>\delta shift=(0.52+(1.5)2) >δ,所以再次以 R = 5 R=5 R=5 为半径,圈定大佬的势力范围。在范围内的点 P1、P2、P3 即为该大佬的小弟。大佬为了笼络小弟们的心,给范围内的小弟人手一张保命卡。此时所有数据点的保命符情况:
保命符P1P2P3P4P5P6
大佬1212000
  1. 第二届选举大会:
    首先,计算各位小弟到该大佬的向量距离(黑色向量)的平均值 s h i f t shift shift
P1P2P3
距离向量 ( − 1.5 , − 0.5 ) (-1.5,-0.5) (1.5,0.5) ( − 0.5 , 1.5 ) (-0.5,1.5) (0.5,1.5) ( 1.5 , 0.5 ) (1.5,0.5) (1.5,0.5)

则, s h i f t = ( − 1.5 − 0.5 + 1.5 3 , − 0.5 + 1.5 + 0.5 3 ) = ( − 0.5 / 3 , 0.5 ) shift=(\dfrac{-1.5-0.5+1.5}{3}, \dfrac{-0.5+1.5+0.5}{3})=(-0.5/3,0.5) shift=(31.50.5+1.530.5+1.5+0.5)=0.5/30.5

同样的方法选出新的虚拟大佬:P哥 ( 1.5 , 0.5 ) + ( − 0.5 / 3 , 0.5 ) = ( 4 / 3 , 1 ) (1.5, 0.5)+(-0.5/3, 0.5) = (4/3, 1) (1.5,0.5)+(0.5/3,0.5)=(4/3,1)

  1. 由于上一步偏移距离 ∣ ∣ s h i f t ∣ ∣ = ( ( 0.5 / 3 ) 2 + ( 0.5 ) 2 ) < δ ||shift||=\sqrt{((0.5/3)^2+(0.5)^2)}<\delta shift=((0.5/3)2+(0.5)2) <δ,当前的大佬 P 哥终于晋级为候选大佬了。

  2. 比较候选大佬 P 哥跟正式大佬的距离。由于目前还没有正式大佬,P哥直接晋级为正式大佬了,晋级之路结束。此时,所有数据点的保命符情况:

保命符P1P2P3P4P5P6
大佬1212000
  1. 回到第一步,从没有保命卡的数据里随机选一个当第二个初始大佬(就选 P4),开始它的晋级之路;

  2. R = 5 R=5 R=5 为半径,圈定大佬的势力范围。在范围内的点 P5、P6 即为该大佬的小弟。大佬为了笼络小弟们的心,给范围内的小弟人手一张保命卡。此时所有数据点的保命符情况:

保命符P1P2P3P4P5P6
大佬2000011
  1. 召开选举大会,重新选大佬。

    首先,计算各位小弟到该大佬的向量距离(黑色向量)的平均值 s h i f t shift shift

P5P6
距离向量 ( 1 , 2 ) (1,2) (1,2) ( 2 , − 1 ) (2,-1) (2,1)

则, s h i f t = ( 1 + 2 2 , 2 − 1 2 ) = ( 1.5 , 0.5 ) shift=(\dfrac{1+2}{2}, \dfrac{2-1}{2})=(1.5,0.5) shift=(21+2221)=1.50.5

此时,旧大佬沿着shift向量的方向进行漂移,漂移长度为||shift||,即为新大佬 P 哥的位置即为: ( 8 , 8 ) + ( 1.5 , 0.5 ) = ( 9.5 , 8.5 ) (8,8)+(1.5, 0.5) = (9.5, 8.5) (8,8)+(1.5,0.5)=(9.5,8.5)

  1. 由于上一步偏移距离 ∣ ∣ s h i f t ∣ ∣ = ( 1. 5 2 + ( 0.5 ) 2 ) > δ ||shift||=\sqrt{(1.5^2+(0.5)^2)}>\delta shift=(1.52+(0.5)2) >δ,所以再次以 R = 5 R=5 R=5 为半径,圈定大佬的势力范围。在范围内的点 P4、P5、P6 即为该大佬的小弟。大佬为了笼络小弟们的心,给范围内的小弟人手一张保命卡。此时所有数据点的保命符情况:
保命符P1P2P3P4P5P6
大佬2000122
  1. 第二届选举大会:
    首先,计算各位小弟到该大佬的向量距离(黑色向量)的平均值 s h i f t shift shift
P4P5P6
距离向量 ( − 1.5 , − 0.5 ) (-1.5,-0.5) (1.5,0.5) ( − 0.5 , 1.5 ) (-0.5,1.5) (0.5,1.5) ( 0.5 , − 1.5 ) (0.5,-1.5) (0.5,1.5)

则, s h i f t = ( − 1.5 − 0.5 + 0.5 3 , − 0.5 + 1.5 − 1.5 3 ) = ( − 0.5 , − 0.5 / 3 ) shift=(\dfrac{-1.5-0.5+0.5}{3}, \dfrac{-0.5+1.5-1.5}{3})=(-0.5,-0.5/3) shift=(31.50.5+0.530.5+1.51.5)=0.50.5/3

同样的方法选出新的虚拟大佬:P哥 ( 9.5 , 8.5 ) + ( − 0.5 , − 0.5 / 3 ) = ( 9 , 25 / 3 ) (9.5, 8.5)+(-0.5, -0.5/3) = (9, 25/3) (9.5,8.5)+(0.5,0.5/3)=(9,25/3)

  1. 由于上一步偏移距离 ∣ ∣ s h i f t ∣ ∣ = ( ( 0.5 ) 2 + ( − 0.5 / 3 ) 2 ) < δ ||shift||=\sqrt{((0.5)^2+(-0.5/3)^2)}<\delta shift=((0.5)2+(0.5/3)2) <δ,当前的大佬 P 哥终于晋级为候选大佬了。

  2. 令超参 ω = 3 \omega=3 ω=3,比较候选大佬 P 哥跟所有正式大佬的距离。

目前正式大佬有一个 (5/3, 1),由于他们的距离大于阈值 ω \omega ω。P哥晋级为正式大佬了,晋级之路结束。此时,所有数据点的保命符情况:

保命符P1P2P3P4P5P6
大佬2000122
  1. 小弟们就要选择投靠哪位大佬了。此时,比较各位正式大佬在晋级路上给想所有数据点发的保命符情况:
保命符P1P2P3P4P5P6
大佬1 (4/3, 1)212000
大佬2 (9, 25/3)000122

小弟们选择投靠给自己保命卡最多的大佬,即
大佬1:P1、P2、P3
大佬2:P4、P5、P6

注意:假设第二个候选大佬 (9, 25/3) 跟正式大佬 (5/3, 1) 的距离小于阈值时,比如超参 ω = 20 \omega=20 ω=20,则当前候选大佬晋级失败,它发放的保命卡会被合并到该正式大佬中去,即:

保命符P1P2P3P4P5P6
大佬1212122

MeanShift聚类简易应用示例

关于 MeanShift 算法的具体代码,网上有各种版本,这里就不赘述了,下面结合 python 中的一些函数给出一个较为简洁的版本:

>>> from sklearn.cluster import MeanShift
>>> import numpy as np
>>> X = np.array([[0, 0], [1, 2], [3, 1],[8, 8], [9, 10], [10, 7]])
>>> clustering = MeanShift(bandwidth=5).fit(X)
>>> clustering.predict([[0, 0], [1, 2], [3, 1],[8, 8], [9, 10], [10, 7]])>>> print(clustering.cluster_centers_)array([[9.        , 8.33333333],[1.33333333, 1.        ]])

可以看到,计算结果与手工推导一致。

总结

总而言之,投票时,KMeans中的小弟比较懒,投票的标准是:离哪位大佬近就投给谁。新大佬的位置即为小弟们坐标的加和平均;
MeanShift: 小弟是墙头草,投票的标准是:哪里的小弟多就往哪边投。新大佬的位置为旧大佬沿着小弟们投票出来的方向和距离漂移得到;

投票方式更新方式
KMeans离谁近投给谁小弟坐标平均均值
MeanShift哪里人多往哪边投沿平均方向偏移

拓展阅读

该部分只对 MeanShift 算法的拓展进行记录,不会深究,感兴趣的可以去查找相关资料。

  1. 核函数:在计算漂移向量的过程中,势力范围内小弟的贡献都是一样的。按照近朱者赤近墨者黑的原则,离中心点越近的小弟的影响力就会越大。因此就有人提出范围内的点需要设置不同的权重来作用于漂移向量的计算,故提出了核函数的概念。比较常见的有高斯核。
  2. 图像追踪:跟踪就是让第K帧的检测框移动到K+1帧对应位置上,这时候就需要指明移动的方向及位移,即mean shift算法中的漂移向量。

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

相关文章

Muduo源码剖析

1、总体流程 1. acceptor 进行listen阶段后&#xff0c; 往channel中注册可读事件。 2. acceptor可读处理中生成TcpConnection指针&#xff0c;通过EventloopThreadPool 轮询出其中一个线程的eventloop, 并将此TcpConnection的可读、可写等事件注册到自己Channel&#xff08;ev…

Muduo - Reactor模式

Muduo - Reactor模式 一、Reactor 是什么 wiki的中文定义&#xff1a;Reactor模式是事件驱动的&#xff0c;有一个或多个并发输入源&#xff0c;有一个Service Handler&#xff0c;有多个Request Handler&#xff0c;这个Service Handler会同步的将输入的请求&#xff08;Even…

muduo网络库——ThreadPool

模型 源码分析 1&#xff09;接口 class ThreadPool : noncopyable {public:typedef std::function<void ()> Task;explicit ThreadPool(const string& nameArg string("ThreadPool"));~ThreadPool();void setMaxQueueSize(int maxSize) { maxQueueSize…

muduo网络库——Channel

模型 实现流程&#xff1a; 前面已经介绍了EPoller类&#xff0c;EPoller主要监听的是Channel对象&#xff0c;每一个Channel对象会绑定一个文件描述符&#xff08;fd_&#xff09;&#xff0c;fd_上绑定要监听的事件。当epoll监听到就绪事件时&#xff0c;会将就绪事件添加到…

muduo源码分析之Buffer

这一次我们来分析下muduo中Buffer的作用&#xff0c;我们知道&#xff0c;当我们客户端向服务器发送数据时候&#xff0c;服务器就会读取我们发送的数据&#xff0c;然后进行一系列处理&#xff0c;然后再发送到其他地方&#xff0c;在这里我们想象一下最简单的EchoServer服务器…

从实例看muduo网络库各模块交互过程

文章目录 muduo网络库的核心代码模块各模块功能解释ChannelPollerEpollPoller EventLoopEventLoopThreadEventLoopThreadPoolTcpServerTcpConnection 从实际应用出发 muduo网络库的核心代码模块 1、channel 2、Poller 和它的子类 EpollPoller 3、EventLoop 4、Thread、EventLo…

muduo总结

本文重点在muduo TcpServer的启动&#xff0c;I/O线程池的启动&#xff0c;以及各种回调 文章目录 baseAsyncLogging.{h,cc}Atomic.hBlockinQueue.hBoundedBlockinQueue.hCondition.hcopyable.hCountDownLatch.{h,cc}Date.{h,cc}Exception.{h,cc}Logging.{h,cc}Mutex.hProcess…

muduo网络库——日志处理

测试程序 #include "muduo/base/AsyncLogging.h" #include "muduo/base/Logging.h" #include "muduo/base/Timestamp.h"#include <stdio.h> #include <sys/resource.h> #include <unistd.h>off_t kRollSize 500*1000*1000;m…

Muduo日志模块详解

Muduo日志模块解析 图片取自muduo网络库源码解析(1):多线程异步日志库(上)_李兆龙的技术博客_51CTO博客也是很好的日志讲解博客,这篇讲解流程基本上和它差不多,并且写的比我条理清楚很多 AppendFile::append() 这个函数是日志写入文件的最终函数,并且AppendFile这个类里面也是…

Muduo 定时器

TimeQueue定时器 图片转载自:muduo网络库源码解析(4):TimerQueue定时机制_李兆龙的技术博客_51CTO博客 添加新的定时器 TimerId TimerQueue::addTimer(TimerCallback cb, //用户自定义回调Timestamp when, //定时器的超时时刻double interval) //重复触发间隔,小于0则不重…

《muduo网络库》学习笔记——muduo学习总结

muduo是基于非阻塞的IO和事件驱动的网络库&#xff08;Reactor模式&#xff09;&#xff0c;其核心是一个事件循环EventLoop&#xff0c;用于响应计时器和IO事件。muduo采用基于对象&#xff08;object-based&#xff09;而非面向对象&#xff08;object-oriented&#xff09;的…

Ubuntu安装muduo库

1. 首先安装boost库&#xff1b; sudo apt-get update sudo apt-get install libboost-all-dev 2. 下载muduo库&#xff0c; https://github.com/chenshuo/muduo 3. 解压后进入解压目录&#xff0c;vim CMakeLists.txt&#xff0c;注释掉略过unit_test测试用例代码的编译&#…

linux muduo 编译安装,muduo记录

1.muduo编译安装 编译muduo遇见的报错可以在github上的issue上面查找。一般都能顺利解决,我遇到的就是没有安装boost-dev. centos7系统 执行: sudo yum install boost-dev 2.截取流程图 图片截取自《Linux多线程服务端编程&#xff1a;使用muduo C网络库》 3.源码摘录 摘录一个…

muduo源码分析之TcpServer模块

这次我们开始muduo源代码的实际编写&#xff0c;首先我们知道muduo是LT模式&#xff0c;Reactor模式&#xff0c;下图为Reactor模式的流程图[来源1] 然后我们来看下muduo的整体架构[来源1] 首先muduo有一个主反应堆mainReactor以及几个子反应堆subReactor&#xff0c;其中子反应…

muduo网络库学习(1)

muduo网络库学习&#xff08;1&#xff09; 文章目录 muduo网络库学习&#xff08;1&#xff09;前言一、muduo是什么&#xff1f;二、代码结构1.base库2.net库3.附属库 二、网络库结构总结 前言 本章节主要介绍muduo网络库的整体架构&#xff01;一、muduo是什么&#xff1f;…

muduo

muduo 概述 muduo是基于Reactor模式的网络库&#xff0c;用于响应计时器和IO事件。 muduo采用基于对象而非面向对象的设计风格&#xff0c;其事件回调采用functionbind&#xff0c;用户在使用muduo的时候不需要继承其中的class 架构 Multiple Reactor Reactor模式&#xff1a…

muduo日志库原理以及源码分析

muduo日志库特点 日志批量写入批量唤醒写线程写日志用notifywait_timeout 方式触发日志的写入锁的粒度&#xff0c;双缓冲&#xff0c;双队列buffer默认 4M 缓冲区&#xff0c; buffers 是 buffer 队列&#xff0c; push 、 pop 时使用 move 语义 减少内存拷贝 muduo的这些特点…

muduo网络库与服务模型介绍

目录 一、muduo网络库简介 1、特点 2、代码结构 &#xff08;1&#xff09;公共接口 &#xff08;2&#xff09;内部实现 二、muduo线程模型 1、单线程Reactor 2、Reactor线程池 3、one loop per thread 4、one loop per thread 线程池 muduo是陈硕个人使用C开发的一…

muduo 架构解析

muduo是一个基于Reactor模式的C网络库。它采用非阻塞I/O模型&#xff0c;基于事件驱动和回调。我们不仅可以通过muduo来学习linux服务端多线程编程&#xff0c;还可以通过它来学习C11。     Reactor是网络编程的一般范式。我们这里从reactor模式为出发点&#xff0c;根据R…

muduo库介绍

muduo库是一个多线程服务器开发库 muduo 作者陈硕&#xff0c;现在在美国加州硅谷某互联网大公司工作&#xff0c;从事大规模分布式的可靠系统工程。这个库是作者多年工作的总结&#xff0c;可以说大家学通了这个库&#xff0c;找一份Linux服务器开发的工作是没问题的&#xf…