Q. Liu, S. Wu, D. Wang, Z. Li and L. Wang, “Context-Aware Sequential Recommendation,” 2016 IEEE 16th International Conference on Data Mining (ICDM), Barcelona, Spain, 2016, pp. 1053-1058, doi: 10.1109/ICDM.2016.0135.
论文链接:https://arxiv.org/pdf/1609.05787.pdf
代码链接:http://qiangliucasia.github.io/homepage/files/CARNNcode.zip
Introduction
本文提出一个上下文感知的循环神经网络模型(CA-RNN),同时使用序列信息和上下文信息产生推荐。
论文中定义了两种类型的上下文信息:
- input context: 指用户与物品产生交互时的外部情况,包括地点(location)、时间(time)、天气(weather)等。
- transition context: 指交互序列中相邻两次交互之间的转换信息,常表示相邻两次交互之间的时间间隔。
下图展现了input context和transition context如何影响预测用户产生的下一交互:
与传统RNN模型使用不变的input matrix不同的是CA-RNN应用了自适应的上下文感知input matrices和自适应的上下文感知transition matrices。
Proposed Model
求当前时间步的隐藏层状态 h k u h_k^u hku:
r
k
u
r_k^u
rku:用户在
k
k
k时间步交互的物品的
v
e
c
t
o
r
vector
vector。
h
k
−
1
u
h_{k-1}^u
hk−1u:上一时间步的隐藏层状态。
M
c
I
,
k
u
M_{c_{I,k}^u}
McI,ku:与
k
k
k输入的上下文信息
c
I
,
k
u
c_{I,k}^u
cI,ku相关的自适应的上下文感知input matrices。
W
⌊
t
k
u
−
t
k
−
1
u
⌋
W_{\left\lfloor t_k^u-t_{k-1}^u\right\rfloor}
W⌊tku−tk−1u⌋:
k
k
k时间步与
k
−
1
k-1
k−1时间步之间的的时间间隔(
t
k
u
−
t
k
−
1
u
t_k^u-t_{k-1}^u
tku−tk−1u)生成的自适应上下文感知transition matrices。由于对于每个可能的连续时间间隔值独立学习transition matrix是不可能的,因此将所有可能的时间间隔划分到几个离散的时间区间内,从而将所有可能的时间间隔离散化为所在时间区间的
f
l
o
o
r
floor
floor值。
对比传统RNN模型中求当前时间步的隐藏状态:
可以看到在本论文中通过将
r
r
r的参数转化为与输入上下文信息相关的
M
c
I
,
k
u
M_{c_{I,k}^u}
McI,ku,将
h
k
−
1
u
h_{k-1}^u
hk−1u的参数转化为与相邻时间步间的时间间隔相关的
W
⌊
t
k
u
−
t
k
−
1
u
⌋
W_{\left\lfloor t_k^u-t_{k-1}^u\right\rfloor}
W⌊tku−tk−1u⌋,实现将上下文信息与序列信息结合,同时也考虑到时间间隔对推荐结果产生的影响。
产生预测
在产生预测时,除了前面建模的历史交互上下文信息,当前上下文信息同样会对预测结果产生影响。以上公式的含义是:在
k
+
1
k+1
k+1时间步,用户
u
u
u是否会在上下文信息为
c
I
,
k
+
1
u
c_{I,k+1}^u
cI,k+1u和
c
T
,
k
+
1
u
c_{T,k+1}^u
cT,k+1u的情况下选择物品
v
v
v。(由于RNN可以近似看作softmax多分类,因此得到的是预测为该物品的打分)
W
c
T
,
k
+
1
u
′
W'_{c_{T,k+1}^u}
WcT,k+1u′:当前的输入上下文信息(input context)
c
T
,
k
+
1
u
c_{T,k+1}^u
cT,k+1u相关的转移矩阵。
M
c
I
,
k
+
1
u
′
M'_{c_{I,k+1}^u}
McI,k+1u′:当前的转移上下文信息(transition context)
c
I
,
k
+
1
u
c_{I,k+1}^u
cI,k+1u相关的转移矩阵。
参数学习
BPR
使用了BPR学习用户的隐式反馈信息。
*拓展:
1. 隐式反馈
用户反馈的类型:显式反馈、隐式反馈
- 显式反馈:能被显式量化的反馈,如:评分。
- 隐式反馈:用户的行为只有两种可能(即发生与不发生),如:点击、购买。
隐式反馈只能观察到positive的反馈,不能显示出用户的偏好:
2. BPR
BPR :贝叶斯个性化排序(Bayesian Personalized Ranking),它是一种排序算法,做的是TopN任务,适用于隐性反馈数据。
- 以往那些方法的建模目标是对于要进行推荐的物品尽可能的预测为1,对不应该进行推荐的物品尽可能的预测为0。这样的建模目标并不能够学习到物品之间的先后顺序(虽然我们一直假想的把预测值作为排序的依据)。
- BPR的提出旨在解决物品之间的顺序问题,提出了一个可以应用于各个已有的推荐算法的算法框架,这个算法框架的建模目标是优化物品之间的顺序,解决推荐的物品之间的排序问题。
- 当然这里还有一个建模目标是解决隐性反馈数据不能显示出用户偏好的问题。
BPR建模思路
- 如果用户在有i、j两个物品的同时消费了i,则我们可以得出用户在i和j之间更加喜欢物品i的假设,则我们可以得到一个三元组<u,i,j>,也就是说,我们希望对于u来说,i物品应该排在j物品的前面。
- 所以对于所有已经有的隐性反馈集合m条,比如用户购买了某物品的记录,虽然隐性反馈记录并不一定能够完全显示用户的偏好,但是在一定程度上我们可以假设用户更喜欢自己有过购买记录的物品,毕竟他选择购买了。所以如此,我们可以构建m条三元组的训练集。
BPR优化方法
提高用户更喜欢物品
i
i
i的概率可以通过增加物品
i
i
i和物品
j
j
j间的差异实现。
具体BPR解释可参考以下博文,讲的很清晰:
https://blog.csdn.net/qiqi123i/article/details/100102805
https://www.cnblogs.com/pinard/p/9128682.html
BPTT
BPTT(back-propagation through time)算法是常用的训练RNN的方法,其实本质还是BP算法,只不过RNN处理时间序列数据,所以要基于时间反向传播,故叫随时间反向传播。BPTT的中心思想和BP算法相同,沿着需要优化的参数的负梯度方向不断寻找更优的点直至收敛。
1. RNN结构特点
这是一个标准的RNN结构图,图中每个箭头代表做一次变换,也就是说箭头连接带有权值。左侧是折叠起来的样子,右侧是展开的样子,左侧中h旁边的箭头代表此结构中的“循环“体现在隐层。
在展开结构中我们可以观察到,在标准的RNN结构中,隐层的神经元之间也是带有权值的。也就是说,随着序列的不断推进,前面的隐层将会影响后面的隐层。图中
O
O
O代表输出,
y
y
y代表样本给出的确定值,
L
L
L代表损失函数,我们可以看到,“损失“也是随着序列的推荐而不断积累的。
除上述特点之外,标准RNN的还有以下特点:
- 权值共享,图中的 W W W全是相同的, U U U和 V V V也一样。
- 每一个输入值都只与它本身的那条路线建立权连接,不会和别的神经元连接。
x x x是输入, h h h是隐层单元, o o o为输出, L L L为损失函数, y y y为训练集的标签。这些元素右上角带的 t t t代表t时刻的状态,其中需要注意的是,隐藏单元 h h h在 t t t时刻的表现不仅由此刻的输入决定,还受 t t t时刻之前时刻的影响。 V V V、 W W W、 U U U是权值,同一类型的权连接权值相同。
有了上面的理解,前向传播算法其实非常简单,对于
t
t
t时刻:
其中
ϕ
\phi
ϕ()为激活函数,一般来说会选择
t
a
n
h
tanh
tanh函数,
b
b
b为偏置。
t
t
t时刻的输出就更为简单:
最终模型的预测输出为:
其中
σ
\sigma
σ为激活函数,通常RNN用于分类,故这里一般用
s
o
f
t
m
a
x
softmax
softmax函数。
2. BPTT
待优化的参数有三个,分别是
U
U
U、
V
V
V、
W
W
W。与BP算法不同的是,其中
W
W
W和
U
U
U两个参数的寻优过程需要追溯之前的历史数据,参数
V
V
V相对简单只需关注目前,那么我们就来先求解参数
V
V
V的偏导数。
由于RNN的损失会随着时间累加,所以不能只求
t
t
t时刻的偏导:
W
W
W和
U
U
U的偏导的求解由于需要涉及到历史数据,其偏导求起来相对复杂,我们先假设只有三个时刻,那么在第三个时刻
L
L
L对
W
W
W的偏导数为:
相应的,
L
L
L在第三个时刻对
U
U
U的偏导数为:
可以观察到,在某个时刻的对
W
W
W或是
U
U
U的偏导数,需要追溯这个时刻之前所有时刻的信息,这还仅仅是一个时刻的偏导数,上面说过损失也是会累加的,那么整个损失函数对
W
W
W和
U
U
U的偏导数将会非常繁琐。虽然如此但好在规律还是有迹可循,我们根据上面两个式子可以写出
L
L
L在
t
t
t时刻对
W
W
W和
U
U
U偏导数的通式:
整体的偏导公式就是将其按时刻再一一加起来。
BPTT的内容参考自以下博文,讲的很清晰:
https://blog.csdn.net/zhaojc1995/article/details/80572098
回到论文
在BPR中,对用户交互的物品序列中的每个positive物品
v
v
v,设置一个对应的negative物品
v
′
v'
v′。
因此CA-RNN的目的是最大化用户喜欢
v
v
v多于
v
′
v'
v′的概率,而由
B
P
R
BPR
BPR,这一概率等价于最大化物品
v
v
v和物品
v
′
v'
v′之间的差异:
g
(
x
)
g(x)
g(x)为非线性函数:
g
(
x
)
=
1
/
1
+
e
−
x
g(x)=1/1+e^{-x}
g(x)=1/1+e−x。
由此可得等价的损失函数:
其中
Θ
=
{
R
,
M
,
W
}
\Theta = \{ R,M,W\}
Θ={R,M,W},指代所有需要进行调整的参数。
λ
\lambda
λ为控制正则化程度的参数(权重衰退,防止过拟合)。因此CA-RNN的目的为最小化损失函数
J
J
J。
CA-RNN中的参数可进一步通过BPTT习得。
代码
在这里只看文件夹里的CARNN.py文件,里面用到的数据来自user_cart_input+transition.json文件。
1. 数据集
打开user_cart_input+transition.json文件,如下:
每行为每个用户交互的物品序列信息,每个序列信息的组成为:物品编号、weekday、month、interval。意味着在论文的实验中,从原始数据的timestamp中提取到两种input context:7填为一周、3个十天为一个月。将用户交互序列中相邻两次交互的时间间隔作为transition context,并将其离散到1天的时间区间上。
本数据集的用户总数为
1904
1904
1904,物品总数为
1157
1157
1157。
2. 训练模型
基于当前时间步更新隐藏状态
h
h
h,从而生成下一时间步的预测。
将当前时间步的weekday转移矩阵和month转移矩阵结合,作为公式
(
5
)
(5)
(5)中的
M
M
M,将当前时间步的interval转移矩阵作为公式
(
5
)
(5)
(5)中的
W
W
W。
将下一时间步的weekday转移矩阵、month转移矩阵、interval转移矩阵结合,作为公式
(
6
)
(6)
(6)中的
M
′
M'
M′和
W
′
W'
W′。
def train(user_cart, weekday_cart, month_cart, interval_cart): # user_cart:一个用户交互的商品编号
global U, W, X
dhlist = [] # bpr中对h的梯度
hiddenlist = [] # 记录[1,T]状态hidden layer (不包括1的上一个状态的hidden layer)
midlist = [] # BPTT中传到第一层的导数 sigmoid(bi)*(1-sigmoid(bi))
hl = np.copy(H_ZERO) # 上一个时间点的隐藏状态last hidden layer,这里进行初始化
sumdUW = [] # 记录BPTT中每个weekday的总更新
sumdUM = [] # 记录BPTT中每个month的总更新
sumdV = [] # 记录BPTT中每个interval的总更新
loss = 0
for i in range(7):
sumdUW.append(0)
for i in range(3):
sumdUM.append(0)
for i in range(5):
sumdV.append(0)
# BPR
dh1 = np.copy(H_ZERO) # dh for the back process
for i in range(len(user_cart)-1): # i stands for a time step in sequence
# 对于要预测的item进行负采样
neg = random.randint(1, ITEM_SIZE) # 生成[1, ITEM_SIZE]之间的随机整数
while (user_cart[i+1]) == neg: # 如果生成的随机物品编号在样本中
neg = random.randint(1, ITEM_SIZE) # 重新生成随机数
item_pos = X[user_cart[i+1]-1, :].reshape(1, HIDDEN_SIZE) # positive sample's vector
item_curt = X[user_cart[i]-1, :].reshape(1, HIDDEN_SIZE) # current input vector
item_neg = X[neg-1, :].reshape(1, HIDDEN_SIZE) # negative sample's vector
month_now = month_cart[i]
month_next = month_cart[i+1]
weekday_now = weekday_cart[i]
weekday_next = weekday_cart[i+1]
interval_now = interval_cart[i]
interval_next = interval_cart[i+1]
uw_now = UWF[weekday_now] # 第一个weekday转移矩阵
uw_next = UWS[weekday_next] # 第二个weekday转移矩阵
um_now = UMF[month_now] # 第一个month转移矩阵
um_next = UMS[month_next] # 第二个month转移矩阵
v_now = VF[interval_now] # 第一个interval转移矩阵
v_next = VS[interval_next] # 第二个interval转移矩阵
# 计算状态t的h、dh
# RNN
b = np.dot(item_curt, (uw_now + um_now)) + np.dot(hl, v_now) # 当前用户交互物品特征*(第一个weekday转移矩阵+第一个month转移矩阵) + 上一个hidden_layer*第一个interval转移矩阵
h = sigmoid(b) # 激活函数,求得当前的隐藏状态hidden_layer(公式1)
xi_j = item_pos - item_neg # 正样本i和负样本j的向量差异
xij = np.dot(np.dot(h, (uw_next + um_next + v_next)), xi_j.T) # 公式6对正负样本vector计算产生的预测值之差(计算模型对于正样本i和负样本j的打分差异)
loss += xij
# 若为tmp = sigmoid(-Xij) 则LEARNING_RATE和LAMBDA为负
tmp = -(1 - sigmoid(xij)) # tmp为公式8的损失函数对xij的导数:∂J/∂xij
hiddenlist.append(h) # 将当前时间步的隐藏状态存至隐藏状态列表
mid = h * (1 - h) # 当前时间步的隐藏层状态h对b的导数:∂h/∂b
midlist.append(mid)
dhlist.append(tmp * np.dot(item_pos - item_neg, (uw_next.T + um_next.T + v_next.T))) # 公式6:用于保存在BPR算法中每个时间步对隐藏状态h的导数:∂J/∂h,∂J/∂h=∂J/∂xij*∂xij/∂h
# 计算对于负样本的导数 并更新负样本的vector
dneg = -tmp * np.dot(h, (uw_next + um_next + v_next)) + LAMBDA * item_neg # 公式6:计算了对负样本的vector的梯度:∂J/∂r',∂J/∂r'=∂J/∂xij*∂xij/∂r','-'为了抵消∂(item_pos-item_neg)/∂item_neg时产生的负号
X[neg-1, :] += -LEARNING_RATE * (dneg.reshape(HIDDEN_SIZE, )) # 更新负样本的vector
# 计算对于正样本的导数 并更新正样本的vector
ditem = tmp * np.dot(h, (uw_next + um_next + v_next)) + LAMBDA * item_pos # 公式6:计算了对正样本的vector的梯度:∂J/∂r,∂J/∂r=∂J/∂xij*∂xij/∂r
X[user_cart[i+1]-1, :] += -LEARNING_RATE * (ditem.reshape(HIDDEN_SIZE,)) # 更新正样本的vector
# 计算next UW的更新量
# dUW = tmp * np.dot((item_pos - item_neg).T, h)
dUWS = tmp * np.dot(h.T, (item_pos - item_neg)) # 公式6:计算第二个weekday转移矩阵在当前时间步的梯度,认为W'和M'相互独立,所以将M'视作常数
UWS[weekday_next] += -LEARNING_RATE * (dUWS + LAMBDA * UWS[weekday_next]) # 更新第二个weekday转移矩阵
dUMS = tmp * np.dot(h.T, (item_pos - item_neg)) # 公式6:计算第二个month转移矩阵在当前时间步的梯度
UMS[month_next] += -LEARNING_RATE * (dUMS + LAMBDA * UMS[month_next]) # 更新第二个month转移矩阵
dVS = tmp * np.dot(h.T, (item_pos - item_neg)) # 公式6:计算第二个interval转移矩阵在当前时间步的梯度
VS[interval_next] += -LEARNING_RATE * (dVS + LAMBDA * VS[interval_next]) # 更新第二个interval转移矩阵
# 更新last hidden layer
hl = h
# BPTT
for i in range(len(user_cart) - 1)[::-1]: # 生成从len(user_cart)-2到0的逆序整数序列。在这个代码中,它的作用是从最后一个时间步开始向前迭代,以计算BPTT中每个时间步的梯度。
item = X[user_cart[i] - 1, :].reshape(1, HIDDEN_SIZE) # 获取用户在当前时间步交互的物品的vector
weekday_now = weekday_cart[i] # 用户在当前时间步交互的物品的weekday
month_now = month_cart[i] # 用户在当前时间步交互的物品的month
interval_now = interval_cart[i] # 用户在当前时间步交互的物品的interval
uw_now = UWF[weekday_now] # 用户在当前时间步交互的物品的weekday的vector
um_now = UMF[month_now] # 用户在当前时间步交互的物品的month的vector
v_now = VF[interval_now] # 用户在当前时间步交互的物品的interval的vector
hnminus2 = hiddenlist[i] # 当前时间步对应的隐藏状态
dh = dhlist[i] + dh1 # 将当前时间步的BPR梯度和之前时间步的BPTT梯度相加,得到了当前时间步对隐藏层状态h的总梯度
# dhlist[i]表示在BPR算法中在i对隐藏层状态h的贡献的梯度:∂J/∂h。这些梯度已经在BPR过程中计算并存储在dhlist列表中。
# dh1表示前一个时间步的隐藏层状态h的梯度。在每个时间步,这个值都会从后往前传播,累积梯度。
dUW = np.dot(item.T, dh * midlist[i]) # 计算第一个weekday转移矩阵在当前时间步的梯度:计算∂J/∂W,先计算∂J/∂b:∂J/∂b=∂J/∂h*∂h/∂b,再计算:∂J/∂W=∂J/∂b*∂b/∂W,由公式5可得∂b/∂W=在当前时间步交互的物品的vector
dUM = np.dot(item.T, dh * midlist[i]) # 计算第一个month转移矩阵在当前时间步的梯度:计算∂J/∂M,先计算∂J/∂b:∂J/∂b=∂J/∂h*∂h/∂b,再计算:∂J/∂M=∂J/∂b*∂b/∂M,由公式5可得∂b/∂M=在当前时间步交互的物品的vector
dV = np.dot(hnminus2.T, dh * midlist[i]) # 计算第一个interval转移矩阵在当前时间步的梯度:计算∂J/∂V,先计算∂J/∂b:∂J/∂b=∂J/∂h*∂h/∂b,再计算:∂J/∂V=∂J/∂b*∂b/∂V,由公式5可得∂b/∂V=上一时间步的隐藏状态h(?)
sumdUW[weekday_now] += dUW
sumdUM[month_now] += dUM
sumdV[interval_now] += dV
# 更新输入的样本
dx = np.dot(dh * midlist[i], (uw_now.T + um_now.T)) # 计算∂J/∂x,先计算∂J/∂b:∂J/∂b=∂J/∂h*∂h/∂b,再计算:∂J/∂x=∂J/∂b*∂b/∂x,由公式5可得∂b/∂x=M=uw_now+um_now
X[user_cart[i]-1, :] += -LEARNING_RATE*(dx.reshape(HIDDEN_SIZE, ) + LAMBDA * X[user_cart[i]-1, :]) # 更新X的vector
dh1 = np.dot(dh * midlist[i], v_now.T) # 计算∂J/∂h(前一个时间步的h),先计算∂J/∂b:∂J/∂b=∂J/∂h*∂h/∂b,再计算:∂J/∂h=∂J/∂b*∂b/∂h,由公式5可得∂b/hx=W=v_now
# 将训练的转移矩阵更新
for weekday in range(7):
UWF[weekday] += -LEARNING_RATE * (sumdUW[weekday] + LAMBDA * UWF[weekday]) # 更新第一个weekday转移矩阵
for month in range(3):
UMF[month] += -LEARNING_RATE * (sumdUM[month] + LAMBDA * UMF[month]) # 更新第一个month转移矩阵
for interval in range(5):
VF[interval] += -LEARNING_RATE *(sumdV[interval] + LAMBDA * VF[interval]) # 更新第一个interval转移矩阵
return loss
可以看到在训练部分分成了BPR和BPTT两个部分。
- 在BPR中通过最大化正负样本之间的差异来更新第二个weekday转移矩阵、第二个month转移矩阵、第二个interval转移矩阵,这对应着公式(6)中的 M ′ M' M′、 W ′ W' W′参数。由BPTT可得某一时刻 M ′ M' M′、 W ′ W' W′参数的偏导不需要将这个时刻之前的所有时刻的隐藏层信息累加起来,但由于RNN的损失会随着时间累加,所以整体的偏导需将其按时刻再一一加起来。
- BPTT更新第一个weekday转移矩阵、第一个month转移矩阵、第一个interval转移矩阵,这对应着公式(5)中的
M
M
M、
W
W
W参数。需要将这个时刻之前的所有时刻的隐藏层信息累加起来,同时整体的偏导需将其按时刻再一一加起来。代码中通过逆序遍历用户交互的物品序列,从而实现在求某个时刻对
M
M
M、
W
W
W的偏导数时,能追溯这个时刻之前所有时刻的信息,并最后按时刻进行累加。
3. 预测
def predict():
relevant = 0.0 # 所预测的总次数
hit = {} # 第n个位置所命中的个数
recall = {} # 前n个位置所命中的总数
recallatx = {} # RecallAtN/relevant
for i in range(TOP):
hit[i+1] = 0
recall[i+1] = 0
for n in ITEM_TEST.keys(): # 用于进行测试的用户编号
item_train = ITEM_TRAIN[n] # 该用户编号对应用于训练的物品序列
item_test = ITEM_TEST[n] # 该用户编号对应用于测试的物品序列
weekday_train = WEEKDAY_TRAIN[n] # 该用户编号对应用于训练的weekday
weekday_test = WEEKDAY_TEST[n] # 该用户编号对应用于测试的weekday
month_train = MONTH_TRAIN[n] # 该用户编号对应用于训练的month
month_test = MONTH_TEST[n] # 该用户编号对应用于测试的month
interval_train = INTERVAL_TRAIN[n] # 该用户编号对应用于训练的interval
interval_test = INTERVAL_TEST[n] # 该用户编号对应用于测试的interval
hl = np.copy(H_ZERO) # 上一时间步的隐藏状态
h = np.copy(H_ZERO)
# 计算需要预测的状态对应的hidden layer
for i in range(len(item_train)):
month_now = month_train[i]
weekday_now = weekday_train[i]
interval_now = interval_train[i]
umf = UMF[month_now]
uwf = UWF[weekday_now]
vf = VF[interval_now]
item = X[item_train[i]-1]
b = np.dot(item, (uwf + umf)) + np.dot(hl, vf)
h = sigmoid(b)
hl = h # 更新上一时间步的隐藏状态为当前时间步的隐藏状态
# 预测
for j in range(len(item_test)): # 对test中交互的每个物品
month_now = month_test[j]
weekday_now = weekday_test[j]
interval_now = interval_test[j]
ums = UMS[month_now]
uws = UWS[weekday_now]
vs = VS[interval_now]
relevant += 1
predict_matrix = np.dot(np.dot(h, (vs+uws+ums)), X.T) # 生成预测值:形状为1*ITEM_SIZE的矩阵:对每个物品进行评分
rank = np.argpartition(predict_matrix[0], -TOP)[-TOP:] # 找到最大的TOP个评分对应的下标(物品编号-1)
rank = rank[np.argsort(predict_matrix[0][rank])] # 将最大的TOP个评分对应的下标按评分的从小到大进行排序
rank_index_list = list(reversed(list(rank))) # 反转为从大到小排序,rank_index_list:预测物品编号序列
if item_test[j]-1 in rank_index_list: # 如果测试的物品编号在生成的预测序列里
index = rank_index_list.index(item_test[j]-1) # 找到rank_index_list中第一次出现的值等于测试物品编号的位置(在序列中最前第几个踩中正确答案)
hit[index+1] += 1 # 计数预测序列20个位置,每个位置命中的次数
item = X[item_test[j] - 1]
uwf = UWF[weekday_now]
umf = UMF[month_now]
vf = VF[interval_now]
b = np.dot(item, (uwf+umf)) + np.dot(h, vf)
h = sigmoid(b) # 计算当前时间步的隐藏状态
for i in range(20):
for j in range(20-i):
recall[20-j] += hit[i+1] # 前n个位置所命中的总数
for i in range(20):
recallatx[i+1] = recall[i+1]/relevant # 在前i+1个推荐项中的召回率
print (relevant) # 预测的总次数
print (recall)
print (recallatx)
return recall, recallatx
3. 运行结果
第1行:迭代次数
第4行:当前迭代的总损失
第5行:预测的总次数
第6行:前20个预测物品的编号
第7行:前n个位置的命中率
结语
- 本论文通过将经典RNN模型中的权重参数转化为与上下文信息相关的转移矩阵,从而实现将上下文信息融入RNN模型。
- 本论文中结合的上下文信息都是关于时间戳的,没有提到其他诸如地点、天气等信息的结合方式。
- 本文主要是记录一下方便自己以后看,有错误的地方欢迎大家指出来讨论。