機器學習算法與Python實踐之k近鄰(KNN)


 轉自:http://blog.csdn.net/zouxy09/article/details/16955347

       機器學習算法與Python實踐這個系列主要是參考《機器學習實戰》這本書。因爲自己想學習Python,然後也想對一些機器學習算法加深下了解,所以就想通過Python來實現幾個比較常用的機器學習算法。恰好遇見這本同樣定位的書籍,所以就參考這本書的過程來學習了。

 

一、kNN算法分析

       K最近鄰(k-Nearest Neighbor,KNN)分類算法可以說是最簡單的機器學習算法了。它採用測量不同特徵值之間的距離方法進行分類。它的思想很簡單:如果一個樣本在特徵空間中的k個最相似(即特徵空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。

        比如上面這個圖,我們有兩類數據,分別是藍色方塊和紅色三角形,他們分佈在一個上圖的二維中間中。那麼假如我們有一個綠色圓圈這個數據,需要判斷這個數據是屬於藍色方塊這一類,還是與紅色三角形同類。怎麼做呢?我們先把離這個綠色圓圈最近的幾個點找到,因爲我們覺得離綠色圓圈最近的纔對它的類別有判斷的幫助。那到底要用多少個來判斷呢?這個個數就是k了。如果k=3,就表示我們選擇離綠色圓圈最近的3個點來判斷,由於紅色三角形所佔比例爲2/3,所以我們認爲綠色圓是和紅色三角形同類。如果k=5,由於藍色四方形比例爲3/5,因此綠色圓被賦予藍色四方形類。從這裏可以看到,k的值還是很重要的。

       KNN算法中,所選擇的鄰居都是已經正確分類的對象。該方法在定類決策上只依據最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。由於KNN方法主要靠周圍有限的鄰近的樣本,而不是靠判別類域的方法來確定所屬類別的,因此對於類域的交叉或重疊較多的待分樣本集來說,KNN方法較其他方法更爲適合。

       該算法在分類時有個主要的不足是,當樣本不平衡時,如一個類的樣本容量很大,而其他類樣本容量很小時,有可能導致當輸入一個新樣本時,該樣本的K個鄰居中大容量類的樣本佔多數。因此可以採用權值的方法(和該樣本距離小的鄰居權值大)來改進。該方法的另一個不足之處是計算量較大,因爲對每一個待分類的文本都要計算它到全體已知樣本的距離,才能求得它的K個最近鄰點。目前常用的解決方法是事先對已知樣本點進行剪輯,事先去除對分類作用不大的樣本。該算法比較適用於樣本容量比較大的類域的自動分類,而那些樣本容量較小的類域採用這種算法比較容易產生誤分[參考機器學習十大算法]。

       總的來說就是我們已經存在了一個帶標籤的數據庫,然後輸入沒有標籤的新數據後,將新數據的每個特徵與樣本集中數據對應的特徵進行比較,然後算法提取樣本集中特徵最相似(最近鄰)的分類標籤。一般來說,只選擇樣本數據庫中前k個最相似的數據。最後,選擇k個最相似數據中出現次數最多的分類。其算法描述如下:

1)計算已知類別數據集中的點與當前點之間的距離;

2)按照距離遞增次序排序;

3)選取與當前點距離最小的k個點;

4)確定前k個點所在類別的出現頻率;

5)返回前k個點出現頻率最高的類別作爲當前點的預測分類。

 

二、Python實現

       對於機器學習而已,Python需要額外安裝三件寶,分別是Numpy,scipy和Matplotlib。前兩者用於數值計算,後者用於畫圖。安裝很簡單,直接到各自的官網下載回來安裝即可。安裝程序會自動搜索我們的python版本和目錄,然後安裝到python支持的搜索路徑下。反正就python和這三個插件都默認安裝就沒問題了。

       另外,如果我們需要添加我們的腳本目錄進Python的目錄(這樣Python的命令行就可以直接import),可以在系統環境變量中添加:PYTHONPATH環境變量,值爲我們的路徑,例如:E:\Python\Machine Learning in Action

 

