【机器学习-监督学习】双线性模型

在这里插入图片描述

【作者主页】Francek Chen
【专栏介绍】 ⌈ ⌈ Python机器学习 ⌋ ⌋ 机器学习是一门人工智能的分支学科,通过算法和模型让计算机从数据中学习,进行模型训练和优化,做出预测、分类和决策支持。Python成为机器学习的首选语言,依赖于强大的开源库如Scikit-learn、TensorFlow和PyTorch。本专栏介绍机器学习的相关算法以及基于Python的算法实现。
【GitCode】专栏资源保存在我的GitCode仓库:https://gitcode.com/Morse_Chen/Python_machine_learning。

文章目录

    • 一、矩阵分解
    • 二、动手实现矩阵分解
    • 三、因子分解机
    • 四、动手实现因子分解机
    • 五、拓展:概率矩阵分解


  从本文开始,我们介绍参数化模型中的非线性模型。在前几篇文章中,我们介绍了线性回归与逻辑斯谛回归模型。这两个模型都有一个共同的特征:包含线性预测因子 θ T x \boldsymbol\theta^\mathrm{T}\boldsymbol x θTx。将该因子看作 x \boldsymbol x x的函数,如果输入 x \boldsymbol x x变为原来的 λ \lambda λ倍,那么输出为 θ T ( λ x ) = λ θ T x \boldsymbol\theta^\mathrm{T}(\lambda \boldsymbol x) = \lambda \boldsymbol\theta^\mathrm{T} \boldsymbol x θT(λx)=λθTx,也变成原来的 λ \lambda λ倍。在逻辑斯谛回归的扩展阅读中,我们将这一类模型都归为广义线性模型。然而,此类模型所做的线性假设在许多任务上并不适用,我们需要其他参数假设来导出更合适的模型。本文首先讲解在推荐系统领域很常用的双线性模型(bilinear model)。

  双线性模型虽然名称中包含“线性模型”,但并不属于线性模型或广义线性模型,其正确的理解应当是“双线性”模型。在数学中,双线性的含义为,二元函数固定任意一个自变量时,函数关于另一个自变量线性。具体来说,二元函数 f ⁣ : R n × R m → R l f \colon \mathbb{R}^n \times \mathbb{R}^m \to \mathbb{R}^l f:Rn×RmRl 是双线性函数,当且仅当对任意 u , v ∈ R n , s , t ∈ R m , λ ∈ R \boldsymbol u, \boldsymbol v \in \mathbb{R}^n, \boldsymbol s, \boldsymbol t \in \mathbb{R}^m, \lambda \in \mathbb{R} u,vRn,s,tRm,λR 都有:

  1. f ( u , s + t ) = f ( u , s ) + f ( u , t ) f(\boldsymbol u, \boldsymbol s + \boldsymbol t) = f(\boldsymbol u, \boldsymbol s) + f(\boldsymbol u, \boldsymbol t) f(u,s+t)=f(u,s)+f(u,t)
  2. f ( u , λ s ) = λ f ( u , s ) f(\boldsymbol u, \lambda \boldsymbol s) = \lambda f(\boldsymbol u, \boldsymbol s) f(u,λs)=λf(u,s)
  3. f ( u + v , s ) = f ( u , s ) + f ( v , s ) f(\boldsymbol u + \boldsymbol v, \boldsymbol s) = f(\boldsymbol u, \boldsymbol s) + f(\boldsymbol v, \boldsymbol s) f(u+v,s)=f(u,s)+f(v,s)
  4. f ( λ u , s ) = λ f ( u , s ) f(\lambda \boldsymbol u, \boldsymbol s) = \lambda f(\boldsymbol u, \boldsymbol s) f(λu,s)=λf(u,s)

  最简单的双线性函数的例子是向量内积 ⟨ ⋅ , ⋅ ⟩ \langle \cdot, \cdot \rangle ,,我们按定义验证前两条性质:

  • ⟨ u , s + t ⟩ = ∑ i u i ( s i + t i ) = ∑ i ( u i s i + u i t i ) = ∑ i u i s i + ∑ i u i t i = ⟨ u , s ⟩ + ⟨ u , t ⟩ \small\langle \boldsymbol u, \boldsymbol s + \boldsymbol t \rangle = \sum_i u_i(s_i+t_i) = \sum_i(u_is_i + u_it_i) = \sum_i u_is_i + \sum_i u_it_i = \langle \boldsymbol u,\boldsymbol s \rangle + \langle \boldsymbol u, \boldsymbol t\rangle u,s+t=iui(si+ti)=i(uisi+uiti)=iuisi+iuiti=u,s+u,t
  • ⟨ u , λ s ⟩ = ∑ i u i ( λ s i ) = λ ∑ i u i s i = λ ⟨ u , s ⟩ \small\langle \boldsymbol u, \lambda \boldsymbol s \rangle = \sum_i u_i(\lambda s_i) = \lambda \sum_i u_is_i = \lambda \langle \boldsymbol u, \boldsymbol s \rangle u,λs=iui(λsi)=λiuisi=λu,s

后两条性质由对称性,显然也是成立的。而向量的加法就不是双线性函数。虽然加法满足第1、3条性质,但对第2条,如果 u ≠ 0 \boldsymbol u \neq \boldsymbol 0 u=0 λ ≠ 1 \lambda\neq 1 λ=1,则有 u + λ s ≠ λ ( u + s ) \boldsymbol u + \lambda \boldsymbol s \neq \lambda (\boldsymbol u + \boldsymbol s) u+λs=λ(u+s)

  与线性模型类似,双线性模型并非指模型整体具有双线性性质,而是指其包含双线性因子。该特性赋予模型拟合一些非线性数据模式的能力,从而得到更加精准预测性能。接下来,我们以推荐系统场景为例,介绍两个基础的双线性模型:矩阵分解模型和因子分解机。

一、矩阵分解

  矩阵分解(matrix factorization,MF)是推荐系统中评分预测(rating prediction)的常用模型,其任务为根据用户和商品已有的评分来预测用户对其他商品的评分。为了更清晰地解释MF模型的任务场景,我们以用户对电影的评分为例进行详细说明。如图1所示,设想有 N N N个用户和 M M M部电影,每个用户对一些电影按自己的喜好给出了评分。现在,我们的目标是需要为用户从他没有看过的电影中,向他推荐几部他最有可能喜欢看的电影。理想情况下,如果这个用户对所有电影都给出了评分,那么这个任务就变为从已有评分的电影中进行推荐——直接按照用户打分的高低排序。但实际情况下,在浩如烟海的电影中,用户一般只对很小一部分电影做了评价。因此,我们需要从用户已经做出的评价中推测用户为其他电影的打分,再将电影按推测的打分排序,从中选出最高的几部推荐给该用户。

在这里插入图片描述

图1 用户对电影的评分矩阵

  我们继续从生活经验出发来思考这一问题。假设某用户为一部电影打了高分,那么可以合理猜测,该用户喜欢这部电影的某些特征。例如,电影的类型是悬疑、爱情、战争或是其他种类;演员、导演和出品方分别是哪些;叙述的故事发生在什么年代;时长是多少,等等。假如我们有一个电影特征库,可以将每部电影用一个特征向量表示。向量的每一维代表一种特征,值代表电影具有这一特征的程度。同时,我们还可以构建一个用户画像库,包含每个用户更偏好哪些类型的特征,以及偏好的程度。假设特征的个数是 d d d,那么所有电影的特征构成的矩阵是 P ∈ R M × d \boldsymbol P \in \mathbb{R}^{M \times d} PRM×d,用户喜好构成的矩阵是 Q ∈ R N × d \boldsymbol Q \in \mathbb{R}^{N \times d} QRN×d。图2给出了两个矩阵的示例。

在这里插入图片描述

图2 电影和用户的隐变量矩阵

  需要说明的是,我们实际上分解出的矩阵只是某种交互结果背后的隐变量,并不一定对应真实的特征。这样,我们就把一个用户与电影交互的矩阵拆分成了用户、电影两个矩阵,并且这两个矩阵中包含了更多的信息。最后,用这两个矩阵的乘积 R = P T Q \boldsymbol R = \boldsymbol P^\mathrm{T} \boldsymbol Q R=PTQ 可以还原出用户对电影的评分。即使用户对某部电影并没有打分,我们也能通过矩阵乘积,根据用户喜欢的特征和该电影具有的特征,预测出用户对电影的喜好程度。

