機器學習實戰(2)—— k-近鄰算法

老闆:來了,老弟!python

我:來了來了。算法

老闆:今天你要去看看KNN了,而後我給你安排一個工做!機器學習

我:好嘞!就是第二章嗎?函數

老闆:對!去吧!性能

可惡的老闆又給我安排任務了!學習

《機器學習實戰》這本書中的第二章爲咱們介紹了K-近鄰算法,這是本書中第一個機器學習算法,它很是有效並且易於掌握,因此能夠算是入門級算法了。測試

那咱們如今就一塊兒去學習一下!網站

2.1 k-近鄰算法概述

簡單的說,k-近鄰算法採用測量不一樣特徵值之間的距離進行分類。3d

其工做原理是:code

  1. 存在一個樣本數據集合,也稱做訓練樣本集,而且樣本集中每一個數據都存在標籤,即咱們知道樣本集中的每一數據與所屬分類的對應關係。
  2. 輸入沒有標籤的新數據後,將新數據的每一個特徵與樣本集中對應的數據對應的特徵進行比較,而後算法提取出樣本集中特徵最類似數據(最近鄰)的分類標籤。(通常來講,咱們只選擇樣本數據集中前k個最類似的數據,這就是k-近鄰算法中k的出處,一般k是不大於20的整數。)
  3. 最後,咱們選擇k個最類似數據中出現次數最多的分類,做爲新數據的分類。

其實,光看概念,仍是比較抽象的。那麼接下來,咱們來看一個例子(原諒我思惟沒有那麼豐富,就給你們說一下書中的例子吧,但我稍微作了一些修改,使其更形象一些)。書中的例子也不難,就是一個簡單的電影題材分類問題。

老闆:小韓啊,看完KNN的原理了吧?

我:看完了!

老闆:那我先給你一個簡單的任務。

我:≧ ﹏ ≦

老闆:你先去預測一下新上映的電影《諾手的愛情》是什麼題材的。

我:(啥,這是什麼鬼?)沒問題沒問題!

老闆給了一個已知電影題材的數據集,這也就是咱們的訓練樣本集,其中只有兩種類型的電影:愛情片和動做片。好,第一步,咱們先來看看數據:

電影名稱 打鬥鏡頭 接吻鏡頭 電影類型
蓋倫的愛情 3 104 愛情片
蓋倫的愛情2 2 100 愛情片
蓋倫的愛情3 1 81 愛情片
諾手VS奧特曼 101 10 動做片
諾手VS葫蘆娃 99 5 動做片
諾手VS豬豬俠 98 2 動做片
諾手的愛情 18 90 ???

​ 咱們如今知道每部電影的兩個特徵,一個是打鬥鏡頭次數,一個是接吻鏡頭次數。那麼,根據KNN的原理,咱們就須要計算《蓋倫VS諾手》,也就是未知電影與樣本集中其餘電影的距離。咱們先不看怎麼計算距離(後面算會提供),咱們先來看結果。

電影名稱 與未知電影的距離
蓋倫的愛情 20.5
蓋倫的愛情2 18.7
蓋倫的愛情3 19.2
諾手VS奧特曼 115.3
諾手VS葫蘆娃 117.4
諾手VS豬豬俠 118.9

​ 好了,通過一番計算,咱們獲得了樣本集中全部電影與未知電影的距離,按照距離遞增排序,能夠找到k個距離最近的電影。

​ 咱們假設k=3,那麼距離最近的三部電影分別是:蓋倫的愛情,蓋倫的愛情2,蓋倫的愛情3。這三部電影都是愛情片,因此咱們斷定《諾手的愛情》是愛情片

Bingo!!!老闆說,加雞腿!!!

好了,老闆也加雞腿了,流程也走完了,放假了!!!等等,好像少了點啥???少了代碼怎麼行!!!

老闆佈置了新的任務,用Python代碼實現K-近鄰算法。沒事,不要慌,慢慢來

2.1.1 準備:使用Python導入數據

首先,新建一個名爲kNN.py的文件,寫入如下代碼:

from numpy import *
import operator

def createDataSet():
    group = array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
    labels = ['A', 'A', 'B', 'B']
    return group, labels

在上面的代碼中,咱們導入了兩個模塊:第一個是科學計算包NumPy;第二個是運算符模塊,k-近鄰算法執行排序操做時將使用這個模塊提供的函數。

createDataSet()函數,顧名思義,咱們能夠看出,這個函數用來建立數據集,與此同時,也建立了對應的標籤。group就是咱們的數據集,而每一條數據對應的類別標籤就是labels。

接下來,寫好代碼後,咱們先測試一下。無論你用什麼電腦,咱們都先進入python交互式環境,而後輸入下列命令導入上面編輯的程序模塊:

>>> import kNN

而後,咱們繼續輸入如下命令,建立兩個變量,分別表明數據集和標籤。

>>> group, labels = kNN.createDataSet()

在python命令提示符下,輸入變量的名字以檢驗是否正確的定義變量:

>>> group
array([[1. , 1.1],
       [1. , 1. ],
       [0. , 0. ],
       [0. , 0.1]])
>>> labels
['A', 'A', 'B', 'B']

