機器學習之kNN演算法(純python實現)

swensun發表於2018-02-28

前面文章分別簡單介紹了線性迴歸,邏輯迴歸,貝葉斯分類,並且用python簡單實現。這篇文章介紹更簡單的 knn, k-近鄰演算法(kNN,k-NearestNeighbor)。 k-近鄰演算法(kNN,k-NearestNeighbor),是最簡單的機器學習分類演算法之一,其核心思想在於用距離目標最近的k個樣本資料的分類來代表目標的分類(這k個樣本資料和目標資料最為相似)。

原理

kNN演算法的核心思想是用距離最近(多種衡量距離的方式)的k個樣本資料來代表目標資料的分類。

具體講,存在訓練樣本集, 每個樣本都包含資料特徵和所屬分類值。 輸入新的資料,將該資料和訓練樣本集匯中每一個樣本比較,找到距離最近的k個,在k個資料中,出現次數做多的那個分類,即可作為新資料的分類。

image.png
如上圖: 需要判斷綠色是什麼形狀。當k等於3時,屬於三角。當k等於5是,屬於方形。 因此該方法具有一下特點:

  • 監督學習:訓練樣本集中含有分類資訊
  • 演算法簡單, 易於理解實現
  • 結果收到k值的影響,k一般不超過20.
  • 計算量大,需要計算與樣本集中每個樣本的距離。
  • 訓練樣本集不平衡導致結果不準確問題

接下來用oython 做個簡單實現, 並且嘗試用於約會網站配對。

python簡單實現

def classify(inX, dataSet, labels, k):
    """
    定義knn演算法分類器函式
    :param inX: 測試資料
    :param dataSet: 訓練資料
    :param labels: 分類類別
    :param k: k值
    :return: 所屬分類
    """

    dataSetSize = dataSet.shape[0]  #shape(m, n)m列n個特徵
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5  #歐式距離
    sortedDistIndicies = distances.argsort()  #排序並返回index

    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 #default 0

    sortedClassCount = sorted(classCount.items(), key=lambda d:d[1], reverse=True)
    return sortedClassCount[0][0]
複製程式碼

演算法的步驟上面有詳細的介紹,上面的計算是矩陣運算,下面一個函式是代數運算,做個比較理解。

def classify_two(inX, dataSet, labels, k):
    m, n = dataSet.shape   # shape(m, n)m列n個特徵
    # 計算測試資料到每個點的歐式距離
    distances = []
    for i in range(m):
        sum = 0
        for j in range(n):
            sum += (inX[j] - dataSet[i][j]) ** 2
        distances.append(sum ** 0.5)

    sortDist = sorted(distances)

    # k 個最近的值所屬的類別
    classCount = {}
    for i in range(k):
        voteLabel = labels[ distances.index(sortDist[i])]
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 # 0:map default
    sortedClass = sorted(classCount.items(), key=lambda d:d[1], reverse=True)
    return sortedClass[0][0]
複製程式碼

有了上面的分類器,下面進行最簡單的實驗來預測一下:

def createDataSet():
    group = np.array([[1, 1.1], [1, 1], [0, 0], [0, 0.1]])
    labels = ['A', 'A', 'B', 'B']
    return group, labels
複製程式碼

上面是一個簡單的訓練樣本集。

if __name__ == '__main__':
    dataSet, labels = createDataSet()
    r = classify_two([0, 0.2], dataSet, labels, 3)
    print(r)
複製程式碼

執行上述函式:可以看到輸出B, [0 ,0.2]應該歸入b類。

上面就是一個最簡單的kNN分類器,下面有個例子。

kNN用於判斷婚戀網站中人的受歡迎程度

訓練樣本集中部分資料如下:

40920	8.326976	0.953952	3
14488	7.153469	1.673904	2
26052	1.441871	0.805124	1
75136	13.147394	0.428964	1
38344	1.669788	0.134296	1
複製程式碼

第一列表示每年獲得的飛行常客里程數, 第二列表示玩視訊遊戲所耗時間百分比, 第三類表示每週消費的冰淇淋公升數。第四列表示分類結果,1, 2, 3 分別是 不喜歡,魅力一般,極具魅力。

  1. 將資料轉換成numpy。
# 文字轉換成numpy
def file2matrix(filepath="datingSet.csv"):
    dataSet = np.loadtxt(filepath)
    returnMat = dataSet[:, 0:-1]
    classlabelVector = dataSet[:, -1:]
    return returnMat, classlabelVector
複製程式碼
  1. 首先對資料有個感知,知道是哪些特徵影響分類,進行視覺化資料分析。
# 2, 3列資料進行分析
def show_2_3_fig():
    data, cls = file2matrix()
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(data[:, 1], data[: ,2], c=cls)
    plt.xlabel("playing game")
    plt.ylabel("Icm Cream")
    plt.show()
複製程式碼

image.png

如上圖可以看到並無明顯的分類。
image.png

image.png

可以看到不同的人根據特徵有明顯的區分。因此可以使用kNN演算法來進行分類和預測。

  1. 由於後面要用到距離比較,因此資料之前的影響較大, 比如飛機里程和冰淇淋數目之間的差距太大。因此需要對資料進行歸一化處理
# 資料歸一化
def autoNorm(dataSet):
    minVal = dataSet.min(0)
    maxVal = dataSet.max(0)
    ranges = maxVal - minVal

    normDataSet = np.zeros(dataSet.shape)
    m, n = dataSet.shape  # 行, 特徵
    normDataSet = dataSet - minVal
    normDataSet = normDataSet / ranges
    return normDataSet, ranges, minVal
複製程式碼
  1. 衡量演算法的準確性 knn演算法可以用正確率或者錯誤率來衡量。錯誤率為0,表示分類很好。 因此可以將訓練樣本中的10%用於測試,90%用於訓練。
# 定義測試演算法的函式
def datingClassTest(h=0.1):
    hoRatio = h
    datingDataMat, datingLabels = file2matrix()
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m, n = normMat.shape
    numTestVecs = int(m * hoRatio)  #測試資料行數
    errorCount = 0  # 錯誤分類數


    # 用前10%的資料做測試
    for i in range(numTestVecs):
        classifierResult = classify(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m],  3)
        # print('the classifier came back with: %d,the real answer is: %d' % (int(classifierResult), int(datingLabels[i])))
        if classifierResult != datingLabels[i]:
            errorCount += 1
    print("the total error rate is: %f" % (errorCount / float(numTestVecs)))
複製程式碼

調整不同的測試比例,對比結果。

  1. 使用knn進行預測。 有了訓練樣本和分類器,對新資料可以進行預測。模擬資料並進行預測如下:
# 簡單進行預測
def classifypersion():
    resultList = ["none", 'not at all','in small doses','in large doses']
    # 模擬資料
    ffmiles = 15360
    playing_game = 8.545204
    ice_name = 1.340429

    datingDataMat, datingLabels = file2matrix()
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = np.array([ffmiles, playing_game, ice_name])
    # 預測資料歸一化
    inArr = (inArr - minVals) / ranges
    classifierResult = classify(inArr, normMat, datingLabels, 3)
    print(resultList[int(classifierResult)])
複製程式碼

可以看到基本的得到所屬的分類。

完成程式碼和資料請參考:
github:kNN

總結

  • kNN
  • 監督學習
  • 資料視覺化
  • 資料歸一化,不影響計算

相關文章