阿里Datawhale二手車價格預測——優勝獎方案總結(代碼開源)

阿里天池聯合Datawhale比賽傳送鏈接
官方論壇本文鏈接
比賽代碼開源鏈接

本次比賽的最終名次是13/2814,剛好擠上了優勝獎的末班車。。

比賽介紹

賽題以二手車市場爲背景,要求選手預測二手汽車的交易價格,這是一個典型的迴歸問題。
其他具體流程可以看比賽官網。

數據處理

1、box-cox變換目標值“price”,解決長尾分佈。

2、刪除與目標值無關的列,例如“SaleID”,“name”。這裏可以挖掘一下“name”的長度作爲新的特徵。

3、異常點處理,刪除訓練集特有的數據,例如刪除“seller”==1的值。

4、缺失值處理,分類特徵填充衆數,連續特徵填充平均值。

5、其他特別處理,把取值無變化的列刪掉。

6、異常值處理,按照題目要求“power”位於0~600,因此把“power”>600的值截斷至600,把"notRepairedDamage"的非數值的值替換爲np.nan,讓模型自行處理。

特徵工程

1、時間地區類
從“regDate”,“creatDate”可以獲得年、月、日等一系列的新特徵,然後做差可以獲得使用年長和使用天數這些新特徵。

“regionCode”沒有保留。
因爲嘗試了一系列方法,並且發現了可能會泄漏“price”,因此最終沒保留該特徵。

2、分類特徵
對可分類的連續特徵進行分桶,kilometer是已經分桶了。
然後對"power"和"model"進行了分桶。

使用分類特徵“brand”、“model”、“kilometer”、“bodyType”、“fuelType”與“price”、“days”、“power”進行特徵交叉。
交叉主要獲得的是後者的總數、方差、最大值、最小值、平均數、衆數、峯度等等

這裏可以獲得非常多的新特徵,挑選的時候,直接使用lightgbm幫我們去選擇特徵,一組組的放進去,最終保留了以下特徵。(注意:這裏使用1/4的訓練集進行挑選可以幫助我們更快的鎖定真正Work的特徵)

'model_power_sum','model_power_std', 
'model_power_median', 'model_power_max',
'brand_price_max', 'brand_price_median',
'brand_price_sum', 'brand_price_std',
'model_days_sum','model_days_std', 
'model_days_median', 'model_days_max', 
'model_amount','model_price_max', 
'model_price_median','model_price_min', 
'model_price_sum', 'model_price_std',
'model_price_mean'

3、連續特徵
使用了置信度排名靠前的匿名特徵“v_0”、“v_3”與“price”進行交叉,測試方法以上述一樣,效果並不理想。
因爲都是匿名特徵,比較訓練集和測試集分佈,分析完基本沒什麼問題,並且它們在lightgbm的輸出的重要性都是非常高的,所以先暫且全部保留。

4、補充特徵工程
主要是對輸出重要度非常高的特徵進行處理
特徵工程一期
對14個匿名特徵使用乘法處理得到14*14個特徵

使用sklearn的自動特徵選擇幫我們去篩選,大概運行了半天的時間。
大致方法如下:

from mlxtend.feature_selection import SequentialFeatureSelector as SFS
from sklearn.linear_model import LinearRegression
sfs = SFS(LGBMRegressor(n_estimators = 1000,objective='mae' ),
           k_features=50,
           forward=True,
           floating=False,
           cv = 0)


sfs.fit(X_data, Y_data)
print(sfs.k_feature_names_)

最終篩選得到:

'new3*3', 'new12*14', 'new2*14','new14*14'

特徵工程二期
對14個匿名特徵使用加法處理得到14*14個特徵
這次不選擇使用自動特徵選擇了,因爲運行實在太慢了,筆記本耗不起。
使用的方法是刪除相關性高的變量,把要刪除的特徵記錄下來
大致方法如下:(剔除相關度>0.95的)

corr = X_data.corr(method='spearman')
feature_group = list(itertools.combinations(corr.columns, 2))
print(feature_group)

