K-近鄰算法


1. 概念

測量不同特征值之間的距離來進行分類

優點:精度高、對異常值不敏感、無數據輸入假定

缺點:計算復雜度高、空間復雜度高。

適用范圍:數值型和標稱型

工作原理:

存在一個樣本數據合計,也稱作訓練樣本集,並且樣本集中每個數據都存在標簽,即我們知道樣本集中每一數據與所屬分類的對應關系。輸入沒有標簽的新數據后,將新數據的每個特征與樣本集中數據對應的特征進行比較,然后算法提取樣本集中特征最相似(最近鄰)數據的分類標簽。一般會選擇樣本數據集中前K個最相似的數據,K通常不大於20的整數,選擇K個最相似數據中出現次數最多的分類,作為新數據的分類。

一般流程:

  1.  收集數據:任何方法
  2.  准備數據:距離計算所需要的數值,最好是結構化數據格式
  3.  分析數據:任何方法
  4.  訓練算法:不適用
  5.  測試算法:計算錯誤率
  6.  使用算法:輸入樣本數據和結構化的輸出結果,運行K近鄰算法判斷輸入數據分別屬於哪個分類,最后應用對計算出的分類執行后續的處理

2. Python 實現

1. 數據輸入

建立一個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

operator:運算符模塊

createDataSet函數用於創建simple數據集和標簽

在Console中

import KNN

group,labels = KNN.createDataSet()

group
Out[69]: 
array([[ 1. ,  1.1],
       [ 1. ,  1. ],
       [ 0. ,  0. ],
       [ 0. ,  0.1]])

labels
Out[70]: ['A', 'A', 'B', 'B']

通過KNN中的createDataSet函數創建變量group和labels

4個樣本集在坐標軸上的位置:

2. 實施KNN分類算法

實現步驟:

  1.  計算已知類別數據集中的點與當前點之間的距離;
  2.  按照距離遞增次序排序;
  3.  選取與當前點距離最小的k個點;
  4.  確定前k個點所在類別的出現頻率;
  5.  返回前k個點出現頻率最高的類別作為當前點的預測分類;

在KNN.py文件中創建classify0函數

 1 def classify0(inX,dataSet,labels,k):
 2     dataSetSize = dataSet.shape[0]
 3     #距離計算
 4     diffMat = tile(inX,(dataSetSize,1)) - dataSet
 5     sqDiffMat = diffMat ** 2
 6     sqDistances = sqDiffMat.sum(axis = 1)
 7     distances = sqDistances ** 0.5
 8     sortedDistIndicies = distances.argsort()
 9     classCount = {}
10     for i in range(k):
11         voteIlabel = labels[sortedDistIndicies[i]]
12         classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
13     sortedClassCount = sorted(classCount.iteritems(),
14                               key=operator.itemgetter(1),
15                               reverse=True)
16     return sortedClassCount[0][0]
參數:
  • inX: 輸入樣本
  • dataSet: 訓練樣本集
  • labels:訓練樣本集標簽
  • k: 選取k個點
代碼中使用的python方法:
  • dataSet.shape():返回數據集維度(幾行幾列),這里需要返回有幾條數據(幾行) 可以用len(dataSet)替代?
  • tile(inX,(dataSetSize,1)):將輸入樣本放入(並復制)到一個與數據集相同大小的矩陣
疑問:為什么不直接減?

tile(inX,(dataSetSize,1))- dataSet  與 inX - dataSet        結果一樣

sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True):排序,reverse=True降序排列

計算距離公式

勾股定律:

重新裝載KNN模塊,python提示符中使用classify0函數來預測數據

1 reload(KNN)
2 Out[101]: <module 'KNN' from 'KNN.py'>
3 
4 KNN.classify0([0,0],group,labels,3)
5 Out[102]: 'B'

3. 示例:使用K近鄰算法改進約會網站的配對效果

示例說明:

樣本集中包含三個特征值:

  • 每年獲得的飛行常客里程數
  • 玩視頻游戲所耗時間百分比
  • 每周消費的冰激凌公升數