小故事
  矩阵分解和下面要介绍的因子分解机都属于推荐系统(recommender system)领域的算法。我们在日常使用软件、浏览网站的时候,软件或网站会记录下来我们感兴趣的内容,并在接下来更多地为我们推送同类型的内容。例如,如果我们在购物网站上浏览过牙刷,它就可能再给我们推荐牙刷、毛巾、脸盆等等相关性比较大的商品,这就是推荐系统的作用。推荐系统希望根据用户的特征、商品的特征、用户和商品的交互历史,为用户做出更符合个人喜好的个性化推荐,提高用户的浏览体验,同时为公司带来更高的经济效益。
  机器学习界开始大量关注推荐系统任务源自美国奈飞电影公司(Netflix)于2006年举办的世界范围的推荐系统算法大赛。该比赛旨在探寻一种算法能更加精确地预测48万名用户对1.7万部电影的打分,如果某个参赛队伍给出的评分预测精度超过了基线算法10%,就可以获得100万美元的奖金。该竞赛在1年来吸引了来自全球186个国家的超过4万支队伍的参加,经过3年的“马拉松”竞赛,最终由一支名为BellKor’s Pragmatic Chaos的联合团队摘得桂冠。而团队中时任雅虎研究员的耶胡达·科伦(Yehuda Koren)则在后来成为了推荐系统领域最为著名的科学家之一,他使用的基于矩阵分解的双线性模型则成为了那个时代推荐系统的主流模型。

  实际上,我们通常能获取到的并不是 P \boldsymbol P P Q \boldsymbol Q Q,而是打分的结果 R \boldsymbol R R。并且由于一个用户只会对极其有限的一部分电影打分,矩阵 R \boldsymbol R R是非常稀疏的,绝大多数元素都是空白。因此,我们需要从 R \boldsymbol R R有限的元素中推测出用户的喜好 P \boldsymbol P P和电影的特征 Q \boldsymbol Q Q。MF模型利用矩阵分解的技巧完成了这一任务。设第 i i i个用户的偏好向量是 p i \boldsymbol p_i pi,第 j j j部电影的特征向量是 q j \boldsymbol q_j qj,其维度都是特征数 d d d。MF假设用户 i i i对电影 j j j的评分 r i j r_{ij} rij是用户偏好与电影特征的内积,即 r i j = p i T q j r_{ij} = \boldsymbol p_i^\mathrm{T}\boldsymbol q_j rij=piTqj。在本文开始已经讲过,向量内积是双线性函数,这也是MF模型属于双线性模型的原因。

  既然MF的目标是通过特征还原评分矩阵 R \boldsymbol R R,我们就以还原结果和 R \boldsymbol R R中已知部分的差距作为损失函数。记 I i j = I ( r i j 存在 ) I_{ij} = \mathbb{I}(r_{ij}\text{存在}) Iij=I(rij存在),即当用户为电影打过分时 I i j I_{ij} Iij 1 1 1,否则为 0 0 0。那么损失函数可以写为 J ( P , Q ) = ∑ i = 1 N ∑ j = 1 M I i j L ( p i T q j , r i j ) J(\boldsymbol P, \boldsymbol Q) = \sum_{i=1}^N\sum_{j=1}^M I_{ij}\mathcal{L}(\boldsymbol p_i^\mathrm{T}\boldsymbol q_j, r_{ij}) J(P,Q)=i=1Nj=1MIijL(piTqj,rij) 式中, L ( p i T q j , r i j ) \mathcal{L}(\boldsymbol p_i^\mathrm{T}\boldsymbol q_j, r_{ij}) L(piTqj,rij) 是模型预测和真实值之间的损失。一般情况下,我们就选用最简单的MSE作为损失,那么优化目标为 min ⁡ P , Q J ( P , Q ) = 1 2 ∑ i = 1 N ∑ j = 1 M I i j ( p i T q j − r i j ) 2 \min_{\boldsymbol P, \boldsymbol Q} J(\boldsymbol P, \boldsymbol Q) = \frac12\sum_{i=1}^N\sum_{j=1}^M I_{ij} (\boldsymbol p_i^\mathrm{T}\boldsymbol q_j - r_{ij})^2 P,QminJ(P,Q)=21i=1Nj=1MIij(piTqjrij)2 再加入对 P \boldsymbol P P Q \boldsymbol Q Q L 2 L_2 L2正则化约束,就得到总的优化目标: min ⁡ P , Q J ( P , Q ) = 1 2 ∑ i = 1 N ∑ j = 1 M I i j ( ( p i T q j − r i j ) 2 + λ ( ∥ p i ∥ 2 + ∣ q j ∥ 2 ) ) \min_{\boldsymbol P, \boldsymbol Q} J(\boldsymbol P, \boldsymbol Q) = \frac12\sum_{i=1}^N\sum_{j=1}^M I_{ij} \left((\boldsymbol p_i^\mathrm{T}\boldsymbol q_j - r_{ij})^2 + \lambda(\|\boldsymbol p_i\|^2 +\\|\boldsymbol q_j\|^2)\right) P,QminJ(P,Q)=21i=1Nj=1MIij((piTqjrij)2+λ(pi2+qj2)) 需要注意,这里的 L 2 L_2 L2约束并非对整个矩阵 P \boldsymbol P P或者 Q \boldsymbol Q Q而言。我们知道,正则化的目的是通过限制参数的规模来约束模型的复杂度,使模型的复杂度与数据中包含的信息相匹配。以用户为例,假设不同用户直接的评分是独立的。如果用户甲给10部电影打了分,用户乙给2部电影打了分,那么数据中关于甲的信息就比乙多。反映到正则化上,对甲的参数的约束强度也应当比乙大。因此,总损失函数中 p i \boldsymbol p_i pi的正则化系数是 λ 2 ∑ j = 1 M I i j \frac{\lambda}{2}\sum\limits_{j=1}^M I_{ij} 2λj=1MIij,在 λ 2 \frac{\lambda}{2} 2λ的基础上又乘以用户 i i i评分的数量。对电影向量 q j \boldsymbol q_j qj也是同理。上式对 p i k \boldsymbol p_{ik} pik q j k \boldsymbol q_{jk} qjk的梯度分别为
∇ p i k J ( P , Q ) = I i j ( ( p i T q j − r i j ) q j k + λ p i k ) ∇ q j k J ( P , Q ) = I i j ( ( p i T q j − r i j ) p i k + λ q j k ) \begin{aligned} \nabla_{\boldsymbol p_{ik}} J(\boldsymbol P, \boldsymbol Q) &= I_{ij} \left((\boldsymbol p_i^\mathrm{T}\boldsymbol q_j - r_{ij})\boldsymbol q_{jk} + \lambda\boldsymbol p_{ik} \right) \\[1ex] \nabla_{\boldsymbol q_{jk}} J(\boldsymbol P, \boldsymbol Q) &= I_{ij} \left((\boldsymbol p_i^\mathrm{T}\boldsymbol q_j - r_{ij})\boldsymbol p_{ik} + \lambda\boldsymbol q_{jk} \right) \end{aligned} pikJ(P,Q)qjkJ(P,Q)=Iij((piTqjrij)qjk+λpik)=Iij((piTqjrij)pik+λqjk)可以发现,上面 p i k \boldsymbol p_{ik} pik梯度中含有 q j k \boldsymbol q_{jk} qjk,而 q j k \boldsymbol q_{jk} qjk的梯度中含有 p i k \boldsymbol p_{ik} pik,两者互相包含,这是由双线性函数的性质决定的,也是双线性模型的一个重要特点。

二、动手实现矩阵分解

  下面,我们来动手实现矩阵分解模型。我们选用的数据集是推荐系统中的常用数据集MovieLens,其包含从电影评价网站MovieLens中收集的真实用户对电影的打分信息。简单起见,我们采用其包含来自943个用户对1682部电影的10万条样本的版本MovieLens-100k。我们对原始的数据进行了一些处理,现在数据集的每一行有3个数,依次表示用户编号 i i i、电影编号 j j j、用户对电影的打分 r i j r_{ij} rij,其中 1 ≤ r i j ≤ 5 1\le r_{ij}\le5 1rij5 且三者都是整数。表1展示了数据集movielens_100k.csv中的3个样本,大家也可以从网站上下载更大的数据集,测试模型的预测效果。

