機器學(xué)習(xí):Mean Shift聚類算法

本文由ChardLau原創(chuàng),轉(zhuǎn)載請?zhí)砑釉逆溄?a target="_blank" rel="nofollow">https://www.chardlau.com/mean-shift/

今天的文章介紹如何利用Mean Shift算法的基本形式對數(shù)據(jù)進行聚類操作簇秒。而有關(guān)Mean Shift算法加入核函數(shù)計算漂移向量部分的內(nèi)容將不在本文講述范圍內(nèi)箱季。實際上除了聚類暇番,Mean Shift算法還能用于計算機視覺等場合于个,有關(guān)該算法的理論知識請參考這篇文章镐作。

Mean Shift算法原理

下圖展示了Mean Shift算法計算飄逸向量的過程:

Mean Shift

Mean Shift算法的關(guān)鍵操作是通過感興趣區(qū)域內(nèi)的數(shù)據(jù)密度變化計算中心點的漂移向量,從而移動中心點進行下一次迭代呀酸,直到到達密度最大處(中心點不變)凉蜂。從每個數(shù)據(jù)點出發(fā)都可以進行該操作,在這個過程,統(tǒng)計出現(xiàn)在感興趣區(qū)域內(nèi)的數(shù)據(jù)的次數(shù)窿吩。該參數(shù)將在最后作為分類的依據(jù)茎杂。

K-Means算法不一樣的是,Mean Shift算法可以自動決定類別的數(shù)目纫雁。與K-Means算法一樣的是煌往,兩者都用集合內(nèi)數(shù)據(jù)點的均值進行中心點的移動。

算法步驟

下面是有關(guān)Mean Shift聚類算法的步驟:

  1. 在未被標記的數(shù)據(jù)點中隨機選擇一個點作為起始中心點center轧邪;
  2. 找出以center為中心半徑為radius的區(qū)域中出現(xiàn)的所有數(shù)據(jù)點携冤,認為這些點同屬于一個聚類C。同時在該聚類中記錄數(shù)據(jù)點出現(xiàn)的次數(shù)加1闲勺。
  3. 以center為中心點,計算從center開始到集合M中每個元素的向量扣猫,將這些向量相加菜循,得到向量shift。
  4. center = center + shift申尤。即center沿著shift的方向移動癌幕,移動距離是||shift||。
  5. 重復(fù)步驟2昧穿、3勺远、4,直到shift的很惺蓖摇(就是迭代到收斂)胶逢,記住此時的center。注意饰潜,這個迭代過程中遇到的點都應(yīng)該歸類到簇C初坠。
  6. 如果收斂時當前簇C的center與其它已經(jīng)存在的簇C2中心的距離小于閾值,那么把C2和C合并彭雾,數(shù)據(jù)點出現(xiàn)次數(shù)也對應(yīng)合并碟刺。否則,把C作為新的聚類薯酝。
  7. 重復(fù)1半沽、2、3吴菠、4者填、5直到所有的點都被標記為已訪問。
  8. 分類:根據(jù)每個類做葵,對每個點的訪問頻率幔托,取訪問頻率最大的那個類,作為當前點集的所屬類。

算法實現(xiàn)

下面使用Python實現(xiàn)了Mean Shift算法的基本形式:

import numpy as np
import matplotlib.pyplot as plt

# Input data set
X = np.array([
    [-4, -3.5], [-3.5, -5], [-2.7, -4.5],
    [-2, -4.5], [-2.9, -2.9], [-0.4, -4.5],
    [-1.4, -2.5], [-1.6, -2], [-1.5, -1.3],
    [-0.5, -2.1], [-0.6, -1], [0, -1.6],
    [-2.8, -1], [-2.4, -0.6], [-3.5, 0],
    [-0.2, 4], [0.9, 1.8], [1, 2.2],
    [1.1, 2.8], [1.1, 3.4], [1, 4.5],
    [1.8, 0.3], [2.2, 1.3], [2.9, 0],
    [2.7, 1.2], [3, 3], [3.4, 2.8],
    [3, 5], [5.4, 1.2], [6.3, 2]
])


def mean_shift(data, radius=2.0):
    clusters = []
    for i in range(len(data)):
        cluster_centroid = data[i]
        cluster_frequency = np.zeros(len(data))

        # Search points in circle
        while True:
            temp_data = []
            for j in range(len(data)):
                v = data[j]
                # Handle points in the circles
                if np.linalg.norm(v - cluster_centroid) <= radius:
                    temp_data.append(v)
                    cluster_frequency[i] += 1

            # Update centroid
            old_centroid = cluster_centroid
            new_centroid = np.average(temp_data, axis=0)
            cluster_centroid = new_centroid
            # Find the mode
            if np.array_equal(new_centroid, old_centroid):
                break

        # Combined 'same' clusters
        has_same_cluster = False
        for cluster in clusters:
            if np.linalg.norm(cluster['centroid'] - cluster_centroid) <= radius:
                has_same_cluster = True
                cluster['frequency'] = cluster['frequency'] + cluster_frequency
                break

        if not has_same_cluster:
            clusters.append({
                'centroid': cluster_centroid,
                'frequency': cluster_frequency
            })

    print('clusters (', len(clusters), '): ', clusters)
    clustering(data, clusters)
    show_clusters(clusters, radius)


