1. 程式人生 > >python3.5《機器學習實戰》學習筆記(一):k近鄰演算法

python3.5《機器學習實戰》學習筆記(一):k近鄰演算法

轉載請註明作者和出處:http://blog.csdn.net/u013829973
系統版本:window 7 (64bit)
python版本:python 3.5
IDE:Spyder (一個比較方便的辦法是安裝anaconda,那麼Spyder和jupyter以及python幾個常用的包都有了,甚至可以方便的安裝TensorFlow等,安裝方法連結
程式碼和資料集在: GitHub

k近鄰演算法

1.1 k近鄰演算法概述

  k近鄰(k-nearest neighbor,KNN)是一種基本的分類與迴歸演算法。於1968年由Cover和Hart提出。k近鄰的輸入是例項的特徵向量,對應於特徵空間的點;輸出為例項的類別,可以取多類。k近鄰演算法假設給定一個訓練資料集,其中的例項類別已定,分類時,對新的例項,根據其k個最近鄰的訓練例項的類別,通過多數表決等方式進行預測。因此,k近鄰法不具有顯式的學習過程。簡單的說,給定一個訓練資料集,對新的輸入例項,在訓練集中找到與該例項最近鄰的k個例項,這k個例項的多數屬於哪個類,就把該輸入例項分為這個類。這就是k近鄰演算法中k的出處,通常k是不大於20的整數。
  k近鄰演算法的三個基本要素:k值的選擇、距離度量、分類決策規則
下面我們就用一個簡單的例子來更好的理解k近鄰演算法:
已知表格的前四部電影,根據打鬥鏡頭和接吻鏡頭判斷一個新的電影所屬類別?

電影名稱 打鬥鏡頭 接吻鏡頭 電影型別
1 3 104 愛情片
2 2 100 愛情片
3 99 5 動作片
4 98 2 動作片
未知電影? 18 90 未知
表1.1 每部電影的打鬥鏡頭數、接吻鏡頭數以及電影型別

  已知的訓練集包含兩個特徵(打鬥鏡頭和接吻鏡頭)和類別(愛情片還是動作片)。根據經驗,動作片往往打鬥鏡頭比較多,而愛情片往往就是接吻的鏡頭比較多了。但是knn演算法可沒有我們這麼感性的認識。

1.2 視覺化與距離計算

我們首先對訓練資料進行視覺化:

圖1.1 電影分類
圖1.1 電影分類(圖中的紅色點代表愛情片,藍色點代表動作片,橙色點代表未知電影)

那麼,knn是通過計算什麼來判斷未知電影屬於哪一類的呢?答案:距離。我們首先計算訓練集的所有電影與未知電影的歐式距離:(這裡的距離除了歐式距離,還有曼哈頓距離、切比雪夫距離、閔可夫斯基距離等等)
歐式距離(Euclidean Distance)計算公式:兩個n維向量a(x11,x12,,x1n)b(x21,x22,,x2n)間的歐氏距離:d=k=1n(x1kx2k)2
對於本例子n=2,
電影1與未知電影距離:20.5;
電影,2與未知電影距離:18.7;
電影3與未知電影距離:117.4;
電影4與未知電影距離:118.9;
  現在我們得到了訓練集中所有樣本與未知電影的距離,按照距離遞增排序,可以找到距離最近的電影,假設k=3,則三個最靠近的電影依次是電影1、電影2、電影3,而這三部電影類別2個為愛情片,一個為動作片,所以該未知電影的所屬類別是愛情片。

有了上面的這個例子,我們來總結一下k近鄰演算法步驟


對未知類別屬性的資料集的每個點依次執行以下操作
(1) 計算已知類別資料集中的點與當前點之間的距離;
(2) 按照距離遞增次序排序;
(3)選取與當前點距離最小的k個點;
(4)確定前k個點所在類別的出現頻率;
(5)返回前k個點所出現頻率最高的類別作為當前點的預測分類

1.3 python實現KNN

我們仍然以上面的電影分類進行程式碼編寫

1.3.1 準備資料

對錶1.1中的訓練資料,用numpy建立資料集和標籤

'''
函式功能:建立資料集
Input:     無
Output:     group:資料集
            labels:類別標籤
'''
import numpy as np
def createDataSet():#建立資料集
    group = np.array([[3,104],[2,100],[99,5],[98,2]])
    labels = ['愛情片','愛情片','動作片','動作片']
    return group, labels

'''
函式功能:   主函式    
'''  
if __name__ == '__main__':
    group,labels = createDataSet()#建立資料集
    print('group:\n',group)#列印資料集
    print('labels:',labels)

程式執行結果:

這裡寫圖片描述

1.3.2 k近鄰演算法

'''
函式功能:   kNN分類
Input:      inX: 測試集 (1xN)
            dataSet: 已知資料的特徵(NxM)
            labels: 已知資料的標籤或類別(1xM vector)
            k: k近鄰演算法中的k
Output:     測試樣本最可能所屬的標籤
'''
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0] # shape[0]返回dataSet的行數
    diffMat = np.tile(inX, (dataSetSize,1)) - dataSet 
    # tile(inX,(a,b))函式將inX重複a行,重複b列
    sqDiffMat = diffMat**2 #作差後平方
    sqDistances = sqDiffMat.sum(axis=1)
    #sum()求和函式,sum(0)每列所有元素相加,sum(1)每行所有元素相加
    distances = sqDistances**0.5  #開平方,求歐式距離
    sortedDistIndicies = distances.argsort()  
    #argsort函式返回的是陣列值從小到大的索引值  
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]  
        #取出前k個距離對應的標籤
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 
         #計算每個類別的樣本數。字典get()函式返回指定鍵的值,如果值不在字典中返回預設值0
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    #reverse降序排列字典
    #python2版本中的iteritems()換成python3的items()
    #key=operator.itemgetter(1)按照字典的值(value)進行排序
    #key=operator.itemgetter(0)按照字典的鍵(key)進行排序
    return sortedClassCount[0][0] #返回字典的第一條的key,也即是測試樣本所屬類別

