【BI学习心得04-ALS算法与推荐系统】


1.写在前面的话

根据之前的学习,我们知道推荐系统是一个信息过滤工具,筛选信息,给用户提供合适的信息,这个信息可能会是你想看的悬疑电影,喜欢刷的科普短视频,喜欢听的轻音乐。经常会看到,一部电影的口碑好的电影,评分都很高,这是因为观影人钟情这部电影,认为这次的观影体验好。在推荐系统中对电影进行打分,根据这个评分,我们能知道这些用户除了自己评过分之外的电影可能还偏好哪些电影?把解决这类问题的算法被统称为推荐算法。

2.算法原理

2.1协同过滤

协同过滤简单来说是利用某兴趣相投、拥有共同经验之群体的喜好来推荐用户感兴趣的信息,个人通过合作的机制给予信息相当程度的回应(如评分)并记录下来以达到过滤的目的进而帮助别人筛选信息,回应不一定局限于特别感兴趣的,特别不感兴趣信息的纪录也相当重要。一般来说协同过滤分为4种类型:

(1)基于内容的推荐:这一类一般依赖于自然语言处理NLP的一些知识,通过挖掘文本的TF-IDF特征向量,来得到用户的偏好,进而做推荐。这类推荐算法可以找到用户独特的小众喜好,而且还有较好的解释性。这一类由于需要NLP的基础,在后面专门讲NLP的时候再讨论。

(2)协调过滤推荐:协调过滤是推荐算法中目前最主流的种类,花样繁多,在工业界已经有了很多广泛的应用。它的优点是不需要太多特定领域的知识,可以通过基于统计的机器学习算法来得到较好的推荐效果。最大的优点是工程上容易实现,可以方便应用到产品中。目前绝大多数实际应用的推荐算法都是协同过滤推荐算法。

(3)基于领域的推荐:基于领域的推荐包括UserCF, ItemCF,将用户的所有数据读入到内存中进行运算,也称之为基于内存的协同过滤(Memory-based)。类似我们机器学习中的集成学习,博才众长,通过多个推荐算法的结合,得到一个更好的推荐算法,起到三个臭皮匠顶一个诸葛亮的作用。比如通过建立多个推荐算法的模型,最后用投票法决定最终的推荐结果。

(4)基于模型的推荐(Model-based):假设有m个物品,m个用户的数据,只有部分用户和部分数据之间是有评分数据的,其它部分评分是空白,此时我们要用已有的部分稀疏数据来预测那些空白的物品和数据之间的评分关系,找到最高评分的物品推荐给用户。

对于这个问题,用机器学习的思想来建模解决,主流的方法可以分为:用关联算法聚类算法,分类算法,回归算法,矩阵分解,神经网络,图模型以及隐语义模型来解决。前面几节课讲过了关联算法,聚类算法,今天重点讲矩阵分解隐语义模型ALS

在这里插入图片描述

推荐系统依赖不同类型的输入数据,最方便的是高质量的显式反馈数据,它们包含用户对感兴趣商品明确的评价。例如,Netflix收集的用户对电影评价的星星等级数据。但是显式反馈数据不一定总是找得到,因此推荐系统可以从更丰富的隐式反馈信息中推测用户的偏好。 隐式反馈类型包括购买历史、浏览历史、搜索模式甚至鼠标动作。例如,购买同一个作者许多书的用户可能喜欢这个作者。

许多研究都集中在处理显式反馈,然而在很多应用场景下,应用程序重点关注隐式反馈数据。因为可能用户不愿意评价商品或者由于系统限制我们不能收集显式反馈数据。在隐式模型中,一旦用户允许收集可用的数据,在客户端并不需要额外的显式数据。文献中的系统避免主动地向用户收集显式反馈信息,所以系统仅仅依靠隐式信息。

了解隐式反馈的特点非常重要,因为这些特质使我们避免了直接调用基于显式反馈的算法。最主要的特点有如下几种:

(1) 没有负反馈。通过观察用户行为,我们可以推测那个商品他可能喜欢,然后购买,但是我们很难推测哪个商品用户不喜欢。这在显式反馈算法中并不存在,因为用户明确告诉了我们哪些他喜欢哪些他不喜欢。

(2) 隐式反馈是内在的噪音。虽然我们拼命的追踪用户行为,但是我们仅仅只是猜测他们的偏好和真实动机。例如,我们可能知道一个人的购买行为,但是这并不能完全说明偏好和动机,因为这个商品可能作为礼物被购买而用户并不喜欢它。

(3) 显示反馈的数值值表示偏好preference,隐式回馈的数值值表示信任confidence。基于显示反馈的系统用星星等级让用户表达他们的喜好程度,例如一颗星表示很不喜欢,五颗星表示非常喜欢。基于隐式反馈的数值值描述的是动作的频率,例如用户购买特定商品的次数。一个较大的值并不能表明更多的偏爱。但是这个值是有用的,它描述了在一个特定观察中的信任度。 一个发生一次的事件可能对用户偏爱没有用,但是一个周期性事件更可能反映一个用户的选择。

(4) 评价隐式反馈推荐系统需要合适的手段。

2.2显式反馈模型

