本章首先討論了模型複雜度,然後討論了泛化,或者說學習一個能夠在前所未見的新數據上表現良好的模型。這就引出了欠擬合和過擬合的概念,前者是指一個模型無法獲取訓練數據的所有變化,後者是指模型過分關注訓練數據,但對新數據的泛化性能不好。

本章討論了一系列用於分類和回歸的機器學習模型,各個模型的優點和缺點,以及如何控制它們的模型複雜度。我們發現,對於許多演算法而言,設置正確的參數對模型性能至關重要。有些演算法還對輸入數據的表示方式很敏感,特別是特徵的縮放。因此如果盲目地將一個演算法應用於數據集,而不去理解模型所做的假設以及參數設定的含義,不太可能會得到精度高的模型。

本章包含大量有關演算法的信息,在繼續閱讀後續章節之前你不必記住所有這些細節。但是,這裡提到的有關模型的某些知識(以及在特定情況下使用那些模型)對於在實踐中成功應用機器學習模型是很重要的。關於何時使用哪種模型,下面是一份快速總結。

1.最近鄰

適用於小型數據集,是很好的基準模型,也適用於高維數據。

缺點是預測速度慢且不能處理具有很多特徵的數據集,所以實踐中往往不會用到。

# 分類
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)

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_train, y_train)

# 回歸
X, y = mglearn.datasets.make_wave(n_samples=40)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.neighbors import KNeighborsRegressor
reg = KNeighborsRegressor(n_neighbors=3)
reg.fit(X_train, y_train)

更少的鄰居對應更高的模型複雜度,使用更多的鄰居對應更低的模型複雜度。

2.線性模型

非常可靠的首選演算法,適用於非常大的數據集,也適用於高維數據。

# 線性回歸(普通最小二乘法)
from sklearn.linear_model import LinearRegression
X, y = mglearn.datasets.make_wave(n_samples=60)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
lr = LinearRegression().fit(X_train, y_train)

print("lr.coef_: {}".format(lr.coef_))
print("lr.intercept_: {}".format(lr.intercept_))

lr.coef_: [0.394] # 權重或系統
lr.intercept_: -0.03180434302675973 # 偏移或斜距

# 嶺回歸
from sklearn.linear_model import Ridge
ridge = Ridge().fit(X_train, y_train)
ridge01 = Ridge(alpha=0.1).fit(X_train, y_train)
ridge10 = Ridge(alpha=10).fit(X_train, y_train)

嶺回歸用到L2正則化,要設置alpha參數。增大alpha會使線性模型的係數更加趨向於0,從而降低訓練集性能,但可能會提高泛化性能。減小alpha可以讓係數受到的限制更小。

# lasso
from sklearn.linear_model import Lasso

lasso = Lasso().fit(X_train, y_train)
lasso001 = Lasso(alpha=0.01, max_iter=100000).fit(X_train, y_train)

lasso是另外一種正則化的線性回歸,與嶺回歸相同,使用lasso也是約束係數使其接近於0,但用到的方法不同,叫做L1正則化。L1正則化的結果是,使用lasso時某些係數剛好為0。lasso有一個正則化參數alpha,可以控制係數趨向於0的強度。這麼做的同時,我們還需要增加max_iter的值(運行迭代的最大次數)。alpha變小,我們可以你和一個更複雜的模型,但如果把alpha設得太小,那麼就會消除正則化的效果,並出現過擬合,得到與LinearRegression類似的結果。

在實踐中,兩個模型中一般首選嶺回歸。但如果特徵很多,你認為只有其中幾個是重要的,那麼選擇Lasso可能更好。同樣,如果你想要一個容易解釋的模型,Lasso可以給出更容易理解的模型,因為它只選擇了一部分輸入特徵。scikit-learn還提供了ElasticNet類,結合了Lasso和Ridge的懲罰項。在實踐中,這種結合的效果最好,不過代價是要調節兩個參數:一個用於L1正則化,一個用於L2正則化。

