1. 程式人生 > >步步學習之用python實戰機器學習1-kNN (K-NearestNeighbors)演算法(a)

步步學習之用python實戰機器學習1-kNN (K-NearestNeighbors)演算法(a)

我最近才開始接觸機器學習,我大學數學學的幾乎忘了,最近才接觸python。所以我以一個完全初學者角度來學習機器學習。

我主要用的書籍就是machine learning in action (機器學習實戰)這本書。我主要是用文中已有的程式碼來講解機器學習。

同時對程式碼進行大量註釋,主要針對初學者以及python剛學的,這樣理解透徹。

第一章  K近鄰演算法kNN(K-NearestNeighbors)

 這個演算法是最基礎的機器學習分類方法。我先給定義。

如果一個樣本在特徵空間中k個最相似的樣本中的大多數屬於一類,則這個樣本屬於這個類別。

書中舉出了一個例子。也就是電影的例子。

比如怎麼把自己看過的電影進行分類,怎麼定義它。

比如把電影分成 愛情片還是動作片。比如有些人通過接吻和武打的次數來來進行分類。圖中給出了一些電影的分類按照接吻的次數和武打的次數。


但是現在有一部電影你想知道是愛情片還是動作片。

首先根據以前的資料,比如下表中的電影類別來判別未知電影的類別。


首先我們計算未知電影與已知電影的距離。


然後我們假設k=3。那麼我們對已經算好的上個表格中距離資料排序。找出距離最近的前三位電影。然後我們發現He’sNot Really into Dudes, Beautiful Woman, and California Man 這三部電影是浪漫電影。而是我們推測這個未知電影就是愛情片。其實這就是kNN演算法。

我們介紹kNN演算法基本內容。

1 收集:任何方法

2. 準備:數字值用來計算距離,結構化的資料最重要

3  分析:任何方法

4  訓練演算法:此步驟不適用於k-NN近鄰演算法

5. 測試演算法: 計算錯誤率

5  使用演算法

在用code實現kNN演算法之前,我詳細講一下k在其中啥意思。我剛開始看英文原文的時候不知道是為啥取3(下面這個例子)。後來我查看了別人的部落格,我知道k是啥意思。看下面這個圖就知道啥意思啦。


比如圖中有藍色正方形和紅色三角形兩種型別的圖形,那麼想知道那個上面帶著問號的圓形是什麼類別:藍色正方形還是紅色三角形?首先根據kNN演算法我們算每個圖(包含三角形和正方形)到未知圓形的距離。然後我們排序(按照遠近),比如像圖中那樣遠近一看便知。那麼k值就是我們需要選擇幾個與圓形最近的值來判別圓形的類別。比如圖中第一個k=3時候,我們統計三個圖形中三角形2個正方形1個。那麼就知道三角形出現最多,而是我們知道未知圖形類別是紅色三角形。但是當我們選擇k=5時候,也就是選擇前面5個最近的圖形。也就是圖中第二圈中五個圖形。我們統計發現圖中有5個正方形2個三角形,而是未知圖形現在變成啦正方形類別。我們發現由於k的取值不同。未知圖形的類別就不一樣啦。這就是k值非常重要。講的通俗一點就是你周圍有一批朋友,有各種類別(特徵),你想知道你啥類別,其實你知道你心裡跟這些人的距離(假設這裡心理距離可以直接計算,但是可以估算)最近,然後選去最近k個朋友,然後看看這個k個朋友哪種類別(特徵)最多。比如k個人中正值老實人最多,那麼你的類別就是正值老實人。這就是kNN演算法的核心。我相信大家都懂啦,連我這種菜鳥也懂啦。

下面就上程式碼實現第一個kNN例子吧。

# -*- coding: utf-8 -*-
from numpy import *
import operator

def createDataSet():
    """
    函式作用:構建一組訓練資料(也就是樣本),其實這裡的樣本是我們自己構造
    的資料,樣本容量很少,真實的情況是讀取文字資料。
    同時給出樣本的標籤以及labels。標籤的意思就是樣本四個資料的各個類別
    比如[1.0,1.1]屬於A類等。
    """
    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels = ['A', 'A', 'B','B']
    #最後返回樣本資料以及標籤,不過python還是真厲害,可以返回多個數據
    return group, labels
    
