機器學習算法之LightGBM
LigthGBM是boosting集合模型中的新進成員,由微軟提供,它和XGBoost一樣是對GBDT的高效實現,原理上它和GBDT及XGBoost類似,都采用損失函數的負梯度作為當前決策樹的殘差近似值,去擬合新的決策樹。
LightGBM在很多方面會比XGBoost表現的更為優秀。它有以下優勢:
- 更快的訓練效率
- 低內存使用
- 更高的准確率
- 支持並行化學習
- 可處理大規模數據
- 支持直接使用category特征
從下圖實驗數據可以看出, LightGBM比XGBoost快將近10倍,內存占用率大約為XGBoost的1/6,並且准確率也有提升。
看完這些驚人的實驗結果以后,對下面兩個問題產生了疑惑:XGBoost已經十分完美了,為什么還要追求速度更快、內存使用更小的模型?對GBDT算法進行改進和提升的技術細節是什么?
提出LightGBM的動機
常用的機器學習算法,例如神經網絡等算法,都可以以mini-batch的方式訓練,訓練數據的大小不會受到內存限制。而GBDT在每一次迭代的時候,都需要遍歷整個訓練數據多次。如果把整個訓練數據裝進內存則會限制訓練數據的大小;如果不裝進內存,反復地讀寫訓練數據又會消耗非常大的時間。尤其面對工業級海量的數據,普通的GBDT算法是不能滿足其需求的。
LightGBM提出的主要原因就是為了解決GBDT在海量數據遇到的問題,讓GBDT可以更好更快地用於工業實踐。
XGBoost的優缺點
精確貪心算法
每輪迭代時,都需要遍歷整個訓練數據多次。如果把整個訓練數據裝進內存則會限制訓練數據的大小;如果不裝進內存,反復地讀寫訓練數據又會消耗非常大的時間。
優點:
- 可以找到精確的划分條件
缺點:
- 計算量巨大
- 內存占用巨大
- 易產生過擬合
Level-wise迭代方式
預排序方法(pre-sorted):首先,空間消耗大。這樣的算法需要保存數據的特征值,還保存了特征排序的結果(例如排序后的索引,為了后續快速的計算分割點),這里需要消耗訓練數據兩倍的內存。其次時間上也有較大的開銷,在遍歷每一個分割點的時候,都需要進行分裂增益的計算,消耗的代價大。
優點:
- 可以使用多線程
- 可以加速精確貪心算法
缺點:
- 效率低下,可能產生不必要的葉結點
對cache優化不友好
在預排序后,特征對梯度的訪問是一種隨機訪問,並且不同的特征訪問的順序不一樣,無法對cache進行優化。同時,在每一層長樹的時候,需要隨機訪問一個行索引到葉子索引的數組,並且不同特征訪問的順序也不一樣,也會造成較大的cache miss。
LightGBM在哪些地方進行了優化?
以上與其說是XGBoost的不足,倒不如說是LightGBM作者們構建新算法時着重瞄准的點。解決了什么問題,那么原來模型沒解決就成了原模型的缺點。
概括來說,lightGBM主要有以下特點:
- 基於Histogram的決策樹算法
- 帶深度限制的Leaf-wise的葉子生長策略
- 直方圖做差加速
- 直接支持類別特征(Categorical Feature)
- Cache命中率優化
- 基於直方圖的稀疏特征優化
- 多線程優化
決策樹算法
XGBoost使用的是pre-sorted算法,能夠更精確的找到數據分隔點。
- 首先,對所有特征按數值進行預排序。
- 其次,在每次的樣本分割時,用O(# data)的代價找到每個特征的最優分割點。
- 最后,找到最后的特征以及分割點,將數據分裂成左右兩個子節點。
這種pre-sorting算法能夠准確找到分裂點,但是在空間和時間上有很大的開銷。
- 由於需要對特征進行預排序並且需要保存排序后的索引值(為了后續快速的計算分裂點),因此內存需要訓練數據的兩倍。
- 在遍歷每一個分割點的時候,都需要進行分裂增益的計算,消耗的代價大。
LightGBM使用的是histogram算法,占用的內存更低,數據分隔的復雜度更低。其思想是將連續的浮點特征離散成k個離散值,並構造寬度為k的Histogram。然后遍歷訓練數據,統計每個離散值在直方圖中的累計統計量。在進行特征選擇時,只需要根據直方圖的離散值,遍歷尋找最優的分割點。
使用直方圖算法有很多優點。首先最明顯就是內存消耗的降低,直方圖算法不僅不需要額外存儲預排序的結果,而且可以只保存特征離散化后的值,而這個值一般用8位整型存儲就足夠了,內存消耗可以降低為原來的1/8。
然后在計算上的代價也大幅降低,預排序算法每遍歷一個特征值就需要計算一次分裂的增益,而直方圖算法只需要計算k次(k可以認為是常數),時間復雜度從O(#data*#feature)優化到O(k*#features)。
Histogram algorithm
Histogram algorithm應該翻譯為直方圖算法,直方圖算法的思想也很簡單,首先將連續的浮點數據轉換為bin數據,具體過程是首先確定對於每一個特征需要多少的桶bin,然后均分,將屬於該桶的樣本數據更新為bin的值,最后用直方圖表示。(看起來很高大上,其實就是直方圖統計,最后我們將大規模的數據放在了直方圖中)
直方圖算法有幾個需要注意的地方:
- 使用bin替代原始數據相當於增加了正則化;
- 使用bin意味着很多數據的細節特征被放棄了,相似的數據可能被划分到相同的桶中,這樣的數據之間的差異就消失了;
- bin數量選擇決定了正則化的程度,bin越少懲罰越嚴重,欠擬合風險越高。
直方圖算法需要注意的地方:
- 構建直方圖時不需要對數據進行排序(比XGBoost快),因為預先設定了bin的范圍;
- 直方圖除了保存划分閾值和當前bin內樣本數以外還保存了當前bin內所有樣本的一階梯度和(一階梯度和的平方的均值等價於均方損失);
- 閾值的選取是按照直方圖從小到大遍歷,使用了上面的一階梯度和,目的是得到划分之后△loss最大的特征及閾值。
Histogram 算法的優缺點:
- Histogram算法並不是完美的。由於特征被離散化后,找到的並不是很精確的分割點,所以會對結果產生影響。但在實際的數據集上表明,離散化的分裂點對最終的精度影響並不大,甚至會好一些。原因在於decision tree本身就是一個弱學習器,采用Histogram算法會起到正則化的效果,有效地防止模型的過擬合。
- 時間上的開銷由原來的O(#data * #features)降到O(k * #features)。由於離散化,#bin遠小於#data,因此時間上有很大的提升。
Histogram算法還可以進一步加速。一個葉子節點的Histogram可以直接由父節點的Histogram和兄弟節點的Histogram做差得到。一般情況下,構造Histogram需要遍歷該葉子上的所有數據,通過該方法,只需要遍歷Histogram的k個捅。速度提升了一倍。
決策樹生長策略
在Histogram算法之上,LightGBM進行進一步的優化。首先它拋棄了大多數GBDT工具使用的按層生長 (level-wise)的決策樹生長策略,而使用了帶有深度限制的按葉子生長 (leaf-wise)算法。
XGBoost采用的是按層生長level(depth)-wise生長策略,能夠同時分裂同一層的葉子,從而進行多線程優化,不容易過擬合;但不加區分的對待同一層的葉子,帶來了很多沒必要的開銷。因為實際上很多葉子的分裂增益較低,沒必要進行搜索和分裂。
LightGBM采用leaf-wise生長策略,每次從當前所有葉子中找到分裂增益最大(一般也是數據量最大)的一個葉子,然后分裂,如此循環。因此同Level-wise相比,在分裂次數相同的情況下,Leaf-wise可以降低更多的誤差,得到更好的精度。Leaf-wise的缺點是可能會長出比較深的決策樹,產生過擬合。因此LightGBM在Leaf-wise之上增加了一個最大深度的限制,在保證高效率的同時防止過擬合。
直方圖差加速
LightGBM另一個優化是Histogram(直方圖)做差加速。一個容易觀察到的現象:一個葉子的直方圖可以由它的父親節點的直方圖與它兄弟的直方圖做差得到。通常構造直方圖,需要遍歷該葉子上的所有數據,但直方圖做差僅需遍歷直方圖的k個桶。利用這個方法,LightGBM可以在構造一個葉子的直方圖后,可以用非常微小的代價得到它兄弟葉子的直方圖,在速度上可以提升一倍。
直接支持類別特征
實際上大多數機器學習工具都無法直接支持類別特征,一般需要把類別特征,轉化one-hotting特征,降低了空間和時間的效率。而類別特征的使用是在實踐中很常用的。基於這個考慮,LightGBM優化了對類別特征的支持,可以直接輸入類別特征,不需要額外的0/1展開。並在決策樹算法上增加了類別特征的決策規則。
one-hot編碼是處理類別特征的一個通用方法,然而在樹模型中,這可能並不一定是一個好的方法,尤其當類別特征中類別個數很多的情況下。主要的問題是:
- 可能無法在這個類別特征上進行切分(即浪費了這個特征)。使用one-hot編碼的話,意味着在每一個決策節點上只能使用one vs rest(例如是不是狗,是不是貓等)的切分方式。當類別值很多時,每個類別上的數據可能會比較少,這時候切分會產生不平衡,這意味着切分增益也會很小(比較直觀的理解是,不平衡的切分和不切分沒有區別)。
- 會影響決策樹的學習。因為就算可以在這個類別特征進行切分,也會把數據切分到很多零碎的小空間上,如圖1左邊所示。而決策樹學習時利用的是統計信息,在這些數據量小的空間上,統計信息不准確,學習會變差。但如果使用下圖右邊的分裂方式,數據會被切分到兩個比較大的空間,進一步的學習也會更好。
下圖右邊葉子節點的含義是X=A或者X=C放到左孩子,其余放到右孩子。
LightGBM處理分類特征大致流程:
為了解決one-hot編碼處理類別特征的不足。LightGBM采用了Many vs many的切分方式,實現了類別特征的最優切分。用LightGBM可以直接輸入類別特征,並產生上圖右邊的效果。在1個k維的類別特征中尋找最優切分,朴素的枚舉算法的復雜度是$O(2^k)$,而LightGBM采用了如On Grouping For Maximum Homogeneity的方法實現了$O(k\log k)$的算法。
算法流程下圖所示:在枚舉分割點之前,先把直方圖按每個類別的均值進行排序;然后按照均值的結果依次枚舉最優分割點。從下圖可以看到,Sum(y)/Count(y)為類別的均值。當然,這個方法很容易過擬合,所以在LGBM中加入了很多對這個方法的約束和正則化。
下圖是一個簡單的對比實驗,可以看到該最優方法在AUC上提升了1.5個點,並且時間只多了20%。
下面具體來講下在代碼中如何求解類別特征的最優切分的流程:
- 離散特征建立直方圖的過程:統計該特征下每一種離散值出現的次數,並從高到低排序,並過濾掉出現次數較少的特征值, 然后為每一個特征值,建立一個bin容器, 對於在bin容器內出現次數較少的特征值直接過濾掉,不建立bin容器。
- 計算分裂閾值的過程:
- 先看該特征下划分出的bin容器的個數,如果bin容器的數量小於4,直接使用one vs other方式, 逐個掃描每一個bin容器,找出最佳分裂點;
- 對於bin容器較多的情況, 先進行過濾,只讓子集合較大的bin容器參加划分閾值計算, 對每一個符合條件的bin容器進行公式計算(公式如下: 該bin容器下所有樣本的一階梯度之和/該bin容器下所有樣本的二階梯度之和 + 正則項(參數cat_smooth),這里為什么不是label的均值呢?其實上例中只是為了便於理解,只針對了學習一棵樹且是回歸問題的情況, 這時候一階導數是Y, 二階導數是1),得到一個值,根據該值對bin容器從小到大進行排序,然后分從左到右、從右到左進行搜索,得到最優分裂閾值。但是有一點,沒有搜索所有的bin容器,而是設定了一個搜索bin容器數量的上限值,程序中設定是32,即參數max_num_cat。LightGBM中對離散特征實行的是many vs many 策略,這32個bin中最優划分的閾值的左邊或者右邊所有的bin容器就是一個many集合,而其他的bin容器就是另一個many集合。
- 對於連續特征,划分閾值只有一個,對於離散值可能會有多個划分閾值,每一個划分閾值對應着一個bin容器編號,當使用離散特征進行分裂時,只要數據樣本對應的bin容器編號在這些閾值對應的bin集合之中,這條數據就加入分裂后的左子樹,否則加入分裂后的右子樹。
直接支持高效並行
LightGBM原生支持並行學習,目前支持特征並行和數據並行的兩種。特征並行的主要思想是在不同機器在不同的特征集合上分別尋找最優的分割點,然后在機器間同步最優的分割點。數據並行則是讓不同的機器先在本地構造直方圖,然后進行全局的合並,最后在合並的直方圖上面尋找最優分割點。
LightGBM針對這兩種並行方法都做了優化,在特征並行算法中,通過在本地保存全部數據避免對數據切分結果的通信;在數據並行中使用分散規約(Reduce scatter)把直方圖合並的任務分攤到不同的機器,降低通信和計算,並利用直方圖做差,進一步減少了一半的通信量。
基於投票的數據並行則進一步優化數據並行中的通信代價,使通信代價變成常數級別。在數據量很大的時候,使用投票並行可以得到非常好的加速效果。
網絡通信優化
XGBoost由於采用pre-sorted算法,通信代價非常大,所以在並行的時候也是采用histogram算法;LightGBM采用的histogram算法通信代價小,通過使用集合通信算法,能夠實現並行計算的線性加速。
LightGBM原理
提升樹是利用加模型與前向分布算法實現學習的優化過程,它有一些高效實現,如XGBoost, pGBRT,GBDT(Gradient Boosting Decision Tree)等。其中GBDT采用負梯度作為划分的指標(信息增益),XGBoost則利用到二階導數。他們共同的不足是,計算信息增益需要掃描所有樣本,從而找到最優划分點。在面對大量數據或者特征維度很高時,它們的效率和擴展性很難使人滿意。解決這個問題的直接方法就是減少特征量和數據量而且不影響精確度,有部分工作根據數據權重采樣來加速booisting的過程,但由於GBDT沒有樣本權重不能應用。
微軟開源的LightGBM(基於GBDT的)則很好的解決這些問題,它主要包含兩個算法:
單邊梯度采樣,Gradient-based One-Side Sampling(GOSS)
GOSS(從減少樣本角度):排除大部分小梯度的樣本,僅用剩下的樣本計算信息增益。GBDT雖然沒有數據權重,但每個數據實例有不同的梯度,根據計算信息增益的定義,梯度大的實例對信息增益有更大的影響,因此在下采樣時,我們應該盡量保留梯度大的樣本(預先設定閾值,或者最高百分位間),隨機去掉梯度小的樣本。我們證明此措施在相同的采樣率下比隨機采樣獲得更准確的結果,尤其是在信息增益范圍較大時。
互斥特征綁定,Exclusive Feature Bundling(EFB)
- EFB(從減少特征角度):捆綁互斥特征,也就是他們很少同時取非零值(也就是用一個合成特征代替)。通常真是應用中,雖然特征量比較多,但是由於特征空間十分稀疏,是否可以設計一種無損的方法來減少有效特征呢?特別在稀疏特征空間上,許多特征幾乎是互斥的(例如許多特征不會同時為非零值,像one-hot),我們可以捆綁互斥的特征。最后,我們將捆綁問題歸約到圖着色問題,通過貪心算法求得近似解。
結合使用 GOSS 和 EFB 的 GBDT 算法就是 LightGBM。
Gradient-based One-Side Sampling(GOSS)
GOSS是一種在減少數據量和保證精度上平衡的算法。GOSS是通過區分不同梯度的實例,保留較大梯度實例同時對較小梯度隨機采樣的方式減少計算量,從而達到提升效率的目的。
算法描述
AdaBoost中,樣本權重是數據實例重要性的指標。然而在GBDT中沒有原始樣本權重,不能應用權重采樣。幸運的事,我們觀察到GBDT中每個數據都有不同的梯度值,對采樣十分有用,即實例的梯度小,實例訓練誤差也就較小,已經被學習得很好了,直接想法就是丟掉這部分梯度小的數據。然而這樣做會改變數據的分布,將會影響訓練的模型的精確度,為了避免此問題,我們提出了GOSS。
GOSS保留所有的梯度較大的實例,在梯度小的實例上使用隨機采樣。為了抵消對數據分布的影響,計算信息增益的時候,GOSS對小梯度的數據引入常量乘數。GOSS首先根據數據的梯度絕對值排序,選取top a個實例。然后在剩余的數據中隨機采樣b個實例。接着計算信息增益時為采樣出的小梯度數據乘以(1-a)/b,這樣算法就會更關注訓練不足的實例,而不會過多改變原數據集的分布。
理論分析
GBDT使用決策樹,來學習獲得一個將輸入空間映射到梯度空間的函數。假設訓練集有n個實例${x_1,…,x_n}$,特征維度為s。每次梯度迭時,模型數據變量的損失函數的負梯度方向表示為${g_1,…,g_n}$,決策樹通過最優切分點(最大信息增益點)將數據分到各個節點。GBDT通過分割后的方差衡量信息增益。
定義:O表示某個固定節點的訓練集,分割特征j的分割點d定義為:
$$V_{j|O}(d) = \frac{1}{n_O} ( \frac{ ( \sum_{ \{x_i \in O: x_{ij} \le d \} } g_i )^2 }{n^j_{l|O}(d)} + \frac{ ( \sum_{ \{x_i \in O: x_{ij} > d \} } g_i )^2 }{n^j_{r|O}(d)} )$$
其中,$n_O = \sum I[x_i \in O], n^j_{l|O} = \sum I[x_i \in O: x_i \ge d ], n^j_{r|O} = \sum I[x_i \in O: x_i >d ]$
遍歷每個特征的每個分裂點,找到$d^*_j = argmax_d V_j(d)$並計算最大的信息增益$V_j(d^*_j)$ ,然后,將數據根據特征$j^*$的分裂點$d^*_j$將數據分到左右子節點。
在GOSS中,
- 首先根據數據的梯度將訓練降序排序。
- 保留top a個數據實例,作為數據子集A。
- 對於剩下的數據的實例,隨機采樣獲得大小為b的數據子集B。
- 最后我們通過以下方程估計信息增益:
$$\tilde{V}_j (d) = \frac{1}{n} ( \frac{ ( \sum_{ {x_i \in A: x_{ij} \le d } } g_i + \frac{1-a}{b}\sum_{ {x_i \in B: x_{ij} \le d } } g_i )^2 }{n^j{l}(d)} + \frac{ ( \sum_{ {x_i \in A: x{ij} > d } } g_i + \frac{1-a}{b}\sum_{ {x_i \in B: x{ij} > d } } g_i )^2 }{n^j_{r}(d)} ) \tag{1}$$
此處GOSS通過較小的數據集估計信息增益$\tilde{V}{j}(d)$將大大地減小計算量。更重要的,我們接下來理論表明GOSS不會丟失許多訓練精度,勝過隨機采樣,理論的證明在附加材料。
我們定義GOSS近似誤差為:
$$\varepsilon(d) = |\tilde{V}_j(d) – V_j(d) |$$
$$\bar{g}_l^j(d)=\frac{\sum_{x_i \in (A \cup A^c)_l} |g_i|}{n_l^j(d)}$$
概率至少是$1- \delta1$,有:
$$\varepsilon(d) \le C_{a, b}^2 \ln (1/\delta) * \max\{ \frac{1}{n_l^j(d)} , \frac{1}{n_r^j(d)} \} + 2*D*C_{a,b} \sqrt{ \frac{\ln(1/\delta)}{n}} \tag{2}$$
其中$C_{a,b}=\frac{1-a}{\sqrt{b}} \max_{x \in A^c}{|g_i|}, D=\max(\bar{g}_l^j(d), \bar{g}_r^j(d))$
根據上述理論,我們得出以下結論:
- GOSS的漸近逼近比率$O(\frac{1}{n_l^j(d)}+ \frac{1}{n_r^j(d)} + \frac{1}{\sqrt{n}})$ 。如果數據分割不是極不平衡(例如$n_l^j(d) \ge O(\sqrt{n})$ 和 $n_r^j \ge O(\sqrt{n})$),那么不等式(2)中近似誤差將由第二項主導,當n趨於無窮(數據量很大時),$O(\sqrt{n})$將趨於0,即數據量越大,誤差越小,精度越高。
- 隨機采樣是GOSS在a=0的一種情況。多數情況下,GOSS性能優於隨機采樣,即以下情況:$C_{0, \beta} > C_{a, \beta-a}$,即$\frac{\alpha_a}{\sqrt{\beta}} > \frac{1-a}{\sqrt{\beta-a}}$,其中$\alpha_a = \max_{x_i \in A \cup A^c} |g_i| / \max_{x_i \in A^c} |g_i|$
下面分析GOSS的泛化性。考慮GOSS泛化誤差$\varepsilon_{gen}^{GOSS}(d) = |\tilde{V}_j(d) – V_*(d)|$ ,這是GOSS抽樣的的實例計算出的方差增益與實際樣本方差增益之間的差距。變換為$\varepsilon_{gen}^{GOSS}(d) = |\tilde{V}_j(d) – V_j(d)|+ |V_j(d) – V_*(d)| := \varepsilon_{GOSS}(d)+ \varepsilon_{gen}(d)$,因此,在GOSS准確的情況下,GOSS泛化誤差近似於全量的真實數據。另一方面,采樣將增加基學習器的多樣性(因為每次采樣獲得的數據可能會不同),這將提高泛化性。
Exclusive Feature Bundling(EFB)
EFB是通過特征捆綁的方式減少特征維度(其實是降維技術)的方式,來提升計算效率。通常被捆綁的特征都是互斥的(一個特征值為零,一個特征值不為零),這樣兩個特征捆綁起來才不會丟失信息。如果兩個特征並不是完全互斥(部分情況下兩個特征都是非零值),可以用一個指標對特征不互斥程度進行衡量,稱之為沖突比率,當這個值較小時,我們可以選擇把不完全互斥的兩個特征捆綁,而不影響最后的精度。
EBF的算法步驟如下:
- 將特征按照非零值的個數進行排序
- 計算不同特征之間的沖突比率
- 遍歷每個特征並嘗試合並特征,使沖突比率最小化
高位的數據通常是稀疏的,這種稀疏性啟發我們設計一種無損地方法來減少特征的維度。特別的,稀疏特征空間中,許多特征是互斥的,例如他們從不同時為非零值。我們可以綁定互斥的特征為單一特征,通過仔細設計特征臊面算法,我們從特征捆綁中構建了與單個特征相同的特征直方圖。這種方式的間直方圖時間復雜度從O(#data * #feature)降到O(#data * #bundle),由於#bundle << # feature,我們能夠極大地加速GBDT的訓練過程而且損失精度。
有兩個問題:
- 怎么判定那些特征應該綁在一起(build bundled)?
- 怎么把特征綁為一個(merge feature)?
bundle(什么樣的特征被綁定)?
**理論1:**將特征分割為較小量的互斥特征群是NP難的。
證明:將圖着色問題歸約為此問題,而圖着色是NP難的,所以此問題就是NP難的。
給定圖着色實例G=(V, E)。以G的關聯矩陣的每一行為特征,得到我們問題的一個實例有|V|個特征。 很容易看到,在我們的問題中,一個獨特的特征包與一組具有相同顏色的頂點相對應,反之亦然。
理論1說明多項式時間中求解這個NP難問題不可行。為了尋找好的近似算法,我們將最優捆綁問題歸結為圖着色問題,如果兩個特征之間不是相互排斥,那么我們用一個邊將他們連接,然后用合理的貪婪算法(具有恆定的近似比)用於圖着色來做特征捆綁。 此外,我們注意到通常有很多特征,盡管不是100%相互排斥的,也很少同時取非零值。 如果我們的算法可以允許一小部分的沖突,我們可以得到更少的特征包,進一步提高計算效率。經過簡單的計算,隨機污染小部分特征值將影響精度最多$O([(1 – \gamma)n]^{-2/3})$,$\gamma$是每個綁定中的最大沖突比率,當其相對較小時,能夠完成精度和效率之間的平衡。
**算法3:**基於上面的討論,我們設計了算法3,偽代碼見下圖,具體算法:
- 建立一個圖,每個點代表特征,每個邊有權重,其權重和特征之間總體沖突相關。
- 按照降序排列圖中的度數來排序特征。
- 檢查排序之后的每個特征,對他進行特征綁定或者建立新的綁定使得操作之后的總體沖突最小。
算法3的時間復雜度是$O(feature^2)$,訓練之前只處理一次,其時間復雜度在特征不是特別多的情況下是可以接受的,但難以應對百萬維的特征。為了繼續提高效率,我們提出了一個更加高效的無圖的排序策略:將特征按照非零值個數排序,這和使用圖節點的度排序相似,因為更多的非零值通常會導致沖突,新算法在算法3基礎上改變了排序策略。
merging features(特征合並)
如何合並同一個bundle的特征來降低訓練時間復雜度。關鍵在於原始特征值可以從bundle中區分出來。鑒於直方圖算法存儲離散值而不是連續特征值,我們通過將互斥特征放在不同的箱中來構建bundle。這可以通過將偏移量添加到特征原始值中實現,例如,假設bundle中有兩個特征,原始特征A取值[0, 10],B取值[0, 20]。我們添加偏移量10到B中,因此B取值[10, 30]。通過這種做法,就可以安全地將A、B特征合並,使用一個取值[0, 30]的特征取代AB。算法見算法4,
EFB算法能夠將許多互斥的特征變為低維稠密的特征,就能夠有效的避免不必要0值特征的計算。實際,通過用表記錄數據中的非零值,來忽略零值特征,達到優化基礎的直方圖算法。通過掃描表中的數據,建直方圖的時間復雜度將從O(#data)降到O(#non_zero_data)。當然,這種方法在構建樹過程中需要而額外的內存和計算開銷來維持預特征表。我們在lightGBM中將此優化作為基本函數,因為當bundles是稀疏的時候,這個優化與EFB不沖突(可以用於EFB)。
如何使用LightGBM
LightGBM調參指導
針對 leaf-wise 樹的參數優化:
- num_leaves:控制了葉節點的數目。它是控制樹模型復雜度的主要參數。
- 如果是level-wise,則該參數為$2^{depth}$,其中depth為樹的深度。但是當葉子數量相同時,leaf-wise的樹要遠遠深過level-wise樹,非常容易導致過擬合。因此應該讓num_leaves小於$2^{depth}$。在leaf-wise樹中,並不存在depth的概念。因為不存在一個從leaves到depth的合理映射。
- min_data_in_leaf:每個葉節點的最少樣本數量。它是處理leaf-wise樹的過擬合的重要參數。將它設為較大的值,可以避免生成一個過深的樹。但是也可能導致欠擬合。
- max_depth: 控制了樹的最大深度。該參數可以顯式的限制樹的深度。
針對更快的訓練速度:
- 通過設置 bagging_fraction 和 bagging_freq 參數來使用 bagging 方法
- 通過設置 feature_fraction 參數來使用特征的子抽樣
- 使用較小的 max_bin
- 使用 save_binary 在未來的學習過程對數據加載進行加速
獲取更好的准確率:
- 使用較大的 max_bin (學習速度可能變慢)
- 使用較小的 learning_rate 和較大的 num_iterations
- 使用較大的 num_leaves (可能導致過擬合)
- 使用更大的訓練數據
- 嘗試 dart
緩解過擬合:
- 使用較小的 max_bin
- 使用較小的 num_leaves
- 使用 min_data_in_leaf 和 min_sum_hessian_in_leaf
- 通過設置 bagging_fraction 和 bagging_freq 來使用 bagging
- 通過設置 feature_fraction 來使用特征子抽樣
- 使用更大的訓練數據
- 使用 lambda_l1, lambda_l2 和 min_gain_to_split 來使用正則
- 嘗試 max_depth 來避免生成過深的樹
核心參數:
- config 或者config_file:一個字符串,給出了配置文件的路徑。默認為空字符串。
- task: 一個字符串,給出了要執行的任務。可以為:
- ‘train’ 或者 ‘training’:表示是訓練任務。默認為’train’。
- ‘predict’ 或者 ‘prediction’或者’test’:表示是預測任務。
- ‘convert_model’: 表示是模型轉換任務。將模型文件轉換成if-else 格式。
- application或者objective或者app:一個字符串,表示問題類型。可以為:
- ‘regression’或’regression_l2’或’mean_squared_error’或’mse’或’l2_root’或’root_mean_squred_error’或’rmse’:表示回歸任務,但是使用L2損失函數。默認為’regression’
- ‘regression_l1’或者mae或者mean_absolute_error:表示回歸任務,但是使用L1損失函數。
- ‘huber’: 表示回歸任務,但是使用huber 損失函數。
- ‘fair’: 表示回歸任務,但是使用fair 損失函數。
- ‘poisson’: 表示Poisson 回歸任務。
- ‘quantile’: 表示quantile回歸任務。
- ‘quantile_l2’:表示quantile回歸任務,但是使用了L2 損失函數。
- ‘mape’ 或者’mean_absolute_precentage_error’: 表示回歸任務,但是使用MAPE 損失函數
- ‘gamma’: 表示gamma 回歸任務。
- ‘tweedie’: 表示tweedie 回歸任務。
- ‘binary’: 表示二分類任務,使用對數損失函數作為目標函數。
- ‘multiclass’: 表示多分類任務,使用softmax 函數作為目標函數。必須設置num_class 參數
- ‘multiclassova’ 或者’multiclass_ova’ 或者’ova’ 或者’ovr’: 表示多分類任務,使用one-vs-all 的二分類目標函數。必須設置num_class 參數
- ‘xentropy’ 或者’cross_entropy’: 目標函數為交叉熵(同時具有可選擇的線性權重)。要求標簽是[0,1] 之間的數值。
- ‘xentlambda’ 或者’cross_entropy_lambda’: 替代了參數化的cross_entropy 。要求標簽是[0,1]之間的數值。
- ‘lambdarank’:表示排序任務。在lambdarank 任務中,標簽應該為整數類型,數值越大表示相關性越高。label_gain 參數可以用於設置整數標簽的增益(權重)
- boosting 或者’boost’ 或者 ‘boosting_type’: 一個字符串,給出了基學習器模型算法。可以為:
- ‘gbdt’: 表示傳統的梯度提升決策樹。默認值為’gbdt’
- ‘rf’: 表示隨機森林。
- ‘dart’: 表示帶dropout 的gbdt
- goss:表示Gradient-based One-Side Sampling 的gbdt
- data或者train或者train_data:一個字符串,給出了訓練數據所在的文件的文件名。默認為空字符串。lightgbm將使用它來訓練模型。
- valid或者test或者valid_data或者test_data:一個字符串,表示驗證集所在的文件的文件名。默認為空字符串。lightgbm將輸出該數據集的度量。如果有多個驗證集,則用逗號分隔。
- num_iterations或者num_iteration或者num_tree或者num_trees或者num_round或者num_rounds或者num_boost_round 一個整數,給出了boosting的迭代次數。默認為 100。
- 對於python/R包,該參數是被忽略的。對於python,使用train()/cv()的輸入參數num_boost_round來代替。
- 在內部,lightgbm對於multiclass 問題設置了num_class*num_iterations 棵樹。
- learning_rate或者shrinkage_rate: 個浮點數,給出了學習率。默認為1。在dart 中,它還會影響dropped trees 的歸一化權重。
- num_leaves或者num_leaf:一個整數,給出了一棵樹上的葉子數。默認為 31
- tree_learner或者tree:一個字符串,給出了tree learner,主要用於並行學習。 默認為’serial’。 可以為:
- ‘serial’: 單台機器的tree learner
- ‘feature’: 特征並行的tree learner
- ‘data’: 數據並行的tree learner
- ‘voting’: 投票並行的tree learner
- num_threads 或者num_thread 或者nthread:一個整數, 給出了lightgbm 的線程數。默認為OpenMP_default。
- 為了更快的速度,應該將它設置為真正的CPU 內核數,而不是線程的數量(大多數CPU 使用超線程來使每個CPU內核生成2個線程)。
- 當數據集較小的時候,不要將它設置的過大
- 對於並行學習,不應該使用全部的CPU核心,因為這會使得網絡性能不佳
- device: 一個字符串,指定計算設備。默認為’cpu’。 可以為’gpu’,’cpu’。
- 建議使用較小的max_bin 來獲得更快的計算速度
- 為了加快學習速度,GPU 默認使用32位浮點數來求和。你可以設置gpu_use_dp=True 來啟動64位浮點數,但是它會使得訓練速度降低。
學習控制參數:
- max_depth: 一個整數,限制了樹模型的最大深度,默認值為-1。如果小於0,則表示沒有限制。
- min_data_in_leaf 或者 min_data_per_leaf 或者 min_data或者min_child_samples: 一個整數,表示一個葉子節點上包含的最少樣本數量。默認值為 20
- min_sum_hessian_in_leaf 或者 min_sum_hessian_per_leaf或者 min_sum_hessian 或者 min_hessian或者min_child_weight: 一個浮點數,表示一個葉子節點上的最小hessian 之和。(也就是葉節點樣本權重之和的最小值) 默認為1e-3 。
- feature_fraction或者sub_feature或者colsample_bytree:一個浮點數,取值范圍為[0.0,1.0], 默認值為0。如果小於1.0,則lightgbm 會在每次迭代中隨機選擇部分特征。如0.8 表示:在每棵樹訓練之前選擇80% 的特征來訓練。
- feature_fraction_seed: 一個整數,表示feature_fraction 的隨機數種子,默認為2。
- bagging_fraction 或者sub_row 或者 subsample:一個浮點數,取值范圍為[0.0,1.0], 默認值為0。如果小於1.0,則lightgbm 會在每次迭代中隨機選擇部分樣本來訓練(非重復采樣)。如0.8 表示:在每棵樹訓練之前選擇80% 的樣本(非重復采樣)來訓練。
- bagging_freq 或者subsample_freq:一個整數,表示每bagging_freq 次執行bagging。如果該參數為0,表示禁用bagging。
- bagging_seed 或者 bagging_fraction_seed:一個整數,表示bagging 的隨機數種子,默認為 3 。
- early_stopping_round 或者 early_stopping_rounds或者early_stopping:一個整數,默認為0。如果一個驗證集的度量在early_stopping_round 循環中沒有提升,則停止訓練。如果為0則表示不開啟早停。
- lambda_l1 或者reg_alpha: 一個浮點數,表示L1正則化系數。默認為0
- lambda_l2 或者reg_lambda: 一個浮點數,表示L2正則化系數。默認為0
- min_split_gain 或者min_gain_to_split: 一個浮點數,表示執行切分的最小增益,默認為0
- drop_rate: 一個浮點數,取值范圍為[0.0,1.0],表示dropout 的比例,默認為1。 該參數僅在dart 中使用
- skip_drop: 一個浮點數,取值范圍為[0.0,1.0],表示跳過dropout 的概率,默認為5。 該參數僅在dart 中使用
- max_drop: 一個整數,表示一次迭代中刪除樹的最大數量,默認為50。 如果小於等於0,則表示沒有限制。 該參數僅在dart 中使用
- uniform_drop:一個布爾值,表示是否想要均勻的刪除樹,默認值為False。 該參數僅在dart 中使用
- xgboost_dart_mode: 一個布爾值,表示是否使用xgboost dart 模式,默認值為False。該參數僅在dart 中使用
- drop_seed: 一個整數,表示dropout 的隨機數種子,默認值為 4。 該參數僅在dart 中使用
- top_rate: 一個浮點數,取值范圍為[0.0,1.0],表示在goss 中,大梯度數據的保留比例,默認值為2。該參數僅在goss 中使用
- other_rate: 一個浮點數,取值范圍為[0.0,1.0],表示在goss 中,小梯度數據的保留比例,默認值為1。該參數僅在goss 中使用
- min_data_per_group:一個整數,表示每個分類組的最小數據量,默認值為100。用於排序任務
- max_cat_threshold: 一個整數,表示category 特征的取值集合的最大大小。默認為 32 。
- cat_smooth: 一個浮點數,用於category 特征的概率平滑。默認值為 10。它可以降低噪聲在category 特征中的影響,尤其是對於數據很少的類。
- cat_l2: 一個浮點數,用於category 切分中的L2 正則化系數。默認為 10 。
- top_k 或者 topk: 一個整數,用於投票並行中。默認為20 。將它設置為更大的值可以獲得更精確的結果,但是會降低訓練速度。
IO 參數:
- max_bin: 一個整數,表示最大的桶的數量。默認值為 255。lightgbm 會根據它來自動壓縮內存。如max_bin=255 時,則lightgbm 將使用uint8 來表示特征的每一個值。
- min_data_in_bin: 一個整數,表示每個桶的最小樣本數。默認為3。該方法可以避免出現一個桶只有一個樣本的情況。
- data_random_seed: 一個整數,表示並行學習數據分隔中的隨機數種子。默認為1它不包括特征並行。
- output_model或者model_output或者model_out: 一個字符串,表示訓練中輸出的模型被保存的文件的文件名。默認txt 。
- input_model或者model_input或者model_in: 一個字符串,表示輸入模型的文件的文件名。默認空字符串。對於prediction任務,該模型將用於預測數據,對於train任務,訓練將從該模型繼續
- output_result或者 predict_result或者prediction_result:一個字符串,給出了prediction 結果存放的文件名。默認為txt。
- pre_partition 或者 is_pre_partition: 一個布爾值,指示數據是否已經被划分。默認值為False。 如果為true,則不同的機器使用不同的partition 來訓練。它用於並行學習(不包括特征並行)
- is_sparse或者 is_enable_sparse或者enable_sparse: 一個布爾值,表示是否開啟稀疏優化,默認為True。如果為True則啟用稀疏優化。
- two_round 或者two_round_loading或者 use_two_round_loading: 一個布爾值,指示是否啟動兩次加載。默認值為False,表示只需要進行一次加載。默認情況下,lightgbm 會將數據文件映射到內存,然后從內存加載特征,這將提供更快的數據加載速度。但是當數據文件很大時,內存可能會被耗盡。如果數據文件太大,則將它設置為True
- save_binary或者is_save_binary或者 is_save_binary_file: 一個布爾值,表示是否將數據集(包括驗證集)保存到二進制文件中。默認值為False。如果為True,則可以加快數據的加載速度。
- verbosity 或者verbose:一個整數,表示是否輸出中間信息。默認值為1。如果小於0,則僅僅輸出critical 信息;如果等於0,則還會輸出error,warning 信息; 如果大於0,則還會輸出info 信息。
- header或者has_header:一個布爾值,表示輸入數據是否有頭部。默認為False。
- label 或者label_column:一個字符串,表示標簽列。默認為空字符串。你也可以指定一個整數,如label=0 表示第0列是標簽列。你也可以為列名添加前綴,如label=prefix:label_name
- weight 或者weight_column: 一個字符串,表示樣本權重列。默認為空字符串。你也可以指定一個整數,如weight=0 表示第0列是權重列。注意:它是剔除了標簽列之后的索引。假如標簽列為0,權重列為1,則這里weight=0。你也可以為列名添加前綴,如weight=prefix:weight_name
- query 或者query_column或者gourp 或者group_column: 一個字符串,query/group ID 列。默認為空字符串。你也可以指定一個整數,如query=0 表示第0列是query列。注意:它是剔除了標簽列之后的索引。假如標簽列為0,query列為1,則這里query=0。你也可以為列名添加前綴,如query=prefix:query_name
- ignore_column 或者 ignore_feature或者blacklist: 一個字符串,表示訓練中忽略的一些列,默認為空字符串。可以用數字做索引,如ignore_column=0,1,2 表示第0,1,2 列將被忽略。注意:它是剔除了標簽列之后的索引。
- 你也可以為列名添加前綴,如ignore_column=prefix:ign_name1,ign_name2
- categorical_feature 或者categorical_column或者cat_feature或者 cat_column:一個字符串,指定category 特征的列。默認為空字符串。可以用數字做索引,如categorical_feature=0,1,2 表示第0,1,2 列將作為category 特征。注意:它是剔除了標簽列之后的索引。你也可以為列名添加前綴,如categorical_feature=prefix:cat_name1,cat_name2 在categorycal 特征中,負的取值被視作缺失值。
- predict_raw_score 或者raw_score或者 is_predict_raw_score:一個布爾值,表示是否預測原始得分。默認為False。如果為True則僅預測原始得分。該參數只用於prediction 任務。
- predict_leaf_index 或者 leaf_index或者 is_predict_leaf_index: 一個布爾值,表示是否預測每個樣本在每棵樹上的葉節點編號。默認為False。在預測時,每個樣本都會被分配到每棵樹的某個葉子節點上。該參數就是要輸出這些葉子節點的編號。該參數只用於prediction 任務。
- predict_contrib 或者 contrib或者 is_predict_contrib: 一個布爾值,表示是否輸出每個特征對於每個樣本的預測的貢獻。默認為False。輸出的結果形狀為[nsamples,nfeatures+1], 之所以+1 是考慮到bais 的貢獻。所有的貢獻加起來就是該樣本的預測結果。該參數只用於prediction 任務。
- bin_construct_sample_cnt 或者 subsample_for_bin:一個整數,表示用來構建直方圖的樣本的數量。默認為200000。如果數據非常稀疏,則可以設置為一個更大的值,如果設置更大的值,則會提供更好的訓練效果,但是會增加數據加載時間。
- num_iteration_predict: 一個整數,表示在預測中使用多少棵子樹。默認為-1。小於等於0表示使用模型的所有子樹。該參數只用於prediction 任務。
- pred_early_stop:一個布爾值,表示是否使用早停來加速預測。默認為False。如果為True,則可能影響精度。
- pred_early_stop_freq: 一個整數,表示檢查早停的頻率。默認為10
- pred_early_stop_margin: 一個浮點數,表示早停的邊際閾值。默認為0
- use_missing: 一個布爾值,表示是否使用缺失值功能。默認為True如果為False 則禁用缺失值功能。
- zero_as_missing: 一個布爾值,表示是否將所有的零(包括在libsvm/sparse矩陣 中未顯示的值)都視為缺失值。 默認為False。如果為False,則將nan 視作缺失值。如果為True,則np.nan 和 零都將視作缺失值。
- init_score_file: 一個字符串,表示訓練時的初始化分數文件的路徑。默認為空字符串,表示train_data_file+”.init” (如果存在)
- valid_init_score_file: 一個字符串,表示驗證時的初始化分數文件的路徑。默認為空字符串,表示valid_data_file+”.init” (如果存在)。如果有多個(對應於多個驗證集),則可以用逗號, 來分隔。
目標函數的參數:
- sigmoid: 一個浮點數,用sigmoid 函數的參數,默認為0。它用於二分類任務和lambdarank 任務。
- alpha: 一個浮點數,用於Huber 損失函數和Quantile regression ,默認值為0。它用於huber回歸任務和Quantile 回歸任務。
- fair_c: 一個浮點數,用於Fair 損失函數,默認值為0 。它用於fair 回歸任務。
- gaussian_eta: 一個浮點數,用於控制高斯函數的寬度,默認值為0 。它用於regression_l1 回歸任務和huber回歸任務。
- posson_max_delta_step: 一個浮點數,用於Poisson regression 的參數,默認值為7 。它用於poisson 回歸任務。
- scale_pos_weight: 一個浮點數,用於調整正樣本的權重,默認值為0它用於二分類任務。
- boost_from_average: 一個布爾值,指示是否將初始得分調整為平均值(它可以使得收斂速度更快)。默認為True。它用於回歸任務。
- is_unbalance或者unbalanced_set : 一個布爾值,指示訓練數據是否均衡的。默認為True。它用於二分類任務。
- max_position: 一個整數,指示將在這個NDCG 位置優化。默認為 20 。它用於lambdarank 任務。
- label_gain: 一個浮點數序列,給出了每個標簽的增益。默認值為0,1,3,7,15,….它用於lambdarank 任務。
- num_class或者num_classes : 一個整數,指示了多分類任務中的類別數量。默認為 1它用於多分類任務。
- reg_sqrt: 一個布爾值,默認為False。如果為True,則擬合的結果為:$\sqrt{label}$。同時預測的結果被自動轉換為:${pred}^2$。它用於回歸任務。
度量參數:
- metric:一個字符串,指定了度量的指標,默認為:對於回歸問題,使用l2 ;對於二分類問題,使用binary_logloss;對於lambdarank 問題,使用ndcg。如果有多個度量指標,則用逗號, 分隔。
- ‘l1’ 或者 mean_absolute_error或者 mae或者 regression_l1: 表示絕對值損失
- ‘l2’ 或者mean_squared_error或者 mse或者 regression_l2或者 regression:表示平方損失
- ‘l2_root’ 或者root_mean_squared_error或者 rmse:表示開方損失
- ‘quantile’: 表示Quantile 回歸中的損失
- ‘mape’ 或者 ‘mean_absolute_percentage_error’ :表示MAPE 損失
- ‘huber’: 表示huber 損失
- ‘fair’: 表示fair 損失
- ‘poisson’: 表示poisson 回歸的負對數似然
- ‘gamma’: 表示gamma 回歸的負對數似然
- ‘gamma_deviance’: 表示gamma 回歸的殘差的方差
- ‘tweedie’: 表示Tweedie 回歸的負對數似然
- ‘ndcg’: 表示NDCG
- ‘map’ 或者’mean_average_precision’: 表示平均的精度
- ‘auc’: 表示AUC
- ‘binary_logloss’或者’binary’: 表示二類分類中的對數損失函數
- ‘binary_error’: 表示二類分類中的分類錯誤率
- ‘multi_logloss’或者 ‘multiclass’或者 ‘softmax’或者 ‘multiclassova’或者 ‘multiclass_ova’,或者’ova’或者 ‘ovr’: 表示多類分類中的對數損失函數
- ‘multi_error’: 表示多分類中的分類錯誤率
- ‘xentropy’或者’cross_entropy’: 表示交叉熵
- ‘xentlambda’ 或者’cross_entropy_lambda’: 表示intensity 加權的交叉熵
- ‘kldiv’或者’kullback_leibler’: 表示KL 散度
- metric_freq或者’output_freq’:一個正式,表示每隔多少次輸出一次度量結果。默認為1。
- train_metric 或者training_metric或者 is_training_metric: 一個布爾值,默認為False。如果為True,則在訓練時就輸出度量結果。
- ndcg_at 或者 ndcg_eval_at 或者eval_at: 一個整數列表,指定了NDCG 評估點的位置。默認為1,2,3,4,5 。
調參示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import
lightgbm
as
lgb
X=
df
.
iloc
[
:
,
:-
1
]
y=
df
.
iloc
[
:
,-
1
]
category_feature=
[
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
]
cv_params=
{
'num_leaves'
:
[
13
,
14
,
15
]
,
# 'max_depth': [-1,4,6,8],
# 'learning_rate': [0.07,0.08,0.09],
# 'n_estimators':[10,15,20],
# 'min_child_samples':[15,20,25],
# 'subsample':[0.4,0.5,0.6,0.7],
# 'colsample_bytree':[0.4,0.5,0.6,0.7],
# 'reg_alpha':[0,1,2,3,5,8],
# 'reg_lambda':[7,8,9,10],
# 'num_iterations':[30,40,50],
# 'min_data_in_leaf': [30, 50, 100, 300, 400],
# 'cat_smooth':[150,160,170,180,190]
}
# cv_params = {'learning_rate': [0.06,0.07,0.08,0.09]}
other_params=
{
'max_depth'
:
4
,
'num_leaves'
:
15
,
'learning_rate'
:
0.07
,
'cat_smooth'
:
180
,
'num_iterations'
:
100
,
'colsample_bytree'
:
0.7
,
'subsample'
:
0.4
,
'reg_alpha'
:
3
,
'reg_lambda'
:
9
,
}
model_lgb=
lgb
.
LGBMRegressor
(**
other_params
)
optimized_lgb=
GridSearchCV
(
estimator=
model_lgb
,
param_grid=
cv_params
,
scoring=
'r2'
,
cv=
5
,
verbose=
1
,
n_jobs=
2
)
optimized_lgb
.
fit
(
X
,
y
,
categorical_feature=
category_feature
)
print
(
'參數的最佳取值:{0}'
.
format
(
optimized_lgb
.
best_params_
)
)
print
(
'最佳模型得分:{0}'
.
format
(
optimized_lgb
.
best_score_
)
)
print
(
optimized_lgb
.
cv_results_
[
'mean_test_score'
]
)
print
(
optimized_lgb
.
cv_results_
[
'params'
]
)
|
sklearn接口形式的LightGBM示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
import
lightgbm
as
lgb
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
# 加載數據
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=
lgb
.
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
)
# 測試機預測
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
)
# feature importances
print
(
'Feature importances:'
,
list
(
gbm
.
feature_importances_
)
)
# 網格搜索,參數優化
estimator=
lgb
.
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_
)
|
原生形式使用lightgbm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import
lightgbm
as
lgb
from
sklearn
.
metrics
import
mean_squared_error
from
sklearn
.
datasets
import
load_iris
from
sklearn
.
model_selection
import
train_test_split
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
)
# 創建成lgb特征的數據集格式
lgb_train=
lgb
.
Dataset
(
X_train
,
y_train
)
lgb_eval=
lgb
.
Dataset
(
X_test
,
y_test
,
reference=
lgb_train
)
# 將參數寫成字典下形式
params=
{
'task'
:
'train'
,
'boosting_type'
:
'gbdt'
,
# 設置提升類型
'objective'
:
'regression'
,
# 目標函數
'metric'
:
{
'l2'
,
'auc'
}
,
# 評估函數
'num_leaves'
:
31
,
# 葉子節點數
'learning_rate'
:
0.05
,
# 學習速率
'feature_fraction'
:
0.9
,
# 建樹的特征選擇比例
'bagging_fraction'
:
0.8
,
# 建樹的樣本采樣比例
'bagging_freq'
:
5
,
# k 意味着每 k 次迭代執行bagging
'verbose'
:
1
# <0 顯示致命的, =0 顯示錯誤 (警告), >0 顯示信息
}
# 訓練 cv and train
gbm=
lgb
.
train
(
params
,
lgb_train
,
num_boost_round=
20
,
valid_sets=
lgb_eval
,
early_stopping_rounds=
5
)
# 保存模型到文件
gbm
.
save_model
(
'model.txt'
)
# 預測數據集
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
)
|
參數速查
xgb | lgb | xgb.sklearn | lgb.sklearn |
booster=’gbtree’ | boosting=’gbdt’ | booster=’gbtree’ | boosting_type=’gbdt’ |
objective=’binary:logistic’ | application=’binary’ | objective=’binary:logistic’ | objective=’binary’ |
max_depth=7 | num_leaves=2**7 | max_depth=7 | num_leaves=2**7 |
eta=0.1 | learning_rate=0.1 | learning_rate=0.1 | learning_rate=0.1 |
num_boost_round=10 | num_boost_round=10 | n_estimators=10 | n_estimators=10 |
gamma=0 | min_split_gain=0.0 | gamma=0 | min_split_gain=0.0 |
min_child_weight=5 | min_child_weight=5 | min_child_weight=5 | min_child_weight=5 |
subsample=1 | bagging_fraction=1 | subsample=1.0 | subsample=1.0 |
colsample_bytree=1.0 | feature_fraction=1 | colsample_bytree=1.0 | colsample_bytree=1.0 |
alpha=0 | lambda_l1=0 | reg_alpha=0.0 | reg_alpha=0.0 |
lambda=1 | lambda_l2=0 | reg_lambda=1 | reg_lambda=0.0 |
scale_pos_weight=1 | scale_pos_weight=1 | scale_pos_weight=1 | scale_pos_weight=1 |
seed | bagging_seed feature_fraction_seed | random_state=888 | random_state=888 |
nthread | num_threads | n_jobs=4 | n_jobs=4 |
evals | valid_sets | eval_set | eval_set |
eval_metric | metric | eval_metric | eval_metric |
early_stopping_rounds | early_stopping_rounds | early_stopping_rounds | early_stopping_rounds |
verbose_eval | verbose_eval | verbose | verbose |