# 刪除相關性高的變量,調試好直接去主函數進行剔除
def filter_corr(corr, cutoff=0.7):
    cols = []
    for i,j in feature_group:
        if corr.loc[i, j] > cutoff:
            print(i,j,corr.loc[i, j])
            i_avg = corr[i][corr[i] != 1].mean()
            j_avg = corr[j][corr[j] != 1].mean()
            if i_avg >= j_avg:
                cols.append(i)
            else:
                cols.append(j)
    return set(cols)

drop_cols = filter_corr(corr, cutoff=0.95)
print(drop_cols)

最終獲得的應該刪除的特徵爲:

['new14+6', 'new13+6', 'new0+12', 'new9+11', 'v_3', 'new11+10', 'new10+14', 'new12+4', 'new3+4', 'new11+11', 'new13+3', 'new8+1', 'new1+7', 'new11+14', 'new8+13', 'v_8', 'v_0', 'new3+5', 'new2+9', 'new9+2', 'new0+11', 'new13+7', 'new8+11', 'new5+12', 'new10+10', 'new13+8', 'new11+13', 'new7+9', 'v_1', 'new7+4', 'new13+4', 'v_7', 'new5+6', 'new7+3', 'new9+10', 'new11+12', 'new0+5', 'new4+13', 'new8+0', 'new0+7', 'new12+8', 'new10+8', 'new13+14', 'new5+7', 'new2+7', 'v_4', 'v_10', 'new4+8', 'new8+14', 'new5+9', 'new9+13', 'new2+12', 'new5+8', 'new3+12', 'new0+10', 'new9+0', 'new1+11', 'new8+4', 'new11+8', 'new1+1', 'new10+5', 'new8+2', 'new6+1', 'new2+1', 'new1+12', 'new2+5', 'new0+14', 'new4+7', 'new14+9', 'new0+2', 'new4+1', 'new7+11', 'new13+10', 'new6+3', 'new1+10', 'v_9', 'new3+6', 'new12+1', 'new9+3', 'new4+5', 'new12+9', 'new3+8', 'new0+8', 'new1+8', 'new1+6', 'new10+9', 'new5+4', 'new13+1', 'new3+7', 'new6+4', 'new6+7', 'new13+0', 'new1+14', 'new3+11', 'new6+8', 'new0+9', 'new2+14', 'new6+2', 'new12+12', 'new7+12', 'new12+6', 'new12+14', 'new4+10', 'new2+4', 'new6+0', 'new3+9', 'new2+8', 'new6+11', 'new3+10', 'new7+0', 'v_11', 'new1+3', 'new8+3', 'new12+13', 'new1+9', 'new10+13', 'new5+10', 'new2+2', 'new6+9', 'new7+10', 'new0+0', 'new11+7', 'new2+13', 'new11+1', 'new5+11', 'new4+6', 'new12+2', 'new4+4', 'new6+14', 'new0+1', 'new4+14', 'v_5', 'new4+11', 'v_6', 'new0+4', 'new1+5', 'new3+14', 'new2+10', 'new9+4', 'new2+6', 'new14+14', 'new11+6', 'new9+1', 'new3+13', 'new13+13', 'new10+6', 'new2+3', 'new2+11', 'new1+4', 'v_2', 'new5+13', 'new4+2', 'new0+6', 'new7+13', 'new8+9', 'new9+12', 'new0+13', 'new10+12', 'new5+14', 'new6+10', 'new10+7', 'v_13', 'new5+2', 'new6+13', 'new9+14', 'new13+9', 'new14+7', 'new8+12', 'new3+3', 'new6+12', 'v_12', 'new14+4', 'new11+9', 'new12+7', 'new4+9', 'new4+12', 'new1+13', 'new0+3', 'new8+10', 'new13+11', 'new7+8', 'new7+14', 'v_14', 'new10+11', 'new14+8', 'new1+2']]

特徵工程三、四期
這兩期的效果不明顯,爲了不讓特徵冗餘,所以選擇不添加這兩期的特徵,具體的操作可以在feature處理的代碼中看到。

5、神經網絡的特徵工程補充說明
以上特徵工程處理都是針對於樹模型來進行的,接下來,簡單說明神經網絡的數據預處理。
各位都知道由於NN的不可解釋性,可以生成大量的我們所不清楚的特徵,因此我們對於NN的數據預處理只要簡單處理異常值以及缺失值。

