简单易学的机器学习算法——Mean Shift聚类算法

一、Mean Shift算法概述

Mean Shift算法,又称为均值漂移算法,Mean Shift的概念最早是由Fukunage在1975年提出的,在后来由Yizong Cheng对其进行扩充,主要提出了两点的改进:

  • 定义了核函数;
  • 增加了权重系数。

核函数的定义使得偏移值对偏移向量的贡献随之样本与被偏移点的距离的不同而不同。权重系数使得不同样本的权重不同。Mean Shift算法在聚类,图像平滑、分割以及视频跟踪等方面有广泛的应用。

二、Mean Shift算法的核心原理

2.1、核函数

在Mean Shift算法中引入核函数的目的是使得随着样本与被偏移点的距离不同,其偏移量对均值偏移向量的贡献也不同。核函数是机器学习中常用的一种方式。核函数的定义如下所示:

X表示一个d维的欧式空间,x是该空间中的一个点x={x1,x2,x3?,xd},其中,x的模∥x∥2=xxT,R表示实数域,如果一个函数K:X→R存在一个剖面函数k:[0,∞]→R,即

K(x)=k(∥x∥2)

并且满足:

(1)、k是非负的

(2)、k是非增的

(3)、k是分段连续的

那么,函数K(x)就称为核函数。

常用的核函数有高斯核函数。高斯核函数如下所示:

N(x)=12π??√he?x2h2

其中,h称为带宽(bandwidth),不同带宽的核函数如下图所示:

上图的画图脚本如下所示:

‘‘‘
Date:201604026
@author: zhaozhiyong
‘‘‘
import matplotlib.pyplot as plt
import math

def cal_Gaussian(x, h=1):
    molecule = x * x
    denominator = 2 * h * h
    left = 1 / (math.sqrt(2 * math.pi) * h)
    return left * math.exp(-molecule / denominator)

x = []

for i in xrange(-40,40):
    x.append(i * 0.5);

score_1 = []
score_2 = []
score_3 = []
score_4 = []

for i in x:
    score_1.append(cal_Gaussian(i,1))
    score_2.append(cal_Gaussian(i,2))
    score_3.append(cal_Gaussian(i,3))
    score_4.append(cal_Gaussian(i,4))

plt.plot(x, score_1, ‘b--‘, label="h=1")
plt.plot(x, score_2, ‘k--‘, label="h=2")
plt.plot(x, score_3, ‘g--‘, label="h=3")
plt.plot(x, score_4, ‘r--‘, label="h=4")

plt.legend(loc="upper right")
plt.xlabel("x")
plt.ylabel("N")
plt.show()

2.2、Mean Shift算法的核心思想

2.2.1、基本原理

对于Mean Shift算法,是一个迭代的步骤,即先算出当前点的偏移均值,将该点移动到此偏移均值,然后以此为新的起始点,继续移动,直到满足最终的条件。此过程可由下图的过程进行说明(图片来自参考文献3):

  • 步骤1:在指定的区域内计算偏移均值(如下图的黄色的圈)

  • 步骤2:移动该点到偏移均值点处

  • 步骤3: 重复上述的过程(计算新的偏移均值,移动)

  • 步骤4:满足了最终的条件,即退出

从上述过程可以看出,在Mean Shift算法中,最关键的就是计算每个点的偏移均值,然后根据新计算的偏移均值更新点的位置。

2.2.2、基本的Mean Shift向量形式

对于给定的d维空间Rd中的n个样本点xi,i=1,?,n,则对于x点,其Mean Shift向量的基本形式为:

Mh(x)=1k∑xi∈Sh(xi?x)

其中,Sh指的是一个半径为h的高维球区域,如上图中的蓝色的圆形区域。Sh的定义为:

Sh(x)=(y∣(y?x)(y?x)T?h2)

这样的一种基本的Mean Shift形式存在一个问题:在Sh的区域内,每一个点对x的贡献是一样的。而实际上,这种贡献与x到每一个点之间的距离是相关的。同时,对于每一个样本,其重要程度也是不一样的。