表1 MovieLens-100k数据集示例
用户编号电影编号评分
1962423
1863023
223771
!pip install tqdm
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm # 进度条工具data = np.loadtxt('movielens_100k.csv', delimiter=',', dtype=int)
print('数据集大小:', len(data))
# 用户和电影都是从1开始编号的,我们将其转化为从0开始
data[:, :2] = data[:, :2] - 1# 计算用户和电影数量
users = set()
items = set()
for i, j, k in data:users.add(i)items.add(j)
user_num = len(users)
item_num = len(items)
print(f'用户数:{user_num},电影数:{item_num}')# 设置随机种子,划分训练集与测试集
np.random.seed(0)ratio = 0.8
split = int(len(data) * ratio)
np.random.shuffle(data)
train = data[:split]
test = data[split:]# 统计训练集中每个用户和电影出现的数量,作为正则化的权重
user_cnt = np.bincount(train[:, 0], minlength=user_num)
item_cnt = np.bincount(train[:, 1], minlength=item_num)
print(user_cnt[:10])
print(item_cnt[:10])# 用户和电影的编号要作为下标,必须保存为整数
user_train, user_test = train[:, 0], test[:, 0]
item_train, item_test = train[:, 1], test[:, 1]
y_train, y_test = train[:, 2], test[:, 2]

在这里插入图片描述

  然后,我们将MF模型定义成类,在其中实现梯度计算方法。根据上面的推导,模型的参数是用户喜好 P ∈ R N × d \boldsymbol P \in \mathbb{R}^{N\times d} PRN×d 和电影特征 Q ∈ R M × d \boldsymbol Q \in \mathbb{R}^{M \times d} QRM×d,其中特征数 d d d是我们自己指定的超参数。在参数初始化部分,考虑到最终电影的得分都是正数,我们将参数都初始化为1。

class MF:def __init__(self, N, M, d):# N是用户数量,M是电影数量,d是特征维度# 定义模型参数self.user_params = np.ones((N, d))self.item_params = np.ones((M, d))def pred(self, user_id, item_id):# 预测用户user_id对电影item_id的打分# 获得用户偏好和电影特征user_param = self.user_params[user_id]item_param = self.item_params[item_id]# 返回预测的评分rating_pred = np.sum(user_param * item_param, axis=1)return rating_preddef update(self, user_grad, item_grad, lr):# 根据参数的梯度更新参数self.user_params -= lr * user_gradself.item_params -= lr * item_grad

  下面定义训练函数,以SGD算法对MF模型的参数进行优化。对于回归任务来说,我们仍然以MSE作为损失函数,RMSE作为的评价指标。在训练的同时,我们将其记录下来,供最终绘制训练曲线使用。

def train(model, learning_rate, lbd, max_training_step, batch_size):train_losses = []test_losses = []batch_num = int(np.ceil(len(user_train) / batch_size))with tqdm(range(max_training_step * batch_num)) as pbar:for epoch in range(max_training_step):# 随机梯度下降train_rmse = 0for i in range(batch_num):# 获取当前批量st = i * batch_sizeed = min(len(user_train), st + batch_size)user_batch = user_train[st: ed]item_batch = item_train[st: ed]y_batch = y_train[st: ed]# 计算模型预测y_pred = model.pred(user_batch, item_batch)# 计算梯度P = model.user_paramsQ = model.item_paramserrs = y_batch - y_predP_grad = np.zeros_like(P)Q_grad = np.zeros_like(Q)for user, item, err in zip(user_batch, item_batch, errs):P_grad[user] = P_grad[user] - err * Q[item] + lbd * P[user]Q_grad[item] = Q_grad[item] - err * P[user] + lbd * Q[item]model.update(P_grad / len(user_batch), Q_grad / len(user_batch), learning_rate)train_rmse += np.mean(errs ** 2)# 更新进度条pbar.set_postfix({'Epoch': epoch,'Train RMSE': f'{np.sqrt(train_rmse / (i + 1)):.4f}','Test RMSE': f'{test_losses[-1]:.4f}' if test_losses else None})pbar.update(1)# 计算 RMSE 损失train_rmse = np.sqrt(train_rmse / len(user_train))train_losses.append(train_rmse)y_test_pred = model.pred(user_test, item_test)test_rmse = np.sqrt(np.mean((y_test - y_test_pred) ** 2))test_losses.append(test_rmse)return train_losses, test_losses

  最后,我们定义超参数,并实现MF模型的训练部分,并将损失随训练的变化曲线绘制出来。

# 超参数
feature_num = 16 # 特征数
learning_rate = 0.1 # 学习率
lbd = 1e-4 # 正则化强度
max_training_step = 30
batch_size = 64 # 批量大小# 建立模型
model = MF(user_num, item_num, feature_num)
# 训练部分
train_losses, test_losses = train(model, learning_rate, lbd, max_training_step, batch_size)plt.figure()
x = np.arange(max_training_step) + 1
plt.plot(x, train_losses, color='blue', label='train loss')
plt.plot(x, test_losses, color='red', ls='--', label='test loss')
plt.xlabel('Epoch')
plt.ylabel('RMSE')
plt.legend()
plt.show()

在这里插入图片描述

  为了直观地展示模型效果,我们输出一些模型在测试集中的预测结果与真实结果进行对比。上面我们训练得到的模型在测试集上的RMSE大概是1左右,所以这里模型预测的评分与真实评分大致也差1。

y_test_pred = model.pred(user_test, item_test)
print(y_test_pred[:10]) # 把张量转换为numpy数组
print(y_test[:10])

在这里插入图片描述

三、因子分解机

  本节我们介绍推荐系统中用户行为预估的另一个常用模型:因子分解机(factorization machines,FM)。FM的应用场景与MF有一些区别,MF的目标是从交互的结果中计算出用户和物品的特征;而FM则正好相反,希望通过物品的特征和某个用户点击这些物品的历史记录,预测该用户点击其他物品的概率,即点击率(click through rate,CTR)。由于被点击和未被点击是一个二分类问题,CTR预估可以用逻辑斯谛回归模型来解决。在逻辑斯谛回归中,线性预测子 θ T x \boldsymbol\theta^\mathrm{T} \boldsymbol x θTx为数据中的每一个特征 x i x_i xi赋予权重 θ i \theta_i θi,由此来判断数据的分类。然而,这样的线性参数化假设中,输入的不同特征 x i x_i xi x j x_j xj之间并没有运算,相当于假设不同特征之间是独立的。而在现实中,输入数据的不同特征之间有可能存在关联。例如,假设我们将一张照片中包含的物品作为其特征,那么“红灯笼”与“对联”这两个特征就很可能不是独立的,因为它们都是与春节相关联的意象。因此,作为对线性的逻辑斯谛回归模型的改进,我们进一步引入双线性部分,将输入的不同特征之间的联系也考虑进来。改进后的预测函数为 y ^ ( x ) = θ 0 + ∑ i = 1 d θ i x i + ∑ i = 1 d − 1 ∑ j = i + 1 d w i j x i x j \hat y(\boldsymbol x) = \theta_0 + \sum_{i=1}^d \theta_i x_i + \sum_{i=1}^{d-1}\sum_{j=i+1}^d w_{ij}x_ix_j y^(x)=θ0+i=1dθixi+i=1d1j=i+1dwijxixj 其中, θ 0 \theta_0 θ0是常数项, w i j w_{ij} wij是权重。上式的第二项将所有不同特征 x i x_i xi x j x_j xj相乘,从而可以通过权重 w i j w_{ij} wij调整特征组合 ( i , j ) (i,j) (i,j)对预测结果的影响。将上式改写为向量形式,为: y ^ ( x ) = θ 0 + θ T x + 1 2 x T W x \hat y(\boldsymbol x) = \theta_0 + \boldsymbol\theta^\mathrm{T} \boldsymbol x + \frac12 \boldsymbol x^\mathrm{T} \boldsymbol W \boldsymbol x y^(x)=θ0+θTx+21xTWx 式中,矩阵 W \boldsymbol W W是对称的,即 w i j = w j i w_{ij} = w_{ji} wij=wji。此外,由于我们已经考虑了单独特征的影响,所以不需要将特征与其自身进行交叉,引入 x i 2 x_i^2 xi2项,从而 W \boldsymbol W W的对角线上元素都为 0 0 0。大家可以自行验证,形如 f ( x , y ) = x T A y f(\boldsymbol x, \boldsymbol y) = \boldsymbol x^\mathrm{T} \boldsymbol A \boldsymbol y f(x,y)=xTAy 的函数是双线性函数。双线性模型由于考虑了不同特征之间的关系,理论上比线性模型要更准确。然而,在实际应用中,该方法面临着稀疏特征的挑战。

  在用向量表示某一事物的离散特征时,一种常用的方法是独热编码(one-hot encoding)。这一方法中,向量的每一维都对应特征的一种取值,样本所具有的特征所在的维度值为1,其他维度为0。如图3所示,某物品的产地是北京、上海、广州、深圳其中之一,为了表示该物品的产地,我们将其编码为4维向量,4个维度依次对应产地北京、上海、广州、深圳。当物品产地为北京时,其特征向量就是 ( 1 , 0 , 0 , 0 ) (1,0,0,0) (1,0,0,0);物品产地为上海时,其特征向量就是 ( 0 , 1 , 0 , 0 ) (0,1,0,0) (0,1,0,0)。如果物品有多个特征,就把每个特征编码成的向量依次拼接起来,形成多域独热编码(multi-field one-hot encoding)。假如某种食品产地是上海、生产日期在2月份、食品种类是乳制品,那么它的编码就如图3所示。

