GCN流程
第一步,构建用户点击商品记录表,按照city进行切分,(过滤掉不置信的交互数小于n的user)每个城市生成一个文件。文件格式为city,user_id,[(item_id)...].
第二步:读取某个city文件,对item_id和user_id进行编码,且得到编码后的数量(n_user和n_item)
第三步:构建adj:长宽为(n_user + n_item)矩阵,有交互的值为1,无交互为0
第四步:创建DGL:self.g = dgl.from_scipy(self.adj)
第五步:保存到本地文件:adj
模型结构:
class LightGCN(keras.Model):
def __init__(self, user_vol, item_vol, dim):
super(LightGCN, self).__init__()
self.user_size = user_vol
self.item_size = item_vol
self.dim = dim
self.gcn_layers = [GraphConv(dim, dim, norm='both') for i in range(3)]
# 也就是三层GraphConv
call方法:
def call(self, graph, training=False, method='full'):
embedding = self.look_up_embedding(graph, method) #全图节点进行Embedding:(1172,128) user和item一共有1172个节点。
out_embedding = embedding
for layer in self.gcn_layers:
embedding = layer(graph, embedding) # 层图卷的输入是上个图卷积的输出
out_embedding += embedding #out_embedding = 全图节点Embedding + 3个图卷积层的输出
return out_embedding, embedding
train方法:
def train(self, g, train_user, train_item, neg_size, i_weight, method='full'): #
with tf.device('/device:GPU:0'):
embedding, _ = self(g, training=True, method=method) # 调用call函数 (对图节点进行Embedding,并进行3次图卷积得到输出 )
u_em = tf.nn.embedding_lookup(embedding, train_user) # 全局图卷积后,再进行look_up
i_em = tf.nn.embedding_lookup(embedding, train_item)
u_em = tf.nn.l2_normalize(u_em, -1) #l2 norm
i_em = tf.nn.l2_normalize(i_em, -1)
u_em = tf.reshape(u_em, (-1, neg_size, self.dim)) # 把n条张样本分成 m组每组neg_size个 u_em (batch,neg_size,128)
i_em = tf.reshape(i_em, (-1, neg_size, self.dim)) # 把n条张样本分成 m组每组neg_size个 i_em (batch,neg_size,128)
# 同行相乘s为其样本,i,j,j为正样本
logits = tf.einsum("itd,isd->its", u_em, i_em) / 0.06 - i_weight #(n,neg,neg)
pos_logits = tf.linalg.diag_part(logits) # (n,neg)
loss = - tf.reduce_mean(pos_logits)
loss += tf.reduce_mean(tf.math.log(tf.reduce_sum(tf.exp(logits), axis=-1)))
l2_loss = tf.nn.l2_loss(self.embedding)
return loss, loss + l2_loss * 2e-4
说明:
- g:是DGLGraph对象,这里面记录着节点的级联关系。
- train_user和train_item:均为长度为6680的list,train_user[i]与train_item[i]为正样本。
- neg_size:167 负采样个数,注意 167是能整除6680。
- i_weight:item权重 (40, 1, 167)。记录6680item各自的权重。
- logits:shape是[40, 167, 167],记录的是每组中,当前向量与其他样本的向量点积得分。
解释:为什么logits中i,j,j就为正样本?
1、创建向量:
假设向量维度dim:4,neg_size:2,样本个数为6.
u_em: i_em:
2、分组:
每两个为一组,把6个样本分成了3组。
u_em: i_em:
3、计算 u的每个样本 与 i中对应组中的所有样本的相似度:得到
logits:
例如u中,组1的 [0.6, 0.3, 0.7, 0.4],依次与i的组1中两个向量[0.8, 0. , 0.9, 0.2]和[0.6, 0.3, 0.8, 0.2]的点积为 0.48+0+0.63+0.08 =1.19 和 0.36+0.09+0.56+0.08=1.09 但是,这两个得分,只有1.19是正样本。
4、计算loss
正样本+batch随机负采样loss+l2_norm
说明:
- 实现图卷积有两种方式:原生tf 或者 DGL。DGL的优势:计算接口更灵活、性能更好、生态更好。