標簽種類:

  • 不喜歡的人
  • 魅力一般的人
  • 極具魅力的人

1. 從文本文件中解析數據

文本文件datingTestSet.txt

在KNN.py中增加文本轉換為Numpy的解析函數

 1 def file2matrix(filename):
 2     fr = open(filename)
 3     array0lines = fr.readlines()
 4     numberOfLines = len(array0lines)
 5     returnMat = np.zeros((numberOfLines,3))
 6     classLabelVector = []
 7     index = 0
 8     for line in array0lines:
 9         line = line.strip()
10         linstFromLine = line.split('\t')
11         returnMat[index,:] = linstFromLine[0:3]
12         classLabelVector.append(int(linstFromLine[-1]))
13         index +=1
14     return returnMat,classLabelVector
參數:
  • filename:文件名
代碼中使用的python方法:
  • np.zeros((numberOfLines,3)) :創建以零填充的矩陣
  • line.strip():去除回車字符
  • line.split('\t'):以\t為分隔符分割字符

python命令提示符下輸入命令:

 1 reload(KNN)
 2 Out[111]: <module 'KNN' from 'KNN.pyc'>
 3 
 4 datingDataMat,datingLabels = KNN.file2matrix('datingTestSet2.txt')
 5 
 6 datingDataMat
 7 Out[113]: 
 8 array([[  4.09200000e+04,   8.32697600e+00,   9.53952000e-01],
 9        [  1.44880000e+04,   7.15346900e+00,   1.67390400e+00],
10        [  2.60520000e+04,   1.44187100e+00,   8.05124000e-01],
11        ..., 
12        [  2.65750000e+04,   1.06501020e+01,   8.66627000e-01],
13        [  4.81110000e+04,   9.13452800e+00,   7.28045000e-01],
14        [  4.37570000e+04,   7.88260100e+00,   1.33244600e+00]])
15 
16 datingLabels[:5]
17 Out[114]: [3, 2, 1, 1, 1]

2. 分析數據:使用Matplotlib創建散點圖

1 import matplotlib
2 import matplotlib.pyplot as plt
3 fig = plt.figure()
4 ax = fig.add_subplot(111)
5 ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
6 fig

1 ax.scatter(datingDataMat[:,1],datingDataMat[:,2],
2            15.0*array(datingLabels),15.0*array(datingLabels))
3 fig

1 ax.scatter(datingDataMat[:,0],datingDataMat[:,1],
2            15.0*array(datingLabels),15.0*array(datingLabels))
3 fig

3. 准備數據:歸一化數值

三個特征值計算樣本距離:

每年獲得飛行常客里程數遠大於其他特征值需要轉化為0-1區間內的值

newValue = (oldValue-min)/(max-min)

在KNN.py中增加歸一化特征值函數

 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

執行歸一化函數

 1 reload(KNN)
 2 Out[145]: <module 'KNN' from 'KNN.py'>
 3 
 4 normMat,ranges,minVals = KNN.autoNorm(datingDataMat)
 5 
 6 normMat
 7 Out[147]: 
 8 array([[ 0.44832535,  0.39805139,  0.56233353],
 9        [ 0.15873259,  0.34195467,  0.98724416],
10        [ 0.28542943,  0.06892523,  0.47449629],
11        ..., 
12        [ 0.29115949,  0.50910294,  0.51079493],
13        [ 0.52711097,  0.43665451,  0.4290048 ],
14        [ 0.47940793,  0.3768091 ,  0.78571804]])
15 
16 ranges
17 Out[148]: array([  9.12730000e+04,   2.09193490e+01,   1.69436100e+00])
18 
19 minVals
20 Out[149]: array([ 0.      ,  0.      ,  0.001156])

 4. 測試算法:作為完整程序驗證分類器

 在KNN.py文件中添加測試函數datingClassTest

 1 def datingClassTest():
 2     hoRatio = 0.1
 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,:],
