機器學習——K近鄰演算法

白二寶發表於2019-02-16

機器學習——K近鄰演算法

概述

k近鄰是一種基本分類與迴歸方法.

  • 輸入:特徵向量
  • 輸出:例項的類別(可取多類)
  • 核心思想:如果一個樣本在特徵空間中的k個最相鄰的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別,並具有這個類別上樣本的特性.
  • 優點:計算精度高、對異常值不敏感、無資料輸入假定
  • 缺點:計算複雜度高、空間複雜度高
  • 適用範圍:數值型和標稱型

演算法流程

  1. 收集資料
  2. 準備資料:距離計算所需要的數值,最好是結構化的資料格式
  3. 分析資料:可以適用任何方法
  4. 訓練演算法:此步驟不適用於KNN
  5. 測試演算法:計算錯誤率
  6. 使用演算法:首先需要輸入樣本資料和結構化的輸出結果,然後執行k-近鄰演算法判定輸入資料分別屬於哪個分類,最後應用對計算出的分類執行後續的處理

kNN演算法

構造資料集
import numpy as np

def create_data_set():
    """構造資料集"""
    group = np.array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
    label = [`A`, `A`, `B`, `B`]
    return group, label
生成資料集
group, label = create_data_set()
實現kNN演算法虛擬碼

對未知類別屬性的資料集種的每個點依次執行以下步驟:

1. 計算已知類別屬性的資料集中的每個點與當前點之間的距離

2. 按照距離遞增次序排序

3. 選取與當前點距離最小的k個點

4. 確定前k個點所在類別的出現頻率

5. 返回前k個點出現頻率最高的類別作為當前點的預測分類

import operator

def classify0(inX, data_set, label, k):
    """
    KNN演算法
    :param inX: 用於分類的輸入向量
    :param data_set: 訓練樣本集
    :param label: 訓練標籤向量
    :param k: 選取最近鄰居的數量
    :return: k個鄰居里頻率最高的分類
    """

    """距離計算"""
    # 獲得樣本量
    data_set_size = data_set.shape[0]
    # tile:在行方向重複inX,dataSetSize次,在列方向上重複inX,1次
    diff_mat = np.tile(inX, (data_set_size, 1)) - data_set
    # 離原點的距離,相見後平方
    sq_diff_mat = diff_mat ** 2
    # x軸和y軸差的平方和
    sq_distances = sq_diff_mat.sum(axis=1)
    # 然後開方
    distances = sq_distances ** 0.5
    # argsort函式返回的是陣列值從小到大的索引值
    sorted_distance_index = distances.argsort()
    class_count = {}
    """選擇距離最小的點"""
    for i in range(k):
        # 返回距離最近的第i個樣本所對應的標籤
        vote_label = label[sorted_distance_index[i]]
        # print(voteIlabel)
        # print(classCount.get(voteIlabel, 0))
        # 這裡的0是設定預設值為0,而代替None。而程式碼是給出現情況增加次數,出現一次+1
        class_count[vote_label] = class_count.get(vote_label, 0) + 1
        # print(classCount)
    """排序"""
    # 匯入運算子模組的itemgetter方法,按照第二個元素的次序對元組進行排序,此處的排序為逆序。
    sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
    # 返回頻率最大的Label
    return sorted_class_count[0][0]
新增演算法測試程式碼
classify0([0, 0], group, label, k=3)

約會網站示例

載入並解析資料
# 將文字記錄轉換為numpy的解析程式
def file2matrix(filename):
    fr = open(filename)
    # readlines把檔案所有內容讀取出來,組成一個列表,其中一行為一個元素
    array_of_lines = fr.readlines()
    number_of_lines = len(array_of_lines)
    # 返回一個用1000行每行3個0填充的陣列,形成特徵矩陣
    return_mat = np.zeros((number_of_lines, 3))
    class_label_vector = []
    for index, line in enumerate(array_of_lines):
        # 去除每行前後的空格
        line = line.strip()
        # 根據	把每行分隔成由四個元素組成的列表
        list_from_line = line.split(`	`)
        # 選取前3個元素,將它們按順序儲存到特徵矩陣中
        return_mat[index, :] = list_from_line[0: 3]
        # 將列表的最後一個元素儲存到class_label_vector中去,儲存的元素值為整型
        class_label_vector.append(int(list_from_line[-1]))
    return return_mat, class_label_vector
獲得解析資料
dating_data_mat, dating_labels = file2matrix(`datingTestSet2.txt`)
使用Matplotlib建立散點圖
import matplotlib.pyplot as plt

# 建立Figure例項
fig = plt.figure()
# 新增一個子圖,返回Axes例項
ax = fig.add_subplot(111)  # 選取最近鄰居的數量
# 生成散點圖,x軸使用dating_data_mat第二列資料,y軸使用dating_data_mat的第三列資料
# ax.scatter(x=dating_data_mat[:, 1], y=dating_data_mat[:, 2])
# 個性化標記散點圖,形狀(s)和顏色(c)
ax.scatter(x=dating_data_mat[:, 1], y=dating_data_mat[:, 2], s=15.0 * np.array(dating_labels), c=np.array(dating_labels))
plt.show()
歸一化特徵值

