一、kNN算法基礎
# kNN:k-Nearest Neighboors
# 多用於解決分類問題
1)特點:
- 是機器學習中唯一一個不需要訓練過程的算法,可以別認為是沒有模型的算法,也可以認為訓練數據集就是模型本身;
- 思想極度簡單;
- 應用數學知識少(近乎為零);
- 效果少;
- 可以解釋機械學習算法使用過程中的很多細節問題
- 更完整的刻畫機械學習應用的流程;
2)思想:
- 根本思想:兩個樣本,如果它們的特征足夠相似,它們就有更高的概率屬於同一個類別;
- 問題:根據現有訓練數據集,判斷新的樣本屬於哪種類型;
- 方法/思路:
- 求新樣本點在樣本空間內與所有訓練樣本的歐拉距離;
- 對歐拉距離排序,找出最近的k個點;
- 對k個點分類統計,看哪種類型的點數量最多,此類型即為對新樣本的預測類型;
3)代碼實現過程:
- 示例代碼:
import numpy as np import matplotlib.pyplot as plt raw_data_x = [[3.3935, 2.3312], [3.1101, 1.7815], [1.3438, 3.3684], [3.5823, 4.6792], [2.2804, 2.8670], [7.4234, 4.6965], [5.7451, 3.5340], [9.1722, 2.5111], [7.7928, 3.4241], [7.9398, 0.7916]] raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] # 訓練集樣本的data x_train = np.array(raw_data_x) # 訓練集樣本的label y_train = np.array(raw_data_y) # 1)繪制訓練集樣本與新樣本的散點圖 # 根據樣本類型(0、1兩種類型),繪制所有樣本的各特征點 plt.scatter(x_train[y_train == 0, 0], x_train[y_train == 0, 1], color = 'g') plt.scatter(x_train[y_train == 1, 0], x_train[y_train == 1, 1], color = 'r') # 新樣本 x = np.array([8.0936, 3.3657]) # 將新樣本的特征點繪制在訓練集的樣本空間 plt.scatter(x[0], x[1], color = 'b') plt.show() # 2)在特征空間中,計算訓練集樣本中的所有點與新樣本的點的歐拉距離 from math import sqrt # math模塊下的sqrt函數:對數值開平方sqrt(number) distances = [] for x_train in x_train: d = sqrt(np.sum((x - x_train) ** 2)) distances.append(d) # 也可以用list的生成表達式實現: # distances = [sqrt(np.sum((x - x_train) ** 2)) for x_train in x_train] # 3)找出距離新樣本最近的k個點,並得到對新樣本的預測類型 nearest = np.argsort(distances) k = 6 # 找出距離最近的k個點的類型 topK_y = [y_train[i] for i in nearest[:k]] # 根據類別對k個點的數量進行統計 from collections import Counter votes = Counter(topK_y) # 獲取所需的預測類型:predict_y predict_y = votes.most_common(1)[0][0]
- 封裝好的Python代碼:
import numpy as np from math import sqrt from collections import Counter def kNN_classify(k, X_train, y_train, x): assert 1 <= k <= X_train.shape[0],"k must be valid" assert X_train.shape[0] == y_train.shape[0], \ "the size of X_train nust equal to the size of y_train" assert X-train.shape[1] == x.shape[0],\ "the feature number of x must be equal to X_train" distances = [sprt(np.sum((x_train - x) ** 2)) for x_train in X_train] nearest = np.argsort(distances) topK_y = [y_train[i] for i in nearest[:k]] vates = Counter(topK_y) return votes.most_common(1)[0][0]
# assert:表示聲明;此處對4個參數進行限定;
- 代碼中的其它Python知識:
-
math模塊下的sprt()方法:對數開平方;
from math import sqrt print(sprt(9)) # 3
- collections模塊下的Counter()方法:對列表中的數據進行分類統計,生產一個Counter對象;
from collections import Counter my_list = [0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3] print(Counter(my_list)) # 一個Counter對象:Counter({0: 2, 1: 3, 2: 4, 3: 5})
- Counter對象的most_common()方法:Counter.most_common(n),返回Counter對象中數量最多的n種數據,返回一個list,list的每個元素為一個tuple;
from collections import Counter my_list = [0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3] votes = Counter(my_list) print(votes.most_common(2)) # [(3, 5), (2, 4)]
二、總結
1)k近鄰算法的作用
1、解決分類問題,而且天然可以解決多分類問題;
2、也可以解決回歸問題,其中scikit-learn庫中封裝的KNeighborsRegressor,就是解決回歸問題;
2)缺點
- 缺點1:效率低下
- 原因:如果訓練集有m個樣本,n個特征,預測每一個新樣本,需要計算與m個樣本的距離,每計算一個距離,要使用n個時間復雜度,則計算m個樣本的距離,使用m * n個時間復雜度;
- 算法的時間復雜度:反映了程序執行時間隨輸入規模增長而增長的量級,在很大程度上能很好反映出算法的優劣與否。
- 算法的時間復雜度與空間復雜度,參考:算法的時間復雜度和空間復雜度
- 可以通過樹結構對k近鄰算法優化:KD-Tree、Ball-Tree,但即便進行優化,效率依然不高;
- 缺點2:高度數據相關
- 機器學習算法,就是通過喂給數據進行預測,理論上所有機器學習算法都是高度數據相關;
- k近鄰算法對outlier更加敏感:比如三近鄰算法,在特征空間中,如果在需要預測的樣本周邊,一旦有兩個樣本出現錯誤值,就足以使預測結果錯誤,哪怕在更高的范圍里,在特征空間中有大量正確的樣本;
- 缺點3:預測的結果不具有可解釋性
- 按k近鄰算法的邏輯:找到和預測樣本比較近的樣本,就得出預測樣本和其最近的這個樣本類型相同;
- 問題:為什么預測的樣本類型就是離它最近的樣本的類型?
- 很多情況下,只是拿到預測結果是不夠的,還需要對此結果有解釋性,進而通過解釋推廣使用,或者制作更多工具,或者以此為基礎發現新的理論/規則,來改進生產活動中的其它方面——這些是kNN算法做不到的;
- 缺點4:維數災難
- 維數災難:隨着維度的增加,“看似相近”的兩個點之間的距離越來越大;
- 例:[0, 0, 0, ...0]和[1, 1, 1,...1],按歐拉定理計算,元素個數越多,兩點距離越大;
- 方案:降維(PCA);
三、使用機器學習算法的流程
- 獲取原始數據——數據分割——數據歸一化——訓練模型——預測
- 獲取原始數據:一般可從scikit-learn庫中調用——# 調用數據集的操作流程 機器學習:scikit-learn中算法的調用、封裝並使用自己所寫的算法
- 數據分割:一般按2 :8進行分割——# 分割數據的代碼實現過程、通過scikit-learn庫分割數據的操作流程 機器學習:訓練數據集、測試數據集
- 數據歸一化:參見 機器學習:數據歸一化(Scaler)
- 訓練模型、模型預測: 機器學習:scikit-learn中算法的調用、封裝並使用自己所寫的算法