2.3 監(jiān)督學習算法
2.3.1 一些樣本數(shù)據(jù)集
我們將使用一些數(shù)據(jù)集來說明不同的算法。其中一些數(shù)據(jù)集很小,而且是模擬的,其目的是強調(diào)算法的某個特定方面。其他數(shù)據(jù)集都是現(xiàn)實世界的大型數(shù)據(jù)集。
forge數(shù)據(jù)集是一個模擬的二分類數(shù)據(jù)集示例,它有兩個特征。下列代碼將繪制一個散點圖,將此數(shù)據(jù)集的所有數(shù)據(jù)點可視化。圖像以第一個特征為x軸,第二個特征為y軸。正如其他散點圖那樣,每個數(shù)據(jù)點對應圖像中的一點。每個點的顏色和形狀對應其類別:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn
# 生成數(shù)據(jù)集
X, y = mglearn.datasets.make_forge()
# 數(shù)據(jù)集繪圖
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.legend(["Class 0", "Class 1"], loc=4)
plt.xlabel("First feature")
plt.ylabel("Second feature")
print("X.shape:{}".format(X.shape))
運行結(jié)果:

從X.shape可以看出,這個數(shù)據(jù)集包含26個數(shù)據(jù)點和2個特征。
我們用模擬的wave數(shù)據(jù)集來說明回歸算法。wave數(shù)據(jù)集只有一個輸入特征和一個連續(xù)的目標變量(或響應),后者是模型想要預測的對象。下面繪制的圖像中單一特征位于x軸,回歸目標(輸出)位于y軸:
X, y = mglearn.datasets.make_wave(n_samples=40)
plt.plot(X, y, 'o')
plt.ylim(-3, 3)
plt.xlabel("Feature")
plt.ylabel("Target")

我們之所以使用這些非常簡單的低維數(shù)據(jù)集,是因為他們的可視化非常簡單——書頁只有兩個維度,所以很難展示特征數(shù)超過兩個的數(shù)據(jù)。從特征較少的數(shù)據(jù)集(也叫低維數(shù)據(jù)集)中得到的結(jié)論可能并不適用于特征較多的數(shù)據(jù)集(也叫高維數(shù)據(jù)集)。只要你記住這一點,那么在低維數(shù)據(jù)集上研究算法也是很有啟發(fā)的。
除了上面這些小型的模擬的數(shù)據(jù)集,我們還將補充兩個現(xiàn)實世界中的數(shù)據(jù)集,它們都包含在scikit-learn中。其中一個是威斯康星州乳腺癌數(shù)據(jù)集(簡稱cancer),里面記錄了乳腺癌腫瘤的臨床測量數(shù)據(jù)。每個腫瘤都被標記為“良性”(benign,表示無害腫瘤)或“惡性”(malignant,表示癌性腫瘤),其任務是基于人體組織的測量數(shù)據(jù)來學習預測腫瘤是否為惡性。
可以用scikit-learn模塊的load_breast_cancer函數(shù)來加載數(shù)據(jù):
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
print("cancer.keys():\n{}".format(cancer.keys()))
運行結(jié)果:
cancer.keys():
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])
包含在scikit-learn中的數(shù)據(jù)集通常被保存為Bunch對象,里面包含真實數(shù)據(jù)以及一些數(shù)據(jù)集信息。關(guān)于Bunch對象,你只需要知道它與字典很相似,而且還有一個額外的好處,就是你可以用點操作符來訪問對象的值(比如用bunch.key來代替bunch['key'])。
這個數(shù)據(jù)集共包含569個數(shù)據(jù)點,每個數(shù)據(jù)點有30個特征:
print("Shape of cancer data:{}".format(cancer.data.shape))
Shape of cancer data:(569, 30)
在569個數(shù)據(jù)點中,212個被標記為惡性,357個被標記為良性:
print("Sample counts per class:\n{}".format(
{n: v for n, v in zip(cancer.target_names, np.bincount(cancer.target))}))
Sample counts per class:
{'malignant': 212, 'benign': 357}
為了得到每個特征的語義說明,我們可以看一下feature_names屬性:
print("Feature names:\n{}".format(cancer.feature_names))
Feature names:
['mean radius' 'mean texture' 'mean perimeter' 'mean area'
'mean smoothness' 'mean compactness' 'mean concavity'
'mean concave points' 'mean symmetry' 'mean fractal dimension'
'radius error' 'texture error' 'perimeter error' 'area error'
'smoothness error' 'compactness error' 'concavity error'
'concave points error' 'symmetry error' 'fractal dimension error'
'worst radius' 'worst texture' 'worst perimeter' 'worst area'
'worst smoothness' 'worst compactness' 'worst concavity'
'worst concave points' 'worst symmetry' 'worst fractal dimension']
我們還會用到一個現(xiàn)實世界中的回歸數(shù)據(jù)集,即波士頓房價數(shù)據(jù)集。與這個數(shù)據(jù)集相關(guān)的任務是,利用犯罪率、是否臨近查爾斯河、公路可達性等信息,來預測20世紀70年代波士頓地區(qū)房屋價格的中位數(shù)。這個數(shù)據(jù)集包含506個數(shù)據(jù)點和13個特征:
from sklearn.datasets import load_boston
boston = load_boston()
print("Data shape:{}".format(boston.data.shape))
Data shape:(506, 13)
同樣,可以閱讀boston對象的DESCR屬性來了解數(shù)據(jù)集的更多信息。對于我們的目的而言,我們需要擴展這個數(shù)據(jù)集,輸入特征不僅包括這13個測量結(jié)果,還包括這些特征之間的乘積(也叫交互項)。換句話說,我們不僅將犯罪率和公路可達性作為特征,還將犯罪率和公路可達性的乘積作為特征。像這樣包含導出特征的方法叫作特征工程(feature engineerng)。這個導出的數(shù)據(jù)集可以用load_extended_boston函數(shù)加載:
X, y = mglearn.datasets.load_extended_boston()
print("X.shpe:{}".format(X.shape))
X.shpe:(506, 104)
最初的13個特征加上這13個特征兩兩組合(有放回)得到的91個特征,一共有104個特征。
2.3.2 k近鄰
k-NN算法可以說是最簡單的機器學習算法。構(gòu)建模型只需要保存訓練數(shù)據(jù)集即可。想要對新數(shù)據(jù)點做出預測,算法會在訓練數(shù)據(jù)集中找到最近的數(shù)據(jù)點,也就是它的“最近鄰”。
1. k近鄰分類
k-NN算法最簡單的版本只考慮一個最近鄰,也就是與我們想要預測的數(shù)據(jù)點最近的訓練數(shù)據(jù)點。預測結(jié)果就是這個訓練數(shù)據(jù)點的已知輸出。下圖給出了這種分類方法在forge數(shù)據(jù)集上的應用:
mglearn.plots.plot_knn_classification(n_neighbors = 1)