10                                      datingLabels[numTestVecs:m],3)
11         print "the classifier came back with: %d, the real answer is: %d" % (classifierResult,datingLabels[i])
12         if (classifierResult != datingLabels[i]):
13             errorCount += 1.0
14     print "the total error rate is: %f" % (errorCount/float(numTestVecs))

 在python命令提示符中運行datingClassTest()

1 reload(KNN)
2 Out[153]: <module 'KNN' from 'KNN.py'>
3 
4 KNN.datingClassTest()
5 the classifier came back with: 3, the real answer is: 3
6 the classifier came back with: 2, the real answer is: 2
7 the classifier came back with: 1, the real answer is: 1
8 the classifier came back with: 3, the real answer is: 1
9 the total error rate is: 0.050000

錯誤率為5%

 5. 使用算法:構建完整可用系統

在KNN.py中加入classifyPerson()函數:

 1 def classifyPerson():
 2     resultList = ['not at all','insmall doses','in large doses']
 3     percentTats = float(raw_input("percentage of time spent playing video games?"))
 4     ffMiles = float(raw_input("frequent flier miles earned per year?"))
 5     iceCream = float(raw_input("liters of ice cream consumed per year?"))
 6     datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
 7     normMat , ranges, minVals = autoNorm(datingDataMat)
 8     inArr = array([ffMiles,percentTats,iceCream])
 9     classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
10     print "You will probably like this person:",resultList[classifierResult - 1]

在python命令提示行執行classifyPerson函數

 1 reload(KNN)
 2 Out[155]: <module 'KNN' from 'KNN.py'>
 3 
 4 KNN.classifyPerson()
 5 
 6 percentage of time spent playing video games?10 7 8 frequent flier miles earned per year?10000 9 10 liters of ice cream consumed per year?0.5 11 You will probably like this person: insmall doses

 4. 示例:手寫數字識別系統

將圖像轉換為測試向量,將32*32的二進制圖像矩陣轉換為1*1024的向量

創建函數img2vector

1 def img2vector(filename):
2     returnVect = zeros(1,1024) #創建1*1024的矩陣
3     fr = open(filename) 
4     for i in range(32):
5         lineStr = fr.readline()
6         for j in range(32):
7             returnVect[0,32*i+j] = int(lineStr[j])

測試算法:使用k近鄰算法識別手寫數字

 1 def handwritingClassTest():
 2     hwLabels = []
 3     trainingFileList = listdir('trainingDigits') #獲取目錄內容
 4     m = len(trainingFileList)
 5     trainingMat = zeros((m,1024))
 6     #獲取訓練集向量和標簽
 7     for i in range(m):
 8         #解析文件名
 9         fileNameStr = trainingFileList[i]
10         fileStr = fileNameStr.split('.')[0]
11         classNumStr = int(fileStr.split('_')[0])
12         hwLabels.append(classNumStr)
13         trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
14     testFileList = listdir('testDigits')
15     errorCount = 0
16     mTest = len(testFileList)
17     #獲取測試集向量進行測試並將測試結果與正式結果比對
18     for i in range(mTest):
19         fileNameStr = testFileList[i]
20         fileStr = fileNameStr.split('.')[0]
21         classNumStr = int(fileStr.split('_')[0])
22         vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
23         classifierResult = KNN.classify0(vectorUnderTest,trainingMat,hwLabels,3)
24         print "the classifier came back with %d,the real answer is:%d" % (classifierResult,classNumStr)
25         if classifierResult != classNumStr:
26             errorCount += 1.0
27     print '\n the total number of errors is:%d' % errorCount
28     print '\n the total error rate is:%f' % (errorCount/float(mTest))

在python命令行運行handwritingClassTest()函數

handwritingClassTest()

the classifier came back with 9,the real answer is:9
the classifier came back with 9,the real answer is:9
the classifier came back with 9,the real answer is:9

 the total number of errors is:11

 the total error rate is:0.011628

錯誤率1.1628%

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM