从0了解机器学习:8——推荐系统

推荐系统在商业领域的关注甚至远远超过了学术界,其实际应用在我们生活中无处不在

本文章将介绍无监督学习中的推荐系统,以及一些其他有关概念

(如果文章中的公式不能正常显示,请刷新该页面。如果还不能解决,请邮箱联系我,谢谢...)

推荐系统(Recommender system)

让我们举一个例子来开始我们的推荐系统之旅:

预测电影评分

用户使用1~5颗星来表示电影的喜爱程度

电影(纯虚构) 1、张三 2、李四 3、王五 4、赵六
1、最后的爱 5 5 0 0
2、永远浪漫 5 0
3、可爱小狗 4 0
4、永不停歇 0 0 5 4
5、剑与勇士 0 0 5

定义以下变量:

  • \(n_u\) :用户数量
  • \(n_m\) :电影数量
  • \(r(i,j)\) :如果用户 \(j\) 对电影 \(i\) 进行了评分则为 1 反之为 0
  • \(y^{(i,j)}\) :用户 \(j\) 给电影 \(i\) 的评分(前提是 \(r(i,j)=1\)

则对于上面的表格来说: \[ \begin{gather} n_u=4\qquad n_m=5\qquad \\ r(1,1)=1\qquad r(3,1)=0\qquad y^{(3,2)}=4 \end{gather} \] 那么我们需要电影的什么特征呢?

我们可以一些小于1的数表示某类型在某电影中的占比

电影(纯虚构) 1、张三 2、李四 3、王五 4、赵六 \(x_1\) (浪漫类) \(x_2\) (动作类)
1、最后的爱 5 5 0 0 0.9 0
2、永远浪漫 5 0 1.0 0.01
3、可爱小狗 4 0 0.99 0
4、永不停歇 0 0 5 4 0.1 1.0
5、剑与勇士 0 0 5 0 0.9

此时对于这个表格来说:

\(n_u=4\)

\(n_m=5\)

\(n=2\) :表示电影的特征数,表中只有浪漫和动作两种特征

那么第一部电影的特征就是:\(x^{(1)}=\begin{bmatrix}0.9 \\ 0 \end{bmatrix}\) 以此类推

对于用户 1 来说,他对电影 \(i\) 的评分预测为:\(w^{(1)}\cdot x^{(i)}+b^{(1)}\)

如果: \[ w^{(1)}= \begin{bmatrix} 5 \\ 0 \end{bmatrix} \qquad b^{(1)}=0\qquad x^{(3)}= \begin{bmatrix} 0.99 \\ 0 \end{bmatrix} \] 那么第一个用户对电影 3 的评分预测为: \[ w^{(1)}\cdot x^{(3)}+b^{(1)}=4.95 \] 所以说,预测用户 \(j\) 对电影 \(i\) 的评分为: \[ w^{(j)}\cdot x^{(i)}+b^{(j)} \]

损失函数

定义变量:

  • \(r(i,j)\) :如果用户 \(j\) 对电影 \(i\) 进行了评分则为 1 反之为 0
  • \(y^{(i,j)}\) :用户 \(j\) 给电影 \(i\) 的评分(前提是 \(r(i,j)=1\)
  • \(w^{(j)},b^{(j)}\) :用户 \(j\) 的参数
  • \(x^{(i)}\) :电影 \(i\) 的特征向量

预测用户 \(j\) 对电影 \(i\) 的评分为: \[ w^{(j)}\cdot x^{(i)}+b^{(j)} \]

  • \(m^{(j)}\) :用户 \(j\) 已经评分的电影数量

目标是学习用户参数:\(w^{(j)},b^{(j)}\)

对于用户 \(j\) 损失函数: \[ \begin{gather} min \\ w^{(j)},b^{(j)} \end{gather} J(w^{(j)},b^{(j)})=\frac{1}{2m^{(j)}}\sum_{i:r(i,j)=1}(w^{(j)}\cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2m^{(j)}}\sum_{k=1}^n(w_k^{(j)})^2 \] 由于 \(m^{(j)}\) 是常数,可以同时去除

以上是对于单个用户的损失函数

而对于所有的用户参数:\((w^{(1)},b^{(1)}),(w^{(2)},b^{(2)}),...,(w^{(n_u)},b^{(n_u)})\) 的损失函数为: \[ J \begin{pmatrix} w^{(1)},...,w^{(n_u)} \\ b^{(1)},...,b^{(n_u)} \end{pmatrix} =\frac{1}{2}\sum_{j=1}^{n_u}\sum_{i:r(i,j)=1}(w^{(j)}\cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_u}\sum_{k=1}^n(w_k^{(j)})^2 \] 但是有一个问题,我们哪来的特征呢: 就像这样

电影(纯虚构) 1、张三 2、李四 3、王五 4、赵六 x_1 (浪漫类) x_2 (动作类)
1、最后的爱 5 5 0 0
2、永远浪漫 5 0
3、可爱小狗 4 0
4、永不停歇 0 0 5 4
5、剑与勇士 0 0 5

我们可以假设我们已经获得了用户的参数:\(w^{(1)},...,w^{(n_u)};b^{(1)},...,b^{(n_u)}\)

假设我们有了 \(w,b\)\[ \begin{gather} w^{(1)}= \begin{bmatrix} 5 \\ 0 \end{bmatrix} \quad w^{(2)}= \begin{bmatrix} 5 \\ 0 \end{bmatrix} \quad w^{(3)}= \begin{bmatrix} 0 \\ 5 \end{bmatrix} \quad w^{(4)}= \begin{bmatrix} 0 \\ 5 \end{bmatrix} \quad \\ b^{(1)}=0\qquad b^{(2)}=0\qquad b^{(3)}=0\qquad b^{(4)}=0\qquad \end{gather} \] 使用公式:\(w^{(j)}\cdot x^{(i)}+b^{(j)}\) \[ \begin{gather} w^{(1)}\cdot x^{(1)}+b^{(1)}\approx5 \\ w^{(2)}\cdot x^{(1)}+b^{(2)}\approx5 \\ w^{(3)}\cdot x^{(1)}+b^{(3)}\approx0 \\ w^{(4)}\cdot x^{(1)}+b^{(4)}\approx0 \end{gather} \quad\Rightarrow\quad x^{(1)}= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \] 以此类推,求出所有的特征

协同过滤(Collaborative filtering)

给定参数 \(w,b\) 去学习特征 \(x^{(i)}\) \[ J(x^{(i)})=\frac{1}{2}\sum_{j:r(i,j)=1}(w^{(j)}\cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2}\sum_{k=1}^n(x_k^{(i)})^2 \] 上述公式是对于单个特征 \(x^{(i)}\) 而言

那么对于所有特征 \(x^{(1)},...,x^{(n_m)}\) 来说,损失函数: \[ J(x^{(1)},...,x^{(n_m)})=\frac{1}{2}\sum_{j=1}^{n_u}\sum_{j:r(i,j)=1}(w^{(j)}\cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_m}\sum_{k=1}^n(x_k^{(i)})^2 \] 到了这里为止,我们给出了学习特征的损失函数,那么这个函数是在假设已知 \(w,b\) 的情况下得到的

那么 \(w,b\) 怎么获取呢?

事实上,之前我们就已经得到了 \(w,b\) 的损失函数,我们可以把它们一起进行训练

这是 \(w,b\) 的: \[ J \begin{pmatrix} w^{(1)},...,w^{(n_u)} \\ b^{(1)},...,b^{(n_u)} \end{pmatrix} =\frac{1}{2}\sum_{j=1}^{n_u}\sum_{i:r(i,j)=1}(w^{(j)}\cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_u}\sum_{k=1}^n(w_k^{(j)})^2 \] 把这两个损失函数放到一起: \[ J \begin{pmatrix} w^{(1)},...,w^{(n_u)} \\ b^{(1)},...,b^{(n_u)} \\ x^{(1)},...,x^{(n_m)} \end{pmatrix} =\frac{1}{2}\sum_{(i,j):r(i,j)=1}(w^{(j)}\cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_u}\sum_{k=1}^n(w_k^{(j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_m}\sum_{k=1}^n(x_k^{(i)})^2 \] 这样,特征与参数一起学习,就是协同过滤

参数更新: \[ \begin{gather} w_i^{(j)}=w_i^{(j)}-lr\cdot\frac{\partial }{\partial w_i^{(j)}}J(w,b,x) \\ b^{(j)}=b^{(j)}-lr\cdot\frac{\partial }{\partial b^{(j)}}J(w,b,x) \\ x_k^{(i)}=x_k^{(i)}-lr\cdot\frac{\partial }{\partial x_k^{(i)}}J(w,b,x) \end{gather} \] 其中的 \(x\) 也作为参数一起更新

二进制标签(Binary label)

二进制标签,比如:点击、点赞、订阅、关注、收藏等

电影(纯虚构) 1、张三 2、李四 3、王五 4、赵六
1、最后的爱 1 1 0 0
2、永远浪漫 1 0
3、可爱小狗 1 0
4、永不停歇 0 0 1 1
5、剑与勇士 0 0 1

表格中的

  • 1:喜欢
  • 0:不喜欢
  • ?:未观看

许多的二进制标签其实都是类似的:

  • 用户 \(j\) 在首页商品展示后购买了吗?(1,0,?)
  • 用户 \(j\) 喜欢这个商品吗?(1,0,?)
  • 用户 \(j\) 在这个商品花费了30秒以上时间吗?(1,0,?)
  • 用户 \(j\) 点击这个商品了吗?(1,0,?)

这些分数的含义:

  • 1:在商品展示之后进行正向操作
  • 0:在商品展示后没有进行正向操作/进行负向操作
  • ?:商品还没有给用户展示

商品其实也可以是视频、图片、博客、文章等

损失函数

二进制标签的损失函数其实和二分类的类似,都是用交叉熵

之前的损失函数为: \[ J \begin{pmatrix} w^{(1)},...,w^{(n_u)} \\ b^{(1)},...,b^{(n_u)} \\ x^{(1)},...,x^{(n_m)} \end{pmatrix} =\frac{1}{2}\sum_{(i,j):r(i,j)=1}(w^{(j)}\cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_u}\sum_{k=1}^n(w_k^{(j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_m}\sum_{k=1}^n(x_k^{(i)})^2 \] 而二进制标签的激活函数为: \[ f_{(w,b,x)}(x)=g(w^{(j)}\cdot x^{(i)}+b^{(j)})=\frac{1}{1+e^{-(w^{(j)}\cdot x^{(i)}+b^{(j)})}} \] 而单个用户的损失函数也变为: \[ L(f_{(w,b,x)}(x),y^{(i,j)})=-y^{(i,j)}\cdot log(f_{(w,b,x)}(x))-(1-y^{(i,j)})\cdot log(1-f_{(w,b,x)}(x)) \] 对于所有的用户,损失函数为: \[ J(w,b,x)=\sum_{(i,j):r(i,j)=1}L(f_{(w,b,x)}(x),y^{(i,j)}) \]

均值归一化(Mean normalization)

如果一个新用户,没有给任何一部电影评过分:

电影(纯虚构) 1、张三 2、李四 3、王五 4、赵六 5、二麻子
1、最后的爱 5 5 0 0 ?
2、永远浪漫 5 0 ?
3、可爱小狗 4 0 ?
4、永不停歇 0 0 5 4 ?
5、剑与勇士 0 0 5 ?

由于新用户没有进行评分所以他的 \(r(i,j)=0\) ,而对于损失函数: \[ J \begin{pmatrix} w^{(1)},...,w^{(n_u)} \\ b^{(1)},...,b^{(n_u)} \\ x^{(1)},...,x^{(n_m)} \end{pmatrix} =\frac{1}{2}\sum_{(i,j):r(i,j)=1}(w^{(j)}\cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_u}\sum_{k=1}^n(w_k^{(j)})^2+\frac{\lambda}{2}\sum_{j=1}^{n_m}\sum_{k=1}^n(x_k^{(i)})^2 \] \(r(i,j)=0\) 意味着损失函数的第一项失效,而此时损失函数只剩下 \(w,x\) 的正则化项,那么它就会一直对 \(w\) 进行最小化,最后 \(w\) 很可能等于0,并且 \(b\) 也很大概率等于0

而带入评分预测的函数中 \(w^{(j)}\cdot x^{(i)}+b^{(j)}\) 中,就会发现评分等于0

一个新用户不可能对每一个电影都打0分,这是不寻常的

使用均值归一化: \[ \begin{gather} M= \begin{bmatrix} 5\quad5\quad0\quad0\quad? \\ 5\quad?\quad?\quad0\quad? \\ ?\quad4\quad0\quad?\quad? \\ 0\quad0\quad5\quad4\quad? \\ 0\quad0\quad5\quad0\quad? \end{bmatrix} \qquad 求出每行均值\Rightarrow\qquad \mu= \begin{bmatrix} 2.5 \\ 2.5 \\ 2 \\ 2.25 \\ 1.25 \end{bmatrix} \\ M-\mu= \begin{bmatrix} 2.5\quad2.5\quad-2.25\quad-2.25\qquad? \\ 2.5\qquad?\;\qquad?\qquad-2.25\qquad? \\ ?\qquad2\qquad-2\quad\qquad?\quad\qquad? \\ -2.25\quad-2.25\quad2.75\quad1.75\qquad? \\ -1.25\quad-1.25\quad3.75\quad-1.25\quad? \end{bmatrix} \end{gather} \] 最后得到的 \(M-\mu\) 就是新的 \(y^{(i,j)}\)

对于用户 \(j\) 对于电影 \(i\) 的预测值为: \[ w^{(j)}\cdot x^{(i)}+b^{(j)}+\mu_i \] 加上的 \(\mu_i\) 是为了让预测结果为正值

那么对于新用户 二麻子 他的预测值为: \[ w^{(5)}= \begin{bmatrix} 0 \\ 0 \end{bmatrix} \quad b^{(5)}=0 \qquad w^{(5)}\cdot x^{(1)}+b^{(5)}+\mu_i=2.5 \] 让新用户的评分为均值,这起码比预测新用户评分全为0更为合理

同时,均值归一化也会加快算法的运行速度

此外,协同过滤算法一般不会使用神经网络,因为协同过滤算法不能很好地拟合进神经网络里

在之前的内容里,我们提到了一个电影的特征 \(x^{(i)}\) ,比如:爱情片,动作片

但是这些特征都是我们人为加以解释的,就像神经网络一样,可能算法它并不知道,这个特征代表什么含义

所以说,通过特征的含义寻找相似的电影,或者相似的特征是比较困难的

其实我们可以使用之前聚类算法的原理,如果两个对象是相似的,那么他们的特征也应该是相似的,也就是说,他们的对应特征值的差值不会太大,这样就好办了

为了找到和对象 \(i\) 相似的特征的对象 \(k\) ,只需要这样做:将二者的特征作差并平方 \[ \sum_{l=1}^n(x_l^{(k)}-x_l^{(i)})^2\quad\Leftrightarrow\quad|| x_l^{(k)}-x_l^{(i)} ||^2 \] 只需要找到特征之间距离最短的,那么他们就是相似的

冷启动问题:

  • 怎么给一个刚刚发布的,几乎没有人评分的内容进行排名?
  • 怎么给一个只评分很少内容的用户推荐内容?

可以使用一些附加信息来加以优化:

  • 对于内容来说:可以根据发布内容的作者,工作室,甚至IP地址作为特征
  • 对于用户来说:可以根据用户的性别,年龄,地址,如果可以获取用户的其他信息,这样也可以(隐私协议)

基于内容的过滤(Content-based filtering)

协同过滤算法:

向你推荐那些别人评分和你相似的内容

基于内容过滤算法:

基于你个人的特征和内容的特征向你推荐(个性化)

定义变量:

\(r(i,j)\) :用户 \(j\) 对内容 \(i\) 进行了评分,则为1,反之为0

\(y^{(i,j)}\) :用户 \(j\) 给内容 \(i\) 的评分(前提是 \(r(i,j)=1\)

用户特征:

  • 年龄
  • 性别
  • 国家
  • 已观看的电影数
  • ...

用户特征使用 \(x_u^{(j)}\) 表示用户 \(j\) 的个人特征

电影(内容)特征:

  • 年份
  • 类型
  • 评论
  • 平均分
  • ...

电影特征使用 \(x_m^{(i)}\) 表示电影 \(i\) 的特征

对于 \(x_u^{(j)},x_m^{(i)}\) 来说,其向量的尺寸可以不同

预测用户 \(j\) 对电影 \(i\) 的评分:

使用评分预测函数:\(w^{(j)}\cdot x^{(i)}+b^{(j)}\)

这里我们可以去除 \(b^{(j)}\) ,事实上,这样做不会影响算法

\[ 用户偏好:v_u= \begin{bmatrix} 4.9 \\ 0.1 \\ . \\ . \\ . \\ 3.0 \end{bmatrix} \qquad 电影特征:v_m= \begin{bmatrix} 4.5 \\ 0.2 \\ . \\ . \\ . \\ 3.5 \end{bmatrix} \] 通过计算 \(v_u\cdot v_m\) 来获得预测值

注意: 两向量 \(v_u,v_m\) 的尺寸是相同的

那么,怎么将两个尺寸不相同的向量 \(x_u^{(j)},x_m^{(i)}\) 转化为尺寸相同的向量 \(v_u,v_m\) 呢?

基于内容过滤的深度学习(DL for content-based filtering)

为了将两个尺寸不相同的向量 \(x_u^{(j)},x_m^{(i)}\) 转化为尺寸相同的向量 \(v_u,v_m\) ,深度学习是一个好的选择

预测:\(v_u\cdot v_m\) (预测评价的分数)

如果要预测一个二进制标签:喜欢、收藏、订阅等:

那么可以使用 sigmoid 函数作为激活函数:\(g(v_u^{(j)}\cdot v_m^{(i)})\)

损失函数: \[ J=\sum_{(i,j):r(i,j)=1}(v_u^{(j)}\cdot v_m^{(i)}-y^{(i,j)})^2+正则化项 \] 查找相关内容(电影):

\(v_u^{(j)}\) 是一个大小为32的,描述用户特征 \(x_u^{(j)}\) 的向量

\(v_m^{(j)}\) 是一个大小为32的,描述电影特征 \(x_m^{(j)}\) 的向量

去查找与电影 \(i\) 相关的电影 \(k\)\[ || v_m^{(k)}-v_m^{(i)} ||^2 \] 使得上述式子最小的电影 \(k\) 就是与电影 \(i\) 相关的电影

如果在现实应用中,一般可以通过夜间运行计算服务器来计算(更新)这些特征,以便第二天给用户更好的推荐体验

从大型目录中推荐(Recommending from a large catalogue)

在应用时,我们会遇到从成百万上千万的内容中选出极少数几个内容进行推荐

  • 数以百万的音乐?
  • 数以千万的广告?
  • 数以百万的电影?
  • 数以亿的商品?

如果这些数据全部进入神经网络:

那么神经网络就会进行数以千万数以亿计的推理计算,如果每次新用户登录的话,计算这些将变得不可能

为了解决这个问题,我们一般有两个步骤:检索和排名

检索(Retrieval) :生成大量的用户可能感兴趣(也许有讨厌的)的内容列表,例如:

  • 选出用户最后观看过的10部电影,找出与这些电影相似的电影
  • 对于用户经常观看的3种类型,找出这三种类型电影中,平均评分最高的10部电影
  • 用户所在国家/地区排名前20的电影

上述的例子也可更换为商品,文章,视频等等

通过以上的步骤(也可以根据情况,添加一些检索的步骤)你大概可以找到数百个电影

虽然听起来也挺多的,但是对于百万,千万甚至是亿来说,根本不算什么

找出的这数百个电影之后,再进行去重,并移除用户已经观看过的电影

剩下的电影就可以进入下一步:

排名(Ranking)

  • 将这数百个电影与用户特征输入神经网络中计算

  • 计算出各个电影对于用户的预测评分后,将这些电影的评分进行排名,选出排名最高的几个电影进行推荐

当然,就像之前提到的,如果你在夜晚就已经将所有的电影的特征计算好了,那么在输入神经网络的时候,就只需要执行用户特征的计算即可

当然,检索步骤中,检索越多的内容可能会在推荐内容时表现得更好,但是这也会使得推荐的速度变慢

总结

本文章讲了无监督学习中的推荐系统,同时也讲了一些其他概念:协同过滤等

在接下来,我们将开始学习一个特殊的机器学习算法——强化学习(Reinforcement learning)

强化学习目前已经作为单独的一类机器学习领域

从0了解机器学习:8——推荐系统

http://www.heimaolala.top/2025/01/08/ML8/

作者

heimaolala

发布于

2025-01-08

更新于

2025-03-10

许可协议

评论