前言
隨機森林的出現,是為了解決決策樹對訓練數據過擬合的問題而出現的。決策樹在訓練的工程中,可以讓每一個葉子節點的不確定性降為0(即熵或者基尼指數為0),這樣做可能把訓練數據中的偶然性、異常值或噪聲也當成了“規 律”去學習了
對於複雜高維的數據,隨機森林的算法可以更好的泛化能力
開始探索
scikit-learn
老規矩,先上代碼,看看隨機森林的用法
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
clf = RandomForestClassifier(n_estimators=100, max_depth=3, random_state=0)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print("隨機森林 分類報告:\n", classification_report(y_test, y_pred))
腳本!啓動:

鳶尾花數據集
決策樹那篇文章演示剪枝的時候,就使用了load_iris,它是 Scikit-learn 自帶的一個內置數據集加載器函數,用於獲取“鳶尾花數據集”,該數據集的信息:
- 150條樣本
- 4個特徵,
sepal length,sepal width,petal length,petal width,分別代表萼片長度、萼片寬度、花瓣長度、花瓣寬度 - 3個分類,Setosa、Versicolor、Virginica,標籤值分別為
012
這是Scikit-learn提供的常用數據集,適用於學習、演示以及本人目前實在是找不到對應的數據來做練習 - -
深入理解隨機森林
隨機森林由許多決策樹組成,每棵樹都是在不同的樣本和特徵子集上訓練出來的。最終結果通過“投票”(分類)或“平均”(迴歸)方式整合
- Bootstrap採樣:有N條樣本,每次隨機抽取N條樣本,並且是有放回抽樣,那就意味着有些樣本可能被抽取多次,有些一次也沒有被抽取
- 假如原始集合\(D=\{x_1,x_2,x_3,x_4,x_5\}\),在一次有放回抽樣中,樣本為\(\{x_1,x_2,x_3,x_5,x_5\}\),那\(x_5\)被抽取了兩次,\(x_4\)一次都沒有被抽中,\(x_4\)被稱為OOB(out of bag)樣本
- 將抽取的樣本訓練成決策樹,在訓練的過程中,特徵並不是全部考慮的,而是隨機選取一部分特徵進行劃分,每棵樹訓練的過程中都不剪枝
- 訓練N棵樹之後之後
- 分類任務採用多數多數投票法,即少數服從多數。比如森林中有5棵決策樹,對於樣本進行預測,3個預測為
0,2個預測為1,那隨機森林對於該樣本的預測結果就是0 - 迴歸任務採用平均值,顧名思義,求和取平均
- 分類任務採用多數多數投票法,即少數服從多數。比如森林中有5棵決策樹,對於樣本進行預測,3個預測為
- 由於有一部分樣本(OOB樣本)一次也沒有被抽取,可以使用這些樣本進行評估,從而在不使用額外驗證集的情況下評估模型性能
理解了決策樹,就理解了隨機森林。決策樹,學習規則,直接預測結果;多棵決策樹組成的集成模型,通過投票(分類)或平均(迴歸)得出最終結果
方差
方差是衡量數據離散程度的數學特徵,方差越大,數據越分散,波動越大;方差越小,數據越集中,波動越小。在機器學習中,方差反應了模型對於訓練數據變化的敏感程度,方差越大,對於噪聲的變化越敏感,容易過擬合;方差越小,對於數據變化不敏感,泛化能力更強,但是有欠擬合的風險
決策樹就是方差較大的模型,因為決策樹會傾向於熵/基尼係數為0,它會一直分裂,導致樹非常的深,並且數據稍有變化,就產生了完全不同的樹結構。剪枝可以有效的避免高方差,預剪枝通過參數(最大深度,最小樣本分裂數等),控制數的深度,後剪枝可以參數(ccp_alpha)減掉樹枝,避免樹過深
隨機森林中是方差較小的模型,注意得益於它的抽樣方式,通過Bootstrap採樣,減少了特定訓練數據的依賴,再加上最後分類是通過所有樹投票,降低了單棵樹帶來的波動。其次不是所有特徵,而是隨機特徵,帶來樹分裂的次數 減少
常用參數
| 參數 | 含義 | 備註 |
|---|---|---|
| n_estimators | 森林中樹的數量 | 默認 100,建議:100~500,越多越穩但訓練慢 |
| max_depth | 每棵樹的最大深度 | 控制過擬合;常設為 5~30;默認 None |
| min_samples_split | 節點最小分裂樣本數 | 控制過擬合,默認 2,設置大些可防止過擬合 |
| min_samples_leaf | 每個葉節點最少樣本數 | 默認 1,設置大些讓樹更“平滑”,防止過擬合 |
| max_features | 每次分裂考慮的特徵數 | 分類常用 'sqrt',迴歸常用 'auto' 或 'log2' |
| bootstrap | 是否啓用 Bootstrap 採樣 | 默認 True;設為 False 表示使用全部數據(不推薦) |
超參數調優
在之前的篇章中,曾經提到過超參數的概念,這裏再來詳細討論一下:超參數是模型訓練之前就要設置的,模型無法自己去學習超參數,超參數往往決定着模型的泛化能力。比如上述的n_estimators max_depth等,合理的設置這些參數能夠提高模型的泛化能力,減少過擬合
網格搜索
一種窮舉搜索的方法,用來尋找在給定超參數空間中,使模型性能最優的參數組合。它嘗試所有可能的參數組合,並對每一組組合進行模型訓練與評估
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
rf = RandomForestClassifier(random_state=0)
param_grid = {
'n_estimators': [50, 100, 150],
'max_depth': [None, 5, 10],
'min_samples_split': [2, 4]
}
grid_search = GridSearchCV(
estimator=rf,
param_grid=param_grid,
cv=5,
scoring='accuracy',
n_jobs=-1
)
grid_search.fit(X_train, y_train)
print("最優參數組合:", grid_search.best_params_)
3個參數,n_estimators,max_depth,min_samples_split,分別將對應的取值進行窮舉搜索訓練,訓練的參考準確率(accuracy),誰的準確率高,就使用對應的參數組合。在訓練的過程中使用5折交叉驗證
腳本!啓動

