def forward(self, graph: dgl.DGLHeteroGraph, feat: tuple, dst_node_transformation_weight: nn.Parameter, src_node_transformation_weight: nn.Parameter, src_nodes_attention_weight: nn.Parameter): r"""Compute graph attention network layer. Parameters ---------- graph : specific relational DGLHeteroGraph feat : pair of torch.Tensor The pair contains two tensors of shape (N_{in}, D_{in_{src}})` and (N_{out}, D_{in_{dst}}). dst_node_transformation_weight: Parameter (input_dst_dim, n_heads * hidden_dim) src_node_transformation_weight: Parameter (input_src_dim, n_heads * hidden_dim) src_nodes_attention_weight: Parameter (n_heads, 2 * hidden_dim) Returns ------- torch.Tensor, shape (N, H, D_out)` where H is the number of heads, and D_out is size of output feature. """ graph = graph.local_var() # Tensor, (N_src, input_src_dim) feat_src = self.dropout(feat[0]) # Tensor, (N_dst, input_dst_dim) feat_dst = self.dropout(feat[1]) # Tensor, (N_src, n_heads, hidden_dim) -> (N_src, input_src_dim) * (input_src_dim, n_heads * hidden_dim) feat_src = torch.matmul(feat_src, src_node_transformation_weight).view(-1, self._num_heads, self._out_feats) # Tensor, (N_dst, n_heads, hidden_dim) -> (N_dst, input_dst_dim) * (input_dst_dim, n_heads * hidden_dim) feat_dst = torch.matmul(feat_dst, dst_node_transformation_weight).view(-1, self._num_heads, self._out_feats) # first decompose the weight vector into [a_l || a_r], then # a^T [Wh_i || Wh_j] = a_l Wh_i + a_r Wh_j, This implementation is much efficient # Tensor, (N_dst, n_heads, 1), (N_dst, n_heads, hidden_dim) * (n_heads, hidden_dim) e_dst = (feat_dst * src_nodes_attention_weight[:, :self._out_feats]).sum(dim=-1, keepdim=True) # Tensor, (N_src, n_heads, 1), (N_src, n_heads, hidden_dim) * (n_heads, hidden_dim) e_src = (feat_src * src_nodes_attention_weight[:, self._out_feats:]).sum(dim=-1, keepdim=True) # (N_src, n_heads, hidden_dim), (N_src, n_heads, 1) graph.srcdata.update({'ft': feat_src, 'e_src': e_src}) # (N_dst, n_heads, 1) graph.dstdata.update({'e_dst': e_dst}) # compute edge attention, e_src and e_dst are a_src * Wh_src and a_dst * Wh_dst respectively. graph.apply_edges(fn.u_add_v('e_src', 'e_dst', 'e')) # shape (edges_num, heads, 1) e = self.leaky_relu(graph.edata.pop('e')) # compute softmax graph.edata['a'] = edge_softmax(graph, e) graph.update_all(fn.u_mul_e('ft', 'a', 'msg'), fn.sum('msg', 'ft')) # (N_dst, n_heads * hidden_dim), (N_dst, n_heads, hidden_dim) reshape dst_features = graph.dstdata.pop('ft').reshape(-1, self._num_heads * self._out_feats) dst_features = F.relu(dst_features) return dst_features
def _propagate_user_to_item(self, block: DGLHeteroGraph) -> th.Tensor: with block.local_scope(): for etype in self._rating_set: block.apply_edges(lambda edges: self._compute_message_user_to_item(edges, etype), etype=etype) block.update_all(dgl_fn.copy_e("m", f"m_{etype}"), dgl_fn.mean(f"m_{etype}", f"h_{etype}"), etype=etype) item_features: th.Tensor = block.dstnodes["item"].data["item_features"] all_feature_on_item = [item_features] for rating in self._rating_set: feature_name = f"h_{rating}" if feature_name in block.dstnodes["item"].data: all_feature_on_item.append(block.dstnodes["item"].data[feature_name]) else: all_feature_on_item.append(th.zeros( item_features.shape[0], self._embedding_dim, dtype=item_features.dtype, device=item_features.device)) return self._agg_activation(self._item_aggregate_layer(th.cat(all_feature_on_item, dim=1)))
def _propagate_item_to_user(self, block: DGLHeteroGraph) -> th.Tensor: with block.local_scope(): block.srcnodes["item"].data["item_id_embedding"] = \ self._item_id_embedding_layer(block.srcnodes["item"].data[dgl.NID]) for etype in [f"rev-{rating}" for rating in self._rating_set]: block.apply_edges(lambda edges: self._compute_message_item_to_user(edges, etype), etype=etype) block.update_all(dgl_fn.copy_e("m", f"m_{etype}"), dgl_fn.mean(f"m_{etype}", f"h_{etype}"), etype=etype) user_feature = block.dstnodes["user"].data["user_features"] all_features_on_user = [user_feature] for rating in self._rating_set: feature_name = f"h_rev-{rating}" if feature_name in block.dstnodes["user"].data: all_features_on_user.append(block.dstnodes["user"].data[feature_name]) else: all_features_on_user.append(th.zeros( user_feature.shape[0], self._embedding_dim, dtype=user_feature.dtype, device=user_feature.device)) return self._agg_activation(self._user_aggregate_layer(th.cat(all_features_on_user, dim=1)))
def forward(self, decode_graph: dgl.DGLHeteroGraph, node_representations: Tensor): with decode_graph.local_scope(): decode_graph.ndata["h"] = node_representations decode_graph.apply_edges(dgl.function.u_dot_v("h", "h", "logits")) return decode_graph.edata["logits"]
def forward(self,g: dgl.DGLHeteroGraph,h,etype='interact'): with g.local_scope(): g.nodes['user'].data['h']=self.dropout(h['user']) g.nodes['item'].data['h'] = self.dropout(h['item']) g.apply_edges(self.apply_edges,etype=etype) return g.edges[etype].data['score']*5