def classify0(inX, dataSet,labels,k):
    """
    inX 是輸入的測試樣本,是一個[x, y]樣式的,也就是我們需要想知道的
    資料到底屬於哪類A活著B,就想上面說的未知電影是愛情還是動作影片。
    dataset是訓練樣本集
    labels是訓練樣本標籤其實就是類別A和B
    k是top k最相近的。說實話開始我並不理解,其實很簡單就是選取離自己
    最近的k個樣本,具體我在上面會有介紹
    """
    dataSetSize = dataSet.shape[0]
    #dataSet.shape 結果是(4,1)返回矩陣的行數和列數。
    # 那麼shape[0]獲取資料集的行數,  
    # 行數就是樣本的數量 這裡就是4
    
       
    """ 
    下面的求距離過程就是按照歐氏距離的公式計算的。 
    即 根號(x2-x1)^2+(y2-y1)^2) 。比如(1,2),(2,3)
    這兩個點的距離就是(2-1)^2+(3-2)^2,然後開根號計算。
    """  
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    """
    由於dataset資料是一個4行1列的資料,而我們測試的資料,是一個一行一列的資料,
    比如(1,0)。由於要把這個值跟測試資料每一個都進行減法。為了方便就必須是構造
    一個同樣結構的矩陣資料,用tile方法相當於copy inX四次沿著行。比如以前是(1,0)
    那麼tile(inX,(4,1))變成啦
    1  0
    1  0
    1  0
    1  0 
    """
    sqDiffMat = diffMat**2
    # 然後就是平方每個進行減法的值
    sqDistances = sqDiffMat.sum(axis=1)
    """
    sqDiffMat =
    [[ 1.    1.21]
    [ 1.    1.  ]
    [ 0.    0.  ]
    [ 0.    0.01]]
   sqDiffMat.sum(axis=1) 把所有的值都加起來沿著 x軸,而都是一行行的加那麼結果。四個值
    [2.21. 2. 0. 0.01] 
   sqDiffMat.sum(axis=0)  但是當我們把axis=0結果就發生了變化。都是一列列的加,
    而只有兩個值所有的x值加在一起,所有的y值加在一起
    我們發現只有x值都都在一起,然後y都加在一起。
    [ 2.    2.22]
    """
    distances = sqDistances**0.5
    #開平方根
    sortedDistIndicies = distances.argsort()
    """ 
    distances  =[ 1.48660687  1.41421356  0.          0.1       ]
    distances.argsort()  這個函式就是返回從小到大的值的索引,也就是距離
    值按照由近到遠排列距離索引值。
    比如最大就是第一個(index = 0)最小值就是第三個(index=2)
    那麼返回順序就是如下。
    sortedDistIndicies=[2 3 1 0]
    """
    classCount={} #建立一個空dict來存取每個標籤的出現的次數,比如A和B的次數
    for i in range(k):#這裡取前k個值
        voteIlabel = labels[sortedDistIndicies[i]]
    # 前k個值根據索引對應的標籤。比如索引2對應的標籤就是B
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
        """ 
        計算每個標籤出現的次數,比如每個標籤的預設值就是0,如果有就是加1,比如開始
        B是一個值,因為第一次出現在classCount,然後就是從0變成1.
       """  
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    """
      sorted函式的作用就是排序,一般是從小到大,但是這裡reverse=True
      表示從反過來啦從大到小
      classCount.iteritems()用於返回一個 iterator,
      關於iteritems()方法以及其他相關函式見下面部落格 我覺得研究的很好。
      Python dictionary items()系列函式的使用
      http://blog.csdn.net/revilwang/article/details/38686635
      operator.itemgetter(1) 獲取前面的classCount.iteritems()中
      第二個值進行操作,按照第二個升序排列。
      比如下面這個例子
      >>> d = {'data1':3, 'data2':1, 'data3':2, 'data4':4}  
      >>> sorted(d.iteritems(), key=itemgetter(1), reverse=True)  
      >>>[('data4', 4), ('data1', 3), ('data3', 2), ('data2', 1)]  
      """ 
    return sortedClassCount[0][0]
    """
    返回這個list或者怎麼說第一個數值對中第一個值。這裡是B,
    比如下面例子就是data4
    >>> d
    [('data4', 4), ('data1', 3), ('data3', 2), ('data2', 1)]
    >>> d[0][0]
    'data4'
    """
group,  labels = createDataSet()    
print classify0([0,0], group, labels, 3)

看了我的程式碼後,你會發現其實kNN演算法就那樣。