之前簡單介紹過決策樹,這篇文章簡單介紹一下GBDT(Gradient Boosting Decision Tree).
Gradient Boosting
決策樹是一種基本的分類與迴歸方法。決策樹模型具有分類速度快,模型容易視覺化的解釋,但是同時是也有容易發生過擬合,雖然有剪枝,但也是差強人意。
**提升方法(boosting)**在分類問題中,它通過改變訓練樣本的權重(增加分錯樣本的權重,減小分隊樣本的的權重),學習多個分類器,並將這些分類器線性組合,提高分類器效能。 於是決策樹與boosting結合產生許多演算法,主要有提升樹、GBDT等。
Gradient Boosting是一種Boosting方法,主要思想是:每一次建立模型是在之前建立模型損失函式的梯度下降方向。損失函式是評價模型效能(一般為擬合程度+正則項),認為損失函式越小,效能越好。讓損失函式持續下降,就能使模型不斷改進提升效能,最好的方法就是使損失函式沿著梯度方向下降。 Gradient Boosting是一個框架,裡面可以套入不同很多的演算法
Gradient Boosting Decision Tree
每一次建立樹模型是在之前建立模型損失函式的梯度下降方向。即利用了損失函式的負梯度在當前模型的值作為迴歸問題提升樹演算法的殘差近似值,去擬合一個迴歸樹。
決策樹的子類
class DecisionTree(object):
"""Super class of RegressionTree and ClassificationTree.
def __init__(self, min_samples_split=2, min_impurity=1e-7,
max_depth=float("inf"), loss=None):
self.root = None # Root node in dec. tree
# Minimum n of samples to justify split
self.min_samples_split = min_samples_split
# The minimum impurity to justify split
self.min_impurity = min_impurity
# The maximum depth to grow the tree to
self.max_depth = max_depth
# Function to calculate impurity (classif.=>info gain, regr=>variance reduct.)
# 切割樹的方法,gini,方差等
self._impurity_calculation = None
# Function to determine prediction of y at leaf
# 樹節點取值的方法,分類樹:選取出現最多次數的值,迴歸樹:取所有值的平均值
self._leaf_value_calculation = None
# If y is one-hot encoded (multi-dim) or not (one-dim)
self.one_dim = None
# If Gradient Boost
self.loss = loss
複製程式碼
上面程式碼中有兩個重要變數。impurity_calculation
和leaf_value_calculation
。
前者代表切割樹的分類標準是什麼。分類樹就是基尼係數,迴歸樹就是最小平方殘差。
後者代表計算節點值的方法。分類樹則切割資料集中數量最多的種類,迴歸樹則計算切割資料集中所有的平均值。
classification Tree:
class ClassificationTree(DecisionTree):
def _calculate_information_gain(self, y, y1, y2):
# 交叉墒
# Calculate information gain
p = len(y1) / len(y)
entropy = calculate_entropy(y)
info_gain = entropy - p * \
calculate_entropy(y1) - (1 - p) * \
calculate_entropy(y2)
# print("info_gain",info_gain)
return info_gain
def _majority_vote(self, y):
# 計算節點,出現最多
most_common = None
max_count = 0
for label in np.unique(y):
# Count number of occurences of samples with label
count = len(y[y == label])
if count > max_count:
most_common = label
max_count = count
# print("most_common :",most_common)
return most_common
def fit(self, X, y):
self._impurity_calculation = self._calculate_information_gain
self._leaf_value_calculation = self._majority_vote
super(ClassificationTree, self).fit(X, y)
複製程式碼
RegressionTree:
class RegressionTree(DecisionTree):
def _calculate_variance_reduction(self, y, y1, y2):
# 平方殘差
var_tot = calculate_variance(y)
var_1 = calculate_variance(y1)
var_2 = calculate_variance(y2)
frac_1 = len(y1) / len(y)
frac_2 = len(y2) / len(y)
# Calculate the variance reduction
variance_reduction = var_tot - (frac_1 * var_1 + frac_2 * var_2)
return sum(variance_reduction)
def _mean_of_y(self, y):
# 平均值
value = np.mean(y, axis=0)
return value if len(value) > 1 else value[0]
def fit(self, X, y):
self._impurity_calculation = self._calculate_variance_reduction
self._leaf_value_calculation = self._mean_of_y
super(RegressionTree, self).fit(X, y)
複製程式碼
GDBT應用--迴歸和分類
分類:每棵樹擬合當前整個模型的損失函式的負梯度,構建新的樹加到當前模型中形成新模型,下一棵樹擬合新模型的損失函式的負梯度。 迴歸:每一棵樹擬合當前整個模型的殘差,構建新的樹加到當前模型中形成新模型,下一棵樹擬合新模型的損失函式的負梯度。
GBDT的原理
如何在不改變原有模型的結構上提升模型的擬合能力
假設存在樣本集(x1, y1), (x2, y2)...., 然後用一個模型f(x)去擬合資料,使的平方損失函式:
最小。 但是發現雖然擬合效果很好,但是仍有差距。 既然不能更改原來模型的引數,意味著要在原來模型的基礎上做改善,直觀就是建議一個新的模型fx來擬合Fx為完全擬合真是樣本的殘差, 即y-F(x)。 對於每個樣本得到的擬合樣本集變為: (x1, y1 - F(x1)), (x2, y2 - F(x2)), (x3, y3 - F(x3)),..., (xn, yn - F(xn))
GBDT構建新的特徵
特徵決定模型上屆。如果能夠將資料表達成為線性可分的資料,那麼使用簡單的線性模型可以得到更好的效果。GBDT構建新的特徵也是使特徵更好的表達資料。
在預測Facebook廣告點選中,使用一種將決策樹與邏輯迴歸結合在一起的模型,其優於其他方法,超過3%。
主要思想:GBDT每棵樹的路徑直接作為LR輸入特徵使用。
用已有特徵訓練GBDT模型,然後利用GBDT模型學習到的樹來構造新特徵,最後把這些新特徵加入原有特徵一起訓練模型。構造的新特徵向量是取值0/1的,向量的每個元素對應於GBDT模型中樹的葉子結點。當一個樣本點通過某棵樹最終落在這棵樹的一個葉子結點上,那麼在新特徵向量中這個葉子結點對應的元素值為1,而這棵樹的其他葉子結點對應的元素值為0。新特徵向量的長度等於GBDT模型裡所有樹包含的葉子結點數之和。
上面簡單介紹了原理,筆者也是不太懂。後續學到再補充。 程式碼這裡就不做實現,有需要請檢視sklearn的工具包。
參考資料:
GBDT原理及利用GBDT構造新的特徵-Python實現
機器學習-一文理解GBDT的原理-20171001
GBDT的python原始碼實現