機器學習 - K近臨法(KNN)

博客內容源於《統計機器學習》一書的閱讀筆記。Python的源碼實現源於《機器學習實戰》部分內容。

1. K近臨算法

【算法描述】給定一個訓練數據集,對於新的輸入實例,在訓練數據集中找到與該實例最近臨的K個實例,這個k個實例的多數屬於某個類,就把該輸入實例分爲這個類。

【數學描述】
輸入:訓練數據集

T={(x1,y1),(x2,y2),...,(xN,yN)}

其中,xiXRn 爲實例的特徵向量,yiY={c1,c2,...,cK} 爲實例類別,i=1,2,...,N ;實例特徵向量x ;
輸出:實例x 所屬的類 y

【算法求解過程】
1. 根據給定的距離度量,在訓練集T 中找出與 x 最近鄰的 k 個點,涵蓋這個k 個點的領域記作Nk(x) ;
2. 在Nk(x) 中根據分類決策規則(比如多數表決)決定 x 的類別y :

y=argmaxcjxiNk(x)I(yi=cj)i=1,2,...,N;j=1,2,...,K

上式中,函數I 爲指示函數,當yi=cjI 爲1,否則I 爲0。

2. K近臨模型

K近臨模型由三個參數決定:距離度量k值分類決策規則

2.1 距離度量

設特徵空間Xn 維實數向量空間Rnxi,xjX,xi=(x(1)i,x(2)i,...,x(n)i)xj=(x(1)j,x(2)j,...,x(n)j)xi,xjLp 距離定義爲:

Lp(xi,yj)=(l=1n|x(l)ix(l)j|p)1pp>=1

2.1.1 歐氏距離(Euclidean Distance)

歐氏距離是最易於理解的一種距離計算方法,源自歐氏空間中兩點間的距離公式。上述公式中p=2 時,稱爲歐式距離:

L2(xi,yj)=(l=1n|x(l)ix(l)j|2)12

2.1.2 曼哈頓距離(Manhattan Distance)

p=1 時,稱爲曼哈頓距離:

L1(xi,yj)=(l=1n|x(l)ix(l)j|)

2.1.3 夾角餘弦(Cosine)

幾何中夾角餘弦可用來衡量兩個向量方向的差異,機器學習常常中用這一概念來衡量樣本向量之間的差異。

cosθ=x⃗ ix⃗ j|x⃗ i||x⃗ j|

2.2 K值選取

K值的選取會對k近臨算法的結果產生很大的影響。

  • K值很小時,預測結果會對噪聲特別敏感。假設臨近的實例點恰好是噪聲,預測的結果就會出錯。
  • K值很大,與輸入實例距離較遠的實例點也會起作用,導致預測的結果出錯。

2.3 分類決策

k近臨中的分類決策規則往往使用多數表決,即由輸入實例的k個鄰近的訓練實例中的多數類決定輸入實例的類。
【多數表決】
如果分類的損失函數爲0-1損失函數,分類函數爲

f:Rn{c1,c2,...,cK}

那麼誤分的概率是
P(Yf(X))=1P(Y=f(X))

對於給定的實例xX , 其最近鄰的k個訓練實例點構成集合Nk(x) 。如果涵蓋Nk(x) 的區域的類別是cj , 那麼誤分率是:
1kxiNk(x)I(yicj)=11kxiNk(x)I(yi=cj)

要使誤分類率最小(經驗風險最小),就要使xiNk(x)I(yi=cj) 最大,所以多數表決規則等價於經驗風險最小化。

3. K近臨舉例

數據來自於《機器學習實戰》一書中,數據下載鏈接:
https://github.com/bzhou830/ML-python/blob/master/ch1-KNN/datingTestSet2.txt
1.從文件中導入元數據

def file2matrix(filename):
    fr = open(filename)                                             #打開文件
    arrayOfLines = fr.readlines()                                   #讀取文件行數
    numberOfLines = len(arrayOfLines)                               #文件行數
    returnMat=zeros((numberOfLines,3))                              #矩陣
    classLabelVector = []                                           #標籤
    index = 0                           
    for line in arrayOfLines:           
        line = line.strip() 
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector

2.使用matplotlib繪製數據集合圖像

def PrintFigure(datingDataMat,datingLabels):
    fig = plt.figure()
    ax=fig.add_subplot(111, projection='3d')
    #繪製三維圖
    num = len(datingDataMat)
    for i in range(num):
        if datingLabels[i] == 1:
            ax.scatter(datingDataMat[i][0],datingDataMat[i][1],datingDataMat[i][2], c='b', marker='x')
        elif datingLabels[i] == 2:
             ax.scatter(datingDataMat[i][0],datingDataMat[i][1],datingDataMat[i][2], c='r', marker='o')
        elif datingLabels[i] == 3:
             ax.scatter(datingDataMat[i][0],datingDataMat[i][1],datingDataMat[i][2], c='g',marker='*')
        elif datingLabels[i] == 0:
             ax.scatter(datingDataMat[i][0],datingDataMat[i][1],datingDataMat[i][2], marker='1')

    plt.show()


數據集合

可以直觀的看到元數據有三類。

3.完整程序

'''
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()                        #數組從小到大的索引值
    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]                                   #返回數量最多的標籤


def file2matrix(filename):
    fr = open(filename)                                             #打開文件
    arrayOfLines = fr.readlines()                                   #讀取文件行數
    numberOfLines = len(arrayOfLines)                               #文件行數
    returnMat=zeros((numberOfLines,3))                              #矩陣
    classLabelVector = []                                           #標籤
    index = 0                           
    for line in arrayOfLines:           
        line = line.strip() 
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector

'''
dataSet 傳入矩陣數據集
normDataSet 返回歸一化後數據集
ranges 
minVals 
'''
def autoNorm(dataSet):
    minVals = dataSet.min(0)                                        #
    maxVals = dataSet.max(0)                                        #
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals,(m,1))
    normDataSet = normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,minVals


def datingClassTest():
    hoRatio = 0.20                                                  #測試數據集所佔比例
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')  #加載數據
    normMat, ranges, minVals = autoNorm(datingDataMat)              #歸一化數據
    m = normMat.shape[0]                                            
    numTestVecs = int(m*hoRatio)                                    #測試數數量
    errorcount = 0.0
    for i in range(numTestVecs):                                    #
        classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
        print "Rt is %d, Real is %d" % (classifierResult,datingLabels[i])
        if(classifierResult != datingLabels[i]):
            errorcount += 1.0

    print " %f " % (errorcount/float(numTestVecs))

程序文件可以可以到github上下載:https://github.com/bzhou830/ML-python/

發佈了110 篇原創文章 · 獲贊 125 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章