這裏定義了4組數據,每組數據有兩個咱們已知的屬性或者特徵值。上面的group矩陣每行包含一個不一樣的數據,因爲人類大腦的限制, 咱們一般只能可視化處理三維如下的事務。所以爲了簡單地實現數據可視化,對於每一個數據點我 們一般只使用兩個特徵。

向量labels包含了每一個數據點的標籤信息,labels包含的元素個數等於group矩陣行數。這裏咱們將數據點(1, 1.1)定義爲類A,數據點(0, 0.1)定義爲類B。爲了說明方便,例子中的數值是任意選擇的,並無給出軸標籤,圖2-2是帶有類標籤信息的四個數據點。

表格形式:

數據點 特徵1 特徵2 類別
1 1.0 1.1 A
2 1.0 1.0 A
3 0 0 B
4 0 0.1 B

圖表形式(引用書中內容):

好了,咱們已經知道了Python如何解析數據、加載數據以及kNN算法的工做原理,接下來咱們就用這些方法編寫KNN分類算法。

2.1.2 實施kNN分類算法

首先,咱們先來看一下k-近鄰算法的僞代碼:

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

而後,咱們再來看一下實際的Python代碼:

# inX: 分類的輸入向量
# dataSet: 輸入的訓練樣本集
# labels: 標籤向量
# k: 選擇最近鄰居的數目
def classify0(inX, dataSet, labels, k):
    # 1.計算距離,這裏採用的是歐式距離
    dataSetSize = dataSet.shape[0]  # 獲取數據集大小,也就是有多少條數據
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet  # 將待測試數據擴展
    sqDiffMat = diffMat ** 2  # 差值矩陣中,矩陣元素分別平方
    sqDistances = sqDiffMat.sum(axis = 1)  # 橫向求和,獲得矩陣
    distances = sqDistances ** 0.5  # 矩陣中,每一個元素都開方
    sortedDistIndicies = distances.argsort()  # 距離排序
    
    # 2.選擇距離最小的k個點
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    
    # 3.排序
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=Ture)
    return sortedClassCount[0][0]

上述代碼中有幾點須要解釋一下,也是我在學習的過程當中查閱相關資料的地方。

第一,tile函數:

Numpy的 tile() 函數,就是將原矩陣橫向、縱向地複製。

定義原矩陣:

>>> originMat = array([[1, 3], [2, 4]])

橫向:

>>> tile(originMat, 4)
array([[1, 3, 1, 3, 1, 3, 1, 3],
       [2, 4, 2, 4, 2, 4, 2, 4]])

縱向:

>>> tile(originMat, (3, 1))
array([[1, 3],
       [2, 4],
       [1, 3],
       [2, 4],
       [1, 3],
       [2, 4]])

橫向 + 縱向:

>>> tile(originMat, (3, 2))
array([[1, 3, 1, 3],
       [2, 4, 2, 4],
       [1, 3, 1, 3],
       [2, 4, 2, 4],
       [1, 3, 1, 3],
       [2, 4, 2, 4]])

第二,歐式距離,兩個點的歐式距離公式以下:
$$
d = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}
$$
第三,分組後的排序。將classCount字典分解爲元組列表,而後使用程序第二行導入運算符模塊的itemgetter方法,按照第二個元素的次序對元組進行排序 。此處的排序爲逆序,即從大到小排序。

到如今爲止,咱們已經構造了第一個分類器,使用這個分類器能夠完成不少分類任務。從這個實例出發,構造使用分類算法將會更加容易。

搞定!!!老闆此次給咱們加了一瓶可樂!!!有雞腿有可樂!!!美滋滋!!!

2.1.3 如何測試分類器

咱們在老闆的安排下,已經使用k-近鄰算法構造了第一個分類器,也能夠檢驗分類器給出的答案是否符合咱們的預期。

可是,分類器並不會獲得百分百正確的結果,咱們可使用多種方法檢測分類器的正確率。 此外分類器的性能也會受到多種因素的影響,如分類器設置和數據集等。不一樣的算法在不一樣數據集上的表現可能徹底不一樣。

那麼,問題來了,咱們該如何測試分類器的效果呢?其中一種方法就是錯誤率。這是一種比較經常使用的評估方法,主要用於評估分類器在某個數據集上的執行效果。後面咱們還會遇到更加適合的方法來測試分類器,但這裏咱們先只介紹錯誤率。
$$
錯誤率 = \frac {分類器給出錯誤結果的次數} {預測執行的總數}
$$
咱們在已知答案的數據上進行測試,固然答案不能告訴分類器,檢驗分類器給出的結果是否符合預期結果。簡單點說,咱們用KNN訓練出來了一個分類器,而後咱們再把訓練集丟到分類器裏面,看看分類器的分類效果。


到如今,老闆給出的任務咱們也完成了,代碼也寫了一部分,也掌握了kNN算法的基本使用,仍是很開心的!!

可是,老闆又來了。

老闆:小韓啊,你這個分類器卻是出來了,可是隻在你本身捏造的數據集上運行,不行啊!!

我:(什麼?敢說個人算法不行!!!)

老闆:這樣吧,明天我給你安排一個實在一點的任務,去改進一下約會網站的配對效果。

我:約會網站?聽起來不錯啊!那加雞腿不?

老闆:不加!!!

我: ̄へ ̄,老闆,我先下班了,明天見!!!


最後,歡迎你們關注個人公衆號,有什麼問題也能夠給我留言哦!

相關文章
相關標籤/搜索