機器學習專案|Kaggle — Airbnb New User Prediction(Top 24%)
這篇文章主要紀錄 Airbnb 之前在 Kaggle 上的預測競賽,Airbnb New User Bookings 。題目的目的,主要在預測根據已經有的使用者 Log 與基礎資料,來預測未來註冊的新用戶可能會預定那個地區的房子,並藉此在用戶剛註冊時,就以演算法來推薦用戶選項。
其中,評分的方式則是以 nDCG 的計算方式來評估推薦排序,藉以計算該演算法距離理想還有多少的差異,若想知道如何計算可以參考本篇文章。
Why do I want to choose this competition?
當初會想要以這個題目為主,是因為在疫情其間其實很想要出國去走走放輕鬆;但當時無奈只能選擇待在台灣。因此希望能把原本的動力,結合自己對機器學習的理解來嘗試解決業界的實際問題。
這個問題其實也和我之前所寫的文章 深入淺出常用推薦系統演算法 的目的相近,都是以使用者的歷史資料來進行推薦。這也是當平台開始累積用戶資料後,能夠提升使用者體驗(User Experience)的一大環節。
本篇文章將會透過多次的探索性分析(Exploratory Data Analysis)與特徵工程進行資料處理,並藉由極限梯度提升樹(XGBoost)作為訓練的演算法。最終使用 18 個翠取特徵,並達到比賽中的 Top24%(360/1458)。因為系統運算限制,並未採取更多層的模型來進行計算。
The result is as below:
使用的語言和工具則以 Python 和 Pandas、numpy、Sklearn、XGboost、matplotlib 等套件,基本上沒有太多過於困難的工具與模型。具體流程可以直接參考下面兩篇文章,皆有更完整的解說方式:
Content
1. Data & Environment Setting
- 平台使用與資料處理。
- 第一次探索性分析(EDA)
- Baseline Model
2. Feature Engineering
- 處理 training data
- 處理 session data
3. Model Building
- 建立模型與預測
- 評估模型
Data & Environment Setting
平台使用與資料處理
讀者可以直接在 Data Source 的地方下載到所有要分析的數據,整體的資料其實並不算多,再加上我本身使用的 Macbook Air 也是五年前的電腦,因此決定直接使用 Kaggle 提供的 Kernel 來運算。
第一次探索性分析(EDA)
本文的重點一樣會放在特徵工程與模型表現上,而這次的參賽隊伍其實有 1458 隊參與競賽;但可惜的是在 Kernel 的幾乎都僅有 Baseline 可以參考。
Importing and loading data
將資料與必要的套件讀取進來,並檢視整個資料集的 Shape。
Glance at train data table and details
接著快速檢視一下 train & test data 的 missing value,並列印出來。
我們可以發現 date_first_booking
在 test data
100% 完全沒有資料,換句話說這筆資料對預測完全沒有幫助,因此需要刪除;而 age
則也各有 40% 左右的資料有遺失值,等等需要考慮以 KNN 填補。
接著,大概瞥一眼整體的資料狀況,以及個別的資料擁有哪一些類別。
df_total.head()
從前面的五筆,我們就可以發現雖然 gender
並沒有任何缺失值;但實際上仍然有 -unknown-
的值。因此等等需要想辦法處理缺失值。
其他的變數則分別有:
從 train_data
的資料中,我們可以發現一些有趣且可以利用的資料。舉個例子來說:例如第一次的啟動時間、第一次的申請時間、裝置差異等資料,可以計算啟動與申請時間的差異,或是進行 EDA 瞭解使用不同的裝置是否有行為上的差異。
再來便是參考裡面很多人參考的 EDA Kernel 進行資料的探索性資料分析,為了避免文章過常,這裡僅附上部分的 plot code 來說明 EDA 的內容,有興趣的讀者可以直接參考文末的 Github Code。
Feature: Gender
可以發現 Unknown 的比例佔比為整體最高,因此可能需要考慮統一以隱含特徵來處理未知的資料。
Feature: Year & Month
接著我們以年份和 account_created 的數量來繪圖,可以發現在 Training data 當中,不同的年份確實有很明顯的分部差異;且我們可以看到月份確實會影響用戶的註冊意願。
進一步的分析,還可以帶到不同年份的用戶組成分佈是否不同?哪一部份的用戶崛起的最快等等問題。以下我們透過 Pivot_table 來進一步檢視資料的分佈狀況:
上面可以看到很有趣的趨勢是,15~30 歲的年輕人在之後隨著時間明顯上升,而在 7~10 月平穩一陣子後又明顯提升。進一步瞭解後面的 2014~2015 年後的數據,發現正是該族群的用戶明顯提升,讓第一張圖表的每年使用者大幅提昇。
Glance at session data in details
session_data.head()
我們可以觀察到 action_type
和 detail
的缺失,實際上和 action
有直接相關,凡是 action
為 lookup
的 type
和 detail
皆為 NaN,因此等等可以直接以 Simple Imputer
插值來將 NaN
填補成 lookup
。
進一步觀察,有一些 secs_elapsed
是隨機缺失的,因此預期一樣考慮以均值或 KNN 來進行填補。
接著,在 action
的項目我發現總共高達 360 個項目,因此我好奇這些項目的佔比為何,是否需要以分箱法來減少特徵量,避免稍後 one-hot encoding 有維度爆炸的危險。
'Ratio contains: 89.70893805376394'
我們發現僅是前面 30 個變數,就已經佔了接近 90% 的佔比。因此後來決定以前面 10 個變數各為一類,剩下的歸納成一類。
Baseline Model
一開始的 Baseline,僅將所有在 train data 的特徵進行資料轉換,並將所有的缺失值以 -1 補上。其 nDCG 獲得 0.851 的分數。因為多數的 Destination 都是 NDF(即沒有預定),因此很容易就獲得很不錯的預測,但其實在這個分數下,排名已經接近在後段的 80% 了。
我們也可以從下列的 train data 分類可以看到 NDF 和 US 就佔了 87%,因此 Baseline 基本都可以達到不錯的分數;但 nDCG 便是透過和理想排名之間的差異,來避免掉評估前面五名時,都只是把 NDF 和 US 放在前面,就因此難以評估差異了。
Building Hypothesis and Tasks
在我們檢視完資料後,就能以觀察到的資料,來預先設計我的特徵工程任務清單,以下是我具體做過的特徵清單。
Training data
- 看到所有的 Date 類別的資料,我預期 year、month 應該會對於預測有影響,因此將
timestamp_first_active
個別切成一項特徵。 - 看到所有的 Date 類別的資料,我預期 year、month 應該會對於預測有影響,因此將
date_account_created
個別切成一項特徵。 - 看到
age
的資料有些型態為 198X,猜測為將年紀誤填寫成西元年。因此將超出 1000 的資料減掉 2015 年作為新的年齡資料。 - 看到
age
有些資料低於 15 歲很明顯不合法規(因為線上刷卡),因此將低於 15 歲的資料刪除,並填補成 NaN。
Testing data
- Filled NaN with ‘-1’ as a missing value
- Calculate each user by: number of actions taken
- Calculate each user by: number of unique action_type, action_details, device
- Calculate each user by: sum of seconds of elaspe
Feature Engineering
將上述的這些假設與代辦清單整理好後,我們就可以參考上述的資料來進行特徵工程(Feature Engineering)。
Copy data set & separate timestamp
這裡特別提一下,在進行特徵工程時,盡量養成將資料 copy 一份副本後,以該份副本來編輯與處理。原因是如果以原始的資料來處理,一旦在處理的過程中失敗,就必須回到源頭重新將資料重新讀取與處理一次,會導致資料處理的速度變的很慢。
依據前面的清單與 EDA 的結果,我們可以知道 Year 和 Month 應該是具有影響力的特徵,因此特別將該欄位分出來。這裡我們個別處理 date_account_created
和 first_active
兩個 timestamp,並將處理過後的原是使資料丟棄。
Age fill missing value & transform
在這個階段,我簡單地以潛在特徵 -1 對遺失值進行填充,原因是缺失的值已經佔資料的 48%,如果單純以平均值來填補會造成分佈明顯不均,這很明顯不符合真實資料。再來並不使用 KNN 來進行差補,則是因為資料量較大,將導致計算緩慢,且在高維度的情況下很有可能因維度詛咒而是結果不佳。
未來會考慮使用 ANN(Approximate Nearest Neighbor),據論文結果與 KNN 相似度達 99% 以上;但速度快超過 300 倍。
接著將資料以 5 歲為間格,進行分箱以減少資料的維度差異。
Session Preprocessing
一樣將缺失值以 -1 轉換為潛在特徵,在 XGBoost 處理的時候,就會直接將該類別分成一類,以減少缺失值對預測的影響。
接著,分別計算 session data 裡面的動作次數、比例、動作的細節與類別等,並新增為新的特徵欄位。
在將上述特徵計算出來後,就來看個別資料的分佈狀態。從分佈可以看到所有的分佈都是右偏為主,因此對所有右偏的特徵進行一次的 log transform。
在經過 Log transform 之後,可以發現所有的資料都變成接近常態分佈。
最後將資料合併回原本的資料 df_total 來進行下一步的 one-hot encoding。
Model Building
在開始真的訓練模型之前,必須要先將之前的資料轉換成 One-hot Encoding,才能讓 XGBoost Classifier 運算。
接著將資料放進 XGBoost 裡面訓練模型,好了之後就可以獲得預測結果。
最後將資料進行轉換,把所有的資料轉換成 Kaggle 所需的檔案形式。
經過多次的調參和特徵工程之後,這是筆者目前依賴線上的 Kaggle Kernel能夠做到接近盡力的結果;之前曾經將 XGBoost 的步數稍微拉大一些到 100,Kernel 就曾經跑爆過兩次。之後應該就會開始考慮使用 GPU 或是 Colab 來試著運算看看,是否能在調參上更進一步。
Complete Code:
Summary
這是我第一次在學習機器學習後,實際以 Kaggle 的比賽來練習的專案。原本預期自己要在三個禮拜內推進到前 30 名,沒想到一個半禮拜就達到了,而且這也是第一次沒有可以直接參考的資料科學專案,而是看討論區、Baseline 還有各種 Sklearn 的官方文件嘗試自己實作。
對於踏入機器學習一小陣子,是一個非常棒的練習。而且在實作的過程,真的覺得比起去 DataCamp 的專案做好幾個,都不如直接到 Kaggle 挑一個專案實作來的有效。
我會繼續紀錄我在機器學習、產品管理的所見所學,如果喜歡的讀者歡迎大膽地按下你的追蹤,讓筆者繼續提供更多有趣有詳細的文章吧!
You May Interest In:
- 如何快速上手資料科學專案流程(上)A Step-by-Step Guide For A Data Science Project. I
- 快速上手資料科學專案流程全攻略(下)A Step-by-Step Guide For A Data Science Project. II
- 深入淺出常用推薦系統演算法 Recommendation System
- Statistical Thinking in Python|ECDF 進行 EDA 探索式分析的好工具
- Analyzing Police Activity with pandas 案例實作(一)– 如何高效進行數據分析
- Analyzing Police Activity with pandas 案例實作(二)– 數據整理
- 你可能不知道的 SQL 小筆記(查詢篇)
- [數據分析] Lean Analytics — 總說數據分析,你在分析什麼?
- [Statistic] Hypothesis Testing — 以 Python 實踐假設檢定(附程式碼)
- [DataCamp] 快速解析 Python 的各種Import Data 基礎應用技巧
- [DataCamp] Cleaning Data in Python 如何簡單上手資料清洗