LightGBM大戰XGBoost,誰將奪得桂冠?
碼字不易,歡迎給個贊!
歡迎交流與轉載,文章會同步發布在公眾號:機器學習演算法全棧工程師(Jeemy110)
微信文章鏈接:
LightGBM大戰XGBoost,誰將奪得桂冠?0. 引言
如果你是一個機器學習社區的活躍成員,你一定知道 提升機器(Boosting Machine)以及它們的能力。提升機器從AdaBoost發展到目前最流行的XGBoost。XGBoost實際上已經成為贏得在Kaggle比賽中公認的演算法。這很簡單,因為他極其強大。但是,如果數據量極其的大,XGBoost也需要花費很長的時間去訓練。
絕大多數人可能對 Light Gradient Boosting 不熟悉,但是讀完本文後你就會對他們很熟悉。一個很自然的問題將進入你的思索:為什麼又會出現另一個提升機器演算法?它比XGBoost要好嗎?
注意:本文假設讀者已經對 GBMs 和 XGBoost 演算法有一定的了解。如果你不了解他們,請先了解一下他們的原理再來學習本文。
1. 什麼是LightGBM
LightGBM是個快速的、分散式的、高性能的基於決策樹演算法的梯度提升框架。可用於排序、分類、回歸以及很多其他的機器學習任務中。 因為他是基於決策樹演算法的,它採用最優的leaf-wise策略分裂葉子節點,然而其它的提升演算法分裂樹一般採用的是depth-wise或者level-wise而不是leaf-wise。因此,在LightGBM演算法中,當增長到相同的葉子節點,leaf-wise演算法比level-wise演算法減少更多的loss。因此導致更高的精度,而其他的任何已存在的提升演算法都不能夠達。與此同時,它的速度也讓人感到震驚,這就是該演算法名字 Light 的原因。 前文是一個由LightGBM演算法作者的概要式的描述來簡要地解釋LightGBM的不同之處。- XGBoost中決策樹的增長方式示意圖
- LightGBM中決策樹的增長方式示意圖
Leaf-Wise分裂導致複雜性的增加並且可能導致過擬合。但是這是可以通過設置另一個參數 max-depth 來克服,它分裂產生的樹的最大深度。
接下來我們將介紹安裝LightGBM的步驟使用它來跑一個模型。我們將對比LightGBM和XGBoost的實驗結果來證明你應該使用LightGBM在一種輕輕的方式(Light Manner)。2. LightGBM的優勢
首先讓我們看一看LightGBM的優勢。
- 更快的訓練速度和更高的效率: LightGBM使用基於直方圖的演算法。例如,它將連續的特徵值分桶(buckets)裝進離散的箱子(bins),這是的訓練過程中變得更快。
- 更低的內存佔用:使用離散的箱子(bins)保存並替換連續值導致更少的內存佔用。
- 更高的準確率(相比於其他任何提升演算法) : 它通過leaf-wise分裂方法產生比level-wise分裂方法更複雜的樹,這就是實現更高準確率的主要因素。然而,它有時候或導致過擬合,但是我們可以通過設置 max-depth 參數來防止過擬合的發生。
- 大數據處理能力: 相比於XGBoost,由於它在訓練時間上的縮減,它同樣能夠具有處理大數據的能力。
- 支持並行學習
3. 安裝LightGBM
本節介紹如何在各種操作系統下安裝LightGBM。眾所周知,桌面系統目前使用最多的就是Windows、Linux和macOS,因此,就依次介紹如何在這三種操作系統下安裝LightGBM。
3.1 Windows
對於Windows操作系統,由於其並非開源操作系統,因此一直以來Windows系統對開發者來說並不友好。我們需要安裝相應的編譯環境才能對LightGBM源代碼進行編譯。對於Windows下的底層C/C++編譯環境,目前主要有微軟自己的Visual Studio(或者MSBuild)或者開源的MinGW64,下面我們依次介紹這兩種編譯環境下的LightGBM的安裝。
注意,對於以下兩種編譯環境,我們都共同需要確保系統已經安裝Windows下的Git和CMake工具。- 3.1.1 基於Visual Studio(MSBuild)環境
git clone --recursive https://github.com/Microsoft/LightGBMcd LightGBMmkdir buildcd buildcmake -DCMAKE_GENERATOR_PLATFORM=x64 ..cmake --build . --target ALL_BUILD --config Release
最終編譯生成的exe和dll會在 LightGBM/Release 目錄下。
- 3.1.2 基於MinGW64環境
git clone --recursive https://github.com/Microsoft/LightGBMcd LightGBMmkdir buildcd buildcmake -G "MinGW Makefiles" ..mingw32-make.exe -j
最終編譯生成的exe和dll會在 LightGBM/ 目錄下。
3.2 Linux
在Linux系統下,我們同樣適用cmake進行編譯,運行如下的shell命令:
git clone --recursive https://github.com/Microsoft/LightGBMcd LightGBMmkdir buildcd buildcmake ..make -j
3.3 macOS
LightGBM依賴OpenMP來編譯,但是它不支持蘋果的Clang,請使用gcc/g++替代。運行如下的命令進行編譯:
brew install cmakebrew install gcc --without-multilibgit clone --recursive https://github.com/Microsoft/LightGBMcd LightGBMmkdir build cd buildcmake ..make -j
現在,在我們投入研究構建我們第一個LightGBM模型之前,讓我們看一下LightGBM的一些參數,以更好的了解其基本過程。
4. LightGBM的重要參數
- task: 默認值=train,可選項=train,prediction;指定我們希望執行的任務,該任務有兩種類型:訓練 和 預測;
- application: 默認值=regression,type=enum,options=options;
- regression: 執行回歸任務;
- binary:二分類;
- multiclass:多分類;
- lambdarank:lambrank應用;
- data: type=string;training data,LightGBM將從這些數據中進行訓練;
- num_iterations: 默認值為100,類型為int。表示提升迭代次數,也就是提升樹的棵樹;
- num_leaves: 每個樹上的葉子數,默認值為31,類型為int;
- device: 默認值=cpu;可選項:cpu,gpu。也就是我們使用什麼類型的設備去訓練我們的模型。選擇GPU會使得訓練過程更快;
- mindatain_leaf: 每個葉子上的最少數據;
- feature_fraction: 默認值為1;指定每次迭代所需要的特徵部分;
- bagging_fraction: 默認值為1;指定每次迭代所需要的數據部分,並且它通常是被用來提升訓練速度和避免過擬合的。
- mingainto_split: 默認值為1;執行分裂的最小的信息增益;
- max_bin: 最大的桶的數量,用來裝數值的;
- mindatain_bin: 每個桶內最少的數據量;
- numthreads: 默認值為OpenMPdefault,類型為int。指定LightGBM演算法運行時線程的數量;
- label: 類型為string;指定標籤列;
- categorical_feature: 類型為string;指定我們想要進行模型訓練所使用的特徵類別;
- num_class: 默認值為1,類型為int;僅僅需要在多分類的場合。
5. LightGBM與XGBoost對比
現在讓我們通過在同一個數據集上進行訓練,對比一下LightGBM和XGBoost的性能差異。
在這裡我們使用的數據集來自很多國家的個人信息。我們的目標是基於其他的基本信息來預測每個人的年收入是否超過50K(<=50K 和 >50K兩種)。該數據集包含32561個被觀測者和14個描述每個個體的特徵。這裡是數據集的鏈接: http://archive.ics.uci.edu/ml/datasets/Adult。通過對數據集的預測變數有一個正確的理解這樣你才能夠更好的理解下面的代碼。
#importing standard libraries import numpy as np import pandas as pd from pandas import Series, DataFrame #import lightgbm and xgboost import lightgbm as lgb import xgboost as xgb #loading our training dataset adult.csv with name data using pandas data=pd.read_csv(adult.csv,header=None) #Assigning names to the columns data.columns=[age,workclass,fnlwgt,education,education-num,marital_Status,occupation,relationship,race,sex,capital_gain,capital_loss,hours_per_week,native_country,Income] #glimpse of the dataset data.head() # Label Encoding our target variable from sklearn.preprocessing import LabelEncoder,OneHotEncoderl=LabelEncoder() l.fit(data.Income) l.classes_ data.Income=Series(l.transform(data.Income)) #label encoding our target variable data.Income.value_counts() #One Hot Encoding of the Categorical features one_hot_workclass=pd.get_dummies(data.workclass) one_hot_education=pd.get_dummies(data.education) one_hot_marital_Status=pd.get_dummies(data.marital_Status) one_hot_occupation=pd.get_dummies(data.occupation)one_hot_relationship=pd.get_dummies(data.relationship) one_hot_race=pd.get_dummies(data.race) one_hot_sex=pd.get_dummies(data.sex) one_hot_native_country=pd.get_dummies(data.native_country) #removing categorical features data.drop([workclass,education,marital_Status,occupation,relationship,race,sex,native_country],axis=1,inplace=True) #Merging one hot encoded features with our dataset data data=pd.concat([data,one_hot_workclass,one_hot_education,one_hot_marital_Status,one_hot_occupation,one_hot_relationship,one_hot_race,one_hot_sex,one_hot_native_country],axis=1) #removing dulpicate columns _, i = np.unique(data.columns, return_index=True) data=data.iloc[:, i] #Here our target variable is Income with values as 1 or 0. #Separating our data into features dataset x and our target dataset y x=data.drop(Income,axis=1) y=data.Income #Imputing missing values in our target variable y.fillna(y.mode()[0],inplace=True) #Now splitting our dataset into test and train from sklearn.model_selection import train_test_split x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=.3)
5.1 使用XGBoost
#The data is stored in a DMatrix object #label is used to define our outcome variabledtrain=xgb.DMatrix(x_train,label=y_train)dtest=xgb.DMatrix(x_test)#setting parameters for xgboostparameters={max_depth:7, eta:1, silent:1,objective:binary:logistic,eval_metric:auc,learning_rate:.05}#training our model num_round=50from datetime import datetime start = datetime.now() xg=xgb.train(parameters,dtrain,num_round) stop = datetime.now()#Execution time of the model execution_time_xgb = stop-start print(execution_time_xgb)#datetime.timedelta( , , ) representation => (days , seconds , microseconds) #now predicting our model on test set ypred=xg.predict(dtest) print(ypred)#Converting probabilities into 1 or 0 for i in range(0,9769): if ypred[i]>=.5: # setting threshold to .5 ypred[i]=1 else: ypred[i]=0 #calculating accuracy of our model from sklearn.metrics import accuracy_score accuracy_xgb = accuracy_score(y_test,ypred) print(accuracy_xgb)
5.2 使用LightGBM
train_data=lgb.Dataset(x_train,label=y_train)setting parameters for lightgbmparam = {num_leaves:150, objective:binary,max_depth:7,learning_rate:.05,max_bin:200}param[metric] = [auc, binary_logloss]#Here we have set max_depth in xgb and LightGBM to 7 to have a fair comparison between the two.#training our model using light gbmnum_round=50start=datetime.now()lgbm=lgb.train(param,train_data,num_round)stop=datetime.now()#Execution time of the modelexecution_time_lgbm = stop-startprint(execution_time_lgbm)#predicting on test setypred2=lgbm.predict(x_test)print(ypred2[0:5]) # showing first 5 predictions#converting probabilities into 0 or 1for i in range(0,9769): if ypred2[i]>=.5: # setting threshold to .5 ypred2[i]=1 else: ypred2[i]=0#calculating accuracyaccuracy_lgbm = accuracy_score(ypred2,y_test)accuracy_lgbmy_test.value_counts()from sklearn.metrics import roc_auc_score#calculating roc_auc_score for xgboostauc_xgb = roc_auc_score(y_test,ypred)print(auc_xgb)#calculating roc_auc_score for light gbm. auc_lgbm = roc_auc_score(y_test,ypred2)auc_lgbm comparison_dict = {accuracy scoreaccuracy_lgbm,accuracy_xgb),auc scoreauc_lgbm,auc_xgb),execution timeexecution_time_lgbm,execution_time_xgb)}#Creating a dataframe 『comparison_df』 for comparing the performance of Lightgbm and xgb. comparison_df = DataFrame(comparison_dict) comparison_df.index= [LightGBM,xgboost] print(comparison_df)
5.3 性能對比
下面的表格列出了演算法的各項指標對比結果:
從上述的性能對比結果來看,LightGBM對比XGBoost的準確率和AUC值都只有很小的提升。但是,一個至關重要的差別是模型訓練過程的執行時間。LightGBM的訓練速度幾乎比XGBoost快7倍,並且隨著訓練數據量的增大差別會越來越明顯。
這證明了LightGBM在大數據集上訓練的巨大的優勢,尤其是在具有時間限制的對比中。5.4 詳細對比
6. LightGBM的參數調優
LightGBM使用基於depth-wise的分裂的leaf-wise分裂演算法,這使得它能夠更快地收斂。但是,它也會導致過擬合。因此,這裡給出一個LightGBM參數調優的快速指南。
6.1 為了最好的擬合
- numleaves:這個參數是用來設置組成每棵樹的葉子的數量。numleaves 和 maxdepth理論上的聯繫是: numleaves = 2^(maxdepth)。然而,但是如果使用LightGBM的情況下,這種估計就不正確了:因為它使用了leafwise而不是depthwise分裂葉子節點。因此,numleaves必須設置為一個小於2^(maxdepth)的值。否則,他將可能會導致過擬合。LightGBM的numleave和max_depth這兩個參數之間沒有直接的聯繫。因此,我們一定不要把兩者聯繫在一起。
- mindatain_leaf : 它也是一個用來解決過擬合的非常重要的參數。把它的值設置的特別小可能會導致過擬合,因此,我們需要對其進行相應的設置。因此,對於大數據集來說,我們應該把它的值設置為幾百到幾千。
- max_depth: 它指定了每棵樹的最大深度或者它能夠生長的層數上限。
6.2 為了更快的速度
- bagging_fraction : 它被用來執行更快的結果裝袋;
- feature_fraction : 設置每一次迭代所使用的特徵子集;
- maxbin : maxbin的值越小越能夠節省更多的時間:當它將特徵值分桶裝進不同的桶中的時候,這在計算上是很便宜的。
6.3 為了更高的準確率
- 使用更大的訓練數據集;
- num_leaves : 把它設置得過大會使得樹的深度更高、準確率也隨之提升,但是這會導致過擬合。因此它的值被設置地過高不好。
- maxbin : 該值設置地越高導致的效果和numleaves的增長效果是相似的,並且會導致我們的訓練過程變得緩慢。
7. 結束語
在本文中,我給出了關於LightGBM的直觀的想法。現在使用該演算法的一個缺點是它的用戶基礎太少了。但是種局面將很快得到改變。該演算法除了比XGBoost更精確和節省時間以外,現在被使用的很少的原因是他的可用文檔太少。
然而,該演算法已經展現出在結果上遠超其他已存在的提升演算法。我強烈推薦你去使用LightGBM與其他的提升演算法,並且自己親自感受一下他們之間的不同。 也許現在說LightGBM演算法稱雄還為時過早。但是,他確實挑戰了XGBoost的地位。給你一句警告:就像其他任何機器學習演算法一樣,在使用它進行模型訓練之前確保你正確的調試了參數。碼字不易,歡迎給個贊!
歡迎交流與轉載,文章會同步發布在公眾號:機器學習演算法全棧工程師(Jeemy110)
推薦閱讀:
※手把手教你使用ggplot2繪製折線圖
※Kaggle Titanic 生存預測(Top1.4%)完整代碼分享
※對於面試演算法工程師的想法
※玩轉Pandas,讓數據處理更easy系列5