python機器學習案例系列教程——LightGBM算法

安裝

pip install lightgbm

gitup網址:https://github.com/Microsoft/LightGBM

中文教程

http://lightgbm.apachecn.org/cn/latest/index.html

lightGBM簡介

xgboost的出現,讓數據民工們告別了傳統的機器學習算法們:RF、GBM、SVM、LASSO……..。現在微軟推出了一個新的boosting框架,想要挑戰xgboost的江湖地位。

顧名思義,lightGBM包含兩個關鍵點:light即輕量級,GBM 梯度提升機。

LightGBM 是一個梯度 boosting 框架,使用基於學習算法的決策樹。它可以說是分佈式的,高效的,有以下優勢:

更快的訓練效率

低內存使用

更高的準確率

支持並行化學習

可處理大規模數據

xgboost缺點

其缺點,或者說不足之處:

每輪迭代時,都需要遍歷整個訓練數據多次。如果把整個訓練數據裝進內存則會限制訓練數據的大小;如果不裝進內存,反覆地讀寫訓練數據又會消耗非常大的時間。

預排序方法(pre-sorted):首先,空間消耗大。這樣的算法需要保存數據的特徵值,還保存了特徵排序的結果(例如排序後的索引,為了後續快速的計算分割點),這裡需要消耗訓練數據兩倍的內存。其次時間上也有較大的開銷,在遍歷每一個分割點的時候,都需要進行分裂增益的計算,消耗的代價大。

對cache優化不友好。在預排序後,特徵對梯度的訪問是一種隨機訪問,並且不同的特徵訪問的順序不一樣,無法對cache進行優化。同時,在每一層長樹的時候,需要隨機訪問一個行索引到葉子索引的數組,並且不同特徵訪問的順序也不一樣,也會造成較大的cache miss。

lightGBM特點

以上與其說是xgboost的不足,倒不如說是lightGBM作者們構建新算法時著重瞄準的點。解決了什麼問題,那麼原來模型沒解決就成了原模型的缺點。

概括來說,lightGBM主要有以下特點:

基於Histogram的決策樹算法

帶深度限制的Leaf-wise的葉子生長策略

直方圖做差加速

直接支持類別特徵(Categorical Feature)

Cache命中率優化

基於直方圖的稀疏特徵優化

多線程優化

前2個特點使我們尤為關注的。

Histogram算法

直方圖算法的基本思想:先把連續的浮點特徵值離散化成k個整數,同時構造一個寬度為k的直方圖。遍歷數據時,根據離散化後的值作為索引在直方圖中累積統計量,當遍歷一次數據後,直方圖累積了需要的統計量,然後根據直方圖的離散值,遍歷尋找最優的分割點。

帶深度限制的Leaf-wise的葉子生長策略

Level-wise過一次數據可以同時分裂同一層的葉子,容易進行多線程優化,也好控制模型複雜度,不容易過擬合。但實際上Level-wise是一種低效算法,因為它不加區分的對待同一層的葉子,帶來了很多沒必要的開銷,因為實際上很多葉子的分裂增益較低,沒必要進行搜索和分裂。

Leaf-wise則是一種更為高效的策略:每次從當前所有葉子中,找到分裂增益最大的一個葉子,然後分裂,如此循環。因此同Level-wise相比,在分裂次數相同的情況下,Leaf-wise可以降低更多的誤差,得到更好的精度。

Leaf-wise的缺點:可能會長出比較深的決策樹,產生過擬合。因此LightGBM在Leaf-wise之上增加了一個最大深度限制,在保證高效率的同時防止過擬合。

xgboost和lightgbm

決策樹算法

XGBoost使用的是pre-sorted算法,能夠更精確的找到數據分隔點;

首先,對所有特徵按數值進行預排序。