在这里插入图片描述

图3 多域独热编码示意

  像这样的独热特征向量往往维度非常高,但只有少数几个位置是1,其他位置都是0,稀疏程度很高。当我们训练上述的模型时,需要对参数 w i j w_{ij} wij求导,结果为 ∂ y ^ ∂ w i j = x i x j \displaystyle \frac{\partial \hat y}{\partial w_{ij}} = x_ix_j wijy^=xixj。由于特征向量的稀疏性,大多数情况下都有 x i x j = 0 x_ix_j=0 xixj=0,无法对参数 w i j w_{ij} wij进行更新。为了解决这一问题,Steffen Rendle提出了因子分解机模型。该方法将权重矩阵 W \boldsymbol W W分解成 W = V V T \boldsymbol W = \boldsymbol V \boldsymbol V^\mathrm{T} W=VVT,其中 V ∈ R d × k \boldsymbol V \in \mathbb{R}^{d \times k} VRd×k。根据矩阵分解的相关理论,当 W \boldsymbol W W满足某些性质且 k k k足够大时,我们总可以找到分解矩阵 V \boldsymbol V V。即使条件不满足,我们也可以用近似分解 W ≈ V V T \boldsymbol W \approx \boldsymbol V\boldsymbol V^\mathrm{T} WVVT来代替。设 V \boldsymbol V V的行向量是 v 1 , … , v d \boldsymbol v_1, \ldots, \boldsymbol v_d v1,,vd,也即是对每个特征 x i x_i xi配一个 k k k维实数向量 v i \boldsymbol v_i vi,用矩阵乘法直接计算可以得到 w i j = ⟨ v i , v j ⟩ w_{ij} = \langle \boldsymbol v_i, \boldsymbol v_j \rangle wij=vi,vj,这样模型的预测函数可以写为 y ^ ( x ) = θ 0 + θ T x + ∑ i = 1 d − 1 ∑ j = i + 1 d ⟨ v i , v j ⟩ x i x j \hat y(\boldsymbol x) = \theta_0 + \boldsymbol\theta^\mathrm{T} \boldsymbol x + \sum_{i=1}^{d-1}\sum_{j=i+1}^d \langle \boldsymbol v_i, \boldsymbol v_j \rangle x_ix_j y^(x)=θ0+θTx+i=1d1j=i+1dvi,vjxixj 此时,再对参数 v s \boldsymbol v_s vs求梯度的结果为
∇ v s y ^ = ∇ v s ( ∑ i = 1 d − 1 ∑ j = i + 1 d ⟨ v i , v j ⟩ x i x j ) = ∇ v s ( ∑ j = s + 1 d ⟨ v s , v j ⟩ x s x j + ∑ i = 1 s − 1 ⟨ v i , v s ⟩ x i x s ) = x s ∑ j = s + 1 d x j v j + x s ∑ i = 1 s − 1 x i v i = x s ∑ i = 1 d x i v i − x s 2 v s \begin{aligned} \nabla_{\boldsymbol v_s} \hat y &= \nabla_{\boldsymbol v_s} \left(\sum_{i=1}^{d-1}\sum_{j=i+1}^d \langle \boldsymbol v_i, \boldsymbol v_j \rangle x_ix_j \right) \\ &= \nabla_{\boldsymbol v_s} \left( \sum_{j=s+1}^d \langle \boldsymbol v_s, \boldsymbol v_j\rangle x_sx_j + \sum_{i=1}^{s-1} \langle \boldsymbol v_i, \boldsymbol v_s \rangle x_ix_s \right) \\ &= x_s \sum_{j=s+1}^d x_j\boldsymbol v_j + x_s \sum_{i=1}^{s-1} x_i \boldsymbol v_i \\ &= x_s \sum_{i=1}^d x_i \boldsymbol v_i - x_s^2 \boldsymbol v_s \end{aligned} vsy^=vs(i=1d1j=i+1dvi,vjxixj)=vs(j=s+1dvs,vjxsxj+i=1s1vi,vsxixs)=xsj=s+1dxjvj+xsi=1s1xivi=xsi=1dxivixs2vs

  上面的计算过程中,为了简洁,我们采用了不太严谨的写法,当 s = 1 s=1 s=1 s = d s=d s=d 时会出现求和下界大于上界的情况。此时我们规定求和的结果为零。如果要完全展开,只需要做类似于 ∑ j = s + 1 d ⟨ v s , v j ⟩ x s x j \sum\limits_{j=s+1}^d \langle \boldsymbol v_s, \boldsymbol v_j \rangle x_sx_j j=s+1dvs,vjxsxj 变为 ∑ j = s d ⟨ v s , v j ⟩ x s x j − ⟨ v s , v s ⟩ x s 2 \sum\limits_{j=s}^d \langle \boldsymbol v_s, \boldsymbol v_j \rangle x_sx_j - \langle \boldsymbol v_s, \boldsymbol v_s \rangle x_s^2 j=sdvs,vjxsxjvs,vsxs2 的裂项操作即可。从该结果中可以看出,只要 x s ≠ 0 x_s \neq 0 xs=0,参数 v s \boldsymbol v_s vs的梯度就不为零,可以用梯度相关的算法对其更新。因此,即使特征向量 x \boldsymbol x x非常稀疏,FM模型也可以正常进行训练。

  至此,我们的模型还存在一个问题。双线性模型考虑不同特征之间乘积的做法,虽然提升了模型的能力,但也引入了额外的计算开销。对一个样本来说,线性模型需要计算 θ T x \boldsymbol\theta^\mathrm{T} \boldsymbol x θTx,时间复杂度为 O ( d ) O(d) O(d);而我们的模型需要计算每一对特征 ( x i , x j ) (x_i,x_j) (xi,xj)的乘积,以及参数 v i \boldsymbol v_i vi v j \boldsymbol v_j vj的内积,时间复杂度为 O ( k d 2 ) O(kd^2) O(kd2)。上面已经讲过,多热编码的特征向量维度常常特别高,因此这一时间开销是相当巨大的。但是,我们可以对改进后的预测函数 y ^ ( x ) = θ 0 + ∑ i = 1 d θ i x i + ∑ i = 1 d − 1 ∑ j = i + 1 d w i j x i x j \hat y(\boldsymbol x) = \theta_0 + \sum\limits_{i=1}^d \theta_i x_i + \sum\limits_{i=1}^{d-1}\sum\limits_{j=i+1}^d w_{ij}x_ix_j y^(x)=θ0+i=1dθixi+i=1d1j=i+1dwijxixj 中的最后一项做一些变形,改变计算顺序来降低时间复杂度。变形方式如下:
