全棧工程師開發手冊 (作者:欒鵬)
python教程全解 安裝
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所示,能夠同時分裂同一層的葉子,從而進行多線程最佳化,不容易過擬合;但不加區分的對待同一層的葉子,帶來了很多沒必要的開銷。因為實際上很多葉子的分裂增益較低,沒必要進行搜尋和分裂。
LightGBM採用leaf-wise生長策略,如Figure 2所示,每次從當前所有葉子中找到分裂增益最大(一般也是資料量最大)的一個葉子,然後分裂,如此迴圈。因此同Level-wise相比,在分裂次數相同的情況下,Leaf-wise可以降低更多的誤差,得到更好的精度。Leaf-wise的缺點是可能會長出比較深的決策樹,產生過擬合。因此LightGBM在Leaf-wise之上增加了一個最大深度的限制,在保證高效率的同時防止過擬合。
網路通訊最佳化
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 lgbimport pandas as pdfrom sklearn.metrics import mean_squared_errorfrom sklearn.model_selection import GridSearchCVfrom sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_splitfrom sklearn.datasets import make_classification# 載入資料print('Load data...')iris = load_iris()data=iris.datatarget = iris.targetX_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).valuesprint('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 importancesprint('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, C0111import jsonimport lightgbm as lgbimport pandas as pdfrom sklearn.metrics import mean_squared_errorfrom sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_splitfrom sklearn.datasets import make_classificationiris = load_iris()data=iris.datatarget = iris.targetX_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 traingbm = 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)