提升樹和adaboost基本流程是相似的
我看到提升樹的時候,懵了
這…跟adaboost有啥區(qū)別???
直到看到有個up主說了,我才稍微懂
相當于,我在adaboost里的弱分類器,換成CART決策樹就好了唄?
書上也沒有明說,唉。。。
還好,有大神提升樹的具體講解
看出來了,提升樹主要是做二叉樹分類和回歸的:
- 如果是處理分類問題,弱分類器用CART決策樹,就是adaboost了
- 如果是處理回歸問題,弱分類器也是用CART決策樹
- 每個新的弱分類器都是降低殘差
1. 推導(dǎo)過程
-
建立提升樹的加法模型
- 假設(shè)構(gòu)成第i個弱分類器的參數(shù)為 θ i θ_i θi?,第i個弱分類器則表示為 T ( x , θ i ) T(x,θ_i) T(x,θi?)
- 當前弱分類器若表示為 T ( x , θ m ) T(x,θ_m) T(x,θm?),強分類器則表示為: f m ( x ) = f m ? 1 ( x ) + T ( x , θ m ) f_m(x) = f_{m-1}(x)+T(x,θ_m) fm?(x)=fm?1?(x)+T(x,θm?)
- 預(yù)測結(jié)果為 y p r e = f m ( x ) = f m ? 1 ( x ) + T ( x , θ m ) y_{pre}=f_m(x)=f_{m-1}(x)+T(x,θ_m) ypre?=fm?(x)=fm?1?(x)+T(x,θm?)
-
損失函數(shù)Loss采用平方誤差損失函數(shù)
- 使用CART回歸樹作為弱分類器,那么每次選取的特征及特征值,都會使平方誤差損失函數(shù)達到最低
- 但弱分類器是不需要完全CART回歸樹一次性就把所有特征及特征值都遍歷訓(xùn)練完成的,只需要挑選平方損失函數(shù)最低的那個特征及特征值
弱分類器,只進行一個樹杈的劃分 - 弱分類器內(nèi)部的平方損失函數(shù),是取二分樹杈的左右兩個數(shù)據(jù)集的平方損失之和最小
L o s s t r e e = ∑ ( y i l e f t ? y ˉ l e f t ) 2 + ∑ ( y j r i g h t ? y ˉ r i g h t ) 2 Loss_{tree} = ∑(y_i^{left}-\bar{y}_{left})^2+ ∑(y_j^{right}-\bar{y}_{right})^2 Losstree?=∑(yileft??yˉ?left?)2+∑(yjright??yˉ?right?)2 - 強分類器的平方損失函數(shù),是取所有樣本的預(yù)測值與真實值的平方損失之和最小
L o s s = ∑ ( y i ? y i p r e ) 2 Loss = ∑(y_i-y_i^{pre})^2 Loss=∑(yi??yipre?)2, y i y_i yi?表示真實值, y i p r e y_i^{pre} yipre?表示預(yù)測值
用來選取弱分類器的特征及特征值,進而將所有樣本數(shù)據(jù)劃分成兩個子集
每個子集的預(yù)測值,是子集的均值- 根據(jù)
y
p
r
e
=
f
m
(
x
)
=
f
m
?
1
(
x
)
+
T
(
x
,
θ
m
)
y_{pre}=f_m(x)=f_{m-1}(x)+T(x,θ_m)
ypre?=fm?(x)=fm?1?(x)+T(x,θm?),可得
- L o s s = ∑ ( y i ? f m ? 1 ( x ) ? T ( x , θ m ) ) 2 Loss=∑(y_i-f_{m-1}(x)-T(x,θ_m))^2 Loss=∑(yi??fm?1?(x)?T(x,θm?))2
- 其中 y i ? f m ? 1 ( x ) y_i-f_{m-1}(x) yi??fm?1?(x)表示上次強分類器的預(yù)測值與實際值的差,一般叫做殘差(殘留的差值)
- 我們可以設(shè)為 r i = y i ? f m ? 1 ( x ) r_i = y_i-f_{m-1}(x) ri?=yi??fm?1?(x),表示殘差
- 那么 要使Loss達到最小,只需要當前的弱分類器,盡可能地擬合殘差即可, L o s s = ∑ ( r i ? T ( x , θ m ) ) 2 Loss=∑(r_i-T(x,θ_m))^2 Loss=∑(ri??T(x,θm?))2
- 那么我們無需求出當前弱分類器的參數(shù) θ,只要計算出每次的強分類器后的殘差,再新增一個弱分類器,對殘差進行CART回歸樹的擬合即可
-
每次只對殘差擬合,直到Loss函數(shù)達到某個極小的閾值、特征及特征值已完全分完了,或達到迭代次數(shù)即可
2. 程序推演
設(shè)置閾值
獲取所有特征及特征值
第一輪:
- 更改CART決策樹,讓它只每次只選擇一個特征及特征值,劃分數(shù)據(jù)集
- 每次劃分后,計算出當前弱分類器的預(yù)測值 T m ( x , θ ) T_m(x,θ) Tm?(x,θ)——對樣本的數(shù)值預(yù)測
- 計算出強分類器的預(yù)測值 f m = f m ? 1 + T ( x , θ ) f_m=f_{m-1}+T(x,θ) fm?=fm?1?+T(x,θ)
- 再計算所有樣本的殘差(預(yù)測值-真實值)
- 計算強分類器的平方損失函數(shù)Loss,判斷是否低于閾值,若低于閾值,停止程序
第二輪:
- 根據(jù)殘差,再用CART決策樹,選擇一個特征及特征值,劃分數(shù)據(jù)集
- 每次劃分后,計算出當前弱分類器的預(yù)測值 T m ( x , θ ) T_m(x,θ) Tm?(x,θ)——對樣本更新后的殘差預(yù)測
- 計算出強分類器的預(yù)測值 f m = f m ? 1 + T ( x , θ ) f_m=f_{m-1}+T(x,θ) fm?=fm?1?+T(x,θ)
- 再計算所有樣本殘差的殘差(預(yù)測值-殘差值)
- 計算強分類器的平方損失函數(shù)Loss,判斷是否低于閾值,若低于閾值,停止程序
第三輪同第二輪…
perfect!
二叉回歸樹代碼
確實,預(yù)測值的還不錯的感覺,但不知道會不會過擬合,還沒用測試數(shù)據(jù)去試。。。大概率是會過擬合的吧。。。
最終預(yù)測值和原值的殘差,呈正態(tài)分布,且大多數(shù)聚集在0附近,本來想做個配對樣本T檢驗的。。。但好像均值差距太小,搞不起來文章來源:http://www.zghlxwxcb.cn/news/detail-703925.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-703925.html
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
pd.options.display.max_columns = None
pd.options.display.max_rows = None
# 獲取所需數(shù)據(jù):'推薦分值', '專業(yè)度','回復(fù)速度','服務(wù)態(tài)度','推薦類型'
datas = pd.read_excel('./datas4.xlsx')
important_features = ['專業(yè)度','回復(fù)速度','服務(wù)態(tài)度','推薦分值'] #
datas_1 = datas[important_features]
Y = datas_1['推薦分值']
X = datas_1.drop('推薦分值',axis=1)
X_features = X.columns
Y_features = '推薦分值'
# 設(shè)置閾值
# 獲取所有特征及特征值
# 單次:
# 1. 更改CART決策樹,讓它只每次只選擇一個特征及特征值,劃分數(shù)據(jù)集
# 2. 每次劃分后,計算出當前弱分類器的預(yù)測值$T_m(x,θ)$
# 3. 計算出強分類器的預(yù)測值$f_m=f_{m-1}+T(x,θ)$
# 4. **再計算并更新所有樣本的殘差(預(yù)測值-真實值)**
# 5. 計算強分類器的平方損失函數(shù)Loss,判斷是否低于閾值,若低于閾值,停止程序
class CartRegTree:
def __init__(self,datas,Y_feat,X_feat):
self.tree_num = 0
self.datas = datas
self.Y_feat = Y_feat
self.X_feat = X_feat
self.all_feat_and_point = self.get_feat_and_point()
self.T = {} # 用于存儲所有弱分類器
self.last_Loss = 0
# 獲取所有特征及特征值
def get_feat_and_point(self):
all_feat_and_point = {}
for i in self.X_feat:
divide_points = self.datas[i].unique()
points = [j for j in divide_points]
all_feat_and_point[i]=points
return all_feat_and_point
def get_tree_name(self):
self.tree_num += 1
return 'T'+str(self.tree_num)
def get_subtree(self,datas):
# 1. 選擇最優(yōu)的特征及特征值,劃分數(shù)據(jù)集
min_Loss = None
feat_and_point = None
for feat,points in self.all_feat_and_point.items():
for point in points:
temp_Loss = self.get_Loss_tree(datas,feat,point)
if min_Loss == None or temp_Loss<min_Loss:
min_Loss = temp_Loss
feat_and_point = (feat,point)
left_datas = datas[datas[feat_and_point[0]]<=feat_and_point[1]]
right_datas = datas[datas[feat_and_point[0]] > feat_and_point[1]]
# 2.計算出當前弱分類器的預(yù)測值,存儲左右子樹的預(yù)測值
left_Y = left_datas[self.Y_feat].mean()
right_Y = right_datas[self.Y_feat].mean()
T_name = self.get_tree_name()
self.T[T_name]={'feat':feat_and_point[0],
'point':feat_and_point[1],
'left_Y':left_Y,
'right_Y':right_Y}
# 3. 計算并更新所有樣本的殘差,
datas['Tm'] = np.where(datas[feat_and_point[0]]<=feat_and_point[1],left_Y,right_Y)
datas[self.Y_feat] = datas[self.Y_feat]-datas['Tm']
# 4. 計算殘差平方和,判斷是否停止
Loss = round((datas[self.Y_feat]**2).sum(),2)
if Loss==self.last_Loss or self.tree_num>10**3:
return self.T
else:
self.last_Loss = Loss
self.get_subtree(datas)
def get_Loss_tree(self,datas,feat,point):
left_datas = datas[datas[feat]<=point]
right_datas = datas[datas[feat]>point]
# 求左右兩邊的平方損失和
left_mean = left_datas[self.Y_feat].mean()
right_mean = right_datas[self.Y_feat].mean()
left_r = left_datas[self.Y_feat]-left_mean
right_r = right_datas[self.Y_feat]-right_mean
left_loss = (left_r**2).sum()
right_loss = (right_r**2).sum()
Loss = left_loss+right_loss
return Loss
def predict_one(self,data):
Y_temp = 0
for tree_key,tree_value in self.T.items():
feat = tree_value['feat']
point = tree_value['point']
left_Y = tree_value['left_Y']
right_Y = tree_value['right_Y']
if data[feat]<=point:
Y_temp += left_Y
else:
Y_temp += right_Y
return Y_temp
def predict(self,datas):
Y_pre_all = datas.apply(self.predict_one,axis=1)
return Y_pre_all
# 應(yīng)用了pandas中的apply函數(shù),將每行數(shù)據(jù)都進行predict運算預(yù)測
tree = CartRegTree(datas_1,Y_features,X_features)
tree.get_subtree(datas_1)
Y_hat = tree.predict(datas_1)
lenth = len(Y_hat)
result = pd.DataFrame([[i[0],i[1],i[2]] for i in zip(Y,Y_hat,Y-Y_hat)])
# result = pd.DataFrame([list(Y),list(Y_hat),list(Y-Y_hat)])
print(result)
# print(f"{Y},{Y_hat},殘差:{Y-Y_hat}")
writer = pd.ExcelWriter('datas_reg_result.xlsx')
# 獲取所需數(shù)據(jù)
result.to_excel(writer,"result")
writer._save()
到了這里,關(guān)于機器學(xué)習——boosting之提升樹的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!