文章推薦系統(tǒng) | 十一、基于 LR 模型的離線排序

推薦閱讀:
文章推薦系統(tǒng) | 一、推薦流程設(shè)計
文章推薦系統(tǒng) | 二、同步業(yè)務(wù)數(shù)據(jù)
文章推薦系統(tǒng) | 三、收集用戶行為數(shù)據(jù)
文章推薦系統(tǒng) | 四、構(gòu)建離線文章畫像
文章推薦系統(tǒng) | 五、計算文章相似度
文章推薦系統(tǒng) | 六、構(gòu)建離線用戶畫像
文章推薦系統(tǒng) | 七、構(gòu)建離線文章特征和用戶特征
文章推薦系統(tǒng) | 八、基于模型的離線召回
文章推薦系統(tǒng) | 九、基于內(nèi)容的離線及在線召回
文章推薦系統(tǒng) | 十、基于熱門文章和新文章的在線召回

前面,我們已經(jīng)完成了召回階段的全部工作,通過召回,我們可以從數(shù)百萬甚至上億的原始物品數(shù)據(jù)中,篩選出和用戶相關(guān)的幾百、幾千個可能感興趣的物品。接下來,我們將要進(jìn)入到排序階段,對召回的幾百、幾千個物品進(jìn)行進(jìn)一步的篩選和排序。

排序流程包括離線排序和在線排序:

  • 離線排序
    讀取前天(第 T - 2 天)之前的用戶行為數(shù)據(jù)作為訓(xùn)練集,對離線模型進(jìn)行訓(xùn)練;訓(xùn)練完成后,讀取昨天(第 T - 1 天)的用戶行為數(shù)據(jù)作為驗(yàn)證集進(jìn)行預(yù)測,根據(jù)預(yù)測結(jié)果對離線模型進(jìn)行評估;若評估通過,當(dāng)天(第 T 天)即可將離線模型更新到定時任務(wù)中,定時執(zhí)行預(yù)測任務(wù);明天(第 T + 1 天)就能根據(jù)今天的用戶行為數(shù)據(jù)來觀察更新后離線模型的預(yù)測效果。(注意:數(shù)據(jù)生產(chǎn)有一天時間差,第 T 天生成第 T - 1 天的數(shù)據(jù))

  • 在線排序
    讀取前天(第 T - 2 天)之前的用戶行為數(shù)據(jù)作為訓(xùn)練集,對在線模型進(jìn)行訓(xùn)練;訓(xùn)練完成后,讀取昨天(第 T - 1 天)的用戶行為數(shù)據(jù)作為驗(yàn)證集進(jìn)行預(yù)測,根據(jù)預(yù)測結(jié)果對在線模型進(jìn)行評估;若評估通過,當(dāng)天(第 T 天)即可將在線模型更新到線上,實(shí)時執(zhí)行排序任務(wù);明天(第 T + 1 天)就能根據(jù)今天的用戶行為數(shù)據(jù)來觀察更新后在線模型的預(yù)測效果。

這里再補(bǔ)充一個數(shù)據(jù)集劃分的小技巧:可以橫向劃分,隨機(jī)或按用戶或其他樣本選擇策略;也可以縱向劃分,按照時間跨度,比如一周的數(shù)據(jù)中,周一到周四是訓(xùn)練集,周五周六是測試集,周日是驗(yàn)證集。

利用排序模型可以進(jìn)行評分預(yù)測和用戶行為預(yù)測,通常推薦系統(tǒng)利用排序模型進(jìn)行用戶行為預(yù)測,比如點(diǎn)擊率(CTR)預(yù)估,進(jìn)而根據(jù)點(diǎn)擊率對物品進(jìn)行排序,目前工業(yè)界常用的點(diǎn)擊率預(yù)估模型有如下 3 種類型:

  • 寬模型 + 特征?程
    LR / MLR + 非 ID 類特征(??離散 / GBDT / FM),可以使用 Spark 進(jìn)行訓(xùn)練
  • 寬模型 + 深模型
    Wide&Deep,DeepFM,可以使用 TensorFlow 進(jìn)行訓(xùn)練
  • 深模型:
    DNN + 特征 Embedding,可以使用 TensorFlow 進(jìn)行訓(xùn)練

這里的寬模型即指線性模型,線性模型的優(yōu)點(diǎn)包括:

  • 相對簡單,訓(xùn)練和預(yù)測的計算復(fù)雜度都相對較低
  • 可以集中精力發(fā)掘新的有效特征,且可以并行化工作
  • 解釋性較好,可以根據(jù)特征權(quán)重做解釋

