GAT.py
# -*- coding: utf-8 -*-
# Python version: 3.7
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import pdb
'''
*****user调用model调用GAT*****
(1) 采用GAT层学习节点嵌入
(2) GAT层通过应用自注意机制设计
(3) 采用注意层聚合社交邻居和项目邻居
(4) 利用本地GAT层最终推断节点嵌入
'''
class GraphAttentionLayer(nn.Module):
def __init__(self, in_features, out_features, alpha=0.1):
super().__init__()
self.in_features = in_features
self.out_features = out_features
self.alpha = alpha
'''self.W是W1, W2'''
self.W = nn.Parameter(torch.empty(size=(in_features, out_features)))
nn.init.xavier_uniform_(self.W.data)
# a为权重向量,attention layer vector for user-user interaction
self.a = nn.Parameter(torch.empty(size=(2 * out_features, 1)))
nn.init.xavier_uniform_(self.a.data)
'''这里的W_1映射权重矩阵是共享的'''
self.W_1 = nn.Parameter(torch.randn(in_features, out_features))
# LeakyReLU
self.leakyrelu = nn.LeakyReLU(self.alpha)
def forward(self, h, adj): # h表示中心节点 adj表示邻居节点(用户邻居、项目邻居)
"""
GAT是通用的,不仅用于用户-用户(邻居用户),也用户-项目(邻居项目)
W1和W2是映射矩阵,相不相同?要看self.W = nn.Parameter(torch.empty(size=(in_features, out_features)))
调用GraphAttentionLayer的in_features, out_features相不相同
model.py中调用了
self.GAT_neighbor = GraphAttentionLayer(embed_size, embed_size)
self.GAT_item = GraphAttentionLayer(embed_size, embed_size)
user调用了model.py
self.model = model(embed_size)
embed_size是设定好的
【【【所以用户邻居的W1和项目邻居的W2是相同的吗?】】】
"""
# W为user-user交互的线性映射矩阵
'''h为中心节点u的嵌入'''
'''对于用户邻居W1*e_un 对于项目邻居W2*e_un'''
W_h = torch.matmul(h, self.W)
# W为user-user交互的线性映射矩阵
'''adj为用户邻居节点的嵌入W1*e_up'''
'''adj为项目邻居节点的嵌入W2*e_un'''
W_adj = torch.mm(adj, self.W)
# 连接层,将两个向量W_h和W_adj串联 [We_un||We_up]
'''将两个向量W_h和W_adj串联 [W*e_un||W*e_ik]'''
a_input = torch.cat((W_h.repeat(W_adj.shape[0], 1), W_adj), dim=1)
'''【【【【(一)两种关系的注意层不同之处】】】'''
'''对于用户邻居self.a指的是a(user-user交互的注意层权重向量)'''
'''对于项目邻居self.a指的是b(user-item交互的注意层权重向量)'''
# 通过注意层 LeakyReLU(a^T[W1e_un||We_up])得到注意力权重attention
'''对于项目邻居,LeakyReLU(b^T[W2e_un||We_ik]) W2为item-item interaction的线性映射矩阵'''
attention = self.leakyrelu(torch.matmul(a_input, self.a)).squeeze(-1)
# 通过softmax层 通过使用softmax函数形成<概率分布>,这里的attention是最终的注意力权重
'''最终得到用户-邻居(u_n,u_p)的注意权重'''
'''最终得到用户-项目(u_n,i_k)的注意权重'''
attention = F.softmax(attention, dim=-1)
'''推断中心用户嵌入需要将邻居用户节点和邻居项目节点与其关联的注意权重进行聚合'''
'''这里的W_1映射权重矩阵是共享的'''
# W*e_up和W*e_tk W为线性映射权重矩阵
W_adj_transform = torch.mm(adj, self.W_1)
'''这里的attention就是得到的归一化注意权重'''
# 得到聚合用户邻居的隐藏嵌入h_u和聚合项目邻居的隐藏嵌入h_t
h = torch.matmul(attention, W_adj_transform)
'''直观地,我们应该聚合隐藏嵌入和中心节点嵌入来推断u_n的嵌入,但是社会关系、用户-项目交互和中心节点嵌入'''
'''在学习过程中的作用并不相同,我们应该在聚合步骤中处理异构性'''
return h