Q:分類和聚類有什么不同正勒?
兩者的區(qū)別可以主要從兩個方面看出章贞。第一阱驾,它們面對的根本問題不同。分類的根本問題是判斷給定的某一個樣本屬于那一個類別里覆;聚類的根本問題是探索給定的數(shù)據(jù)集可以分成哪幾種類別喧枷。第二隧甚,兩者使用的訓練數(shù)據(jù)集有差異渡冻。分類任務使用的訓練數(shù)據(jù)集中每個樣本除了有屬性數(shù)據(jù)外族吻,還必須有一個標記值,用以表示該樣本屬于哪一類砍艾;聚類任務的數(shù)據(jù)集中每個樣本可以只有屬性值脆荷,沒有標記值(當然也可以有)懊悯。這也可以認為是我們常說的,監(jiān)督學習與無監(jiān)督學習桃焕。
Q:聚類的一般方法有哪些覆旭?
聚類算法多種多樣岖妄,但是萬變不離其宗,最基本的思想還是先提出一中衡量樣本之間相似程度的的手段七兜,如距離腕铸、相關系數(shù)等,然后逐一計算數(shù)據(jù)集中各樣本之間的相似度虽界,盡量把相似度高的樣本放到同一類涛菠。
一般來說聚類算法有三類:基于原型的聚類、基于密度的聚類礁叔、和基于層次的聚類琅关。當然讥蔽,還可以有更多勤篮,比如混合高斯模型也能用于聚類任務。
Q:衡量樣本間相似度的指標有那些账劲?
所謂樣本金抡,其數(shù)學本質(zhì)就是向量梗肝。所以樣本間的相似度常用距離來表示。
最簡單的距離就是內(nèi)積距離
最直觀的距離則是歐氏距離
更一般的距離有明可夫斯基距離,其中p值視情況而定
上面提到的距離都是適用于有序?qū)傩缘陌用獭K^有序?qū)傩郧昙叮褪侵笇傩缘娜≈涤写笮≈帧1热缥鞴系摹昂锹省边@個屬性帽芽,0.1就比0.9要小。無序?qū)傩赃@位是指屬性取值無大小之分的屬性披泪。比如西瓜的“顏色”屬性付呕,取“0”表示淺綠跌捆、取“1”表示深綠佩厚,但不能說0比1小抄瓦,“顏色”屬性的取值不能比較大小陶冷。此時,要更亮無序?qū)傩缘木嚯x要用到VDM距離煞额,即
所以膊毁,對于屬性中既含有有序?qū)傩曰槲拢趾袩o序?qū)傩缘臉颖鞠狈瘢瑒t需要統(tǒng)一明可夫斯基距離和VDM距離
Q:k-means聚類算法的思路是怎樣的篱竭?
當我們?nèi)タ疾煲欢褦?shù)據(jù)的時候,一般會通過平均數(shù)和方差來獲取一個大致的了解搪哪。也就是說,平均數(shù)和方差可以代表一組數(shù)據(jù)惑朦。那么我們就會想漓概,有沒有一個均值向量可以代表一組向量?
自然是有的(反正概念什么的都是人定的)梁肿。
上式代表類中所有向量的均值向量吩蔑。
那么填抬,如果有k個均值向量,就代表有k個類赘娄『牝龋可以分別計算某個向量與這k個均值向量的距離拾并,哪個距離更短,就將該向量(樣本)歸到哪一類辟灰。這便是k-means(k均值)算法的基本思想芥喇。
實際的算法過程是一個循環(huán)。首先人為設定k值大小械馆,即要劃分多少個類武通。然后第一輪在數(shù)據(jù)集中隨機挑選k個樣本作為初始的均值向量,接著計算剩下的樣本與這幾個均值向量的距離尾菇,并且聚類派诬,可以得到k個類別,每個類別至少一個樣本沛鸵。此時重新計算各個類別的均值向量缆八,得到k個新的均值向量。下面重新計算距離栏妖,再度分類底哥,再度重新計算均值向量,得到k個新的均值向量,如此循環(huán)往復翰守,直到得到的k個均值向量與上一輪的均值向量一樣疲酌,也就是兩輪的聚類結果一樣的時候朗恳,循環(huán)結束。
Q:雖說聚類術語無監(jiān)督學習油航,不需要用到樣本標記信息谊囚,但是既然存在標記信息执赡,可不可以利用一下?
LVQ(學習向量量化)算法就是利用樣本標記信息來輔助聚類的算法奠伪。LVQ算法利用和k-means的均值向量類似的代表性向量(也可以叫原型向量),同樣也使用距離計算然后根據(jù)最短距離分類的的思想谨敛。不過LVQ算法的目的不是直接得到劃分好的幾個類即舌,而是得到t個原型向量。也就是說肥惭,LVQ的輸出是t個原型向量紊搪。先上一張抽象的算法流程
LVQ算法同樣人為設定一個t值耀石,也就是要劃分的類別數(shù)。然后初始化t個原型向量——不是像k-means一樣隨便選揭鳞,而是根據(jù)預先說好了的標記野崇,在隨便選亩钟。比如預先說好要選3個好瓜,2個壞瓜扶镀,就從好瓜中隨便選三個臭觉,壞瓜中隨便選兩個辱志。
選好初始原型向量后,開始和k-means一樣不斷循環(huán)計算距離菱肖,不過LVQ每次都是隨機抽取訓練數(shù)據(jù)集的一個樣本稳强,計算抽到的樣本和各個原型向量的距離。距離最短的一個原型向量退疫,如果其標記和抽到的標記一樣褒繁,就“使原型向量向抽到的樣本向量靠近”棒坏,如果其標記和抽到的標記不同,就“使原型向量向抽到的樣本向量遠離”——
如此循環(huán)往復直到達到最大循環(huán)次數(shù)磨澡,或者原型向量的調(diào)整幅度已經(jīng)小于某個閥值稳摄。
Q:有沒有不是基于原型向量的聚類算法秩命?
除了基于原型向量的聚類算法褒傅,還有基于密度的聚類算法,如DBSCAN霹菊,和基于層次的聚類算法旋廷,如AGNES礼搁。
在了解什么是基于密度的算法前,先參考一下人際社交的生活經(jīng)驗扎运。“物以類聚洞拨,人以群分”负拟,人群中會自發(fā)地誕生多個人際關系圈子掩浙。圈子中的核心人物一般來說往往是人脈最豐富的,或者直接說認識人最多的示辈,這些人暫且稱為社交領袖矾麻。一個人一般來說可以很迅速地和自己的朋友建立聯(lián)系险耀。只要兩個人處于同一個圈子之中,或者說他們處于不同的圈子玖喘,但是兩個圈子的人有交集甩牺,那么即使他們不認識,也可以通過朋友的朋友的朋友······建立聯(lián)系累奈。弱兩個人分屬不同的圈子贬派,且兩個圈子沒有交集,但是兩個圈子都和第三個圈子有交集澎媒,那么這兩個人同樣可以通過朋友的朋友的朋友······建立聯(lián)系搞乏。
根據(jù)上面這個例子,我們可以建立一個樣本密度模型
DBSCAN算法中的簇類比于交際關系里的圈子戒努,鄰域類比于一個人的朋友圈请敦,核心對象類比于社交領袖侍筛,密度直達匣椰、可達弛车、相連分別類比于三種人與人知啊今建立聯(lián)系的方式纷跛。DBSCAN算法中的一個簇是指所有可以相互之間密度直達贫奠、可達、相連的樣本的集合谢肾。
而DBSCAN算法的原理也就很簡單了,首先確定數(shù)據(jù)集中所有核心對象酸茴,然后隨機從一個核心對象出發(fā)找出所有與之密度可達的樣本薪捍,找完以后這些樣本就是一個簇了,然后再隨機選一個不在現(xiàn)有的簇范圍內(nèi)的核心對象被济,繼續(xù)上面的步驟净响,知道所有核心對象都被分到某個簇中赞别。
DBSCAN算法最后可能會產(chǎn)生幾個不在任何簇里的樣本仿滔,他們被稱為噪聲鞠绰,同時也可以視為異常數(shù)據(jù)。
基于層次的聚類算法試圖用自頂向下或者自底向上的方式在不同層次上對數(shù)據(jù)進行劃分翁巍。AGNES算法是一中自底向上的算法灶壶。算法的基本思想是在一開始把所有樣本都看作一個簇類。那么有n個樣本的數(shù)據(jù)集就有n個初始簇洒嗤。接下來兩兩計算簇間距離,距離最小的兩個簇合并為一個簇(有點像霍夫曼樹的子樹合并)间唉,直到所有的簇合并為一個簇為止呈野。各種簇間距離計算公式如下被冒,視情況挑選
AGNES算法最終得到的結果如下圖
我們可以通過選擇簇間距離來確定到底分幾個簇率触。簇間距離越大葱蝗,得到的簇越少皂甘,簇間距離越小,得到的簇越多益老。
Talk is cheap, show me the code!
下面是一個KMeans模型的簡單實現(xiàn)
"""
K-means clustering model
:file: supervised.py
:author: Richy Zhu
:email: rickyzhu@foxmail.com
"""
import numpy as np
class MyKMeans:
def __init__(self, k=3):
'''
K-means clustering model
:k: int, number of clusters
'''
self.k = k
self.X = None
self.labels = None
self.cluster_centers = None
def fit(self, X):
'''
Train the K-means clustering model
Parameters
----------
X: ndarray of shape (m, n)
sample data where row represent sample and column represent feature
Returns
-------
self
trained model
'''
ran_index = np.random.choice(np.arange(0,len(X)), self.k)
centres = [X[i] for i in ran_index]
prev_labels = np.zeros(len(X))
i = 0
while True:
labels = []
for x in X:
labels.append(np.argmin([np.linalg.norm(x - c) for c in centres]))
if np.array_equal(prev_labels, labels):
break
prev_labels = labels
for i in range(len(centres)):
centres[i] = np.mean(X[np.array(labels)==i], axis=0)
i += 0
self.labels = np.array(labels)
self.cluster_centers = np.array(centres)
return self
def predict(self, X):
'''
Make prediction by the trained model.
Parameters
----------
X: ndarray of shape (m, n)
data to be predicted, the same shape as trainning data
Returns
-------
C: ndarray of shape (m,)
Predicted class label per sample.
'''
labels = []
for x in X:
labels.append(np.argmin([
np.linalg.norm(x - c) for c in self.cluster_centers
]))
return np.array(labels)
測試代碼如下
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from unsupervised import *
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y)
print('\nK-means clustering')
print('---------------------------------------------------------------------')
mykm = MyKMeans(k=3)
mykm.fit(X_train)
print('My KMeans:')
print(mykm.cluster_centers)
print(mykm.predict(X_test))
print()
from sklearn.cluster import KMeans
skkm = KMeans(n_clusters=3)
skkm.fit(X_train)
print('Sk KMeans:')
print(skkm.cluster_centers_)
print(skkm.predict(X_test))
測試結果為
$ python unsupervised_examples.py
K-means clustering
---------------------------------------------------------------------
[[4.78333333 3.11666667 1.50555556 0.26666667]
[6.24 2.87866667 4.90133333 1.66933333]
[5.25789474 3.74210526 1.49473684 0.26315789]]
[2 1 2 1 2 1 2 1 1 0 1 1 1 1 0 1 1 1 0 0 0 1 0 1 1 0 2 1 1 2 1 1 1 0 0 1 1
0]
Sk KMeans:
[[5.025 3.46388889 1.45833333 0.24166667]
[6.78888889 3.08888889 5.72962963 2.08888889]
[5.91428571 2.75510204 4.40612245 1.42653061]]
[0 1 0 1 0 2 0 2 2 0 1 1 2 2 0 1 1 2 0 0 0 2 0 1 1 2 0 2 1 0 1 2 2 2 0 2 1
0]
更多代碼請參考https://github.com/qige96/programming-practice/tree/master/machine-learning
本作品首發(fā)于簡書 和 博客園平臺,采用知識共享署名 4.0 國際許可協(xié)議進行許可伞梯。