步步學習之用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演算法就那樣。