∑ i = 1 d − 1 ∑ j = i + 1 d ⟨ v i , v j ⟩ x i x j = 1 2 ( ∑ i = 1 d ∑ j = 1 d ⟨ v i , v j ⟩ x i x j − ∑ i = 1 d ⟨ v i , v i ⟩ x i 2 ) = 1 2 ( ∑ i = 1 d ∑ j = 1 d ⟨ x i v i , x j v j ⟩ − ∑ i = 1 d ⟨ x i v i , x i v i ⟩ ) = 1 2 ⟨ ∑ i = 1 d x i v i , ∑ j = 1 d x j v j ⟩ − 1 2 ∑ i = 1 d ⟨ x i v i , x i v i ⟩ = 1 2 ∑ l = 1 k ( ∑ i = 1 d v i l x i ) 2 − 1 2 ∑ l = 1 k ∑ i = 1 d v i l 2 x i 2 \begin{aligned} \sum_{i=1}^{d-1}\sum_{j=i+1}^d \langle \boldsymbol v_i, \boldsymbol v_j \rangle x_ix_j &= \frac{1}{2} \left(\sum_{i=1}^d\sum_{j=1}^d \langle \boldsymbol v_i, \boldsymbol v_j \rangle x_ix_j - \sum_{i=1}^d \langle \boldsymbol v_i, \boldsymbol v_i \rangle x_i^2 \right) \\ &= \frac{1}{2} \left(\sum_{i=1}^d\sum_{j=1}^d \langle x_i \boldsymbol v_{i}, x_j \boldsymbol v_{j} \rangle - \sum_{i=1}^d \langle x_i \boldsymbol v_i, x_i \boldsymbol v_i \rangle \right) \\ &= \frac12 \left\langle \sum_{i=1}^d x_i \boldsymbol v_i, \sum_{j=1}^d x_j \boldsymbol v_j\right\rangle - \frac12 \sum_{i=1}^d \langle x_i \boldsymbol v_i, x_i \boldsymbol v_i \rangle \\ &= \frac12 \sum_{l=1}^k \left(\sum_{i=1}^d v_{il}x_i \right)^2 - \frac12 \sum_{l=1}^k \sum_{i=1}^d v_{il}^2x_i^2 \end{aligned} i=1d1j=i+1dvi,vjxixj=21(i=1dj=1dvi,vjxixji=1dvi,vixi2)=21(i=1dj=1dxivi,xjvji=1dxivi,xivi)=21i=1dxivi,j=1dxjvj21i=1dxivi,xivi=21l=1k(i=1dvilxi)221l=1ki=1dvil2xi2

  在变形的第二步和第三步,我们利用了向量内积的双线性性质,将标量 x i , x j x_i, x_j xi,xj以及求和都移到内积中去。最后的结果中只含有两重求和,外层为 k k k次,内层为 d d d次,因此整体的时间复杂度为 O ( k d ) O(kd) O(kd)。这样,FM的时间复杂度关于特征规模 d d d的增长从平方变为线性,得到了大幅优化。至此,FM的预测公式为 y ^ ( x ) = θ 0 + ∑ i = 1 d θ i x i + 1 2 ∑ l = 1 k ( ( ∑ i = 1 d v i l x i ) 2 − ∑ i = 1 d v i l 2 x i 2 ) \hat y(\boldsymbol x) = \theta_0 + \sum_{i=1}^d \theta_i x_i + \frac12 \sum_{l=1}^k \left(\left(\sum_{i=1}^d v_{il}x_i \right)^2 - \sum_{i=1}^d v_{il}^2 x_i^2 \right) y^(x)=θ0+i=1dθixi+21l=1k (i=1dvilxi)2i=1dvil2xi2 如果要做分类任务,只需要再加上softmax函数即可。

  在上面的模型中,我们只考虑了两个特征之间的组合,因此该FM也被称为二阶FM。如果进一步考虑多个特征的组合,如 x i x j x k x_ix_jx_k xixjxk,就可以得到高阶的FM模型。由于高阶FM较为复杂,并且也不再是双线性模型,本文在此略去,如果感兴趣可以自行查阅相关资料。

四、动手实现因子分解机

  下面,我们来动手实现二阶FM模型。本节采用的数据集是为FM制作的示例数据集fm_dataset.csv,包含了某个用户浏览过的商品的特征,以及用户是否点击过这个商品。数据集的每一行包含一个商品,前24列是其特征,最后一列是0或1,分别表示用户没有或有点击该商品。我们的目标是根据输入特征预测用户在测试集上的行为,是一个二分类问题。我们先导入必要的模块和数据集并处理数据,将其划分为训练集和测试集。

import numpy as np
import matplotlib.pyplot as plt
from sklearn import metrics # sklearn中的评价指标函数库
from tqdm import tqdm# 导入数据集
data = np.loadtxt('fm_dataset.csv', delimiter=',')# 划分数据集
np.random.seed(0)
ratio = 0.8
split = int(ratio * len(data))
x_train = data[:split, :-1]
y_train = data[:split, -1]
x_test = data[split:, :-1]
y_test = data[split:, -1]
# 特征数
feature_num = x_train.shape[1]
print('训练集大小:', len(x_train))
print('测试集大小:', len(x_test))
print('特征数:', feature_num)

在这里插入图片描述

  然后,我们将FM模型定义成类。与MF相同,我们在类中实现预测和梯度更新方法。

class FM:def __init__(self, feature_num, vector_dim):# vector_dim代表公式中的k,为向量v的维度self.theta0 = 0.0 # 常数项self.theta = np.zeros(feature_num) # 线性参数self.v = np.random.normal(size=(feature_num, vector_dim)) # 双线性参数self.eps = 1e-6 # 精度参数def _logistic(self, x):# 工具函数,用于将预测转化为概率return 1 / (1 + np.exp(-x))def pred(self, x):# 线性部分linear_term = self.theta0 + x @ self.theta# 双线性部分square_of_sum = np.square(x @ self.v)sum_of_square = np.square(x) @ np.square(self.v)# 最终预测y_pred = self._logistic(linear_term + 0.5 * np.sum(square_of_sum - sum_of_square, axis=1))# 为了防止后续梯度过大,对预测值进行裁剪,将其限制在某一范围内y_pred = np.clip(y_pred, self.eps, 1 - self.eps)return y_preddef update(self, grad0, grad_theta, grad_v, lr):self.theta0 -= lr * grad0self.theta -= lr * grad_thetaself.v -= lr * grad_v

  对于分类任务,我们仍用MLE作为训练时的损失函数。在测试集上,我们采用AUC作为评价指标。由于我们在逻辑斯谛回归中已经动手实现过AUC,简单起见,这里我们就直接使用sklearn中的函数直接计算AUC。我们用SGD进行参数更新,训练完成后,我们把训练过程中的准确率和AUC绘制出来。

# 超参数设置,包括学习率、训练轮数等
vector_dim = 16
learning_rate = 0.01
lbd = 0.05
max_training_step = 200
batch_size = 32# 初始化模型
np.random.seed(0)
model = FM(feature_num, vector_dim)train_acc = []
test_acc = []
train_auc = []
test_auc = []with tqdm(range(max_training_step)) as pbar:for epoch in pbar:st = 0while st < len(x_train):ed = min(st + batch_size, len(x_train))X = x_train[st: ed]Y = y_train[st: ed]st += batch_size# 计算模型预测y_pred = model.pred(X)# 计算交叉熵损失cross_entropy = -Y * np.log(y_pred) - (1 - Y) * np.log(1 - y_pred)loss = np.sum(cross_entropy)# 计算损失函数对y的梯度,再根据链式法则得到总梯度grad_y = (y_pred - Y).reshape(-1, 1)# 计算y对参数的梯度# 常数项grad0 = np.sum(grad_y * (1 / len(X) + lbd))# 线性项grad_theta = np.sum(grad_y * (X / len(X) + lbd * model.theta), axis=0)# 双线性项grad_v = np.zeros((feature_num, vector_dim))for i, x in enumerate(X):# 先计算sum(x_i * v_i)xv = x @ model.vgrad_vi = np.zeros((feature_num, vector_dim))for s in range(feature_num):grad_vi[s] += x[s] * xv - (x[s] ** 2) * model.v[s]grad_v += grad_y[i] * grad_vigrad_v = grad_v / len(X) + lbd * model.vmodel.update(grad0, grad_theta, grad_v, learning_rate)pbar.set_postfix({'训练轮数': epoch,'训练损失': f'{loss:.4f}','训练集准确率': train_acc[-1] if train_acc else None,'测试集准确率': test_acc[-1] if test_acc else None})# 计算模型预测的准确率和AUC# 预测准确率,阈值设置为0.5y_train_pred = (model.pred(x_train) >= 0.5)acc = np.mean(y_train_pred == y_train)train_acc.append(acc)auc = metrics.roc_auc_score(y_train, y_train_pred) # sklearn中的AUC函数train_auc.append(auc)y_test_pred = (model.pred(x_test) >= 0.5)acc = np.mean(y_test_pred == y_test)test_acc.append(acc)auc = metrics.roc_auc_score(y_test, y_test_pred) test_auc.append(auc)print(f'测试集准确率:{test_acc[-1]},\t测试集AUC:{test_auc[-1]}')