用於分類的線性模型

from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC

X, y = mglearn.datasets.make_forge()

fig, axes = plt.subplots(1, 2, figsize=(10, 3))

for model, ax in zip([LinearSVC(), LogisticRegression()], axes):
clf = model.fit(X, y)
mglearn.plots.plot_2d_separator(clf, X, fill=False, eps=0.5,
ax=ax, alpha=.7)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
ax.set_title("{}".format(clf.__class__.__name__))
ax.set_xlabel("Feature 0")
ax.set_ylabel("Feature 1")
axes[0].legend()

mglearn.plots.plot_linear_svc_regularization()

兩個模型都默認使用L2正則化,決定正則化強度的權衡參數叫做C。C值越大,對應正則化越弱。換句話說,如果參數C值越大,那麼LogisticRegression和LinearSVC將儘可能將訓練集擬合到最好,而如果C值較小,那麼模型更強調使係數向量(w)接近於0。參數C的作用還有另一個有趣之處。較小的C值可以讓演算法盡量適應「大多數」數據點,而較大的C值更強調每個數據點都分類正確的重要性。(見上圖)

for C, marker in zip([0.001, 1, 100], [o, ^, v]):
lr_l1 = LogisticRegression(C=C, penalty="l1").fit(X_train, y_train)
print("Training accuracy of l1 logreg with C={:.3f}: {:.2f}".format(
C, lr_l1.score(X_train, y_train)))
print("Test accuracy of l1 logreg with C={:.3f}: {:.2f}".format(
C, lr_l1.score(X_test, y_test)))
plt.plot(lr_l1.coef_.T, marker, label="C={:.3f}".format(C))

plt.xticks(range(cancer.data.shape[1]), cancer.feature_names, rotation=90)
xlims = plt.xlim()
plt.hlines(0, xlims[0], xlims[1])
plt.xlim(xlims)
plt.xlabel("Feature")
plt.ylabel("Coefficient magnitude")

plt.ylim(-5, 5)
plt.legend(loc=3)

Training accuracy of l1 logreg with C=0.001: 0.91
Test accuracy of l1 logreg with C=0.001: 0.92
Training accuracy of l1 logreg with C=1.000: 0.96
Test accuracy of l1 logreg with C=1.000: 0.96
Training accuracy of l1 logreg with C=100.000: 0.99
Test accuracy of l1 logreg with C=100.000: 0.98

如果想要一個可解釋性更強的模型,使用L1正則化可能更好,因為它約束模型只使用少數的幾個特徵。上圖是使用L1正則化的係數圖像和分類精度。用於二分類的線性模型與用於回歸的線性模型有許多相似之處。與用於回歸的線性模型一樣,模型的主要差別在與penalty參數,這個參數會影響正則化,也會影響模型是使用所有可用特徵還是只選擇特徵的一個子集。

用於多分類的線性模型

將二分類演算法推廣到多分類演算法的一種常見方法是「一對其餘」(one-vs.-rest)方法。在「一對其餘」方法中,對每個類別都學習一個二分類模型,將這個類別與所有其他類別盡量分開,這樣就生成了與類別個數一樣多的二分類模型。在測試點上運行所有二分類器來進行預測。在對應類別上分數最高的分類器「勝出」,將這個類別標籤返回作為預測結果。

from sklearn.datasets import make_blobs

X, y = make_blobs(random_state=42)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.legend(["Class 0", "Class 1", "Class 2"])

linear_svm = LinearSVC().fit(X, y)
print("Coefficient shape: ", linear_svm.coef_.shape)
print("Intercept shape: ", linear_svm.intercept_.shape)

Coefficient shape: (3, 2)
Intercept shape: (3,)

3個分類器可視化:

mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
line = np.linspace(-15, 15)
for coef, intercept, color in zip(linear_svm.coef_, linear_svm.intercept_,
mglearn.cm3.colors):
plt.plot(line, -(line * coef[0] + intercept) / coef[1], c=color)
plt.ylim(-10, 15)
plt.xlim(-10, 8)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.legend([Class 0, Class 1, Class 2, Line class 0, Line class 1,
Line class 2], loc=(1.01, 0.3))

二維空間所有區域的預測結果:

mglearn.plots.plot_2d_classification(linear_svm, X, fill=True, alpha=.7)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
line = np.linspace(-15, 15)
for coef, intercept, color in zip(linear_svm.coef_, linear_svm.intercept_,
mglearn.cm3.colors):
plt.plot(line, -(line * coef[0] + intercept) / coef[1], c=color)
plt.legend([Class 0, Class 1, Class 2, Line class 0, Line class 1,
Line class 2], loc=(1.01, 0.3))
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")

優點、缺點和參數

線性模型的主要參數是正則化參數,在回歸模型中叫做alpha,在LinearSVC和LogisticRegression中叫做C。alpha值較大或C值較小,說明模型比較簡單。特別是對於回歸模型而言,調節這些參數非常重要。通常在對數尺度上對C和alpha進行搜索。你還需要確定的是用L1正則化還是L2正則化。如果你假定只有幾個特徵是真正重要的,那麼你應該用L1正則化,否則應默認使用L2正則化。如果模型的可解釋性是很重要的話,使用L1也會有幫助。由於L1隻用到幾個特徵,所以更容易解釋哪些特徵對模型是重要的,以及這些特徵的作用。

線性模型的訓練速度非常快,預測速度也很快。這種模型可以推廣到非常大的數據集,對稀疏數據也很有效。如果你的數據包包含數十萬甚至上百萬個樣本,你可能需要研究如何使用LogisticRegression和Ridge模型的solver=『sag』選項,在處理大型數據時,這一選項比默認值要更快。其它選項還有SGDClassifier和SGDRegressor類,它們對本節介紹的線性模型實現了可擴展性更強的版本。

線性模型的另一個優點在於,利用我們之間見過的用於回歸和分類的公式,理解如何進行預測是相對比較容易的。不幸的是,往往並不完全清楚係數為什麼是這樣的。如果你的數據集中包含高度相關的特性,這一問題尤為突出。在這種情況下,可能很難對係數做出解釋。

如果特徵數量大於樣本數量,線性模型的表現通常都很好。它也常用於非常大的數據集,只是因為訓練其他模型並不可行。但在更低維的空間中,其他模型的泛化性能可能更好。

3.樸素貝葉斯

只適用於分類問題。比線性模型速度還快,適用於非常大的數據集和高維數據。精度通常要低於線性模型。

GaussianNB可應用於任意連續數據,而BernoulliNB假定輸入數據為二分類數據,MultinomialNB假定輸入數據為計數數據(即每個特徵代表某個對象的整數計數,比如一個單詞在句子里出現的次數)BernoulliNB和MultinomialNB主要用於文本數據分類。

優點、缺點和參數

MultinomialNB和BernoulliNB都只有一個參數alpha,用於控制模型複雜度。alpha的工作原理是,演算法向數據中添加alpha這麼多的虛擬數據點,這些點對所有特徵都取正值。這可以將統計數據「平滑化」(smoothing)。alpha越大,平滑化越強,模型複雜度就越低。演算法性能對alpha的魯棒性相對較好,也就是說,alpha值對模型性能並不重要。但調整這個參數通常都會使精度略有提高。

GaussianNB主要用於高維數據,而另外兩種樸素貝葉斯模型則廣泛用於稀疏計數數據,比如文本。MultinomialNB的性能通常要優於BernoulliNB,特別是在包含很多非零特徵的數據集(即大型文檔)上。

樸素貝葉斯模型的許多優點和缺點都與線性模型相同。它的訓練和與預測速度都很快,訓練過程也很容易理解。該模型對高維稀疏數據的效果很好,對參數的魯棒性也相對較好。樸素貝葉斯模型是很好的基準模型,常用語非常大的數據集,在這些數據集上即使訓練線性模型可能也要花費大量時間。

