集成學習:隨機森林和GBDT
[外鏈圖片轉存失敗(img-23PmTXbA-1567001324533)(images/suijisenlin.gif)]
王境澤的機器學習技巧
什麼是集成學習(Voting Classifier)?
同一數據,同時應用多種差異模型,將預測結果用某種方式投票選出最佳結果
例如:新出的電影好不好看?根據其他人評價自行判斷
日常工作應用中,監督學習算法的選擇:
- 如果爲了模型的可解釋性,如數據分析報告(準確率不重要),一般使用獨立模型(線性迴歸和邏輯迴歸)
- 如果爲了模型的性能
- 中小型數據(表格):集成學習
- 大型/海量數據(圖片,音頻、視頻):深度學習
算法準確率:集成學習(隨機森林,GBDT)是僅次於深度學習的第二大算法
- 深度學習需要海量數據支持,集成學習不需要大量數據,更簡單,應用更廣泛
- 非常常用
- 其他機器學習算法無論性能還是可解釋性都不如上述三類算法,實際工作很少使用
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
# 噪點參數0.3
X, y = datasets.make_moons(n_samples=500, noise=0.3, random_state=42)
plt.scatter(X[y==0,0], X[y==0,1], alpha=0.3)
plt.scatter(X[y==1,0], X[y==1,1], alpha=0.3)
<matplotlib.collections.PathCollection at 0x11906e80>
[外鏈圖片轉存失敗(img-jRiZvp8T-1567001324539)(output_4_1.png)]
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
from sklearn.neighbors import KNeighborsClassifier # KNN
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train, y_train)
knn_clf.score(X_test, y_test)
0.912
from sklearn.tree import DecisionTreeClassifier # 決策樹
dt_clf = DecisionTreeClassifier(random_state=666)
dt_clf.fit(X_train, y_train)
dt_clf.score(X_test, y_test)
0.864
from sklearn.linear_model import LogisticRegression # 邏輯迴歸
log_clf = LogisticRegression(solver='lbfgs')
log_clf.fit(X_train, y_train)
log_clf.score(X_test, y_test)
0.864
手動集成三種算法學習結果
y_predict1 = knn_clf.predict(X_test)
y_predict1
y_predict2 = dt_clf.predict(X_test)
y_predict2
y_predict3 = log_clf.predict(X_test)
y_predict3
array([1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0,
1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0], dtype=int64)
# 000,001 011,111
y_predict1 + y_predict2 + y_predict3
# 0,1, 2, 3
# 0, 1 = 0
# 2, 3 = 1
y_predict = ((y_predict1 + y_predict2 + y_predict3) >= 2).astype(np.int)
y_predict
array([1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0,
1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0])
from sklearn.metrics import accuracy_score
accuracy_score(y_test, y_predict)
0.92
使用Voting Classifier自動集成學習
建議先分別執行各個算法,調好參數後再統一集成到一起
from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(
estimators=[
('df_clf', DecisionTreeClassifier(random_state=222)),
('knn_clf', KNeighborsClassifier()),
('log_clf', LogisticRegression(solver='lbfgs')),
],
voting='hard'
)
voting_clf.fit(X_train, y_train)
VotingClassifier(estimators=[('df_clf',
DecisionTreeClassifier(class_weight=None,
criterion='gini',
max_depth=None,
max_features=None,
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
min_samples_leaf=1,
min_samples_split=2,
min_weight_fraction_leaf=0.0,
presort=False,
random_state=222,
splitter='best')),
('knn_clf',
KNeighborsClassifier(a...
n_jobs=None, n_neighbors=5,
p=2, weights='uniform')),
('log_clf',
LogisticRegression(C=1.0, class_weight=None,
dual=False, fit_intercept=True,
intercept_scaling=1,
l1_ratio=None, max_iter=100,
multi_class='warn',
n_jobs=None, penalty='l2',
random_state=None,
solver='lbfgs', tol=0.0001,
verbose=0,
warm_start=False))],
flatten_transform=True, n_jobs=None, voting='hard',
weights=None)
voting_clf.predict(X_test)
array([1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0,
0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0,
1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0], dtype=int64)
voting_clf.score(X_test, y_test)
0.928
參數voting值
- hard
- soft
例:某數據有2種分類,使用五種模型訓練並預測,得到結果爲:
模型1:A 99%;B 1%
模型2:A 49%;B 51%
模型3:A 40%;B 60%
模型4:A 90%;B 10%
模型5:A 30%;B 70%
- hard voting模式:考慮投票數。
- A:2票
- B:3票
- 最終結果爲B
- soft voting模式:考慮投票權重
- A = (0.99+0.49+0.4+0.9+0.3)/5 = 0.616
- B = (0.01+0.51+0.6+0.1+0.7)/5 = 0.384
- 最終結果爲A
soft voting要求集成的每個模型都能估計概率,否則無法運算(kNN、決策樹、邏輯迴歸都可以)
voting_clf = VotingClassifier(
estimators=[
('df_clf', DecisionTreeClassifier(random_state=666)),
('knn_clf', KNeighborsClassifier()),
('log_clf', LogisticRegression(solver='lbfgs')),
],
voting='soft',
)
voting_clf.fit(X_train, y_train)
voting_clf.score(X_test, y_test)
0.888
更好的集成學習方式
現有的集成學習方式,能用於集成的模型太少,想要提高集成學習的準確率需要:
- 創建和集成更多子模型
- 子模型之間不能一致,要有差異性
子模型不需要太高的準確率,只要足夠多,就可以極大提升最終模型準確率
例如:每個子模型有60%準確率
- 如果只有一個子模型,準確率60%
- 如果有3個子模型:$ 0.6{3}+c{2}_{3}\cdot 0.6^{2}\cdot 0.4 =0.648 $
- 如果有500個子模型:$ \sum {500}_{i=251}C{i}_{500}\cdot 0.6^{i}\cdot 0.4^{500-i} =0.9999 $
注意:子模型的準確率最好高於平均準確率
0.6 ** 3 + (2*3/2*1) * 0.6**2 * 0.4
0.648
如何產生大量有差異的子模型?
- 機器學習算法就那麼點,就算所有算法和所有算法的參數都用上,也無法生成大量子模型
- 隨機抽樣方式,每個子模型只抽取一部分樣本進行訓練
- 例如:所有子模型都用一個算法,訓練集500條數據,每個子模型隨機抽取100條進行訓練,可以生成任意多個子模型
- 子模型算法基本只使用決策樹算法,因爲決策樹大量的剪枝方式可以生成差異更大的模型,優於其他模型
兩種採樣方式:
Bagging:放回採樣(bootstrap),更常用,能製造更多子模型
Pasting:不放回採樣
使用Bagging方式集成學習
這裏Bagging方式集成學習只使用決策樹模型,
通過改變訓練數據生成子模型,決策樹大量的剪枝方式可以生成差異更大的模型,優於其他模型
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(), # 集成的模型
n_estimators=500, # 集成多少個子模型
max_samples=100, # 每個子模型需要多少訓練數據
bootstrap=True # 放回抽樣
)
bagging_clf.fit(X_train, y_train)
bagging_clf.score(X_test, y_test)
0.92
子模型越多,準確率越高,模型訓練越慢
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(), # 集成的模型
n_estimators=5000, # 集成多少個子模型
max_samples=100, # 每個子模型需要多少訓練數據
bootstrap=True) # 放回採樣
bagging_clf.fit(X_train, y_train)
bagging_clf.score(X_test, y_test)
0.912
沒有99%那麼多,因爲一些子模型準確率會低於均值,數據抽取次數過多,有大量相似模型
OOB:out of bag
放回採樣導致一部分樣本始終沒有被用於訓練
平均約有37%的樣本沒有被用到
所以不需要分離測試集,直接使用沒有被用過的樣本數據做測試集即可
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
bootstrap=True,
oob_score=True) # OOB爲True
bagging_clf.fit(X, y) # 訓練數據採用全部樣本數據,不需要測試集
bagging_clf.oob_score_ # 調用OOB方法計算準確率
0.916
n_jobs 並行化處理
Bagging非常易於並行化處理
每個子模型可以獨立訓練,使用獨立cpu內核,加快速度
注意:如果訓練時間非常久,同時使用所有內核 n_jobs=-1, 非常容易卡死,訓練完成後才能恢復運算
%%time
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
bootstrap=True,
oob_score=True)
bagging_clf.fit(X, y)
Wall time: 2.82 s
BaggingClassifier(base_estimator=DecisionTreeClassifier(class_weight=None,
criterion='gini',
max_depth=None,
max_features=None,
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
min_samples_leaf=1,
min_samples_split=2,
min_weight_fraction_leaf=0.0,
presort=False,
random_state=None,
splitter='best'),
bootstrap=True, bootstrap_features=False, max_features=1.0,
max_samples=100, n_estimators=500, n_jobs=None,
oob_score=True, random_state=None, verbose=0,
warm_start=False)
%%time
bagging_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
bootstrap=True,
oob_score=True,
n_jobs=-1) # 使用全部cpu內核
bagging_clf.fit(X, y)
更多Bagging方式
除了針對樣本數據進行隨機採樣,還有更多方式
- 針對特徵進行隨機採樣,Random Subspaces
- 既針對樣本、又針對特徵進行隨機採樣,Random Patches
bootstrap_features 針對特徵隨機取樣
random_subspaces_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=500, # 關閉樣本隨機採樣,因爲子模型需要數據,設爲全部500條,不再選取部分數據
bootstrap=True,
oob_score=True,
max_features=1, # 隨機取1列特徵(因爲數據一共就2列特徵)
bootstrap_features=True) # 對特徵隨機採樣,放回採樣
random_subspaces_clf.fit(X, y)
random_subspaces_clf.oob_score_
0.834
既對樣本、又對特徵進行隨機採樣
random_patches_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100, # 打開樣本隨機採樣
bootstrap=True,
oob_score=True,
max_features=1, # 隨機取1列特徵(因爲數據一共就2列特徵)
bootstrap_features=True) # 對特徵隨機採樣,放回採樣
random_patches_clf.fit(X, y)
random_patches_clf.oob_score_
0.856
隨機森林
上面使用決策樹、以Bagging集成學習的方式,就是隨機森林
除了手動集成學習外,sklearn自帶一個隨機森林類,可以方便的創建隨機森林模型
隨機森林模型集成了決策樹和Bagging分類器,所以擁有決策樹和BaggingClassifier的所有參數
############################################################
from sklearn.ensemble import RandomForestClassifier
rf_clf = RandomForestClassifier(
n_estimators=500, # 500棵樹
oob_score=True, # 使用未被抽取的數據做測試集
random_state=666, # 隨機數種子固定
# n_jobs=-1) # 所有cpu內核並行運算
)
rf_clf.fit(X, y)
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=500,
n_jobs=None, oob_score=True, random_state=666, verbose=0,
warm_start=False)
rf_clf.oob_score_
0.896
調節參數,提升準確率
rf_clf2 = RandomForestClassifier(
n_estimators=500,
max_leaf_nodes=16, # 每個決策樹最多有幾個葉子節點
oob_score=True,
random_state=666,
n_jobs=-1)
rf_clf2.fit(X, y)
rf_clf2.oob_score_
0.92
# help(RandomForestClassifier)
集成學習解決迴歸問題的子庫
from sklearn.ensemble import BaggingRegressor
from sklearn.ensemble import RandomForestRegressor # 迴歸隨機森林
上面的集成學習算法叫Bagging
使用Bagging方式集成學習
- Bagging:集成多個模型(並行)
- 數據集每次抽取若干數據,用於子模型
- 若干子模型同時進行訓練,和預測
- 採用投票方式算出集成後的預測結果
- Boosting:每個模型都在嘗試增強(Boosting)整體效果(串行)
- 同一時間只有一個模型,只使用一個數據集
- 每次訓練預測完成後,增加錯誤數據權重,降低預測正確數據權重,然後重新訓練
- 迭代循環,直到準確率達到一定標準
Ada Boosting
[外鏈圖片轉存失敗(img-aBQMuefN-1567001324544)(images/adaboosting.png)]
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
ada_clf = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=2), # 基礎學習算法
n_estimators=500)
ada_clf.fit(X_train, y_train)
AdaBoostClassifier(algorithm='SAMME.R',
base_estimator=DecisionTreeClassifier(class_weight=None,
criterion='gini',
max_depth=2,
max_features=None,
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
min_samples_leaf=1,
min_samples_split=2,
min_weight_fraction_leaf=0.0,
presort=False,
random_state=None,
splitter='best'),
learning_rate=1.0, n_estimators=500, random_state=None)
ada_clf.score(X_test, y_test)
0.856
Gradient Boosting,(GBDT,更強)
- 訓練模型m1,產生錯誤e1
- 針對e1訓練模型m2,產生錯誤e2
- 針對e2訓練模型m3, 產生錯誤e3…
- 最終預測結果是:m1+m2+m3…
[外鏈圖片轉存失敗(img-eu2N16PR-1567001324550)(images/gboosting.png)]
from sklearn.ensemble import GradientBoostingClassifier
# Gradient Boosting就是以決策樹爲基礎的,不需要寫,設好集成子模型數量就行
gb_clf = GradientBoostingClassifier(max_depth=3, n_estimators=30)
gb_clf.fit(X_train, y_train)
gb_clf.score(X_test, y_test)
0.92
總結
scikit-learn自帶的常用集成算法:
- Bagging:隨機森林
- Boosting:GBDT
不過實際工作和比賽中,我們更常用的時專用集成學習庫
其他常用Boosting算法實現,如:XGboost,LightGBM(這兩個庫都是Boosting算法中的GBDT第三方實現),廣泛應用於各種數據科學機器學習競賽中
- 運算速度更快
- 準確率更高
sklearn不帶,需要另行安裝