日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

gbdt源码解读

發布時間:2025/4/5 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 gbdt源码解读 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近看了一些gbdt方面的內容,發現網上的一些文章都語焉不詳,在git上找了份源碼

gbdt算法要點

  • for i in range(self.maxTreeNum)
  • gbdt采用bosting方法,就是一棵棵樹逐步的降低殘差
  • gbdt的基分類器是二叉樹,二叉樹的某個節點分裂方式是
for i in range(dim):#遍歷所有的數據for j in range(size):#令 x_train[j,i]為分裂點 splitNum = x_train[j,i]leftSubTree=[]rightSubTree=[]#以splitNum 為分裂點,對于第i個feature ,將數據分成兩類,for k in range(size):tmpNum=x_train[k,i]if tmpNum<=splitNum:leftSubTree.append(residualGradient[k])else:rightSubTree.append(residualGradient[k])sumLoss=0.0#分別計算左右子樹的loss,再求和,通過最小化loss,來決定分裂的feature和分裂的值sumLoss+=self.calculateSquareLoss(np.array(leftSubTree))sumLoss+=self.calculateSquareLoss(np.array(rightSubTree))if sumLoss<minLossValue:bestSplitPointDim=ibestSplitPointValue=splitNumminLossValue=sumLossprint(treeHeight,bestSplitPointDim)'''
  • 一棵二叉樹的計算復雜度是,樹的深度*特征的個數*(樣本個數)的平方

  • gbdt的算法復雜度=樹的個數*樹的深度*特征的個數*(樣本個數)的平方

  • gbdt的每棵樹在分裂是窮舉所有的feature,窮舉這個feature下所有的點,分別計算loss,最小的那個loss對應的那個feature 就是最優feature,對應的那個value 就是分裂點

  • xgboost在窮舉所有的feature 時,采用并行計算,能夠提高計算效率

  • gbdt在算法本質上與xgboost相同,xgboost 采用二階導數,并且加了L1,L2正則,

  • 在現如今,gbdt已經過時,他的優勢是可解釋性,與深度學習相比,gbdt的輸入只是一位的向量,他孤立的遍歷這個向量每個元素的值來實現分類回歸,這不免有些淺薄,模型如果要獲得更好的結果,他一定要有泛化能力,泛化能力來源于高維特征.

  • 在深度學習上,也有類似的做法,比如對同樣的數據,建立多個深度學習模型,然后提取模型的某一層feature,比如將一個矩陣通多個模型過映射成多個向量,再把這些向量,拼接起來,之后嘛,你可用dnn,也可以用gbdt,隨所愿你

from GBDTReg import GBDT class Config(object):learningRate=0.1maxTreeLength=4maxLeafCount=30maxTreeNum=4

首先是遍歷range(self.maxTreeNum)樹的數量, residualGradient=(y-y0)然后開始分裂樹,

def splitTree(self,x_train,residualGradient,treeHeight):""":param x_train:訓練數據:param residualGradient:當前需要擬合的梯度殘差值:param treeHeight:樹的高度:return:建好的GBDT樹"""size = len(x_train)dim = len(x_train[0])#約定:左子樹是小于等于,右子樹是大于bestSplitPointDim=-1bestSplitPointValue=-1curLoss = self.calculateSquareLoss(residualGradient)minLossValue=curLossif treeHeight==self.maxTreeLength:return curLosstree=dict([])for i in range(dim):for j in range(size):splitNum = x_train[j,i]leftSubTree=[]rightSubTree=[]for k in range(size):tmpNum=x_train[k,i]if tmpNum<=splitNum:leftSubTree.append(residualGradient[k])else:rightSubTree.append(residualGradient[k])sumLoss=0.0sumLoss+=self.calculateSquareLoss(np.array(leftSubTree))sumLoss+=self.calculateSquareLoss(np.array(rightSubTree))if sumLoss<minLossValue:bestSplitPointDim=ibestSplitPointValue=splitNumminLossValue=sumLoss#如果損失值沒有變小,則不作任何改變,也就是下面的歸位一個Nodeif minLossValue==curLoss:return np.mean(residualGradient)else:leftSplit=[(x_train[i],residualGradient[i]) for i in range(size) if x_train[i,bestSplitPointDim]<=bestSplitPointValue ]rightSplit=[(x_train[i],residualGradient[i]) for i in range(size) if x_train[i,bestSplitPointDim]>bestSplitPointValue ]# print(leftSplit)newLeftSubTree = list(zip(*leftSplit))[0]newLeftResidual = list(zip(*leftSplit))[1]leftTree = self.splitTree(np.array(newLeftSubTree),newLeftResidual,treeHeight+1)newRightSubTree = list(zip(*rightSplit))[0]newRightResidual =list(zip(*rightSplit))[1]rightTree = self.splitTree(np.array(newRightSubTree),newRightResidual,treeHeight+1)tree[(bestSplitPointDim,bestSplitPointValue)]=[leftTree,rightTree]return tree

通過對split方法的解讀,可以看出所謂梯度提升樹枝,就是遞歸的方式更新梯度的同時,劃分左子樹和右子樹。一直到loss不在降低。如果本棵樹loss不再降低,self.tree.append(curTree),最后的模型是多棵樹的疊加。

源碼鏈接

我面試候選人時必問的一個問題GBDT中的梯度是什么對什么的梯度? 給一個有m個樣本,n維特征的數據集,如果用LR算法,那么梯度是幾維? 同樣的m*n數據集,如果用GBDT,那么梯度是幾維?m維?n維?m*n維?或者是與樹的深度有關?或者與樹的葉子節點的個數有關? 1 當前y對之前所有輪次的tree的和的gradient 2 n維 3 m維,還有就是gbdt在一階泰勒展開后需要添加正則化以防止naive solution,而xgb展開到二階后,如果cost function的二階導數大于0,相當于某種意義上的自帶正則 for i in range(self.maxTreeNum):print("the tree %i-th"%i)# 之前所有輪次的tree的和的gradientresidualGradient = -1*self.learningRate*(curValue-y_train) #梯度維度與樣本數量有關curTree = self.splitTree(x_train,residualGradient,1)

gbdt細節

總結

以上是生活随笔為你收集整理的gbdt源码解读的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。