潜在因素模型由一个针对协同过滤的交替方法组成,它以一个更加全面的方式发现潜在特征来解释观察的ratings数据。我们关注的模型由奇异值分解SVD推演而来。一个典型的模型将每个用户u(包含一个用户-因素向量ui)和每个商品v(包含一个商品-因素向量vj)联系起来。 预测通过内积 r i j = u i T v j r_{ij}=u^T_iv_j rij=uiTvj来实现。另一个需要关注的地方是参数估计。许多当前的工作都应用到了显式反馈数据集中,这些模型仅仅基于观察到的rating数据直接建模,同时通过一个适当的正则化来避免过拟合。公式(2.1)如下:
m i n u , v ∑ r i j ( r i j − u i T v j ) 2 + λ ∣ ∣ u i ∣ ∣ 2 + ∣ ∣ v j ∣ ∣ 2 min_{u,v}\sum_{r_{ij}}(r_{ij}-u^T_iv_j)^2+\lambda||u_i||^2+||v_j||^2 minu,vrij(rijuiTvj)2+λui2+vj2

在公式(2.1)中,lambda是正则化的参数。正规化是为了防止过拟合的情况发生,具体参见文献[^1]。这样,我们用最小化重构误差来解决协同推荐问题。我们也成功将推荐问题转换为了最优化问题。

2.3 隐式反馈模型