其次,在每次的樣本分割時,用O(# data)的代價找到每個特徵的最優分割點。

最後,找到最後的特徵以及分割點,將數據分裂成左右兩個子節點。

優缺點:

這種pre-sorting算法能夠準確找到分裂點,但是在空間和時間上有很大的開銷。

i. 由於需要對特徵進行預排序並且需要保存排序後的索引值(為了後續快速的計算分裂點),因此內存需要訓練數據的兩倍。

ii. 在遍歷每一個分割點的時候,都需要進行分裂增益的計算,消耗的代價大。

LightGBM使用的是histogram算法,佔用的內存更低,數據分隔的複雜度更低。

其思想是將連續的浮點特徵離散成k個離散值,並構造寬度為k的Histogram。然後遍歷訓練數據,統計每個離散值在直方圖中的累計統計量。在進行特徵選擇時,只需要根據直方圖的離散值,遍歷尋找最優的分割點。

Histogram 算法的優缺點:

Histogram算法並不是完美的。由於特徵被離散化後,找到的並不是很精確的分割點,所以會對結果產生影響。但在實際的數據集上表明,離散化的分裂點對最終的精度影響並不大,甚至會好一些。原因在於decision tree本身就是一個弱學習器,採用Histogram算法會起到正則化的效果,有效地防止模型的過擬合。

時間上的開銷由原來的O(#data * #features)降到O(k * #features)。由於離散化,#bin遠小於#data,因此時間上有很大的提升。

Histogram算法還可以進一步加速。一個葉子節點的Histogram可以直接由父節點的Histogram和兄弟節點的Histogram做差得到。一般情況下,構造Histogram需要遍歷該葉子上的所有數據,通過該方法,只需要遍歷Histogram的k個捅。速度提升了一倍。

決策樹生長策略

XGBoost採用的是按層生長level(depth)-wise生長策略,如Figure 1所示,能夠同時分裂同一層的葉子,從而進行多線程優化,不容易過擬合;但不加區分的對待同一層的葉子,帶來了很多沒必要的開銷。因為實際上很多葉子的分裂增益較低,沒必要進行搜索和分裂。

python機器學習案例系列教程——LightGBM算法

LightGBM採用leaf-wise生長策略,如Figure 2所示,每次從當前所有葉子中找到分裂增益最大(一般也是數據量最大)的一個葉子,然後分裂,如此循環。因此同Level-wise相比,在分裂次數相同的情況下,Leaf-wise可以降低更多的誤差,得到更好的精度。Leaf-wise的缺點是可能會長出比較深的決策樹,產生過擬合。因此LightGBM在Leaf-wise之上增加了一個最大深度的限制,在保證高效率的同時防止過擬合。

python機器學習案例系列教程——LightGBM算法

網絡通信優化

XGBoost由於採用pre-sorted算法,通信代價非常大,所以在並行的時候也是採用histogram算法;LightGBM採用的histogram算法通信代價小,通過使用集合通信算法,能夠實現並行計算的線性加速。

LightGBM支持類別特徵

實際上大多數機器學習工具都無法直接支持類別特徵,一般需要把類別特徵,轉化one-hotting特徵,降低了空間和時間的效率。而類別特徵的使用是在實踐中很常用的。基於這個考慮,LightGBM優化了對類別特徵的支持,可以直接輸入類別特徵,不需要額外的0/1展開。並在決策樹算法上增加了類別特徵的決策規則。

lightGBM調參

所有的參數含義,參考:http://lightgbm.apachecn.org/cn/latest/Parameters.html

調參過程:

(1)num_leaves

LightGBM使用的是leaf-wise的算法,因此在調節樹的複雜程度時,使用的是num_leaves而不是max_depth。

大致換算關係:num_leaves = 2^(max_depth)

(2)樣本分佈非平衡數據集:可以param[‘is_unbalance’]=’true’

(3)Bagging參數:bagging_fraction+bagging_freq(必須同時設置)、feature_fraction

(4)min_data_in_leaf、min_sum_hessian_in_leaf

sklearn接口形式的LightGBM示例

這裡主要以sklearn的使用形式來使用lightgbm算法,包含建模,訓練,預測,網格參數優化。

import lightgbm as lgb
import pandas as pd
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
from sklearn.datasets import make_classification
# 加載數據
print('Load data...')
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)
# df_train = pd.read_csv('../regression/regression.train', header=None, sep='\\t')
# df_test = pd.read_csv('../regression/regression.test', header=None, sep='\\t')
# y_train = df_train[0].values
# y_test = df_test[0].values
# X_train = df_train.drop(0, axis=1).values
# X_test = df_test.drop(0, axis=1).values
print('Start training...')
# 創建模型,訓練模型
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)
print('Start predicting...')
# 測試機預測
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

# coding: utf-8
# pylint: disable = invalid-name, C0111
import json
import lightgbm as lgb
import pandas as pd
from sklearn.metrics import mean_squared_error
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
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)
# 加載你的數據
# print('Load data...')
# df_train = pd.read_csv('../regression/regression.train', header=None, sep='\\t')
# df_test = pd.read_csv('../regression/regression.test', header=None, sep='\\t')
#
# y_train = df_train[0].values
# y_test = df_test[0].values
# X_train = df_train.drop(0, axis=1).values
# X_test = df_test.drop(0, axis=1).values
# 創建成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 顯示信息
}
print('Start training...')
# 訓練 cv and train
gbm = lgb.train(params,lgb_train,num_boost_round=20,valid_sets=lgb_eval,early_stopping_rounds=5)
print('Save model...')
# 保存模型到文件
gbm.save_model('model.txt')
print('Start predicting...')
# 預測數據集
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)


分享到:


相關文章: