Gradient Tree Boosting:梯度提升树详解
理論
數學推導請參考《統計機器學習》-李航,或者參考sklearn的官方文檔,下面是我的部分筆記,也可以作為參考
優缺點
GBRT是對任意的可微損失函數的提升算法的泛化,即可回歸亦可分(sai)類(ting)。
優點:
缺點:
Sklearn note:
查過兩類的分類問題需要在每一次迭代的推導n-classes個回歸樹。因此所有需要推導的樹的數量等于n-classses * nesstimators.所以在數據量較大的時候建議使用其他算法代替GBRT.
下面例子,我們依舊使用Adaboost算法特性中的數據,同時先下面的例子也很好的證明了兩個結論:
特征重要程度相對排名
此處效果類似于之前寫過的RF奔出不再做過多解釋.
散列映射
類似效果參考RF,但是要注意的是一般而言,提升算法系列創建新特征的效果一般優于RF,比如XGBoost,LightGBM等,具體使用上也有一定的策略,這方面你可以先參考Bryan大佬的博客,不過我之后也會寫類似的總結,你也可以參考我的文章.
使用Out-of-bagging進行泛化能力的評估
這種泛化評估方式之前也提到過,有一定的效果,作用類似于CV與holdout,性能應該還可以,但是我沒真正在比賽中使用過這個,或者進行有一定數量和質量的數據集測試,這里先挖個坑,也是以后再填.
下面的例子是對三種泛化能力評估方式的比較,可以看出相比之下
import numpy as np import matplotlib.pyplot as pltfrom sklearn import ensemble from sklearn.model_selection import KFold from sklearn.model_selection import train_test_split# Generate data (adapted from G. Ridgeway's gbm example) n_samples = 1000 random_state = np.random.RandomState(13) x1 = random_state.uniform(size=n_samples) x2 = random_state.uniform(size=n_samples) x3 = random_state.randint(0, 4, size=n_samples)p = 1 / (1.0 + np.exp(-(np.sin(3 * x1) - 4 * x2 + x3))) y = random_state.binomial(1, p, size=n_samples)X = np.c_[x1, x2, x3]X = X.astype(np.float32) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5,random_state=9)# Fit classifier with out-of-bag estimates params = {'n_estimators': 1200, 'max_depth': 3, 'subsample': 0.5,'learning_rate': 0.01, 'min_samples_leaf': 1, 'random_state': 3} clf = ensemble.GradientBoostingClassifier(**params)clf.fit(X_train, y_train) acc = clf.score(X_test, y_test) print("Accuracy: {:.4f}".format(acc))n_estimators = params['n_estimators'] x = np.arange(n_estimators) + 1def heldout_score(clf, X_test, y_test):"""compute deviance scores on ``X_test`` and ``y_test``. """score = np.zeros((n_estimators,), dtype=np.float64)for i, y_pred in enumerate(clf.staged_decision_function(X_test)):score[i] = clf.loss_(y_test, y_pred)return scoredef cv_estimate(n_splits=3):cv = KFold(n_splits=n_splits)cv_clf = ensemble.GradientBoostingClassifier(**params)val_scores = np.zeros((n_estimators,), dtype=np.float64)for train, test in cv.split(X_train, y_train):cv_clf.fit(X_train[train], y_train[train])val_scores += heldout_score(cv_clf, X_train[test], y_train[test])val_scores /= n_splitsreturn val_scores# Estimate best n_estimator using cross-validation cv_score = cv_estimate(3)# Compute best n_estimator for test data test_score = heldout_score(clf, X_test, y_test)# negative cumulative sum of oob improvements cumsum = -np.cumsum(clf.oob_improvement_)# min loss according to OOB oob_best_iter = x[np.argmin(cumsum)]# min loss according to test (normalize such that first loss is 0) test_score -= test_score[0] test_best_iter = x[np.argmin(test_score)]# min loss according to cv (normalize such that first loss is 0) cv_score -= cv_score[0] cv_best_iter = x[np.argmin(cv_score)]# color brew for the three curves oob_color = list(map(lambda x: x / 256.0, (190, 174, 212))) test_color = list(map(lambda x: x / 256.0, (127, 201, 127))) cv_color = list(map(lambda x: x / 256.0, (253, 192, 134)))# plot curves and vertical lines for best iterations plt.figure(figsize=(18.5,10.5),dpi=80) plt.plot(x, cumsum, label='OOB loss', color=oob_color) plt.plot(x, test_score, label='Test loss', color=test_color) plt.plot(x, cv_score, label='CV loss', color=cv_color) plt.axvline(x=oob_best_iter, color=oob_color) plt.axvline(x=test_best_iter, color=test_color) plt.axvline(x=cv_best_iter, color=cv_color)# add three vertical lines to xticks xticks = plt.xticks() xticks_pos = np.array(xticks[0].tolist() +[oob_best_iter, cv_best_iter, test_best_iter]) xticks_label = np.array(list(map(lambda t: int(t), xticks[0])) +['OOB', 'CV', 'Test']) ind = np.argsort(xticks_pos) xticks_pos = xticks_pos[ind] xticks_label = xticks_label[ind] plt.xticks(xticks_pos, xticks_label)plt.legend(loc='upper right') plt.ylabel('normalized loss') plt.xlabel('number of iterations')plt.show() Accuracy: 0.6840損失函數
以下是sklearn目前支持的損失函數,具體損失函數可以通過參數 loss 指定,GBRT的魯棒性就是依靠帶有魯棒性的損失函數實現的:
回歸 (Regression)
- Least squares ( ‘ls’ ): 由于其優越的計算性能,該損失函數成為回歸算法中的自然選擇。 損失函數的初始值 通過目標值的均值給出。
- Least absolute deviation ( ‘lad’ ): 回歸中具有魯棒性的損失函數,損失函數的初始值 通過目標值的中值給出。
- Huber ( ‘huber’ ): 回歸中另一個具有魯棒性的損失函數,它是最小二乘和最小絕對偏差兩者的結合. 其利用 alpha 來控制模型對于異常點的敏感度(詳細介紹請參考 [F2001]).
- Quantile ( ‘quantile’ ): 分位數回歸損失函數.用 0 < alpha < 1 來指定分位數這個損 失函數可以用來產生預測間隔。(詳見 Prediction Intervals for Gradient Boosting Regression )。
分類 (Classification)
- Binomial deviance (‘deviance’): 對于二分類問題(提供概率估計)即負的二項 log 似然損失函數。模型以 log 的比值比來初始化。
- Multinomial deviance (‘deviance’): 對于多分類問題的負的多項log似然損失函數具有 n_classes 個互斥的類。提供概率估計。 初始模型由每個類的先驗概率給出.在每一次迭代中 n_classes 回歸樹被構建,這使得 GBRT 在處理多類別數據集時相當低效。
- Exponential loss (‘exponential’): 與 AdaBoostClassifier 具有相同的損失函數。與 ‘deviance’ 相比,對被錯誤標記的樣本的魯棒性較差,僅用于在二分類問題。
正則化的問題
學習率
一個簡單的正則化策略,通過一個因子 \nu 來衡量每個弱分類器對于最終結果的貢獻:
Fm(x)=Fm?1(x)+νγmhm(x)F_m(x) = F_{m-1}(x) + \nu \gamma_m h_m(x)Fm?(x)=Fm?1?(x)+νγm?hm?(x)
參數 \nu 由于它可以控制梯度下降的步長, 因此也叫作 learning rate ,它可以通過 learning_rate 參數來設置.
一般較小的學習率可以得到更精準的結果,但是同時也需要更多的基學習器配合,通常會設置早停配合使用
子采樣(減小方差)
梯度提升樹作為提升算法的一個分支,其擬合能力確實很強,但是同樣的也帶來了的更多的過擬合風險.子采樣則是減少過擬合(方差)問題的一個有效手段
一般而言,
特征子采樣(進一步減小方差)
一般而言我們所說的子采樣僅僅是樣本上的子采樣,但是假如在使用樣本子采樣之后過擬合帶來的誤差仍然占較大比例,那么我們可以使用特征子采樣的方式來進一步減小方差
下面的例子很好的展示了關于前面的幾個參數關于最終誤差的關系
import numpy as np import matplotlib.pyplot as pltfrom sklearn import ensemble from sklearn import datasetsX, y = datasets.make_hastie_10_2(n_samples=12000, random_state=1) X = X.astype(np.float32)# map labels from {-1, 1} to {0, 1} labels, y = np.unique(y, return_inverse=True)X_train, X_test = X[:2000], X[2000:] y_train, y_test = y[:2000], y[2000:]original_params = {'n_estimators': 1000, 'max_leaf_nodes': 4, 'max_depth': None, 'random_state': 2,'min_samples_split': 5}fig = plt.figure() fig.set_size_inches(18.5, 10.5)for label, color, setting in [('No shrinkage', 'orange',{'learning_rate': 1.0, 'subsample': 1.0}),('learning_rate=0.1', 'turquoise',{'learning_rate': 0.1, 'subsample': 1.0}),('subsample=0.5', 'blue',{'learning_rate': 1.0, 'subsample': 0.5}),('learning_rate=0.1, subsample=0.5', 'gray',{'learning_rate': 0.1, 'subsample': 0.5}),('learning_rate=0.1, max_features=2', 'magenta',{'learning_rate': 0.1, 'max_features': 2})]:params = dict(original_params)params.update(setting)clf = ensemble.GradientBoostingClassifier(**params)clf.fit(X_train, y_train)# compute test set deviancetest_deviance = np.zeros((params['n_estimators'],), dtype=np.float64)for i, y_pred in enumerate(clf.staged_decision_function(X_test)):# clf.loss_ assumes that y_test[i] in {0, 1}test_deviance[i] = clf.loss_(y_test, y_pred)plt.plot((np.arange(test_deviance.shape[0]) + 1)[::5], test_deviance[::5],'-', color=color, label=label)plt.legend(loc='upper left') plt.xlabel('Boosting Iterations') plt.ylabel('Test Set Deviance')plt.show()部分依賴(可用于特征工程)
部分依賴圖,可以展示屬性之間的線性關系,相比與皮爾遜等判斷公式,這種方式能夠更直接地發現同一特征不同區間與我們的y的關系.
部分依賴圖(PDP)展示了目標響應和一系列目標特征的依賴關系,同時邊緣化了其他所有特征值(候選特征)。 直覺上,我們可以將部分依賴解釋為作為目標特征函數的預期目標響應。
由于人類感知能力的限制,目標特征的設置必須小一點(通常是1到2),因此目標特征通常在最重要的特征中選擇。
單向 PDPs 告訴我們目標響應和目標特征的相互影響(例如:線性或者非線性)。下圖圖中的左上圖展示了一個地區的中等收入對中等房價的影響。我們可以清楚的看到兩者之間是線性相關的。
具有兩個目標特征的 PDPs 顯示這兩個特征之間的相互影響。例如:上圖中兩個變量的 PDP 展示了房價中位數與房屋年齡和每戶平均入住人數之間的依賴關系。我們能清楚的看到這兩個特征之間的影響:對于每戶入住均值而言,當其值大于 2 時,房價與房屋年齡幾乎是相對獨立的,而其值小于 2 的時,房價對房屋年齡的依賴性就會很強。
模型 partial_dependence 提供了一個便捷的函數 plot_partial_dependence 來產生單向或雙向部分依賴圖。在下圖的例子中我們展示如何創建一個部分依賴的網格圖:特征值介于 0 和 1 的兩個單向依賴 PDPs 和一個在兩個特征間的雙向 PDPs:
from sklearn.datasets import make_hastie_10_2 from sklearn.ensemble import GradientBoostingClassifier from sklearn.ensemble.partial_dependence import plot_partial_dependence import matplotlib.pyplot as pltX, y = make_hastie_10_2(random_state=0) clf = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0,max_depth=1, random_state=0).fit(X, y) features = [0, 1, (0, 1)] fig, axs = plot_partial_dependence(clf, X, features) plt.show() <matplotlib.figure.Figure at 0x7f80ff1b4710> # 如果你需要部分依賴函數的原始值而不是圖,你可以調用 partial_dependence 函數: from sklearn.ensemble.partial_dependence import partial_dependence pdp, axes = partial_dependence(clf, [0], X=X) pdp array([[ 2.46643157, 2.46643157, 2.46643157, 2.46643157, 2.46643157,2.46643157, 1.15418258, 1.15418258, 1.15418258, 1.15418258,1.15418258, 0.61847569, 0.61847569, 0.61847569, 0.61847569,0.61847569, 0.61847569, 0.61847569, 0.61847569, -0.03524098,-0.03524098, -0.03524098, -0.03524098, -0.03524098, -0.03524098,-0.03524098, -0.03524098, -0.03524098, -0.03524098, -0.03524098,-0.41817365, -0.41817365, -0.41817365, -0.41817365, -0.41817365,-0.41817365, -0.41817365, -0.41817365, -0.41817365, -0.41817365,-0.41817365, -0.41817365, -0.41817365, -0.41817365, -0.41817365,-0.41817365, -0.41817365, -0.41817365, -0.41817365, -0.41817365,-0.41817365, -0.41817365, -0.41817365, -0.41817365, -0.41817365,-0.41817365, -0.41817365, -0.41817365, -0.41817365, -0.41817365,-0.41817365, -0.41817365, -0.41817365, -0.41817365, -0.41817365,-0.41817365, -0.41817365, -0.41817365, -0.41817365, -0.41817365,-0.03532577, -0.03532577, -0.03532577, -0.03532577, -0.03532577,-0.03532577, -0.03532577, -0.03532577, -0.03532577, -0.03532577,0.66315234, 0.66315234, 0.66315234, 0.66315234, 0.66315234,0.66315234, 0.66315234, 0.66315234, 0.66315234, 0.66315234,0.66315234, 1.29415194, 1.29415194, 1.29415194, 1.29415194,1.29415194, 1.29415194, 1.29415194, 2.8904982 , 2.8904982 ]])該函數允許通過 grid 參數指定應該被評估的部分依賴函數的的目標特征值或可以十分便利地通過設置 X 參數從而在訓練數據中自動創建 grid 。如果 X 被給出,函數返回的 axes 為每個目標特征提供軸。
對于 grid 中的每一個 ‘目標’ 特征值,部分依賴函數需要邊緣化一棵樹中所有候選特征的可能值的預測。 在決策樹中,這個函數可以在不參考訓練數據的情況下被高效的評估,對于每一網格點執行加權遍歷: 如果切分點包含 ‘目標’ 特征,遍歷其相關的左分支或相關的右分支,否則就遍歷兩個分支。每一個分支將被通過進入該分支的訓練樣本的占比加權, 最后,部分依賴通過所有訪問的葉節點的權重的平均值給出。組合樹(tree ensembles)的整體結果,需要對每棵樹的結果再次平均得到。
參考
- sklearn官方文檔:ensemble(集成算法)及部分相關示例
- sklearn ApacheCN官方翻譯(集成算法)
- 李航教授——《統計學習方法》
總結
以上是生活随笔為你收集整理的Gradient Tree Boosting:梯度提升树详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RandomForest:随机森林
- 下一篇: 机器学习中的数据集划分问题