(一)kNN

哈哈哈豬在飛發表於2020-11-15

1. kNN概述

k近鄰法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一	
種基本分類與迴歸方法。它的工作原理是:存在一個樣本資料集合,也稱作	為訓練樣本集,並且樣本集中每個資料都存在標籤,即我們知道樣本
集中每一個資料與所屬分類的對應關係。輸入沒有標籤的新資料後,將新的資料的每個特徵與樣本集中資料對應的特徵進行比較,然後演算法提取樣
本最相似資料(最近鄰)的分類標籤。一般來說,我們只選擇樣本資料集中
前k個最相似的資料,這就是k-近鄰演算法中k的出處,通常k是不大於20的
整數。最後,選擇k個最相似資料中出現次數最多的分類,作為新資料的
分類。

在這裡插入圖片描述

  • 看到這裡,有人可能會問:“分類器何種情況下會出錯?”或者“答案是否總是正確的?”答案是否定的,分類器並不會得到百分百正確的結果,我們可以使用多種方法檢測分類器的正確率。
  • 此外分類器的效能也會受到多種因素的影響,如分類器設定和資料集等。不同的演算法在不同資料集上的表現可能完全不同。
  • 為了測試分類器的效果,我們可以使用已知答案的資料,當然答案不能告訴分類器,檢驗分類器給出的結果是否符合預期結果。通過大量的測試資料,我們可以得到分類器的錯誤率-分類器給出錯誤結果的次數除以測試執行的總數。錯誤率是常用的評估方法,主要用於評估分類器在某個資料集上的執行效果。完美分類器的錯誤率為0,最差分類器的錯誤率是1.0。
  • 同時,我們也不難發現,k-近鄰演算法沒有進行資料的訓練,直接使用未知的資料與已知的資料進行比較,得到結果。因此,可以說k-鄰近演算法不具有顯式的學習過程

1.1 kNN演算法的優點

  • 簡單好用,容易理解,精度高,理論成熟,既可以用來做分類也可以用來做迴歸
  • 可用於數值型資料和離散型資料
  • 訓練時間複雜度為 O(n); 無資料輸入假定
  • 對異常值不敏感

1.2 kNN 演算法的缺點

  • 計算複雜度高;空間複雜度高
  • 樣本不平衡問題(有些類別的樣本數量很多,而其他樣本的數量很少);
  • 一般數值很大的時候不適用該演算法,計算量太大。但是單個樣本又不能太少,否則容易發生誤分
  • 最大的缺點是無法給出資料的內在含義。

2. kNN程式碼實現

2.1 kNN

2.2 演算法實戰——海倫約會

收集資料
準備資料(資料解析、資料歸一化)
分析資料()
測試演算法
使用演算法

2.2.1 收集資料集

在這裡插入圖片描述

2.2.2 準備資料:資料解析

import numpy as np

def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    returnMat = np.zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()     # 擷取掉所有的回車字元
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        if listFromLine[-1] == 'didntLike':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'largeDoses':
            classLabelVector.append(3)
        index += 1
    return returnMat, classLabelVector

if __name__ == '__main__':
    filename = 'datingTestSet.txt'
    datingDataMat, datingLabels = file2matrix(filename)
    print(datingDataMat)
    print('--------------------------------------')
    print(datingLabels)
 

在這裡插入圖片描述

2.2.3 分析資料:資料視覺化

import numpy as np
import operator
#from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt

def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    returnMat = np.zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()     # 擷取掉所有的回車字元
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        if listFromLine[-1] == 'didntLike':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'largeDoses':
            classLabelVector.append(3)
        index += 1
    return returnMat, classLabelVector

