樹+神經(jīng)網(wǎng)絡(luò)算法強強聯(lián)手(Python)
結(jié)合論文《Revisiting Deep Learning Models for Tabular Data》的觀點,集成樹模型通常擅長于表格數(shù)據(jù)這種異構(gòu)數(shù)據(jù)集,是實打?qū)嵉谋砀駭?shù)據(jù)王者。
集成樹模型中的LightGBM是增強版的GBDT,支持了分類變量,在工程層面大大提高了訓(xùn)練效率。關(guān)于樹模型的介紹,可見之前文章:一文講透樹模型
DNN深度神經(jīng)網(wǎng)絡(luò)擅長于同構(gòu)的高維數(shù)據(jù),從高維稀疏的表示中學(xué)習(xí)到低維致密的分布式表示,所以在自然語言、圖像識別等領(lǐng)域基本上是稱霸武林(神經(jīng)網(wǎng)絡(luò)的介紹及實踐可見系列文章:一文搞定深度學(xué)習(xí)建模全流程)。對于異構(gòu)致密的表格數(shù)據(jù),個人實踐來看,DNN模型的非線性能力沒樹模型來得高效。
所以一個很樸素的想法是,結(jié)合這樹模型+神經(jīng)網(wǎng)絡(luò)模型的優(yōu)勢。比如通過NN學(xué)習(xí)文本的嵌入特征后,輸入樹模型繼續(xù)學(xué)習(xí)(如word2vec+LGB做文本分類,可見文章:NLP建模全流程)?;蛘呤牵瑯淠P蛯W(xué)習(xí)表格數(shù)據(jù)后,輸出樣本的高維個葉子節(jié)點的特征表示,輸入DNN模型。
接下來,我們使用LightGBM+DNN模型強強聯(lián)手,驗證其在信貸違約的表格數(shù)據(jù)預(yù)測分類效果。
數(shù)據(jù)處理及樹模型訓(xùn)練
lightgbm樹模型,自帶缺失、類別變量的處理,還有很強的非線性擬合能力,特征工程上面不用做很多處理,建模非常方便。
##完整代碼及數(shù)據(jù)請見 算法進階github:https://github.com/aialgorithm/Blog
# 劃分?jǐn)?shù)據(jù)集:訓(xùn)練集和測試集
train_x, test_x, train_y, test_y = train_test_split(train_bank[num_feas + cate_feas], train_bank.isDefault,test_size=0.3, random_state=0)
# 訓(xùn)練模型
lgb=lightgbm.LGBMClassifier(n_estimators=5, num_leaves=5,class_weight= 'balanced',metric = 'AUC')
lgb.fit(train_x, train_y)
print('train ',model_metrics(lgb,train_x, train_y))
print('test ',model_metrics(lgb,test_x,test_y))
簡單處理建模后test的AUC可以達(dá)到0.8656
樹+神經(jīng)網(wǎng)絡(luò)
接下來我們將提取樹模型的葉子節(jié)點的路徑作為特征,并簡單做下特征選擇處理
import numpy as np
y_pred = lgb.predict(train_bank[num_feas + cate_feas],pred_leaf=True)
# 提取葉子節(jié)點
train_matrix = np.zeros([len(y_pred), len(y_pred[0])*lgb.get_params()['num_leaves']],dtype=np.int64)
print(train_matrix.shape)
for i in range(len(y_pred)):
temp = np.arange(len(y_pred[0]))*lgb.get_params()['num_leaves'] + np.array(y_pred[i])
train_matrix[i][temp] += 1
# drop zero-features
df2 = pd.DataFrame(train_matrix)
droplist2 = []
for k in df2.columns:
if not df2[k].any():
droplist2.append(k)
print(len(droplist2))
df2= df2.drop(droplist2,axis=1).add_suffix('_lgb')
# 拼接原特征和樹節(jié)點特征
df_final2 = pd.concat([train_bank[num_feas],df2],axis=1)
df_final2.head()

