集成(ensemble)是合併多個機器學習模型來構建更強大的模型的方法。主要須要解決兩個問題:node
個體學習器能夠分爲同質和異質兩種:python
目前來講,同質個體學習器的應用是最普遍的,通常咱們常說的集成學習的方法都是指的同質個體學習器,而同質個體學習器使用最多的模型是CART決策樹和神經網絡。算法
同質學習器按照個體學習器之間是否存在依賴關係可分爲兩類:緩存
以下圖所示,bagging的個體學習器使用的訓練集由隨機採樣得到,分別訓練後集成。網絡
隨機採樣最經常使用的方法是自助採樣,其方法是在m個樣本中有放回的隨機採樣m次以得到m個樣本的訓練集。其中,某個樣本沒有被使用的機率是$$P=(1-\frac1m)^m$$令m趨向於正無窮可得$$\lim _{m\rightarrow+\infty}P=\frac 1e$$即大約36.8%的數據不會被取到。併發
bagging的結合策略是簡單的投票法和算術平均值,因爲Bagging算法每次都進行採樣來訓練模型,所以泛化能力很強,對於下降模型的方差頗有做用。固然對於訓練集的擬合程度就會差一些,也就是模型的偏倚會大一些。app
隨機森林是使用cart決策樹做爲弱學習器的使用bagging方法的集成模型,可是對cart決策樹進了改進以加強泛化能力。Cart樹在每一個節點進行二分時,選擇的是最優的特徵,即劃分後基尼係數最小或方差最小的特徵,決策樹中的弱學習器在選擇特徵時,先隨機選擇k個特徵,而後在這k個特徵中選擇最優特徵。框架
import numpy as np import matplotlib.pyplot as plt %matplotlib inline import warnings warnings.filterwarnings('ignore') #在two_moon數據集上構造5棵樹組成的隨機森林 import seaborn as sns import mglearn from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import make_moons from sklearn.model_selection import train_test_split 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) RFC = RandomForestClassifier(n_estimators=5,random_state=2).fit(X_train,y_train) #將每棵樹及總預測可視化 fig,axes = plt.subplots(2,3,figsize=(20,10)) for i,(ax,tree) in enumerate(zip(axes.ravel(),RFC.estimators_)): ax.set_title('Tree{}'.format(i)) mglearn.plots.plot_tree_partition(X_train,y_train,tree,ax=ax) mglearn.plots.plot_2d_separator(RFC,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)
能夠看出,5棵決策樹結果徹底不一樣,綜合後獲得隨機森林。dom
#在乳腺癌數據上構建隨機森林 from sklearn.datasets import load_breast_cancer cancer = load_breast_cancer() X_train,X_test,y_train,y_test=train_test_split(cancer.data,cancer.target,stratify=cancer.target,random_state=0) RFC = RandomForestClassifier(n_estimators=100,max_features=5,oob_score=True,random_state=88).fit(X_train,y_train) print('訓練精度:{}'.format(RFC.score(X_train,y_train))) print('測試精度:{}'.format(RFC.score(X_test,y_test))) print('袋外分數:{}'.format(RFC.oob_score_))
訓練精度:1.0 測試精度:0.958041958041958 袋外分數:0.9624413145539906
隨機森林的特徵重要性:機器學習
fig = plt.figure(dpi=100) plt.barh(range(len(RFC.feature_importances_)),RFC.feature_importances_) plt.yticks(range(len(RFC.feature_importances_)),cancer.feature_names) plt.xlabel("Feature Importance") plt.show()
Bagging框架參數:
決策樹參數
決策樹參數中最重要的是前四個,後三個通常不會使用。
優勢
缺點:
以下圖所示,boosting的基本思想是,先訓練出一個模型1,此時,每一個樣本的權重是相同的,都是樣本個數的倒數,計算學習偏差率e1,而後根據學習結果來更新樣本權重,增長被錯誤分類的樣本的權重,訓練模型2,計算學習偏差率e2。重複該過程以得到T個個體學習器,而後使用結合策略結合成強學習器。所以,boosting系列算法須要解決如下四個問題:
Adaboost迴歸有不少種方法,這裏只介紹分類。
Adaboost二分類中標籤y取值爲1,-1
Adaboost框架參數
Adaboost分類器調參較爲簡單,框架參數基本上只須要調節學習率和最大迭代次數。
下面是sklearn實現分類,首先導入相關包並建立數據,數據是二維數據,分佈如圖所示:
from sklearn.ensemble import AdaBoostClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.datasets import make_gaussian_quantiles # 生成2維正態分佈,生成的數據按分位數分爲兩類,500個樣本,2個樣本特徵,協方差係數爲2 X1, y1 = make_gaussian_quantiles(cov=2.0,n_samples=500, n_features=2,n_classes=2, random_state=1) # 生成2維正態分佈,生成的數據按分位數分爲兩類,400個樣本,2個樣本特徵均值都爲3,協方差係數爲2 X2, y2 = make_gaussian_quantiles(mean=(3, 3), cov=1.5,n_samples=400, n_features=2, n_classes=2, random_state=1) #將兩組數據合成一組數據 X = np.concatenate((X1, X2)) y = np.concatenate((y1, - y2 + 1)) plt.scatter(X[:, 0], X[:, 1], marker='o', c=y) plt.show()
#訓練模型 ada = AdaBoostClassifier(DecisionTreeClassifier(max_depth=10,min_samples_leaf=5),n_estimators=600,learning_rate=0.7).fit(X,y) print(ada.score(X,y)) #繪圖 x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02)) Z = ada.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) cs = plt.contourf(xx, yy, Z, cmap=plt.cm.Paired) plt.scatter(X[:, 0], X[:, 1], marker='o', c=y) plt.show()
1.0
優勢:
缺點:
梯度提高樹的原理是,構造一系列決策樹,每一棵樹在前一棵基礎上進行修正,以此來得到效果較好的決策樹。舉個例子,一我的150kg,先訓練一個模型,該模型爲第T個模型,用這個模型估計他的體重爲140kg,誤差爲10kg,而後再訓練一個模型t,用於估計誤差,誤差的估計值爲8kg,而後將這兩個模型加起來做爲T+1個模型,來估計體重。
首先訓練一個Cart樹$$f_0(x) = \underbrace{arg\; min}_{c}\sum\limits_{i=1}^{m}L(y_i, c)$$其損失函數爲$$L=\sum \frac12[f(x_i)-y_i]^2$$其負梯度爲$$r_{ti} = -\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)}=y_i-f(x_i)$$計算每條數據的負梯度,獲得數據集$$(x_i,r_{ti})\;\; (i=1,2,..m)$$負梯度恰好是實際值與模型擬合值之間的誤差,再訓練一個模型用於擬合這個誤差$$c_{tj} = \underbrace{arg\; min}_{c}\sum\limits_{x_i \in R_{tj}} L(y_i,f_{t-1}(x_i) +c)$$而後更新學習器$$f_{t}(x) = f_{t-1}(x) + \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})$$
獲得學習器t,重複此過程直到結束,最終預測值爲$$f(x) = f_T(x) =f_0(x) + \sum\limits_{t=1}^{T}\sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})$$
分類的原理與迴歸是相似的,只是損失函數不一樣,致使負梯度擬合不一樣,其餘步驟相同。損失函數使用似然函數。
二元損失函數$$L(y, f(x)) = log(1+ exp(-yf(x)))$$$$r_{ti} = -\bigg[\frac{\partial L(y, f(x_i)))}{\partial f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)} = y_i/(1+exp(y_if(x_i)))$$多元損失函數$$L(y, f(x)) = - \sum\limits_{k=1}^{K}y_klog\;p_k(x)$$
框架參數
對於分類模型,有對數似然損失函數"deviance"和指數損失函數"exponential"二者輸入選擇。默認是對數似然損失函數"deviance"。通常來講,推薦使用默認的"deviance"。它對二元分離和多元分類各自都有比較好的優化。而指數損失函數等於把咱們帶到了Adaboost算法。
對於迴歸模型,有均方差"ls", 絕對損失"lad", Huber損失"huber"和分位數損失「quantile」。默認是均方差"ls"。通常來講,若是數據的噪音點很少,用默認的均方差"ls"比較好。若是是噪音點較多,則推薦用抗噪音的損失函數"huber"。而若是咱們須要對訓練集進行分段預測的時候,則採用「quantile」。
弱學習器是Cart樹,參數與cart樹相同。
下面是調參實踐,首先訓練一個默認模型:
#梯度提高樹 from sklearn.ensemble import GradientBoostingClassifier from sklearn.metrics import roc_auc_score gbdt=GradientBoostingClassifier(random_state=10).fit(X_train,y_train) print('訓練精度:{}'.format(gbdt.score(X_train,y_train))) print('測試精度:{}'.format(gbdt.score(X_test,y_test))) print('測試AUC:{}'.format(roc_auc_score(y_test,gbdt.predict_proba(X_test)[:,1])))
訓練精度:1.0 測試精度:0.958041958041958 測試AUC:0.978511530398323
而後令學習率爲0.1,尋找最優的學習器個數,爲80個。
from sklearn.model_selection import GridSearchCV param_test1 = {'n_estimators':range(20,101,10)} gsearch1 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1, max_depth=8, random_state=10), param_grid = param_test1, scoring='roc_auc',iid=False,cv=5).fit(X_train,y_train) gsearch1.best_params_,gsearch1.best_score_
({'n_estimators': 80}, 0.9836974622979643)
使用以上兩參數,來網格搜索最大深度和最小葉片分割數以及最小葉片數據量。
param_test2 = {'max_depth':range(2,14,2), 'min_samples_split':range(10,41,5), 'min_samples_leaf':[5,41,5]} gsearch2 = GridSearchCV(GradientBoostingClassifier(n_estimators=80,learning_rate=0.1,random_state=10), param_test2,scoring='roc_auc',cv=5).fit(X_train,y_train) gsearch2.best_params_,gsearch2.best_score_
({'max_depth': 10, 'min_samples_leaf': 5, 'min_samples_split': 15}, 0.9939034991321204)
使用新模型參數預測:
gbdt_grided = GradientBoostingClassifier(max_depth=10,min_samples_leaf=5,min_samples_split=10, n_estimators=160,learning_rate=0.05,random_state=10).fit(X_train,y_train) print('訓練集AUC{}'.format(roc_auc_score(y_train,gbdt_grided.predict_proba(X_train)[:,1]))) print('測試集AUC{}'.format(roc_auc_score(y_test,gbdt_grided.predict_proba(X_test)[:,1])))
訓練集AUC1.0 測試集AUC0.9865828092243186
優勢:
缺點:
XGBoost是GBDT的一個高效實現,主要從下面三個方面作了優化:
Boosting加法模型能夠表示爲$$\hat y_i=\sum _{k=1}^Kf_k(x_i)$$其中K爲弱學習器個數,整個模型的損失函數爲$$L=\sum_{i=1}^n l(y_i,\hat y_i)$$n爲樣本容量,上一節所說的GBDT使用的是MSE做爲損失函數,固然,也能夠用其餘損失函數,考慮正則化項,損失函數變爲$$L=\sum _{i=1}^nl(y_i,\hat y_i)+\sum_{k=1}^K\Omega (f_k)$$因爲$$\hat y_i^t=\hat y_i^{t-1}(x_i)+f_t(x_i)$$其中$f_t(x_i)$就是第t個新加入的模型,所以$$L=\sum _{i=1}^nl(y_i,\hat y_i^{t-1}+f_t)+\sum_{k=1}^K\Omega (f_k)$$
由泰勒定理,$$f(x)=\sum_{i=0}^n\frac {f^{(i)}(a)}{i!}(x-a)^i+R_{n+1}(x)$$其中$R_{n+1}(x)$爲$x^n$的高階無窮小,當n = 2時$$f(x+\delta x)=f(x)+f'(x)\delta x+\frac 12f''(x)\delta x^2$$
作以下替換$$\hat y_i^{t-1}=x,f_t=\delta x$$得$$L=\sum _{i=1}^n[l(y_i,\hat y_i^{t-1})+g_i\delta f_t+\frac 12h_i\delta f_t^2]+\sum_{k=1}^K\Omega (f_k)$$
其中$$g_i=\frac{\partial l(y_i,\hat y_i^{t-1})}{\partial\hat y_i^{t-1}} ,h_i=\frac{\partial^2 l(y_i,\hat y_i^{t-1})}{\partial[\hat y_i^{t-1}]^2}$$
因爲已經訓練完成第t-1個模型,所以,上一個模型的損失函數爲常數,最終第t個模型的損失函數轉化爲$$\begin{split} L&\approx\sum_{i=1}^n(g_if_t+\frac12h_if_t^2)+\sum_{k=1}^K\Omega(f_k)\\&= \sum_{i=1}^n \left [ g_iw_{q(x_i)}+\frac12h_iw_{q(x_i)}^2 \right] + \gamma T + \frac12 \lambda \sum_{j=1}^T w_j^2 \\&= \sum_{j=1}^T \left[(\sum_{i \in I_j}g_i)w_j + \frac12(\sum_{i \in I_j}h_i + \lambda)w_j^2 \right]+ \gamma T\end{split}$$其中因爲決策樹同節點中的預測值相等,有$$f_t(x)=w_{q(x)}$$
令$$G_j=\sum_{i \in I_j}g_i,H_j=\sum_{i \in I_j}h_i$$則$$L = \sum_{j=1}^T \left[G_iw_j + \frac12(H_i + \lambda)w_j^2 \right] + \gamma T$$
令一階偏導爲0得$$w_j^*=-\frac{G_j}{H_j+\lambda}$$此時$$L = -\frac12 \sum_{j=1}^T \frac{G_j^2}{H_j+\lambda} + \gamma T$$
以上就是模型的損失函數,在構建決策樹模型時,使用L代替MSE進行分叉。
import xgboost as xgb from sklearn.datasets.samples_generator import make_classification from sklearn.model_selection import GridSearchCV # X爲樣本特徵,y爲樣本類別輸出, 共10000個樣本,每一個樣本20個特徵,輸出有2個類別,沒有冗餘特徵,每一個類別一個簇 X, y = make_classification(n_samples=10000, n_features=20, n_redundant=0, n_clusters_per_class=1, n_classes=2, flip_y=0.1) X_train, X_test, y_train, y_test = train_test_split(X,y,stratify = y,random_state=1) sklearn_xgb = xgb.XGBClassifier(learning_rate= 0.5, verbosity=1, objective='binary:logistic',random_state=1) sklearn_xgb.fit(X_train, y_train, early_stopping_rounds=10, eval_metric="error", eval_set=[(X_test, y_test)]) gsCv = GridSearchCV(sklearn_xgb, {'max_depth': [4,5,6], 'n_estimators': [5,10,20]}) gsCv.fit(X_train,y_train) print(gsCv.best_score_) print(gsCv.best_params_)
[0] validation_0-error:0.1248 Will train until validation_0-error hasn't improved in 10 rounds. [1] validation_0-error:0.116 [2] validation_0-error:0.116 [3] validation_0-error:0.116 [4] validation_0-error:0.1164 [5] validation_0-error:0.1164 [6] validation_0-error:0.1164 [7] validation_0-error:0.116 [8] validation_0-error:0.1164 [9] validation_0-error:0.1184 [10] validation_0-error:0.118 [11] validation_0-error:0.1172 Stopping. Best iteration: [1] validation_0-error:0.116 0.8838666666666667 {'max_depth': 5, 'n_estimators': 5}
對於XGBoost的框架參數,最重要的是3個參數: booster,n_estimators和objectve。
這裏只討論使用gbtree默認弱學習器的參數。要調參的參數主要是決策樹的相關參數以下:
XGBoost還有一些其餘的參數須要注意,主要是learning_rate。
learning_rate控制每一個弱學習器的權重縮減係數,和sklearn GBDT的learning_rate相似,較小的learning_rate意味着咱們須要更多的弱學習器的迭代次數。一般咱們用步長和迭代最大次數一塊兒來決定算法的擬合效果。因此這兩個參數n_estimators和learning_rate要一塊兒調參纔有效果。固然也能夠先固定一個learning_rate,而後調完n_estimators,再調完其餘全部參數後,最後再來調learning_rate和n_estimators。
此外,n_jobs控制算法的併發線程數, scale_pos_weight用於類別不平衡的時候,負例和正例的比例。相似於sklearn中的class_weight。importance_type則能夠查詢各個特徵的重要性程度。能夠選擇「gain」, 「weight」, 「cover」, 「total_gain」 或者 「total_cover」。最後能夠經過調用booster的get_score方法獲取對應的特徵權重。「weight」經過特徵被選中做爲分裂特徵的計數來計算重要性,「gain」和「total_gain」則經過分別計算特徵被選中作分裂特徵時帶來的平均增益和總增益來計算重要性。「cover」和 「total_cover」經過計算特徵被選中作分裂時的平均樣本覆蓋度和整體樣本覆蓋度來來計算重要性。
from lightgbm import LGBMRegressor from sklearn.metrics import mean_squared_error from sklearn.model_selection import GridSearchCV from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.externals import joblib # 加載數據 iris = load_iris() data = iris.data target = iris.target # 劃分訓練數據和測試數據 X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2) # 模型訓練 gbm = LGBMRegressor(objective='regression', num_leaves=31, learning_rate=0.05, n_estimators=20) gbm.fit(X_train, y_train, eval_set=[(X_test, y_test)], eval_metric='l1', early_stopping_rounds=5) # 模型存儲 joblib.dump(gbm, 'loan_model.pkl') # 模型加載 gbm = joblib.load('loan_model.pkl') # 模型預測 y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration_) # 模型評估 print('The rmse of prediction is:', mean_squared_error(y_test, y_pred) ** 0.5) # 特徵重要度 print('Feature importances:', list(gbm.feature_importances_)) # 網格搜索,參數優化 estimator = LGBMRegressor(num_leaves=31) param_grid = { 'learning_rate': [0.01, 0.1, 1], 'n_estimators': [20, 40] } gbm = GridSearchCV(estimator, param_grid) gbm.fit(X_train, y_train) print('Best parameters found by grid search are:', gbm.best_params_)
[1] valid_0's l1: 0.770574 valid_0's l2: 0.729519 Training until validation scores don't improve for 5 rounds [2] valid_0's l1: 0.736515 valid_0's l2: 0.662428 [3] valid_0's l1: 0.70474 valid_0's l2: 0.602743 [4] valid_0's l1: 0.673927 valid_0's l2: 0.548122 [5] valid_0's l1: 0.647149 valid_0's l2: 0.498853 [6] valid_0's l1: 0.619693 valid_0's l2: 0.454836 [7] valid_0's l1: 0.59361 valid_0's l2: 0.41521 [8] valid_0's l1: 0.568332 valid_0's l2: 0.378919 [9] valid_0's l1: 0.546353 valid_0's l2: 0.346133 [10] valid_0's l1: 0.523806 valid_0's l2: 0.316952 [11] valid_0's l1: 0.502036 valid_0's l2: 0.290314 [12] valid_0's l1: 0.481625 valid_0's l2: 0.266628 [13] valid_0's l1: 0.461931 valid_0's l2: 0.245002 [14] valid_0's l1: 0.445287 valid_0's l2: 0.225741 [15] valid_0's l1: 0.427082 valid_0's l2: 0.20802 [16] valid_0's l1: 0.409383 valid_0's l2: 0.190996 [17] valid_0's l1: 0.392466 valid_0's l2: 0.175393 [18] valid_0's l1: 0.377264 valid_0's l2: 0.162742 [19] valid_0's l1: 0.36181 valid_0's l2: 0.150029 [20] valid_0's l1: 0.348036 valid_0's l2: 0.139806 Did not meet early stopping. Best iteration is: [20] valid_0's l1: 0.348036 valid_0's l2: 0.139806 The rmse of prediction is: 0.37390595890720224 Feature importances: [1, 9, 40, 18] Best parameters found by grid search are: {'learning_rate': 0.1, 'n_estimators': 40}