在这里插入图片描述

  最后,我们把训练过程中在训练集和测试集上的精确率和AUC绘制出来,观察训练效果。

# 绘制训练曲线            
plt.figure(figsize=(13, 5))
x_plot = np.arange(len(train_acc)) + 1plt.subplot(121)
plt.plot(x_plot, train_acc, color='blue', label='train acc')
plt.plot(x_plot, test_acc, color='red', ls='--', label='test acc')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()plt.subplot(122)
plt.plot(x_plot, train_auc, color='blue', label='train AUC')
plt.plot(x_plot, test_auc, color='red', ls='--', label='test AUC')
plt.xlabel('Epoch')
plt.ylabel('AUC')
plt.legend()
plt.show()

在这里插入图片描述

五、拓展:概率矩阵分解

  概率矩阵分解(probabilistic matrix factorization,PMF)是另一种常用的双线性模型。与矩阵分解模型不同,它对用户给电影的评分 r i j r_{ij} rij的分布进行了先验假设,认为其满足正态分布: r i j ∼ N ( p i T q j , σ 2 ) r_{ij} \sim \mathcal{N}(\boldsymbol p_i^\mathrm{T} \boldsymbol q_j, \sigma^2) rijN(piTqj,σ2) 其中 σ 2 \sigma^2 σ2是正态分布的方差,与用户和电影无关。注意, p i \boldsymbol p_i pi q j \boldsymbol q_j qj都是未知的。记 I i j = I ( r i j 存在 ) I_{ij} = \mathbb{I}(r_{ij} \text{存在}) Iij=I(rij存在),即当用户 i i i对电影 j j j打过分时 I i j = 1 I_{ij}=1 Iij=1,否则 I i j = 0 I_{ij}=0 Iij=0。再假设不同的评分采样之间互相独立,那么,我们观测到的 R \boldsymbol R R出现的概率是 P ( R ∣ P , Q , σ ) = ∏ i = 1 N ∏ j = 1 M p N ( r i j ∣ p i T q j , σ 2 ) I i j P(\boldsymbol R | \boldsymbol P, \boldsymbol Q, \sigma) = \prod_{i=1}^N\prod_{j=1}^M p_\mathcal{N}(r_{ij}| \boldsymbol p_i^\mathrm{T} \boldsymbol q_j, \sigma^2)^{I_{ij}} P(RP,Q,σ)=i=1Nj=1MpN(rijpiTqj,σ2)Iij 这里,我们用 p N ( x ∣ μ , σ 2 ) p_\mathcal{N}(x|\mu,\sigma^2) pN(xμ,σ2) 表示正态分布 N ( μ , σ 2 ) \mathcal{N}(\mu, \sigma^2) N(μ,σ2) 的概率密度函数,其完整表达式为 p N ( x ∣ μ , σ 2 ) = 1 2 π σ 2 e − ( x − μ ) 2 2 σ 2 p_\mathcal{N}(x|\mu,\sigma^2) = \frac{1}{\sqrt{2\pi\sigma^2}}\text{e}^{-\frac{(x-\mu)^2}{2\sigma^2}} pN(xμ,σ2)=2πσ2 1e2σ2(xμ)2 对于那些空缺的 r i j r_{ij} rij,由于 I i j = 0 I_{ij}=0 Iij=0 p N ( r i j ∣ p i T q j , σ 2 ) I i j = 1 p_\mathcal{N}(r_{ij}|\boldsymbol p_i^\mathrm{T} \boldsymbol q_j, \sigma^2)^{I_{ij}}=1 pN(rijpiTqj,σ2)Iij=1,对连乘没有贡献,最终的概率只由已知部分计算得出。接下来,我们进一步假设用户的喜好 p i \boldsymbol p_i pi和电影的特征 q j \boldsymbol q_j qj都满足均值为 0 \boldsymbol 0 0的正态分布,协方差矩阵分别为 σ P 2 I \sigma_P^2\boldsymbol I σP2I σ Q 2 I \sigma_Q^2 \boldsymbol I σQ2I,即 P ( P ∣ σ P ) = ∏ i = 1 N p N ( p i ∣ 0 , σ P 2 I ) , P ( Q ∣ σ Q ) = ∏ j = 1 M p N ( q j ∣ 0 , σ Q 2 I ) P(\boldsymbol P | \sigma_P) = \prod_{i=1}^N p_\mathcal{N}(\boldsymbol p_i| \boldsymbol 0, \sigma_P^2 \boldsymbol I), \quad P(\boldsymbol Q | \sigma_Q) = \prod_{j=1}^M p_\mathcal{N}(\boldsymbol q_j | \boldsymbol 0, \sigma_Q^2 \boldsymbol I) P(PσP)=i=1NpN(pi0,σP2I),P(QσQ)=j=1MpN(qj0,σQ2I) 根据全概率公式 P ( X , Y ) = P ( X ∣ Y ) P ( Y ) P(X,Y) = P(X|Y)P(Y) P(X,Y)=P(XY)P(Y),并注意到 R \boldsymbol R R σ P , σ Q \sigma_P, \sigma_Q σP,σQ无关,我们可以计算出 P \boldsymbol P P Q \boldsymbol Q Q的后验概率为