2.2.3、改进的Mean Shift向量形式

基于以上的考虑,对基本的Mean Shift向量形式中增加核函数和样本权重,得到如下的改进的Mean Shift向量形式:

Mh(x)=∑ni=1GH(xi?x)w(xi)(xi?x)∑ni=1GH(xi?x)w(xi)

其中:

GH(xi?x)=|H|?12G(H?12(xi?x))

G(x)是一个单位的核函数。H是一个正定的对称d×d矩阵,称为带宽矩阵,其是一个对角阵。w(xi)?0是每一个样本的权重。对角阵H的形式为:

H=???????h210?00h22?0???00?h2d???????d×d

上述的Mean Shift向量可以改写成:

Mh(x)=∑ni=1G(xi?xhi)w(xi)(xi?x)∑ni=1G(xi?xhi)w(xi)

Mean Shift向量Mh(x)是归一化的概率密度梯度。

2.3、Mean Shift算法的解释

在Mean Shift算法中,实际上是利用了概率密度,求得概率密度的局部最优解。

2.3.1、概率密度梯度

对一个概率密度函数f(x),已知d维空间中n个采样点xi,i=1,?,n,f(x)的核函数估计(也称为Parzen窗估计)为:

f^(x)=∑ni=1K(xi?xh)w(xi)hd∑ni=1w(xi)

其中

w(xi)?0是一个赋给采样点xi的权重

K(x)是一个核函数

概率密度函数f(x)的梯度▽f(x)的估计为

▽f^(x)=2∑ni=1(x?xi)k′(∥∥xi?xh∥∥2)w(xi)hd+2∑ni=1w(xi)

令g(x)=?k′(x),G(x)=g(∥x∥2),则有:

▽f^(x)=2∑ni=1(xi?x)G(∥∥xi?xh∥∥2)w(xi)hd+2∑ni=1w(xi)=2h2???∑ni=1G(xi?xh)w(xi)hd∑ni=1w(xi)??????∑ni=1(xi?x)G(∥∥xi?xh∥∥2)w(xi)∑ni=1G(xi?xh)w(xi)???

其中,第二个方括号中的就是Mean Shift向量,其与概率密度梯度成正比。

2.3.2、Mean Shift向量的修正

Mh(x)=∑ni=1G(∥∥xi?xh∥∥2)w(xi)xi∑ni=1G(xi?xh)w(xi)?x

记:mh(x)=∑ni=1G(∥∥xi?xh∥∥2)w(xi)xi∑ni=1G(xi?xh)w(xi),则上式变成:

Mh(x)=mh(x)+x

这与梯度上升的过程一致。

2.4、Mean Shift算法流程

Mean Shift算法的算法流程如下:

  • 计算mh(x)
  • 令x=mh(x)
  • 如果∥mh(x)?x∥<ε,结束循环,否则,重复上述步骤

三、实验

3.1、实验数据

实验数据如下图所示(来自参考文献1):

画图的代码如下:

‘‘‘
Date:20160426
@author: zhaozhiyong
‘‘‘
import matplotlib.pyplot as plt

f = open("data")
x = []
y = []
for line in f.readlines():
    lines = line.strip().split("\t")
    if len(lines) == 2:
        x.append(float(lines[0]))
        y.append(float(lines[1]))
f.close()  

plt.plot(x, y, ‘b.‘, label="original data")
plt.title(‘Mean Shift‘)
plt.legend(loc="upper right")
plt.show()

3.2、实验的源码

#!/bin/python
#coding:UTF-8
‘‘‘
Date:20160426
@author: zhaozhiyong
‘‘‘

import math
import sys
import numpy as np

MIN_DISTANCE = 0.000001#mini error

def load_data(path, feature_num=2):
    f = open(path)
    data = []
    for line in f.readlines():
        lines = line.strip().split("\t")
        data_tmp = []
        if len(lines) != feature_num:
            continue
        for i in xrange(feature_num):
            data_tmp.append(float(lines[i]))

        data.append(data_tmp)
    f.close()
    return data