這里我們添加了3個新數(shù)據(jù)點(用五角星表示)。對于每個新數(shù)據(jù)點,我們標記了訓練集中與它最近的點。單一最近鄰算法的預測結(jié)果就是那個點的標簽(對應五角星的顏色)。
除了僅考慮最近鄰,還可以考慮任意個(k個)鄰居。這也是k近鄰算法名字的來歷。在考慮多余一個鄰居的情況時,我們用“投票法”(voting)來指定標簽。也就是說,對于每個測試點,我們數(shù)一數(shù)多少個鄰居屬于類別0,多少個鄰居屬于類別1。然后將出現(xiàn)次數(shù)更多的類別(也就是k個近鄰中占多數(shù)的類別)作為預測結(jié)果。下面的例子用到了3個近鄰:
mglearn.plots.plot_knn_classification(n_neighbors = 3)

和上面一樣,預測結(jié)果可以從五角星的顏色看出。你可以發(fā)現(xiàn),左上角新數(shù)據(jù)點的預測結(jié)果與只用一個鄰居時的預測結(jié)果不同。
雖然這張圖對應的是一個二分類問題,但方法同樣適用于多分類的數(shù)據(jù)集。對于多分類問題,我們數(shù)一數(shù)每個類別分別有多少個鄰居,然后將最常見的類別作為預測結(jié)果。
現(xiàn)在看一下如何通過scikit-learn來應用k近鄰算法。首先,要將數(shù)據(jù)分為訓練集和測試集,以便評估泛化性能:
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)
然后,導入類并將其實例化。這時可以設定參數(shù),比如林俊的個數(shù)。這里我們將其設為3:
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors = 3)
現(xiàn)在,利用訓練集對這個分類器進行擬合。對于KNeighborsClassifier來說就是保存數(shù)據(jù)集,以便在預測時計算與鄰居之間的距離:
clf.fit(X_train, y_train)
調(diào)用predict方法來對測試數(shù)據(jù)進行預測。對于測試集中的每個數(shù)據(jù)點,都要計算它在訓練集的最近鄰,然后找出其中出現(xiàn)次數(shù)最多的類別:
print("Test set predictions:{}".format(clf.predict(X_test)))
輸出結(jié)果:
Test set predictions:[1 0 1 0 1 0 0]
為了評估模型的泛化能力好壞,我們可以對測試數(shù)據(jù)和測試標簽調(diào)用score方法:
print("Test set accuracy:{:.2f}".format(clf.score(X_test, y_test)))
輸出結(jié)果:
Test set accuracy:0.86
可以看到,我們的模型精度約為86%,也就是說,在測試數(shù)據(jù)集中,模型對其中86%的樣本預測的類別都是正確的。