推薦閱讀:
文章推薦系統(tǒng) | 一、推薦流程設(shè)計(jì)
文章推薦系統(tǒng) | 二乐横、同步業(yè)務(wù)數(shù)據(jù)
文章推薦系統(tǒng) | 三求橄、收集用戶行為數(shù)據(jù)
文章推薦系統(tǒng) | 四、構(gòu)建離線文章畫像
文章推薦系統(tǒng) | 五葡公、計(jì)算文章相似度
文章推薦系統(tǒng) | 六罐农、構(gòu)建離線用戶畫像
文章推薦系統(tǒng) | 七、構(gòu)建離線文章特征和用戶特征
文章推薦系統(tǒng) | 八催什、基于模型的離線召回
文章推薦系統(tǒng) | 九涵亏、基于內(nèi)容的離線及在線召回
文章推薦系統(tǒng) | 十、基于熱門文章和新文章的在線召回
前面蒲凶,我們已經(jīng)完成了召回階段的全部工作气筋,通過召回,我們可以從數(shù)百萬甚至上億的原始物品數(shù)據(jù)中旋圆,篩選出和用戶相關(guān)的幾百宠默、幾千個(gè)可能感興趣的物品。接下來灵巧,我們將要進(jìn)入到排序階段搀矫,對(duì)召回的幾百抹沪、幾千個(gè)物品進(jìn)行進(jìn)一步的篩選和排序。
排序流程包括離線排序和在線排序:
離線排序
讀取前天(第 T - 2 天)之前的用戶行為數(shù)據(jù)作為訓(xùn)練集艾君,對(duì)離線模型進(jìn)行訓(xùn)練采够;訓(xùn)練完成后,讀取昨天(第 T - 1 天)的用戶行為數(shù)據(jù)作為驗(yàn)證集進(jìn)行預(yù)測(cè)冰垄,根據(jù)預(yù)測(cè)結(jié)果對(duì)離線模型進(jìn)行評(píng)估蹬癌;若評(píng)估通過,當(dāng)天(第 T 天)即可將離線模型更新到定時(shí)任務(wù)中虹茶,定時(shí)執(zhí)行預(yù)測(cè)任務(wù)逝薪;明天(第 T + 1 天)就能根據(jù)今天的用戶行為數(shù)據(jù)來觀察更新后離線模型的預(yù)測(cè)效果。(注意:數(shù)據(jù)生產(chǎn)有一天時(shí)間差蝴罪,第 T 天生成第 T - 1 天的數(shù)據(jù))在線排序
讀取前天(第 T - 2 天)之前的用戶行為數(shù)據(jù)作為訓(xùn)練集董济,對(duì)在線模型進(jìn)行訓(xùn)練;訓(xùn)練完成后要门,讀取昨天(第 T - 1 天)的用戶行為數(shù)據(jù)作為驗(yàn)證集進(jìn)行預(yù)測(cè)虏肾,根據(jù)預(yù)測(cè)結(jié)果對(duì)在線模型進(jìn)行評(píng)估;若評(píng)估通過欢搜,當(dāng)天(第 T 天)即可將在線模型更新到線上封豪,實(shí)時(shí)執(zhí)行排序任務(wù);明天(第 T + 1 天)就能根據(jù)今天的用戶行為數(shù)據(jù)來觀察更新后在線模型的預(yù)測(cè)效果炒瘟。
這里再補(bǔ)充一個(gè)數(shù)據(jù)集劃分的小技巧:可以橫向劃分吹埠,隨機(jī)或按用戶或其他樣本選擇策略;也可以縱向劃分疮装,按照時(shí)間跨度缘琅,比如一周的數(shù)據(jù)中,周一到周四是訓(xùn)練集廓推,周五周六是測(cè)試集刷袍,周日是驗(yàn)證集。
利用排序模型可以進(jìn)行評(píng)分預(yù)測(cè)和用戶行為預(yù)測(cè)樊展,通常推薦系統(tǒng)利用排序模型進(jìn)行用戶行為預(yù)測(cè)做个,比如點(diǎn)擊率(CTR)預(yù)估,進(jìn)而根據(jù)點(diǎn)擊率對(duì)物品進(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)包括:
- 相對(duì)簡(jiǎn)單嘁圈,訓(xùn)練和預(yù)測(cè)的計(jì)算復(fù)雜度都相對(duì)較低
- 可以集中精力發(fā)掘新的有效特征省骂,且可以并行化工作
- 解釋性較好蟀淮,可以根據(jù)特征權(quán)重做解釋
本文我們將采用邏輯回歸作為離線模型,進(jìn)行點(diǎn)擊率預(yù)估钞澳。邏輯回歸(Logistic Regression怠惶,LR)是基礎(chǔ)的二分類模型,也是監(jiān)督學(xué)習(xí)的一種轧粟,通過對(duì)有標(biāo)簽的訓(xùn)練集數(shù)據(jù)進(jìn)行特征學(xué)習(xí)策治,進(jìn)而可以對(duì)測(cè)試集(新數(shù)據(jù))的標(biāo)簽進(jìn)行預(yù)測(cè)。我們這里的標(biāo)簽就是指用戶是否對(duì)文章發(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)計(jì)算好了文章特征和用戶特征,并存儲(chǔ)到了 Hbase 中混蔼。這里我們遍歷用戶歷史行為數(shù)據(jù)履腋,根據(jù)其中文章 ID 和用戶 ID 分別獲取文章特征和用戶特征,再將標(biāo)簽轉(zhuǎn)為 int 類型惭嚣,這樣就將一條用戶行為數(shù)據(jù)構(gòu)造成為了一個(gè)樣本遵湖,再將所有樣本加入到訓(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,即可對(duì) 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()
對(duì)訓(xùn)練集做出預(yù)測(cè)(實(shí)際場(chǎng)景應(yīng)該對(duì)驗(yàn)證集和訓(xùn)練集進(jìn)行預(yù)測(cè))
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ù)測(cè)結(jié)果 sort_res
中包括 clicked 和 probability 列烹卒,其中 clicked 為樣本標(biāo)簽的真實(shí)值闷盔,probability 是包含兩個(gè)元素的列表,第一個(gè)元素是預(yù)測(cè)的不點(diǎn)擊概率旅急,第二個(gè)元素則是預(yù)測(cè)的點(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)
模型評(píng)估
離線模型評(píng)估指標(biāo)包括:
- 評(píng)分準(zhǔn)確度
通常是均方根誤差(RMSE),用來評(píng)估預(yù)測(cè)評(píng)分的效果 - 排序能力
通常采用 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)行評(píng)估迫摔,比例類的包括: 點(diǎn)擊率(CTR)、轉(zhuǎn)化率(CVR)泥从,絕對(duì)類的包括:社交關(guān)系數(shù)量句占、用戶停留時(shí)長(zhǎng)、成交總額(GMV)等躯嫉。
推薦系統(tǒng)的廣度評(píng)估指標(biāo)包括:
- 覆蓋率
表示被有效推薦(推薦列表長(zhǎng)度大于 c)的用戶占全站用戶的比例纱烘,公式如下:
- 失效率
表示被無效推薦(推薦列表長(zhǎng)度為 0)的用戶占全站用戶的比例杨拐,公式如下:
- 新穎性
- 更新率
表示推薦列表的變化程度,當(dāng)前周期與上個(gè)周期相比擂啥,推薦列表中不同物品的比例
推薦系統(tǒng)的健康評(píng)估指標(biāo)包括:
- 個(gè)性化
用于衡量推薦的個(gè)性化程度哄陶,是否大部分用戶只消費(fèi)小部分物品,可以計(jì)算所有用戶推薦列表的平均相似度 - 基尼系數(shù)
用于衡量推薦系統(tǒng)的馬太效應(yīng)哺壶,反向衡量推薦的個(gè)性化程度屋吨。將物品按照累計(jì)推薦次數(shù)排序,排序位置為 i变骡,推薦次數(shù)占總推薦次數(shù)的比例為 离赫,推薦次數(shù)越不平均,基尼系數(shù)越接近 1塌碌,公式為:
- 多樣性
通常是在類別維度上衡量推薦結(jié)果的多樣性渊胸,可以衡量各個(gè)類別在推薦時(shí)的熵
其中,物品共包括 n 個(gè)類別台妆,類別 i 被推薦次數(shù)占總推薦次數(shù)的比例為 翎猛,分母是各個(gè)類別最均勻時(shí)對(duì)應(yīng)的熵,分子是實(shí)際推薦結(jié)果的類別分布熵接剩。這是整體推薦的多樣性切厘,還可以計(jì)算每次推薦和每個(gè)用戶推薦的多樣性。
我們這里主要根據(jù) AUC 進(jìn)行評(píng)估懊缺,首先利用 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()
計(jì)算 AUC
from pyspark.mllib.evaluation import BinaryClassificationMetrics
metrics = BinaryClassificationMetrics(score_label)
metrics.areaUnderROC
也可以利用 sklearn 的 roc_auc_score()
計(jì)算 AUC遗座,accuracy_score()
計(jì)算準(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)