def gaussian_kernel(distance, bandwidth):
    m = np.shape(distance)[0]
    right = np.mat(np.zeros((m, 1)))
    for i in xrange(m):
        right[i, 0] = (-0.5 * distance[i] * distance[i].T) / (bandwidth * bandwidth)
        right[i, 0] = np.exp(right[i, 0])
    left = 1 / (bandwidth * math.sqrt(2 * math.pi))

    gaussian_val = left * right
    return gaussian_val

def shift_point(point, points, kernel_bandwidth):
    points = np.mat(points)
    m,n = np.shape(points)
    #计算距离
    point_distances = np.mat(np.zeros((m,1)))
    for i in xrange(m):
        point_distances[i, 0] = np.sqrt((point - points[i]) * (point - points[i]).T)

    #计算高斯核
    point_weights = gaussian_kernel(point_distances, kernel_bandwidth)

    #计算分母
    all = 0.0
    for i in xrange(m):
        all += point_weights[i, 0]

    #均值偏移
    point_shifted = point_weights.T * points / all
    return point_shifted

def euclidean_dist(pointA, pointB):
    #计算pointA和pointB之间的欧式距离
    total = (pointA - pointB) * (pointA - pointB).T
    return math.sqrt(total)

def distance_to_group(point, group):
    min_distance = 10000.0
    for pt in group:
        dist = euclidean_dist(point, pt)
        if dist < min_distance:
            min_distance = dist
    return min_distance

def group_points(mean_shift_points):
    group_assignment = []
    m,n = np.shape(mean_shift_points)
    index = 0
    index_dict = {}
    for i in xrange(m):
        item = []
        for j in xrange(n):
            item.append(str(("%5.2f" % mean_shift_points[i, j])))

        item_1 = "_".join(item)
        print item_1
        if item_1 not in index_dict:
            index_dict[item_1] = index
            index += 1

    for i in xrange(m):
        item = []
                for j in xrange(n):
                        item.append(str(("%5.2f" % mean_shift_points[i, j])))

                item_1 = "_".join(item)
        group_assignment.append(index_dict[item_1])

    return group_assignment

def train_mean_shift(points, kenel_bandwidth=2):
    #shift_points = np.array(points)
    mean_shift_points = np.mat(points)
    max_min_dist = 1
    iter = 0
    m, n = np.shape(mean_shift_points)
    need_shift = [True] * m

    #cal the mean shift vector
    while max_min_dist > MIN_DISTANCE:
        max_min_dist = 0
        iter += 1
        print "iter : " + str(iter)
        for i in range(0, m):
            #判断每一个样本点是否需要计算偏置均值
            if not need_shift[i]:
                continue
            p_new = mean_shift_points[i]
            p_new_start = p_new
            p_new = shift_point(p_new, points, kenel_bandwidth)
            dist = euclidean_dist(p_new, p_new_start)

            if dist > max_min_dist:#record the max in all points
                max_min_dist = dist
            if dist < MIN_DISTANCE:#no need to move
                need_shift[i] = False

            mean_shift_points[i] = p_new
    #计算最终的group
    group = group_points(mean_shift_points)

    return np.mat(points), mean_shift_points, group

if __name__ == "__main__":
    #导入数据集
    path = "./data"
    data = load_data(path, 2)

    #训练,h=2
    points, shift_points, cluster = train_mean_shift(data, 2)

    for i in xrange(len(cluster)):
        print "%5.2f,%5.2f\t%5.2f,%5.2f\t%i" % (points[i,0], points[i, 1], shift_points[i, 0], shift_points[i, 1], cluster[i])

3.3、实验的结果

经过Mean Shift算法聚类后的数据如下所示:

‘‘‘
Date:20160426
@author: zhaozhiyong
‘‘‘
import matplotlib.pyplot as plt

