第一個參加的跨國大型資料科學競賽,8場小比賽都有全勤參與;
但人生很難,top1%更難,過程中往往望大神興嘆。
為了振作起來,本文挑選跟電商、行銷 domain 相關的第八賽段,同時也是相對比較沒有被虐的 part,來聊聊解題思路和代碼。
本文大綱
(I) Marketing Analytics: 預測用戶是否開信(EDM)
基本描述
- 問題類型:二元分類 (0=未開信,1=開信)
- 資料變數
- User-level
- age
- mailbox domain
- attributes
- EDM-level
- sent datetime
- subject line length
- last open/login/checkout day (最近一次開信/登入/下單購買 為幾天前)
- open/login/checkout count last 10/30/60 days (近10/30/60天內 開信/登入/下單購買 的次數)
- User-level
變數處理:缺失值確認
- 類別型變數
- user-level 屬性中的
attr_1
,attr_2
有大量缺值,各補為一種獨立類別。
- user-level 屬性中的
- 數值型變數
- user-level 屬性中的
age
也存在缺值,且分佈中存在極值,在這邊補中位數處理。
- user-level 屬性中的
變數處理:異常值確認
- 數值型變數
- 觀察到一小部分的紀錄,EDM-level 屬性中的
last_XXX_day
和XXX_count_last_XX_days
的意義衝突,這部分統一先參考 feature importance score 較高的last_XXX_day
。
- 觀察到一小部分的紀錄,EDM-level 屬性中的
(譬如 last_open_day
資料顯示最近一次開信發生在 8 天前,但在 open_count_last_10_days
卻為 0 ,照理說至少要 >=1 次。)
變數處理:其他變數轉換
- 類別型變數
- user-level 屬性中的
domain
下共有 11 種不同信箱網域,除了全部 one-hot encoding 造成比較稀疏的資料格式外,這次嘗試根據 開信率 把domain
分成三組,分別是開信率低/中/高的。
- user-level 屬性中的
- 數值型變數
- EDM-level 屬性中的
last_XXX_day
當中包含 “Never open” (Never login/Never checkout),將其轉換成一個比last_XXX_day
最大值還大的一個數字,並且各新增一個布林值欄位isNeverOpen
(isNeverLogin
,isNeverCheckout
) 額外紀錄該 user 是否從未開過信/登入或結帳。
- EDM-level 屬性中的
特徵生成
- 時間進程的關聯:
open_count_per10days__inlast60days
,open_count_per10days__inlast30days
- 資料中提供了 10/30/60天內的次數,我猜想如果距離當前EDM發送越近的時間點,該 user 是越來越頻繁的開信的話,那他開下一封信的機率也會越高。
因此製造出代表 60天及30天內的平均開信次數(每10天) 的變數。
- 資料中提供了 10/30/60天內的次數,我猜想如果距離當前EDM發送越近的時間點,該 user 是越來越頻繁的開信的話,那他開下一封信的機率也會越高。
- 收信日規律:
dayOfMonth
,dayOfWeek
,isWeekend
- 美中不足:資料中只紀錄了寄出時間,但沒有開信時間
- 針對寄出時間:觀察到不同天的開信率有波動(可能與節日或營銷活動有關),因此在 EDM-level 的資料屬性中將 datetime 拆解為 日、星期幾 以及 是否為週末。
- 彼此相關程度高的變數組合先進行降維、縮減變數數量 (open/login/checkout)
open_count_last_10_days
,open_count_last_30_days
,open_count_last_60_days
(login, checkout 亦然) 三項變數一起降維後,發現 第一主成分即有9成以上解釋力。
建模:(資料集分割、變數縮放)
- 資料集亂序分割
- 在縮放資料以前,先將 dataset 亂序後分成 train, validation, test;其中 train 為訓練集、val 為模型初次驗證集、test 為模型完全沒看過的測試集。
- 在縮放資料以前,先將 dataset 亂序後分成 train, validation, test;其中 train 為訓練集、val 為模型初次驗證集、test 為模型完全沒看過的測試集。
- 變數縮放
- 接著縮放 train set 變數至 0-1 之間,以免不同 scale 的變數影響模型收斂效果,再將同樣的縮放比例應用到 validation set 和 test set。
- 接著縮放 train set 變數至 0-1 之間,以免不同 scale 的變數影響模型收斂效果,再將同樣的縮放比例應用到 validation set 和 test set。
!注意!
(1) 不可以直接用 validation set 和 test set 的分佈進行縮放!
如果這麼做了,導致 train set 和 validation, test set 使用不同的比例縮放,假設本來某一筆 test set 資料和 train set 資料值相同,因為不同的縮放比例,會被模型當成是意義不同的值!以至於最後模型做出的預測可能出現偏差。(2) 也不能對整個資料集一起統一縮放!
舉最常見的縮放,標準化為例,取用資料集標準差和平均數,改變資料數值;如果使用整個資料集,相當於 train set 使用到了 validation set 及 test set 的分佈資訊進行縮放轉換、這樣模型就會學到它本來不該先看到的東西!(偷看答案的概念)可能會造成我們高估模型成效。
成效檢視
(I) ML Classifier: Random Forest / SVM / LightGBM / XGBoost
[Best] XGBoost: 0.49769
(II) DNN (Keras Sequential API)
[Best] 0.51344
最後附上 notebook
(後來還沒寫成模組化 functions,看起來可能比較亂 😅)
https://github.com/A-baoYang/2020-shopee-code-league/tree/master/Week8_20200801