監督機器學習問題主要有兩種,分別叫作分類(classification)與回歸(regression)。
分類問題的目標是預測類別標簽(class label),這些標簽來自預定義的可選列表。在二分類問題中,我們通常將其中一個類別稱為正類(positive class),另一個類別稱為反 類(negative class)。這里的“正”並不代表好的方面或正數,而是代表研究對象。因此在 尋找垃圾郵件時,“正”可能指的是垃圾郵件這一類別。將兩個類別中的哪一個作為“正 類”,往往是主觀判斷,與具體的領域有關。
回歸任務的目標是預測一個連續值,編程術語叫作浮點數(floating-point number) ,數學術 語叫作實數(real number)。根據教育水平、年齡和居住地來預測一個人的年收入,這就是 回歸的一個例子。在預測收入時,預測值是一個金額(amount),可以在給定范圍內任意 取值。回歸任務的另一個例子是,根據上一年的產量、天氣和農場員工數等屬性來預測玉 米農場的產量。同樣,產量也可以取任意數值。
區分分類任務和回歸任務有一個簡單方法,就是問一個問題:輸出是否具有某種連續性。 如果在可能的結果之間具有連續性,那么它就是一個回歸問題。
泛化、過擬合與欠擬合
如果一個模型能夠對沒見過的數據做出准確 預測,我們就說它能夠從訓練集泛化(generalize)到測試集。我們想要構建一個泛化精度 盡可能高的模型。
如果你在擬合模型時過分關注訓練集的細節,得到了一個在訓練 集上表現很好、但不能泛化到新數據上的模型,那么就存在過擬合。與之相反,如果你 的模型過於簡單——比如說,“有房子的人都買船”——那么你可能無法抓住數據的全部 內容以及數據中的變化,你的模型甚至在訓練集上的表現就很差。選擇過於簡單的模型 被稱為欠擬合(underfitting)。
模型復雜度與數據集大小的關系
需要注意,模型復雜度與訓練數據集中輸入的變化密切相關:數據集中包含的數據點的變 化范圍越大,在不發生過擬合的前提下你可以使用的模型就越復雜。通常來說,收集更多 的數據點可以有更大的變化范圍,所以更大的數據集可以用來構建更復雜的模型。但是, 僅復制相同的數據點或收集非常相似的數據是無濟於事的。
收集更多數據,適當構建更復雜的模型,對監督學習任務往往特別有用。本書主要關注固 定大小的數據集。在現實世界中,你往往能夠決定收集多少數據,這可能比模型調參更為 有效。永遠不要低估更多數據的力量!
一個模擬的二分類數據集示例是 forge 數據集,它有兩個特征。下列代碼將繪制一個散點 圖(圖 2-2),將此數據集的所有數據點可視化。圖像以第一個特征為 x 軸,第二個特征為 y 軸。正如其他散點圖那樣,每個數據點對應圖像中的一點。每個點的顏色和形狀對應其 類別:
# 生成數據集 X, y = mglearn.datasets.make_forge() # 數據集繪圖 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))
從 X.shape 可以看出,這個數據集包含 26 個數據點和 2 個特征。
我們用模擬的 wave 數據集來說明回歸算法。wave 數據集只有一個輸入特征和一個連續的 目標變量(或響應),后者是模型想要預測的對象。下面繪制的圖像(圖 2-3)中單一特征 位於 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")
wave 數據集的圖像,x軸表示特征,y軸表示回歸目標。
我們之所以使用這些非常簡單的低維數據集,是因為它們的可視化非常簡單——書頁只有 兩個維度,所以很難展示特征數超過兩個的數據。從特征較少的數據集(也叫低維數據 集)中得出的結論可能並不適用於特征較多的數據集(也叫高維數據集)。只要你記住這 一點,那么在低維數據集上研究算法也是很有啟發的。
k近鄰
k近鄰分類
對於二維數據集,我們還可以在 xy 平面上畫出所有可能的測試點的預測結果。我們根據 平面中每個點所屬的類別對平面進行着色。這樣可以查看決策邊界(decision boundary), 即算法對類別 0 和類別 1 的分界線。
fig, axes = plt.subplots(1, 3, figsize=(10, 3)) 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("{} neighbor(s)".format(n_neighbors)) ax.set_xlabel("feature 0") ax.set_ylabel("feature 1") axes[0].legend(loc=3)
k近鄰回歸
%matplotlib notebook from sklearn.neighbors import KNeighborsRegressor 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) 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")
從圖中可以看出,僅使用單一鄰居,訓練集中的每個點都對預測結果有顯著影響,預測結 果的圖像經過所有數據點。這導致預測結果非常不穩定。考慮更多的鄰居之后,預測結果 變得更加平滑,但對訓練數據的擬合也不好。一般來說,KNeighbors 分類器有 2 個重要參數:鄰居個數與數據點之間距離的度量方法。 在實踐中,使用較小的鄰居個數(比如 3 個或 5 個)往往可以得到比較好的結果,但你應 該調節這個參數。
雖然 k 近鄰算法很容易理解,但由於預測速度慢且不能處理具有很多特征的數據集,所以 在實踐中往往不會用到。