CF在推荐系统的发展中扮演着举足轻重的角色,虽然已经被使用很长时间了,但依旧经久不衰。在各大互联网公司都保存着CF的使用,而气召回的效果也一直都很好。
本文主要是描述一下狭义上的Item CF和广义上的Item CF,欢迎拍砖!
狭义上的Item CF
1.1 ItemCF 的原理
ItemCF是基于Item的协同过滤(Collaboration Filter)算法,它是通过分析用户的行为来计算Item的相似度。与基于内容计算相似、一些embedding方法相比,itemcf中增加了用户的行为,在线上效果表现会比较好。
该算法认为物品A和物品B相似的依据是因为喜欢物品A的用户也喜欢物品B。
下表是一个简易的原始数据集,也称之为User-Item表,即用户-物品列表,记录了每个用户喜爱的物品,数据表格如下:
用户 | 喜爱的物品 |
---|---|
A | {a,b,d} |
B | {b,c,e} |
C | {c,d} |
D | {b,c,d} |
E | {a,d} |
针对用户A构建物品共现矩阵
上图是用户 A A A 的共现矩阵 C C C , C [ i ] [ j ] C[i][j] C[i][j] 代表的含义是同时喜欢物品 i i i 和物品 j j j 的用户数量。举个例子, C [ a ] [ b ] = 1 C[a][b]=1 C[a][b]=1 代表的含义就是同时喜欢物品 a a a 和物品 b b b 的用户有一个,就是用户A。
正常情况下,需要对每个用户构建一个共现矩阵,然后进行叠加,如下所示:
通过对不同用户的喜爱物品集合构成的共现矩阵进行累加,最终得到了上图中的矩阵 C C C,这其实就是相似度公式中的分子部分。
但是在实际操作中,并不会这样操作,而是通过Map这样的数据结构进行累加,继而得到最终的共现矩阵。
比如计算物品
a
a
a 与物品
b
b
b 的相似度:
W
a
b
=
C
[
a
]
[
b
]
N
(
a
)
∗
N
(
b
)
=
1
6
≈
0.41
W_{ab} = \frac{C[a][b]}{\sqrt{ N(a) * N(b) }} = \frac{1}{\sqrt{6}}\approx0.41
Wab=N(a)∗N(b)C[a][b]=61≈0.41
其中:
- 分子表示的是物品 a a a 与物品 b b b 的共现次数
- 分母中 N ( a ) N(a) N(a) 表示的是对喜欢物品 a a a 的人数, N ( b ) N(b) N(b) 表示喜欢物品 b b b 的人数
1.2 相似度算法的改进
在协同过滤中两个物品产生相似度是因为它们共同出现在很多用户的兴趣列表中。换句话说,每个用户的兴趣列表都对物品的相似度产生贡献。那么是不是每个用户的贡献都相同呢?
John S. Breese在论文【Empirical Analysis of Predictive Algorithms for Collaborative Filtering,论文地址】中提出了IUF(Inverse User Frequence),即用户活跃度对数的倒数的参数,他认为活跃用户对物品相似度的贡献应该小于不活跃的用户,他提出应该增加IUF 参数来修正物品相似度的计算公式(这个思想和TF-IDF中的IDF思想是一致的):
W
a
b
=
∑
u
∈
N
(
a
)
⋂
N
(
b
)
1
l
o
g
(
1
+
N
(
u
)
)
N
(
a
)
∗
N
(
b
)
W_{ab}=\frac{ \sum_{u \in N(a) \bigcap N(b) } \frac{1}{ log(1 + N(u))} }{\sqrt{N(a)*N(b)}}
Wab=N(a)∗N(b)∑u∈N(a)⋂N(b)log(1+N(u))1
1.3 相似度归一化
George Karypis 在论文【Evaluation of Item-based Top-N Recommendation Algorithms,论文地址】中提到如果将ItemCF的相似度矩阵按最大值归一化,可以提高推荐的准确率(注意这里的
m
a
x
j
max_j
maxj 表达的是其相似item
j
j
j 最大值的下标)
w
i
j
′
=
w
i
j
m
a
x
j
w
i
j
w_{ij}' = \frac{w_{ij}}{ max_j w_{ij}}
wij′=maxjwijwij
实验表明,归一化的好处不仅仅在于增强推荐的准确度,还可以提高推荐的覆盖率和多样性。
假设物品分为两类——A和B, A类物品之间的相似度为0.5, B类物品之间的相似度为0.6,而A类物品和B类物品之间的相似度是0.2。在这种情况下,如果一个用户喜欢了5个A类物品和5个B类物品,用ItemCF给他进行推荐,推荐的就都是B类物品,因为B类物品之间的相似度大。但如果归一化之后, A类物品之间的相似度变成了1, B类物品之间的相似度也是1,那么这种情况下,用户如果喜欢5个A类物品和5个B类物品,那么他的推荐列表中A类物品和B类物品的数目也应该是大致相等的。
1.4 UserCF 与 ItemCF的对比
广义上的ItemCF
广义上的ItemCF并不局限于传统的基于Item的协同过滤,而是涉及基于 < u s e r , i t e m > <user, item> <user,item> 行为计算item相似度的一系列算法,其中主要包含两大类:
- 对“狭义”上的ItemCF的优化
- ItemCF的延伸
1 对ItemCF的优化
除了上面提到的两点:相似度计算的改进、相似度归一化之外,还包括下面这些
1.1 基于时间权重的优化
用户对Item产生行为的时间是不一样的,通常情况下使用itemcf并没有对不同时间行为进行时间衰减。因此一种常见的思路就是:在计算item
a
,
b
a,b
a,b 相似度时进行时间衰减
W
a
b
=
∑
u
∈
N
(
a
)
⋂
N
(
b
)
f
(
∣
t
u
a
−
t
u
b
∣
)
N
(
a
)
∗
N
(
b
)
W_{ab}=\frac{ \sum_{u \in N(a) \bigcap N(b) } f(|t_{ua} - t_{ub}|)} {\sqrt{N(a)*N(b)}}
Wab=N(a)∗N(b)∑u∈N(a)⋂N(b)f(∣tua−tub∣)
其中
f
f
f 函数的表达式为:
f
(
∣
t
u
a
−
t
u
b
∣
)
=
1
1
+
α
∣
t
u
a
−
t
u
b
∣
f(|t_{ua} - t_{ub}|) = \frac{1}{1+\alpha |t_{ua} - t_{ub}|}
f(∣tua−tub∣)=1+α∣tua−tub∣1
其中
t
u
a
t_{ua}
tua 表示的是用户
u
u
u 对物品
a
a
a 的行为时间。
1.2 基于session的优化
基于session的优化主要考虑的是用户行为的连贯性,因为用户可能在不同的时间段内有不同的偏好行为,比如用户 T T T 时间段搜索的是书籍相关的东西,而在 T + 1 T+1 T+1 时间段可能考虑的是电子数码相关的物品
这里的session可以根据具体的业务场景进行不同的定义,常见的定义方式有以下几种:
- 用户一次会话称为一次session,即用户的一次会话过程
- 按照固定的时间段去划分,比如按天划分,用户一天内的行为是一个session
- 按照间断时间去划分,比如用户的一次会话中,如果两次行为的时间超过30分钟,可以划分为两个session
- 按照行为划分,比如用户构建用户点击+下单序列,按照时间排序后, 可以根据下单进行截断
一个session内的行为是高度相关的,所以可以利用session id代替userid进行item相似度的计算。但是进行session行为划分的前提是用户的行为比较丰富,对于产品初期的话不太建议使用。
session在一些graph、dnn、序列算法中应用的比较多,比如:
- 2018年阿里发表的论文:Billion-scale Commodity Embedding for E-commerce Recommendation in Alibaba(论文地址),中在构建用户的行为序列时,就是以两次行为时间超过1小时进行截断
- 这篇论文里对我们做cf的启发点还包括:
- 点击之后用户停留时间小于1s,这可能是用户的误点击,需要过滤
- 太过活跃的用户进行过滤,比如三个月内购买了1000件以上的商品,点击了3500个以上的商品(这个要根据实际情况去定)
- 同一个ID,但是发生变化的商品需要过滤
- 2018年Airbnb发表的论文:Real-time Personalization using Embeddings for Search Ranking at Airbnb(论文地址),中将用户的行为分为了两类:
- 经过一系列点击之后有预定房源的行为
- 经过一系列点击之后没有预定房源的行为
- 用户短期内预定房源的行为其实在一定程度上和之前的搜索点击行为都有强相关的关系,因此将预定房源的行为作为一个全局的动作加入到每个session中,这一点对于我们的启发:用户行为中包含的偏好信息会随着某一强偏好行为而终结或者发生变化
1.3 对于Match的优化
上面的两种方法主要是计算item相似度和选取构建训练数据集时的优化,match的优化主要集中在user-to-item,主要有两种方式
1.3.1 行为选择
比如基于用户过去一周内的行为进行item的相似召回,但是用户对item的行为包括:曝光、点击、提单、下单、分享、收藏等,针对不同的行为其偏好强度是不一样的,这里假设用户的行为强度大小关系为:下单 > 提单 > 分享 > 收藏 > 点击 > 曝光。
那么可以选取用户过去一周内的除曝光外的行为,按照时间排序,同样spu去除权重低的行为。比如用户分享、收藏、提单、下单的spu,肯定是发生过点击,可以从点击行为中进行删除。
不同行为赋予不同的权重,然后在进行行为相似召回时,进行相应的加权。
1.3.2 时间衰减
该种方法认为用户行为越接近于当前,其权重越大,所以在使用时,可以对用户过去一周内行为的商品进行时间的降权。
关于时间衰减可以采用:
- 指数衰减: N ( t ) = N 0 ∗ e − λ t N(t) = N_0 * e^{-\lambda t} N(t)=N0∗e−λt
- 高斯衰减: f ( x ) = a ∗ e − ( x − b ) 2 / c 2 f(x)= a * e ^{-(x-b)^2 / c^2} f(x)=a∗e−(x−b)2/c2,其中, a a a 表示峰值的最大值, b b b 表示峰值 x x x 轴偏移量, c c c 弧度跨度
举一个高斯衰减的例子:
e
−
(
x
−
1
)
2
=
1
∗
e
−
(
x
−
1
)
2
/
1
2
e^{-(x-1)^2} = 1 * e^{-(x-1)^2 / 1^2}
e−(x−1)2=1∗e−(x−1)2/12
或者可以采用时间最大最小归一化的方法进行加权:
s
o
c
r
e
i
′
=
t
u
,
i
−
m
i
n
(
t
u
)
m
a
x
(
t
u
)
−
m
i
n
(
t
u
)
∗
s
c
o
r
e
i
socre_i' = \frac{t_{u,i} - min(t_u)}{ max(t_u) - min(t_u) } * score_i
socrei′=max(tu)−min(tu)tu,i−min(tu)∗scorei
2 ItemCF的延伸
这里我们重点说一下上图中的绿色部分。
2.1 Swing
swing指的是秋千,例如用户 u u u 和用户 v v v,都购买过同一件商品 i i i,则三者之间会构成一个类似秋千的关系图。若用户 u u u 和用户 v v v 之间除了购买过 i i i 外,还购买过商品 j j j,则认为两件商品是具有某种程度上的相似的。
也就是说,商品与商品之间的相似关系,是通过用户关系来传递的。为了衡量物品 i i i 和 j j j 的相似性,考察都购买了物品 i i i 和 j j j 的用户 u u u 和 v v v , 如果这两个用户共同购买的物品越少,则物品 i i i 和 j j j 的相似性越高。
Swing算法的表达式如下:
s
i
m
<
i
,
j
>
=
∑
u
∈
U
i
⋂
U
j
∑
v
∈
U
i
⋂
U
j
1
α
+
∣
I
u
⋂
I
v
∣
sim<i,j> = \sum_{u \in U_i \bigcap U_j} \sum_{v \in U_i \bigcap U_j} \frac{1}{\alpha + |I_u \bigcap I_v|}
sim<i,j>=u∈Ui⋂Uj∑v∈Ui⋂Uj∑α+∣Iu⋂Iv∣1
Swing算法具体解释和实现参考:Spark推荐实战系列之Swing算法介绍、实现与在阿里飞猪的实战应用
2.2 Matrix Factorization
2.2.1 矩阵分解的思想
以用户对电影的评分为例,矩阵分解直观上来说,就是把原来的大矩阵,划分为两个小矩阵的乘积,在实际推荐计算时不再使用大矩阵,而是使用分解得到的两个小矩阵。
按照矩阵分解的原理,我们会发现原来 m ∗ n m*n m∗n 的大矩阵会分解成 m ∗ k m*k m∗k 和 k ∗ n k*n k∗n 的两个小矩阵,这里多出来一个 k k k 维向量,就是隐因子向量(Latent Factor Vector),类似的表达还有隐因子、隐向量、隐含特征、隐语义、隐变量等
基于矩阵分解的推荐算法的核心假设是:用隐语义(隐变量)来表达用户和物品,他们的乘积关系就成为了原始的元素。
这种假设之所以成立,是因为我们认为实际的交互数据是由一系列的隐变量的影响下产生的(通常隐变量带有统计分布的假设,就是隐变量之间,或者隐变量和显式变量之间的关系,我们往往认为是由某种分布产生的),这些隐变量代表了用户和物品一部分共有的特征,在物品身上表现为属性特征,在用户身上表现为偏好特征,只不过这些因子并不具有实际意义,也不一定具有非常好的可解释性,每一个维度也没有确定的标签名字,所以才会叫做 “隐变量”。
而矩阵分解后得到的两个包含隐变量的小矩阵,一个代表用户的隐含特征,一个代表物品的隐含特征,矩阵的元素值代表着相应用户或物品对各项隐因子的符合程度,有正面的也有负面的。
假设隐因子数量 k k k 是 2,分别代表着喜剧片和动作片两种题材,矩阵分解后的两个小矩阵,分布代表着电影对这两种题材的符合程度以及用户对这两种题材的偏好程度
2.2.2 矩阵分解求解
以上面为例,电影评分预测实际上是一个矩阵补全的过程,整个预测模型的最终目的是得到两个小矩阵,通过这两个小矩阵的乘积来补全大矩阵中没有评分的位置。
因为大矩阵有一部分是有评分的,那么只要保证大矩阵有评分的位置(实际值)与两个小矩阵相乘得到的相应位置的评分(预测值)之间的误差最小即可,其实就是一个均方误差损失,这便是模型的目标函数
目标函数:
o
b
j
=
min
q
∗
,
p
∗
∑
(
u
,
i
)
∈
κ
(
r
u
i
−
p
u
q
i
T
)
+
λ
(
∣
∣
q
i
∣
∣
2
+
∣
∣
p
u
∣
∣
2
)
obj = \min_{q*,p*} \sum_{(u,i) \in \kappa } (r_{ui} - p_u q_i^T) + \lambda (| | q_i | |^2 + | | p_u | |^2)
obj=q∗,p∗min(u,i)∈κ∑(rui−puqiT)+λ(∣∣qi∣∣2+∣∣pu∣∣2)
分成两部分:
- 前一部分: 用分解后的矩阵预测分数,要和实际的用户评分之间误差越小越好
- 后一部分: 得到的隐因子向量要越简单越好,正则化项,控制模型的方差
2.2.3 矩阵分解算法有哪些
参考:点击阅读
矩阵分解应该是一种算法思想,而不是具体的某一种算法,其包含:
- 奇异值分解SVD
- SVD的各种衍生版
- 非负矩阵分解(Non-negative Matrix Factorization,NMF)
- WRMF (weighted regularized matrix factorization)
- 概率矩阵分解(Probabilistic Matrix Factorization,PMF)
上述算法在确定了需要优化的目标函数之后,就可以找到使其目标最小的参数,常见的优化算法包括:
- 随机梯度下降(Stochastic Gradient Descent,SGD)
- 交替最小二乘法(Alternating Least Squares,ALS)
2.3 NCF
论文地址:点击阅读
参考:https://zhuanlan.zhihu.com/p/158417209
Neural Collaborative Filtering (NCF)由何向南博士于17年发表,不同于传统的基于矩阵分解的协同过滤算法,NCF框架引入了神经网络,通过神经网络来学习用户与物品的交互信息,并在实验中取得了一定的效果。
2.3.1 通用的NCF框架
Input Layer:输入层的user和item 通过onehot 编码转化为稀疏向量。
Embedding Layer:将User向量和Item向量分别嵌入到一个较小维数的空间,假设是 k k k 维。embedding层的具体实现是把输入的向量 u i u_i ui 和 矩阵 P P P 相乘,得到该向量的嵌入向量 p i p_i pi,同理可以得到 q i q_i qi
Neural Layer:这一层被称为神经协同过滤层。把用户的向量 p i p_i pi 和 物品的向量 q i q_i qi 送入NCF layers后,通过里边的layer得到一个向量。这里面的每个layer都可以进行定制,论文作者给出了三种实现,分别是GMF、MLP、NeuMF
Output Layer:通过全连接层把NCF layers输出的向量映射到最终的预测分数 y ^ u i \hat{y}_{ui} y^ui ,最后再通过损失函数 l ( y ^ u i , y u i ) l(\hat{y}_{ui}, y_{ui}) l(y^ui,yui) 进行梯度下降更新参数。
2.3.2 GMF
广义矩阵分解与传统的矩阵分解的区别在于它用的是向量对应元素相乘,得到的是向量,而矩阵分解使用的是向量内积,得到的是标量。具体模型如下图所示:
对于通过嵌入层得到的用户
u
u
u 嵌入向量
p
u
p_u
pu 和物品
i
i
i 嵌入向量
p
i
p_i
pi,将他们逐元素相乘,即:
p
u
=
[
p
1
,
.
.
.
,
p
k
]
q
i
=
[
q
1
,
.
.
.
,
q
k
]
ϕ
0
(
p
u
,
q
i
)
=
p
u
⊙
q
i
=
[
p
1
q
1
,
.
.
.
,
p
k
q
k
]
p_u= [p^1, ..., p^k] \\ q_i = [q^1, ..., q^k] \\ \phi_0(p_u, q_i) = p_u \odot q_i = [p^1q^1, ..., p^k q^k]
pu=[p1,...,pk]qi=[q1,...,qk]ϕ0(pu,qi)=pu⊙qi=[p1q1,...,pkqk]
因为是对应元素相乘,所以要求对于用户的嵌入层和对于物品的嵌入层的嵌入维数要相同,都为
k
k
k
在这之后通过无bias的全连接层和激活函数得到预测分数:
y
^
u
i
=
σ
(
(
p
u
⊙
q
i
)
T
h
)
\hat{y}_{ui}=\sigma((p_u \odot q_i)^T h)
y^ui=σ((pu⊙qi)Th)
其中
- σ \sigma σ 为激活函数
- h h h 为全连接层的权重矩阵,大小为 k ∗ 1 k*1 k∗1
2.3.3 MLP
作者使用的多层感知机为全连接层,用ReLU函数作为激活函数,这个模型对嵌入向量的处理与GMF模型中不同。它把用户
u
u
u 嵌入向量
p
u
p_u
pu 和物品
i
i
i 嵌入向量
p
i
p_i
pi 连起来(concatenate),即:
z
1
=
ϕ
1
(
p
u
,
q
i
)
=
[
p
u
,
q
i
]
=
[
p
1
,
.
.
.
,
p
k
1
,
q
1
,
.
.
.
,
q
k
2
]
z_1= \phi_1(p_u, q_i) = [p_u,q_i] = [p^1, ..., p^{k_1},q^1, ..., q^{k_2}]
z1=ϕ1(pu,qi)=[pu,qi]=[p1,...,pk1,q1,...,qk2]
之后就把得到的长度为
k
1
+
k
2
k_1+k_2
k1+k2 的向量送入多层感知机层:
ϕ
2
(
z
1
)
=
σ
2
(
z
1
T
W
2
+
b
2
)
ϕ
2
(
z
2
)
=
σ
3
(
z
2
T
W
3
+
b
3
)
.
.
.
ϕ
X
(
z
X
−
1
)
=
σ
X
(
z
X
−
1
T
W
X
+
b
X
)
\phi_2(z_1) = \sigma_2(z_1^TW_2 +b_2) \\ \phi_2(z_2) = \sigma_3(z_2^TW_3 +b_3) \\ ... \\ \phi_X(z_{X-1}) = \sigma_X(z_{X-1}^TW_X +b_X)
ϕ2(z1)=σ2(z1TW2+b2)ϕ2(z2)=σ3(z2TW3+b3)...ϕX(zX−1)=σX(zX−1TWX+bX)
其中,
σ
\sigma
σ 为激活函数,这里每一层的激活函数都选取了ReLU,
W
i
W_i
Wi 和
b
i
b_i
bi 分别为全连接层的weight和bias。
最后通过无bias的全连接层和激活函数得到预测分数: y ^ u i = σ ( ϕ X ( z X − 1 ) T h ) \hat{y}_{ui}=\sigma(\phi_X(z_{X-1})^T h) y^ui=σ(ϕX(zX−1)Th),其中 σ \sigma σ 为激活函数, h h h 为weight。
2.3.4 NeuMF
NeuMF则是GMF和MLP的结合,整体模型的思路如下所示:
一个比较直接的实现方法是共享GMF和MLP的嵌入层参数,但这样的处理可能会限制融合模型的性能,作者在文中也给出了一个例子:共享参数意味着它们嵌入维数相同,然而在GMF和MLP模型达到最优效果时,它们的嵌入维数并不一定是相同的,甚至可能相差很大。
为了使融合模型更加灵活,采用分别学习GMF和MLP的嵌入层矩阵的方法,并在两个模型的最后一层前将他们的输出向量连起来,然后传入最后的NeuMF layer,预测最终的分数。具体公式如下:
ϕ
G
M
F
=
p
u
G
⊙
q
i
G
\phi ^{GMF} = p^G_u \odot q^G_i
ϕGMF=puG⊙qiG
ϕ M L P = a L ( W L T a L − 1 ( . . . a 2 ( W 2 T [ p u M q i M ] + b 2 ) . . . ) ) + b L \phi^{MLP} = a_L( W^T_L a_{L-1}(... a_2(W^T_2 \begin{bmatrix} p^M_u\\ q^M_i \end{bmatrix} + b_2)... ))+b_L ϕMLP=aL(WLTaL−1(...a2(W2T[puMqiM]+b2)...))+bL
y ^ u i = σ ( h T [ ϕ G M F ϕ M L P ] ) \hat y_{ui} = \sigma (h^T \begin{bmatrix} \phi ^{GMF} \\ \phi^{MLP} \end{bmatrix}) y^ui=σ(hT[ϕGMFϕMLP])
MLP层采用的是ReLU激活函数。
如果训练时已经拥有了GMF模型和MLP模型的预训练参数,那么,在NeuMF层可以使用超参数
α
\alpha
α,将两者的输出向量连在一起:
y
^
u
i
=
σ
(
h
T
[
α
∗
ϕ
G
M
F
(
1
−
α
)
∗
ϕ
M
L
P
]
)
\hat y_{ui} = \sigma (h^T \begin{bmatrix} \alpha * \phi ^{GMF} \\ (1-\alpha) * \phi^{MLP} \end{bmatrix})
y^ui=σ(hT[α∗ϕGMF(1−α)∗ϕMLP])
完整版参考:论文|被“玩烂”了的协同过滤加上神经网络怎么搞?
扫一扫关注「搜索与推荐Wiki」!号主「专注于搜索和推荐系统,以系列分享为主,持续打造精品内容!」