18.0 基于传统机器学习的推荐系统

Posted by 子颢 on August 21, 2018

推荐算法具有非常多的应用场景和商业价值,种类很多,但是目前使用最广泛的是以下两种:

  1. 基于内容的推荐。通过NLP的一些技术,挖掘文本内容,进而做推荐。
  2. 基于协同过滤算法的推荐。

协同过滤

协同过滤(Collaborative Filtering),假设我们有m个用户,n个商品,还有一大堆用户对商品的行为数据(浏览、点赞、点踩、评论、分享等),每一种行为可以通过领域知识映射为分值,那么我们就可以得到如下所示的一张用户对商品的打分二维表,只有部分用户和部分商品之间是有评分数据的,其它部分评分是空白,因此这是个稀疏矩阵,此时我们要用已有的部分打分数据来预测那些空白的用户与商品之间的评分关系,找到最高评分的物品推荐给用户。这就是协同过滤。

  item1 item2 …. itemm
user1 2.5      
user2        
….. 6.5      
usern 5.0      

一般来说,协同过滤推荐分为两种类型。第一种是基于用户(user-based)的协同过滤,第二种是基于项目(item-based)的协同过滤。

基于用户(user-based)的协同过滤主要考虑的是用户和用户之间的相似度,只要找出相似用户喜欢的物品,并预测目标用户对对应物品的评分,就可以找到评分最高的若干个物品推荐给用户。(矩阵的行向量可以用来计算用户相似度)。

基于项目(item-based)的协同过滤主要考虑的是物品和物品之间的相似度,只有找到了目标用户对某些物品的评分,那么我们就可以对相似度高的类似物品进行预测,将评分最高的若干个相似物品推荐给用户。(矩阵的列向量可以用来计算物品相似度)。比如你在网上买了一本机器学习相关的书,网站马上会推荐一堆机器学习,大数据相关的书给你,这里就明显用到了基于项目的协同过滤推荐思想。

我们简单比较下基于用户的协同过滤和基于项目的协同过滤:基于用户的协同过滤需要在线找用户和用户之间的相似度关系(实时变动),计算复杂度肯定会比基于基于项目的协同过滤高,但是可以帮助用户找到新类别的有惊喜的物品。而基于项目的协同过滤,由于考虑的物品的相似性一段时间不会改变,因此可以很容易的离线计算,准确度一般也可以接受,但是推荐的多样性来说,就很难带给用户惊喜了。一般对于小型的推荐系统来说,基于项目的协同过滤肯定是主流,但是如果是大型的推荐系统来说,则可以考虑基于用户的协同过滤,或者两者结合。

代码参见 协同过滤推荐系统实战

推荐系统模型

频繁项集挖掘

我们可以找出用户购买的所有物品里,经常在一起出现的物品,来做频繁项集挖掘,如果用户购买了频繁N项集或者序列里的部分物品,那么我们可以将频繁项集或序列里的其他物品按一定的评分准则推荐给用户。 推荐阅读 Apriori算法

聚类挖掘

  1. 基于用户聚类,将用户聚类成不同的目标人群,将同样目标人群评分高的物品推荐给目标用户。
  2. 基于物品聚类,将用户评分高物品的相似同类物品推荐给用户。

分类挖掘

根据用户评分的高低,设置一份评分阈值,评分高于阈值的就是推荐,评分低于阈值就是不推荐,我们将问题变成了一个二分类问题。 特征可以选择用户画像特征、物品画像特征以及物品内容特征(词频、TF-IDF),然后做concat。

基于PageRank的推荐

搜索引擎的搜索结果的排名主要取决于两组信息:一是网页本身的质量(PageRank算法);而是该查询与网页的相关性(TF-IDF)。 recommend recommend

如何确定搜索内容与网页的相关性,假设一个用户的查询有N个关键词,可以计算这N个关键词在该网页的TF-IDF值之和作为相关性score,即: recommend 网页本身的质量有了,相关性score也计算出来,就可以通过二者的乘积得到搜索排名。延伸到推荐领域,A就是用户对物品的评分矩阵,B是物品本身的质量分,可以通过反复的转置后迭代得到最终的物品质量分。

基于NMF的推荐

NMF(Non-negative Matrix Factorization,非负矩阵分解),将一个大矩阵分解为两个矩阵的乘积: recommend

NMF可以用于提取文本主题,假设我们有m个文本,n个词,Aij表示第i个文本的第j个词的特征值(词频或TF-IDF)。k是我们假设的主题数,一般要比文本数少。

NMF分解后,Wik对应第i个文本的和第k个主题的概率相关度,而Hkj对应第j个词和第k个主题的概率相关度。当然也可以反过来去理解:我们输入的有m个词,n个文本,而Aij对应第i个词在第j个文本的特征值。NMF分解后,Wik对应第i个词的和第k个主题的概率相关度,而Hkj对应第j个文本和第k个主题的概率相关度。

