簡介:本文介紹的是阿里巴巴團隊發表在 SIGIR’2018 的論文《Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate》。文章基於 Multi-Task Learning (MTL) 的思路,提出一種名為ESMM的CVR預估模型,有效解決了真實場景中CVR預估面臨的資料稀疏以及樣本選擇偏差這兩個關鍵問題。後續還會陸續介紹MMoE,PLE,DBMTL等多工學習模型。
多工學習背景
目前工業中使用的推薦演算法已不只侷限在單目標(ctr)任務上,還需要關注後續的轉換鏈路,如是否評論、收藏、加購、購買、觀看時長等目標。
本文介紹的是阿里巴巴團隊發表在 SIGIR’2018 的論文《Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate》。文章基於 Multi-Task Learning (MTL) 的思路,提出一種名為ESMM的CVR預估模型,有效解決了真實場景中CVR預估面臨的資料稀疏以及樣本選擇偏差這兩個關鍵問題。後續還會陸續介紹MMoE,PLE,DBMTL等多工學習模型。
論文介紹
CVR預估面臨兩個關鍵問題:
1. Sample Selection Bias (SSB)
轉化是在點選之後才“有可能”發生的動作,傳統CVR模型通常以點選資料為訓練集,其中點選未轉化為負例,點選並轉化為正例。但是訓練好的模型實際使用時,則是對整個空間的樣本進行預估,而非只對點選樣本進行預估。即訓練資料與實際要預測的資料來自不同分佈,這個偏差對模型的泛化能力構成了很大挑戰,導致模型上線後,線上業務效果往往一般。
2. Data Sparsity (DS)
CVR預估任務的使用的訓練資料(即點選樣本)遠小於CTR預估訓練使用的曝光樣本。僅使用數量較小的樣本進行訓練,會導致深度模型擬合困難。
一些策略可以緩解這兩個問題,例如從曝光集中對unclicked樣本抽樣做負例緩解SSB,對轉化樣本過取樣緩解DS等。但無論哪種方法,都沒有從實質上解決上面任一個問題。
由於點選=>轉化,本身是兩個強相關的連續行為,作者希望在模型結構中顯示考慮這種“行為鏈關係”,從而可以在整個空間上進行訓練及預測。這涉及到CTR與CVR兩個任務,因此使用多工學習(MTL)是一個自然的選擇,論文的關鍵亮點正在於“如何搭建”這個MTL。
首先需要重點區分下,CVR預估任務與CTCVR預估任務。
CVR = 轉化數/點選數。是預測“假設item被點選,那麼它被轉化”的機率。CVR預估任務,與CTR沒有絕對的關係。一個item的ctr高,cvr不一定同樣會高,如標題黨文章的瀏覽時長往往較低。這也是不能直接使用全部樣本訓練CVR模型的原因,因為無法確定那些曝光未點選的樣本,假設他們被點選了,是否會被轉化。如果直接使用0作為它們的label,會很大程度上誤導CVR模型的學習。
CTCVR = 轉換數/曝光數。是預測“item被點選,然後被轉化”的機率。
其中x,y,z分別表示曝光,點選,轉換。注意到,在全部樣本空間中,CTR對應的label為click,而CTCVR對應的label為click & conversion,這兩個任務是可以使用全部樣本的。因此,ESMM透過學習CTR,CTCVR兩個任務,再根據上式隱式地學習CVR任務。具體結構如下:
網路結構上有兩點值得強調:
共享Embedding。 CVR-task和CTR-task使用相同的特徵和特徵embedding,即兩者從Concatenate之後才學習各自獨享的引數;
隱式學習pCVR。這裡pCVR 僅是網路中的一個variable,沒有顯示的監督訊號。
具體地,反映在目標函式中:
程式碼實現
基於EasyRec推薦演算法框架,我們實現了ESMM演算法,具體實現可移步至github:EasyRec-ESMM。
EasyRec介紹:EasyRec是阿里雲端計算平臺機器學習PAI團隊開源的大規模分散式推薦演算法框架,EasyRec 正如其名字一樣,簡單易用,整合了諸多優秀前沿的推薦系統論文思想,並且有在實際工業落地中取得優良效果的特徵工程方法,整合訓練、評估、部署,與阿里雲產品無縫銜接,可以藉助 EasyRec 在短時間內搭建起一套前沿的推薦系統。作為阿里雲的拳頭產品,現已穩定服務於數百個企業客戶。
模型前饋網路:
def build_predict_graph(self):
"""Forward function.
Returns:
self._prediction_dict: Prediction result of two tasks.
"""
# 此處從Concatenate後的tensor(all_fea)開始,省略其生成邏輯
cvr_tower_name = self._cvr_tower_cfg.tower_name
dnn_model = dnn.DNN(
self._cvr_tower_cfg.dnn,
self._l2_reg,
name=cvr_tower_name,
is_training=self._is_training)
cvr_tower_output = dnn_model(all_fea)
cvr_tower_output = tf.layers.dense(
inputs=cvr_tower_output,
units=1,
kernel_regularizer=self._l2_reg,
name='%s/dnn_output' % cvr_tower_name)
ctr_tower_name = self._ctr_tower_cfg.tower_name
dnn_model = dnn.DNN(
self._ctr_tower_cfg.dnn,
self._l2_reg,
name=ctr_tower_name,
is_training=self._is_training)
ctr_tower_output = dnn_model(all_fea)
ctr_tower_output = tf.layers.dense(
inputs=ctr_tower_output,
units=1,
kernel_regularizer=self._l2_reg,
name='%s/dnn_output' % ctr_tower_name)
tower_outputs = {
cvr_tower_name: cvr_tower_output,
ctr_tower_name: ctr_tower_output
}
self._add_to_prediction_dict(tower_outputs)
return self._prediction_dict
loss計算:
注意:計算CVR的指標時需要mask掉曝光資料。
def build_loss_graph(self):
"""Build loss graph.
Returns:
self._loss_dict: Weighted loss of ctr and cvr.
"""
cvr_tower_name = self._cvr_tower_cfg.tower_name
ctr_tower_name = self._ctr_tower_cfg.tower_name
cvr_label_name = self._label_name_dict[cvr_tower_name]
ctr_label_name = self._label_name_dict[ctr_tower_name]
ctcvr_label = tf.cast(
self._labels[cvr_label_name] * self._labels[ctr_label_name],
tf.float32)
cvr_loss = tf.keras.backend.binary_crossentropy(
ctcvr_label, self._prediction_dict['probs_ctcvr'])
cvr_loss = tf.reduce_sum(cvr_losses, name="ctcvr_loss")
# The weight defaults to 1.
self._loss_dict['weighted_cross_entropy_loss_%s' %
cvr_tower_name] = self._cvr_tower_cfg.weight * cvr_loss
ctr_loss = tf.reduce_sum(tf.nn.sigmoid_cross_entropy_with_logits(
labels=tf.cast(self._labels[ctr_label_name], tf.float32),
logits=self._prediction_dict['logits_%s' % ctr_tower_name]
), name="ctr_loss")
self._loss_dict['weighted_cross_entropy_loss_%s' %
ctr_tower_name] = self._ctr_tower_cfg.weight * ctr_loss
return self._loss_dict
note: 這裡loss是 weighted_cross_entropy_loss_ctr + weighted_cross_entropy_loss_cvr, EasyRec框架會自動對self._loss_dict中的內容進行加和。
metric計算:
注意:計算CVR的指標時需要mask掉曝光資料。
def build_metric_graph(self, eval_config):
"""Build metric graph.
Args:
eval_config: Evaluation configuration.
Returns:
metric_dict: Calculate AUC of ctr, cvr and ctrvr.
"""
metric_dict = {}
cvr_tower_name = self._cvr_tower_cfg.tower_name
ctr_tower_name = self._ctr_tower_cfg.tower_name
cvr_label_name = self._label_name_dict[cvr_tower_name]
ctr_label_name = self._label_name_dict[ctr_tower_name]
for metric in self._cvr_tower_cfg.metrics_set:
# CTCVR metric
ctcvr_label_name = cvr_label_name + '_ctcvr'
cvr_dtype = self._labels[cvr_label_name].dtype
self._labels[ctcvr_label_name] = self._labels[cvr_label_name] * tf.cast(
self._labels[ctr_label_name], cvr_dtype)
metric_dict.update(
self._build_metric_impl(
metric,
loss_type=self._cvr_tower_cfg.loss_type,
label_name=ctcvr_label_name,
num_class=self._cvr_tower_cfg.num_class,
suffix='_ctcvr'))
# CVR metric
cvr_label_masked_name = cvr_label_name + '_masked'
ctr_mask = self._labels[ctr_label_name] > 0
self._labels[cvr_label_masked_name] = tf.boolean_mask(
self._labels[cvr_label_name], ctr_mask)
pred_prefix = 'probs' if self._cvr_tower_cfg.loss_type == LossType.CLASSIFICATION else 'y'
pred_name = '%s_%s' % (pred_prefix, cvr_tower_name)
self._prediction_dict[pred_name + '_masked'] = tf.boolean_mask(
self._prediction_dict[pred_name], ctr_mask)
metric_dict.update(
self._build_metric_impl(
metric,
loss_type=self._cvr_tower_cfg.loss_type,
label_name=cvr_label_masked_name,
num_class=self._cvr_tower_cfg.num_class,
suffix='_%s_masked' % cvr_tower_name))
for metric in self._ctr_tower_cfg.metrics_set:
# CTR metric
metric_dict.update(
self._build_metric_impl(
metric,
loss_type=self._ctr_tower_cfg.loss_type,
label_name=ctr_label_name,
num_class=self._ctr_tower_cfg.num_class,
suffix='_%s' % ctr_tower_name))
return metric_dict
實驗及不足
我們基於開源AliCCP資料,進行了大量實驗,實驗部分請期待下一篇文章。實驗發現,ESMM的蹺蹺板現象較為明顯,CTR與CVR任務的效果較難同時提升。
參考文獻
Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate
阿里CVR預估模型之ESMM
EasyRec-ESMM使用介紹多工學習模型之ESMM介紹與實現
注:本文圖片及公示均引用自論文:Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate。
原文連結
本文為阿里雲原創內容,未經允許不得轉載。