KNN算法基礎

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)化。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末兽间,一起剝皮案震驚了整個濱河市嘀略,隨后出現的幾起案子帜羊,更是在濱河造成了極大的恐慌讼育,老刑警劉巖窥淆,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扛伍,死亡現場離奇詭異刺洒,居然都是意外死亡逆航,警方通過查閱死者的電腦和手機因俐,發(fā)現死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來澳眷,“玉大人钳踊,你說我怎么就攤上這事拓瞪∥庠澹” “怎么了沟堡?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長粥血。 經常有香客問我趾娃,道長抬闷,這世上最難降的妖魔是什么笤成? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮上祈,結果婚禮上雇逞,老公的妹妹穿的比我還像新娘塘砸。我一直安慰自己掉蔬,他們只是感情好女轿,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著北救,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宅倒。 梳的紋絲不亂的頭發(fā)上蹭劈,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天多矮,我揣著相機與錄音,去河邊找鬼患雏。 笑死淹仑,一個胖子當著我的面吹牛匀借,可吹牛的內容都是我干的吓肋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼囤耳!你這毒婦竟也來了充择?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤铃剔,失蹤者是張志新(化名)和其女友劉穎键兜,沒想到半個月后谜疤,有當地人在樹林里發(fā)現了一具尸體夷磕,經...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片净当。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖萄传,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情衍菱,我是刑警寧澤辫呻,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站怖侦,受9級特大地震影響荷腊,放射性物質發(fā)生泄漏很钓。R本人自食惡果不足惜锭碳,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望歧沪。 院中可真熱鬧歹撒,春花似錦迈着、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春杖小,著一層夾襖步出監(jiān)牢的瞬間予权,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躁劣,地道東北人。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像赊时,于是被迫代替她去往敵國和親祖秒。 傳聞我的和親對象是個殘疾皇子诞吱,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內容