Coverage for hopwise/model/knowledge_graph_embedding_recommender/conve.py: 80%
116 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-09-30 13:25 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-09-30 13:25 +0000
1# @Time : 2024/11/22
2# @Author : Alessandro Soccol
3# @Email : alessandro.soccol@unica.it
5"""ConvE
6##################################################
7Reference:
8 Dettmers et al. "Convolutional 2D Knowledge Graph Embeddings." in AAAI 2018.
10Reference code:
11 https://github.com/TimDettmers/ConvE
12"""
14import torch
15import torch.nn.functional as F
16from torch import nn
18from hopwise.model.abstract_recommender import KnowledgeRecommender
19from hopwise.model.init import xavier_normal_initialization
20from hopwise.utils import InputType
23class ConvE(KnowledgeRecommender):
24 r"""ConvE represent h,r,t in a subset of real number in d dimension. When scoring them,
25 it concatenates and reshape h and r into a unique input [h;r]. This input is passed through
26 a convolutional layers with a set of k filters and then through a dense layer with d neurons
27 and a set of weight W. The output is finally combined with the tail embedding t
28 using the dot product to produce the final score.
30 Note:
31 In this version, we sample recommender data and knowledge data separately, and put them together for training.
32 """
34 input_type = InputType.PAIRWISE
36 def __init__(self, config, dataset):
37 super().__init__(config, dataset)
39 # Load parameters info
40 self.embedding_size = config["embedding_size"]
41 self.device = config["device"]
42 self.label_smoothing = config["label_smoothing"]
43 self.input_dropout = config["input_dropout"]
44 self.hidden_dropout = config["hidden_dropout"]
45 self.feature_dropout = config["feature_dropout"]
46 self.embedding_dim1 = config["embedding_shape"]
47 self.embedding_dim2 = self.embedding_size // self.embedding_dim1
48 self.hidden_size = config["hidden_size"]
49 self.use_bias = config["use_bias"]
50 self.ui_relation = dataset.field2token_id["relation_id"][dataset.ui_relation]
52 # Embeddings
53 self.user_embedding = nn.Embedding(self.n_users + self.n_items, self.embedding_size, padding_idx=0)
54 self.entity_embedding = nn.Embedding(self.n_entities, self.embedding_size, padding_idx=0)
56 self.relations_embeddings = nn.Embedding(self.n_relations, self.embedding_size, padding_idx=0)
58 # Layers
59 self.inp_drop = torch.nn.Dropout(self.input_dropout)
60 self.hidden_drop = torch.nn.Dropout(self.hidden_dropout)
61 self.feature_map_drop = torch.nn.Dropout2d(self.feature_dropout)
62 self.conv1 = torch.nn.Conv2d(1, 32, (3, 3), 1, 0, bias=self.use_bias)
63 self.bn0 = torch.nn.BatchNorm2d(1)
64 self.bn1 = torch.nn.BatchNorm2d(32)
65 self.bn2 = torch.nn.BatchNorm1d(self.embedding_size)
66 self.register_parameter("b_users", nn.Parameter(torch.zeros(self.n_users + self.n_items)))
67 self.register_parameter("b_entities", nn.Parameter(torch.zeros(self.n_entities)))
68 self.fc = torch.nn.Linear(self.hidden_size, self.embedding_size)
70 # Loss
71 self.loss = nn.BCELoss()
73 # Parameters initialization
74 self.apply(xavier_normal_initialization)
76 def forward(self, head, relation, embeddings, bias):
77 stacked_inputs = torch.cat([head, relation], 2)
78 stacked_inputs = self.bn0(stacked_inputs)
79 x = self.inp_drop(stacked_inputs)
80 x = self.conv1(x)
81 x = self.bn1(x)
82 x = F.relu(x)
83 x = self.feature_map_drop(x)
84 x = x.view(x.shape[0], -1)
85 x = self.fc(x)
86 x = self.hidden_drop(x)
87 x = self.bn2(x)
88 x = F.relu(x)
89 x = torch.mm(x, embeddings.weight.transpose(1, 0))
90 x += bias.expand_as(x)
91 pred = torch.sigmoid(x)
92 return pred
94 def _get_rec_embeddings(self, user):
95 relation_users = torch.tensor([self.ui_relation] * user.shape[0], device=self.device)
97 head_embeddings = self.user_embedding(user).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
98 relation_embeddings = self.relations_embeddings(relation_users).view(
99 -1, 1, self.embedding_dim1, self.embedding_dim2
100 )
102 return head_embeddings, relation_embeddings
104 def _get_kg_embeddings(self, head, relation):
105 head_embeddings = self.entity_embedding(head).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
106 relation_embeddings = self.relations_embeddings(relation).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
108 return head_embeddings, relation_embeddings
110 def calculate_loss(self, interaction):
111 user = interaction[self.USER_ID]
113 item = interaction[self.ITEM_ID]
115 head = interaction[self.HEAD_ENTITY_ID]
117 relation = interaction[self.RELATION_ID]
119 tail = interaction[self.TAIL_ENTITY_ID]
121 user_e, rec_r_e = self._get_rec_embeddings(user)
122 head_e, kg_r_e = self._get_kg_embeddings(head, relation)
124 score_users = self.forward(user_e, rec_r_e, self.user_embedding, self.b_users)
125 score_kg = self.forward(head_e, kg_r_e, self.entity_embedding, self.b_entities)
127 items = torch.zeros((item.size(0), self.n_users + self.n_items), device=self.device)
128 items[:, item + self.n_users] = 1.0
130 tails = torch.zeros((tail.size(0), self.n_entities), device=self.device)
131 tails[:, tail] = 1.0
133 if self.label_smoothing:
134 items = ((1.0 - self.label_smoothing) * items) + (1.0 / self.n_items)
135 tails = ((1.0 - self.label_smoothing) * tails) + (1.0 / self.n_entities)
137 rec_loss = self.loss(score_users, items)
138 kg_loss = self.loss(score_kg, tails)
140 return rec_loss + kg_loss
142 def predict(self, interaction):
143 user = interaction[self.USER_ID]
144 item = interaction[self.ITEM_ID]
145 relation = torch.tensor([self.ui_relation] * user.shape[0], device=self.device)
147 users_embedding = self.user_embedding(user).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
148 relation_embeddings = self.relations_embeddings(relation).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
150 score = self.forward(users_embedding, relation_embeddings, self.user_embedding, self.b_users)
152 score = score[:, self.n_users :]
153 score = score[torch.arange(user.size(0)), item]
154 return score
156 def predict_kg(self, interaction):
157 head = interaction[self.HEAD_ENTITY_ID]
158 relation = interaction[self.RELATION_ID]
159 tail = interaction[self.TAIL_ENTITY_ID]
161 head_embeddings = self.entity_embedding(head).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
162 relation_embeddings = self.relations_embeddings(relation).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
164 score = self.forward(head_embeddings, relation_embeddings, self.entity_embedding, self.b_entities)
166 score = score[:, tail]
167 return score
169 def full_sort_predict(self, interaction):
170 user = interaction[self.USER_ID]
171 relation = torch.tensor([self.ui_relation] * user.shape[0], device=self.device)
173 users_embedding = self.user_embedding(user).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
175 relation_embeddings = self.relations_embeddings(relation).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
177 score = self.forward(users_embedding, relation_embeddings, self.user_embedding, self.b_users)
178 score = score[:, self.n_users :]
179 return score
181 def full_sort_predict_kg(self, interaction):
182 head = interaction[self.HEAD_ENTITY_ID]
183 relation = interaction[self.RELATION_ID]
185 head_embeddings = self.entities_embeddings(head).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
186 relation_embeddings = self.relations_embeddings(relation).view(-1, 1, self.embedding_dim1, self.embedding_dim2)
188 score = self.forward(head_embeddings, relation_embeddings, self.entities_embeddings, self.b_entities)
190 return score