2.1、kNN基礎實踐

       一般實現一個算法後,我們需要先用一個很小的數據庫來測試它的正確性,否則一下子給個大數據給它,它也很難消化,而且還不利於我們分析代碼的有效性。

      首先,我們新建一個kNN.py腳本文件,文件裏面包含兩個函數,一個用來生成小數據庫,一個實現kNN分類算法。代碼如下:

  1. #########################################  
  2. # kNN: k Nearest Neighbors  
  3. # Input:      newInput: vector to compare to existing dataset (1xN)  
  4. #             dataSet:  size m data set of known vectors (NxM)  
  5. #             labels:   data set labels (1xM vector)  
  6. #             k:        number of neighbors to use for comparison   
  7.        
  8. # Output:     the most popular class label  
  9. #########################################  
  10.   
  11. from numpy import *  
  12. import operator  
  13.   
  14. # create a dataset which contains 4 samples with 2 classes  
  15. def createDataSet():  
  16.     # create a matrix: each row as a sample  
  17.     group = array([[1.00.9], [1.01.0], [0.10.2], [0.00.1]])  
  18.     labels = ['A''A''B''B'# four samples and two classes  
  19.     return group, labels  
  20.   
  21. # classify using kNN  
  22. def kNNClassify(newInput, dataSet, labels, k):  
  23.     numSamples = dataSet.shape[0# shape[0] stands for the num of row  
  24.   
  25.     ## step 1: calculate Euclidean distance  
  26.     # tile(A, reps): Construct an array by repeating A reps times  
  27.     # the following copy numSamples rows for dataSet  
  28.     diff = tile(newInput, (numSamples, 1)) - dataSet # Subtract element-wise  
  29.     squaredDiff = diff ** 2 # squared for the subtract  
  30.     squaredDist = sum(squaredDiff, axis = 1# sum is performed by row  
  31.     distance = squaredDist ** 0.5  
  32.   
  33.     ## step 2: sort the distance  
  34.     # argsort() returns the indices that would sort an array in a ascending order  
  35.     sortedDistIndices = argsort(distance)  
  36.   
  37.     classCount = {} # define a dictionary (can be append element)  
  38.     for i in xrange(k):  
  39.         ## step 3: choose the min k distance  
  40.         voteLabel = labels[sortedDistIndices[i]]  
  41.   
  42.         ## step 4: count the times labels occur  
  43.         # when the key voteLabel is not in dictionary classCount, get()  
  44.         # will return 0  
  45.         classCount[voteLabel] = classCount.get(voteLabel, 0) + 1  
  46.   
  47.     ## step 5: the max voted class will return  
  48.     maxCount = 0  
  49.     for key, value in classCount.items():  
  50.         if value > maxCount:  
  51.             maxCount = value  
  52.             maxIndex = key  
  53.   
  54.     return maxIndex   

       然後我們在命令行中這樣測試即可:

  1. import kNN  
  2. from numpy import *   
  3.   
  4. dataSet, labels = kNN.createDataSet()  
  5.   
  6. testX = array([1.21.0])  
  7. k = 3  
  8. outputLabel = kNN.kNNClassify(testX, dataSet, labels, 3)  
  9. print "Your input is:", testX, "and classified to class: ", outputLabel  
  10.   
  11. testX = array([0.10.3])  
  12. outputLabel = kNN.kNNClassify(testX, dataSet, labels, 3)  
  13. print "Your input is:", testX, "and classified to class: ", outputLabel  

       這時候會輸出:

  1. Your input is: [ 1.2  1.0and classified to class:  A  
  2. Your input is: [ 0.1  0.3and classified to class:  B  

2.2、kNN進階

       這裏我們用kNN來分類一個大點的數據庫,包括數據維度比較大和樣本數比較多的數據庫。這裏我們用到一個手寫數字的數據庫,可以到這裏下載。這個數據庫包括數字0-9的手寫體。每個數字大約有200個樣本。每個樣本保持在一個txt文件中。手寫體圖像本身的大小是32x32的二值圖,轉換到txt文件保存後,內容也是32x32個數字,0或者1,如下:

       數據庫解壓後有兩個目錄:目錄trainingDigits存放的是大約2000個訓練數據,testDigits存放大約900個測試數據。

        這裏我們還是新建一個kNN.py腳本文件,文件裏面包含四個函數,一個用來生成將每個樣本的txt文件轉換爲對應的一個向量,一個用來加載整個數據庫,一個實現kNN分類算法。最後就是實現這個加載,測試的函數。

  1. #########################################  
  2. # kNN: k Nearest Neighbors  
  3.   
  4. # Input:      inX: vector to compare to existing dataset (1xN)  
  5. #             dataSet: size m data set of known vectors (NxM)  
  6. #             labels: data set labels (1xM vector)  
  7. #             k: number of neighbors to use for comparison   
  8.               
  9. # Output:     the most popular class label  
  10. #########################################  
  11.   
  12. from numpy import *  
  13. import operator  
  14. import os  
  15.   
  16.   
  17. # classify using kNN  
  18. def kNNClassify(newInput, dataSet, labels, k):  
  19.     numSamples = dataSet.shape[0# shape[0] stands for the num of row  
  20.   
  21.     ## step 1: calculate Euclidean distance  
  22.     # tile(A, reps): Construct an array by repeating A reps times  
  23.     # the following copy numSamples rows for dataSet  
  24.     diff = tile(newInput, (numSamples, 1)) - dataSet # Subtract element-wise  
  25.     squaredDiff = diff ** 2 # squared for the subtract  
  26.     squaredDist = sum(squaredDiff, axis = 1# sum is performed by row  
  27.     distance = squaredDist ** 0.5  
  28.   
  29.     ## step 2: sort the distance  
  30.     # argsort() returns the indices that would sort an array in a ascending order  
  31.     sortedDistIndices = argsort(distance)  
  32.   
  33.     classCount = {} # define a dictionary (can be append element)  
  34.     for i in xrange(k):  
  35.         ## step 3: choose the min k distance  
  36.         voteLabel = labels[sortedDistIndices[i]]  
  37.   
  38.         ## step 4: count the times labels occur  
  39.         # when the key voteLabel is not in dictionary classCount, get()  
  40.         # will return 0  
  41.         classCount[voteLabel] = classCount.get(voteLabel, 0) + 1  
  42.   
  43.     ## step 5: the max voted class will return  
  44.     maxCount = 0  
  45.     for key, value in classCount.items():  
  46.         if value > maxCount:  
  47.             maxCount = value  
  48.             maxIndex = key  
  49.   
  50.     return maxIndex   
  51.   
  52. # convert image to vector  
  53. def  img2vector(filename):  
  54.     rows = 32  
  55.     cols = 32  
  56.     imgVector = zeros((1, rows * cols))   
  57.     fileIn = open(filename)  
  58.     for row in xrange(rows):  
  59.         lineStr = fileIn.readline()  
  60.         for col in xrange(cols):  
  61.             imgVector[0, row * 32 + col] = int(lineStr[col])  
  62.   
  63.     return imgVector  
  64.   
  65. # load dataSet  
  66. def loadDataSet():  
  67.     ## step 1: Getting training set  
  68.     print "---Getting training set..."  
  69.     dataSetDir = 'E:/Python/Machine Learning in Action/'  
  70.     trainingFileList = os.listdir(dataSetDir + 'trainingDigits'# load the training set  
  71.     numSamples = len(trainingFileList)  
  72.   
  73.     train_x = zeros((numSamples, 1024))  
  74.     train_y = []  
  75.     for i in xrange(numSamples):  
  76.         filename = trainingFileList[i]  
  77.   
  78.         # get train_x  
  79.         train_x[i, :] = img2vector(dataSetDir + 'trainingDigits/%s' % filename)   
  80.   
  81.         # get label from file name such as "1_18.txt"  
  82.         label = int(filename.split('_')[0]) # return 1  
  83.         train_y.append(label)  
  84.   
  85.     ## step 2: Getting testing set  
  86.     print "---Getting testing set..."  
  87.     testingFileList = os.listdir(dataSetDir + 'testDigits'# load the testing set  
  88.     numSamples = len(testingFileList)  
  89.     test_x = zeros((numSamples, 1024))  
  90.     test_y = []  
  91.     for i in xrange(numSamples):  
  92.         filename = testingFileList[i]  
  93.   
  94.         # get train_x  
  95.         test_x[i, :] = img2vector(dataSetDir + 'testDigits/%s' % filename)   
  96.   
  97.         # get label from file name such as "1_18.txt"  
  98.         label = int(filename.split('_')[0]) # return 1  
  99.         test_y.append(label)  
  100.   
  101.     return train_x, train_y, test_x, test_y  
  102.   
  103. # test hand writing class  
  104. def testHandWritingClass():  
  105.     ## step 1: load data  
  106.     print "step 1: load data..."  
  107.     train_x, train_y, test_x, test_y = loadDataSet()  
  108.   
  109.     ## step 2: training...  
  110.     print "step 2: training..."  
  111.     pass  
  112.   
  113.     ## step 3: testing  
  114.     print "step 3: testing..."  
  115.     numTestSamples = test_x.shape[0]  
  116.     matchCount = 0  
  117.     for i in xrange(numTestSamples):  
  118.         predict = kNNClassify(test_x[i], train_x, train_y, 3)  
  119.         if predict == test_y[i]:  
  120.             matchCount += 1  
  121.     accuracy = float(matchCount) / numTestSamples  
  122.   
  123.     ## step 4: show the result  
  124.     print "step 4: show the result..."  
  125.     print 'The classify accuracy is: %.2f%%' % (accuracy * 100)  

       測試非常簡單,只需要在命令行中輸入:

  1. import kNN  
  2. kNN.testHandWritingClass()  

       輸出結果如下:

  1. step 1: load data...  
  2. ---Getting training set...  
  3. ---Getting testing set...  
  4. step 2: training...  
  5. step 3: testing...  
  6. step 4: show the result...  
  7. The classify accuracy is98.84
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章