機器學習中經常要用到分類算法,在諸多的分類算法中有一種算法名爲k-近鄰算法,也稱爲kNN算法。python
1、kNN算法的工做原理算法
2、適用狀況app
3、算法實例及講解機器學習
---1.收集數據函數
---2.準備數據學習
---3.設計算法分析數據測試
---4.測試算法網站
1、kNN算法的工做原理spa
官方解釋:存在一個樣本數據集,也稱做訓練樣本集,而且樣本中每一個數據都存在標籤,即咱們知道樣本集中每一數據與所屬分類的對應關係,輸入沒有標籤的新數據後,將新數據的每一個特徵與樣本集中的數據對應的特徵進行比較,而後算法提取樣本集中特徵最類似的數據(最近鄰)的分類標籤。通常來講,咱們只選擇樣本集中前k個最類似的數據,這就是k-近鄰算法中k的出處,一般k是不大於20的整數,最後,選擇k個最類似的數據中出現次數最多的分類,做爲新數據的分類。設計
個人理解:k-近鄰算法就是根據「新數據的分類取決於它的鄰居」進行的,好比鄰居中大多數都是退伍軍人,那麼這我的也極有多是退伍軍人。而算法的目的就是先找出它的鄰居,而後分析這幾位鄰居大多數的分類,極有可能就是它本省的分類。
2、適用狀況
優勢:精度高,對異常數據不敏感(你的類別是由鄰居中的大多數決定的,一個異常鄰居並不能影響太大),無數據輸入假定;
缺點:計算髮雜度高(須要計算新的數據點與樣本集中每一個數據的「距離」,以判斷是不是前k個鄰居),空間複雜度高(巨大的矩陣);
適用數據範圍:數值型(目標變量能夠從無限的數值集合中取值)和標稱型(目標變量只有在有限目標集中取值)。
3、算法實例及講解
例子中的案例摘《機器學習實戰》一書中的,代碼例子是用python編寫的(須要matplotlib和numpy庫),不太重在算法,只要算法明白了,用其餘語言都是能夠寫出來的:
海倫一直使用在線約會網站尋找合適本身的約會對象。儘管約會網站會推薦不一樣的人選,但她沒有從中找到喜歡的人。通過一番總結,她發現曾交往過三種類型的人:1.不喜歡的人(如下簡稱1);2.魅力通常的人(如下簡稱2) ;3.極具魅力的人(如下簡稱3)
儘管發現了上述規律,但海倫依然沒法將約會網站推薦的匹配對象納入恰當的分類。她以爲能夠在週一到週五約會哪些魅力通常的人,而週末則更喜歡與那些極具魅力的人爲伴。海倫但願咱們的分類軟件能夠更好的幫助她將匹配對象劃分到確切的分類中。此外海倫還收集了一些約會網站不曾記錄的數據信息,她認爲這些數據更有助於匹配對象的歸類。
咱們先提取一下這個案例的目標:根據一些數據信息,對指定人選進行分類(1或2或3)。爲了使用kNN算法達到這個目標,咱們須要哪些信息?前面提到過,就是須要樣本數據,仔細閱讀咱們發現,這些樣本數據就是「海倫還收集了一些約會網站不曾記錄的數據信息」。好的,下面咱們就開始吧!
----1.收集數據
海倫收集的數據是記錄一我的的三個特徵:每一年得到的飛行常客里程數;玩視頻遊戲所消耗的時間百分比;每週消費的冰淇淋公升數。數據是txt格式文件,以下圖,前三列依次是三個特徵,第四列是分類(1:不喜歡的人,2:魅力通常的人,3:極具魅力的人),每一行表明一我的。
數據文檔的下載連接是:http://pan.baidu.com/s/1jG7n4hS
----2.準備數據
何爲準備數據?以前收集到了數據,放到了txt格式的文檔中了,看起來也比較規整,可是計算機並不認識啊。計算機須要從txt文檔中讀取數據,並把數據進行格式化,也就是說存到矩陣中,用矩陣來承裝這些數據,這樣才能使用計算機處理。
須要兩個矩陣:一個承裝三個特徵數據,一個承裝對應的分類。因而,咱們定義一個函數,函數的輸入時數據文檔(txt格式),輸出爲兩個矩陣。
代碼以下:
1 def file2matrix(filename): 2 fr = open(filename) 3 numberOfLines = len(fr.readlines()) 4 returnMat = zeros((numberOfLines, 3)) 5 classLabelVector = [] 6 fr = open(filename) 7 index = 0 8 for line in fr.readlines(): 9 line = line.strip() 10 listFromLine = line.split('\t') 11 returnMat[index, :] = listFromLine[0:3] 12 classLabelVector.append(int(listFromLine[-1])) 13 index += 1 14 return returnMat, classLabelVector
簡要解讀代碼:首先打開文件,讀取文件的行數,而後初始化以後要返回的兩個矩陣(returnMat、classLabelsVector),而後進入循環,將每行的數據各就各位分配給returnMat和classLabelsVector。
----3.設計算法分析數據
k-近鄰算法的目的就是找到新數據的前k個鄰居,而後根據鄰居的分類來肯定該數據的分類。
首先要解決的問題,就是什麼是鄰居?固然就是「距離」近的了,不一樣人的距離怎麼肯定?這個有點抽象,不過咱們有每一個人的3個特徵數據。每一個人可使用這三個特徵數據來代替這我的——三維點。好比樣本的第一我的就能夠用(40920, 8.326976, 0.953952)來代替,而且他的分類是3。那麼此時的距離就是點的距離:
A點(x1, x2, x3),B點(y1, y2, y3),這兩個點的距離就是:(x1-y1)^2+(x2-y2)^2+(x3-y3)^2的平方根。求出新數據與樣本中每一個點的距離,而後進行從小到大排序,前k位的就是k-近鄰,而後看看這k位近鄰中佔得最多的分類是什麼,也就得到了最終的答案。
這個處理過程也是放到一個函數裏的,代碼以下:
1 def classify0(inX, dataSet, labels, k): 2 dataSetSize = dataSet.shape[0] 3 diffMat = tile(inX, (dataSetSize,1)) - dataSet 4 sqDiffMat = diffMat**2 5 sqDistances = sqDiffMat.sum(axis=1) 6 distances = sqDistances**0.5 7 sortedDistIndicies = distances.argsort() 8 classCount={} 9 for i in range(k): 10 voteIlabel = labels[sortedDistIndicies[i]] 11 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 12 sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True) 13 return sortedClassCount[0][0]
簡要解讀代碼:該函數的4個參數分別爲新數據的三個特徵inX、樣本數據特徵集(上一個函數的返回值)、樣本數據分類(上一個函數的返回值)、k,函數返回位新數據的分類。第二行dataSetSize獲取特徵集矩陣的行數,第三行爲新數據與樣本各個數據的差值,第四行取差值去平方,以後就是再取和,而後平方根。代碼中使用的排序函數都是python自帶的。
好了,如今咱們能夠分析數據了,不過,有一點不知道你們有沒有注意,咱們回到那個數據集,第一列表明的特徵數值遠遠大於其餘兩項特徵,這樣在求距離的公式中就會佔很大的比重,導致兩點的距離很大程度上取決於這個特徵,這固然是不公平的,咱們須要的三個特徵都均平地決定距離,因此咱們要對數據進行處理,但願處理以後既不影響相對大小又能夠不失公平:
這種方法叫作,歸一化數值,經過這種方法能夠把每一列的取值範圍劃到0~1或-1~1:,處理的公式爲:
newValue = (oldValue - min)/(max - min)
歸一化數值的函數代碼爲:
1 def autoNorm(dataSet): 2 minVals = dataSet.min(0) 3 maxVals = dataSet.max(0) 4 ranges = maxVals - minVals 5 normDataSet = zeros(shape(dataSet)) 6 m = dataSet.shape[0] 7 normDataSet = dataSet - tile(minVals, (m, 1)) 8 normDataSet = normDataSet / tile(ranges, (m, 1)) 9 return normDataSet, ranges, minVals
---4.測試算法
通過了格式化數據、歸一化數值,同時咱們也已經完成kNN核心算法的函數,如今能夠測試了,測試代碼爲:
1 def datingClassTest(): 2 hoRatio = 0.10 3 datingDataMat, datingLabels = file2matrix('datingTestSet.txt') 4 normMat, ranges, minVals = autoNorm(datingDataMat) 5 m = normMat.shape[0] 6 numTestVecs = int(m * hoRatio) 7 errorCount = 0.0 8 for i in range(numTestVecs): 9 classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3) 10 print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]) 11 if (classifierResult != datingLabels[i]): errorCount += 1.0 12 print "the total error rate is: %f" % (errorCount / float(numTestVecs))
經過測試代碼咱們能夠在回憶一下這個例子的總體過程:
代碼中你們可能不太明白hoRatio是什麼。注意,這裏的測試數據並非另一批數據而是以前的數據集裏的一部分,這樣咱們能夠把算法獲得的結果和本來的分類進行對比,查看算法的準確度。在這裏,海倫提供的數據集又1000行,咱們把前100行做爲測試數據,後900行做爲樣本數據集,如今你們應該能夠明白hoRatio是什麼了吧。
總體的代碼:
1 from numpy import * 2 import operator 3 4 def classify0(inX, dataSet, labels, k): 5 dataSetSize = dataSet.shape[0] 6 diffMat = tile(inX, (dataSetSize,1)) - dataSet 7 sqDiffMat = diffMat**2 8 sqDistances = sqDiffMat.sum(axis=1) 9 distances = sqDistances**0.5 10 sortedDistIndicies = distances.argsort() 11 classCount={} 12 for i in range(k): 13 voteIlabel = labels[sortedDistIndicies[i]] 14 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 15 sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True) 16 return sortedClassCount[0][0] 17 18 def file2matrix(filename): 19 fr = open(filename) 20 numberOfLines = len(fr.readlines()) 21 returnMat = zeros((numberOfLines, 3)) 22 classLabelVector = [] 23 fr = open(filename) 24 index = 0 25 for line in fr.readlines(): 26 line = line.strip() 27 listFromLine = line.split('\t') 28 returnMat[index, :] = listFromLine[0:3] 29 classLabelVector.append(int(listFromLine[-1])) 30 index += 1 31 return returnMat, classLabelVector 32 33 def autoNorm(dataSet): 34 minVals = dataSet.min(0) 35 maxVals = dataSet.max(0) 36 ranges = maxVals - minVals 37 normDataSet = zeros(shape(dataSet)) 38 m = dataSet.shape[0] 39 normDataSet = dataSet - tile(minVals, (m, 1)) 40 normDataSet = normDataSet / tile(ranges, (m, 1)) 41 return normDataSet, ranges, minVals 42 43 def datingClassTest(): 44 hoRatio = 0.10 45 datingDataMat, datingLabels = file2matrix('datingTestSet.txt') 46 normMat, ranges, minVals = autoNorm(datingDataMat) 47 m = normMat.shape[0] 48 numTestVecs = int(m * hoRatio) 49 errorCount = 0.0 50 for i in range(numTestVecs): 51 classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3) 52 print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]) 53 if (classifierResult != datingLabels[i]): errorCount += 1.0 54 print "the total error rate is: %f" % (errorCount / float(numTestVecs))
運行一下代碼,這裏我使用的是ipython:
最後的錯誤率爲0.05。