1.3.3 KNN演算法完整程式碼

# -*- coding: utf-8 -*-
'''
Created on Sep 10, 2017

kNN: k近鄰(k Nearest Neighbors) 電影分類

author:we-lee
'''
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import operator

'''
函式功能:建立資料集
Input:     無
Output:     group:資料集
            labels:類別標籤
'''
def createDataSet():#建立資料集
    group = np.array([[3,104],[2,100],[99,5],[98,2]])
    labels = ['愛情片','愛情片','動作片','動作片']
    return group, labels

'''
函式功能:   kNN分類
Input:      inX: 測試集 (1xN)
            dataSet: 已知資料的特徵(NxM)
            labels: 已知資料的標籤或類別(1xM vector)
            k: k近鄰演算法中的k
Output:     測試樣本最可能所屬的標籤
'''
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0] # shape[0]返回dataSet的行數
    diffMat = np.tile(inX, (dataSetSize,1)) - dataSet # tile(inX,(a,b))函式將inX重複a行,重複b列
    sqDiffMat = diffMat**2 #作差後平方
    sqDistances = sqDiffMat.sum(axis=1)#sum()求和函式,sum(0)每列所有元素相加,sum(1)每行所有元素相加
    distances = sqDistances**0.5  #開平方,求歐式距離
    sortedDistIndicies = distances.argsort() #argsort函式返回的是陣列值從小到大的索引值  
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]#取出前k個距離對應的標籤
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 
         #計算每個類別的樣本數。字典get()函式返回指定鍵的值,如果值不在字典中返回預設值0
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    #reverse降序排列字典
    #python2版本中的iteritems()換成python3的items()
    #key=operator.itemgetter(1)按照字典的值(value)進行排序
    #key=operator.itemgetter(0)按照字典的鍵(key)進行排序
    return sortedClassCount[0][0] #返回字典的第一條的key,也即是測試樣本所屬類別


'''
函式功能:   主函式    
'''    
if __name__ == '__main__':
    group,labels = createDataSet()#建立資料集
    print('group:\n',group)#列印資料集
    print('labels:',labels)
    zhfont = matplotlib.font_manager.FontProperties(fname=r'c:\windows\fonts\simsun.ttc')#設定中文字型路徑
    fig = plt.figure(figsize=(10,8))#視覺化
    ax = plt.subplot(111)          #圖片在第一行,第一列的第一個位置
    ax.scatter(group[0:2,0],group[0:2,1],color='red',s=50)
    ax.scatter(group[2:4,0],group[2:4,1],color='blue',s=50)
    ax.scatter(18,90,color='orange',s=50)
    plt.annotate('which class?', xy=(18, 90), xytext=(3, 2),arrowprops=dict(facecolor='black', shrink=0.05),)
    plt.xlabel('打鬥鏡頭',fontproperties=zhfont)
    plt.ylabel('接吻鏡頭',fontproperties=zhfont)
    plt.title('電影分類視覺化',fontproperties=zhfont)
    plt.show()
    testclass = classify0([18,90], group, labels, 3)#用未知的樣本來測試演算法
    print('測試結果:',testclass)#列印測試結果

程式執行結果:
這裡寫圖片描述

2 k近鄰演算法總結

優點
1. 簡單、有效、精度高;
2. 對離群值不敏感;
3. 無資料輸入假定;
4 .可用於數值型資料和離散型資料;

缺點:
1. 計算複雜度高、空間複雜度高;
2. 樣本不平衡問題(即有的類別的樣本數量很多,而其它類別的樣本數量很少),影響分類效果;(關於樣本不平衡問題的解決方法,參見http://blog.csdn.net/u013829973/article/details/77675147
3. 一般數值很大的時候不用這個,計算量太大。但是單個樣本又不能太少 ,否則容易發生誤分;
4. 最大的缺點是無法給出資料的內在含義,無法給出任何資料的基礎結構資訊,無法知曉平均例項樣本和典型例項樣本具有什麼特徵。

注意事項
1. K值的設定
  較小的k值,學習的近似誤差減小,但是估計誤差會增大,意味著整體模型變得複雜,容易過擬合。
  較大的k值,學習的近似誤差增大,但是估計誤差會減小,意味著整體模型變得簡單。
  在應用中k值一般取一個比較小的數值。採用交叉驗證選取最優的k值。

2.優化改進
  在確定最終的類別時,不是簡單的使用多數表決投票,而是進行加權投票,距離越近權重越高。
  k近鄰法中,當訓練集、距離度量、k值和分類決策規則確定後,其結果唯一確定。
  k近鄰法的實現需要考慮如何快速搜尋k個最近鄰點。kd樹是一種便於對k維空間中的資料進行快速檢索的資料結構。詳見李航《統計學習方法》第三章

下一篇,我們會將knn演算法應用到實際問題中,實戰:改進約會網站的配對效果和手寫識別系統!

如有不當之處,請留言,謝謝!