大部分的方法都包含在以上針對樹模型數據處理方法中,重點講述幾個不同點:
在對於“notRepairedDamage”的編碼處理,對於二分類的缺失值,往往取其中間值。
在對於其他缺失值的填充,在測試了效果後,發現填充衆數的效果比平均數更好,因此均填充衆數。

選擇的模型

本次比賽,我選擇的是lightgbm+catboost+neural network。
本來也想使用XGBoost的,不過因爲它需要使用二階導,因此目標函數沒有MAE,並且用於逼近的一些自定義函數效果也不理想,因此沒有選擇使用它。

經過上述的數據預處理以及特徵工程:
樹模型的輸入有83個特徵;神經網絡的輸入有29個特徵。

1、lightgbm和catboost
因爲它們都是樹模型,因此我同時對這兩個模型進行分析

第一:lgb和cab的訓練收斂速度非常快,比同樣參數的xgb快非常多。
第二:它們可以處理缺失值,計算取值的增益,擇優錄取。
第三:調整正則化係數,均使用正則化,防止過擬合。
第四:降低學習率,獲得更小MAE的驗證集預測輸出。
第五:調整早停輪數,防止陷入過擬合或欠擬合。
第六:均使用交叉驗證,使用十折交叉驗證,減小過擬合。
其他參數設置無明顯上分跡象,以代碼爲準,不一一闡述。

2、neural network
在這裏插入圖片描述
設計了一個五層的神經網絡,大致框架如上圖所示,但結點數由於太多隻是展示部分結點畫圖。(無聊畫了個。。)
以下爲全連接層的結點個數設置,具體實施可參考代碼。
在這裏插入圖片描述
接下來對神經網絡進行具體分析:
第一:訓練模型使用小batchsize,512,雖然在下降方向上可能會出現小偏差,但是對收斂速度的收益大,2000代以內可以收斂。
第二:神經網絡對於特徵工程這一類不用操心很多,就能達到與樹模型相差無幾的精度。
第三:調整正則化係數,使用正則化,防止過擬合。
第四:調整學習率,對訓練過程的誤差進行分析,選擇學習率下降的時機進行調整。
第五:使用交叉驗證,使用十折交叉驗證,減小過擬合。
第六:選擇梯度下降的優化器爲Adam,它是目前綜合能力較好的優化器,具備計算高效,對內存需求少等等優點。

集成的方法

由於兩個樹模型的訓練數據一樣且結構相似,首先對兩個樹模型進行stacking,然後再與神經網絡的輸出進行mix。
由於樹模型和神經網絡是完全不同的架構,它們得到的分數輸出相近,預測值差異較大,往往在MAE上差異爲200左右,因此將他們進行MIX可以取到一個更好的結果,加權平均選擇係數選擇0.5,雖然神經網絡的分數確實會比樹模型高一點點,但是我們的最高分是多組線上最優輸出的結合,因此可以互相彌補優勢。

在這裏插入圖片描述

後期上分

單個神經網絡和樹模型的結合在本地驗證集上都是420分左右;然後經過以上的模型集成,線下驗證集爲415分左右,線上得到的分數位於404~410,後期因爲沒有其他的上分方法,因此每上一分都是非常困難的,最終會使用更換種子輸出多個文件,挑選了最好的三個分數進行平均最終獲得線上第十三名。

賽後總結

本次賽後,看到論壇上的單模型就可以到達400分,因此個人對本次比賽總結一下幾點:

  • 我的最大的敗筆應該是沒有充分發揮神經網絡的優勢,沒有人爲的製造多一些的特徵;
  • 從這單單29個特徵的nn就可以媲美千挑萬選特徵的樹模型,我猜想高分選手使用的是神經網絡+更好的特徵工程;
  • 或者,本次比賽存在着leaky,讓敏銳的前排大佬們給捕捉到了,期待前排的開源;
  • 最後,感謝主辦方阿里天池與Datawhale,希望能夠繼續舉辦這樣有趣的賽事~
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章