4.決策樹

速度很快,不需要數據縮放,可以可視化,很容易解釋。

from sklearn.tree import DecisionTreeClassifier

cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, stratify=cancer.target, random_state=42)
tree = DecisionTreeClassifier(random_state=0)
tree.fit(X_train, y_train)
tree = DecisionTreeClassifier(max_depth=4, random_state=0)
tree.fit(X_train, y_train)
print("Feature importances:
{}".format(tree.feature_importances_))

Feature importances:
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.01 0.048
0. 0. 0.002 0. 0. 0. 0. 0. 0.727 0.046 0. 0.
0.014 0. 0.018 0.122 0.012 0. ]

def plot_feature_importances_cancer(model):
n_features = cancer.data.shape[1]
plt.barh(range(n_features), model.feature_importances_, align=center)
plt.yticks(np.arange(n_features), cancer.feature_names)
plt.xlabel("Feature importance")
plt.ylabel("Feature")
plt.ylim(-1, n_features)

plot_feature_importances_cancer(tree)

上圖是樹的特徵重要性。它為每個特徵對樹的決策的重要性進行排序。對於每個重要性來說,它都是一個介於0和1之間的數字,其中0表示「根本沒用到」,1表示「完美預測目標值」。特徵重要性的求和始終為1。如果某個特徵的feature_importance_很小,並不能說明這個特徵沒有提供任何信息。這隻能說明該特徵沒有被樹選中,可能是因為另一個特徵也包含了同樣的信息。

優點、缺點和參數

控制決策樹模型複雜度的參數是預剪枝參數,它在樹完全展開之前停止樹的構造。通常來說,選擇一種預剪枝策略(設置max_depth、max_leaf_nodes或min_samples_leaf)足以防止過擬合。

與前面討論過的許多演算法相比,決策樹有兩個優點:一是得到的模型很容易可視化,非專家也容易理解(至少對於較小的樹而言);而是演算法完全不受數據縮放的影響。由於每個特徵被單獨處理,而且數據的劃分也不依賴與縮放,因此決策樹演算法不需要特徵預處理,比如歸一化或標準化。特別是特徵的尺度完全不一樣時或者二元特徵和連續特徵存在時,決策樹的效果很好。

決策樹的主要缺點在於,即使做了預剪枝,它也經常會過擬合,泛化性能很差。因此,大多數應用中,往往使用決策樹集成的方法來代替單顆決策樹。

5.隨機森林

幾乎總是比單顆決策樹的表現要好,魯棒性很好,非常強大。不需要數據縮放。不適用於高維稀疏數據。

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_moons

X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y,
random_state=42)

forest = RandomForestClassifier(n_estimators=5, random_state=2)
forest.fit(X_train, y_train)

fig, axes = plt.subplots(2, 3, figsize=(20, 10))
for i, (ax, tree) in enumerate(zip(axes.ravel(), forest.estimators_)):
ax.set_title("Tree {}".format(i))
mglearn.plots.plot_tree_partition(X_train, y_train, tree, ax=ax)

mglearn.plots.plot_2d_separator(forest, X_train, fill=True, ax=axes[-1, -1],
alpha=.4)
axes[-1, -1].set_title("Random Forest")
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)

X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, random_state=0)
forest = RandomForestClassifier(n_estimators=100, random_state=0)
forest.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(forest.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(forest.score(X_test, y_test)))

Accuracy on training set: 1.000
Accuracy on test set: 0.972

用於回歸和分類的隨機森林是目前應用最廣泛的機器學習方法之一。這種方法非常強大,通常不需要反覆調節參數就可以給出很好的結果,也不需要對數據進行縮放。