在显式反馈的基础上,我们需要做一些改动得到我们的隐式反馈模型。首先,我们需要形式化由 r i j r_{ij} rij变量衡量的信任度的概念。我们引入了一组二元变量 p i j p_{ij} pij,它表示用户u对商品v的偏好。 p i j p_{ij} pij的公式如下:
f ( x ) = { 1 r i j > 0 0 r i j = 0 f(x)=\left\{ \begin{aligned} 1 && r_{ij}>0 \\ 0 && r_{ij}=0\\ \end{aligned} \right. f(x)={10rij>0rij=0

换句话说,如果用户购买了商品,我们认为用户喜欢该商品,否则我们认为用户不喜欢该商品。然而我们的信念beliefs与变化的信任confidence等级息息相关。首先,很自然的, p i j p_{ij} pij的值为0和低信任有关。用户对一个商品没有得到一个正的偏好可能源于多方面的原因,并不一定是不喜欢该商品。例如,用户可能并不知道该商品的存在。 另外,用户购买一个商品也并不一定是用户喜欢它。因此我们需要一个新的信任等级来显示用户偏爱某个商品。一般情况下, r i j r_{ij} rij越大,越能暗示用户喜欢某个商品。因此,我们引入了一组变量 c i j c_{ij} cij,它衡量了我们观察到pij的信任度。 c i j c_{ij} cij一个合理的选择如下所示:
c i j = 1 + α r i j c_{ij}=1+\alpha r_{ij} cij=1+αrij

按照这种方式,我们存在最小限度的信任度,并且随着我们观察到的正偏向的证据越来越多,信任度也会越来越大。

我们的目的是找到用户向量ui以及商品向量vj来表明用户偏好。这些向量分别是用户因素(特征)向量和商品因素(特征)向量。本质上,这些向量将用户和商品映射到一个公用的隐式因素空间,从而使它们可以直接比较。这和用于显式数据集的矩阵分解技术类似,但是包含两点不一样的地方:
(1)我们需要考虑不同的信任度,
(2)最优化需要考虑所有可能的u,v对,而不仅仅是和观察数据相关的u,v对。显性反馈的矩阵分解优化时,对于missing data(没有评分),是不会当做训练数据输入到模型的,优化时针对已知评分数据优化。而这里隐性反馈,是利用所有可能的u,i键值对,所以总的数据是m*n,其中m是用户数量,n是物品数量。这里没有所谓的missing data,因为假如u对i没有任何动作,我们就认为偏好值为0,只不过置信度较低而已。因此,通过最小化下面的损失函数来计算相关因素factors
m i n u , v ∑ i , j c i j ( p i j − u i T v j ) 2 + λ ( ∑ i ∣ u i ∣ 2 + ∑ j ∣ v j ∣ 2 ) min_{u,v}\sum_{i,j}c_{ij}(p_{ij}-u_{i}^{T}v_{j})^{2} + \lambda (\sum_{i}\left | u_{i} \right |^{2} + \sum_{j}\left |v_{j} \right |^{2}) minu,vi,jcij(pijuiTvj)2+λ(iui2+jvj2)

3.隐语义模型(LFM)

该算法最早在文本领域被提出,用于找到文本的隐含语义。核心思想是通过隐含特征(latent factor)联系用户兴趣和物品(item)。是基于机器学习的方法。找出潜在的主题和分类。基于用户的行为对item进行自动聚类,划分到不同类别/主题,即用户的兴趣。比如,用户平时喜欢看新闻,那用户的感兴趣是那个话题?
在这里插入图片描述

  1. 和ItemCF在物品分类方面的思想类似,若物品同时被很多用户同时喜欢,则这两个物品很可能属于同一类。

  2. 我们可以指定隐特征的个数(K)。数字越大,分类粒度越细。

  3. 计算出物品属于每个类的权重(模糊聚类),因此每个物品都不是硬性地被分到某一个类中。

  4. 给出的每一个分类都不是同一个维度的,是基于用户的共同兴趣计算出来的,若用户的共同兴趣是某一个维度,则LFM给出的类也是相同的维度。

  5. 可通过统计用户行为决定物品在每个类中的权重。若喜欢某个类的用户都会喜欢某个物品,则这个物品在该类中的权重就可能较高。

3.1基于模型与基于邻域的推荐之间的区别

  • 基于邻域的协同过滤包括UserCF, ItemCF,将用户的所有数据读入到内存中进行运算,也称之为基于内存的协同过滤(Memory-based)。数据量少的情况下,可以在线实时推荐

  • 基于模型的推荐(Model-based),采用机器学习的方式,分成训练集和测试集。离线训练时间比较长,但训练完成后,推荐过程比较快。

4.矩阵分解

4.1矩阵分解的含义

在推荐系统中,具体的应用场景中有以下两个方面:

  • 评分预测(Rating Prediction)
    主要用于评价网站,比如用户给自己看过的电影评多少分(MovieLens),或者用户给自己看过的书籍评价多少分(Douban)。矩阵分解技术主要应用于评分预测问题。

  • Top-N推荐(Item Ranking)
    常用于购物网站,拿不到显式评分,通过用户的隐式反馈为用户提供一个可能感兴趣的Item列表。排序任务,需要排序模型进行建模。

在这里插入图片描述
为用户找到其感兴趣的item推荐给他,用矩阵表示收集到的用户行为数据,12个用户,9部电影。用聚类方法把用户电影数据分为3类, 动 作 、 军 事 类 ( A ) \color{yellow}动作、军事类(A) A 动 画 片 ( B ) \color{green}动画片(B) B 爱 情 片 ( C ) \color{blue}爱情片(C) C,表中的1代表用户打过分。

对于不同的用户来说,他们可能有不同的兴趣。就拿豆瓣电影单的例子来说,A用户会关注红海行动,战狼2,湄公河行动2方面的电影,B用户喜欢汪汪队立大功,小猪佩奇,超级飞侠方面的电影,C用户喜欢小时代4,致青春,同桌的你方面的电影。那我们在推荐的时候,肯定是向用户推荐他感兴趣的类别下的电影。那么前提是我们要对所有item(电影)进行分类。那如何分呢?大家注意到没有,分类标准这个东西是因人而异的,每个用户的想法都不一样。拿B用户来说,他喜欢的3个类别可以是动作片,动画片,爱情片方面的电影,也就是说A用户的分类粒度要比B用户小;用户可能不止喜欢上面三种的,也有可能喜欢其他类型的电影,有些item不能简单的将其划归到确定的单一类别;拿C用户来说,他倾向的是爱情片的电影,只看某几个特定类型的电影,那么跟A,B相比它的分类角度就完全不同了。

我们可以看到这张表有很多的空缺,用户有很多电影没有看过,但是我们也不知道他(她)是否喜欢看。矩阵分解要做的是预测出矩阵中缺失的评分,使得预测评分能反映用户的喜欢程度。将评分矩阵分解为两个小矩阵,评分矩阵(12x9)= User矩阵(12x3)*Item矩阵(3x9),隐含特征数(k)=3。
在这里插入图片描述
如果现在我们要给用户推荐其他电影是要经过哪些步骤?

第一步算出User矩阵和Item矩阵里面的电影评分数据
在这里插入图片描述
第二步合并User矩阵和Item矩阵里面的电影评分数据
在这里插入图片描述
从上面的计算结果来看,如果现在想要给user1用户推荐电影2部电影,我们是优先推荐湄公河行动2,其次是汪汪队立大功,湄公河行动2这部电影虽然被聚类分到一个类别,但是用户没有评分,没有看过,分配的权重会比其他电影大。MF用user向量和item向量的内积去拟合评分矩阵中该user对该item的评分,内积的大小反映了user对item的喜欢程度。内积大匹配度高,内积小匹配度低。隐含特征个数k,k越大,隐类别分得越细,计算量越大。

4.2矩阵分解的目标函数

交替最小二乘法(ALS)算法将一个给定的 R R R矩阵因式分解为 X X X Y Y Y两个因子,例如 R ≈ X T Y R≈X^TY RXTY。 未知的行的维度被用作算法的参数,叫做潜在因子。 由于矩阵因式分解可以用在推荐系统的场景, X X X Y Y Y矩阵可以分别称为用户和商品矩阵。 用户矩阵的第i列用 x u x_u xu表示,商品矩阵的第i列用 y i y_i yi表示。 R R R矩阵称为评价矩阵可以用 ( R ) i , j = r i j (R)_{i,j}=r_{ij} (R)i,j=rij表示。 为了找到用户和商品矩阵,如下问题得到了解决:
在这里插入图片描述

r u i r_{ui} rui表示用户u 对item i 的评分,当 >0时,表示有评分,当=0时,表示没有评分,
x u T ∗ y i x^T_u*y_i xuTyi表示用户向量与物品向量的内积,表示用户u 对物品i 的预测评分
λ \lambda λ表示L2正则化,使用正则项避免过拟合
X = [ x 1 , x 2 , . . . , x N ] X=[x_1,x_2,...,x_N] X=[x1,x2,...,xN]表示用户矩阵X,用户数为N
Y = [ y 1 , y 2 , . . . , y M ] Y=[y_1,y_2,...,y_M] Y=[y1,y2,...,yM]表示商品矩阵Y,商品数为M

通过修复 U U U V V V矩阵,我们获得可以直接解析的二次形式。 问题的解决办法是保证总消耗函数的单调递减。通过对 X X X Y Y Y矩阵的这一步操作,我们逐步的改进了矩阵的因式分解。

4.3ALS算法

本文要介绍的算法ALS是基于矩阵分解,所谓矩阵分解就是将矩阵拆解为多个矩阵的乘积。如果想通过矩阵分解的方法实现基于模型的协同过滤,ALS算法是一个不错的选择,ALS的全称为Alternating Least Square,翻译过来为交替最小二乘法。这里假设用户为a,物品为b,评分矩阵为R(m, n)可以分解为用户矩阵U(k,m)和物品矩阵I(k,n),其中m,n,k代表矩阵的维度。

4.3.1求解ALS

Step1,固定Y 优化X
在这里插入图片描述

将目标函数转化为矩阵表达形式
在这里插入图片描述
目标函数相关参数说明
用户u 对m个物品的评分

R u R_u Ru用户u 对m个物品的评分

在这里插入图片描述

Y u Y_u Yu表示m 个物品的向量

对目标函数J关于 x u x_u xu求梯度,并令梯度为零,得
在这里插入图片描述
求解后可得:
在这里插入图片描述

Step2,固定X 优化Y

同理可得:
y i = ( X i X i T + λ I ) − 1 X i R i y_i=(X_iX^T_{i}+\lambda I)^{-1}X_iR_i yi=(XiXiT+λI)1XiRi

重复Step1和2,直到X 和Y收敛。每次固定一个矩阵,优化另一个矩阵,都是最小二乘问题

4.3.2简化版求解ALS

  • 根据矩阵分解的定义,有

R = U T ∗ I R=U^T*I R=UTI

  • 用MSE作为损失函数,为了方便化简,加法符号左侧的常数改为 − 1 2 -\frac{1}{2} 21

− 1 2 ∑ i = 0 T ( R a i − U a T ∗ I i ) 2 -\frac{1}{2}\sum^T_{i=0}(R_{ai}-U^T_a*I_i)^2 21i=0T(RaiUaTIi)2

  • 对损失函数求 U a U_a Ua的一阶偏导数,那么

d L d U a = ( R a − U a T ∗ I ) I T \frac{dL}{dU_a}=(R_a-U^T_a*I)I^T dUadL=(RaUaTI)IT
d L d U a = I ∗ ( R a T − I T ∗ U a ) \frac{dL}{dU_a}=I*(R^T_a-I^T*U_a) dUadL=I(RaTITUa)

  • 令一阶偏导数等于0

I ∗ R a T = I ∗ I T ∗ U a I*R^T_a=I*I^T*U_a IRaT=IITUa
U a = ( I ∗ I T ) − 1 ∗ I ∗ R a T . . . ( 1 ) U_a=(I*I^T)^{-1}*I*R^T_{a}...(1) Ua=(IIT)1IRaT...(1)

  • 同理,可求

U b = ( U ∗ U T ) − 1 ∗ U ∗ R b T . . . ( 2 ) U_b=(U*U^T)^{-1}*U*R^T_{b}...(2) Ub=(UUT)1URbT...(2)

5.SGD算法

随机梯度(SGD)下降基本思路是以随机方式遍历训练集中的数据,并给出每个已知评分的预测评分。用户和物品特征向量的调整就沿着评分误差越来越小的方向迭代进行,直到误差达到要求。所以,SGD不需要遍历所有的样本即可完成特征向量的求解。

大多数机器学习或者深度学习算法都涉及某种形式的优化。 优化指的是改变 x x x以最小化或最大化某个函数 f ( x ) f(x) f(x)的任务。 我们通常以最小化 f ( x ) f(x) f(x)指代大多数最优化问题。 最大化可经由最小化算法最小化 − f ( x ) -f(x) f(x)来实现。

我们把要最小化或最大化的函数称为目标函数或准则。 当我们对其进行最小化时,我们也把它称为代价函数、损失函数或误差函数

下面,我们假设一个损失函数为在这里插入图片描述
,其中
在这里插入图片描述
然后要使得最小化它。

其中 θ \theta θ参数,代表权重,n为特征数
用它来模拟y,需要将损失函数最小化

我们知道曲面上方向导数的最大值的方向就代表了梯度的方向,因此我们在做梯度下降的时候,应该是沿着梯度的反方向进行权重的更新,可以有效的找到全局的最优解。这个 θ i \theta_i θi的更新过程可以描述为
在这里插入图片描述
[a表示的是步长或者说是学习率(learning rate)]

好了,怎么理解?在直观上,我们可以这样理解,看下图,一开始的时候我们随机站在一个点,把他看成一座山,每一步,我们都以下降最多的路线来下山,那么,在这个过程中我们到达山底(最优点)是最快的,而上面的a,它决定了我们“向下山走”时每一步的大小,过小的话收敛太慢,过大的话可能错过最小值,扯到蛋…)。这是一种很自然的算法,每一步总是寻找使J下降最“陡”的方向(就像找最快下山的路一样)。
在这里插入图片描述
当然了,我们直观上理解了之后,接下来肯定是从数学的角度,我们可以这样想,先想在低维的时候,比如二维,我们要找到最小值,其实可以是这样的方法,具体化到1元函数中时,梯度方向首先是沿着曲线的切线的,然后取切线向上增长的方向为梯度方向,2元或者多元函数中,梯度向量为函数值f对每个变量的导数,该向量的方向就是梯度的方向,当然向量的大小也就是梯度的大小。现在假设我们要求函数的最值,采用梯度下降法,结合如图所示:

在这里插入图片描述
如图所示,我们假设函数是 y = x 2 + 1 y=x^2+1 y=x2+1,那么如何使得这个函数达到最小值呢,简单的理解,就是对x求导,得到 y ‘ = 2 x y^`=2x y=2x,然后用梯度下降的方式,如果初始值是(0的左边)负值,那么这是导数也是负值,用梯度下降的公式,使得x更加的靠近0,如果是正值的时候同理。注意:这里的梯度也就是一元函数的导数,高维的可以直接类推之

然后是优缺点,这里比较对象是批量梯度和mini-batch梯度下降,先看下他们三者:

  • 批量梯度下降:在每次更新时用所有样本,要留意,在梯度下降中,对于 θ i \theta_i θi的更新,所有的样本都有贡献,也就是参与调整 θ \theta θ。其计算得到的是一个标准梯度,对于最优化问题,凸问题,也肯定可以达到一个全局最优。因而理论上来说一次更新的幅度是比较大的。如果样本不多的情况下,当然是这样收敛的速度会更快啦。但是很多时候,样本很多,更新一次要很久,这样的方法就不合适啦。下图是其更新公式
    在这里插入图片描述
  • 随机梯度下降:在每次更新时用1个样本,可以看到多了随机两个字,随机也就是说我们用样本中的一个例子来近似我所有的样本,来调整θ,因而随机梯度下降是会带来一定的问题,因为计算得到的并不是准确的一个梯度,对于最优化问题,凸问题,虽然不是每次迭代得到的损失函数都向着全局最优方向, 但是大的整体的方向是向全局最优解的,最终的结果往往是在全局最优解附近。但是相比于批量梯度,这样的方法更快,更快收敛,虽然不是全局最优,但很多时候是我们可以接受的,所以这个方法用的也比上面的多。下图是其更新公式:

在这里插入图片描述

  • mini-batch梯度下降:在每次更新时用b个样本,其实批量的梯度下降就是一种折中的方法,他用了一些小样本来近似全部的,其本质就是我1个指不定不太准,那我用个30个50个样本那比随机的要准不少了吧,而且批量的话还是非常可以反映样本的一个分布情况的。在深度学习中,这种方法用的是最多的,因为这个方法收敛也不会很慢,收敛的局部最优也是更多的可以接受!

在这里插入图片描述
总的来说,随机梯度下降一般来说效率高,收敛到的路线曲折,但一般得到的解是我们能够接受的,在深度学习中,用的比较多的是mini-batch梯度下降。

最后是收敛性,能收敛吗?收敛到什么地方?

总的来说就是从expected loss用特卡洛(monte carlo)来表示计算,那batch GD, mini-batch GD, SGD都可以看成SGD的范畴。因为大家都是在一个真实的分布中得到的样本,对于分布的拟合都是近似的。那这个时候三种方式的梯度下降就都是可以看成用样本来近似分布的过程,都是可以收敛的!

收敛到什么地方?

最小值,极小值,鞍点。这些都是能收敛到的地方,也就是梯度为0的点。

当然,几乎不存在找到鞍点的可能,除非很碰巧,因为梯度下降是对损失函数每个维度分别求极小值,即分别求 J ( θ ) J(\theta) J(θ)关于 θ 1 , θ 2 , . . . , θ n \theta_1,\theta_2,...,\theta_n θ1θ2...θn极小值。

6.Baseline算法

要评估一个策略的好坏,就需要建立一个对比基线,以便后续观察算法效果的提升。此处我们可以简单地对推荐算法进行建模作为基线。假设我们的训练数据为: <user, item, rating>三元组, 其中user为用户id, item为物品id(item可以是MovieLens上的电影,Amazon上的书, 或是百度关键词工具上的关键词), rating为user对item的投票分数, 其中用户u对物品i的真实投票分数我们记为rui,基线(baseline)模型预估分数为bui,则可建模如下:
在这里插入图片描述

bui 预测值
bu 用户对整体的偏差
bi 商品对整体的偏差

其中mu(希腊字母mu)为所有已知投票数据中投票的均值,bu为用户的打分相对于平均值的偏差(如果某用户比较苛刻,打分都相对偏低, 则bu会为负值;相反,如果某用户经常对很多片都打正分, 则bu为正值); bi为该item被打分时,相对于平均值得偏差,可反映电影受欢迎程度。 bui则为基线模型对用户u给物品i打分的预估值。该模型虽然简单, 但其中其实已经包含了用户个性化和item的个性化信息, 而且特别简单(很多时候, 简单就是一个非常大的特点, 特别是面对大规模数据时)。

基线模型中, mu可以直接统计得到,我们的优化函数可以写为(其实就是最小二乘法):
在这里插入图片描述
也可以直接写成如下式子,因为它本身就是经验似然:
在这里插入图片描述

7.推荐系统工具—Surprise

Surprise(Simple Python Recommendation System Engine)是一款推荐系统库,是scikit系列中的一个。surprise设计时考虑到以下目的:

  • 让用户完美控制他们的实验。为此,特别强调文档,试图通过指出算法的每个细节尽可能清晰和准确。
  • 减轻数据集处理的痛苦。用户可以使用内置数据集(Movielens, Jester)和他们自己的自定义数据集。
  • 提供各种即用型预测算法,例如基线算法,邻域方法,基于矩阵因子分解(SVD,PMF,SVD ++,NMF)等等。此外,内置了各种相似性度量(余弦,MSD,皮尔逊…)。
  • 可以轻松实现新的算法思路。
  • 提供评估,分析和比较算法性能的工具。使用强大的CV迭代器(受scikit-learn优秀工具启发)以及对一组参数的详尽搜索,可以非常轻松地运行交叉验证程序。
    在这里插入图片描述

Surprise的主要特点是简单易用,同时支持多种推荐算法:

  • 基础算法/baseline algorithms
    • NormalPredictor: 根据训练集的分布特征随机给出一个预测值
    • BaselineOnly:给定用户和Item,给出基于baseline的估计值
  • 基于近邻方法(协同过滤)/neighborhood methods
    • KNNBasic:最基础的协同过滤
    • KNNWithMeans:将每个用户评分的均值考虑在内的协同过滤实现
    • KNNBaseline:考虑基线评级的协同过滤
  • 矩阵分解方法/matrix factorization-based
    • SVD:SVD实现
    • SVDpp:SVD++,即LFM+SVD
    • NMF:基于矩阵分解的协同过滤
    • SlopeOne:一个简单但精确的协同过滤算法
    • CoClustering:基于协同聚类的协同过滤算法

其中基于近邻的方法(协同过滤)可以设定不同的相似度度量标准:

  • Cosine:余弦相似度
  • msd:均方差异相似度
  • pearson:Pearson相关系数
  • pearson_baseline:(缩小的)Pearson相关系数,使用基线进行居中而不是平均值;

支持不同的评估准则:

  • RMSE:均方根误差
    在这里插入图片描述

  • MAE:平均绝对误差
    在这里插入图片描述

  • FCP:协调对的分数

7.1Surprise的使用

接下来,在surprise内置的数据集(movielens)上用协同过滤算法来构建一个简单的电影推荐系统。

利用协同过滤算法构建推荐系统,并测试效果

from __future__ import (absolute_import, division, print_function, unicode_literals)
import os
import io
 
from surprise import KNNBaseline
from surprise import Dataset
from evaluate, print_perf
 
# 定义read_item_names()函数,以获取电影名到电影id和电影id到电影名的映射
def read_item_names():
    file_name = (os.path.expanduser('~') + '/.surprise_data/ml-100k/ml-100k/u.item')
    rid_to_name = {}
    name_to_rid = {}
    with io.open(file_name, 'r', encoding='ISO-8859-1') as f:
        for line in f:
            line = line.split('|')
            rid_to_name[line[0]] = line[1]
            name_to_rid[line[1]] = line[0]
 
    return rid_to_name, name_to_rid
 
# 用KNNBaseline算法构建推荐系统,计算相似度
data = Dataset.load_builtin('ml-100k')
trainset = data.build_full_trainset()
sim_options = {'name': 'pearson_baseline', 'user_based': False}
algo = KNNBaseline(sim_options=sim_options)
algo.train(trainset)
 
# 用RMSE、MAE和FCP测试推荐系统效果
perf = evaluate(algo, data, measures=['RMSE', 'MAE', 'FCP'])
# 输出结果
print_perf(perf)

利用构建的电影推荐系统进行推荐

# 获取电影名到电影id 和 电影id到电影名的映射
rid_to_name, name_to_rid = read_item_names()
# 根据Toy Story这部电影来进行推荐
# 找到Toy Story这部电影对应的item id
toy_story_raw_id = name_to_rid['Toy Story (1995)']
toy_story_raw_id
toy_story_inner_id = algo.trainset.to_inner_iid(toy_story_raw_id)
toy_story_inner_id
# 找到相似度最近的10个电影
toy_story_neighbors = algo.get_neighbors(toy_story_inner_id, k=10)
toy_story_neighbors
# 从近邻的id映射回电影名称
toy_story_neighbors = (algo.trainset.to_raw_iid(inner_id)
                       for inner_id in toy_story_neighbors)
toy_story_neighbors = (rid_to_name[rid]
                       for rid in toy_story_neighbors)
 
print()
print('The 10 nearest neighbors of Toy Story are:')
for movie in toy_story_neighbors:
    print(movie)

7.2SlopeOne算法

SlopeOne是一种推荐算法,属于Rating-based collaborative filtering领域,即是预测某个user对于某个给定的item的评分,本质上是一种协同过滤算法。该算法易于实现,可以实时动态更新,响应快,给出的结果也足够好,可适用于冷启动用户,方便部署。Slope one算法可以直译为斜率为一,因据其作者认为,该算法可以近似用f(x)=x+b这个函数式来表示。具体如何,下面分解。

该算法的motivation是基于商品的’popularity differential’,即商品间受欢迎度的差异,念来十分拗口,但想法十分朴素。
在这里插入图片描述
如上图所示,来做个小学数学题,试想有商品ItemI,ItemJ,User A对二者的评分分别是1,1.5,已知User B 对ItemI 的评分是2,试预测UserB 对ItemJ的评分,那么我们可以这么认为,商品间受欢迎的差异从某种程度上是固定的,打个不恰当的比方,所有人都更喜欢美女,而相较而言不那么喜欢长相奇怪的,但不同人间是有差异的,某男A资深直男癌,眼光比较高,对林志玲评分为3,村东头的翠花评分为1,某男B死肥宅,是个女的都可以接受,对村东头的翠花评分都到了3,对林志玲更是疯狂打出了5分的最高分,那么注意了现在,男A和男B的喜好眼光都是不同的,但对于林志玲和翠花的打分的分数的差却是一样的,3-1=5-3,都更喜欢林志玲而不是翠花,喜欢的程度差是一定的,这就是slope one算法的精髓,据这样的思考方式,很容易就能求出UserB 对ItemJ的评分2+(1.5-1)=2.5

理解了上面那个例子就相当于理解了slope one算法的全部,剩下的就是据此朴素想法上的一些修补而产生的一些变体。

接着来比较一下slope one算法跟其它算法,试看看相对优劣。

第一部分(Per user average): 在预测一个对新item的评分时,将用户过去对所有item的评分加以平均,简单粗暴,效果随缘。

第二部分: bias from mean

在这里插入图片描述
P ( u ) i P(u)_i P(u)i是用户u对商品i的评分, S i ( X ) S_i(X) Si(X)是有对i作过评分的用户的集合, V i V_i Vi是用户v对商品i的评分,其余两个上面带一横的分别是用户u,v以往做过的评分的均值,card简单来说就是其内集合的元素项的个数,整体式子不难理解,含义就是bias from mean。

第三部分: adjusted cosine item-based
在这里插入图片描述
在这里插入图片描述
整个式子看上去较为复杂,我们一步一步来看, S i , j ( X ) S_{i,j}(X) Si,j(X)是有对i,j都作过评分的用户的集合,其它符号与上面是一样的含义, s i m i , j sim_{i,j} simi,j整体是计算商品i,j间的相似度,根据这相似度的不同衡量方式可以衍生出不同的算法,然后第二个式子的 P ( u ) i P(u)_i P(u)i,即用户u对商品i的评分便可以根据该用户对其它商品的评分进行参考(根据商品间的相似度作为权重)计算,当然两商品间越相似,用户对两商品的评分也越相似。Alpha,Belta在这里就当做某种校正因子不必深究。

第四部分:the pearson reference scheme
该算法属于memory-based,简单来说就是很耗内存,lazy learning,事先不做或很少做准备,等开始使用的时候才从内存(已经存了大量数据)中提出一堆数据开始计算并返回结果。

在这里插入图片描述
式子又比较复杂,慢慢来看,Gamma(u,v)其实就是用户u,v间的相似度,所以这其实就是在上面bias from mean算法的基础上引入用户间的相似度作为权重而已

第五部分:the slope one

终于到了我们的主角出场,
在这里插入图片描述
在这里插入图片描述
呃,跟我们上面的朴素想法不同,这式子看着也挺复杂的,慢慢来看, d e v j , i dev_{j,i} devj,i就是我们前面说过的不同商品i,j间受欢迎度的差异,第一个式子就是简单的对不同人取个均值,第二个式子就是简单的对不同物再取个均值,最后得到的 P ( u ) j P(u)_j P(u)j就是用户u对商品j的评分,果然是一个朴素的想法

第六部分:the weighted slope one scheme
这就是slope one的一个变体,如其名所示,就是多考虑了下某方面的权重,那么这个权重究竟是哪方面的?从式子来看,就是那个 c j , i c_{j,i} cj,i,那么这个 c j , i c_{j,i} cj,i是什么? c a r d ( S j , i ( X ) ) card(S_{j,i}(X)) card(Sj,i(X)) S j , i ( X ) S_{j,i}(X) Sj,i(X)是什么,上面已经说过,其是有对i,j都作过评分的用户的集合, c a r d ( S j , i ( X ) ) card(S_{j,i}(X)) card(Sj,i(X))就是对i,j都作过评分的用户的个数,通常来说,若更多的人对某商品i,j对都作过评分,其相应的统计均值更接近均衡,更可靠,所以相应的权重更高也容易理解。

step7:the bi-polar slope one scheme
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这又是对slope one的另一个变体,对比本体,我们发现,dev部分就是在左上角多了个like,对应的,这里应该还有个dislike,这里略去,领会精神,知道还存在对应的另一个就好。其实好好看上面的式子,会发现,其实这个式子的各个组成部分在上面算法的介绍中都出现过。这里只要详细介绍下 S j , i l i k e S^{like}_{j,i} Sj,ilike 就好, S j , i ( X ) S_{j,i}(X) Sj,i(X) 表示是有对i,j都作过评分的用户的集合, S j , i l i k e S^{like}_{j,i} Sj,ilike 其实就是有对i,j都作过评分,而且都表示喜欢商品i,j的用户的集合,dislike与此相反,不必赘述。那么什么叫用户表示喜欢某商品呢,其实就是对某商品的评分高于其对以往所有商品评分的均值,毕竟这世界同时存在着直男癌和死肥宅,直男癌对所有商品评分都低,死肥宅对所有商品评分都高,只有取他们自己评分的均值才能知道到底什么才是他们的真爱。接下来我们来分析下为什么这么做。我们发现,对比 S j , i ( X ) S_{j,i}(X) Sj,i(X) S j , i l i k e S^{like}_{j,i} Sj,ilike S j , i d i s l i k e S^{dislike}_{j,i} Sj,idislike对集合中的元素有更严格的定义,如果有某对商品i,j,某用户a表示喜欢i却不喜欢j,则 S j , i l i k e S^{like}_{j,i} Sj,ilike S j , i d i s l i k e S^{dislike}_{j,i} Sj,idislike中都将不包含某用户a这个元素,跟 S j , i ( X ) S_{j,i}(X) Sj,i(X)相比,则对应的这条信息记录就从此在式子里消失不见了,不再参与最后p(u)的计算,第一直觉是,有些数据不见了,那么根据大数据时代的基本定律,数据即石油,该算法应该是比本体slope one更差才对,但结果是更好,为什么呢?其实本质是这里做了个过滤,某用户a喜欢i却不喜欢j,与同时喜欢i,j或同时不喜欢i,j的人本质就不是同类型的人,我们预测评分,用协同过滤,要做参考当然是参考同类型的人比较好,那些不同类型的人带入的数据其实只带来了干扰,过滤掉他们当然就带来更好的效果,所以大数据时代的数据即石油,应该修正为质量高的数据才为石油更好些。

在这里插入图片描述
最后是各个算法间预测的比较,上面那个数值越低越好。可以看到,pearson预测性能表现也不错,但论对内存的消耗,响应速度,对冷启动用户的支持,slope one类型的算法就胜过太多了。

7.2.1SlopeOne算法步骤

说了那么多,SlopeOne算法也就是三个步骤:

  • Step1,计算Item之间的评分差的均值,记为评分偏差(两个item都评分过的用户)在这里插入图片描述

商品i和j之间被共同用户打分过的个数

  • Step2,根据Item间的评分偏差和用户的历史评分,预测用户对未评分的item的评分在这里插入图片描述
    在这里插入图片描述
  • Step3,将预测评分排序,取topN对应的item推荐给用户
7.2.2SlopeOne算法小例子

Step1,计算Item之间的评分差的均值
在这里插入图片描述
b与a:((3.5-5)+(5-2)+(3.5-4.5))/3=0.5/3
c与a:((4-2)+(1-4.5))/2=-1.5/2
d与a:((2-2)+(4-4.5))/2=-0.5/2
c与b:((4-5)+(1-3.5))/2=-3.5/2
d与b:((2-5)+(4-3.5))/2=-2.5/2
d与c:((2-4)+(4-1))/2=1/2

Step2,预测用户A对商品c和d的评分
a对c评分=((-0.75+5)+(-1.75+3.5))/2=3
a对d评分=((-0.25+5)+(-1.25+3.5))/2=3.5
在这里插入图片描述
Step3,将预测评分排序,推荐给用户推荐顺序为{d, c}
在这里插入图片描述

7.2.3SlopeOne算法的优缺点

SlopeOne算法,把三个步骤通过一个例子的去讲述,发现这个算法也不是很难。每个算法的提出,都是为了解决某个问题,这个问题或大或小,如果帮忙解决当时很大的问题,我们称之为Basline算法系列了。那么属于SlopeOne算法的特点是什么?

  • 适用于item更新不频繁,数量相对较稳定
  • item数<<user数
  • 算法简单,易于实现,执行效率高
  • 依赖用户行为,存在冷启动问题和稀疏性问题
7.2.4SlopeOne算法实战
#导入surprise 库的相关模块
from surprise import Dataset
from surprise import Reader
from surprise import SlopeOne
from surprise import accuracy
from surprise.model_selection import KFold

# 数据读取
file_path = 'E:/python/machina/kaggle_practice/week4/data/ratings.csv'
reader = Reader(line_format='user item rating timestamp', sep=',', skip_lines=1)
data = Dataset.load_from_file(file_path, reader=reader)
train_set = data.build_full_trainset()

'''
    该算法适用于物品更新不频繁,数量相对较稳定并且物品数目明显小于用户数的场景。依赖用户的用户行为日志和物品偏好的相关内容。
    优点:
    1.算法简单,易于实现,执行效率高;
    2.可以发现用户潜在的兴趣爱好;
    缺点:
    依赖用户行为,存在冷启动问题和稀疏性问题。
'''
# SlopeOne
algo = SlopeOne()
# 定义K折交叉验证迭代器,K=3
kf = KFold(n_splits=3)
for trainset, testset in kf.split(data):
    algo.fit(trainset)
    predictions = algo.test(testset)
    accuracy.rmse(predictions, verbose=True)
uid = str(196)
iid = str(302)
pred = algo.predict(uid, iid, r_ui=4, verbose=True)
# 打印结果 
# RMSE: 0.8689
print(pred) 

总结

在这里插入图片描述

  1. ALS和SGD都是数学上的优化方法,可以解决最优化问题(损失函数最小化)
  2. ALS-WR算法,可以解决过拟合问题,当隐特征个数很多的时候也不会造成过拟合
  3. ALS,SGD都可以进行并行化处理
  4. SGD方法可以不需要遍历所有的样本即可完成特征向量的求解
  5. Facebook把SGD和ALS两个算法进行了揉合,提出了旋转混合式求解方法,可以处理1000亿数据,效率比普通的Spark MLlib快了10倍

参考资料

1.对比ALS和FM
2.交换最小二乘
3.Python推荐系统库:Surprise

参考文献

[^1]:Yunhong Zhou, Dennis Wilkinson, Robert Schreiber and Rong Pan. Large-scale Parallel Collaborative Filtering for the Netflix Prize

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水花

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值