在我看来,作为一位中国人的我们不管做什么决定都在面临多种选择。例如,如果我这个时候想要买一本书,但是我却不知道我想看什么书、不知道类型、不知道方向,那么这个时候打开各种进行软件搜索可能会出现各种各样的结果。我可能会浪费大量时间在互联网上浏览各种并在各个希望淘金的站点中进行拖曳。我可能会寻求其他人的建议。
但是,如果有一个网站或应用程序可以根据我之前阅读的内容向我推荐书,那么会很好的帮助到我,不用浪费时间在各种网站上自己去找书,我只需登录网站或者应用程序就好了!它会根据我的口味量身定制的10本书推荐给我
这就是推荐引擎所做的事情,现如今,大多数企业正在利用它的力量。从亚马逊到网飞(Netflix),从谷歌到Goodreads,毫不夸张的说推荐引擎是机器学习技术中使用最广泛的应用之一。
在本文中,我们将介绍各种类型的推荐引擎算法以及在Python中创建推荐引擎的基础知识。我们还将看到这些算法工作背后的数学原理。最后,我们将使用矩阵分解来创建自己的推荐引擎。
1.什么是推荐引擎?
在买东西的时候,如果对产品有任何疑问的时候,人们通常倾向于购买他们的朋友或信任的人推荐给他们的产品。这在曾经是人们主要的购买方式。但是随着数字时代的到来,这个圈子已经扩大到包括利用某种推荐引擎的网站。
推荐引擎使用不同的算法过滤数据,并向用户推荐最相关的物品。它首先记录了客户的过去行为,并在此基础上推荐了用户可能购买的产品。
如果是一个全新的用户访问了电子商务网站,则该网站将没有该用户的任何历史记录。那么在这种情况下,网站如何向用户推荐产品呢?一种可能的解决方案是推荐网站内最畅销的产品,即需求量大的产品。另一个可能的解决方案是推荐可以为企业带来最大利润的产品。
如果我们可以根据客户的需求和兴趣向他们推荐一些商品,那么它将对用户的体验产生积极影响并可能会导致用户的频繁访问。因此,当今的企业正在通过研究用户的过去行为来构建智能的推荐引擎。
现在,我们对推荐引擎有了一个大体的了解,现在让我们看一下它是怎么工作的。
2.推荐引擎的工作方式
在深入探讨这个主题之前,首先我们先考虑一下如何向用户推荐商品:
- 我们可以向用户推荐在所有用户中最受欢迎的商品
- 我们可以根据用户的偏好(用户特性)将其分为多个部分,并根据其所属的部分向他们推荐商品
以上的两种方法都有其缺点。在第一种情况下,每个用户最受欢迎的商品都是相同的,因此每个人都会看到相同的推荐。在第二种情况下,随着用户数量的增加,用户特性的数量也会增加。因此,将用户划分为不同的部分将是一项非常困难的任务。
这里的主要问题是我们无法根据用户的特定兴趣来定制建议。就像亚马逊推荐你购买笔记本电脑,只是因为它被大多数购物者购买了。但值得庆幸的是,亚马逊(或任何其他大公司)都不推荐使用上述方法的产品。他们使用一些个性化的方法来帮助他们更准确地推荐产品。
现在,通过执行以下的步骤来重点介绍推荐引擎的工作方式。
2.1数据收集
这是构建推荐引擎的第一步,也是最关键的一步。可以通过两种方式收集数据:显式和隐式。 显式数据是有意提供的信息,即来自用户的输入,例如电影分级。隐式数据是不是有意提供的信息,而是从可用数据流(例如搜索历史,点击次数,订单历史记录等)中收集的信息。
在上图中,Netflix正在以用户对不同电影给出的评分的形式明确地收集数据。
在这里,用户的订单历史记录是由Amazon记录的,这是隐式数据收集模式的一个例子。
2.2数据存储
数据量决定了模型推荐的质量。例如,在电影推荐系统中,用户对电影的评分越高,对其他用户的推荐就越好。数据类型在决定必须使用的存储类型方面起着重要作用。这种类型的存储可以包括标准SQL数据库,NoSQL数据库或某种对象存储。
2.3过滤数据
在收集和存储数据之后,我们必须对其进行过滤,以提取出最终推荐所需的相关信息。
有各种各样的算法可以帮助我们简化过滤的过程。在下一节中,我们将详细介绍每种算法。
2.3.1基于内容的过滤
该算法推荐与用户过去喜欢的产品相似的产品。
例如,如果某人喜欢电影“盗梦空间”,那么这个算法将推荐给他属于同一类型的电影。但是,该算法如何理解从哪个类型中挑选电影并推荐呢?
**以Netflix为例。**他们以向量的形式保存与每个用户有关的所有信息。该向量包含用户过去的行为,即用户喜欢/不喜欢的电影以及他们给出的评分。这个向量称为轮廓向量。与电影有关的所有信息都存储在另一个称为物品向量的向量中。物品向量包含每部电影的详细信息,例如类型,演员,导演等。
基于内容的过滤算法找到轮廓向量和物品向量之间的角度的余弦值,即余弦相似度。假设A是轮廓向量,B是物品向量,则它们之间的相似度可以计算为:
sim
(
A
,
B
)
=
cos
(
θ
)
=
A
⋅
B
∥
A
∥
∣
∣
B
∥
\operatorname{sim}(A, B)=\cos (\theta)=\frac{A \cdot B}{\|A\||| B \|}
sim(A,B)=cos(θ)=∥A∥∣∣B∥A⋅B
基于介于-1到1之间的余弦值,电影以降序排列,推荐使用以下两种方法之一:
- Top-n方式:推荐前n部电影(此处n可由业务决定)
- 评分表方法:设置阈值并推荐所有超过该阈值的电影
其他可用于计算相似度的方法是:
- 欧几里德距离:如果在n维空间中绘制,相似的物品将彼此紧邻。因此,我们可以计算物品之间的距离,并根据该距离向用户推荐物品。欧氏距离的公式由下式给出:
Euclidean Distance = ( x 1 − y 1 ) 2 + … + ( x N − y N ) 2 \text { Euclidean Distance }=\sqrt{\left(x_{1}-y_{1}\right)^{2}+\ldots+\left(x_{N}-y_{N}\right)^{2}} Euclidean Distance =(x1−y1)2+…+(xN−yN)2
- 皮尔逊相关性:它告诉我们两个项目之间有多少相关性。相关性越高,相似性越高。皮尔逊相关性可以使用以下公式计算:
sim ( u , v ) = ∑ ( r u i − r ˉ u ) ( r v i − r ˉ v ) ∑ ( r u i − r ˉ u ) 2 ∑ ( r v i − r ˉ v ) 2 \operatorname{sim}(u, v)=\frac{\sum\left(r_{u i}-\bar{r}_{u}\right)\left(r_{v i}-\bar{r}_{v}\right)}{\sqrt{\sum\left(r_{u i}-\bar{r}_{u}\right)^{2}} \sqrt{\sum\left(r_{v i}-\bar{r}_{v}\right)^{2}}} sim(u,v)=∑(rui−rˉu)2∑(rvi−rˉv)2∑(rui−rˉu)(rvi−rˉv)
该算法的主要缺点是它仅限于推荐相同类型的项目。绝不会推荐用户过去从未购买或不喜欢的产品。因此,如果用户过去仅观看或喜欢动作电影,则系统将仅推荐动作电影。这是构建引擎的一种非常狭窄的方法。
为了改进这种类型的系统,我们需要一种算法,该算法不仅可以根据内容来推荐商品,还可以根据用户的行为来推荐商品。
2.3.2协同过滤
让我们通过一个例子来理解这一点。如果A喜欢《星际穿越》,《盗梦空间》和《前目的地》等三部电影,而B人喜欢《盗梦空间》,《前目的地》和《致命魔术》,那么他们的兴趣几乎相同。我们可以肯定地说,A应该喜欢《致命魔术》,B应该喜欢《星际穿越》。协同过滤算法使用“用户行为”来推荐项目。这是业界最常用的算法之一,因为它不依赖于任何其他信息。协同过滤技术有多种类型,我们将在下面详细介绍它们。
基于用户的协同过滤
该算法首先找到用户之间的相似度得分。然后,根据相似度评分,挑选出最相似的用户,并推荐这些相似的用户之前喜欢或购买的产品。
就我们之前的电影例子来说,该算法根据每个用户先前对不同电影给予的评分来找到每个用户之间的相似性。通过计算其他用户对项目i给出的用户评分的加权总和,可以计算出用户u对某个项目的预测。
预测Pu,i由下式给出:
P
u
,
i
=
∑
v
(
r
v
,
i
∗
s
u
,
v
)
∑
v
s
u
,
v
P_{u, i}=\frac{\sum_{v}\left(r_{v, i} * s_{u, v}\right)}{\sum_{v} s_{u, v}}
Pu,i=∑vsu,v∑v(rv,i∗su,v)
公式解读,
- Pu,i是一个项目的预测
- Rv,i是用户v对电影i的评级
- Su,v是用户之间的相似性
现在,我们在配置文件向量中具有用户的评分,并基于此我们必须预测其他用户的评分。为此我们将要采取以下的步骤:
- 为了进行预测,我们需要用户u和v之间的相似性。我们可以利用皮尔逊相关性。
- 首先,我们找到两个用户都评价过的物品,并根据这些评价来计算用户之间的相关性。
- 可以使用相似度值来计算预测。该算法首先计算每个用户之间的相似度,然后基于每个相似度计算预测结果。具有较高相关性的用户将趋于相似。
- 基于这些预测值,提出建议。让我们通过一个例子来理解它:
考虑用户电影评分矩阵:
用户/电影 | X1 | X2 | X3 | X4 | X5 | 平均用户评分 |
---|---|---|---|---|---|---|
A | 4 | 1 | – | 4 | – | 3 |
B | – | 4 | – | 2 | 3 | 3 |
C | – | 1 | – | 4 | 4 | 3 |
这里我们有一个用户电影分级矩阵。为了更实际地理解这一点,让我们在上表中找到用户(A,C)和(B,C)之间的相似性。由A和C评级的电影是电影x2和x4,由B和C评级的电影是x2,x4和x5。
r
A
C
=
[
(
1
−
3
)
∗
(
1
−
3
)
+
(
4
−
3
)
∗
(
4
−
3
)
]
[
(
1
(
1
−
3
)
2
+
(
4
−
3
)
2
)
32
+
(
(
1
−
3
)
2
+
(
4
−
3
)
2
)
3
/
3
]
=
1
r
B
C
=
[
(
4
−
3
)
∗
(
1
−
3
)
+
(
2
−
3
)
∗
(
4
−
3
)
+
(
3
−
3
)
∗
(
4
−
3
)
M
(
(
4
−
3
)
2
+
(
2
⋅
3
)
2
+
(
3
−
3
)
2
)
3
/
2
+
(
(
1
−
3
)
2
+
(
4
−
3
)
2
+
(
4
−
3
)
2
)
1
/
2
]
=
−
0.866
\begin{aligned} &\mathrm{r}_{\mathrm{AC}}=\left[(1-3)^{*}(1-3)+(4-3)^{*}(4-3)\right]\left[\left(1(1-3)^{2}+(4-3)^{2}\right)^{32+}\left((1-3)^{2}+(4-3)^{2}\right)^{3 / 3}\right]=1\\ &\mathrm{r}_{\mathrm{BC}}=\left[(4-3)^{*}(1-3)+(2-3)^{*}(4-3)+(3-3)^{*}(4-3) M\left((4-3)^{2}+(2 \cdot 3)^{2}+(3-3)^{2}\right)^{3 / 2}+\left((1-3)^{2}+(4-3)^{2}+(4-3)^{2}\right)^{1 / 2}\right]=-0.866 \end{aligned}
rAC=[(1−3)∗(1−3)+(4−3)∗(4−3)][(1(1−3)2+(4−3)2)32+((1−3)2+(4−3)2)3/3]=1rBC=[(4−3)∗(1−3)+(2−3)∗(4−3)+(3−3)∗(4−3)M((4−3)2+(2⋅3)2+(3−3)2)3/2+((1−3)2+(4−3)2+(4−3)2)1/2]=−0.866
用户A和C之间的相关性大于B和C之间的相关性。因此,用户A和C具有更高的相似性,并且将向用户C推荐用户A喜欢的电影,反之亦然。
该算法非常耗时,因为它涉及为每个用户计算相似度,然后为每个相似度分数计算预测。处理此问题的一种方法是仅选择几个用户(邻居)而不是全部进行预测,也就是我们不选择对所有相似性值进行预测,而是仅选择几个相似性值。选择邻居的方法有很多:
- 选择一个阈值相似度,然后选择高于该值的所有用户
- 随机选择用户
- 以相似度值从大到小的顺序排列邻居,并选择前N个用户
- 使用集群选择邻居
当用户数较少时,此算法很有用。当有大量用户时,此方法无效,因为计算所有用户对之间的相似度将花费大量时间。这导致我们进行物品-物品协同过滤,当用户数量超过建议的物品数时,这种方法是有效的。
基于物品的协同过滤
在该算法中,我们计算每对项目之间的相似度。
因此,在我们的案例中,我们将发现每个电影对之间的相似性,并以此为基础,推荐过去用户喜欢的相似电影。该算法的工作原理与基于用户的协同过滤类似,只是有一点点变化–我们计算“项目-邻居”的加权总和,而不是计算“用户-邻居”的加权总和。预测如下:
P
u
,
i
=
∑
N
(
s
i
,
N
∗
R
u
,
N
)
∑
N
(
∣
s
i
,
N
∣
)
P_{u, i}=\frac{\sum_{N}\left(s_{i, N} * R_{u, N}\right)}{\sum_{N}\left(\left|s_{i, N}\right|\right)}
Pu,i=∑N(∣si,N∣)∑N(si,N∗Ru,N)
现在我们将发现项目之间的相似性。
sim
(
i
,
j
)
=
cos
(
i
⃗
,
j
⃗
)
=
i
⃗
⋅
j
⃗
∥
i
⃗
∥
2
∗
∥
j
⃗
∥
2
\operatorname{sim}(i, j)=\cos (\vec{i}, \vec{j})=\frac{\vec{i} \cdot \vec{j}}{\|\vec{i}\|_{2} *\|\vec{j}\|_{2}}
sim(i,j)=cos(i,j)=∥i∥2∗∥j∥2i⋅j
现在,由于我们有了每部电影和评分之间的相似性,因此进行了预测,并根据这些预测推荐了类似的电影。让我们通过一个例子来理解它。
用户/电影 | x1 | X2 | X3 | X4 | X5 |
---|---|---|---|---|---|
A | 4 | 1 | 2 | 4 | 4 |
B | 2 | 4 | 4 | 2 | 1 |
C | – | 1 | – | 3 | 4 |
平均物品评分 | 3 | 2 | 3 | 3 | 3 |
这里的平均物品评分是对特定物品给予的所有评分的平均值(将其与我们在基于用户的协同过滤中看到的表进行比较)。与我们之前看到的基于用户的相似度不同,我们查找到的基于项目的相似度。
C
14
=
[
(
4
−
3
)
∗
(
4
−
3
)
+
(
2
−
3
)
∗
(
2
−
3
)
]
/
[
(
(
4
−
3
)
2
+
(
2
−
3
)
2
)
1
/
2
∗
(
(
4
−
3
)
2
+
(
2
−
3
)
2
)
1
/
2
]
=
1
C
15
=
[
(
4
−
3
)
∗
(
4
−
3
)
+
(
2
−
3
)
∗
(
1
−
3
)
]
/
[
(
(
4
−
3
)
2
+
(
2
−
3
)
2
)
1
/
2
∗
(
(
4
−
3
)
2
+
(
1
−
3
)
2
)
1
/
2
]
=
0.94
\begin{aligned} &\mathrm{C}_{14}=\left[(4-3)^{*}(4-3)+(2-3)^{*}(2-3)\right] /\left[\left((4-3)^{2}+(2-3)^{2}\right)^{1/2} *\left((4-3)^{2}+(2-3)^{2}\right)^{1 / 2}\right]=1\\ &\mathrm{C}_{15}=\left[(4-3)^{*}(4-3)+(2-3)^{*}(1-3)\right] /\left[\left((4-3)^{2}+(2-3)^{2}\right)^{1/2} *\left((4-3)^{2}+(1-3)^{2}\right)^{1/2}\right]=0.94 \end{aligned}
C14=[(4−3)∗(4−3)+(2−3)∗(2−3)]/[((4−3)2+(2−3)2)1/2∗((4−3)2+(2−3)2)1/2]=1C15=[(4−3)∗(4−3)+(2−3)∗(1−3)]/[((4−3)2+(2−3)2)1/2∗((4−3)2+(1−3)2)1/2]=0.94
电影x1和x4之间的相似度大于电影x1和x5之间的相似度。因此,基于这些相似值,如果有任何用户搜索电影x1,那么将会向他们推荐电影X4,反之亦然。在进一步实施这些概念之前,我们必须知道一个问题的答案–如果在数据集中添加新用户或新物品,将会发生什么?这被称为 冷启动。冷启动可以有两种类型:
- 用户冷启动
- 物品冷启动
用户冷启动意味着在数据集中引入了一个新用户。由于没有该用户的历史记录,因此系统不知道该用户的首选项。向该用户推荐物品变得越来越困难。那么,如何解决这个问题呢?一种基本方法是应用基于流行度的策略,即推荐最受欢迎的物品。这些可以通过最近整体或区域流行的内容来确定。一旦我们了解了用户的喜好,推荐物品就会更加容易。
另一方面,物品冷启动意味着将新产品投放市场或添加到系统中。用户行为是对于确定任何产品的价值的最重要的因素。物品获得的互动越多,我们的模型就越容易将物品推荐给合适的用户。我们可以利用基于内容的过滤来解决此问题。系统首先将新产品的内容用于推荐,然后最终用户对该产品执行操作。
现在,使用Python中的一个案例研究来巩固对这些概念的理解。准备好你的电脑,因为这将会很有趣!
3.使用MovieLens数据集的Python案例研究
我们将处理MovieLens数据集,并建立一个模型来向最终用户推荐电影。这些数据是由明尼苏达大学的GroupLens研究项目收集的。该数据集包括:
- 从943个用户对1682个电影中选出的10万个评分(1-5分)
- 用户的人口统计信息(年龄,性别,职业等)
首先,我们将导入标准库并使用Python读取数据集。你可以通过下方的代码,帮助你进行入门。你可以在自己的电脑上运行代码并获取输出:
# importing libraries
import pandas as pd
import numpy as np
# 传递每个CSV的列名,因为该文件没有给出列名,使用Pandas进行读取他们
#你可以从readme文件中检查列名
#阅读用户文件
u_cols = ['user_id', 'age', 'sex', 'occupation', 'zip_code']
users = pd.read_csv('ml-100k/u.user', sep='|', names=u_cols,encoding='latin-1')
# 阅读评级文件:
r_cols = ['user_id', 'movie_id', 'rating', 'unix_timestamp']
ratings = pd.read_csv('ml-100k/u.data', sep='\t', names=r_cols,encoding='latin-1')
# 阅读物品文件:
i_cols = ['movie id', 'movie title' ,'release date','video release date', 'IMDb URL', 'unknown', 'Action', 'Adventure',
'Animation', 'Children\'s', 'Comedy', 'Crime', 'Documentary', 'Drama', 'Fantasy',
'Film-Noir', 'Horror', 'Musical', 'Mystery', 'Romance', 'Sci-Fi', 'Thriller', 'War', 'Western']
items = pd.read_csv('ml-100k/u.item', sep='|', names=i_cols,
encoding='latin-1')
#加载数据集之后,我们应该查看每个文件的内容 (用户, 评分, 物品).
#查看用户文件
print("\nUser Data :")
print("shape : ", users.shape)
print(users.head())
#数据集中有943个用户,每个用户有5个特征,也就是用户ID,年龄,性别,职业,和编码
# 评分数据
print("\nRatings Data :")
print("shape : ", ratings.shape)
print(ratings.head())
# 我们有不同的用户和电影的组合的10万行数据量的评分,现在最后检查物品文件
# Item Data
print("\nItem Data :")
print("shape : ", items.shape)
print(items.head())
该数据集包含1682部电影的属性。有24列,其中最后19列指定特定电影的类型。这些列都是二进制列,即值为1表示电影属于该类型,否则为0。
数据集已由GroupLens分为训练集和测试集,其中每个用户的测试数据具有10个等级,即总计9,430行。我们将把这两个文件读入我们的Python中。
#创建列名
r_cols = ['user_id', 'movie_id', 'rating', 'unix_timestamp']
#读取训练集
ratings_train = pd.read_csv('ml-100k/ua.base', sep='\t', names=r_cols, encoding='latin-1')
#读取测试集
ratings_test = pd.read_csv('ml-100k/ua.test', sep='\t', names=r_cols, encoding='latin-1')
#查看训练集与测试集
ratings_train.shape, ratings_test.shape
现在是构建我们推荐的引擎的时候了!
4.从头开始构建协同过滤模型
我们将根据基于用户相似度和基于物品相似度进行推荐电影。为此,首先我们需要计算唯一身份用户和电影的数量。
n_users = ratings.user_id.unique().shape[0]
n_items = ratings.movie_id.unique().shape[0]
现在,我们将创建一个用户-物品向量,该向量可用于计算用户和物品之间的相似度。
data_matrix = np.zeros((n_users, n_items))
for line in ratings.itertuples():
data_matrix[line[1]-1, line[2]-1] = line[3]
现在,我们将计算相似度。我们可以使用sklearn中的pairwi se_distance函数来计算余弦相似度。
from sklearn.metrics.pairwise import pairwise_distances
user_similarity = pairwise_distances(data_matrix, metric='cosine')
item_similarity = pairwise_distances(data_matrix.T, metric='cosine')
这为我们提供了数组形式的基于物品和基于用户的相似度。下一步是基于这些相似性进行预测。让我们定义一个函数来做到这一点。
def predict(ratings, similarity, type='user'):
if type == 'user':
mean_user_rating = ratings.mean(axis=1)
#我们用np.newaxis使得mean_user_rating具有与ratings相同的格式
ratings_diff = (ratings - mean_user_rating[:, np.newaxis])
pred = mean_user_rating[:, np.newaxis] + similarity.dot(ratings_diff) / np.array([np.abs(similarity).sum(axis=1)]).T
elif type == 'item':
pred = ratings.dot(similarity) / np.array([np.abs(similarity).sum(axis=1)])
return pred
最后,我们将基于用户相似性和物品相似性进行预测。
user_prediction = predict(data_matrix, user_similarity, type='user')
item_prediction = predict(data_matrix, item_similarity, type='item')
事实证明,我们还有一个可自动生成所有这些推荐的库。现在让我们学习如何在Python中使用turicreate创建推荐引擎。要熟悉turicreate并将其安装在你的计算机上,请上GitHub上搜索turicreate。
5.使用Turicreate建立简单的受欢迎程度和协作过滤模型
安装turicreate之后,首先让我们导入它,并在我们的环境中读取训练和测试数据集。由于我们将使用turicreate,因此需要在SFrame中转换数据集。
import turicreate
train_data = turicreate.SFrame(ratings_train)
test_data = turicreate.Sframe(ratings_test)
我们具有用户行为以及用户和电影的属性,因此我们可以制作基于内容的内容协同过滤算法。我们将从一个简单的流行度模型开始,然后构建一个协同过滤模型。
首先,我们将建立一个模型,该模型将根据最受欢迎的选择来推荐电影,即所有用户都收到相同推荐的模型。我们将使用turicreate推荐功能的 popularity_recommender函数。
popularity_model = turicreate.popularity_recommender.create(train_data, user_id='user_id', item_id='movie_id', target='rating')
我们使用的各种参数:
- train_data:包含所需训练数据的SFrame
- user_id:代表每个用户ID的列名
- item_id:代表要推荐的每个物品的列名(movie_id)
- **目标:**代表用户给出的分数/等级的列名
现在是预测时间!我们将为数据集中的前5个用户推荐前5个物品。
popularity_recomm = popularity_model.recommend(users=[1,2,3,4,5],k=5)
popularity_recomm.print_rows(num_rows=25)
注意,所有用户的建议都是相同的-1467、1201、1189、1122、814。而且它们的顺序相同的!这确认所有推荐电影的平均评分为5,即所有观看该电影的用户都将其评为最高评分。因此,我们的评分系统可以按预期工作。
建立受欢迎度模型后,我们现在将建立一个协作过滤模型。让我们训练商品相似性模型,并为前5个用户提出前5个建议。
#训练模型
item_sim_model = turicreate.item_similarity_recommender.create(train_data, user_id='user_id', item_id='movie_id', target='rating', similarity_type='cosine')
#提出推荐
item_sim_recomm = item_sim_model.recommend(users=[1,2,3,4,5],k=5)
item_sim_recomm.print_rows(num_rows=25)
在这里,我们可以看到每个用户的建议(movie_id)是不同的。因此个性化是存在的,即对于不同的用户,我们有不同的推荐集。
在此模型中,我们没有每个用户给出的每部电影的评分。我们必须找到一种方法来预测所有这些缺失的评分。为此,我们必须找到一组可以定义用户如何评价电影的特征。这些称为潜在特征。我们需要找到一种从现有特征中提取最重要的潜在特征的方法。下一部分将介绍的矩阵分解是一种这样的技术,它使用较低维度的密集矩阵并有助于提取重要的潜在特征。
6.矩阵分解简介
让我们用一个例子来了解矩阵分解。考虑由不同用户给不同电影的用户电影评级矩阵(1-5)。
这里的user_id是不同用户的唯一ID,每部电影也都分配了唯一的ID。评分为0.0表示用户尚未对特定电影评分(1是用户可以给予的最低评分)。我们要预测这些缺失的评分。使用矩阵分解,我们可以找到一些潜在的特征,这些这些特征可以确定用户如何评价电影。我们将矩阵分解为组成部分,使这些部分的乘积将生成原始矩阵。
假设我们必须找到k个潜在特征。因此,我们可以将评级矩阵R(MxN)分为P(MxK)和Q(NxK),以使P x QT(此处QT是Q矩阵的转置)近似于R矩阵:
R
=
P
Σ
Q
T
\mathrm{R}=\mathrm{P} \Sigma \mathrm{Q}^{\mathrm{T}}
R=PΣQT
公式内容:
- M是用户总数
- N是电影总数
- K是总潜在特征
- R是MxN用户电影评级矩阵
- P是MxK用户特征关联矩阵,表示用户与特征之间的关联
- Q是NxK项特征相关性矩阵,表示电影和特征之间的关联
- Σ是KxK对角特征权重矩阵,表示特征的基本权重
通过矩阵分解选择潜在特征可消除数据中的噪声。怎么样?好吧,它删除了那些无法确定用户如何评价电影的特征。要获得用户puk对一部电影qik的所有潜在特征k的评分,我们可以计算这两个向量的点积,并将它们相加,得到基于所有潜在特征的评分。
r
u
i
=
Σ
k
=
1
K
p
u
k
σ
k
q
k
i
r_{u i}=\Sigma^{K}_{k=1} p_{u k} \sigma_{k} q_{k i}
rui=Σk=1Kpukσkqki
这就是矩阵分解为我们提供未由用户评级的电影的评级的方式。但是,如何将新数据添加到我们的用户-电影评分矩阵中呢?也就是说新用户加入并对电影进行评分,我们如何将这些数据添加到现有矩阵中?
让我用矩阵分解方法使你更轻松的理解。如果有新用户加入系统,则对角特征权重矩阵Σ以及项目特征相关性矩阵Q都将保持不变。唯一的变化将发生在用户特征相似度矩阵P中。我们可以应用一些矩阵乘法方法可以做到这一点。
我们有,
R
=
P
Σ
Q
T
\mathrm{R}=\mathrm{P} \Sigma \mathrm{Q}^{\mathrm{T}}
R=PΣQT
让我们在两边都乘以Q。
R
Q
=
P
Σ
Q
T
Q
\mathrm{RQ}=\mathrm{P} \Sigma \mathrm{Q}^{\mathrm{T}} \mathrm{Q}
RQ=PΣQTQ
现在,我们有
Q
⊤
Q
=
1
\mathrm{Q}^{\top} \mathrm{Q}=1
Q⊤Q=1
所以,
R
Q
=
P
Σ
\mathrm{RQ}=\mathrm{P} \Sigma
RQ=PΣ
进一步简化,我们可以得到P矩阵:
R Q Σ − 1 = P \mathrm{RQ\Sigma}^{-1}=\mathrm{P} RQΣ−1=P
这是更新后的用户特征相似性矩阵。类似地,如果将新电影添加到系统中,我们可以遵循类似的步骤来获取更新的物品特征相关性矩阵Q。
记住,我们将R矩阵分解为P和Q。但是我们如何确定哪个P和Q矩阵将逼近R矩阵呢?我们可以使用梯度下降算法来做到这一点。此处的目的是实际等级与使用P和Q估算的评分之间的平方误差最小。该平方误差由下式给出:
e u i 2 = ( r u i − r ˇ u i ) 2 = ( r u i − Σ k = 1 k p u k σ k q k i ) 2 \mathrm{e}_{\mathrm{ui}^{2}}=\left(\mathrm{r}_{\mathrm{ui}}-\check{\mathrm{r}}_{\mathrm{ui}}\right)^{2}=\left(\mathrm{r}_{\mathrm{ui}}-\Sigma_{\mathrm{k}=1}^{\mathrm{k}} \mathrm{p}_{\mathrm{uk}} \sigma_{\mathrm{k}} \mathrm{q}_{\mathrm{ki}}\right)^{2} eui2=(rui−rˇui)2=(rui−Σk=1kpukσkqki)2
这里,
- eui是错误
- rui是用户u对电影i的实际评分
- řui是用户u对电影i的预测评分
我们的目标是确定p和q的值,使得误差最小化。我们需要更新p和q值,以便获得这些矩阵的优化值,从而使误差最小。现在,我们将为puk和qki定义一个更新规则。梯度下降中的更新规则由要最小化的误差的梯度定义。
∂
∂
p
u
k
(
e
u
i
2
)
=
−
2
(
r
u
i
−
r
ˇ
u
i
)
q
k
i
=
−
2
e
u
i
q
k
i
\frac{\partial}{\partial p u k}\left(e_{u i}^{2}\right)=-2\left(r_{u i}-\check{r}_{u i}\right) q_{k i}=-2 e_{u i} q_{k i}
∂puk∂(eui2)=−2(rui−rˇui)qki=−2euiqki
∂ ∂ q k i ( e u i 2 ) = − 2 ( r u i − r ~ u i ) p u k = − 2 e u i p u k \frac{\partial}{\partial q k i}\left(e_{u i}^{2}\right)=-2\left(r_{u i}-\tilde{r}_{u i}\right) p_{u k}=-2 e_{u i} p_{u k} ∂qki∂(eui2)=−2(rui−r~ui)puk=−2euipuk
由于现在我们有了梯度,我们可以为puk和qki应用更新规则。
p
u
k
′
=
p
u
k
−
α
∗
∂
∂
p
u
k
(
e
u
l
2
)
=
p
u
k
+
2
e
u
i
q
k
\mathrm{p}_{\mathrm{uk}}^{\prime}=\mathrm{p}_{\mathrm{uk}}-\alpha^{*} \frac{\partial}{\partial p u k}\left(\mathrm{e}_{u \mathrm{l}}^{2}\right)=\mathrm{p}_{\mathrm{uk}}+2 \mathrm{e}_{\mathrm{ui}} \mathrm{q}_{\mathrm{k}}
puk′=puk−α∗∂puk∂(eul2)=puk+2euiqk
q k j ′ = q k i − α ∗ ∂ ∂ q k i ( e u i 2 ) = q k i + 2 e u i p u k q_{k j}^{\prime}=q_{k i}-\alpha^{*} \frac{\partial}{\partial q k i}\left(e_{u i}^{2}\right)=q_{k i}+2 e_{u i} p_{u k} qkj′=qki−α∗∂qki∂(eui2)=qki+2euipuk
α是学习速率决定每个更新的大小。可以重复上述的更新,直到将错误最小化为止。完成之后,我们将获得可用于预测收视率的最佳P和Q矩阵。让我们快速回顾一下该算法的工作原理,然后我们将构建推荐引擎来预测未评分电影的评分。
以下是矩阵分解如何用于预测收视率的方法:
# for f = 1,2,....,k :
# for rui ε R :
# predict rui
# update puk and qki
因此,基于每个潜在特征,将使用预测的rui值填充R矩阵中所有缺失的评分。然后使用梯度下降对puk和qki进行更新,并获得它们的最佳值。可以如下显示:
现在,我们已经了解了该算法的内部工作原理,我们将举一个例子,看看如何将矩阵分解为它的组成部分。
考虑一个2 X 3矩阵A 2X3,如下所示:
在这里,我们有2个用户和3个电影的相应评分。现在,我们将把这个矩阵分解为子部分,像这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nO23UsOj-1589942971148)(.\图片\14.png)]
AAT的特征值将给我们P矩阵,而ATA的特征值将给我们Q矩阵。Σ是AAT或ATA的特征值的平方根。
计算AAT的特征值。
det
(
A
A
⊤
−
λ
l
)
=
0
λ
2
−
34
λ
+
225
=
(
λ
−
25
)
(
λ
−
9
)
=
0
\begin{aligned} &\operatorname{det}\left(A A^{\top}-\lambda l\right)=0\\ &\lambda^{2}-34 \lambda+225=(\lambda-25)(\lambda-9)=0 \end{aligned}
det(AA⊤−λl)=0λ2−34λ+225=(λ−25)(λ−9)=0
因此,AAT的特征值是25、9。类似地,我们可以计算ATA的特征值。这些值将分别为25、9、0。现在,我们必须为AAT和ATA计算相应的特征向量。
对于λ= 25,我们有:
可以将其简化为:
该矩阵中的单位长度向量为:
同样,λ= 9我们有:
我们可以将行简化为
该矩阵中的单位长度向量为:
该矩阵的内核中的单位长度向量为:
同样,对于λ= 9,我们有:
可以将其减少为:
该矩阵的内核中的单位长度向量为:
对于最后一个特征向量,我们可以找到一个垂直于q1和q2的单位向量。所以,
Σ 2X3矩阵是矩阵特征值的平方根AAT或 ATA,即,25和9。
最后,我们可以通过公式σpi= Aqi或pi = 1 /σ(Aqi)来计算P 2X2。给出:
因此,A矩阵的分解形式为:
由于我们拥有P和Q矩阵,因此我们可以使用梯度下降方法来获得它们的优化版本。让我们使用矩阵分解来构建推荐引擎。
7.使用矩阵分解构建推荐引擎
我们定义一个函数来预测用户对所有未由他/她评分的电影的评分。
class MF():
# 初始化用户电影评级矩阵, no.潜在的特征,α和β。
def __init__(self, R, K, alpha, beta, iterations):
self.R = R
self.num_users, self.num_items = R.shape
self.K = K
self.alpha = alpha
self.beta = beta
self.iterations = iterations
#初始化用户特征和电影矩阵
def train(self):
self.P = np.random.normal(scale=1./self.K, size=(self.num_users, self.K))
self.Q = np.random.normal(scale=1./self.K, size=(self.num_items, self.K))
#初始化偏差项
self.b_u = np.zeros(self.num_users)
self.b_i = np.zeros(self.num_items)
self.b = np.mean(self.R[np.where(self.R != 0)])
#训练样本名单
self.samples = [
(i, j, self.R[i, j])
for i in range(self.num_users)
for j in range(self.num_items)
if self.R[i, j] > 0
]
#给定迭代次数的随机梯度下降
training_process = []
for i in range(self.iterations):
np.random.shuffle(self.samples)
self.sgd()
mse = self.mse()
training_process.append((i, mse))
if (i+1) % 20 == 0:
print("Iteration: %d ; error = %.4f" % (i+1, mse))
return training_process
# 计算总平均平方误差
def mse(self):
xs, ys = self.R.nonzero()
predicted = self.full_matrix()
error = 0
for x, y in zip(xs, ys):
error += pow(self.R[x, y] - predicted[x, y], 2)
return np.sqrt(error)
# 随机梯度下降得到优化的P和Q矩阵
def sgd(self):
for i, j, r in self.samples:
prediction = self.get_rating(i, j)
e = (r - prediction)
self.b_u[i] += self.alpha * (e - self.beta * self.b_u[i])
self.b_i[j] += self.alpha * (e - self.beta * self.b_i[j])
self.P[i, :] += self.alpha * (e * self.Q[j, :] - self.beta * self.P[i,:])
self.Q[j, :] += self.alpha * (e * self.P[i, :] - self.beta * self.Q[j,:])
# 用户 i 和 电影 j的评分
def get_rating(self, i, j):
prediction = self.b + self.b_u[i] + self.b_i[j] + self.P[i, :].dot(self.Q[j, :].T)
return prediction
# 完整的用户电影评分矩阵
def full_matrix(self):
return mf.b + mf.b_u[:,np.newaxis] + mf.b_i[np.newaxis:,] + mf.P.dot(mf.Q.T)
现在我们有了一个可以预测评分的函数。此函数的输入是:
- R –用户电影评分矩阵
- K –潜在特征数
- alpha –随机梯度下降的学习率
- beta –偏差的正则化参数
- iterations–执行随机梯度下降的迭代次数
我们必须将用户项目评分转换为矩阵形式。可以使用python中的Pivot函数来完成。
R= np.array(ratings.pivot(index = 'user_id', columns ='movie_id', values = 'rating').fillna(0))
fillna(0)将使用0填充所有缺失的评分。现在我们有了R矩阵。我们可以初始化潜在特征的数量,但是这些特征的数量必须小于或等于原始特征的数量。
现在让我们预测所有缺失的评分。让我们假设K = 20,alpha = 0.001,beta = 0.01和iterations = 100。
mf = MF(R, K=20, alpha=0.001, beta=0.01, iterations=100)
training_process = mf.train()
print()
print("P x Q:")
print(mf.full_matrix())
print()
这将为我们提供与每20次迭代相对应的误差值,最后是得到完整的用户电影评分矩阵。输出看起来像这样:
我们已经创建了推荐引擎。在下一节中,我们将重点介绍如何评估推荐引擎。
8.推荐引擎的评估指标
为了评估推荐引擎,我们可以使用以下指标
8.1召回(Recall)
- 用户喜欢的商品中有多少是被推荐的
- 它由下式给出:
Recall = t p t p + f n \text { Recall }=\frac{t p}{t p+f n} Recall =tp+fntp
-
- 这里tp代表推荐给用户他/她的喜欢的数量,tp + fn代表用户喜欢的数量总和
- 如果用户喜欢5个项目,推荐引擎决定推荐3个项目,则召回率为0.6
- 召回次数越大,建议就越好
8.2精度(Precision):
-
在所有推荐项目中,用户实际喜欢的有多少?
-
它由下式给出:
Precision = t p t p + f p \text { Precision }=\frac{t p}{t p+f p} Precision =tp+fptp -
- 这里tp代表推荐给用户他/她喜欢的项目数量,tp + fp代表推荐给用户的项目总数
- 如果向用户推荐了5个项目,而他最喜欢4个项目,则精度为0.8
- 精度越高,建议越好
- 但是请考虑一下这种情况:如果我们仅简单的推荐所有项目,它们肯定会涵盖用户喜欢的项目。因此,我们有100%的召回率!但是请考虑一下精度。如果我们建议说1000个项目,而用户只喜欢其中的10个项目,则精度为0.1%。这真的很低。因此,我们的目标应该是最大限度地提高准确性和召回率。
8.3 均方根误差(RMSE):
-
它测量预测分数中的误差:
R M S E = ∑ i = 1 N ( Predicted i − Actual i ) 2 N R M S E=\sqrt{\frac{\sum_{i=1}^{N}\left(\text {Predicted}_{i}-\text {Actual}_{i}\right)^{2}}{N}} RMSE=N∑i=1N(Predictedi−Actuali)2 -
- 在这里,“Predicted”是模型预测的评分,而“Actual”是原始评分
- 如果用户给电影评分为5,而我们预测的评分为4,则RMSE为1
- RMSE值越小,建议越好
上面的指标告诉我们我们的建议有多准确,但是它们不关注建议的顺序,即它们不关注首先推荐的产品以及之后推荐的产品。我们需要一些指标,也应考虑推荐产品的顺序。因此,让我们看一些排名指标:
8.4平均倒数排名(Mean Reciprocal Rank):
-
评估建议列表
M R R = 1 n ⋅ ∑ i = 1 n 1 r ( Q i ) \mathrm{MRR}=\frac{1}{\mathrm{n}} \cdot \sum_{i=1}^{\mathrm{n}} \frac{1}{\mathrm{r}\left(\mathrm{Q}_{i}\right)} MRR=n1⋅i=1∑nr(Qi)1 -
- 假设我们向用户推荐了3部电影,按照给定的顺序为A,B,C,但用户只喜欢电影C。由于电影C的顺序为3,那么顺序的倒数为1/3
- 平均倒数排名越大,建议越好
8.5 K点映射:截止k点平均精度(MAP at k )
-
Precision和Recall并不在乎建议中的顺序
-
截止点k的精度是仅考虑从等级1到k级建议的子集所计算得出的精度
M A P i = 1 ∣ R i ∣ ∑ k = 1 ∣ R i ∣ P ( R i [ k ] ) M A P_{i}=\frac{1}{\left|R_{i}\right|} \sum_{k=1}^{\left|R_{i}\right|} P\left(R_{i}[k]\right) MAPi=∣Ri∣1k=1∑∣Ri∣P(Ri[k]) -
- 假设我们提出了三个建议[0,1,1]。此处0表示推荐不正确,而1表示推荐正确。那么在k处的精度将为[0,1/2,2/3],平均精度将为(1/3)*(0 + 1/2 + 2/3)= 0.38
- 所以平均精度越大,建议的精度就越高
8.6归一化折现累积增益(NDCG)
- MAP和NDCG之间的主要区别在于MAP假设某个项目是你感兴趣的(或不感兴趣的),而NDCG则给出了相关性得分
- 让我们以一个例子来理解它:假设从A到J的10部电影中,我们可以推荐前五部电影,即A,B,C,D和E,而我们不能推荐其他五部电影,即F, G,H,I和J。推荐为[A,B,C,D]。因此,在这种情况下,NDCG将为1,因为推荐产品与用户相关
- NDCG值越高,建议越好
9.我们还有什么可以尝试的?
到目前为止,我们已经了解了什么是推荐引擎,它的不同类型及其工作方式。基于内容的过滤和基于物品的过滤算法都有其优点和缺点。
在某些领域,生成有用的内容描述可能非常困难。如果用户的先前行为没有为此提供相关证据,则基于内容的过滤模型将不会选择项目。必须使用其他技术,以便系统可以在用户已经表现出兴趣的范围之外提出建议。
基于物品的过滤模型没有这些缺点。因为不需要描述所建议的项目,所以系统可以处理任何类型的信息。此外,它可以推荐用户以前不感兴趣的产品。但是,如果没有基于用户评分的预测,则基于物品的过滤无法为新项目提供建议。即使用户开始对该商品进行评分,该商品也需要花费一些时间才能获得足够的评分,以便做出准确的推荐。
结合了基于内容的过滤和基于物品的过滤的系统可能会从内容的表示形式以及用户之间的相似性中受益。结合基于物品的协作过滤和基于内容的过滤的一种方法是基于基于内容的推荐和基于物品的推荐的加权平均值进行预测。这样做的各种方法是:
-
合并项目分数
- 在这种方法中,我们结合了从两种过滤方法获得的评级。最简单的方法是取评分的平均值
- 假设一种方法建议电影的评分为4,而另一种方法建议同一电影的评分为5。因此最终的建议将是娶两个分数的平均值,即4.5
- 我们也可以为不同的方法分配不同的权重
-
组合物品等级:
- 假设基于物品的协同过滤推荐了5部电影A,B,C,D和E按以下顺序进行推荐:A,B,C,D,E,而基于内容的过滤按以下顺序推荐了它们:B,D,A,C,E
- 那么电影的推荐排名为:
基于物品的协同过滤
电影 | 排名 |
---|---|
A | 1 |
B | 0.8 |
C | 0.6 |
D | 0.4 |
E | 0.2 |
基于内容的过滤:
电影 | 排名 |
---|---|
B | 1 |
D | 0.8 |
A | 0.6 |
C | 0.4 |
E | 0.2 |
因此,混合推荐引擎将合并这些排名,并根据合并后的排名做出最终推荐。合并排名为:
电影 | 新排名 |
---|---|
A | 1 + 0.6 = 1.6 |
B | 0.8 + 1 = 1.8 |
C | 0.6 + 0.4 = 1 |
D | 0.4 + 0.8 = 1.2 |
E | 0.2 + 0.2 = 0.4 |
推荐将根据这些排名进行。因此,最终建议将如下所示:B,A,D,C,E。
通过这种方式,可以将两种或更多种技术组合起来以构建混合推荐引擎并提高它们的总体推荐准确性和能力。
结束语
这是一篇有关推荐引擎的非常全面的文章。本教程应该足够好,可以帮助你开始学习推荐引擎这个主题。我们不仅介绍了基本的推荐技术,还介绍了如何实施当今行业中可用的一些更先进的技术。
我们还介绍了与每种技术相关的一些关键事实。作为想学习如何创建推荐引擎的人,我建议你学习本教程中讨论的技术,然后在模型中实现它们。
原文链接:https://www.analyticsvidhya.com/blog/2018/06/comprehensive-guide-recommendation-engine-python/?utm_source=blog&utm_medium=comprehensive-guide-k-means-cluster