需要調節的重要參數有n_estimators和max_features,可能還包括預剪枝選項(如max_depth)。n_estimators總是越大越好。常用的經驗法則是「在你的時間/內存允許的情況下盡量多」。max_features決定每棵樹隨機性大小,較小的maxfeatures可以降低過擬合。好的經驗是使用默認值:對於分類,默認值是maxfeatures=sqrt(n_features);對於回歸,默認值是max_features=n_features。增大max_features或max_features有時也可以提高性能。它還可以大大降低用於訓練和預測時間和空間要求。

6.梯度提升決策樹

精度通常比隨機森林略高。與隨機森林相比,訓練速度更慢,但預測速度更快,需要的內存也更少。比隨機森林需要更多的參數調節。通常也不適用於高維稀疏矩陣。

from sklearn.ensemble import GradientBoostingClassifier

X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, random_state=0)

gbrt = GradientBoostingClassifier(random_state=0)
gbrt.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))

Accuracy on training set: 1.000
Accuracy on test set: 0.958

gbrt = GradientBoostingClassifier(random_state=0, max_depth=1)
gbrt.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))

Accuracy on training set: 0.991
Accuracy on test set: 0.972

gbrt = GradientBoostingClassifier(random_state=0, learning_rate=0.01)
gbrt.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))

Accuracy on training set: 0.988
Accuracy on test set: 0.965

梯度提升樹模型的主要參數包括樹的數量n_estimators和學習率learningrate,後者用於控制每棵樹對前一棵樹的錯誤的糾正強度。這兩個參數高度相關 ,因為learning_rate越低,就需要更多的樹來構建具有相似複雜度的模型。隨機森林的n_estimators值總是越大越好,但梯度不同,增大n_estimators會導致模型更加複雜,進而可能導致過擬合。通常的做法是根據時間和內存的預算選擇合適的n_estimators,然後對不同的learning_rate進行遍歷。

另一個重要參數是max_depth(或max_leaf_nodes),用於降低每棵樹的複雜度。梯度提升模型的max_depth通常都設置得很小,一般不超過5。

7.支持向量機

對於特徵含義相似的中等大小的數據集很強大。需要數據縮放,對參數敏感。

在訓練過程中,SVM學習每個訓練數據點對於表示兩個類別之間的決策邊界的重要性。通常只有一部分訓練數據點對於定義決策邊界來說很重要:位於類別之間邊界上的那些點。這些點叫做支持向量(support vector),支持向量機正是由此得名。想要對新樣本點進行預測,需要測量它與每個支持向量之間的距離。分類決策是基於它與支持向量之間的距離以及在訓練過程中學到的支持向量的重要性(保存在SVC的sual_coef_屬性中)來做出的。

from sklearn.svm import SVC

X, y = mglearn.tools.make_handcrafted_dataset()
svm = SVC(kernel=rbf, C=10, gamma=0.1).fit(X, y)
mglearn.plots.plot_2d_separator(svm, X, eps=.5)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
# plot support vectors
sv = svm.support_vectors_
# class labels of support vectors are given by the sign of the dual coefficients
sv_labels = svm.dual_coef_.ravel() > 0
mglearn.discrete_scatter(sv[:, 0], sv[:, 1], sv_labels, s=15, markeredgewidth_=3)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")

fig, axes = plt.subplots(3, 3, figsize=(15, 10))

for ax, C in zip(axes, [-1, 0, 3]):
for a, gamma in zip(ax, range(-1, 2)):
mglearn.plots.plot_svm(log_C=C, log_gamma=gamma, ax=a)

axes[0, 0].legend(["class 0", "class 1", "sv class 0", "sv class 1"],
ncol=4, loc=(.9, 1.2))

在書上的例子中,SVM給出了非常平滑且非線性的(不是直線)的邊界。這裡調節了兩個參數:C參數和gamma參數。

gamma參數是上一節給出的公式中的參數,用於控制高斯核的寬度。它決定了點與點之間「靠近」是指多大距離。C參數是正則化參數,與線性模型中用到的類似。它限制每個點的重要性(或者確切地說,每個點的dual_coef_)

