推荐算法
推荐算法
Gorse 已经实现了多种类型的推荐算法,包括非个性化推荐和个性化推荐。这些推荐算法是构成推荐工作流程的基础模块。
用数学公式介绍复杂的算法比较方便,本节中使用的数学符号定义如下。
符号 | 含义 |
---|---|
<cache_size> | |
正反馈集合 | |
负反馈集合 | |
物品集合 | |
用户喜欢的物品的集合 | |
具有标签的物品的集合 | |
用户集合 | |
具有标签的用户的集合 | |
用户的标签 | |
物品的标签 | |
所有用户使用的标签 | |
所有物品使用的标签 | |
用户的相似用户 | |
物品的相似物品 | |
如果条件满足则等于1,否则等于0 |
提示
<cache_size>
来自于配置文件。
[recommend]
# The cache size for recommended/popular/latest items. The default value is 100.
cache_size = 100
非个性化推荐算法
非个性化算法向所有用户推荐相同的内容。
最新物品推荐
根据时间戳向用户展示最新的物品,可以让新内容及时暴露给用户。要在 Gorse 中启用最新推荐,您需要为物品设置时间戳信息。如果没有时间戳,Gorse 将不会生成最新物品列表。
最新物品推荐相当于以下SQL:
select item_id from items order by time_stamp desc limit <cache_size>;
人气物品推荐
许多网站向用户显示最近的热门,如Twitter的趋势。流行物品的推荐相当于以下SQL:
select item_id from (
select item_id, count(_) as feedback_count from feedback
where feedback_type in <positive_feedback_types>
and time_stamp >= NOW() - INTERVAL <popular_window>
group by item_id) t
order by feedback_count desc limit <cache_size>;
提示
配置文件中的<popular_window>
对应的是热门物品的窗口。
[recommend.popular]
# The time window of popular items. The default values is 4320h.
popular_window = "720h"
相似算法
在很多情况下,用户喜欢特定类型的物品,例如解密游戏玩家喜欢解谜游戏、B站的年轻小伙子喜欢看小姐姐跳舞。
物品相似度
如果一个物品与另一个物品比其他物品有更多的共同用户或标签,则该物品与另一个物品更相似。余弦相似度非常适合度量两个物品之间的相似度。由于每个用户或标签的重要性不同,我们使用 IDF 来计算每个用户或标签的权重。
- 标签权重:标签权重定义如下
如果一个标签被更多的物品使用,则该标签更通用所以权重更低。
- 用户权重:用户权重定义为
如果一个用户很多喜欢的物品,就意味着这个用户有更广泛的兴趣,他或她的权重更小。
基于标签权重和用户权重,Gorse实现了三种相似度算法:
- 相似: 根据物品之间的标签重叠程度来计算相似度
- 相关: 根据物品之间用户的重叠程度计算相似度。
- 自动: 根据物品之间的标签重叠程度和用户重叠程度来计算相似度。
对于每个物品,相似度最高的个物品将被缓存为这个物品的相似物品。物品相似度算法可以在配置文件中设置。
[recommend.item_neighbors]
# The type of neighbors for items. There are three types:
# similar: Neighbors are found by number of common labels.
# related: Neighbors are found by number of common users.
# auto: Neighbors are found by number of common labels and users.
# The default value is "auto".
neighbor_type = "similar"
如果物品有高质量的标签,similar
是最佳选择。如果物品没有标签,则使用related
。对于其他情况,请考虑auto
。
用户相似度
用户相似度的计算与物品相似度的计算类似。首先,我们使用IDF来计算每个物品或标签的权重。
- 标签权重:标签权重定义如下
如果一个标签被更多的用户使用,这个标签就更通用,权重也更小。
- 物品权重:物品权重定义如下
如果一个物品有更多的用户,这意味着这个物品很受欢迎,表示用户的偏好时权重也就更低。
基于标签权重和物品权重,Gorse 实现了三种相似度算法:
- 相似: 根据用户之间的标签重叠程度来计算相似度
- 相关: 根据用户之间的物品重叠程度来计算相似度。
- 自动: 根据用户之间的标签重叠程度和物品重叠程度来计算相似度。
对于每个用户,相似度最高的n$用户将被缓存为该用户的相似用户。用户相似度的算法可以在配置文件中设置。
[recommend.user_neighbors]
# The type of neighbors for users. There are three types:
# similar: Neighbors are found by number of common labels.
# related: Neighbors are found by number of common favorite items.
# auto: Neighbors are found by number of common labels and favorite items.
# The default value is "auto".
neighbor_type = "similar"
如果用户附加了高质量的标签,similar
是最佳选择。如果用户没有标签,则使用related
。对于其他情况,可以考虑auto
。
聚类索引
Gorse 需要为每个用户和物品生成相似推荐,但这是一个昂贵的过程。通过暴力为所有物品生成相似推荐的复杂度是(为简单起见,假设相似度计算的复杂度是恒定的)。聚类索引[1]在可接受的精度衰减的情况下,为每个物品搜索相似物品的效率更高。聚类索引的使用过程包括两个步骤
- 聚类: 使用_spherical k-means_算法将物品聚类到类,类的中心点为()。每个物品都属于第类。
- while 或 在上一步迭代中发生改变 do
- 第 类的中心点
- end while
- 搜索: 搜索物品的相似物品
- 第1步,找到与物品最近的个中心点。
- 第2步,在个类包含的物品上找到与最近的个物品。
时间复杂度为,其中是停止聚类算法的最大迭代数。在 Gorse 实现中,,时间复杂度变为。因此,如果,聚类索引是高效的。
聚类索引可以通过enable_index
打开和关闭,默认情况下是打开的。聚类索引需要设置参数,也就是要查询的聚类数量。太小的会导致索引无法达到要求的召回率,而太大的则会降低性能。构建过程中会尝试增加。如果查询召回率达到index_recall
,或者增长次数达到index_fit_epoch
,构建过程将停止增加。
[recommend.item_neighbors]
# Enable approximate item neighbor searching using vector index. The default value is true.
enable_index = true
# Minimal recall for approximate item neighbor searching. The default value is 0.8.
index_recall = 0.8
# Maximal number of fit epochs for approximate item neighbor searching vector index. The default value is 3.
index_fit_epoch = 3
[recommend.user_neighbors]
# Enable approximate user neighbor searching using vector index. The default value is true.
enable_index = true
# Minimal recall for approximate user neighbor searching. The default value is 0.8.
index_recall = 0.8
# Maximal number of fit epochs for approximate user neighbor searching vector index. The default value is 3.
index_fit_epoch = 3
个性化推荐算法
现在有很多花哨的推荐算法,其中大部分是基于深度学习的[2]。然而,我们认为没有深度学习的传统方法足以实现至少可以用的推荐性能。
基于物品相似度的推荐
对于一个喜爱物品的用户,如果我们知道任何一对物品之间的相似性,那么用户喜欢一个物品的概率通过该物品与喜爱物品之间的相似性之和预测的。
其中是物品和物品目之间的相似度。对于一个用户来说,在整个项目集中搜索推荐物品的时间复杂度为。在实践中,对于大多数成对的两个物品,它们的相似度为零。因此,我们可以在最喜欢的物品的相似推荐中搜索推荐物品。
- 从喜欢的物品的相似推荐中收据候选物品。
- 对于中的每个物品,通过以下方式计算预测分数
其中表示只有当在的相似物品中时,相似度才会被加到预测分数上。由于Gorse只缓存每个物品前个相似物品及其相似度,所以很多相似度在缓存是缺失的。优化算法的时间复杂度为。
基于用户相似度的推荐
基于用户相似性的推荐与基于物品相似性的推荐非常类似。
- 从用户的相似用户的最爱物品中收集候选物品。
- 对于中的每个物品,通过以下公式计算预测分数
表示只有当受到用户的青睐时,相似度才会被加到预测分数上。时间复杂度也是。
矩阵分解
在矩阵分解模型中,物品和用户由向量表示。一个用户喜欢一个物品的概率是由两个向量的点乘来预测的。
其中是用户的嵌入向量,是物品的嵌入向量。矩阵分解的模型很简单,但有不止一种训练算法。
BPR
BPR[3](Bayesian Personalized Ranking)是一种基于对优化的训练算法。BPR的训练数据由三元组集合构成。
中的表示用户喜欢而不是。
为所有物品寻找最优的个性化排名的贝叶斯公式是使以下后验概率的最大化,其中代表矩阵分解模型的参数
其中是用户的期望但不为人知的偏好。所有的用户都被认为是独立的。BPR还假设特定用户的每对物品的排序与其他每对物品的排序无关。因此,上述针对用户的似然函数首先可以改写为单个密度的乘积,其次可以对所有用户进行组合。
其中,。
对于参数,BPR引入了一般的先验密度,这是一个正态分布,均值为零,方差矩阵为。
其中。那么,个性化排序的优化目标是
BPR-Opt 中模型参数的梯度为:
基于自举抽样三元组的随机梯度训练算法如下:
- 初始化
- repeat
- 从 采样得到
- util 收敛
- return
其中 为学习率。嵌入向量的梯度为
eALS
eALS[4]是一种基于点优化的训练算法。对于一对用户和物品,用于训练的基础事实是
嵌入向量通过最小化以下代价函数进行优化[5]:
其中,,为反馈的权重。
() is the weight for negative feedbacks. The derivative of the objective function with respect to is
通过将此导数设为0,得到的解(公式1)。
其中表示的缓存的元素,定义为。同理,得到一个物品嵌入向量的最优解(公式2)。
其中表示缓存的第个元素,定义为。该训练算法总结为
- 随机初始化 和
- for do
- while 结束条件满足则停止 do
- for to do
- for to do
- for do
- 公式 1
- for do
- end
- end
- for to do
- for to do
- for do
- 公式 2
- for do
- end
- end
- return 和
超参的随机搜索
有一些用于模型训练的超参,如学习率、正则化系数等。Gorse 使用随机搜索[6]来寻找最佳超参。超参的优化行为由model_search_epoch
和model_search_trials
设置。大的值可能会产生更好的推荐结果,但要花费更多的CPU时间。最佳的模型超参组合是相对稳定的,除非数据集发生巨大的变化。
[recommend.collaborative]
# The number of epochs for model searching. The default value is 100.
model_search_epoch = 100
# The number of trials for model searching. The default value is 10.
model_search_trials = 10
HNSW 索引
Gorse 中的矩阵分解模型将用户和物品表示为嵌入向量。对于每个用户来说,嵌入向量与用户的点积大的物品被选为推荐物品。因此,搜索推荐物品最直观的方法是扫描所有物品,在扫描过程中计算嵌入向量的点积,并选择点积最大的前几个物品作为推荐结果。假设有N个用户和M个物品,为所有用户生成推荐结果的计算复杂度为。然而,如果物品和用户的数量很大,整体的计算量是不可接受的。
一个更有效的方法是使用向量索引HNSW[7]。HNSW索引为所有物品向量创建了一个导航图。HNSW的结果并不准确,但召回率的小损失换回了性能提升。HNSW需要设置一个参数ef_construction,ef_construction太小会阻止向量索引达到所需的召回率,太大会降低搜索性能。构建过程试图会不断增加,如果召回率达到index_recall
,或者如果迭代数达到index_fit_epoch
,则停止增加。
[recommend.collaborative]
# Enable approximate collaborative filtering recommend using vector index. The default value is true.
enable_index = true
# Minimal recall for approximate collaborative filtering recommend. The default value is 0.9.
index_recall = 0.9
# Maximal number of fit epochs for approximate collaborative filtering recommend vector index. The default value is 3.
index_fit_epoch = 3
注
HNSW索引很复杂,欲了解更多信息,请阅读原始论文:https://arxiv.org/abs/1603.09320
因子分解机
用户标签和物品标签是个性化推荐的重要信息,但矩阵分解只处理用户嵌入向量和物品嵌入向量。因式分解机[8]能够利用丰富特征,如用户特征和物品特征。
与矩阵分解的训练算法不同,因子分解机的训练需要用到负反馈。训练数据集的构建方法是
输入向量的维度是物品数量、用户数量、物品标签数量和用户标签数量之和。对于一对来说,中的每个元素的定义为
对一个输入向量的预测输出是
其中需要训练的模型参数为, , 。是两个向量的点积。参数通过SGD针对logit代价函数进行优化。代价函数为
每个参数的梯度为
超参同样通过随机搜索进行优化,继续使用recommend.collaborative
这个配置项。
Auvolat, Alex, et al. "Clustering is efficient for approximate maximum inner product search." arXiv preprint arXiv:1507.05910 (2015). ↩︎
Zhang, Shuai, et al. "Deep learning based recommender system: A survey and new perspectives." ACM Computing Surveys (CSUR) 52.1 (2019): 1-38. ↩︎
Rendle, Steffen, et al. "BPR: Bayesian personalized ranking from implicit feedback." Proceedings of the Twenty-Fifth Conference on Uncertainty in Artificial Intelligence. 2009. ↩︎
He, Xiangnan, et al. "Fast matrix factorization for online recommendation with implicit feedback." Proceedings of the 39th International ACM SIGIR conference on Research and Development in Information Retrieval. 2016. ↩︎
Hu, Yifan, Yehuda Koren, and Chris Volinsky. "Collaborative filtering for implicit feedback datasets." 2008 Eighth IEEE international conference on data mining. Ieee, 2008. ↩︎
Bergstra, James, and Yoshua Bengio. "Random search for hyper-parameter optimization." Journal of machine learning research 13.2 (2012). ↩︎
Malkov, Yu A., and Dmitry A. Yashunin. "Efficient and robust approximate nearest neighbor search using hierarchical navigable small world graphs." IEEE transactions on pattern analysis and machine intelligence 42.4 (2018): 824-836. ↩︎
Rendle, Steffen. "Factorization machines." 2010 IEEE International conference on data mining. IEEE, 2010. ↩︎