本文我們將采用邏輯回歸作為離線模型,進(jìn)行點(diǎn)擊率預(yù)估。邏輯回歸(Logistic Regression,LR)是基礎(chǔ)的二分類模型,也是監(jiān)督學(xué)習(xí)的一種,通過對有標(biāo)簽的訓(xùn)練集數(shù)據(jù)進(jìn)行特征學(xué)習(xí),進(jìn)而可以對測試集(新數(shù)據(jù))的標(biāo)簽進(jìn)行預(yù)測。我們這里的標(biāo)簽就是指用戶是否對文章發(fā)生了點(diǎn)擊行為。

構(gòu)造訓(xùn)練集

讀取用戶歷史行為數(shù)據(jù),將 clicked 作為訓(xùn)練集標(biāo)簽

spark.sql("use profile")
user_article_basic = spark.sql("select * from user_article_basic").select(['user_id', 'article_id', 'clicked'])

user_article_basic 結(jié)果如下所示

之前我們已經(jīng)計算好了文章特征和用戶特征,并存儲到了 Hbase 中。這里我們遍歷用戶歷史行為數(shù)據(jù),根據(jù)其中文章 ID 和用戶 ID 分別獲取文章特征和用戶特征,再將標(biāo)簽轉(zhuǎn)為 int 類型,這樣就將一條用戶行為數(shù)據(jù)構(gòu)造成為了一個樣本,再將所有樣本加入到訓(xùn)練集中

train = []
for user_id, article_id, clicked in user_article_basic:
    try:
        article_feature = eval(hbu.get_table_row('ctr_feature_article', '{}'.format(article_id).encode(), 'article:{}'.format(article_id).encode()))
    except Exception as e:
        article_feature = []
    try:
        user_feature = eval(hbu.get_table_row('ctr_feature_user', '{}'.format(temp.user_id).encode(), 'channel:{}'.format(temp.channel_id).encode()))
    except Exception as e:
        user_feature = []

    if not article_feature:
        article_feature = [0.0] * 111
    if not user_feature:
        user_feature = [0.0] * 10

    sample = []
    sample.append(user_feature)
    sample.append(article_feature)
    sample.append(int(clicked))

    train.append(sample)

接下來,還需要利用 Spark 的 Vectors 將 array<double> 類型的 article_feature 和 user_feature 轉(zhuǎn)為 vector 類型

columns = ['article_feature', 'user_feature', 'clicked']

def list_to_vector(row):
    from pyspark.ml.linalg import Vectors
    
    return Vectors.dense(row[0]), Vectors.dense(row[1]), row[2]

train = train.rdd.map(list_to_vector).toDF(columns) 

再將 article_feature, user_feature 合并為統(tǒng)一輸入到 LR 模型的特征列 features,這樣就完成訓(xùn)練集的構(gòu)建

train = VectorAssembler().setInputCols(columns[0:1]).setOutputCol('features').transform(train)

模型訓(xùn)練

Spark 已經(jīng)實(shí)現(xiàn)好了 LR 模型,通過指定訓(xùn)練集 train 的特征列 features 和標(biāo)簽列 clicked,即可對 LR 模型進(jìn)行訓(xùn)練,再將訓(xùn)練好的模型保存到 HDFS

from pyspark.ml.feature import VectorAssembler
from pyspark.ml.classification import LogisticRegression

lr = LogisticRegression()
model = lr.setLabelCol("clicked").setFeaturesCol("features").fit(train)
model.save("hdfs://hadoop-master:9000/headlines/models/lr.obj")

加載訓(xùn)練好的 LR 模型,調(diào)用 transform() 對訓(xùn)練集做出預(yù)測(實(shí)際場景應(yīng)該對驗(yàn)證集和訓(xùn)練集進(jìn)行預(yù)測)

from pyspark.ml.classification import LogisticRegressionModel

online_model = LogisticRegressionModel.load("hdfs://hadoop-master:9000/headlines/models/lr.obj")
sort_res = online_model.transform(train)

預(yù)測結(jié)果 sort_res 中包括 clicked 和 probability 列,其中 clicked 為樣本標(biāo)簽的真實(shí)值,probability 是包含兩個元素的列表,第一個元素是預(yù)測的不點(diǎn)擊概率,第二個元素則是預(yù)測的點(diǎn)擊概率,可以提取點(diǎn)擊率(CTR)

def get_ctr(row):
    return float(row.clicked), float(row.probability[1]) 