P ( P , Q ∣ R , σ , σ P , σ Q ) = P ( P , Q , R , σ , σ P , σ Q ) P ( R , σ , σ P , σ Q ) = P ( R ∣ P , Q , σ ) P ( P , Q ∣ σ P , σ Q ) P ( σ , σ P , σ Q ) P ( R , σ , σ P , σ Q ) = C ⋅ P ( R ∣ P , Q , σ ) P ( P ∣ σ P ) P ( Q ∣ σ Q ) = C ∏ i = 1 N ∏ j = 1 M p N ( r i j ∣ p i T q j , σ 2 ) I i j ⋅ ∏ i = 1 N p N ( p i ∣ 0 , σ P 2 I ) ⋅ ∏ j = 1 M p N ( q j ∣ 0 , σ Q 2 I ) \small\begin{aligned} P(\boldsymbol P, \boldsymbol Q | \boldsymbol R, \sigma, \sigma_P, \sigma_Q) &= \frac{P(\boldsymbol P, \boldsymbol Q, \boldsymbol R, \sigma, \sigma_P, \sigma_Q)}{P(\boldsymbol R, \sigma, \sigma_P, \sigma_Q)} \\[2ex] &= \frac{P(\boldsymbol R | \boldsymbol P, \boldsymbol Q, \sigma)P(\boldsymbol P, \boldsymbol Q | \sigma_P, \sigma_Q) P(\sigma, \sigma_P, \sigma_Q)}{P(\boldsymbol R, \sigma, \sigma_P, \sigma_Q)} \\[2ex] &= C \cdot P(\boldsymbol R | \boldsymbol P, \boldsymbol Q, \sigma)P(\boldsymbol P|\sigma_P)P(\boldsymbol Q|\sigma_Q) \\ &= C\prod_{i=1}^N\prod_{j=1}^M p_\mathcal{N}(r_{ij}| \boldsymbol p_i^\mathrm{T} \boldsymbol q_j, \sigma^2)^{I_{ij}} \cdot \prod_{i=1}^N p_\mathcal{N}(\boldsymbol p_i| \boldsymbol 0, \sigma_P^2 \boldsymbol I) \cdot \prod_{j=1}^M p_\mathcal{N}(\boldsymbol q_j | \boldsymbol 0, \sigma_Q^2 \boldsymbol I) \end{aligned} P(P,QR,σ,σP,σQ)=P(R,σ,σP,σQ)P(P,Q,R,σ,σP,σQ)=P(R,σ,σP,σQ)P(RP,Q,σ)P(P,QσP,σQ)P(σ,σP,σQ)=CP(RP,Q,σ)P(PσP)P(QσQ)=Ci=1Nj=1MpN(rijpiTqj,σ2)Iiji=1NpN(pi0,σP2I)j=1MpN(qj0,σQ2I) 其中 C C C是常数。为了简化这一表达式,我们利用与MLE中相同的技巧,将上式取对数,从而把连乘变为求和:
log ⁡ P ( P , Q ∣ R , σ , σ P , σ Q ) = ∑ i = 1 N ∑ j = 1 M I i j log ⁡ p N ( r i j ∣ p i T q j , σ 2 ) + ∑ i = 1 N log ⁡ p N ( p i ∣ 0 , σ P 2 I ) + ∑ j = 1 M log ⁡ p N ( q j ∣ 0 , σ Q 2 I ) + log ⁡ C \begin{aligned} \log P(\boldsymbol P, \boldsymbol Q | \boldsymbol R, \sigma, \sigma_P, \sigma_Q) &= \sum_{i=1}^N\sum_{j=1}^M I_{ij} \log p_\mathcal{N}(r_{ij} | \boldsymbol p_i^\mathrm{T} \boldsymbol q_j, \sigma^2) + \sum_{i=1}^N \log p_\mathcal{N}(\boldsymbol p_i| \boldsymbol 0, \sigma_P^2 \boldsymbol I) \\ &\quad+ \sum_{j=1}^M \log p_\mathcal{N}(\boldsymbol q_j | \boldsymbol 0, \sigma_Q^2 \boldsymbol I) + \log C \end{aligned} logP(P,QR,σ,σP,σQ)=i=1Nj=1MIijlogpN(rijpiTqj,σ2)+i=1NlogpN(pi0,σP2I)+j=1MlogpN(qj0,σQ2I)+logC 再代入 p N p_\mathcal{N} pN取对数后的表达式 log ⁡ p N ( x ∣ μ , σ 2 ) = − 1 2 log ⁡ ( 2 π σ 2 ) − ( x − μ ) 2 2 σ 2 \log p_\mathcal{N}(x|\mu, \sigma^2) = -\frac12 \log (2\pi\sigma^2) - \frac{(x-\mu)^2}{2\sigma^2} logpN(xμ,σ2)=21log(2πσ2)2σ2(xμ)2 计算得到
log ⁡ P ( P , Q ∣ R , σ , σ P , σ Q ) = − 1 2 log ⁡ ( 2 π σ 2 ) ∑ i = 1 N ∑ j = 1 M I i j − 1 2 σ 2 ∑ i = 1 N ∑ j = 1 M I i j ( r i j − p i T q j ) 2 − N d 2 log ⁡ ( 2 π σ P 2 ) − 1 2 σ P 2 ∑ i = 1 N p i T p i − M d 2 log ⁡ ( 2 π σ Q 2 ) − 1 2 σ Q 2 ∑ j = 1 M q j T q j + log ⁡ C = − 1 σ 2 [ 1 2 ∑ i = 1 N ∑ j = 1 M I i j ( r i j − p i T q j ) 2 + λ P 2 ∥ P ∥ F 2 + λ Q 2 ∥ Q ∥ F 2 ] + C 1 \small\begin{aligned} \log P(\boldsymbol P, \boldsymbol Q | \boldsymbol R, \sigma, \sigma_P, \sigma_Q) &= -\frac12 \log(2\pi\sigma^2) \sum_{i=1}^N\sum_{j=1}^M I_{ij} - \frac{1}{2\sigma^2}\sum_{i=1}^N\sum_{j=1}^M I_{ij}(r_{ij} - \boldsymbol p_i^\mathrm{T} \boldsymbol q_j)^2 \\ &\quad-\frac{Nd}{2} \log(2\pi\sigma_P^2) - \frac{1}{2\sigma_P^2}\sum_{i=1}^N \boldsymbol p_i^\mathrm{T} \boldsymbol p_i \\ &\quad-\frac{Md}{2} \log(2\pi\sigma_Q^2) - \frac{1}{2\sigma_Q^2}\sum_{j=1}^M \boldsymbol q_j^\mathrm{T} \boldsymbol q_j + \log C \\ &= -\frac{1}{\sigma^2} \left[\frac12 \sum_{i=1}^N\sum_{j=1}^M I_{ij}(r_{ij} - \boldsymbol p_i^\mathrm{T} \boldsymbol q_j)^2 + \frac{\lambda_P}{2} \lVert \boldsymbol P \lVert_F^2 + \frac{\lambda_Q}{2} \lVert \boldsymbol Q \lVert_F^2 \right] + C_1 \end{aligned} logP(P,QR,σ,σP,σQ)=21log(2πσ2)i=1Nj=1MIij2σ21i=1Nj=1MIij(rijpiTqj)22Ndlog(2πσP2)2σP21i=1NpiTpi2Mdlog(2πσQ2)2σQ21j=1MqjTqj+logC=σ21[21i=1Nj=1MIij(rijpiTqj)2+2λPPF2+2λQQF2]+C1 其中, λ P = σ 2 / σ P 2 \lambda_P = \sigma^2/\sigma_P^2 λP=σ2/σP2 λ Q = σ 2 / σ Q 2 \lambda_Q = \sigma^2 / \sigma_Q^2 λQ=σ2/σQ2 C 1 C_1 C1是与参数 P \boldsymbol P P Q \boldsymbol Q Q无关的常数。根据最大似然的思想,我们应当最大化上面计算出的对数概率。因此,定义损失函数为 J ( P , Q ) = 1 2 ∑ i = 1 N ∑ j = 1 M I i j ( r i j − p i T q j ) 2 + λ P 2 ∥ P ∥ F 2 + λ Q 2 ∥ Q ∥ F 2 J(\boldsymbol P, \boldsymbol Q) = \frac12 \sum_{i=1}^N\sum_{j=1}^M I_{ij}(r_{ij} - \boldsymbol p_i^\mathrm{T} \boldsymbol q_j)^2 + \frac{\lambda_P}{2} \lVert \boldsymbol P \lVert_F^2 + \frac{\lambda_Q}{2} \lVert \boldsymbol Q \lVert_F^2 J(P,Q)=21i=1Nj=1MIij(rijpiTqj)2+2λPPF2+2λQQF2 于是,最大化对数概率就等价于最小化损失函数 J ( P , Q ) J(\boldsymbol P, \boldsymbol Q) J(P,Q)。并且,这一损失函数恰好为目标值 r i j r_{ij} rij与参数内积 p i T q i \boldsymbol p_i^\mathrm{T} \boldsymbol q_i piTqi之间的平方损失,再加上 L 2 L_2 L2正则化的形式。由于向量内积是双线性函数,PMF模型也属于双线性模型的一种。

  将损失函数对 p i \boldsymbol p_i pi求导,得到 ∇ p i J ( P , Q ) = ∑ j = 1 M I i j ( r i j − p i T q j ) q j − λ P p i \nabla_{\boldsymbol p_i} J(\boldsymbol P, \boldsymbol Q) = \sum_{j=1}^M I_{ij}(r_{ij} - \boldsymbol p_i^\mathrm{T} \boldsymbol q_j) \boldsymbol q_j - \lambda_P \boldsymbol p_i piJ(P,Q)=j=1MIij(rijpiTqj)qjλPpi 令梯度为零,解得 p i = ( ∑ j = 1 M I i j q j q j T + λ P I ) − 1 ( ∑ j = 1 M I i j r i j q j ) \boldsymbol p_i = \left(\sum_{j=1}^MI_{ij}\boldsymbol q_j\boldsymbol q_j^\mathrm{T} + \lambda_P \boldsymbol I\right)^{-1} \left(\sum_{j=1}^M I_{ij}r_{ij}\boldsymbol q_j\right) pi=(j=1MIijqjqjT+λPI)1(j=1MIijrijqj)

  在正则化约束一节中我们讲过,根据矩阵相关的理论,只要 λ P \lambda_P λP足够大,上式的第一项逆矩阵就总是存在。同理,对 q j \boldsymbol q_j qj也有类似的结果。因此,我们可以通过如上形式的 J ( P , Q ) J(\boldsymbol P, \boldsymbol Q) J(P,Q)来求解参数 P \boldsymbol P P Q \boldsymbol Q Q。在参数的高斯分布假设下,我们自然导出了带有 L 2 L_2 L2正则化的MF模型,这并不是偶然。我们会在概率图模型中进一步阐释其中的原理。

