推薦閱讀:
文章推薦系統(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)的用戶占全站用戶的比例,公式如下:
- 失效率
表示被無效推薦(推薦列表長度為 0)的用戶占全站用戶的比例,公式如下:
- 新穎性
- 更新率
表示推薦列表的變化程度,當(dāng)前周期與上個周期相比,推薦列表中不同物品的比例
推薦系統(tǒng)的健康評估指標(biāo)包括:
- 個性化
用于衡量推薦的個性化程度,是否大部分用戶只消費(fèi)小部分物品,可以計算所有用戶推薦列表的平均相似度 - 基尼系數(shù)
用于衡量推薦系統(tǒng)的馬太效應(yīng),反向衡量推薦的個性化程度。將物品按照累計推薦次數(shù)排序,排序位置為 i,推薦次數(shù)占總推薦次數(shù)的比例為,推薦次數(shù)越不平均,基尼系數(shù)越接近 1,公式為:
- 多樣性
通常是在類別維度上衡量推薦結(jié)果的多樣性,可以衡量各個類別在推薦時的熵
其中,物品共包括 n 個類別,類別 i 被推薦次數(shù)占總推薦次數(shù)的比例為,分母是各個類別最均勻時對應(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)