score_label = sort_res.select(["clicked", "probability"]).rdd.map(get_ctr)

模型評估

離線模型評估指標(biāo)包括:

  • 評分準(zhǔn)確度
    通常是均方根誤差(RMSE),用來評估預(yù)測評分的效果
  • 排序能力
    通常采用 AUC(Area Under the Curve),即 ROC 曲線下方的面積
  • 分類準(zhǔn)確率(Precision)
    表示在 Top K 推薦列表中,用戶真實(shí)點(diǎn)擊的物品所占的比例
  • 分類召回率(Recall)
    表示在用戶真實(shí)點(diǎn)擊的物品中,出現(xiàn)在 Top K 推薦列表中所占的比例

當(dāng)模型更新后,還可以根據(jù)商業(yè)指標(biāo)進(jìn)行評估,比例類的包括: 點(diǎn)擊率(CTR)、轉(zhuǎn)化率(CVR),絕對類的包括:社交關(guān)系數(shù)量、用戶停留時長、成交總額(GMV)等。

推薦系統(tǒng)的廣度評估指標(biāo)包括:

  • 覆蓋率
    表示被有效推薦(推薦列表長度大于 c)的用戶占全站用戶的比例,公式如下:
    Con_{UV}=\frac{N_{l >c}}{N_{UV}}
  • 失效率
    表示被無效推薦(推薦列表長度為 0)的用戶占全站用戶的比例,公式如下:
    Lost_{UV}=\frac{N_{l =0}}{N_{UV}}
  • 新穎性
  • 更新率
    表示推薦列表的變化程度,當(dāng)前周期與上個周期相比,推薦列表中不同物品的比例
    Update=\frac{N_{diff}}{N_{last}}

推薦系統(tǒng)的健康評估指標(biāo)包括:

  • 個性化
    用于衡量推薦的個性化程度,是否大部分用戶只消費(fèi)小部分物品,可以計算所有用戶推薦列表的平均相似度
  • 基尼系數(shù)
    用于衡量推薦系統(tǒng)的馬太效應(yīng),反向衡量推薦的個性化程度。將物品按照累計推薦次數(shù)排序,排序位置為 i,推薦次數(shù)占總推薦次數(shù)的比例為 P_i,推薦次數(shù)越不平均,基尼系數(shù)越接近 1,公式為:
    Gini=\frac{1}{n}\sum_{i=1}^n P_i(2i-n-i)
  • 多樣性
    通常是在類別維度上衡量推薦結(jié)果的多樣性,可以衡量各個類別在推薦時的熵
    Div=\frac{\sum_{i=1}^n-P_i\log(P_i)}{n\log(n)}
    其中,物品共包括 n 個類別,類別 i 被推薦次數(shù)占總推薦次數(shù)的比例為 P_i,分母是各個類別最均勻時對應(yīng)的熵,分子是實(shí)際推薦結(jié)果的類別分布熵。這是整體推薦的多樣性,還可以計算每次推薦和每個用戶推薦的多樣性。

我們這里主要根據(jù) AUC 進(jìn)行評估,首先利用 model.summary.roc 繪制 ROC 曲線

import matplotlib.pyplot as plt

plt.figure(figsize=(5,5))
plt.plot([0, 1], [0, 1], 'r--')
plt.plot(model.summary.roc.select('FPR').collect(),
         model.summary.roc.select('TPR').collect())
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.show()

ROC 曲線如下所示,曲線下面的面積即為 AUC(Area Under the Curve),AUC 值越大,排序效果越好

利用 Spark 的 BinaryClassificationMetrics() 計算 AUC

from pyspark.mllib.evaluation import BinaryClassificationMetrics

metrics = BinaryClassificationMetrics(score_label)
metrics.areaUnderROC

也可以利用 sklearn 的 roc_auc_score() 計算 AUC,accuracy_score() 計算準(zhǔn)確率

from sklearn.metrics import accuracy_score, roc_auc_score,
import numpy as np

arr = np.array(score_label.collect())
# AUC
roc_auc_score(arr[:, 0], arr[:, 1]) # 0.719274521004087

# 準(zhǔn)確率
accuracy_score(arr[:, 0], arr[:, 1].round()) # 0.9051438053097345

參考

https://www.bilibili.com/video/av68356229
https://book.douban.com/subject/34872145/
https://pan.baidu.com/s/1-uvGJ-mEskjhtaial0Xmgw(學(xué)習(xí)資源已保存至網(wǎng)盤, 提取碼:eakp)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容