f = open("data_mean")
cluster_x_0 = []
cluster_x_1 = []
cluster_x_2 = []
cluster_y_0 = []
cluster_y_1 = []
cluster_y_2 = []
center_x = []
center_y = []
center_dict = {}

for line in f.readlines():
    lines = line.strip().split("\t")
    if len(lines) == 3:
        label = int(lines[2])
        if label == 0:
            data_1 = lines[0].strip().split(",")
            cluster_x_0.append(float(data_1[0]))
            cluster_y_0.append(float(data_1[1]))
            if label not in center_dict:
                center_dict[label] = 1
                data_2 = lines[1].strip().split(",")
                center_x.append(float(data_2[0]))
                center_y.append(float(data_2[1]))
        elif label == 1:
            data_1 = lines[0].strip().split(",")
            cluster_x_1.append(float(data_1[0]))
            cluster_y_1.append(float(data_1[1]))
            if label not in center_dict:
                center_dict[label] = 1
                data_2 = lines[1].strip().split(",")
                center_x.append(float(data_2[0]))
                center_y.append(float(data_2[1]))
        else:
            data_1 = lines[0].strip().split(",")
            cluster_x_2.append(float(data_1[0]))
            cluster_y_2.append(float(data_1[1]))
            if label not in center_dict:
                center_dict[label] = 1
                data_2 = lines[1].strip().split(",")
                center_x.append(float(data_2[0]))
                center_y.append(float(data_2[1]))
f.close()

plt.plot(cluster_x_0, cluster_y_0, ‘b.‘, label="cluster_0")
plt.plot(cluster_x_1, cluster_y_1, ‘g.‘, label="cluster_1")
plt.plot(cluster_x_2, cluster_y_2, ‘k.‘, label="cluster_2")
plt.plot(center_x, center_y, ‘r+‘, label="mean point")
plt.title(‘Mean Shift 2‘)
#plt.legend(loc="best")
plt.show()

参考文献

  1. Mean Shift Clustering
  2. Meanshift,聚类算法
  3. meanshift算法简介
时间: 2024-10-08 22:48:02

简单易学的机器学习算法——Mean Shift聚类算法的相关文章

简单易学的机器学习算法——EM算法

一.机器学习中的參数预计问题 在前面的博文中,如"简单易学的机器学习算法--Logistic回归"中,採用了极大似然函数对其模型中的參数进行预计,简单来讲即对于一系列样本,Logistic回归问题属于监督型学习问题,样本中含有训练的特征 X_i" title="X_i" >以及标签.在Logistic回归的參数求解中.通过构造样本属于类别和类别的概率: 这样便能得到Logistic回归的属于不同类别的概率函数: 此时,使用极大似然预计便可以预计出模型

简单易学的机器学习算法——AdaBoost

一.集成方法(Ensemble Method) 集成方法主要包括Bagging和Boosting两种方法,随机森林算法是基于Bagging思想的机器学习算法,在Bagging方法中,主要通过对训练数据集进行随机采样,以重新组合成不同的数据集,利用弱学习算法对不同的新数据集进行学习,得到一系列的预测结果,对这些预测结果做平均或者投票做出最终的预测.AdaBoost算法和GBDT(Gradient Boost Decision Tree,梯度提升决策树)算法是基于Boosting思想的机器学习算法.

[转载]简单易学的机器学习算法-决策树之ID3算的

一.决策树分类算法概述 决策树算法是从数据的属性(或者特征)出发,以属性作为基础,划分不同的类.例如对于如下数据集 (数据集) 其中,第一列和第二列为属性(特征),最后一列为类别标签,1表示是,0表示否.决策树算法的思想是基于属性对数据分类,对于以上的数据我们可以得到以下的决策树模型 (决策树模型) 先是根据第一个属性将一部份数据区分开,再根据第二个属性将剩余的区分开. 实现决策树的算法有很多种,有ID3.C4.5和CART等算法.下面我们介绍ID3算法. 二.ID3算法的概述 ID3算法是由Q

简单易学的机器学习算法——基于密度的聚类算法DBSCAN

一.基于密度的聚类算法的概述 最近在Science上的一篇基于密度的聚类算法<Clustering by fast search and find of density peaks>引起了大家的关注(在我的博文"论文中的机器学习算法--基于密度峰值的聚类算法"中也进行了中文的描述).于是我就想了解下基于密度的聚类算法,熟悉下基于密度的聚类算法与基于距离的聚类算法,如K-Means算法之间的区别. 基于密度的聚类算法主要的目标是寻找被低密度区域分离的高密度区域.与基于距离的聚

简单易学的机器学习算法——谱聚类(Spectal Clustering)

一.复杂网络中的一些基本概念 1.复杂网络的表示 在复杂网络的表示中,复杂网络可以建模成一个图,其中,表示网络中的节点的集合,表示的是连接的集合.在复杂网络中,复杂网络可以是无向图.有向图.加权图或者超图. 2.网络簇结构 网络簇结构(network cluster structure)也称为网络社团结构(network community structure),是复杂网络中最普遍和最重要的拓扑属性之一.网络簇是整个网络中的稠密连接分支,具有同簇内部节点之间相互连接密集,不同簇的节点之间相互连接

简单易学的机器学习算法——因子分解机(Factorization Machine)

一.因子分解机FM的模型 因子分解机(Factorization Machine, FM)是由Steffen Rendle提出的一种基于矩阵分解的机器学习算法. 1.因子分解机FM的优势 对于因子分解机FM来说,最大的特点是对于稀疏的数据具有很好的学习能力.现实中稀疏的数据很多,例如作者所举的推荐系统的例子便是一个很直观的具有稀疏特点的例子. 2.因子分解机FM的模型 对于度为2的因子分解机FM的模型为: 其中,参数,,.表示的是两个大小为的向量和向量的点积: 其中,表示的是系数矩阵的第维向量,

机器学习:Python实现聚类算法(三)之总结

考虑到学习知识的顺序及效率问题,所以后续的几种聚类方法不再详细讲解原理,也不再写python实现的源代码,只介绍下算法的基本思路,使大家对每种算法有个直观的印象,从而可以更好的理解函数中参数的意义及作用,而重点是放在如何使用及使用的场景. (题外话: 今天看到一篇博文:刚接触机器学习这一个月我都做了什么?  里面对机器学习阶段的划分很不错,就目前而言我们只要做到前两阶段即可) 因为前两篇博客已经介绍了两种算法,所以这里的算法编号从3开始. 3.Mean-shift 1)概述 Mean-shift

机器学习——利用K-均值聚类算法对未标注数据分组

聚类是一种无监督的学习,它将相似的对象归到同一簇中.它有点像全自动分类.聚类方法几乎可以应用到所有对象,簇内的对象越相似,聚类的效果越好. K-均值(K-means)聚类算法,之所以称之为K-均值是因为它可以发现k个不同的簇,且每个簇的中心采用簇中所含值的均值计算而成. 簇识别(cluster identification)给出簇类结果的含义.假定有一些数据,现在将相似数据归到一起,簇识别会告诉我们这些簇到底都是些什么. K-均值聚类算法 优点:容易实现 缺点:可能收敛到局部最小值,在大规模数据

简单易学的机器学习算法——集成方法(Ensemble Method)

一.集成学习方法的思想 前面介绍了一系列的算法,每个算法有不同的适用范围,例如有处理线性可分问题的,有处理线性不可分问题.在现实世界的生活中,常常会因为"集体智慧"使得问题被很容易解决,那么问题来了,在机器学习问题中,对于一个复杂的任务来说,能否将很多的机器学习算法组合在一起,这样计算出来的结果会不会比使用单一的算法性能更好?这样的思路就是集成学习方法. 集成学习方法是指组合多个模型,以获得更好的效果,使集成的模型具有更强的泛化能力.对于多个模型,如何组合这些模型,主要有以下几种不同的