def showdatas(datingDataMat, datingLabels):
    fig, axs = plt.subplots(nrows=2, ncols=2, sharex=False, sharey=False, figsize=(13,8))
    
    numberOfLabels = len(datingLabels)
    LabelsColors = []
    for i in datingLabels:
        if i == 1:
            LabelsColors.append('black')
        if i == 2:
            LabelsColors.append('orange')
        if i == 3:
            LabelsColors.append('red')
    
    axs[0][0].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=LabelsColors, s=15, alpha=0.5)
    axs0_title_text = axs[0][0].set_title(u'每年獲得的飛行常客里程數與玩視訊遊戲所消耗時間佔比',fontProperties='SimHei', fontsize=14)
    axs0_xlabel_text = axs[0][0].set_xlabel(u'每年獲得的飛行常客里程數',fontProperties='SimHei', fontsize=14)
    axs0_ylabel_text = axs[0][0].set_ylabel(u'玩視訊遊戲所消耗時間佔',fontProperties='SimHei', fontsize=14)
    plt.setp(axs0_title_text, size=9, weight='bold', color='red') 
    plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black') 
    plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')

    #畫出散點圖,以datingDataMat矩陣的第一(飛行常客例程)、第三列(冰激凌)資料畫散點資料,散點大小為15,透明度為0.5
    axs[0][1].scatter(x=datingDataMat[:,0], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5)
    #設定標題,x軸label,y軸label
    axs1_title_text = axs[0][1].set_title(u'每年獲得的飛行常客里程數與每週消費的冰激淋公升數', fontProperties='SimHei', fontsize=14)
    axs1_xlabel_text = axs[0][1].set_xlabel(u'每年獲得的飛行常客里程數',fontProperties='SimHei', fontsize=14)
    axs1_ylabel_text = axs[0][1].set_ylabel(u'每週消費的冰激淋公升數',fontProperties='SimHei', fontsize=14)
    plt.setp(axs1_title_text, size=9, weight='bold', color='red') 
    plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black') 
    plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black')

    #畫出散點圖,以datingDataMat矩陣的第二(玩遊戲)、第三列(冰激凌)資料畫散點資料,散點大小為15,透明度為0.5
    axs[1][0].scatter(x=datingDataMat[:,1], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5)
    #設定標題,x軸label,y軸label
    axs2_title_text = axs[1][0].set_title(u'玩視訊遊戲所消耗時間佔比與每週消費的冰激淋公升數', fontProperties='SimHei', fontsize=14)
    axs2_xlabel_text = axs[1][0].set_xlabel(u'玩視訊遊戲所消耗時間佔比',fontProperties='SimHei', fontsize=14)
    axs2_ylabel_text = axs[1][0].set_ylabel(u'每週消費的冰激淋公升數',fontProperties='SimHei', fontsize=14)
    plt.setp(axs2_title_text, size=9, weight='bold', color='red') 
    plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black') 
    plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black')

    #設定圖例
    didntLike = mlines.Line2D([], [], color='black', marker='.', markersize=6, label='didntLike')
    smallDoses = mlines.Line2D([], [], color='orange', marker='.', markersize=6, label='smallDoses')
    largeDoses = mlines.Line2D([], [], color='red', marker='.', markersize=6, label='largeDoses')
    
    #新增圖例
    axs[0][0].legend(handles=[didntLike, smallDoses, largeDoses])
    axs[0][1].legend(handles=[didntLike, smallDoses, largeDoses])
    axs[1][0].legend(handles=[didntLike, smallDoses, largeDoses])
    
    #顯示圖片
    plt.show()
    
if __name__ == '__main__':
    filename = 'datingTestSet.txt'
    datingDataMat, datingLabels = file2matrix(filename)
    print(datingDataMat)
    print('--------------------------------------')
    print(datingLabels)
    
    showdatas(datingDataMat, datingLabels)

在這裡插入圖片描述

2.2.4 準備資料:資料歸一化

在這裡插入圖片描述

import numpy as np
import operator
#from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt

def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    returnMat = np.zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()     # 擷取掉所有的回車字元
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        if listFromLine[-1] == 'didntLike':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'largeDoses':
            classLabelVector.append(3)
        index += 1
    return returnMat, classLabelVector


