# 十四、K 最近鄰
> 作者:[Chris Albon](https://chrisalbon.com/)
>
> 譯者:[飛龍](https://github.com/wizardforcel)
>
> 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
## 確定 K 的最佳值

```py
# 加載庫
from sklearn.neighbors import KNeighborsClassifier
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.model_selection import GridSearchCV
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 創建標準化器
standardizer = StandardScaler()
# 標準化特征
X_std = standardizer.fit_transform(X)
# 擬合 5 個鄰居的 KNN 分類器
knn = KNeighborsClassifier(n_neighbors=5, metric='euclidean', n_jobs=-1).fit(X_std, y)
# 創建流水線
pipe = Pipeline([('standardizer', standardizer), ('knn', knn)])
# 創建候選值空間
search_space = [{'knn__n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}]
# 創建網格搜索
clf = GridSearchCV(pipe, search_space, cv=5, verbose=0).fit(X_std, y)
# 最佳鄰居大小(K)
clf.best_estimator_.get_params()['knn__n_neighbors']
# 6
```
## KNN 分類
K 最近鄰分類器(KNN)是一種簡單而強大的分類學習器。
KNN 有三個基本部分
* : 觀測的類別(我們試圖在測試數據中預測的東西)。
* : 觀察的預測因子/ IV /屬性。
* : 研究者指定的正數。 K 表示最接近特定觀測的觀測數,它定義了“鄰域”。 例如,`K = 2`意味著每個觀測都有一個鄰域,包含最接近它的另外兩個觀測。
想象一下,我們有一個觀測,我們知道它的自變量 ,但不知道它的類別 。 KNN 學習器找到最接近  的K個其他觀測,并使用他們已知的類別,將類別分配給 。
```py
import pandas as pd
from sklearn import neighbors
import numpy as np
%matplotlib inline
import seaborn
```
這里我們創建三個變量,`test_1`和`test_2`是我們的自變量,`outcome`是我們的因變量。 我們將使用這些數據來訓練我們的學習器。
```py
training_data = pd.DataFrame()
training_data['test_1'] = [0.3051,0.4949,0.6974,0.3769,0.2231,0.341,0.4436,0.5897,0.6308,0.5]
training_data['test_2'] = [0.5846,0.2654,0.2615,0.4538,0.4615,0.8308,0.4962,0.3269,0.5346,0.6731]
training_data['outcome'] = ['win','win','win','win','win','loss','loss','loss','loss','loss']
training_data.head()
```
| | test_1 | test_2 | outcome |
| --- | --- | --- | --- |
| 0 | 0.3051 | 0.5846 | win |
| 1 | 0.4949 | 0.2654 | win |
| 2 | 0.6974 | 0.2615 | win |
| 3 | 0.3769 | 0.4538 | win |
| 4 | 0.2231 | 0.4615 | win |
這不是必需的,但因為我們只有三個變量,所以我們可以繪制訓練數據集。 X 軸和 Y 軸是自變量,而點的顏色是它們的類別。
```py
seaborn.lmplot('test_1', 'test_2', data=training_data, fit_reg=False,hue="outcome", scatter_kws={"marker": "D","s": 100})
# <seaborn.axisgrid.FacetGrid at 0x11008aeb8>
```

`scikit-learn`庫需要將數據格式化為`numpy`數組。 這是重新格式化的代碼。
```py
X = training_data.as_matrix(columns=['test_1', 'test_2'])
y = np.array(training_data['outcome'])
```
這是我們的重點。 我們使用“觀測的鄰域是其三個最近的鄰居”的參數來訓練 KNN 學習器。 `weights ='uniform'`可以當做所用的投票系統。 例如,`uniform`意味著所有鄰居對觀測的類別進行同等權重的“投票”,而`weight ='distance'`則告訴學習器根據到我們正在分類的觀測的距離,來調整每個觀測的“投票”。
```py
clf = neighbors.KNeighborsClassifier(3, weights = 'uniform')
trained_model = clf.fit(X, y)
```
與訓練數據相比,我們訓練的模型有多好?
```py
trained_model.score(X, y)
# 0.80000000000000004
```
我們的模型準確率達 80%!
注:在任何現實世界的例子中,我們都希望將訓練的模型與一些保留的測試數據進行比較。 但由于這是一個玩具示例,我使用了訓練數據。
現在我們已經訓練了我們的模型,我們可以預測班級的任何新觀測,。 我們現在就這樣做吧!
```py
# 使用 'test_1' 第一個和第二個自變量的值
# 創建一個新觀測,為 .4 和 .6
x_test = np.array([[.4,.6]])
# 將學習者應用于新的未分類的觀測。
trained_model.predict(x_test)
# array(['loss'], dtype=object)
```
好哇! 我們可以看到學習器預測的新觀測的類是“輸”。
我們甚至可以查看學習器分配給每個分類的概率:
```py
trained_model.predict_proba(x_test)
# array([[ 0.66666667, 0.33333333]])
```
根據這個結果,模型預測觀測結果是“輸”的概率約為 67%,“贏”的概率為 33%。 因為觀測有更大的“輸”的概率,所以它預測為這個分類。
## 注
* K 的選擇對創建的分類器有重大影響。
* K 越大,決策邊界越線性(高偏差和低方差)。
* 有多種方法可以測量距離,兩種流行的方法是簡單的歐幾里德距離和余弦相似度。
# 基于半徑的 KNN 分類器

```py
# 加載庫
from sklearn.neighbors import RadiusNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn import datasets
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 創建標準化器
standardizer = StandardScaler()
# 標準化特征
X_std = standardizer.fit_transform(X)
```
在 scikit-learn 中,`RadiusNeighborsClassifier`與`KNeighborsClassifier`非常相似,但有兩個參數除外。 首先,在`RadiusNeighborsClassifier`中,我們需要指定固定區域的半徑,用于確定觀測是否是半徑內的鄰居。 將半徑設置為某個值,最好將其視為任何其他超參數,并在模型選擇期間對其進行調整。 第二個有用的參數是`outlier_label`,它表示半徑內沒有觀測的觀測的標簽 - 這本身通常可以是識別異常值的有用工具。
```py
# 訓練半徑鄰居分類器
rnn = RadiusNeighborsClassifier(radius=.5, n_jobs=-1).fit(X_std, y)
# 創建兩個觀測
new_observations = [[ 1, 1, 1, 1]]
# 預測兩個觀測的類別
rnn.predict(new_observations)
# array([2])
```