KNN算法是機器學習中最好理解的算法之一长搀,屬于惰性學習算法的典例轿钠。惰性指模型僅通過對訓練數據集的記憶功能進行預測硫朦,而不產生判別函數瞒斩。
引申:參數化模型和非參數化模型
機器學習算法可以劃分為參數化模型和非參數化模型兩大類胸囱。使用參數化模型時抛丽,需要通過訓練估計參數,得到一個模式。常見的參數化模型諸如感知器凤跑、邏輯斯蒂回歸仔引、SVM等褐奥。
非參數化模型無法通過一組固定的參數對樣本進行表征咖耘,參數的數量會隨著訓練集樣本數量的增加而增加。常見的非參數化模型有決策樹夫否、Kernal SVM等叫胁。
KNN屬于非參數化模型的一個子類森篷,可以被描述為基于實例的學習钓辆。這類模型的特點是對訓練數據進行記憶蛀恩,KNN所屬的惰性學習是基于實例學習的一個特例。
sklearn中的KNN實現
KNN算法本身很簡單席揽,歸納為如下幾步:
①選擇近鄰數量k和距離度量的方法
②找到待分類樣本的k個最近鄰
③根據最近鄰類標進行多數投票
sklearn中已經很好的實現了KNN算法分類器,位于sklearn.neighbors包下的KNeighborsClassifier類中寸谜。給出最基本的實現属桦。
from sklearn.model_selection import train_test_split
import sklearn.datasets as dataset
from sklearn.neighbors import KNeighborsClassifier
'''加載數據'''
data = dataset.load_digits()
X = data.data
y = data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=666)
knn_clf = KNeighborsClassifier(n_neighbors=5)
knn_clf.fit(X_train, y_train)
score = knn_clf.score(X_test, y_test)
print('when k chose 5, fit score is : %s' % score)
選用sklearn.datasets的digits數據集,分類器的k近鄰數量選用5系谐,這也是默認值,算是經驗上分類效果最佳的一個k值纪他。獲得如下輸出:
when k chose 5, fit score is : 0.9866666666666667
在k選5的情況下,分類器的準確度為98%止喷。
KNN算法的超參數
上面的實現屬于最簡單的實現,KNN算法因為不生成一個固定參數的模型乾巧,因此预愤,在訓練過程中的調參的要求非常高。
(一)k值的選擇
k值的選擇是第一項植康,一般的根據經驗k=5是最能得到最佳效果的點,但在實際開發(fā)過程中需要進行驗證供璧。
'''最好的k值'''
best_k = -1
best_score = 0.0
for k in range(1, 11):
knn_clf = KNeighborsClassifier(n_neighbors=k)
knn_clf.fit(X_train, y_train)
score = knn_clf.score(X_test, y_test)
if score > best_score:
best_score = score
best_k = k
print("best k is : %s, best_score is : %s\n" % (best_k, best_score))
在之前代碼中增加如下代碼,使用best_k冻记、best_score記錄最佳的k和分類效果,k從1到10中進行選擇演顾,使用for循環(huán)依次測試。獲得結果:
best k is : 5, best_score is : 0.9866666666666667
似乎和剛才是一樣的钠至。強調一點胎源,如果在1到10中求得最佳k值為10,那么有必要對10以上的值選擇區(qū)間再進行測試涕蚤,因為可能含有效果更好的k值。
(二)考慮距離權重
之前的分類中赞季,僅僅通過找到待分類樣本最近的k個樣本進行多數投票,但可能存在如下情況
如果按照投票的方式次绘,應該分為藍色類別,但從距離上看邮偎,樣本距離紅色類別更近,劃為紅色似乎更加合理义黎,這里就需要引入距離權重的概念。
在KNeighborsClassifier中有一個參數weight泻云,不指定該參數的情況下默認為uniform也就是多數投票艇拍,也可以指定weight為distance宠纯,即可采用距離權重的方式進行分類。
'''考慮距離權重'''
best_method = ''
best_k = -1
best_score = 0.0
for method in ['uniform', 'distance']:
for k in range(1, 11):
knn_clf = KNeighborsClassifier(n_neighbors=k, weights=method)
knn_clf.fit(X_train, y_train)
score = knn_clf.score(X_test, y_test)
if score > best_score:
best_score = score
best_k = k
best_method = method
print('best method is : %s, best k is : %s, best score is : %s\n' % (best_method, best_k, best_score))
增加使用best_method對最佳分類采用的權重方法進行記錄快集。得到輸出:
best method is : uniform, best k is : 5, best score is : 0.9866666666666667
似乎又沒變廉白。
(三)距離系數p
分類時候使用的距離是什么距離?距離的種類有很多猴蹂,最常見的有歐氏距離ρ(A,B) =√ [ ∑( a[i] - b[i] )^2 ] (i = 1晕讲,2,…弄息,n),此外還有曼哈頓距離ρ(A摹量,B) =[ ∑( a[i] - b[i] ) ] (i = 1馒胆,2,…祝迂,n)
如圖給出的綠色的直線就是歐式距離,其他的線雖然走法不同但距離是一樣的当凡,都是曼哈頓距離。仔細觀察下兩種距離的公式沿量,可以進一步合并為ρ(A冤荆,B) =[ ∑( a[i] - b[i] )^p ]^(1/p) (i = 1,2钓简,…汹想,n)
當p為1時芥被,代入發(fā)現是曼哈頓距離坐榆,p=2時則是歐氏距離。在sklearn的KNN分類器中匹中,也有p參數,默認值為2即采用歐式距離顶捷,這個公式被稱為閔可夫斯基距離屎篱。訓練過程中,還可以實驗不同p值的分類效果
'''距離p值'''
best_score = 0.0
best_k = -1
best_p = -1
for k in range(1, 11):
for p in range(1, 6):
knn_clf = KNeighborsClassifier(n_neighbors=k, weight='distance', p=p)
knn_clf.fit(X_train, y_train)
score = knn_clf.score(X_test, y_test)
if score > best_score:
best_score = score
best_k = k
best_p = p
print('best k is : %s, best p is : %s, best score is : %s\n' % (best_k, best_p, best_score))
得到結果
best k is : 5, best p is : 2, best score is : 0.9866666666666667
好像歐式距離的效果最好交播。
網格搜索與KNN超參數
上面的部分中,我們不斷通過for循環(huán)和定義變量的方式記錄最佳分類效果的相應的超參數缺厉,sklearn中提供了更簡單的實現,位于sklearn.model_selection包下的GridSearchCV類就是對網格搜索的封裝提针,還是剛剛的尋找最佳分類的參數,使用GridSearchCV的方式就變?yōu)椋?/p>
from sklearn.model_selection import train_test_split
import sklearn.datasets as dataset
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
'''加載數據'''
data = dataset.load_digits()
X = data.data
y = data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=666)
'''網格搜索獲取最佳參數'''
grid_params = [
{
'weights' : ['uniform'],
'n_neighbors' : [i for i in range(1, 11)]
},{
'weights' : ['distance'],
'n_neighbors' : [i for i in range(1, 11)],
'p' : [p for p in range(1, 6)]
}
]
通過對參數進行封裝曹傀,裝入到grid_params中,作為參數傳入到網格當中
'''創(chuàng)建一個KNN分類器皆愉,作為網格搜索使用的分類器參數'''
knn_clf = KNeighborsClassifier()
grid_search = GridSearchCV(knn_clf, param_grid=grid_params, n_jobs=-1, verbose=2)
grid_search.fit(X_train, y_train)
創(chuàng)建一個KNN分類器,作為網格搜索使用的分類器參數炭剪,再指定之前創(chuàng)建的grid_params,GridSearchCV就可以自動的尋找最佳分類方式翔脱。n_jobs是進行搜索時的任務數,可以指定為電腦的核數错妖,-1就是使用全部的核進行搜索,verbose是在搜索過程中打印相關信息暂氯,值越高信息越詳細,一般2比較常用痴施。
Fitting 3 folds for each of 60 candidates, totalling 180 fits
[CV] n_neighbors=1, weights=uniform ..................................
[CV] n_neighbors=1, weights=uniform ..................................
[CV] n_neighbors=1, weights=uniform ..................................
[CV] n_neighbors=2, weights=uniform ..................................
[CV] ................... n_neighbors=1, weights=uniform, total= 0.0s
[CV] ................... n_neighbors=1, weights=uniform, total= 0.1s
[CV] n_neighbors=2, weights=uniform ..................................
[CV] ................... n_neighbors=1, weights=uniform, total= 0.1s
......
verbose的作用就是在搜索過程中打印如下信息,注意第一行60 candidates辣吃,回頭看grid_params中weight選用uniform時,需要對k從1到10進行測試厘惦,而weights選用distance時哩簿,對k從1到10和p從1到5進行測試,一共需要進行110 + 510 = 60項測試节榜,對應了candidates的值。
打印得到的最佳分類模式
print('best estimator is :')
print(grid_search.best_estimator_)
print('best score is %s:' % grid_search.best_score_)
# knn_clf = grid_search.best_estimator_
# score = knn_clf.score(X_test, y_test)
print("test by KNN Classifier's score() function, the score is : %s" % score)
輸出
best estimator is :
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=5, p=3,
weights='distance')
best score is 0.9866369710467706:
test by KNN Classifier's score() function, the score is : 0.9822222222222222
可以看到best_estimator_其實是一個KNeighborsClassifier對象,這里得到的是使用距離權重浓若,p值為3和n_neighbors為5的分類器,通過網格搜索測試的best_score_為0.9866369710467706挪钓,不如之前自己實現的0.9866666666666667,原因是使用GridSearchCV的打分機制和分類器的機制不一樣倚评,更為復雜馏予,因此分數也就不同天梧。之后使用X_test和y_test測試待預測樣本的準確度為0.9822222222222222呢岗。
獲得的最佳KNeighborsClassifier中還有個屬性是metric,目前對應的值是minkowski就是之前講過的閔可夫斯基距離悉尾。此外還有些距離,作為擴展給出挫酿,可以參考sklearn手冊sklearn.neighbors包下的DistanceMetric构眯。
KNN解回歸問題
在sklearn.neighbors包下還有KNeighborsRegressor用KNN算法解決回歸問題,其思路基本上和解分類問題一樣早龟,但回歸問題是需要求出一個具體的值惫霸,求值可能采用最近k個點的值的均值、或者考慮上距離的權重等方式拄衰。API和KNeighborsClassifier非常相似它褪,這里不做講解。
KNN算法的缺點
最大的缺點就是效率低翘悉,假如一個數據集有m個樣本n中特征,則預測一個樣本的時間復雜度為O(mn)居触,即需要和m個樣本求距離并挑選前k個妖混,每個特征維度都需要計算距離,故需要O(mn)轮洋≈剖校可以使用樹結構優(yōu)化,如KD-Tree弊予、Ball-Tree等祥楣。
缺點二:高度數據相關
缺點三:預測不具有可解釋性
缺點四:維數災難
隨著維數增加,看似很近的兩個點距離越來越大汉柒。
維數 坐標1 坐標2 距離
1 0 1 1
2 (0,0) (1, 1) 1.414
3 (0,0,0) (1,1,1) 1.73
...
維數災難可以用降維的方式進行優(yōu)化。