def autoNorm(dataSet):
    # dataSet --- 1000*3
    # minVals --- 1*3
    # maxVals --- 1*3
    minVals = dataSet.min(0)  # 0 表示從當前列中選取最小值
    maxVals = dataSet.max(0)   
    range = maxVals - minVals
    
    normDataSet = np.zeros(np.shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    normDataSet = normDataSet / np.tile(range, (m, 1))
    return normDataSet, range, minVals
    
    
if __name__ == '__main__':
    filename = 'datingTestSet.txt'
    datingDataMat, datingLabels = file2matrix(filename)
   
    print('--------------------------------------')
    normDataSet, ranges, minVals = autoNorm(datingDataMat)
    print(normDataSet)
    print('--------------------------------------')
    print(ranges)
    print('--------------------------------------')
    print(minVals)
    
    #showdatas(datingDataMat, datingLabels)
   

在這裡插入圖片描述

2.2.5 測試演算法:驗證分類器

import numpy as np
import operator
#from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt

# kNN演算法,分類器
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = np.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.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]


# 解析資料
def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    returnMat = np.zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()     # 擷取掉所有的回車字元
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        if listFromLine[-1] == 'didntLike':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'largeDoses':
            classLabelVector.append(3)
        index += 1
    return returnMat, classLabelVector


# 歸一化資料
def autoNorm(dataSet):
    # dataSet --- 1000*3
    # minVals --- 1*3
    # maxVals --- 1*3
    minVals = dataSet.min(0)  # 0 表示從當前列中選取最小值
    maxVals = dataSet.max(0)   
    range = maxVals - minVals
    
    normDataSet = np.zeros(np.shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    normDataSet = normDataSet / np.tile(range, (m, 1))
    return normDataSet, range, minVals
    


def datingClassTest():
    filename = 'datingTestSet.txt'
    datingDataMat, datingLabels = file2matrix(filename)
    
    hoRatio = 0.10
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]
    numTestVecs = int(m * hoRatio)
    errorCount = 0.0
    
    for i in range(numTestVecs):
         #前numTestVecs個資料作為測試集,後m-numTestVecs個資料作為訓練集
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 4)
        print('分類結果:%d\t真實類別:%d' % (classifierResult, datingLabels[i]))
        if classifierResult != datingLabels[i]:
            errorCount += 1.0
    print('錯誤率:%f%%' % (errorCount/float(numTestVecs)*100))
   
    
if __name__ == '__main__':
    datingClassTest()

在這裡插入圖片描述

2.2.6 使用演算法:構建完整可用系統

我們可以給海倫一個小段程式,通過該程式海倫會在約會網站上找到某個人並輸入他的資訊。程式會給出她對男方喜歡程度的預測值。

import numpy as np
import operator
#from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt

# kNN演算法,分類器
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = np.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.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]


# 解析資料
def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    returnMat = np.zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()     # 擷取掉所有的回車字元
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        if listFromLine[-1] == 'didntLike':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'largeDoses':
            classLabelVector.append(3)
        index += 1
    return returnMat, classLabelVector


# 歸一化資料
def autoNorm(dataSet):
    # dataSet --- 1000*3
    # minVals --- 1*3
    # maxVals --- 1*3
    minVals = dataSet.min(0)  # 0 表示從當前列中選取最小值
    maxVals = dataSet.max(0)   
    range = maxVals - minVals
    
    normDataSet = np.zeros(np.shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    normDataSet = normDataSet / np.tile(range, (m, 1))
    return normDataSet, range, minVals
    

# 輸入一個人的三維特徵,進行分類輸出
def classifyPerson():
    resultList = ['討厭', '有些喜歡', '非常喜歡']
    
    #使用者輸入三維特徵
    percentTats = float(input('玩視訊遊戲所耗時間百分比:'))       
    ffMiles = float(input("每年獲得的飛行常客里程數:"))
    iceCream = float(input("每週消費的冰激淋公升數:"))
    
    filename = 'datingTestSet.txt'
    datingDataMat, datingLabels = file2matrix(filename)
    normMat, ranges, minVals = autoNorm(datingDataMat)
    
    #生成Numpy陣列,測試集
    inArr = np.array([percentTats, ffMiles, iceCream])
    norminArr = (inArr - minVals) / ranges

    #返回分類結果
    classifierResult = classify0(norminArr, normMat, datingLabels, 3)
    print('你可能%s這個人' % (resultList[classifierResult-1]))   
       
    
if __name__ == '__main__':
    classifyPerson()

在這裡插入圖片描述

2.3 演算法實戰——sklearn手寫數字識別

相關文章