:以上文中的数据集及相关资源下载地址:
链接:https://pan.quark.cn/s/0f31109b2b13
提取码:gTBK

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/410990.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[HZNUCTF 2023 preliminary]flask

[HZNUCTF 2023 preliminary]flask 点开之后页面如图所示&#xff1a; 猜测是SSTI模板注入&#xff0c;先输入{7*7},发现模板是倒序输入的&#xff1a; 然后我们输入}}‘7’*7{{返回777777&#xff0c;这是jinja2模板&#xff1a; 我们需要让用户输入一个字符串&#xff0c;对其…

VS2022 QT环境显示中文乱码问题

1.问题描述 在VS2022中搭配QT6.2环境&#xff0c;在文本处设置中文&#xff0c;运行程序文本处显示乱码&#xff0c;未成功显示想要的中文。 2.VS2015解决方案 如果是VS2015的话&#xff0c;直接文件->高级保存选项可以设置编码格式。 修改编码格式如图所示&#xff1a;…

C语言迷宫制造

目录 开头程序程序的流程图程序的效果我推荐要制造的迷宫下一篇博客要讲的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #include <string.h> void printmaze(const cha…

基于多案例全流程防洪评价报告编制方法与水流数学模型建模

《防洪评价报告编制导则解读河道管理范围内建设项目编制导则》&#xff08;SL/T808- 2021&#xff09;解读 编制导则解读 1.规范适用范围&#xff1b; 2.规范名词概念解读&#xff1b; 3.防洪评价计算类别及分析主要内容&#xff1b; 4.消除和减轻影响措施&#xff1b; 5.…

使用 pg_profile 在 Postgres 中生成性能报告

使用 pg_profile 在 Postgres 中生成性能报告 下载安装 pg_profile 从github链接下载pg_profile: https://github.com/zubkov-andrei/pg_profile/releases 解压缩到以下位置 # cd $PGHOME/share/extension/ # su - postgress $ psql -h 127.0.0.1 -d postgres -U postgress p…

Improving Language Understandingby Generative Pre-Training

摘要 自然语言理解包括各种各样的任务&#xff0c;如文本蕴涵、问题回答、语义相似性评估和文档分类。尽管大量未标记的文本语料库丰富&#xff0c;但用于学习这些特定任务的标记数据很少&#xff0c;这使得判别训练模型难以充分执行。我们证明&#xff0c;通过在不同的未标记…

筛质数(线性筛法)

线性筛法&#xff1a; 假设有一个非质数 x&#xff0c;那么这个数可以被表示为一个最小质因数和一个因子相乘的形式 如 x 12 &#xff0c;那么 x 2*6 其中&#xff1a;2 就是 12 的最小质因数&#xff0c; 6 就是另一个因子 线性筛法就是利用每个数的最小质因数筛掉这个非…

解决idea始终无法导入本地jar包

问题描述 maven刷新也没有用 解决&#xff1a; 找到本地Maven仓库的jar包手动引入 之后。导入成功

《黑神话:悟空》研发公司的薪资水平

作者&#xff1a;程序员晓凡 最近全网最火爆的要属《黑神话&#xff1a;悟空》了&#xff0c;即便是我这个平时不沾游戏、不追直播的人&#xff0c;也看直播看得津津有味。 一、销量与热度背后 首先&#xff0c;让我们来看看那些令人瞩目的数字。《黑神话&#xff1a;悟空》…

VBA技术资料MF194:屏蔽右键菜单

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

uniapp(微信小程序如何使用单选框、复选框)

一、先看效果 二、数据结构 说明&#xff1a;selected用来记录每次用户选择的值&#xff0c;当是单选的时候属性中的selected属性需要设置成字符串&#xff0c;当是复选框的时候&#xff0c;此时选择的是数组&#xff0c;selected属性应设置为数组。type用来区分当前是单选还是…

Wordpress 6.x 修改文件上传大小限制

1. 安装并启用Big File Uploads插件 插件 → 安装新插件 媒体→添加文件 修改后Save保存 2. 修改Nginx配置文件 # 我的配置在wordpress.conf文件中 vim /etc/nginx/conf.d/wordpress.conf 在server节点中加入下面这句配置 # 文件上传大小限制 client_max_body_size 500M;# 重…

xss-labs通关攻略 11-15关

第十一关&#xff1a;less-11 步骤一&#xff1a;利用burp抓包 步骤二&#xff1a;添加referer:click me!" type"button" οnmοuseοver"alert(/xss/)进行放包 第十二关&#xff1a;less-12 步骤一&#xff1a;利用burp抓包 步骤二&#xff1a;修改User A…

java 中的设计模式

文章目录 一、前言二、设计模式的分类三、设计模式的原则1、开闭原则&#xff08;Open Close Principle&#xff09;2、里氏代换原则&#xff08;Liskov Substitution Principle&#xff09;3、依赖倒转原则&#xff08;Dependence Inversion Principle&#xff09;4、接口隔离…

【计算机系统架构】从0开始构建一台现代计算机|时序逻辑、主存储器|第2章

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; Yaoyao2024往期回顾&#xff1a; 【计算机系统架构】从0开始构建一台现代计算机|二进制、布尔运算和ALU|第2章每日一言&#x1f33c;: 孤独和喧嚣都令人难以忍受。如果一定要忍…

参会投稿 | 第三届先进传感与智能制造国际学术会议(ASIM 2024)

第三届先进传感与智能制造国际会议&#xff08;The 3rd International Conference on Advanced Sensing, Intelligent Manufacturing&#xff09;&#xff0c;由江汉大学、西安交通大学和山东大学主办&#xff0c;由江西省机械工程学会、东华理工大学机械与电子工程学院等联合协…

iPhone突然黑屏?别慌,这里有你的自救指南

在日常使用iPhone的过程中&#xff0c;不少用户可能会遇到手机突然黑屏的情况&#xff0c;这往往让人措手不及。别担心&#xff0c;今天我们就来详细探讨一下iPhone突然黑屏的可能原因及解决方法&#xff0c;帮助你快速恢复手机的正常使用。 一、iPhone突然黑屏的可能原因 1. …

IPD+敏捷高级训战盛典(上海站)圆满落幕,引领创新管理新纪元

在金秋送爽的八月末&#xff0c;翰德恩咨询精心筹备的为期两日的“IPD敏捷高级训战盛典&#xff08;上海站&#xff09;”于24日至25日璀璨落幕&#xff0c;标志着一场汇聚行业精英、融合前沿管理智慧的盛宴圆满收官。此次盛典不仅是知识的碰撞与交融&#xff0c;更是企业转型升…

序列化组件对比

1、msgpack介绍 1.MsgPack产生的数据更小&#xff0c;从而在数据传输过程中网络压力更小 2.MsgPack兼容性差&#xff0c;必须按照顺序保存字段 3.MsgPack是二进制序列化格式&#xff0c;兼容跨语言 官网地址&#xff1a; https://msgpack.org/ 官方介绍&#xff1a;Its lik…

ST表(区间查询

解决的问题&#xff1a; 数组区间查询最大值和最小值对于解决数组的树状数组的区间修改 ------- 线段树倍增思想 核心代码&#xff1a; #include<bits/stdc.h> using namespace std; const int N1e5; int num[N]; int f[N][N]; int main(){int n;cin>>n;//输入默…