一. 论文解读
协同记忆网络 (CMN) 不但能够捕获协同过滤中隐向量的全局结构,还能对局部相似用户的行为进行建模。其记忆模块通过注意力机制很好的对用户-物品近邻关系进行了编码;输出模块利用用户-物品的记忆编码进行排序分数的输出。多层记忆模块的堆叠能玩挖掘更复杂的用户-物品关系。
1. 模型结构:
2. 计算流程:
2.1 User Embedding 的计算
如果 Hop == 1:
else:
其中, 表示对物品 产生过反馈的所有用户, 分别表示用户 和物品 的 embedding 向量。
2.2 Neighborhood Attention 的计算
这里的 表示用户 的另外一个嵌入向量。
2.3 Output Module
其中 ,, 表示激活函数。
3. 损失函数:
其中,, 表示用户 交互过的正样本, 表示负样本。
二. 代码实现
1. 系统环境
- tensorflow 2.0
- python 3.6.8
代码实现主要利用tensorflow.keras,其中将 和 最后的预测层抽象成了 Layer 的子类。
2. z_ui
计算层
class HopLayer(tf.keras.layers.Layer):
"""
计算z_ui
"""
def __init__(self, activation=tf.nn.relu, embedding_size=32, **kwargs):
super().__init__(**kwargs)
self.activation = activation
self.embedding_size = embedding_size
def build(self, input_shape):
self.w = self.add_weight(name='w',
shape=[self.embedding_size, self.embedding_size],
initializer=tf.zeros_initializer())
self.b = self.add_weight(name='b',
shape=[self.embedding_size],
initializer=tf.zeros_initializer())
def call(self, inputs):
z_ui, o_ui = inputs
outputs = self.activation(tf.matmul(z_ui, self.w) + o_ui + self.b)
return outputs
2. 输出层
class PredictLayer(tf.keras.layers.Layer):
"""
计算输出
"""
def __init__(self, activation=tf.nn.relu, embedding_size=32, **kwargs):
self.activation = activation
self.embedding_size = embedding_size
super().__init__(**kwargs)
def build(self, input_shape):
"""
:param input_shape: [[batch_size, embedding_size], []
:return:
"""
self.u = self.add_weight(name='u',
shape=[self.embedding_size, self.embedding_size],
initializer=tf.zeros_initializer())
self.w = self.add_weight(name='w',
shape=[self.embedding_size, self.embedding_size],
initializer=tf.zeros_initializer())
self.b = self.add_weight(name='b',
shape=[self.embedding_size],
initializer=tf.zeros_initializer())
self.v = self.add_weight(name='v',
shape=[self.embedding_size],
initializer=tf.zeros_initializer())
def call(self, inputs):
user_embeddings, item_embeddings, hop_ouput = inputs
inner = tf.matmul(tf.multiply(user_embeddings, item_embeddings), self.u) + tf.matmul(hop_ouput, self.w) + self.b
outputs = tf.matmul(self.activation(inner), tf.expand_dims(self.v, axis=-1))
return outputs
3. CMN
模型
class CMN(tf.keras.Model):
def __init__(self, user_size, item_size, neighbor_size, embedding_size=32, hops=1,
activation=tf.nn.relu, l2_reg=0.01, **kwargs):
super().__init__(**kwargs)
self.hops = hops
self.neighbor_size = neighbor_size
self.user_embedding = tf.keras.layers.Embedding(user_size, embedding_size,
embeddings_regularizer=tf.keras.regularizers.l2(l2_reg))
self.item_embedding = tf.keras.layers.Embedding(item_size, embedding_size,
embeddings_regularizer=tf.keras.regularizers.l2(l2_reg))
self.neighbor_embedding = tf.keras.layers.Embedding(user_size, embedding_size,
embeddings_regularizer=tf.keras.regularizers.l2(l2_reg))
self.hop_layer = HopLayer(activation=activation, embedding_size=embedding_size)
self.predict_layer = PredictLayer(activation=activation, embedding_size=embedding_size)
def call(self, inputs):
user_ids, item_ids, neighbors = inputs
user_embeddings = self.user_embedding(user_ids) # [batch_size, embedding_size]
item_embeddings = self.item_embedding(item_ids)
# [batch_size, neighbor_size]
neighbors = tf.keras.preprocessing.sequence.pad_sequences(neighbors, maxlen=self.neighbor_size)
neighbor_embeddings = self.neighbor_embedding(neighbors) # [batch_size, neighbor_size, embedding_size]
if self.hops == 1:
# [batch_size, neighbor_size, 1]
q_ui = tf.matmul(neighbor_embeddings, tf.expand_dims(user_embeddings, axis=-1)) + \
tf.matmul(neighbor_embeddings, tf.expand_dims(item_embeddings, axis=-1))
p_ui = tf.nn.softmax(q_ui, axis=1)
# [batch_size, embedding_size]
o_ui = tf.squeeze(tf.matmul(tf.transpose(p_ui, perm=[0, 2, 1]), neighbor_embeddings))
else:
query = user_embeddings + item_embeddings # z_0
for i in range(self.hops):
# [batch_size, neighbor_size, 1]
q_ui = tf.matmul(neighbor_embeddings, tf.expand_dims(query, axis=-1))
p_ui = tf.nn.softmax(q_ui, axis=1)
# [batch_size, embedding_size]
o_ui = tf.squeeze(tf.matmul(tf.transpose(p_ui, perm=[0, 2, 1]), neighbor_embeddings))
query = self.hop_layer((query, o_ui))
predictions = self.predict_layer((user_embeddings, item_embeddings, o_ui))
return predictions
【参考】
- https://arxiv.org/abs/1804.10862