目前为止都是在讨论
T
o
p
N
TopN
TopN 推荐,即给定一个用户,如何给他生成一个长度为
N
N
N 的推荐列表,使该推荐列表能够尽量满足用户的兴趣和需求。本书之所以如此重视
T
o
p
N
TopN
TopN 推荐,是因为它非常接近于满足实际系统的需求,实际系统绝大多数情况下就是给用户提供一个包括N个物品的个性化推荐列表。 但是,很多从事推荐系统研究的童鞋最早接触的却是评分预测问题,评分预测问题一直是推荐系统研究的核心。评分预测问题最基本的数据集就是用户评分数据集。该数据集由用户评分记录组成,每一条评分记录是一个三元组
(
u
,
i
,
r
)
(u,i, r)
(u,i,r),表示用户
u
u
u 给物品
i
i
i 赋予了评分
r
r
r,本章用
r
u
i
r_{ui}
rui 表示用户
u
u
u 对物品i的评分。因为用户不可能对所有物品都评分,因此评分预测问题就是如何通过已知的用户历史评分记录预测未知的用户评分记录。
平均预测算法
最简单的评分预测算法,就是使用平均值的方法,下面会讲讲各种平均值的使用:
全局平均值 在平均值里最简单的是全局平均值。它的定义为训练集中所有评分记录的评分平均值:
μ
=
∑
(
u
,
i
)
∈
T
r
a
i
n
r
u
i
∑
(
u
,
i
)
∈
T
r
a
i
n
1
\mu {\text{ = }}\frac{{\sum\nolimits_{(u,i) \in Train} {{r_{ui}}} }}{{\sum\nolimits_{(u,i) \in Train} 1 }}
μ = ∑(u,i)∈Train1∑(u,i)∈Trainrui
用户评分平均值 用户
u
u
u 的评分平均值
r
u
‾
\overline {{r_u}}
ru 定义为用户
u
u
u 在训练集中所有评分的平均值:
r
u
‾
=
∑
i
∈
N
(
u
)
r
u
i
∑
i
∈
N
(
u
)
1
\overline {{r_u}} = \frac{{\sum\nolimits_{i \in N(u)} {{r_{ui}}} }}{{\sum\nolimits_{i \in N(u)} 1 }}
ru=∑i∈N(u)1∑i∈N(u)rui
物品评分平均值 物品
i
i
i 的评分平均值
r
i
‾
\overline {{r_i}}
ri 定义为物品
i
i
i 在训练集中所有评分的平均值:
r
i
‾
=
∑
u
∈
N
(
i
)
r
u
i
∑
u
∈
N
(
i
)
1
\overline {{r_i}} = \frac{{\sum\nolimits_{u \in N(i)} {{r_{ui}}} }}{{\sum\nolimits_{u \in N(i)} 1 }}
ri=∑u∈N(i)1∑u∈N(i)rui
用户分类对物品分类的平均值 假设有两个分类函数,一个是用户分类函数
ϕ
\phi
ϕ,一个是物品分类函数
φ
\varphi
φ 。
ϕ
(
u
)
\phi(u)
ϕ(u) 定义了用户
u
u
u 所属的类,
φ
(
i
)
\varphi(i)
φ(i) 定义了物品
i
i
i 所属的类。那么,我们可以利用训练集中同类用户对同类物品评分的平均值预测用户对物品的评分,即:
r
u
i
^
=
∑
(
v
,
j
)
∈
T
r
a
i
n
,
ϕ
(
u
)
=
ϕ
(
v
)
,
φ
(
i
)
=
φ
(
j
)
r
v
j
∑
(
v
,
j
)
∈
T
r
a
i
n
,
ϕ
(
u
)
=
ϕ
(
v
)
,
φ
(
i
)
=
φ
(
j
)
1
\widehat {{r_{ui}}} = \frac{{\sum\nolimits_{(v,j) \in Train,\phi (u) = \phi (v),\varphi (i) = \varphi (j)} {{r_{vj}}} }}{{\sum\nolimits_{(v,j) \in Train,\phi (u) = \phi (v),\varphi (i) = \varphi (j)} 1 }}
rui=∑(v,j)∈Train,ϕ(u)=ϕ(v),φ(i)=φ(j)1∑(v,j)∈Train,ϕ(u)=ϕ(v),φ(i)=φ(j)rvj
前面提出的全局平均值,用户评分平均值和物品评分平均值都是类类平均值的一种特例。
如果定义
ϕ
(
u
)
=
0
\phi(u)=0
ϕ(u)=0,
φ
(
i
)
=
0
\varphi(i)=0
φ(i)=0,那么
r
u
i
^
\widehat {{r_{ui}}}
rui 就是全局平均值。
如果定义
ϕ
(
u
)
=
μ
\phi(u)=\mu
ϕ(u)=μ,
φ
(
i
)
=
0
\varphi(i)=0
φ(i)=0,那么
r
u
i
^
\widehat {{r_{ui}}}
rui 就是用户评分平均值。
如果定义
ϕ
(
u
)
=
0
\phi(u)=0
ϕ(u)=0,
φ
(
i
)
=
i
\varphi(i)=i
φ(i)=i,那么
r
u
i
^
\widehat {{r_{ui}}}
rui 就是物品评分平均值。 除了这3种特殊的平均值,在用户评分数据上还可以定义很多不同的分类函数。
这里主要讲一下基于邻域的方法,其他方法我这里暂时就不考虑了。 基于用户的邻域算法和基于物品的邻域算法都可以应用到评分预测中。基于用户的邻域算法认为预测一个用户对一个物品的评分,需要参考和这个用户兴趣相似的用户对该物品的评分,即:
r
u
i
^
=
r
u
‾
+
∑
v
∈
S
(
u
,
K
)
∩
N
(
i
)
w
u
v
(
r
v
i
−
r
v
‾
)
∑
v
∈
S
(
u
,
K
)
∩
N
(
i
)
∣
w
u
v
∣
\widehat {{r_{ui}}} = \overline {{r_u}} + \frac{{\sum\nolimits_{v \in S(u,K) \cap N(i)} {{w_{uv}}({r_{vi}} - \overline {{r_v}} )} }}{{\sum\nolimits_{v \in S(u,K) \cap N(i)} {|{w_{uv}}|} }}
rui=ru+∑v∈S(u,K)∩N(i)∣wuv∣∑v∈S(u,K)∩N(i)wuv(rvi−rv) 这里,
S
(
u
,
K
)
S(u, K)
S(u,K) 是和用户
u
u
u 兴趣最相似的
K
K
K 个用户的集合,
N
(
i
)
N(i)
N(i) 是对物品
i
i
i评过分的用户集合,
r
v
i
r_{vi}
rvi 是用户
v
v
v 对物品
i
i
i 的评分,
r
v
r_{v}
rv 是用户
v
v
v 对他评过分的所有物品评分的平均值。用户之间的相似度
w
u
v
w_{uv}
wuv可以通过皮尔逊系数计算,
I
I
I 为用户
u
,
v
u,v
u,v 同时评价过的物品:
w
u
v
=
∑
i
∈
I
(
r
u
i
−
r
u
‾
)
(
r
v
i
−
r
v
‾
)
∑
i
∈
I
(
r
u
i
−
r
u
‾
)
2
∑
i
∈
I
(
r
v
i
−
r
v
‾
)
2
{{\text{w}}_{uv}} = \frac{{\sum\nolimits_{i \in I} {({r_{ui}} - \overline {{r_u}} )({r_{vi}} - \overline {{r_v}} )} }}{{\sqrt {\sum\nolimits_{i \in I} {{{({r_{ui}} - \overline {{r_u}} )}^2}} \sum\nolimits_{i \in I} {{{({r_{vi}} - \overline {{r_v}} )}^2}} } }}
wuv=∑i∈I(rui−ru)2∑i∈I(rvi−rv)2∑i∈I(rui−ru)(rvi−rv)
3. 基于邻域算法的实例分析
虎口脱险
变形金刚
唐山大兄
少林足球
大话西游
黑客帝国
A
1
5
4
5
B
4
2
3
5
C
4
3
5
D
5
5
2
E
5
4
4
数据处理 在这里用户没有对电影评过分的我们将其评分默认为0来处理
defload_data(filePath):
f =open(filePath,"r", encoding="utf-8")
records =dict()for line in f:
user, movie, score = line.strip().split("\t")
records.setdefault(user,{})
records[user][movie]=int(score)return records
defUserSimilarity(records, item_users, ave_vote):
nu =dict()
W =dict()for i, items in item_users.items():for u, rui in items.items():if rui ==0:# 用户没有评分,直接跳过continuefor v, rvi in items.items():if u == v or rvi ==0:# 用户没有评分或者相同用户,直接跳过continue# u,v 必须同时对 i 产生评分
nu.setdefault(u,{})
nu[u].setdefault(v,0)
nu[u][v]+=pow((rui - ave_vote[u]),2)
W.setdefault(u,{})
W[u].setdefault(v,0)
W[u][v]+=(rui - ave_vote[u])*(rvi - ave_vote[v])for u in W:
W[u]={v: y/sqrt(nu[u][v]*nu[v][u])for v, y in W[u].items()}print("用户相似度: ", W)return W