IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
5.0KB

  1. #
  2. # This module implements a single layer of the Decagon
  3. # model. This is going to be already quite complex, as
  4. # we will be using all the graph convolutional building
  5. # blocks.
  6. #
  7. # h_{i}^(k+1) = ϕ(∑_r ∑_{j∈N{r}^{i}} c_{r}^{ij} * \
  8. # W_{r}^(k) h_{j}^{k} + c_{r}^{i} h_{i}^(k))
  9. #
  10. # N{r}^{i} - set of neighbors of node i under relation r
  11. # W_{r}^(k) - relation-type specific weight matrix
  12. # h_{i}^(k) - hidden state of node i in layer k
  13. # h_{i}^(k)∈R^{d(k)} where d(k) is the dimensionality
  14. # of the representation in k-th layer
  15. # ϕ - activation function
  16. # c_{r}^{ij} - normalization constants
  17. # c_{r}^{ij} = 1/sqrt(|N_{r}^{i}| |N_{r}^{j}|)
  18. # c_{r}^{i} - normalization constants
  19. # c_{r}^{i} = 1/|N_{r}^{i}|
  20. #
  21. import torch
  22. from .convolve import SparseDropoutGraphConvActivation
  23. from .data import Data
  24. from typing import List, \
  25. Union, \
  26. Callable
  27. from collections import defaultdict
  28. class Layer(torch.nn.Module):
  29. def __init__(self, output_dim: Union[int, List[int]], **kwargs) -> None:
  30. super().__init__(**kwargs)
  31. self.output_dim = output_dim
  32. class InputLayer(Layer):
  33. def __init__(self, data: Data, output_dim: Union[int, List[int]]= None, **kwargs) -> None:
  34. output_dim = output_dim or \
  35. list(map(lambda a: a.count, data.node_types))
  36. if not isinstance(output_dim, list):
  37. output_dim = [output_dim,] * len(data.node_types)
  38. super().__init__(output_dim, **kwargs)
  39. self.data = data
  40. self.node_reps = None
  41. self.build()
  42. def build(self) -> None:
  43. self.node_reps = []
  44. for i, nt in enumerate(self.data.node_types):
  45. reps = torch.rand(nt.count, self.output_dim[i])
  46. reps = torch.nn.Parameter(reps)
  47. self.register_parameter('node_reps[%d]' % i, reps)
  48. self.node_reps.append(reps)
  49. def forward(self) -> List[torch.nn.Parameter]:
  50. return self.node_reps
  51. def __repr__(self) -> str:
  52. s = ''
  53. s += 'GNN input layer with output_dim: %s\n' % self.output_dim
  54. s += ' # of node types: %d\n' % len(self.data.node_types)
  55. for nt in self.data.node_types:
  56. s += ' - %s (%d)\n' % (nt.name, nt.count)
  57. return s.strip()
  58. class DecagonLayer(Layer):
  59. def __init__(self,
  60. data: Data,
  61. previous_layer: Layer,
  62. output_dim: Union[int, List[int]],
  63. keep_prob: float = 1.,
  64. rel_activation: Callable[[torch.Tensor], torch.Tensor] = lambda x: x,
  65. layer_activation: Callable[[torch.Tensor], torch.Tensor] = torch.nn.functional.relu,
  66. **kwargs):
  67. if not isinstance(output_dim, list):
  68. output_dim = [ output_dim ] * len(data.node_types)
  69. super().__init__(output_dim, **kwargs)
  70. self.data = data
  71. self.previous_layer = previous_layer
  72. self.input_dim = previous_layer.output_dim
  73. self.keep_prob = keep_prob
  74. self.rel_activation = rel_activation
  75. self.layer_activation = layer_activation
  76. self.convolutions = None
  77. self.build()
  78. def build(self):
  79. self.convolutions = {}
  80. for (node_type_row, node_type_column) in self.data.relation_types.keys():
  81. adjacency_matrices = \
  82. self.data.get_adjacency_matrices(node_type_row, node_type_column)
  83. self.convolutions[node_type_row, node_type_column] = SparseMultiDGCA(self.input_dim,
  84. self.output_dim, adjacency_matrices,
  85. self.keep_prob, self.rel_activation)
  86. # for node_type_row, node_type_col in enumerate(self.data.node_
  87. # if rt.node_type_row == i or rt.node_type_col == i:
  88. def __call__(self):
  89. prev_layer_repr = self.previous_layer()
  90. next_layer_repr = defaultdict(list)
  91. for (nt_row, nt_col), rel in self.data.relation_types.items():
  92. conv = SparseDropoutGraphConvActivation(self.input_dim[nt_col],
  93. self.output_dim[nt_row], rel.adjacency_matrix,
  94. self.keep_prob, self.rel_activation)
  95. next_layer_repr[nt_row].append(conv)
  96. conv = SparseDropoutGraphConvActivation(self.input_dim[nt_row],
  97. self.output_dim[nt_col], rel.adjacency_matrix.transpose(0, 1),
  98. self.keep_prob, self.rel_activation)
  99. next_layer_repr[nt_col].append(conv)
  100. next_layer_repr = list(map(sum, next_layer_repr))
  101. return next_layer_repr
  102. #for i, nt in enumerate(self.data.node_types):
  103. # new_repr = []
  104. # for nt_row, nt_col in self.data.relation_types.keys():
  105. # if nt_row != i and nt_col != i:
  106. # continue
  107. # if nt_row == i:
  108. # x = prev_layer_repr[nt_col]
  109. # else:
  110. # x = prev_layer_repr[nt_row]
  111. # conv = self.convolutions[key]
  112. # new_repr.append(conv(x))
  113. # new_repr = sum(new_repr)
  114. # new_layer_repr.append(new_repr)
  115. # return new_layer_repr