K-近鄰算法概述
簡單地說,**k-近鄰算法採用測量不同特徵值之間的距離的方法進行分類。他的優點是精度高、對異常值不敏感、無數據輸入設定。缺點是計算複雜度高、空間複雜度高。**使用數據範圍爲:數值型和標稱型。
k-近鄰算法(kNN)的工作原理是:存在一個樣本數據集合,也稱作訓練樣本集,並且樣本集合中每個數據都存在標籤,即我們知道樣本集中每一數據與所屬分類的對應關係。輸入沒有標籤的新數據後,**將新數據的每個特徵與樣本集中數據對應的特徵進行比較,然後算法提取樣本集中特徵最相似(最近鄰)的分類標籤。一般來說,我們只選擇樣本數據集中前k個最相似數據的數據,**這就是k-近鄰算法中k的出處,**通常,k是不大於20的整數。**最後,選擇k個最相似的數據中出現的次數最多的分類,作爲新數據的分類。
k-近鄰算法的一般流程
- 收集數據: 可以使用任何方法
- 準備數據:距離計算所需要的數值,最好是結構化的數據格式
- 分析數據:可以使用任何方法
- 訓練算法:此步驟不適合k-近鄰算法
- 測試算法:計算錯誤率
- 使用算法:首先需要輸入樣本數據和結構化的輸出結果。然後運行k-近鄰算法判定輸入數據分別屬於哪個分類,最後應用對計算出的分類執行後續的處理。
k-近鄰算法的實施
對未知類別屬性的數據集中的每個點依次執行以下操作:
- 計算已知類別數據集中的點中的每個點與當前點之間的距離
- 按照距離遞增次序排序
- 選取與當前距離最小的k個點
- 確定前k個點所在類別的出現頻率
- 返回前k個點出現頻率最高的類別作爲當前點的預測分類
點與點之間的距離的算法有很多,這裏使用了歐氏距離公式:
例如,點(0,0)與(1,2)之間的距離計算爲:
數據的歸一化
對於某些數據,其特徵值之間的數值差異比較大,而差值最大的屬性對結果影響也很大。這會大大提高分類模型的錯誤率。**因此將該類數據歸一化是一個重要的操作。**下面的公式可以將任取值範圍的特徵值轉換爲0到1之間的值:。其中,min和max分別是數據集中的最小特徵值和最大特徵值。
如何測試分類器
分類器並不會得到百分百正確的結果,我們可以使用多種方法檢測分類器的正確率。此外分類器的性能也會受到多種因素的影響,如分類器設置和數據集等。不同的算法在不同數據集上的表現可能完全不同。
爲了測試分類器的效果,我們可以使用已知答案的數據,檢驗分類器給出的結果是否符合預期結果。通過大量的測試數據,我們可以得到分類器的錯誤率----分類器給出錯誤結果的次數除以測試執行的總數。錯誤率是常用的評估方法,主要用於評估分類器在某個數據集上的執行效果。完美分類器的錯誤率爲0,最差分類器的錯誤率是1.0。
示例: 使用k-近鄰算法改進約會網站的配對效果
數據集見:datingTestSet.txt(提取碼:80ms)。數據一共分四列,前三列爲三種特徵,第四列爲三種人物類型。我們將通過前三種特徵值,生成一個kNN分類器,來預測不同人對於你的吸引力。
飛行里程數/年 | 視頻遊戲時間百分比 | 每週消費冰激凌公升數 | 吸引力 |
---|---|---|---|
40920 | 8.326976 | 0.953952 | largeDoses |
14488 | 7.153469 | 1.673904 | smallDoses |
26052 | 1.441871 | 0.805124 | didntLike |
約會網站K-近鄰分類器生成步驟:
- 收集數據:提供文本文件
- 準備數據:使用python解析文本文件
- 測試算法:使用部分數據作爲測試樣本。計算分類器的錯誤率
- 使用算法:產生簡單的命令行程序,然後可以輸入一些特徵數據以判斷對方是否爲自己喜歡的類型。
python3代碼實現
#!/usr/bin/env python3
# coding: utf-8
# Author:Shen Yi
# Date :2020/2/13 21:19
"""機器學習實戰 第二章 k-近鄰算法
k-近鄰算法的完整實現,包含了k-近鄰算法的距離計算,錯誤率統計,數據集矩陣標準化以及一個戀愛網站的demo.
"""
from collections import Counter
from numpy import *
def knn_classify(in_x, data_set, labels, k_num):
""" implementation of algorithm of knn
calculation of distance between prediction and training, then sorted and extract the most common labels in top k_num
closer data
:param in_x: the vector to predict
:param data_set: training data
:param labels: label of training data
:param k_num: number of k
:return: label of predict
"""
# calculation of distance
data_set_size = data_set.shape[0]
diff_mat = tile(in_x, (data_set_size, 1)) - data_set
distances = ((diff_mat ** 2).sum(axis=1)) ** 0.5
sort_distances_index = distances.argsort()
# extract the most common label
vote_labels = [labels[index] for index in sort_distances_index[0: k_num]]
most_label = Counter(vote_labels).most_common(1)[0][0]
return most_label
def auto_norm(data_set):
"""data normalization"""
min_vals = data_set.min(0)
max_vals = data_set.max(0)
ranges = max_vals - min_vals
m = data_set.shape[0]
norm_data_set = data_set - tile(min_vals, (m, 1))
norm_data_set = norm_data_set / tile(ranges, (m, 1))
return norm_data_set, ranges, min_vals
def dating_class_test(data_mat, labels, ho_ratio, k_num):
"""calculate the accuracy of the model"""
m = data_mat.shape[0]
error_count = 0
number_test_vecs = int(m * ho_ratio)
for i in range(number_test_vecs):
classifier_rslt = knn_classify(data_mat[i, :], data_mat[number_test_vecs: m, :], labels[number_test_vecs: m], k_num)
if classifier_rslt != labels[i]:
error_count += 1
print(f"the total error rate is: {error_count / number_test_vecs}")
return error_count / number_test_vecs
def demo_date():
"""demo: used knn algorithm model to predict the matching effect of dating websites"""
file_name = "data\\Ch02\\datingTestSet.txt"
lines = open(file_name).readlines()
lines_number = len(lines)
data_mat = zeros((lines_number, 3))
labels = []
index = 0
for line in lines:
line = line.strip().split('\t')
data_mat[index, :] = line[0: 3]
labels.append(line[-1])
index += 1
norm_data_set, ranges, min_vals = auto_norm(data_mat)
dating_class_test(norm_data_set, labels, 0.1, 3)
percent_tas = float(input("percentage of time spent playing video games: "))
ffmiles = float(input("frequent flier miles earned per year: "))
ice_cream = float(input("liters of ice cream consumed per year:"))
in_array = (array([ffmiles, percent_tas, ice_cream]) - min_vals) / ranges
classifier_result = knn_classify(in_array, norm_data_set, labels, 3)
print(f"you will probably like this person: {classifier_result}")
if __name__ == '__main__':
demo_date()
運行結果:
the total error rate is: 0.05
percentage of time spent playing video games: 10
frequent flier miles earned per year: 10000
liters of ice cream consumed per year:0.5
you will probably like this person: smallDoses
從結果可以看出,該分類器的錯誤率爲5%,在輸出一組特徵值後,得到了預期的分類標籤。