若是你是一個機器學習社區的活躍成員,你必定知道 提高機器(Boosting Machine)以及它們的能力。提高機器從AdaBoost發展到目前最流行的XGBoost。XGBoost實際上已經成爲贏得在Kaggle比賽中公認的算法。這很簡單,由於他極其強大。可是,若是數據量極其的大,XGBoost也須要花費很長的時間去訓練。python
絕大多數人可能對 Light Gradient Boosting 不熟悉,可是讀完本文後你就會對他們很熟悉。一個很天然的問題將進入你的思索:爲何又會出現另外一個提高機器算法?它比XGBoost要好嗎?git
注意:本文假設讀者已經對 GBMs 和 XGBoost 算法有必定的瞭解。若是你不瞭解他們,請先了解一下他們的原理再來學習本文。github
LightGBM是個快速的、分佈式的、高性能的基於決策樹算法的梯度提高框架。可用於排序、分類、迴歸以及不少其餘的機器學習任務中。算法
由於他是基於決策樹算法的,它採用最優的leaf-wise策略分裂葉子節點,然而其它的提高算法分裂樹通常採用的是depth-wise或者level-wise而不是leaf-wise。所以,在LightGBM算法中,當增加到相同的葉子節點,leaf-wise算法比level-wise算法減小更多的loss。所以致使更高的精度,而其餘的任何已存在的提高算法都不可以達。與此同時,它的速度也讓人感到震驚,這就是該算法名字 Light 的緣由。shell
前文是一個由LightGBM算法做者的概要式的描述來簡要地解釋LightGBM的不一樣之處。api
Leaf-Wise分裂致使複雜性的增長而且可能致使過擬合。可是這是能夠經過設置另外一個參數 max-depth 來克服,它分裂產生的樹的最大深度。緩存
接下來咱們將介紹安裝LightGBM的步驟使用它來跑一個模型。咱們將對比LightGBM和XGBoost的實驗結果來證實你應該使用LightGBM在一種輕輕的方式(Light Manner)。ruby
首先讓咱們看一看LightGBM的優點。bash
更快的訓練速度和更高的效率:LightGBM使用基於直方圖的算法。例如,它將連續的特徵值分桶(buckets)裝進離散的箱子(bins),這是的訓練過程當中變得更快。網絡
更低的內存佔用:使用離散的箱子(bins)保存並替換連續值致使更少的內存佔用。
**更高的準確率(相比於其餘任何提高算法) **:****它經過leaf-wise分裂方法產生比level-wise分裂方法更復雜的樹,這就是實現更高準確率的主要因素。然而,它有時候或致使過擬合,可是咱們能夠經過設置 max-depth 參數來防止過擬合的發生。
大數據處理能力:****相比於XGBoost,因爲它在訓練時間上的縮減,它一樣可以具備處理大數據的能力。
支持並行學習
3
本節介紹如何在各類操做系統下安裝LightGBM。衆所周知,桌面系統目前使用最多的就是Windows、Linux和macOS,所以,就依次介紹如何在這三種操做系統下安裝LightGBM。
對於Windows操做系統,因爲其並不是開源操做系統,所以一直以來Windows系統對開發者來講並不友好。咱們須要安裝相應的編譯環境才能對LightGBM源代碼進行編譯。對於Windows下的底層C/C++編譯環境,目前主要有微軟本身的Visual Studio(或者MSBuild)或者開源的MinGW64,下面咱們依次介紹這兩種編譯環境下的LightGBM的安裝。
注意,對於如下兩種編譯環境,咱們都共同須要確保系統已經安裝Windows下的Git和CMake工具。
git clone --recursive https://github.com/Microsoft/LightGBM cd LightGBM mkdir build cd build cmake -DCMAKE_GENERATOR_PLATFORM=x64 .. cmake --build . --target ALL_BUILD --config Release
最終編譯生成的exe和dll會在 LightGBM/Release 目錄下。
git clone --recursive https://github.com/Microsoft/LightGBM cd LightGBM mkdir build cd build cmake -G "MinGW Makefiles" .. mingw32-make.exe -j
最終編譯生成的exe和dll會在 LightGBM/ 目錄下。
在Linux系統下,咱們一樣適用cmake進行編譯,運行以下的shell命令:
git clone --recursive https://github.com/Microsoft/LightGBM cd LightGBM mkdir build cd build cmake .. make -j
LightGBM依賴OpenMP來編譯,可是它不支持蘋果的Clang,請使用gcc/g++替代。運行以下的命令進行編譯:
brew install cmake
brew install gcc --without-multilib
git clone --recursive https://github.com/Microsoft/LightGBM cd LightGBM mkdir build cd build cmake .. make -j
如今,在咱們投入研究構建咱們第一個LightGBM模型以前,讓咱們看一下LightGBM的一些參數,以更好的瞭解其基本過程。
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會使得訓練過程更快;
min_data_in_leaf:每一個葉子上的最少數據;
feature_fraction:默認值爲1;指定每次迭代所須要的特徵部分;
bagging_fraction:默認值爲1;指定每次迭代所須要的數據部分,而且它一般是被用來提高訓練速度和避免過擬合的。
min_gain_to_split:默認值爲1;執行分裂的最小的信息增益;
max_bin:最大的桶的數量,用來裝數值的;
min_data_in_bin:每一個桶內最少的數據量;
num_threads:默認值爲OpenMP_default,類型爲int。指定LightGBM算法運行時線程的數量;
label:類型爲string;指定標籤列;
categorical_feature:類型爲string;指定咱們想要進行模型訓練所使用的特徵類別;
num_class:默認值爲1,類型爲int;僅僅須要在多分類的場合。
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,OneHotEncoder l=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)
#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)
train_data=lgb.Dataset(x_train,label=y_train)
setting parameters for lightgbm param = {'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-start print(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_lgbm y_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 score':(accuracy_lgbm,accuracy_xgb),'auc score':(auc_lgbm,auc_xgb),'execution time':(execution_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)
下面的表格列出了算法的各項指標對比結果:
算法 | accuracy score | auc score | 執行時間(S) |
---|---|---|---|
LightGBM | 0.861501 | 0.764492 | 0.283759 |
XGBoost | 0.861398 | 0.764284 | 2.047220 |
從上述的性能對比結果來看,LightGBM對比XGBoost的準確率和AUC值都只有很小的提高。可是,一個相當重要的差異是模型訓練過程的執行時間。LightGBM的訓練速度幾乎比XGBoost快7倍,而且隨着訓練數據量的增大差異會愈來愈明顯。
這證實了LightGBM在大數據集上訓練的巨大的優點,尤爲是在具備時間限制的對比中。
對比項 | XGBoost | LightGBM |
---|---|---|
正則化 | L1/L2 | L1/L2 |
列採樣 | yes | yes |
Exact Gradient | yes | yes |
近似算法 | yes | no |
稀疏數據 | yes | yes |
分佈式並行 | yes | yes |
緩存 | yes | no |
out of core | yes | no |
加權數據 | yes | yes |
樹增加方式 | level-wise | leaf-wise |
基於算法 | pre-sorted | histgram |
最大樹深度控制 | 無 | 有 |
dropout | no | yes |
Bagging | yes | yes |
用途 | 迴歸、分類、rank | 迴歸、分類、lambdrank |
GPU支持 | no | yes |
網絡通訊 | point-to-point | collective-communication |
CategoricalFeatures | 無優化 | 優化 |
Continued train with input GBDT model | no | yes |
Continued train with input | no | yes |
Early Stopping(both training and prediction) | no | yes |
LightGBM的參數調優
1.爲了最好的擬合
num_leaves:這個參數是用來設置組成每棵樹的葉子的數量。num_leaves 和 max_depth理論上的聯繫是: num_leaves = 2(max_depth)。然而,可是若是使用LightGBM的狀況下,這種估計就不正確了:由於它使用了leaf_wise而不是depth_wise分裂葉子節點。所以,num_leaves必須設置爲一個小於2(max_depth)的值。不然,他將可能會致使過擬合。LightGBM的num_leave和max_depth這兩個參數之間沒有直接的聯繫。所以,咱們必定不要把二者聯繫在一塊兒。
min_data_in_leaf : 它也是一個用來解決過擬合的很是重要的參數。把它的值設置的特別小可能會致使過擬合,所以,咱們須要對其進行相應的設置。所以,對於大數據集來講,咱們應該把它的值設置爲幾百到幾千。
max_depth: 它指定了每棵樹的最大深度或者它可以生長的層數上限。
2.爲了更快的速度
bagging_fraction : 它被用來執行更快的結果裝袋;
feature_fraction : 設置每一次迭代所使用的特徵子集;
max_bin : max_bin的值越小越可以節省更多的時間:當它將特徵值分桶裝進不一樣的桶中的時候,這在計算上是很便宜的。
3.爲了更高的準確率
使用更大的訓練數據集;
num_leaves : 把它設置得過大會使得樹的深度更高、準確率也隨之提高,可是這會致使過擬合。所以它的值被設置地太高很差。
max_bin : 該值設置地越高致使的效果和num_leaves的增加效果是類似的,而且會致使咱們的訓練過程變得緩慢。
結束語
在本文中,我給出了關於LightGBM的直觀的想法。如今使用該算法的一個缺點是它的用戶基礎太少了。可是種局面將很快獲得改變。該算法除了比XGBoost更精確和節省時間之外,如今被使用的不多的緣由是他的可用文檔太少。
然而,該算法已經展示出在結果上遠超其餘已存在的提高算法。我強烈推薦你去使用LightGBM與其餘的提高算法,而且本身親自感覺一下他們之間的不一樣。
也許如今說LightGBM算法稱雄還爲時過早。可是,他確實挑戰了XGBoost的地位。給你一句警告:就像其餘任何機器學習算法同樣,在使用它進行模型訓練以前確保你正確的調試了參數。