注意到这里我们使用的是”概率相关度”,这是因为我们使用的是”非负”的矩阵分解,这样我们的W,H矩阵值的大小可以用概率值的角度去看,从而可以得到文本和主题的概率分布关系。

利用文本与主题的相关程度,可以将文本聚类;利用词和主题的相关程度,可以提取每个主题的关键词。

import numpy as np
from sklearn.decomposition import NMF

# 假设我们有4个词,5个文本组成的特征值矩阵
X = np.array([[1,1,5,2,3], [0,6,2,1,1], [3, 4,0,3,1], [4, 1,5,6,3]])
# n_components即我们的主题数k,确定它需要一些对于要分析文本主题大概的先验知识
model = NMF(n_components=2, alpha=0.01)

# 单词话题矩阵
W = model.fit_transform(X)
# 文本话题矩阵
H = model.components_

print(W)
print(H)

运行结果:

[[1.67371185 0.02013017]
 [0.40564826 2.17004352]
 [0.77627836 1.5179425 ]
 [2.66991709 0.00940262]]
[[1.32014421 0.40901559 2.10322743 1.99087019 1.29852389]
 [0.25859086 2.59911791 0.00488947 0.37089193 0.14622829]]

分析H矩阵可以看到,第1,3,4,5个文本和第一个隐含主题更相关,而第二个文本与第二个隐含主题更加相关。 分析W矩阵可以看到,第1,4个词和第一个隐含主题更相关,而第2,3个词与第二个隐含主题更加相关。

基于LDA的推荐

LDA(Latent Dirichlet Allocation,隐含狄利克雷分布),这是一个被广泛使用的主题模型。其原理如下:

  1. 假设我们有m个文本,n个词,Aij表示第i个文本的第j个词的特征值(词频或TF-IDF),当然我们还是需要先假定一个主题数目K;
  2. 对于每篇文章,都有各自的主题分布(为多项分布,该多项分布的概率参数服从Dirichlet分布,该Dirichlet分布的参数为α);
  3. 对于每个主题,都有各自的词分布(为多项分布,该多项分布的概率参数服从Dirichlet分布,该Dirichlet分布的参数为β)。

recommend

  1. 对于任一文档d,其主题分布θd为:θd = Dirichlet(α⃗),其中,α为分布的超参数,是一个K维向量,K为主题个数;
  2. 对于任一主题k, 其词分布βk为:βk = Dirichlet(η⃗),其中,η为分布的超参数,是一个V维向量,V代表词汇表里所有词的个数;
  3. 对于每一篇文档d,它的每一个词n,我们可以先从他的主题分布θd中得到它的主题Zd,n = multi(θd);
  4. 然后根据该主题,从它的词分布βk中得到我们看到的词Wd,n = multi(βzdn)。

那么我们应该如何根据训练样本求解这个LDA模型的各个参数值呢(即求出每一篇文档的主题分布和每一个主题中的词分布)? 一般有两种方法:

  1. 第一种是基于Gibbs采样算法求解。即通过Gibbs采样得到所有词的主题,然后统计所有词的主题计数,就可以得到各个主题的词分布;接着统计各个文档对应词的主题计数,得到各个文档的主题分布。
  2. 第二种是基于变分推断EM算法求解。模型的隐变量为θ,β,z,模型的参数是α,η。
import numpy as np
from sklearn.decomposition import NMF, LatentDirichletAllocation

# 假设我们有4个词,5个文本组成的特征值矩阵
X = np.array([[1, 1, 5, 2, 3], [0, 6, 2, 1, 1], [3, 4, 0, 3, 1], [4, 1, 5, 6, 3]])
# n_components即我们的主题数k,确定它需要一些对于要分析文本主题大概的先验知识
# model = NMF(n_components=2, alpha=0.01)
model = LatentDirichletAllocation(n_components=2, random_state=0, learning_method='batch')

# 单词话题矩阵
W = model.fit_transform(X)
# 文本话题矩阵
H = model.components_
# 模型的困惑度,代表生成整个训练集文档的似然估计的负值,perplexity值越小,模型能力越强。可以用来作为选择超参K的参考。
perplexity = model.perplexity(X)

print(W)
print(H)
print(perplexity)

运行结果:

[[0.90770735 0.09229265]
 [0.2976618  0.7023382 ]
 [0.07779857 0.92220143]
 [0.9318666  0.0681334 ]]
[[ 5.37754968  1.88301326 12.18651534  8.71274273  6.96941893]
 [ 3.62245032 11.11698674  0.81348466  4.28725727  2.03058107]]
5.96164339425483

社群


分享到: 微博 微信


返回顶部