從左到右,我們將參數gamma的值從0.1增加到10。gamma較小,說明高斯核的半徑較大,許多點都被看做比較靠近。這一點可以在圖中看出:左側的圖決策邊界非常平滑,越向右的圖決策邊界更關注單個點。小的gamma值表示決策邊界變化很慢,生成的是複雜度較低的模型,而大的gamma值則會生成更為複雜的模型。

從上到下,我們將參數C的值從0.1增加到1000。與線性模型相同,C值很小,說明模型非常受限,每個數據點的影響範圍都有限。你可以看到,左上角的圖中,決策邊界看起來幾乎是線性的,誤分類的點對邊界幾乎沒有任何影響。再看左下角的圖,增大C之後這些點對模型的影響變大,使得決策邊界發生彎曲來將這些點正確分類。

在所有特徵的測量單位相似(比如都是像素密度)而且範圍也差不多時,SVM是值得嘗試的。

8.神經網路

可以構建非常複雜的模型,特別是對於大型數據集而言。對數據縮放敏感,對參數選取敏感。大型網路需要很長的訓練時間。

(1)包含2個隱層,每個隱層包含10個隱單元的神經網路學到的決策邊界(激活函數為relu)

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons

# using two hidden layers, with 10 units each
mlp = MLPClassifier(solver=lbfgs, random_state=0,
hidden_layer_sizes=[10, 10])
mlp.fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")

(2)包含2個隱層,每個隱層包含10個隱單元的神經網路學到的決策邊界(激活函數tanh)

# using two hidden layers, with 10 units each, now with tanh nonlinearity.
mlp = MLPClassifier(solver=lbfgs, activation=tanh,
random_state=0, hidden_layer_sizes=[10, 10])
mlp.fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")

(3)不同隱單元個數與alpha參數的不同設定下的決策函數

X, y = make_moons(n_samples=100, noise=0.25, random_state=3)

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y,
random_state=42)
fig, axes = plt.subplots(2, 4, figsize=(20, 8))
for axx, n_hidden_nodes in zip(axes, [10, 100]):
for ax, alpha in zip(axx, [0.0001, 0.01, 0.1, 1]):
mlp = MLPClassifier(solver=lbfgs, random_state=0,
hidden_layer_sizes=[n_hidden_nodes, n_hidden_nodes],
alpha=alpha)
mlp.fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3, ax=ax)
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train, ax=ax)
ax.set_title("n_hidden=[{}, {}]
alpha={:.4f}".format(
n_hidden_nodes, n_hidden_nodes, alpha))

(4)相同參數,但不同隨機初始化的情況下學到的決策函數

fig, axes = plt.subplots(2, 4, figsize=(20, 8))
for i, ax in enumerate(axes.ravel()):
mlp = MLPClassifier(solver=lbfgs, random_state=i,
hidden_layer_sizes=[100, 100])
mlp.fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3, ax=ax)
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train, ax=ax)

控制神經網路複雜度的方法有很多種:隱層的個數、每個隱層中的單元個數與正則化(alpha)。神經網路的一個重要性質是,在開始學習之前其權重是隨機設置的,這種隨機初始化會影響學到的模型。也就是說,即使使用完全相同的參數,如果隨機種子不同的話,我們也可能得到非常不一樣的模型。

MLP的精度相當好,但沒有其他模型好。與較早的SVC例子相同,原因可能在於數據的縮放。神經網路也要求所有輸入特徵的變化範圍相似,最理想的情況是均值為0、方差為1。

神經網路調參的常用方法是,首先創建一個達到足以過擬合的網路,確保這個網路可以對任務進行學習。知道訓練數據可以被學習之後,要麼縮小網路,要麼增大alpha來增強正則化,這可以提高泛化性能。

