機器學習與數據挖掘—K鄰近算法(KNN)

KNN:分類算法

目標:

對未知類別的樣本進行分類預測

步驟:

1.對於某個未知類別樣本,根據距離度量計算每個已知類別樣本與其距離。
2.選出K個與該未知類別樣本距離最小的已知類別的樣本。
3.在K個已知類別樣本里得到頻數最多的類別,該類別就是未知類別樣本的預測。

KNN算法:


# 尋找K值 1~60 打印誤差最小的K值和對應的誤差個數及誤差率
from numpy import *

import operator  # 引入運算符模塊,K鄰近算法會使用到其中的函數


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 classifyO(inX, dataSet, labels, k):  # KNN算法
    # 用於分類的輸入向量是inX,
    # 輸入的訓練樣本集爲dataSet,標籤向量爲labe1s,最後的參數k表示用於選擇最近鄰居的數目,
    # 其中標籤向量的元素數目和矩陣dataset的行數相同。

    dataSetSize = dataSet.shape[0]  # shape[0]就是讀取矩陣第一維度的長度。
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet
    #根據公式可推測出計算差值
    # 把inx行列重複copy(dataSetSize,1)次。
    # inX是個向量,而dataset是個矩陣,兩者之間要進行相減的運算,需要把這個向量也補成一個和dataset有相同行數列數的矩陣,
    # tile()的第二個參數,也就是(datasetsize,1),這個參數的意思就是把inX補成有datasetsize行數的矩陣。
    # 然後和dataset相減就是根據矩陣的減法進行的。因此diffMat是一個差值矩陣。
    sqDiffMat = diffMat ** 2  # 歐氏距離計算公式
    sqDistances = sqDiffMat.sum(axis=1)  # axis這個參數,它決定對矩陣求和時候的順序,axis=0是按照行求和,axis=1是按照列進行求和。
    distances = sqDistances ** 0.5  # 0.5次方即開根號
    sortedDisIndicies = distances.argsort()  # 就是把向量中每個元素進行排序,而它的結果是元素的索引(原來的下標)形成的向量。
    classCount = {}  # 生成字典,對classCount元素賦值,其實是個計數器
    # 選擇距離待分類點最小的k個點
    for i in range(k):  # 遍歷k,尋找該樣本標籤的類型
        voteIlabel = labels[sortedDisIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    # python3字典的 items() 方法,以列表的形式返回可遍歷的(鍵,值)元組數組。
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    # 內建函數 sorted 方法返回的是一個新的 list,而不是在原來的基礎上進行的操作與sort方法不一樣。
    # reverse -- 排序規則,reverse = True 降序 , reverse = False 升序(默認)。
    # sorted函數返回類型爲list
    return sortedClassCount[0][0]  # 返回前k個點出現頻率最高的類別作爲當前點的預測分類。即最符合的標籤

# 文件導入

def file2matrix(filename):
    fr = open(filename)
    # 得到文件行數
    arrayOLines = fr.readlines()
    # 創建返回的NumPy矩陣
    numberOfLines = len(arrayOLines)
    returnMat = zeros((numberOfLines, 3))
    # 解析文件數據到列表
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector

#散點圖代碼塊 (已註釋需要運行出圖片解註釋即可)

#from numpy import array
#import matplotlib
#import matplotlib.pyplot as plt
#fig = plt.figure()
#ax = fig.add_subplot(111)
#datingDataMat,datingLabels = file2matrix('F:\python項目\KNN\datingTestSet2.txt')
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
#plt.show()

#歸一化特徵值
def autoNorm(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    #將每列的最小值放在變量minvals中,將最大值放在變量maxVals中,
    # 其中dataset.min(0)中的參數0使得函數可以從列中選取最小值,而不是選取當前行的最小值。
    # 然後,函數計算可能的取值範圍,並創建新的返回矩陣。
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (m,1))#特徵在短陣有1000×3個值,而minvals與raange的值都爲1×3。因此利用tile()
    #函數將變量內容複製成輸入矩陣同樣大小的矩陣
    normDataSet = normDataSet/tile(ranges, (m,1)) #矩陣除法
    return normDataSet, ranges, minVals



#自動化調試模塊代碼


def Autodebug(k):
    minCount = 20 #假如其中有20個誤差,結果當然沒有這麼多,設置這麼大也沒關係,反正minCount會被覆蓋掉
    for k in range(1,60):
        datingClassTest(k)
        #print("the total error rate is : ", datingClassTest(k)[1]) #該條註釋用來驗證結果
       #print("the total error count is : ", datingClassTest(k)[0]) #該條註釋用來驗證結果
        if(datingClassTest(k)[0]<minCount):
                                             #誤差最小一定誤差數最少,誤差數最少,誤差率一定最小
            minCount=datingClassTest(k)[0]

    print("The minErroK's minErroCount= ",minCount)
    print("The minErroK's minErroRate=",minCount/100)
    for k in range(1,60):
        datingClassTest(k)
        if datingClassTest(k)[0]==minCount:
            print("The nice K= ",datingClassTest(k)[2])




#分類器
def datingClassTest(k):
    hoRatio=0.1
    datingDataMat,datingLabels=file2matrix('F:\python項目\KNN\datingTestSet2.txt')
    normMat,minVals,ranges=autoNorm(datingDataMat)
    m=normMat.shape[0]
    errorCount=0.0
    numTestVecs=int(m*hoRatio)
    for i in range(numTestVecs):
        classifierResult=classifyO(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],k) #//K取3時
        if (classifierResult!=datingLabels[i]):
            errorCount+=1.0
            x=errorCount/float(numTestVecs)
    return (errorCount,x,k) #稍作修改,方便Autodebug()函數調用,自動化測試,返回誤差個數和誤差率即可






Autodebug(1) #直接Run即可


#如果k值選擇較大的話,距離較遠的訓練樣本也能夠對實例預測結果產生影響。
# 這時候,模型相對比較魯棒,不會因爲個別噪聲點對最終預測結果產生影響。
# 但是缺點也十分明顯:算法的近鄰誤差會偏大,距離較遠的點(與預測實例不相似)也會同樣對預測結果產生影響,
# 使得預測結果產生較大偏差,此時模型容易發生欠擬合。
#因此,在實際工程實踐中,我們一般採用交叉驗證的方式選取k值。
# 通過以上分析可知,一般k值選得比較小,我們會在較小範圍內選取k值。
# 同時把測試集上準確率最高的那個確定爲最終的算法超參數k。

#雖然K值4與7都滿足最小誤差率,但k值4作爲超參數更好
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章