在Airbnb使用機(jī)器學(xué)習(xí)預(yù)測房源的價(jià)格
位于希臘愛琴海伊莫洛維里的一個(gè) Airbnb 民宿的美好風(fēng)景
簡介
數(shù)據(jù)產(chǎn)品一直是 Airbnb 服務(wù)的重要組成部分,不過我們很早就意識到開發(fā)一款數(shù)據(jù)產(chǎn)品的成本是很高的。例如,個(gè)性化搜索排序可以讓客戶更容易發(fā)現(xiàn)中意的房屋,智能定價(jià)可以讓房東設(shè)定更具競爭力的價(jià)格。然而,需要許多數(shù)據(jù)科學(xué)家和工程師付出許多時(shí)間和精力才能做出這些產(chǎn)品。
最近,Airbnb 機(jī)器學(xué)習(xí)的基礎(chǔ)架構(gòu)進(jìn)行了改進(jìn),使得部署新的機(jī)器學(xué)習(xí)模型到生產(chǎn)環(huán)境中的成本降低了許多。例如,我們的 ML Infra 團(tuán)隊(duì)構(gòu)建了一個(gè)通用功能庫,這個(gè)庫讓用戶可以在他們的模型中應(yīng)用更多高質(zhì)量、經(jīng)過篩選、可復(fù)用的特征。數(shù)據(jù)科學(xué)家們也開始將一些自動(dòng)化機(jī)器學(xué)習(xí)工具納入他們的工作流中,以加快模型選擇的速度以及提高性能標(biāo)準(zhǔn)。此外,ML Infra 還創(chuàng)建了一個(gè)新的框架,可以自動(dòng)將 Jupyter notebook 轉(zhuǎn)換成 Airflow pipeline 能接受的格式。
在本文中,我將介紹這些工具是如何協(xié)同運(yùn)作來加快建模速度,從而降低開發(fā) LTV 模型(預(yù)測 Airbnb 民宿價(jià)格)總體成本的。
什么是 LTV?
LTV 全稱 Customer Lifetime Value,意為“客戶終身價(jià)值”,是電子商務(wù)、市場公司中很流行的一種概念。它定義了在未來一個(gè)時(shí)間段內(nèi)用戶預(yù)期為公司帶來的收益,通常以美元為單位。
在一些例如 Spotify 或者 Netflix 之類的電子商務(wù)公司里,LTV 通常用于制定產(chǎn)品定價(jià)(例如訂閱費(fèi)等)。而在 Airbnb 之類的市場公司里,知曉用戶的 LTV 將有助于我們更有效地分配營銷渠道的預(yù)算,更明確地根據(jù)關(guān)鍵字做在線營銷報(bào)價(jià),以及做更好的類目細(xì)分。
我們可以根據(jù)過去的數(shù)據(jù)來計(jì)算歷史值,當(dāng)然也可以進(jìn)一步使用機(jī)器學(xué)習(xí)來預(yù)測新登記房屋的 LTV。
LTV 模型的機(jī)器學(xué)習(xí)工作流
數(shù)據(jù)科學(xué)家們通常比較熟悉和機(jī)器學(xué)習(xí)任務(wù)相關(guān)的東西,例如特征工程、原型制作、模型選擇等。然而,要將一個(gè)模型原型投入生產(chǎn)環(huán)境中需要的是一系列數(shù)據(jù)工程技術(shù),他們可能對此不太熟練。
不過幸運(yùn)的是,我們有相關(guān)的機(jī)器學(xué)習(xí)工具,可以將具體的生產(chǎn)部署工作流從機(jī)器學(xué)習(xí)模型的分析建立中分離出來。如果沒有這些神奇的工具,我們就無法輕松地將模型應(yīng)用于生產(chǎn)環(huán)境。下面將通過 4 個(gè)主題來分別介紹我們的工作流以及各自用到的工具:
- 特征工程:定義相關(guān)特征
- 原型設(shè)計(jì)與訓(xùn)練:訓(xùn)練一個(gè)模型原型
- 模型選擇與驗(yàn)證:選擇模型以及調(diào)參
- 生產(chǎn)部署:將選擇好的模型原型投入生產(chǎn)環(huán)境使用
特征工程
使用工具:Airbnb 內(nèi)部特征庫 — Zipline
任何監(jiān)督學(xué)習(xí)項(xiàng)目的***步都是去找到會影響到結(jié)果的相關(guān)特征,這一個(gè)過程被稱為特征工程。例如在預(yù)測 LTV 時(shí),特征可以是某個(gè)房源房屋在接下來 180 天內(nèi)的可使用天數(shù)所占百分比,或者也可以是其與同市場其它房屋定價(jià)的差異。
在 Airbnb 中,要做特征工程一般得從頭開始寫 Hive 查詢語句來創(chuàng)建特征。但是這個(gè)工作相當(dāng)無聊,而且需要花費(fèi)很多時(shí)間。因?yàn)樗枰恍┨囟ǖ念I(lǐng)域知識和業(yè)務(wù)邏輯,也因此這些特征 pipeline 并不容易共享或復(fù)用。為了讓這項(xiàng)工作更具可擴(kuò)展性,我們開發(fā)了 Zipline—— 一個(gè)訓(xùn)練特征庫。它可以提供不同粒度級別(例如房主、客戶、房源房屋及市場級別)的特征。
這個(gè)內(nèi)部工具“多源共享”的特性讓數(shù)據(jù)科學(xué)家們可以在過去的項(xiàng)目中找出大量高質(zhì)量、經(jīng)過審查的特征。如果沒有找到希望提取的特征,用戶也可以寫一個(gè)配置文件來創(chuàng)建他自己需要的特征:
- source: {
- type: hive
- query:"""
- SELECT
- id_listing as listing
- , dim_city as city
- , dim_country as country
- , dim_is_active as is_active
- , CONCAT(ds, ' 23:59:59.999') as ts
- FROM
- core_data.dim_listings
- WHERE
- ds BETWEEN '{{ start_date }}' AND '{{ end_date }}'
- """
- dependencies: [core_data.dim_listings]
- is_snapshot: true
- start_date: 2010-01-01
- }
- features: {
- city: "City in which the listing is located."
- country: "Country in which the listing is located."
- is_active: "If the listing is active as of the date partition."
- }
在構(gòu)建訓(xùn)練集時(shí),Zipline 將會找出訓(xùn)練集所需要的特征,自動(dòng)的按照 key 將特征組合在一起并填充數(shù)據(jù)。在構(gòu)造房源 LTV 模型時(shí),我們使用了一些 Zipline 中已經(jīng)存在的特征,還自己寫了一些特征。模型總共使用了 150 多個(gè)特征,其中包括:
- 位置:國家、市場、社區(qū)以及其它地理特征
- 價(jià)格:過夜費(fèi)、清潔費(fèi)、與相似房源的價(jià)格差異
- 可用性:可過夜的總天數(shù),以及房主手動(dòng)關(guān)閉夜間預(yù)訂的占比百分?jǐn)?shù)
- 是否可預(yù)訂:預(yù)訂數(shù)量及過去 X 天內(nèi)在夜間訂房的數(shù)量
- 質(zhì)量:評價(jià)得分、評價(jià)數(shù)量、便利設(shè)施
實(shí)例數(shù)據(jù)集
在定義好特征以及輸出變量之后,就可以根據(jù)我們的歷史數(shù)據(jù)來訓(xùn)練模型了。
原型設(shè)計(jì)與訓(xùn)練
使用工具:Python 機(jī)器學(xué)習(xí)庫 — scikit-learn
以前面的訓(xùn)練集為例,我們在做訓(xùn)練前先要對數(shù)據(jù)進(jìn)行一些預(yù)處理:
- 數(shù)據(jù)插補(bǔ):我們需要檢查是否有數(shù)據(jù)缺失,以及它是否為隨機(jī)出現(xiàn)的缺失。如果不是隨機(jī)現(xiàn)象,我們需要弄清楚其根本原因;如果是隨機(jī)缺失,我們需要填充空缺數(shù)據(jù)。
- 對分類進(jìn)行編碼:通常來說我們不能在模型里直接使用原始的分類,因?yàn)槟P筒⒉荒苋M合字符串。當(dāng)分類數(shù)量比較少時(shí),我們可以考慮使用 one-hot encoding 進(jìn)行編碼。如果分類數(shù)量比較多,我們就會考慮使用 ordinal encoding, 按照分類的頻率計(jì)數(shù)進(jìn)行編碼。
在這一步中,我們還不知道最有效的一組特征是什么,因此編寫可快速迭代的代碼是非常重要的。如 Scikit-Learn、Spark 等開源工具的 pipeline 結(jié)構(gòu)對于原型構(gòu)建來說是非常方便的工具。Pipeline 可以讓數(shù)據(jù)科學(xué)家們設(shè)計(jì)藍(lán)圖,指定如何轉(zhuǎn)換特征、訓(xùn)練哪一個(gè)模型。更具體來說,可以看下面我們 LTV 模型的 pipeline:
- transforms = []
- transforms.append(
- ('select_binary', ColumnSelector(features=binary))
- )
- transforms.append(
- ('numeric', ExtendedPipeline([
- ('select', ColumnSelector(features=numeric)),
- ('impute', Imputer(missing_values='NaN', strategy='mean', axis=0)),
- ]))
- )
- for field in categorical:
- transforms.append(
- (field, ExtendedPipeline([
- ('select', ColumnSelector(features=[field])),
- ('encode', OrdinalEncoder(min_support=10))
- ])
- )
- )
- features = FeatureUnion(transforms)
在高層設(shè)計(jì)時(shí),我們使用 pipeline 來根據(jù)特征類型(如二進(jìn)制特征、分類特征、數(shù)值特征等)來指定不同特征中數(shù)據(jù)的轉(zhuǎn)換方式。***使用 FeatureUnion 簡單將特征列組合起來,形成最終的訓(xùn)練集。
使用 pipeline 開發(fā)原型的優(yōu)勢在于,它可以使用 data transforms 來避免繁瑣的數(shù)據(jù)轉(zhuǎn)換。總的來說,這些轉(zhuǎn)換是為了確保數(shù)據(jù)在訓(xùn)練和評估時(shí)保持一致,以避免將原型部署到生產(chǎn)環(huán)境時(shí)出現(xiàn)的數(shù)據(jù)不一致。
另外,pipeline 還可以將數(shù)據(jù)轉(zhuǎn)換過程和訓(xùn)練模型過程分開。雖然上面代碼中沒有,但數(shù)據(jù)科學(xué)家可以在***一步指定一種 estimator(估值器)來訓(xùn)練模型。通過嘗試使用不同的估值器,數(shù)據(jù)科學(xué)家可以為模型選出一個(gè)表現(xiàn)***的估值器,減少模型的樣本誤差。
模型選擇與驗(yàn)證
使用工具:各種自動(dòng)機(jī)器學(xué)習(xí)框架
如上一節(jié)所述,我們需要確定候選模型中的哪個(gè)最適合投入生產(chǎn)。為了做這個(gè)決策,我們需要在模型的可解釋性與復(fù)雜度中進(jìn)行權(quán)衡。例如,稀疏線性模型的解釋性很好,但它的復(fù)雜度太低了,不能很好地運(yùn)作。一個(gè)足夠復(fù)雜的樹模型可以擬合各種非線性模式,但是它的解釋性很差。這種情況也被稱為偏差(Bias)和方差(Variance)的權(quán)衡。
上圖引用自 James、Witten、Hastie、Tibshirani 所著《R 語言統(tǒng)計(jì)學(xué)習(xí)》
在保險(xiǎn)、信用審查等應(yīng)用中,需要對模型進(jìn)行解釋。因?yàn)閷δP蛠碚f避免無意排除一些正確客戶是很重要的事。不過在圖像分類等應(yīng)用中,模型的高性能比可解釋更重要。
由于模型的選擇相當(dāng)耗時(shí),我們選擇采用各種自動(dòng)機(jī)器學(xué)習(xí)工具來加速這個(gè)步驟。通過探索大量的模型,我們最終會找到表現(xiàn)***的模型。例如,我們發(fā)現(xiàn) XGBoost (XGBoost) 明顯比其他基準(zhǔn)模型(比如 mean response 模型、嶺回歸模型、單一決策樹)的表現(xiàn)要好。
上圖:我們通過比較 RMSE 可以選擇出表現(xiàn)更好的模型
鑒于我們的最初目標(biāo)是預(yù)測房源價(jià)格,因此我們很舒服地在最終的生產(chǎn)環(huán)境中使用 XGBoost 模型,比起可解釋性它更注重于模型的彈性。
生產(chǎn)部署
使用工具:Airbnb 自己寫的 notebook 轉(zhuǎn)換框架 — ML Automator
如開始所說,構(gòu)建生產(chǎn)環(huán)境工作流和在筆記本上構(gòu)建一個(gè)原型是完全不同的。例如,我們?nèi)绾芜M(jìn)行定期的重訓(xùn)練?我們?nèi)绾斡行У卦u估大量的實(shí)例?我們?nèi)绾谓⒁粋€(gè) pipeline 以隨時(shí)監(jiān)視模型性能?
在 Airbnb,我們自己開發(fā)了一個(gè)名為 ML Automator 的框架,它可以自動(dòng)將 Jupyter notebook 轉(zhuǎn)換為 Airflow 機(jī)器學(xué)習(xí) pipeline。該框架專為熟悉使用 Python 開發(fā)原型,但缺乏將模型投入生產(chǎn)環(huán)境經(jīng)驗(yàn)的數(shù)據(jù)科學(xué)家準(zhǔn)備。
ML Automator 框架概述(照片來源:Aaron Keys)
首先,框架要求用戶在 notebook 中指定模型的配置。該配置將告訴框架如何定位訓(xùn)練數(shù)據(jù)表,為訓(xùn)練分配多少計(jì)算資源,以及如何計(jì)算模型評價(jià)分?jǐn)?shù)。
另外,數(shù)據(jù)科學(xué)家需要自己寫特定的 fit 與 transform 函數(shù)。fit 函數(shù)指定如何進(jìn)行訓(xùn)練,而 transform 函數(shù)將被 Python UDF 封裝,進(jìn)行分布式計(jì)算(如果有需要)。
下面的代碼片段展示了我們 LTV 模型中的 fit 與 transform 函數(shù)。fit 函數(shù)告訴框架需要訓(xùn)練 XGBoost 模型,同時(shí)轉(zhuǎn)換器將根據(jù)我們之前定義的 pipeline 轉(zhuǎn)換數(shù)據(jù)。
- def fit(X_train, y_train):
- import multiprocessing
- from ml_helpers.sklearn_extensions import DenseMatrixConverter
- from ml_helpers.data import split_records
- from xgboost import XGBRegressor
- global model
- model = {}
- n_subset = N_EXAMPLES
- X_subset = {k: v[:n_subset] for k, v in X_train.iteritems()}
- model['transformations'] = ExtendedPipeline([
- ('features', features),
- ('densify', DenseMatrixConverter()),
- ]).fit(X_subset)
- # 并行使用轉(zhuǎn)換器
- Xt = model['transformations'].transform_parallel(X_train)
- # 并行進(jìn)行模型擬合
- model['regressor'] = XGBRegressor().fit(Xt, y_train)
- def transform(X):
- # return dictionary
- global model
- Xt = model['transformations'].transform(X)
- return {'score': model['regressor'].predict(Xt)}
一旦 notebook 完成,ML Automator 將會把訓(xùn)練好的模型包裝在 Python UDF 中,并創(chuàng)建一個(gè)如下圖所示的 Airflow pipeline。數(shù)據(jù)序列化、定期重訓(xùn)練、分布式評價(jià)等數(shù)據(jù)工程任務(wù)都將被載入到日常批處理作業(yè)中。因此,這個(gè)框架顯著降低了數(shù)據(jù)科學(xué)家將模型投入生產(chǎn)的成本,就像有一位數(shù)據(jù)工程師在與科學(xué)家一起工作一樣!
我們 LTV 模型在 Airflow DAG 中的圖形界面,運(yùn)行于生產(chǎn)環(huán)境中
Note:除了模型生產(chǎn)化之外,還有一些其它項(xiàng)目(例如跟蹤模型隨著時(shí)間推移的性能、使用彈性計(jì)算環(huán)境建模等)我們沒有在這篇文章中進(jìn)行介紹。這些都是正在進(jìn)行開發(fā)的熱門領(lǐng)域。
經(jīng)驗(yàn)與展望
過去的幾個(gè)月中,我們的數(shù)據(jù)科學(xué)家們與 ML Infra 密切合作,產(chǎn)生了許多很好的模式和想法。我們相信這些工具將會為 Airbnb 開發(fā)機(jī)器學(xué)習(xí)模型開辟新的范例。
- 首先,顯著地降低了模型的開發(fā)成本:通過組合各種不同的獨(dú)立工具的優(yōu)點(diǎn)(Zipline 用于特征工程、Pipeline 用于模型原型設(shè)計(jì)、AutoML 用于模型選擇與驗(yàn)證,以及***的 ML Automator 用于模型生產(chǎn)化),我們大大減短了模型的開發(fā)周期。
- 其次,notebook 的設(shè)計(jì)降低了入門門檻:還不熟悉框架的數(shù)據(jù)科學(xué)家可以立即得到大量的真實(shí)用例。在生產(chǎn)環(huán)境中,可以確保 notebook 是正確、自解釋、***的。這種設(shè)計(jì)模式受到了新用戶的好評。
- 因此,團(tuán)隊(duì)將更愿意關(guān)注機(jī)器學(xué)習(xí)產(chǎn)品的 idea:在本文撰寫時(shí),我們還有其它幾支團(tuán)隊(duì)在采用類似的方法探索機(jī)器學(xué)習(xí)產(chǎn)品的 idea:為檢查房源隊(duì)列進(jìn)行排序、預(yù)測房源是否會增加合伙人、自動(dòng)標(biāo)注低質(zhì)量房源等等。
我們對這個(gè)框架和它帶來的新范式的未來感到無比的興奮。通過縮小原型與生產(chǎn)環(huán)境間的差距,我們可以讓數(shù)據(jù)科學(xué)家和數(shù)據(jù)工程師更多去追求端到端的機(jī)器學(xué)習(xí)項(xiàng)目,讓我們的產(chǎn)品做得更好。