ashuonnet commited on
Commit
e923d6f
·
verified ·
1 Parent(s): 93db8d2

Upload 2 files

Browse files
modules/embedding_module.py ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ import numpy as np
4
+ import math
5
+
6
+ from model.temporal_attention import TemporalAttentionLayer
7
+
8
+
9
+ class EmbeddingModule(nn.Module):
10
+ def __init__(self, node_features, edge_features, memory, neighbor_finder, time_encoder, n_layers,
11
+ n_node_features, n_edge_features, n_time_features, embedding_dimension, device,
12
+ dropout):
13
+ super(EmbeddingModule, self).__init__()
14
+ self.node_features = node_features
15
+ self.edge_features = edge_features
16
+ # self.memory = memory
17
+ self.neighbor_finder = neighbor_finder
18
+ self.time_encoder = time_encoder
19
+ self.n_layers = n_layers
20
+ self.n_node_features = n_node_features
21
+ self.n_edge_features = n_edge_features
22
+ self.n_time_features = n_time_features
23
+ self.dropout = dropout
24
+ self.embedding_dimension = embedding_dimension
25
+ self.device = device
26
+
27
+ def compute_embedding(self, memory, source_nodes, timestamps, n_layers, n_neighbors=20, time_diffs=None,
28
+ use_time_proj=True):
29
+ return NotImplemented
30
+
31
+
32
+ class IdentityEmbedding(EmbeddingModule):
33
+ def compute_embedding(self, memory, source_nodes, timestamps, n_layers, n_neighbors=20, time_diffs=None,
34
+ use_time_proj=True):
35
+ return memory[source_nodes, :]
36
+
37
+
38
+ class TimeEmbedding(EmbeddingModule):
39
+ def __init__(self, node_features, edge_features, memory, neighbor_finder, time_encoder, n_layers,
40
+ n_node_features, n_edge_features, n_time_features, embedding_dimension, device,
41
+ n_heads=2, dropout=0.1, use_memory=True, n_neighbors=1):
42
+ super(TimeEmbedding, self).__init__(node_features, edge_features, memory,
43
+ neighbor_finder, time_encoder, n_layers,
44
+ n_node_features, n_edge_features, n_time_features,
45
+ embedding_dimension, device, dropout)
46
+
47
+ class NormalLinear(nn.Linear):
48
+ # From Jodie code
49
+ def reset_parameters(self):
50
+ stdv = 1. / math.sqrt(self.weight.size(1))
51
+ self.weight.data.normal_(0, stdv)
52
+ if self.bias is not None:
53
+ self.bias.data.normal_(0, stdv)
54
+
55
+ self.embedding_layer = NormalLinear(1, self.n_node_features)
56
+
57
+ def compute_embedding(self, memory, source_nodes, timestamps, n_layers, n_neighbors=20, time_diffs=None,
58
+ use_time_proj=True):
59
+ source_embeddings = memory[source_nodes, :] * (1 + self.embedding_layer(time_diffs.unsqueeze(1)))
60
+
61
+ return source_embeddings
62
+
63
+
64
+ class GraphEmbedding(EmbeddingModule):
65
+ def __init__(self, node_features, edge_features, memory, neighbor_finder, time_encoder, n_layers,
66
+ n_node_features, n_edge_features, n_time_features, embedding_dimension, device,
67
+ n_heads=2, dropout=0.1, use_memory=True):
68
+ super(GraphEmbedding, self).__init__(node_features, edge_features, memory,
69
+ neighbor_finder, time_encoder, n_layers,
70
+ n_node_features, n_edge_features, n_time_features,
71
+ embedding_dimension, device, dropout)
72
+
73
+ self.use_memory = use_memory
74
+ self.device = device
75
+
76
+ def compute_embedding(self, memory, source_nodes, timestamps, n_layers, n_neighbors=20, time_diffs=None,
77
+ use_time_proj=True):
78
+ """Recursive implementation of curr_layers temporal graph attention layers.
79
+
80
+ src_idx_l [batch_size]: users / items input ids.
81
+ cut_time_l [batch_size]: scalar representing the instant of the time where we want to extract the user / item representation.
82
+ curr_layers [scalar]: number of temporal convolutional layers to stack.
83
+ num_neighbors [scalar]: number of temporal neighbor to consider in each convolutional layer.
84
+ """
85
+
86
+ assert (n_layers >= 0)
87
+
88
+ source_nodes_torch = torch.from_numpy(source_nodes).long().to(self.device)
89
+ timestamps_torch = torch.unsqueeze(torch.from_numpy(timestamps).float().to(self.device), dim=1)
90
+
91
+ # query node always has the start time -> time span == 0
92
+ source_nodes_time_embedding = self.time_encoder(torch.zeros_like(
93
+ timestamps_torch))
94
+
95
+ source_node_features = self.node_features[source_nodes_torch, :]
96
+
97
+ if self.use_memory:
98
+ source_node_features = memory[source_nodes, :] + source_node_features
99
+
100
+ if n_layers == 0:
101
+ return source_node_features
102
+ else:
103
+
104
+ source_node_conv_embeddings = self.compute_embedding(memory,
105
+ source_nodes,
106
+ timestamps,
107
+ n_layers=n_layers - 1,
108
+ n_neighbors=n_neighbors)
109
+
110
+ neighbors, edge_idxs, edge_times = self.neighbor_finder.get_temporal_neighbor(
111
+ source_nodes,
112
+ timestamps,
113
+ n_neighbors=n_neighbors)
114
+
115
+ neighbors_torch = torch.from_numpy(neighbors).long().to(self.device)
116
+
117
+ edge_idxs = torch.from_numpy(edge_idxs).long().to(self.device)
118
+
119
+ edge_deltas = timestamps[:, np.newaxis] - edge_times
120
+
121
+ edge_deltas_torch = torch.from_numpy(edge_deltas).float().to(self.device)
122
+
123
+ neighbors = neighbors.flatten()
124
+ neighbor_embeddings = self.compute_embedding(memory,
125
+ neighbors,
126
+ np.repeat(timestamps, n_neighbors),
127
+ n_layers=n_layers - 1,
128
+ n_neighbors=n_neighbors)
129
+
130
+ effective_n_neighbors = n_neighbors if n_neighbors > 0 else 1
131
+ neighbor_embeddings = neighbor_embeddings.view(len(source_nodes), effective_n_neighbors, -1)
132
+ edge_time_embeddings = self.time_encoder(edge_deltas_torch)
133
+
134
+ edge_features = self.edge_features[edge_idxs, :]
135
+
136
+ mask = neighbors_torch == 0
137
+
138
+ source_embedding = self.aggregate(n_layers, source_node_conv_embeddings,
139
+ source_nodes_time_embedding,
140
+ neighbor_embeddings,
141
+ edge_time_embeddings,
142
+ edge_features,
143
+ mask)
144
+
145
+ return source_embedding
146
+
147
+ def aggregate(self, n_layers, source_node_features, source_nodes_time_embedding,
148
+ neighbor_embeddings,
149
+ edge_time_embeddings, edge_features, mask):
150
+ return NotImplemented
151
+
152
+
153
+ class GraphSumEmbedding(GraphEmbedding):
154
+ def __init__(self, node_features, edge_features, memory, neighbor_finder, time_encoder, n_layers,
155
+ n_node_features, n_edge_features, n_time_features, embedding_dimension, device,
156
+ n_heads=2, dropout=0.1, use_memory=True):
157
+ super(GraphSumEmbedding, self).__init__(node_features=node_features,
158
+ edge_features=edge_features,
159
+ memory=memory,
160
+ neighbor_finder=neighbor_finder,
161
+ time_encoder=time_encoder, n_layers=n_layers,
162
+ n_node_features=n_node_features,
163
+ n_edge_features=n_edge_features,
164
+ n_time_features=n_time_features,
165
+ embedding_dimension=embedding_dimension,
166
+ device=device,
167
+ n_heads=n_heads, dropout=dropout,
168
+ use_memory=use_memory)
169
+ self.linear_1 = torch.nn.ModuleList([torch.nn.Linear(embedding_dimension + n_time_features +
170
+ n_edge_features, embedding_dimension)
171
+ for _ in range(n_layers)])
172
+ self.linear_2 = torch.nn.ModuleList(
173
+ [torch.nn.Linear(embedding_dimension + n_node_features + n_time_features,
174
+ embedding_dimension) for _ in range(n_layers)])
175
+
176
+ def aggregate(self, n_layer, source_node_features, source_nodes_time_embedding,
177
+ neighbor_embeddings,
178
+ edge_time_embeddings, edge_features, mask):
179
+ neighbors_features = torch.cat([neighbor_embeddings, edge_time_embeddings, edge_features],
180
+ dim=2)
181
+ neighbor_embeddings = self.linear_1[n_layer - 1](neighbors_features)
182
+ neighbors_sum = torch.nn.functional.relu(torch.sum(neighbor_embeddings, dim=1))
183
+
184
+ source_features = torch.cat([source_node_features,
185
+ source_nodes_time_embedding.squeeze()], dim=1)
186
+ source_embedding = torch.cat([neighbors_sum, source_features], dim=1)
187
+ source_embedding = self.linear_2[n_layer - 1](source_embedding)
188
+
189
+ return source_embedding
190
+
191
+
192
+ class GraphAttentionEmbedding(GraphEmbedding):
193
+ def __init__(self, node_features, edge_features, memory, neighbor_finder, time_encoder, n_layers,
194
+ n_node_features, n_edge_features, n_time_features, embedding_dimension, device,
195
+ n_heads=2, dropout=0.1, use_memory=True):
196
+ super(GraphAttentionEmbedding, self).__init__(node_features, edge_features, memory,
197
+ neighbor_finder, time_encoder, n_layers,
198
+ n_node_features, n_edge_features,
199
+ n_time_features,
200
+ embedding_dimension, device,
201
+ n_heads, dropout,
202
+ use_memory)
203
+
204
+ self.attention_models = torch.nn.ModuleList([TemporalAttentionLayer(
205
+ n_node_features=n_node_features,
206
+ n_neighbors_features=n_node_features,
207
+ n_edge_features=n_edge_features,
208
+ time_dim=n_time_features,
209
+ n_head=n_heads,
210
+ dropout=dropout,
211
+ output_dimension=n_node_features)
212
+ for _ in range(n_layers)])
213
+
214
+ def aggregate(self, n_layer, source_node_features, source_nodes_time_embedding,
215
+ neighbor_embeddings,
216
+ edge_time_embeddings, edge_features, mask):
217
+ attention_model = self.attention_models[n_layer - 1]
218
+
219
+ source_embedding, _ = attention_model(source_node_features,
220
+ source_nodes_time_embedding,
221
+ neighbor_embeddings,
222
+ edge_time_embeddings,
223
+ edge_features,
224
+ mask)
225
+
226
+ return source_embedding
227
+
228
+
229
+ def get_embedding_module(module_type, node_features, edge_features, memory, neighbor_finder,
230
+ time_encoder, n_layers, n_node_features, n_edge_features, n_time_features,
231
+ embedding_dimension, device,
232
+ n_heads=2, dropout=0.1, n_neighbors=None,
233
+ use_memory=True):
234
+ if module_type == "graph_attention":
235
+ return GraphAttentionEmbedding(node_features=node_features,
236
+ edge_features=edge_features,
237
+ memory=memory,
238
+ neighbor_finder=neighbor_finder,
239
+ time_encoder=time_encoder,
240
+ n_layers=n_layers,
241
+ n_node_features=n_node_features,
242
+ n_edge_features=n_edge_features,
243
+ n_time_features=n_time_features,
244
+ embedding_dimension=embedding_dimension,
245
+ device=device,
246
+ n_heads=n_heads, dropout=dropout, use_memory=use_memory)
247
+ elif module_type == "graph_sum":
248
+ return GraphSumEmbedding(node_features=node_features,
249
+ edge_features=edge_features,
250
+ memory=memory,
251
+ neighbor_finder=neighbor_finder,
252
+ time_encoder=time_encoder,
253
+ n_layers=n_layers,
254
+ n_node_features=n_node_features,
255
+ n_edge_features=n_edge_features,
256
+ n_time_features=n_time_features,
257
+ embedding_dimension=embedding_dimension,
258
+ device=device,
259
+ n_heads=n_heads, dropout=dropout, use_memory=use_memory)
260
+
261
+ elif module_type == "identity":
262
+ return IdentityEmbedding(node_features=node_features,
263
+ edge_features=edge_features,
264
+ memory=memory,
265
+ neighbor_finder=neighbor_finder,
266
+ time_encoder=time_encoder,
267
+ n_layers=n_layers,
268
+ n_node_features=n_node_features,
269
+ n_edge_features=n_edge_features,
270
+ n_time_features=n_time_features,
271
+ embedding_dimension=embedding_dimension,
272
+ device=device,
273
+ dropout=dropout)
274
+ elif module_type == "time":
275
+ return TimeEmbedding(node_features=node_features,
276
+ edge_features=edge_features,
277
+ memory=memory,
278
+ neighbor_finder=neighbor_finder,
279
+ time_encoder=time_encoder,
280
+ n_layers=n_layers,
281
+ n_node_features=n_node_features,
282
+ n_edge_features=n_edge_features,
283
+ n_time_features=n_time_features,
284
+ embedding_dimension=embedding_dimension,
285
+ device=device,
286
+ dropout=dropout,
287
+ n_neighbors=n_neighbors)
288
+ else:
289
+ raise ValueError("Embedding Module {} not supported".format(module_type))
290
+
291
+
modules/memory_updater.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from torch import nn
2
+ import torch
3
+
4
+
5
+ class MemoryUpdater(nn.Module):
6
+ def update_memory(self, unique_node_ids, unique_messages, timestamps):
7
+ pass
8
+
9
+
10
+ class SequenceMemoryUpdater(MemoryUpdater):
11
+ def __init__(self, memory, message_dimension, memory_dimension, device):
12
+ super(SequenceMemoryUpdater, self).__init__()
13
+ self.memory = memory
14
+ self.layer_norm = torch.nn.LayerNorm(memory_dimension)
15
+ self.message_dimension = message_dimension
16
+ self.device = device
17
+
18
+ def update_memory(self, unique_node_ids, unique_messages, timestamps):
19
+ if len(unique_node_ids) <= 0:
20
+ return
21
+
22
+ assert (self.memory.get_last_update(unique_node_ids) <= timestamps).all().item(), "Trying to " \
23
+ "update memory to time in the past"
24
+
25
+ memory = self.memory.get_memory(unique_node_ids)
26
+ self.memory.last_update[unique_node_ids] = timestamps
27
+
28
+ updated_memory = self.memory_updater(unique_messages, memory)
29
+
30
+ self.memory.set_memory(unique_node_ids, updated_memory)
31
+
32
+ def get_updated_memory(self, unique_node_ids, unique_messages, timestamps):
33
+ if len(unique_node_ids) <= 0:
34
+ return self.memory.memory.data.clone(), self.memory.last_update.data.clone()
35
+
36
+ assert (self.memory.get_last_update(unique_node_ids) <= timestamps).all().item(), "Trying to " \
37
+ "update memory to time in the past"
38
+
39
+ updated_memory = self.memory.memory.data.clone()
40
+ updated_memory[unique_node_ids] = self.memory_updater(unique_messages, updated_memory[unique_node_ids])
41
+
42
+ updated_last_update = self.memory.last_update.data.clone()
43
+ updated_last_update[unique_node_ids] = timestamps
44
+
45
+ return updated_memory, updated_last_update
46
+
47
+
48
+ class GRUMemoryUpdater(SequenceMemoryUpdater):
49
+ def __init__(self, memory, message_dimension, memory_dimension, device):
50
+ super(GRUMemoryUpdater, self).__init__(memory, message_dimension, memory_dimension, device)
51
+
52
+ self.memory_updater = nn.GRUCell(input_size=message_dimension,
53
+ hidden_size=memory_dimension)
54
+
55
+
56
+ class RNNMemoryUpdater(SequenceMemoryUpdater):
57
+ def __init__(self, memory, message_dimension, memory_dimension, device):
58
+ super(RNNMemoryUpdater, self).__init__(memory, message_dimension, memory_dimension, device)
59
+
60
+ self.memory_updater = nn.RNNCell(input_size=message_dimension,
61
+ hidden_size=memory_dimension)
62
+
63
+
64
+ def get_memory_updater(module_type, memory, message_dimension, memory_dimension, device):
65
+ if module_type == "gru":
66
+ return GRUMemoryUpdater(memory, message_dimension, memory_dimension, device)
67
+ elif module_type == "rnn":
68
+ return RNNMemoryUpdater(memory, message_dimension, memory_dimension, device)