在這一節中,可以瞭解到K近鄰算法,並應用於分類與迴歸的例子。
k近鄰又稱作k-NN算法,是最簡單的機器學習算法。非常的適合小白入門瞭解機器學習原理。k-NN會保存訓練的數據,在面對新的數據時,算法會在訓練數據集中找到最近的數據點,這也就是爲什麼被叫作“最近鄰”的原因。
本節的數據將基於上一章節的數據,如有疑問,請查看上一章節。
備註:mgleran中的數據是用來展示的,不與機器學習的sklern模塊相重疊。
安裝mgleran請使用pip install mglearn命令。
# 在學習之前,先導入這些常用的模塊
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import mglearn
k近鄰分類
k-NN 算法最簡單的版本是隻考慮一個最近鄰,即被預測的新的數據點離訓練的數據集中的哪個點最近,它將被歸類爲哪個類別。
在mglearn的forge中,內置了此種情況,如下圖爲單一最近鄰模型對forge數據集的預測結果,五角星爲被預測的數據點,根據其離得最近的訓練數據集,通過設置參數n_neighbors=1
來設定“最近鄰”的個數。
mglearn.plots.plot_knn_classification(n_neighbors=1)
C:\Users\Administrator\Anaconda3\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function make_blobs is deprecated; Please import make_blobs directly from scikit-learn
warnings.warn(msg, category=DeprecationWarning)
從上圖我們就能明白,新數據離得誰最近,他就會被歸類爲哪一類。
左上角的五角星其實應該屬於三角星一類,爲了提高準確率,這裏可以提高“最近鄰”個數,即n_neighbors值。再來看一下當n_neighbors=3時的情況:
mglearn.plots.plot_knn_classification(n_neighbors=3)
C:\Users\Administrator\Anaconda3\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function make_blobs is deprecated; Please import make_blobs directly from scikit-learn
warnings.warn(msg, category=DeprecationWarning)
當提高“最近鄰”的個數後,可以看到,對於新數據的預測更準確了,左上角的五角星近相近的是一個圓和兩個三角星,因此它被歸類於三角一類。
下面來看看如何通過 scikit-learn 來應用 k 近鄰算法。
# 導入 train_test_split
from sklearn.model_selection import train_test_split
# 導入二分類數據集
X, y = mglearn.datasets.make_forge()
# 將數據打亂,並分爲訓練集與測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
C:\Users\Administrator\Anaconda3\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function make_blobs is deprecated; Please import make_blobs directly from scikit-learn
warnings.warn(msg, category=DeprecationWarning)
上面的代碼提供了數據集,接下來,需要實例化k-NN對象。
# 導入k-NN模型
from sklearn.neighbors import KNeighborsClassifier
# 將k-NN模型實例爲對象,並指定最近鄰個數
clf = KNeighborsClassifier(n_neighbors=3)
現在,我們就有了最近鄰算法模型實例化的對象了,該對象的引用被保存到了變量clf
中。
接下來,我們使用該對象對訓練集數據進行訓練,以得出一個模型結果。
clf.fit(X_train, y_train)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=3, p=2,
weights='uniform')
對於訓練集訓練的結果,會被保存在k-NN對象中,所以,該對象會對新數據在此基礎上進行預測。
接下來,對測試集進行預測,並查看預測結果。
# 預測測試集數據
clf.predict(X_test)
array([1, 0, 1, 0, 1, 0, 0])
# 評估模型的泛化能力
clf.score(X_test, y_test)
0.8571428571428571
可以看到,模型的精度爲86%,也就是說,該模型對於測試數據的預測,有86%的結果是正確的。
接下來要做的,是在多張圖表中展示不同“最近鄰”參數下,模型對於數據的分類能力的體現。而這種分類,採用決策邊界來展示。
接下來將分析“最近鄰”分另爲1、3、9的情況。
# 通過subplots創建一個幕布,在幕布上創建三個繪圖區,幕布大小爲10*3
fig, axes = plt.subplots(1, 3, figsize=(10,3))
# 通過zip函數,將兩個集體打包併成對的返回
for n_neighbors, ax in zip([1, 3, 9], axes):
# fit方法返回對象本身,所以我們可以將實例化和擬合放在一行代碼中
clf = KNeighborsClassifier(n_neighbors=n_neighbors).fit(X, y)
# 展示模型的決策邊界
mglearn.plots.plot_2d_separator(clf, X, fill=True, eps=0.5, ax=ax, alpha=.4)
# 將原始數據的位置在圖中展示
mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
# 設置標題
ax.set_title('n_neighbors{}'.format(n_neighbors))
# 設置橫座標標籤
ax.set_xlabel('feature 0')
# 設置縱座標標籤
ax.set_ylabel('feature 1')
# 在第一張圖上顯示圖例
axes[0].legend(loc=3)
plt.show()
從上圖可以發現,“最近鄰”的個數變大後,決策邊界將變得更加的平緩,曲線也變得更加的簡單。這種更簡單的情形,比較適合於大多數的數據。
這種結論也能說明,更簡單的模型,泛化能力更好。
接下來認證一下模型複雜度與泛化能力之間的關係
這次將使用乳腺癌數據集,對該數據用不同的“最鄰近”個數進行訓練與測試,然後對於評估結果展示一個線性的結果。
# 導入乳腺癌數據集模塊
from sklearn.datasets import load_breast_cancer
# 加載數據
cancer = load_breast_cancer()
# 將數據分爲訓練集與測試集
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=66)
# 創建兩個列表,分別用來記錄不同“最近鄰”參數下模型的訓練集精度和測試集精度
training_accuracy = []
test_accuracy = []
# n_neighbors 取值從1到10
neighbors_settings = range(1, 11)
# 循環測試不同“最近鄰”參數
for n_neighbors in neighbors_settings:
# 構建模型
clf = KNeighborsClassifier(n_neighbors=n_neighbors)
# 訓練
clf.fit(X_train, y_train)
# 記錄訓練集精度
training_accuracy.append(clf.score(X_train, y_train))
# 記錄測試集精度
test_accuracy.append(clf.score(X_test, y_test))
# 畫出訓練集數據的曲線圖
plt.plot(neighbors_settings, training_accuracy, label="training accuracy")
# 畫出測試集的曲線圖
plt.plot(neighbors_settings, test_accuracy, label="test accuracy")
# 設置x與y軸標籤
plt.xlabel('n_neighbors')
plt.ylabel('Accuracy')
# 展示圖例
plt.legend()
plt.show()
能過上圖的展示發現,訓練集的泛化精度隨着“最近鄰”個數的增加,精度越來越低。而測試集的泛化精度隨着“最近鄰”個數的增加,先上長後下降。兩者的最佳取值大概在n_neighbors=6這個位置。
也就是說,當“最近鄰”個數過小的時候,模型過於複雜,不適用於新數據。而“最近鄰”個數過大,則模型會變得過於簡單,性能也會變差。
k近鄰迴歸
k近鄰算法還適用於迴歸。在mglearn的wave中內置了一個迴歸數據集。現在,在該數據集上再添加三個測試點,利用單一“最近鄰”參數來預測目標的結果值。如下:
mglearn.plots.plot_knn_regression(n_neighbors=1)
再來看下“最近鄰”個數爲3時的預測值。
mglearn.plots.plot_knn_regression(n_neighbors=3)
上面兩個圖例是現成的模型,在 scikit-learn 中的 KNeighborsRegressor 類用於迴歸算法的實現,它的用法與 KNeighborsClassifier 的用法相似。
# 導入 KNeighborsRegressor 模塊
from sklearn.neighbors import KNeighborsRegressor
# 加載40個迴歸數據集
X, y = mglearn.datasets.make_wave(n_samples=40)
# 將wave數據集分爲訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 模型實例化,並將鄰居個數設置爲3
reg = KNeighborsRegressor(n_neighbors=3)
# 利用訓練數據和訓練目標值爲擬合模型
reg.fit(X_train, y_train)
KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=3, p=2,
weights='uniform')
然後對測試集進行預測:
reg.predict(X_test)
array([-0.05396539, 0.35686046, 1.13671923, -1.89415682, -1.13881398,
-1.63113382, 0.35686046, 0.91241374, -0.44680446, -1.13881398])
評估一下模型的泛化精度:
reg.score(X_test, y_test)
0.8344172446249604
對於迴歸模型,scroe方法返回的是R的平方數,也叫做決定系統,是迴歸模型預測的優度度量,位於0到1之間。R的平方等於1對應完美預測,R的平方等於0對應常數模型,即總是預測訓練集響應(y_train)的平均值。
分析 KNeighborsRegressor
同樣,這裏來展示“最近鄰”個數分別爲1、3、9下的泛化能力。
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# 創建1000個數據點,在-3和3之間均勻分佈
line = np.linspace(-3, 3, 1000).reshape(-1, 1)
for n_neighbors, ax in zip([1, 3, 9], axes):
# 利用1、3、9個鄰居分別進行預測
reg = KNeighborsRegressor(n_neighbors=n_neighbors)
reg.fit(X_train, y_train)
# 展示預測曲線與訓練集和測試集的關係
ax.plot(line, reg.predict(line))
ax.plot(X_train, y_train, '^', c=mglearn.cm2(0), markersize=8)
ax.plot(X_test, y_test, 'v', c=mglearn.cm2(1), markersize=8)
ax.set_title('{} neighbor(s)\n train score: {:.2f} test score: {:.2f}'.format(n_neighbors, reg.score(X_train, y_train), reg.score(X_test, y_test)))
ax.set_xlabel('Feature')
ax.set_ylabel('Target')
axes[0].legend(['Model predictions', 'Training data/target', 'Test data/target'], loc='best')
plt.show()
從圖中可以看出,僅使用單一“最近鄰”參數,訓練集中的第個點都對預測結果有顯著影響,非常不穩定。
而更大的“最近鄰”個數所對應的預測結果也更平滑,但對訓練數據的擬合不好。
總結:
一般來說,KNeighbors分類器有2個重要參數:鄰居的個數與數據點之間距離的度量方法,在實踐中,使用較小的鄰居個數(比如3或5)往往可以得到比較好的結果。
k-NN的優點之一是比較容易理解,比較適合機器學習小白入門,但如果數據量特別大,該模型的表現就會很差。