<html><center>newValue=(oldValue−min)/(max−min)</center></html>

def auto_num(data_set):
    """
    歸一化特徵值
    :param data_set: 資料集
    :return 歸一化後的資料集, 列的差值範圍, 列的最小值
    """
    # 列的最小值
    min_val = data_set.min()
    # 列的最大值
    max_val = data_set.max()
    # 列的差值範圍
    range_s = max_val - min_val
    # 構造返回矩陣
    norm_data_set = np.zeros(shape=np.shape(data_set))
    # m = data_set.shape[0]
    # oldValue - min
    norm_data_set = data_set - np.tile(min_val, (data_set.shape[0], 1))
    # (oldValue - min) / (max - min)
    norm_data_set = norm_data_set / np.tile(range_s, (data_set.shape[0], 1))
    return norm_data_set, range_s, min_val
歸一化測試
normalize_data_set, ranges, min_val = auto_num(dating_data_mat)
print(normalize_data_set)
測試演算法
def dating_class_test():
    # 選擇測試資料量
    ho_ratio = 0.10
    # 解析資料
    dating_data_mat, dating_labels = file2matrix(`datingTestSet2.txt`)
    # 歸一化資料
    norm_mat, range_s, min_val = auto_num(dating_data_mat)
    # 拆分10%資料作為測試資料
    m = norm_mat.shape[0]  # 總資料量
    num_test_vec = int(m * ho_ratio)  # 測試資料量
    # 錯誤樣本計數
    error_count = 0.0
    # 對測試資料進行分類,並對比檢驗結果正確率
    for i in range(num_test_vec):
        classifier_result = classify0(  # classifier_result : k個鄰居里頻率最高的分類
            norm_mat[i, :],  # 用於分類的輸入向量(測試資料, : 表示一行內所有元素)
            norm_mat[num_test_vec: m, :],  # 訓練樣本集(從測試的資料開始到總資料量結束)
            dating_labels[num_test_vec:m],  # 訓練標籤向量(從測試的資料開始到總資料量結束)
            3  # 選取最近鄰居的數量
        )
        print(`the classifier came back with: %d, the real answer is: %d` % (classifier_result, dating_labels[i]))
        if classifier_result != dating_labels[i]:
            error_count += 1.0
    print(`the total error rate is: %f` % (error_count / float(num_test_vec)))
執行測試
dating_class_test()
使用演算法
def classify_person():
    """
    根據輸入指標,通過分類器進行預測喜歡程度
    :return:
    """
    result_list = [`not at all`, `in small doses`, `in large doses`]
    percent_tats = float(input(`percentage of time spent playing vedio games?`))
    ff_miles = float(input(`frequent flier miles earned per year?`))
    ice_cream = float(input(`liters of ice cream consumed per year?`))
    dating_data, dating_labels = file2matrix(`datingTestSet2.txt`)
    normalize_matrix, ranges, min_val = auto_num(dating_data)
    # 將輸入指標,歸一化後代入分類器進行預測
    in_arr = np.array([ff_miles, percent_tats, ice_cream])
    print(in_arr, min_val, ranges, (in_arr-min_val)/ranges)
    print(ranges)
    classifier_result = classify0((in_arr-min_val)/ranges, normalize_matrix, dating_labels, 3)
    print("You will probably like this person: ", result_list[classifier_result - 1])
執行函式
classify_person()
輸出
percentage of time spent playing vedio games?20
frequent flier miles earned per year?299
liters of ice cream consumed per year?1
You will probably like this person:  in large doses
sklearn中實現
from sklearn import neighbors

def knn_classify_person():
    """
        根據輸入指標,通過分類器進行預測喜歡程度
    :return:
    """
    result_list = np.array([`not at all`, `in small doses`, `in large doses`])
    percent_tats = float(input(`percentage of time spent playing vedio games?`))
    ff_miles = float(input(`frequent flier miles earned per year?`))
    ice_cream = float(input(`liters of ice cream consumed per year?`))
    dating_data, dating_labels = file2matrix(`datingTestSet2.txt`)
    normalize_matrix, ranges, min_val = auto_num(dating_data)
    # 將輸入指標,歸一化後代入分類器進行預測
    in_arr = np.array([ff_miles, percent_tats, ice_cream])
    # 宣告k為3的knn演算法,n_neighbors即是鄰居數量,預設值為5
    knn = neighbors.KNeighborsClassifier(n_neighbors=3)
    # 訓練演算法
    knn.fit(normalize_matrix, dating_labels)
    # 預測
    classifier_result = knn.predict([(in_arr - min_val) / ranges])
    print("You will probably like this person: ", result_list[classifier_result - 1][0])


# 執行函式
knn_classify_person()

相關文章