1. 程式人生 > >【2】機器學習之兄弟連:K近鄰和K-means

【2】機器學習之兄弟連:K近鄰和K-means

關鍵詞:從K近鄰到最近鄰,監督學習,資料帶lable,效率優化(從線性搜尋到kd樹搜尋),缺點是需要儲存所有資料,空間複雜度大。可以利用kd數來優化k-means演算法。

學習了kNN和K-means演算法後,仔細分析比較了他們之間的異同以及應用場景總結成此文供讀者參閱。

首先,kNN是分類演算法,其主要任務是將例項資料劃分到合適的分類中,屬於監督學習,什麼叫監督學習?就是這類演算法必須知道需要預測什麼(即目標變數的分類資訊),線性迴歸、樸素貝葉斯、支援向量機、決策樹等常用演算法均屬於監督學習的範疇。K-means是聚類演算法,將一個數據集合中的類似物件分成多個類即可,屬於無監督學習。什麼是無監督的學習?就是訓練結果是未知的,也許能發現一些資料之間的規律與聯絡。

在說這兩類演算法之前我先明確一個距離的概念,如何評判兩個點之間的距離與相似性?介紹兩種最簡單也是最廣泛的方法--歐幾里得距離和皮爾遜係數。兩點的歐幾里得距離就是兩點的實際距離這兩點的歐氏距離越小說明兩點越相關,比較簡單,不再贅述。皮爾遜相關係數是根據這個式子來計算兩個向量之間的相關性的,它的好處是在資料不是很規範的時候(個別資料和平均資料相差很大的時候)結果往往趨向更好的結果,它是一種巨集觀上的相似。還有其他的距離與相似性的衡量演算法可以參考博文:

接下來我們來說kNN演算法,kNN又叫k-近鄰演算法,顧名思義就是找出k個距離最近的資料。給定一個分類集合,我們將目標變數的和這個分類集合中的每個元素求距離(距離用上面討論的任意一種距離都可以,為了計算方便後面我們用歐氏距離來計算),然後將距離進行排序得到最小的那個元素分類就是我們的目標變數分類了。kNN演算法程式碼如下:

def classify0(inX,dataSet,labels,k):
    dataSetSize=dataSet.shape[0]
    diffMat=tile(inX,(dataSetSize,1))-dataSet
    sqDiffMat=diffMat**2
    #這裡用numpy裡面的矩陣計算提高了運算效率
    sqDistances=sqDiffMat.sum(axis=1)
    distances=sqDistances**0.5 
    sortedDistIndicies=distances.argsort()
    classCount={}
    for
i in range(k): voteIlabel=labels[sortedDistIndicies[i]] classCount[voteIlabel]=classCount.get(voteIlabel,0)+1 sortedClassCount=sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True) return sortedClassCount[0][0]

但是要注意的一點是,為了避免資料本身大小影響歐氏距離的大小,所以用歐氏距離必須先對資料進行歸一化處理

def autoNorm(dataSet):
    minVals=dataSet.min(0)
    maxVals=dataSet.max(0)
    ranges=maxVals-minVals
    normDataSet=zeros(shape(dataSet))
    m=dataSet.shape[0]
    normDataSet=dataSet-tile(minVals,(m,1))
    normDataSet=normDataSet/tile(ranges,(m,1))
    return normDataSet

kNN是分類演算法中最簡單有效的方法,而且效果也是很好的(我測試了幾個分類資料集,錯誤率不超過2%)但是kNN必須儲存全部資料集,如果訓練資料集很大,必須大量佔用儲存空間,而且必須對每個資料計算次距離,非常耗時.

再來說K-means聚類演算法,K-means又叫-k均值聚類演算法,演算法思想就是首先會隨機確定k箇中心點作為聚類中心,然後把各個資料點分配給最鄰近的中心點,分配完成後將中心點移動到所表示的聚類的平均中心位置處,然後重複迭代上述步驟直到分配過程不再產生變化位置。

K-means演算法程式碼如下:(這裡用的皮爾遜相關係數作距離衡量)

def kcluster(rows,distance=pearson,k=4):
ranges=[(min([row[i] for row in rows]),max([row[i] for row in rows])) for i in range(len(rows[0]))]
clusters=[[random.random()*(ranges[i][1]-ranges[i][0])+ranges[i][0] for i in range(len(rows[0]))] for j in range(k)] 
lastmatches=None
for t in range(100):
print 'Iteration %d' %t
bestmatches=[[] for i in range(k)]
for j in range(len(rows)):
row=rows[j]
bestmatch=0
for i in range(k):
d=distance(clusters[i],row)
if d<distance(clusters[bestmatch],row):
bestmatch=i
bestmatches[bestmatch].append(j)
if bestmatches==lastmatches:break
lastmatches=bestmatches
for i in range(k):
avgs=[0.0]*len(rows[0])
if len(bestmatches[i])>0:
for rowid in bestmatches[i]:
for m in range(len(rows[rowid])):
avgs[m]+=rows[rowid][m]
for j in range(len(avgs)):
avgs[j]/=len(bestmatches[i])
clusters[i]=avgs
return bestmatches

最後總結一下這兩種演算法的異同:

  1. kNN是監督的分類演算法,K-means是無監督的聚類方法。

  2. 給kNN的資料是帶label的,給K-means的資料是不帶label的、雜亂無章的

  3. 相似點:均包含一個給定一個點找尋離它最近的點的過程,均會用距離相似度來衡量。即都用到了NN,一般會用k-d樹來實現。