資料科學競賽分享 (I) – 2020 Shopee Code League (附 github 連結)

[Competition] 2020-shopee-code-league-competition-8-edm-open-prediction

第一個參加的跨國大型資料科學競賽,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 屬性中的 attr_1, attr_2 有大量缺值,各補為一種獨立類別。
  • 數值型變數
    • user-level 屬性中的 age 也存在缺值,且分佈中存在極值,在這邊補中位數處理。

變數處理:異常值確認

  • 數值型變數
    • 觀察到一小部分的紀錄,EDM-level 屬性中的 last_XXX_dayXXX_count_last_XX_days 的意義衝突,這部分統一先參考 feature importance score 較高的 last_XXX_day

(譬如 last_open_day 資料顯示最近一次開信發生在 8 天前,但在 open_count_last_10_days 卻為 0 ,照理說至少要 >=1 次。)

變數處理:其他變數轉換

  • 類別型變數
    • user-level 屬性中的 domain 下共有 11 種不同信箱網域,除了全部 one-hot encoding 造成比較稀疏的資料格式外,這次嘗試根據 開信率 把 domain 分成三組,分別是開信率低/中/高的。

  • 數值型變數
    • EDM-level 屬性中的 last_XXX_day 當中包含 “Never open” (Never login/Never checkout),將其轉換成一個比 last_XXX_day 最大值還大的一個數字,並且各新增一個布林值欄位 isNeverOpen (isNeverLogin, isNeverCheckout) 額外紀錄該 user 是否從未開過信/登入或結帳。

特徵生成

  • 時間進程的關聯open_count_per10days__inlast60days, open_count_per10days__inlast30days
    • 資料中提供了 10/30/60天內的次數,我猜想如果距離當前EDM發送越近的時間點,該 user 是越來越頻繁的開信的話,那他開下一封信的機率也會越高
      因此製造出代表 60天及30天內的平均開信次數(每10天) 的變數。

  • 收信日規律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成以上解釋力。

建模:(資料集分割、變數縮放)

  1. 資料集亂序分割
    • 在縮放資料以前,先將 dataset 亂序後分成 train, validation, test;其中 train 為訓練集、val 為模型初次驗證集、test 為模型完全沒看過的測試集。
  2. 變數縮放
    • 接著縮放 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

Leave a Reply