【機器學習】k近鄰分類與迴歸實戰

在這一節中,可以瞭解到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的優點之一是比較容易理解,比較適合機器學習小白入門,但如果數據量特別大,該模型的表現就會很差。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章