k-近鄰算法(KNN)是一種基本的分類與迴歸方法。html
算法介紹:算法
給定一個訓練數據集,對於新的輸入實例,在訓練數據集中找到與該實例最鄰近的K個實例。若是這K個實例多數屬於某個類別,則把該輸入實例分爲這個類。簡單來講,KNN算法的思想就是「近朱者赤,近墨者黑」。數組
算法描述:app
總結:從以上算法描述咱們能夠看出,K鄰近算法的3個關鍵問題是:距離度量、K值選擇和分類決策規則。機器學習
Python實例:函數
說明:本實例來自於《機器學習實戰》一書,代碼有我標記的大量註釋,後面我會附上具體的數據樣本。性能
一、首先咱們須要關注的就是KNN算法的具體實現,即classify0()方法:學習
1 """ 2 使用K-近鄰算法改進約會網站的配對效果 3 """ 4 5 from numpy import * 6 import matplotlib.pyplot as plt 7 import operator # 運算符模塊 8 import matplotlib as mpl 9 # 解決圖表中文亂碼的問題 10 mpl.rcParams['font.sans-serif'] = [u'SimHei'] 11 12 13 def classify0(inX, dataSet,labels,k): 14 """ 15 採用KNN算法分類 16 :param inX: 測試樣本 17 :param dataSet: 訓練樣本數據集 18 :param labels: 標籤向量 19 :param k: 最近鄰居的數目 20 :return: 21 """ 22 23 # 計算測試樣本和訓練樣本間的距離 24 dataSetSize = dataSet.shape[0] # 數據集大小 25 # 能夠用tile()函數 或者用numpy的廣播規則 26 # diffMat2 = inX - dataSet # numpy的廣播規則 27 diffMat = tile(inX,(dataSetSize,1)) - dataSet # tile()函數至關於將inX向量在列方向上重複了dataSetSize次,行方向上重複1次 28 sqDiffMat = diffMat ** 2 # 矩陣的乘方 29 sqDistance = sqDiffMat.sum(axis=1) # 按行相加 30 distance = sqDistance ** 0.5 # 取平方根32 33 # 將計算的距離從小到大排列,找出其中最小的K個 34 sortedDistIndicies = distance.argsort() # argsort()返回的是排序後的索引 35 # 採用的決策規則爲「投票規則」,用字典的方式存儲 36 classCount = {} 37 for i in range(k): 38 voteIlabel = labels[sortedDistIndicies[i]] 39 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 # dict.get(key, default=None) key -- 字典中要查找的鍵。default -- 若是指定鍵的值不存在時,返回該默認值 40 41 # 排序距離最小的K個訓練樣本的分類個數 42 # Python 字典(Dictionary) items() 函數以列表返回可遍歷的(鍵, 值) 元組數組。 43 44 # sorted函數 45 # reverse = True 降序 或者 reverse = False 升序,有默認值。 46 # key=operator.itemgetter(1) 表示用元組的第二個屬性比較 47 48 sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) 49 50 # sortedClassCount 得到的是通過排序的由元組構成的數組(降序) 51 return sortedClassCount[0][0]
總結:以上KNN算法的三個關鍵問題就是:距離度量採用了「歐式距離」,不熟悉的童鞋能夠上網查查。另外,決策規則採用的是「投票規則」,K值取爲3。測試
二、在搞清楚KNN算法的的具體實現流程之後,咱們就能夠用數據來測試算法的性能了。咱們知道,分類算法的測試評估方法有不少,一般採用的是經過「錯誤率」來評估分類器的好壞。如下代碼就是KNN分類器的測試代碼:網站
2.1 首先是測試數據的處理,這裏因爲數據是存儲在文本文件中,所以首先須要讀取文本文檔的數據:
1 def file2matrix(filename): 2 """ 3 獲取文本文件的數據 4 :param filename: 5 :return: 6 """ 7 fr = open(filename) 8 arrayOLines = fr.readlines() # 讀取文件內容 9 numberOfLines = len(arrayOLines) # 獲取文件行數 10 returnMat = zeros((numberOfLines,3)) # 建立要返回的矩陣 11 classLabelVector = [] # 類標籤向量 12 index = 0 13 14 # 解析文件 15 for line in arrayOLines: 16 # 用strip截取掉全部的回車字符 17 line = line.strip() # strip()方法用於移除字符串頭尾指定的字符(默認爲空格)。 18 listFromLine = line.split('\t') # 用分割製表符獲取數據 19 returnMat[index,:] = listFromLine[0:3] 20 classLabelVector.append(int(listFromLine[-1])) 21 index += 1 22 return returnMat, classLabelVector
在通過以上步驟後,咱們能夠拿到由文本文件獲取而來的樣本實例矩陣returnMat以及類標記向量classLabelVector。那麼在提出第二個步驟以前,咱們先來看一個示例,假設咱們要計算測試樣本X=(0,20000,1.1)和訓練樣本Y=(67,32000,0.1)之間的距離,採用歐式距離的計算方法,有如下一對數據的計算公式:
咱們能夠看到,樣本的第二維特徵的計算(20000-32000)的值要遠遠大於其餘兩個維的屬性值的計算值,這在三個屬性特徵同等重要的前提下,顯然是不符合實際的,由於如此巨大的數值差顯然會影響其餘兩維特徵對分類器的決策能力。因而爲了解決這個問題,咱們提出了「歸一化數值」的方法。即將全部的數值的取值範圍處理到0-1或者-1到1之間。即:
newValue = (oldValue - minValue) / (maxValue - minValue)
其中minValue和maxValue是數據集中的最小、最大特徵值。
如下貼出「歸一化數值「的代碼:
2.二、數值歸一化處理:
1 def autoNorm(dataSet): 2 """ 3 數據歸一化處理,將數字的特徵值轉化到0-1區間 4 newValue = (OldValue-min) / (max - min) 5 :return: 6 """ 7 minVals = dataSet.min(0) # 返回每一列的最小值 8 maxVals = dataSet.max(0) # 返回每一列的最大值 9 ranges = maxVals - minVals 10 normDataSet = zeros(shape(dataSet)) 11 m = dataSet.shape[0] 12 normDataSet = dataSet -tile(minVals,(m,1)) # 將minVals在行方向上重複m次,列方向上重複1次 13 normDataSet = normDataSet/tile(ranges,(m,1)) # 具體特徵值相除 14 return normDataSet,ranges,minVals
總結:以上數值歸一化處理只須要注意一個問題,就是選取的是每一維的最大和最小特徵值,而非整個樣本矩陣的。另外對於tile函數的用法,若是不明白的話,能夠去網上查,或者看我收藏的另一篇帖子:http://www.cnblogs.com/ma-lijun/articles/7867243.html
三、KNN分類器測試代碼:
1 def datingClassTest(): 2 """ 3 分類器測試代碼,採用「錯誤率」來評估分類器的好壞 4 :return: 5 """ 6 hoRatio = 0.10 7 # 獲取文本數據,將數據和分類標記分別開來 8 datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') 9 # 採用數據歸一化處理,將全部數據化爲0-1的數值範圍 10 normMat,ranges,minVals = autoNorm(datingDataMat) 11 12 m = normMat.shape[0] # 獲取歸一化矩陣的行數,這裏共有1000行數據 13 numTestVecs = int(m*hoRatio) # 需測試樣本的行數 14 errorCount = 0.0 # 錯誤率 15 # 用前numTestVecs個樣本測試分類器的準確率 16 for i in range(numTestVecs): 17 results = classify0(normMat[i,:],normMat[numTestVecs:,:],datingLabels[numTestVecs:],3) 18 print("分類器分爲:%d,=========真實分類爲:%d"%(results,datingLabels[i])) 19 if(results != datingLabels[i]): 20 errorCount += 1.0 21 print("最重測試分類器的錯誤率爲:%f" %(errorCount/numTestVecs))
總結:這裏測試代碼選取了數據集的前10%做爲測試樣本,後90%做爲訓練樣本。實際上,樣本數據一共有1000個,因此這裏用了前100個樣本做爲測試樣本,後900個樣本做爲訓練樣本。經過計算分類「錯誤率」,來評估算法的好壞。測試結果爲5%。
以上便是這篇帖子的所有內容,初次寫這麼長的帖子,有什麼錯誤或者意見,歡迎指正。下面我貼上這期KNN算法的樣本數據以及算法源碼。