隨機搜索
從給定的超參數空間中隨機採樣若干組參數組合進行訓練和評估
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV, train_test_split
from sklearn.metrics import classification_report, accuracy_score
from scipy.stats import randint
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
rf = RandomForestClassifier(random_state=0)
param_dist = {
'n_estimators': randint(50, 200),
'max_depth': [None, 5, 10, 20],
'min_samples_split': randint(2, 10),
'max_features': ['sqrt', 'log2', None]
}
random_search = RandomizedSearchCV(
estimator=rf,
param_distributions=param_dist,
n_iter=20,
cv=5,
scoring='accuracy',
random_state=0,
n_jobs=-1
)
random_search.fit(X_train, y_train)
print("最優參數組合:", random_search.best_params_)
n_iter是比較重要的參數,代碼中它限制了要嘗試20種不同的參數組合,在這其中選擇準確率最高的

貝葉斯優化
當面對海量的參數時,窮舉參數組合顯然不再適合,而貝葉斯優化的做法構造一個模型,該模型用來模擬:超參數與用這一組超參數訓練隨機森林之後的準確率之間的關係,從而智能地選擇下一組超參數進行評估。在每次嘗試的過程中,都會記錄超參數與隨機森林準確率,最終選擇準確率最高的那一組超參數
與隨機搜索的不同,就在於超參數的選擇,隨機搜索顧名思義,採取隨機的組合方式選擇參數,而貝葉斯則是選擇參數的過程是構建摸一個預測模型,對超參數和模型性能之間的關係進行建模,用模型來指導下一組參數應該選什麼,從而更快、更高效地找到最優組合
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from skopt import BayesSearchCV
from skopt.space import Real, Integer, Categorical
X, y = load_iris(return_X_y=True)
model = RandomForestClassifier(random_state=0)
search_space = {
'n_estimators': Integer(50, 200),
'max_depth': Integer(2, 20),
'min_samples_split': Integer(2, 10),
'max_features': Categorical(['sqrt', 'log2', None])
}
opt = BayesSearchCV(
estimator=model,
search_spaces=search_space,
n_iter=30,
cv=5,
scoring='accuracy',
n_jobs=-1,
random_state=0
)
opt.fit(X, y)
print("最優參數:", opt.best_params_)

小結
總結一下三種超參數調優的方法
| 方法 | 思想 | 優點 | 缺點 | 使用場景 |
|---|---|---|---|---|
| 網格搜索 | 窮舉所有可能的參數組合 | 簡單直觀,易於並行 | 參數過多導致計算代價高,無學習能力 | 參數較少,對性能要求不敏感 |
| 隨機搜索 | 隨機採樣參數組合 | 比網格搜索高效 | 不利用歷史信息採樣,隨機性大 | 參數空間大,資源有限 |
| 貝葉斯優化 | 構建一個代理模型來預測每組參數組合的性能,並基於此智能選擇下一個組合 | 樣本效率高,可快速收斂最優解,特別適合開銷大的模型 | 實現複雜,開銷大 | 超參數空間大且訓練開銷高,希望精細調優性能 |
聯繫我
- 聯繫我,做深入的交流

至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教...