在書中的實驗中,主要關注模型的定義:層數、每層的節點個數、正則化和非線性。這些內容定義了我們想要學習的模型。還有一個問題是,如何學習模型或用來學習參數的演算法,這一點由solver參數設定。solver有兩個好用的選項。默認選項是『adam』,在大多數情況下效果都很好,但對數據的縮放相當敏感(因此,始終將數據縮放為均值為0、方差為1是很重要的)。另一個選項是『lbfgs』,其魯棒性相當好,但在大型模型或大型數據集上的時間會比較長。還有更高級的『sgd』選項,許多深度學習研究人員都會用到。在開始使用MLP時,建議使用『adam』和『lbfgs』。

9.分類器的不確定度估計

(1)決策函數

# show the first few entries of decision_function
print("Decision function:
{}".format(gbrt.decision_function(X_test)[:6]))

Decision function:
[ 4.136 -1.702 -3.951 -3.626 4.29 3.662]

決策函數的值表示模型對該數據點屬於「正」類的置信程度。正值表示對正類的偏好,負值表示對「反類」(其他類)的偏好。

(2)預測概率

predict_proba的輸出是每個類別的概率,通常比decision_function的輸出更容易理解。對於二分類問題,它的形狀始終是(n_sample,2):

Shape of probabilities: (25, 2)

Predicted probabilities:
[[ 0.016 0.984]
[ 0.846 0.154]
[ 0.981 0.019]
[ 0.974 0.026]
[ 0.014 0.986]
[ 0.025 0.975]]

每行的第一個元素是第一個類別的估計概率,第二個元素是第二個類別的估計概率。由於predict_proba的輸出是一個概率,因此總是在0和1之間,兩個類別的元素之和始終為1。

總結

面對新數據集,通常最好先從簡單模型開始,比如線性模型、樸素貝葉斯或最近鄰分類器,看能得到什麼樣的結果。對數據有了進一步了解之後,你可以考慮用於構建更複雜模型的演算法,比如隨機森林、梯度提升決策樹、SVM或神經網路。

現在你應該對如何應用、調節和分析我們介紹過的模型有一定的了解。本章主要介紹了二分類問題,因為這通常是最容易理解的。不過本章大多數演算法都可以同時用於分類和回歸,而且所有分類演算法都可以同時用於二分類和多分類。你可以嘗試將這些演算法應用於scikit-learn的內置數據集,比如用於回歸的boston_housing或diabetes數據集,或者用於多分類的digits數據集。在不同的數據集上實驗這些演算法,可以讓你更好地感受它們所需的訓練時間、分析模型的難易程度以及它們對數據表示的敏感程度。

雖然我們分析了不同的參數設定對演算法的影響,但在生產環境中實際構建一個對新數據泛化性能很好的模型要更複雜一些。我們將在第6章介紹正確調參的方法和自動尋找最佳參數的方法。

幾個概念:

分類(classification)

回歸(regression)

區分分類任務和回歸任務的簡單方法:問一個問題:輸出是否具有某種連續性,如果在可能的結果之間具有連續性,那麼它就是一個回歸問題,否則是一個分類問題。

如果一個模型能夠對沒見過的數據做出準確預測,我們就說它能夠從訓練集泛化(generalize)到測試集。

構建一個對現有信息量來說過於複雜的模型,被稱為過擬合(overfitting)。在訓練集上表現很好、但不能泛化到新數據上的模型,那麼就存在過擬合。

選擇過於簡單的模型被稱為欠擬合(underfitting)

模型越複雜,在訓練數據上的預測結果就越好。但是,如果我們的模型過於複雜,我們開始過多關注訓練集中每個單獨的數據點,模型就不能很好地泛化到新數據上。二者存在一個最佳位置,可以得到最好地泛化性能。這就是我們想要的模型。

收集更多數據,適當構建更複雜的模型,對監督學習任務往往特別有用。在現實世界中,你往往能夠決定收集多少數據,這可能比模型調參更為有效。永遠不要低估更多數據的力量。


推薦閱讀:
相关文章