將拼接好原特征及樹節(jié)點路徑特征輸入神經(jīng)網(wǎng)絡(luò)模型,并使用網(wǎng)格搜索調(diào)優(yōu)神經(jīng)網(wǎng)絡(luò)模型。
# 劃分?jǐn)?shù)據(jù)集:訓(xùn)練集和測試集
train_x, test_x, train_y, test_y = train_test_split(df_final2, train_bank.isDefault,test_size=0.3, random_state=0)
# 神經(jīng)網(wǎng)絡(luò)模型評估
def model_metrics2(nnmodel, x, y):
yprob = nnmodel.predict(x.replace([np.inf, -np.inf], np.nan).fillna(0))[:,0]
fpr,tpr,_ = roc_curve(y, yprob,pos_label=1)
return auc(fpr, tpr),max(tpr-fpr)
import keras
from keras import regularizers
from keras.layers import Dense,Dropout,BatchNormalization,GaussianNoise
from keras.models import Sequential, Model
from keras.callbacks import EarlyStopping
from sklearn.metrics import mean_squared_error
np.random.seed(1) # 固定隨機種子,使每次運行結(jié)果固定
bestval = 0
# 創(chuàng)建神經(jīng)模型并暴力搜索較優(yōu)網(wǎng)絡(luò)結(jié)構(gòu)超參: 輸入層; n層k個神經(jīng)元的relu隱藏層; 輸出層
for layer_nums in range(2): #隱藏層的層數(shù)
for k in list(range(1,100,5)): # 網(wǎng)格神經(jīng)元數(shù)
for norm in [0.01,0.05,0.1,0.2,0.4,0.6,0.8]:#正則化懲罰系數(shù)
print("************隱藏層vs神經(jīng)元數(shù)vs norm**************",layer_nums,k,norm)
model = Sequential()
model.add(BatchNormalization()) # 輸入層 批標(biāo)準(zhǔn)化 input_dim=train_x.shape
for _ in range(layer_nums):
model.add(Dense(k,
kernel_initializer='random_uniform', # 均勻初始化
activation='relu', # relu激活函數(shù)
kernel_regularizer=regularizers.l1_l2(l1=norm, l2=norm), # L1及L2 正則項
use_bias=True)) # 隱藏層1
model.add(Dropout(norm)) # dropout正則
model.add(Dense(1,use_bias=True,activation='sigmoid')) # 輸出層
# 編譯模型:優(yōu)化目標(biāo)為回歸預(yù)測損失mse,優(yōu)化算法為adam
model.compile(optimizer='adam', loss=keras.losses.binary_crossentropy)
# 訓(xùn)練模型
history = model.fit(train_x.replace([np.inf, -np.inf], np.nan).fillna(0),
train_y,
epochs=1000, # 訓(xùn)練迭代次數(shù)
batch_size=1000, # 每epoch采樣的batch大小
validation_data=(test_x.replace([np.inf, -np.inf], np.nan).fillna(0),test_y), # 從訓(xùn)練集再拆分驗證集,作為早停的衡量指標(biāo)
callbacks=[EarlyStopping(monitor='val_loss', patience=10)], #早停法
verbose=False) # 不輸出過程
print("驗證集最優(yōu)結(jié)果:",min(history.history['loss']),min(history.history['val_loss']))
print('------------train------------\n',model_metrics2(model, train_x,train_y))
print('------------test------------\n',model_metrics2(model, test_x,test_y))
test_auc = model_metrics2(model, test_x,test_y)[0]
if test_auc > bestval:
bestval = test_auc
bestparas = ['bestval, layer_nums, k, norm',bestval, layer_nums, k, norm]
# 模型評估:擬合效果
plt.plot(history.history['loss'],c='blue') # 藍(lán)色線訓(xùn)練集損失
plt.plot(history.history['val_loss'],c='red') # 紅色線驗證集損失
plt.show()
model.summary() #模型概述信息
print(bestparas)


在我們這個實驗中,使用樹模型+神經(jīng)網(wǎng)絡(luò)模型在test的auc得到一些不錯的提升,樹模型的AUC 0.8656,而樹模型+神經(jīng)網(wǎng)絡(luò)的AUC 0.8776,提升了1.2%
其他試驗結(jié)果
結(jié)合微軟的試驗,樹+神經(jīng)網(wǎng)絡(luò)(DeepGBM),在不同的任務(wù)上也是可以帶來一些的效果提升的。有興趣可以閱讀下文末參考文獻(xiàn)。
LGB+DNN(或者單層的LR)是一個很不錯的想法,有提升模型的一些效果。但需要注意的是,這也會加重模型的落地及迭代的復(fù)雜度。綜合來看,樹+神經(jīng)網(wǎng)絡(luò)是一個好的故事,但是結(jié)局沒有太驚艷。
- END -參考論文:https://www.microsoft.com/en-us/research/uploads/prod/2019/08/deepgbm_kdd2019__CR_.pdf https://github.com/motefly/DeepGBM
