1. 程式人生 > >KNN(K近鄰)演算法的簡單入門

KNN(K近鄰)演算法的簡單入門

機器學習實戰(第二章:k-近鄰演算法)

今天學習了第二章,在此就我理解做一下簡單的總結,算是加深我的理解和用我自己的語言描述出這個演算法吧。

距離計算

基於向量空間的歐幾里得距離的計算。(L2距離)

特別情況下可採用Lp距離(明氏距離) L1距離。

簡單點來說就是 在一個具有大量樣本集中,每一個例項都具有3個或以上的特徵屬性,其中有一個屬性必然是分類屬性,其餘屬性為數值型屬性(即使是標稱型屬性,也可以通過 某些方法轉變過來),每一個例項都是由屬性特徵值組成的一個向量,一個樣本集就是多個向量組成。

例如下面這個例子

 身高 體重 年齡 性別
170 140 22
160 100 21

“”性別“”可以看成是一個分類屬性,然後其他看特徵屬性 ,組成一個例項向量就為[170,140,22]和[160,100,21]

一、演算法步驟:
1、計算已知類別資料集中的點與當前點之間的距離;
2、按照距離遞增次序排序;
3、選取與當前點距離最小的k個點;
4、確定k個點所在類別的出現頻率;

(K用於選擇最近鄰的數目,K的選擇非常敏感。K值越小意味著模型複雜度越高,從而容易產生過擬合;K值越大則 意味著整體的模型變得簡單,學習的近似誤差會增大,在實際的應用中,一般採用一個比較小的K值,用交叉驗證的 方法,選取一個最優的K值。)
5、返回前k個點出現頻率最高的類別作為當前點的預測分類

二、例子說明(python)

首先按照演算法編寫一個knn演算法類

'''
inX:用於分類的輸入向量
dataSet:訓練樣本集
labels:標籤向量
k:用於選擇最近鄰的數目

'''
def classify0(inX,dataSet,labels,k):
#距離計算
dataSetSize = dataSet.shape[0]
diffMat = tile(inX,(dataSetSize,1))-dataSet
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort() #按從小到大的排序後的索引值
#選擇距離最小的k個點
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0)+1
#排序

sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]


舉一個例項說明(手寫識別資料)

資料說明:


我們需要把每一個樣本資料轉變成向量的格式,由於圖片是32X32的txt文字格式存在,我們轉變成1X1024的向量,並把所有的訓練樣本儲存為一個矩陣,對應的每一行就是每一個例項。

#把一個32X32的變成1X1024的向量
def img2vector(filename):
    returnVec = zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVec[0,32*i+j] = int(lineStr[j])
    return returnVec


運用我們事先寫好的knn演算法類,按照格式代進去訓練並且計算錯誤率。

def handwritingClassTest():
    hwLabels = []
    #獲取訓練集
    trainingFileList = os.listdir('digits//trainingDigits') #獲取該目錄下所有檔名 型別:list
    m = len(trainingFileList)
    trainingMat = zeros((m,1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]               #7_173.txt
        fileStr = fileNameStr.split('.')[0]             #'7_173' 'txt'
        classNumStr = int(fileStr.split('_')[0])        #7  173
        hwLabels.append(classNumStr)
        trainingMat[i,:] = img2vector("digits//trainingDigits//"+fileNameStr)
    #獲取測試集
    testFileList = os.listdir("digits//testDigits")
    errorCount = 0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]  # '7_173' 'txt'
        classNumStr = int(fileStr.split('_')[0])  # 7  173
        vectorUnderTest = img2vector("digits//testDigits//"+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
    print "the total number of errors is:%d" %errorCount
    print "the total error rate is:%f" %(errorCount/float(mTest))

總結:

優點
1.精度高
2.對異常值不敏感
3.沒有對資料的分佈假設
缺點
1、knn演算法不像其他演算法有一個訓練的過程
2、knn演算法針對那些分類不均勻的分類訓練樣本可能誤差較大
3、計算量太大,每一個待測測試樣本都要遍歷一遍訓練樣本來計算距離
4、我們無法知曉例項樣本和典型例項樣本具有什麼特徵,無法給出任何資料的基礎結構資訊