k-近鄰算法


K-近鄰算法

機器學習與實戰是對《機器學習實戰》這本書的學習總結。

一:k-近鄰算法分析

簡單說,k-近鄰算法採用測量不同特徵值之間的舉例方法進行分類。

工作原理:存在一個樣本數據集合,也稱訓練樣本集,並且樣本集中每個數據都存在標籤,即我們知道樣本集中每一數據與所屬分類對應的關係。輸入沒有標籤的新數據後,將新數據的每個特徵與樣本集中數據對應的特徵進行比較,然後算法提取樣本集中特徵最相似的數據(最近鄰)的分類標籤。一般來說,我們只選擇樣本數據集中前k個最相似的數據,這就是k-近鄰算法k的出處,通常k是不大於20的整數。最後,選擇k個最相似數據中出現次數最多的分類,做爲新數據的標籤。

二:示例

注:示例中用的Numpy,Matplotlib等請自行網上下載安裝

示例1

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

#分類
def classify0(inX, dataSet, labels, k): 
    dataSetSize = dataSet.shape[0]

    #計算歐式距離  
    # tile(A, reps): 構造一個數據,重複 A reps 次  
    diffMat = tile(inX, (dataSetSize,1)) - dataSet # Subtract element-wise

    sqDiffMat = diffMat**2 # 平方  

    sqDistances = sqDiffMat.sum(axis=1)# sum is performed by row
    distances = sqDistances**0.5

    # argsort() 升序方式排序,返回索引值
    sortedDistIndicies = distances.argsort()

    classCount = {} # 定義一個字典,可以追加元素

    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
    
        ## 計算 label 出現的次數 ,如果沒有返回0
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 

   #返回最大值的類
    maxCount = 0   
    for key, value in classCount.items():  
        if value > maxCount:  
            maxCount = value  
            maxIndex = key  

    return maxIndex

歐式距離公式:計算兩個向量A(x0,y0),B(x1,y1)的距離

d = \sqrt{(x_0-y_0)^2+(y_0-y_1)^2}

測試:

group,labels = kNN.createDataSet();
kNN.classify0([0,0],group, labels, 3)
結果:


示例2 使用k-近鄰算法改進約會網站的配對效果

1、將文本記錄轉換爲NumPy的解析程序

def file2Matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)#獲得行數
    returnMat = zeros((numberOfLines,3))#創建一個相同行,3列的矩陣
    classLabelVector = []
    index = 0 
    for line in arrayOLines:
        line = line.strip()#截取掉所有的回車字符
        listFormLine = line.split('\t')#將整行數據分割成一個元素列表
        returnMat[index,:] = listFormLine[0:3]#每行取前3個元素
        classLabelVector.append(int(listFormLine[-1]))#將每行的最後一列的元素,添加到向量中
        index +=1 
    return returnMat,classLabelVector
2、使用Matplotlib創建散點圖

from numpy import *
import numpy as np  
import matplotlib
import matplotlib.pyplot as plt 
import kNN

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,0],datingDataMat[:,1],15*array(datingLabels),15*array(datingLabels))
plt.show()

結果如下圖



3、歸一化數值 數值的屬性對計算結果影響很大,因此在處理不同取值範圍的特徵值時,通常採用的方法是將數值歸一化。 將任意取值範圍的數值轉化爲0到1區間內:

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

def autoNorm(dataSet):
    minVals = dataSet.min(0)#每列的最小值
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))#創建和dataSet同樣維度的矩陣
    m = dataSet.shape[0]#獲取dataSet的列數
    normDataSet = dataSet - tile(minVals,(m,1)) #矩陣對應元素相減
    normDataSet = dataSet / tile(ranges,(m,1)) 
    return normDataSet, ranges, minVals
4、驗證分類器

#創建測試代碼, 10%用來測試
def datingClassTest():
    hoRatio = 0.10 #10%
    datingDataMat,datingLabels = file2Matrix('datingTestSet2.txt')
    normMat,ranges,minVals = autoNorm(datingDataMat)
    m = normMat.shape[0] #  Matrix normMat's row number is  m
    numTestVecs = int(m * hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
        print "the classifier came back with: %d , the real answer is: %d" %(classifierResult,datingLabels[i])
        if(classifierResult != datingLabels[i]): errorCount +=1.0
    print "the total error rate is : %f" %(errorCount/float(numTestVecs))
測試:
kNN.datingClassTest()

測試結果

the classifier came back with: 1 , the real answer is: 1
the classifier came back with: 2 , the real answer is: 2
the classifier came back with: 1 , the real answer is: 1
the classifier came back with: 3 , the real answer is: 3
the classifier came back with: 3 , the real answer is: 3
the classifier came back with: 2 , the real answer is: 2
the classifier came back with: 1 , the real answer is: 1
the classifier came back with: 3 , the real answer is: 1
the total error rate is : 0.050000

示例3:手寫識別系統

#將一個filename中矩陣轉化爲一個向量
def img2vector(filename):
    returnVect = zeros((1,1024))    #定義一個1行1024列的矩陣
    fr = open(filename)
    for i in range(32):             #共32 行
        lineStr = fr.readline()     #讀一行
        for j in range(32):         #讀一個值
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect


def handWritingClassTest():
    hwLabels = []           #定義一個列表
    trainingFileList = listdir('trainingDigits') #打開訓練集目錄,獲取目錄(trainingDigits)的內容
    m = len(trainingFileList) #目錄中文件個數
    trainingMat = zeros((m,1024)) #定義一個m行1024列的矩陣
    for i in range(m):
        fileNameStr = trainingFileList[i]   #第i個文件名
        fileStr = fileNameStr.split('.')[0] #以“.”分割文件名,取前面的部分
        classNumStr = int(fileStr.split('_')[0])#以“-”分割文件名,取前面的部分
        hwLabels.append(classNumStr) #將改文件代表的數字,添加到列表中
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
    testFileList = listdir('testDigits')#打開測試集目錄
    errorCount = 0.0 
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
        classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,3 )   
        print "the classifier came back with : %d , the real answer is : %d " %(classifierResult,classNumStr)
        if (classifierResult != classNumStr): errorCount += 1.0 
    print "\n the total number of errors is : %d " % errorCount
    print "\n the total error rate is : %f " % (errorCount / float(mTest)) 

源代碼混合數據下載地址

(End)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章