# Clustering data using frequency
def clustering(data, clusters):
    t = []
    for cluster in clusters:
        cluster['data'] = []
        t.append(cluster['frequency'])
    t = np.array(t)
    # Clustering
    for i in range(len(data)):
        column_frequency = t[:, i]
        cluster_index = np.where(column_frequency == np.max(column_frequency))[0][0]
        clusters[cluster_index]['data'].append(data[i])


# Plot clusters
def show_clusters(clusters, radius):
    colors = 10 * ['r', 'g', 'b', 'k', 'y']
    plt.figure(figsize=(5, 5))
    plt.xlim((-8, 8))
    plt.ylim((-8, 8))
    plt.scatter(X[:, 0], X[:, 1], s=20)
    theta = np.linspace(0, 2 * np.pi, 800)
    for i in range(len(clusters)):
        cluster = clusters[i]
        data = np.array(cluster['data'])
        plt.scatter(data[:, 0], data[:, 1], color=colors[i], s=20)
        centroid = cluster['centroid']
        plt.scatter(centroid[0], centroid[1], color=colors[i], marker='x', s=30)
        x, y = np.cos(theta) * radius + centroid[0], np.sin(theta) * radius + centroid[1]
        plt.plot(x, y, linewidth=1, color=colors[i])
    plt.show()


mean_shift(X, 2.5)

代碼鏈接

上述代碼執(zhí)行結(jié)果如下:


執(zhí)行結(jié)果

其他

Mean Shift算法還有很多內(nèi)容未提及重挑。其中有“動態(tài)計算感興趣區(qū)域半徑”嗓化、“加入核函數(shù)計算漂移向量”等。本文作為入門引導(dǎo)谬哀,暫時只覆蓋這些內(nèi)容刺覆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市史煎,隨后出現(xiàn)的幾起案子谦屑,更是在濱河造成了極大的恐慌鲫寄,老刑警劉巖均澳,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萍恕,死亡現(xiàn)場離奇詭異寨闹,居然都是意外死亡府阀,警方通過查閱死者的電腦和手機在辆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門渴逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悠轩,“玉大人袍患,你說我怎么就攤上這事坦康。” “怎么了诡延?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵滞欠,是天一觀的道長。 經(jīng)常有香客問我肆良,道長筛璧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任惹恃,我火速辦了婚禮隧哮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘座舍。我一直安慰自己沮翔,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布曲秉。 她就那樣靜靜地躺著采蚀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪承二。 梳的紋絲不亂的頭發(fā)上榆鼠,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音亥鸠,去河邊找鬼妆够。 笑死识啦,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的神妹。 我是一名探鬼主播颓哮,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸵荠!你這毒婦竟也來了冕茅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蛹找,失蹤者是張志新(化名)和其女友劉穎姨伤,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庸疾,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡乍楚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了届慈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徒溪。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拧篮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牵舱,我是刑警寧澤串绩,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站芜壁,受9級特大地震影響礁凡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慧妄,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一顷牌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧塞淹,春花似錦窟蓝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至套耕,卻和暖如春谁帕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冯袍。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工匈挖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碾牌,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓儡循,卻偏偏與公主長得像舶吗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子贮折,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 聚類算法 前面介紹的集中算法都是屬于有監(jiān)督機器學(xué)習(xí)方法裤翩,這章和前面不同,介紹無監(jiān)督學(xué)習(xí)算法调榄,也就是聚類算法踊赠。在無監(jiān)...
    飄涯閱讀 41,353評論 3 52
  • 一年前需要用聚類算法時,自己從一些sklearn文檔和博客粗略整理了一些相關(guān)的知識每庆,記錄在電子筆記里備忘筐带,現(xiàn)在發(fā)到...
    wong11閱讀 44,432評論 0 19
  • 我生命中的絕大多數(shù)公歷新年(也即元旦)都在無聲無息中隱入了時光之河中腮出,只能怪“公歷”二字如此之官方帖鸦,讓人禁不住擺出...
    凡人淘鹽閱讀 300評論 0 1
  • 小豆豆的媽媽真是一個非常善良,有正能量的人,孩子在這樣的環(huán)境下生長起來胚嘲,肯定賦有媽媽的 美德作儿。看到了正男媽媽的焦慮...
    255b13afce2f閱讀 1,133評論 0 0
  • 我想我又有了喜歡某人的能力馋劈,仿佛是許多年不見得某種魔咒重現(xiàn)攻锰,而許多個夜晚積攢下的悲傷與自卑會在想起時涌動。 我遇到...
    荒蕪在廣闊貧瘠的心中閱讀 112評論 0 0