torch-molecule 0.1.0.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. torch_molecule/__init__.py +70 -0
  2. torch_molecule/base/__init__.py +8 -0
  3. torch_molecule/base/base.py +396 -0
  4. torch_molecule/base/encoder.py +44 -0
  5. torch_molecule/base/generator.py +24 -0
  6. torch_molecule/base/old_encoder.py +322 -0
  7. torch_molecule/base/old_predictor.py +904 -0
  8. torch_molecule/base/old_predictor2.py +436 -0
  9. torch_molecule/base/predictor.py +112 -0
  10. torch_molecule/encoder/attrmask/__init__.py +3 -0
  11. torch_molecule/encoder/attrmask/model.py +113 -0
  12. torch_molecule/encoder/attrmask/modeling_attrmask.py +304 -0
  13. torch_molecule/encoder/constant.py +13 -0
  14. torch_molecule/encoder/contextpred/__init__.py +3 -0
  15. torch_molecule/encoder/contextpred/model.py +207 -0
  16. torch_molecule/encoder/contextpred/modeling_contextpred.py +299 -0
  17. torch_molecule/encoder/contextpred/utils.py +206 -0
  18. torch_molecule/encoder/edgepred/__init__.py +3 -0
  19. torch_molecule/encoder/edgepred/model.py +104 -0
  20. torch_molecule/encoder/edgepred/modeling_edgepred.py +285 -0
  21. torch_molecule/encoder/graphmae/__init__.py +3 -0
  22. torch_molecule/encoder/graphmae/dataloader.py +206 -0
  23. torch_molecule/encoder/graphmae/model.py +137 -0
  24. torch_molecule/encoder/graphmae/modeling_graphmae.py +323 -0
  25. torch_molecule/encoder/infograph/__init__.py +3 -0
  26. torch_molecule/encoder/infograph/loss_function.py +128 -0
  27. torch_molecule/encoder/infograph/model.py +92 -0
  28. torch_molecule/encoder/infograph/modeling_infograph.py +301 -0
  29. torch_molecule/encoder/infograph/module.py +31 -0
  30. torch_molecule/encoder/moama/__init__.py +3 -0
  31. torch_molecule/encoder/moama/model.py +131 -0
  32. torch_molecule/encoder/moama/modeling_moama.py +307 -0
  33. torch_molecule/encoder/moama/utils.py +143 -0
  34. torch_molecule/encoder/pretrained/__init__.py +3 -0
  35. torch_molecule/encoder/pretrained/modeling_pretrained.py +274 -0
  36. torch_molecule/encoder/supervised/__init__.py +3 -0
  37. torch_molecule/encoder/supervised/model.py +97 -0
  38. torch_molecule/encoder/supervised/modeling_supervised.py +354 -0
  39. torch_molecule/generator/digress/__init__.py +3 -0
  40. torch_molecule/generator/digress/diffusion.py +194 -0
  41. torch_molecule/generator/digress/modeling_digress.py +503 -0
  42. torch_molecule/generator/digress/transformer.py +360 -0
  43. torch_molecule/generator/digress/utils.py +196 -0
  44. torch_molecule/generator/gdss/__init__.py +3 -0
  45. torch_molecule/generator/gdss/layers.py +257 -0
  46. torch_molecule/generator/gdss/model.py +204 -0
  47. torch_molecule/generator/gdss/modeling_gdss.py +474 -0
  48. torch_molecule/generator/gdss/sde.py +334 -0
  49. torch_molecule/generator/gdss/solver.py +227 -0
  50. torch_molecule/generator/gdss/toremove/ScoreNetworkA.py +167 -0
  51. torch_molecule/generator/gdss/toremove/ScoreNetworkX.py +85 -0
  52. torch_molecule/generator/gdss/utils.py +240 -0
  53. torch_molecule/generator/graph_dit/__init__.py +3 -0
  54. torch_molecule/generator/graph_dit/diffusion.py +188 -0
  55. torch_molecule/generator/graph_dit/modeling_graph_dit.py +581 -0
  56. torch_molecule/generator/graph_dit/transformer.py +207 -0
  57. torch_molecule/generator/graph_dit/utils.py +220 -0
  58. torch_molecule/generator/graph_ga/__init__.py +3 -0
  59. torch_molecule/generator/graph_ga/crossover.py +182 -0
  60. torch_molecule/generator/graph_ga/modeling_graph_ga.py +310 -0
  61. torch_molecule/generator/graph_ga/mutate.py +136 -0
  62. torch_molecule/generator/graph_ga/oracle.py +95 -0
  63. torch_molecule/generator/jtvae/__init__.py +3 -0
  64. torch_molecule/generator/jtvae/jtnn/chemutils.py +450 -0
  65. torch_molecule/generator/jtvae/jtnn/datautils.py +127 -0
  66. torch_molecule/generator/jtvae/jtnn/jtmpn.py +140 -0
  67. torch_molecule/generator/jtvae/jtnn/jtnn_dec.py +350 -0
  68. torch_molecule/generator/jtvae/jtnn/jtnn_enc.py +131 -0
  69. torch_molecule/generator/jtvae/jtnn/mol_tree.py +111 -0
  70. torch_molecule/generator/jtvae/jtnn/mpn.py +129 -0
  71. torch_molecule/generator/jtvae/jtnn/nnutils.py +66 -0
  72. torch_molecule/generator/jtvae/jtnn/vocab.py +31 -0
  73. torch_molecule/generator/jtvae/jtnn_vae.py +246 -0
  74. torch_molecule/generator/jtvae/modeling_jtvae.py +247 -0
  75. torch_molecule/generator/lstm/__init__.py +3 -0
  76. torch_molecule/generator/lstm/action_sampler.py +91 -0
  77. torch_molecule/generator/lstm/lstm.py +59 -0
  78. torch_molecule/generator/lstm/modeling_lstm.py +263 -0
  79. torch_molecule/generator/lstm/smiles_char_dict.py +123 -0
  80. torch_molecule/generator/lstm/utils.py +62 -0
  81. torch_molecule/generator/molgpt/__init__.py +3 -0
  82. torch_molecule/generator/molgpt/dataset.py +87 -0
  83. torch_molecule/generator/molgpt/gpt.py +251 -0
  84. torch_molecule/generator/molgpt/modeling_molgpt.py +427 -0
  85. torch_molecule/generator/pretrained/modeling_pretrained.py +1 -0
  86. torch_molecule/nn/__init__.py +16 -0
  87. torch_molecule/nn/attention.py +63 -0
  88. torch_molecule/nn/embedder.py +183 -0
  89. torch_molecule/nn/gnn.py +631 -0
  90. torch_molecule/nn/mlp.py +34 -0
  91. torch_molecule/predictor/bfgnn/__init__.py +3 -0
  92. torch_molecule/predictor/bfgnn/model.py +116 -0
  93. torch_molecule/predictor/bfgnn/modeling_bfgnn.py +81 -0
  94. torch_molecule/predictor/dir/__init__.py +3 -0
  95. torch_molecule/predictor/dir/model.py +235 -0
  96. torch_molecule/predictor/dir/modeling_dir.py +161 -0
  97. torch_molecule/predictor/dir/utils.py +40 -0
  98. torch_molecule/predictor/gnn/__init__.py +3 -0
  99. torch_molecule/predictor/gnn/model.py +114 -0
  100. torch_molecule/predictor/gnn/modeling_gnn.py +706 -0
  101. torch_molecule/predictor/gnn/old_modeling_gnn.py +707 -0
  102. torch_molecule/predictor/grea/__init__.py +3 -0
  103. torch_molecule/predictor/grea/model.py +190 -0
  104. torch_molecule/predictor/grea/modeling_grea.py +107 -0
  105. torch_molecule/predictor/grea/old1_features.py +229 -0
  106. torch_molecule/predictor/grea/old1_modeling_grea.py +493 -0
  107. torch_molecule/predictor/grea/utils.py +97 -0
  108. torch_molecule/predictor/grin/__init__.py +3 -0
  109. torch_molecule/predictor/grin/model.py +116 -0
  110. torch_molecule/predictor/grin/modeling_grin.py +91 -0
  111. torch_molecule/predictor/irm/__init__.py +3 -0
  112. torch_molecule/predictor/irm/model.py +127 -0
  113. torch_molecule/predictor/irm/modeling_irm.py +179 -0
  114. torch_molecule/predictor/lstm/__init__.py +3 -0
  115. torch_molecule/predictor/lstm/model.py +145 -0
  116. torch_molecule/predictor/lstm/modeling_lstm.py +627 -0
  117. torch_molecule/predictor/lstm/token_from_smiles.py +85 -0
  118. torch_molecule/predictor/old_modeling_graph.py +650 -0
  119. torch_molecule/predictor/rpgnn/__init__.py +3 -0
  120. torch_molecule/predictor/rpgnn/model.py +185 -0
  121. torch_molecule/predictor/rpgnn/modeling_rpgnn.py +76 -0
  122. torch_molecule/predictor/sgir/__init__.py +3 -0
  123. torch_molecule/predictor/sgir/modeling_sgir.py +260 -0
  124. torch_molecule/predictor/sgir/strategy.py +266 -0
  125. torch_molecule/predictor/smiles_transformer/__init__.py +3 -0
  126. torch_molecule/predictor/smiles_transformer/model.py +424 -0
  127. torch_molecule/predictor/smiles_transformer/modeling_transformer.py +252 -0
  128. torch_molecule/predictor/ssr/__init__.py +3 -0
  129. torch_molecule/predictor/ssr/model.py +283 -0
  130. torch_molecule/predictor/ssr/modeling_ssr.py +267 -0
  131. torch_molecule/predictor/ssr/utils.py +185 -0
  132. torch_molecule/utils/__init__.py +31 -0
  133. torch_molecule/utils/checker.py +158 -0
  134. torch_molecule/utils/checkpoint.py +294 -0
  135. torch_molecule/utils/format.py +140 -0
  136. torch_molecule/utils/generic/metrics.py +530 -0
  137. torch_molecule/utils/generic/pseudo_tasks.py +11 -0
  138. torch_molecule/utils/generic/weights.py +30 -0
  139. torch_molecule/utils/graph/features.py +178 -0
  140. torch_molecule/utils/graph/graph_from_smiles.py +134 -0
  141. torch_molecule/utils/graph/graph_to_smiles.py +312 -0
  142. torch_molecule/utils/hf.py +232 -0
  143. torch_molecule/utils/search.py +64 -0
  144. torch_molecule-0.1.0.dev2.dist-info/METADATA +243 -0
  145. torch_molecule-0.1.0.dev2.dist-info/RECORD +148 -0
  146. torch_molecule-0.1.0.dev2.dist-info/WHEEL +5 -0
  147. torch_molecule-0.1.0.dev2.dist-info/licenses/LICENSE +21 -0
  148. torch_molecule-0.1.0.dev2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,123 @@
1
+ class SmilesCharDictionary(object):
2
+ """
3
+ A fixed dictionary for druglike SMILES.
4
+ Enables smile<->token conversion.
5
+
6
+ With a space:0 for padding, Q:1 as the start token and end_of_line \n:2 as the stop token.
7
+ """
8
+
9
+ PAD = ' '
10
+ BEGIN = 'Q'
11
+ END = '\n'
12
+
13
+ def __init__(self) -> None:
14
+ self.forbidden_symbols = {'Ag', 'Al', 'Am', 'Ar', 'At', 'Au', 'D', 'E', 'Fe', 'G', 'K', 'L', 'M', 'Ra', 'Re',
15
+ 'Rf', 'Rg', 'Rh', 'Ru', 'T', 'U', 'V', 'W', 'Xe',
16
+ 'Y', 'Zr', 'a', 'd', 'f', 'g', 'h', 'k', 'm', 'si', 't', 'te', 'u', 'v', 'y'}
17
+
18
+ self.char_idx = {self.PAD: 0, self.BEGIN: 1, self.END: 2, '#': 20, '%': 22, '(': 25, ')': 24, '+': 26, '-': 27,
19
+ '.': 30,
20
+ # '*': 30,
21
+ '*': 47,
22
+ '0': 32, '1': 31, '2': 34, '3': 33, '4': 36, '5': 35, '6': 38, '7': 37, '8': 40,
23
+ '9': 39, '=': 41, 'A': 7, 'B': 11, 'C': 19, 'F': 4, 'H': 6, 'I': 5, 'N': 10,
24
+ 'O': 9, 'P': 12, 'S': 13, 'X': 15, 'Y': 14, 'Z': 3, '[': 16, ']': 18,
25
+ 'b': 21, 'c': 8, 'n': 17, 'o': 29, 'p': 23, 's': 28,
26
+ "@": 42, "R": 43, '/': 44, "\\": 45, 'E': 46,
27
+ }
28
+
29
+ self.idx_char = {v: k for k, v in self.char_idx.items()}
30
+ self.encode_dict = {"Br": 'Y', "Cl": 'X', "Si": 'A', 'Se': 'Z', '@@': 'R', 'se': 'E'}
31
+ self.decode_dict = {v: k for k, v in self.encode_dict.items()}
32
+
33
+ def allowed(self, smiles) -> bool:
34
+ """
35
+ Determine if smiles string has illegal symbols
36
+
37
+ Args:
38
+ smiles: SMILES string
39
+
40
+ Returns:
41
+ True if all legal
42
+ """
43
+ for symbol in self.forbidden_symbols:
44
+ if symbol in smiles:
45
+ # print('Forbidden symbol {:<2} in {}'.format(symbol, smiles))
46
+ return False
47
+ return True
48
+
49
+ def encode(self, smiles: str) -> str:
50
+ """
51
+ Replace multi-char tokens with single tokens in SMILES string.
52
+
53
+ Args:
54
+ smiles: SMILES string
55
+
56
+ Returns:
57
+ sanitized SMILE string with only single-char tokens
58
+ """
59
+
60
+ temp_smiles = smiles
61
+ for symbol, token in self.encode_dict.items():
62
+ temp_smiles = temp_smiles.replace(symbol, token)
63
+ return temp_smiles
64
+
65
+ def decode(self, smiles):
66
+ """
67
+ Replace special tokens with their multi-character equivalents.
68
+
69
+ Args:
70
+ smiles: SMILES string
71
+
72
+ Returns:
73
+ SMILES string with possibly multi-char tokens
74
+ """
75
+ temp_smiles = smiles
76
+ for symbol, token in self.decode_dict.items():
77
+ temp_smiles = temp_smiles.replace(symbol, token)
78
+ return temp_smiles
79
+
80
+ def get_char_num(self) -> int:
81
+ """
82
+ Returns:
83
+ number of characters in the alphabet
84
+ """
85
+ return len(self.idx_char)
86
+
87
+ @property
88
+ def begin_idx(self) -> int:
89
+ return self.char_idx[self.BEGIN]
90
+
91
+ @property
92
+ def end_idx(self) -> int:
93
+ return self.char_idx[self.END]
94
+
95
+ @property
96
+ def pad_idx(self) -> int:
97
+ return self.char_idx[self.PAD]
98
+
99
+ def matrix_to_smiles(self, array):
100
+ """
101
+ Converts an matrix of indices into their SMILES representations
102
+
103
+ Args:
104
+ array: torch tensor of indices, one molecule per row
105
+
106
+ Returns: a list of SMILES, without the termination symbol
107
+ """
108
+ smiles_strings = []
109
+
110
+ for row in array:
111
+ predicted_chars = []
112
+
113
+ for j in row:
114
+ next_char = self.idx_char[j.item()]
115
+ if next_char == self.END:
116
+ break
117
+ predicted_chars.append(next_char)
118
+
119
+ smi = ''.join(predicted_chars)
120
+ smi = self.decode(smi)
121
+ smiles_strings.append(smi)
122
+
123
+ return smiles_strings
@@ -0,0 +1,62 @@
1
+ from rdkit import Chem
2
+ from typing import Set, Any, Optional
3
+ import torch
4
+ from .smiles_char_dict import SmilesCharDictionary
5
+
6
+ def canonicalize(smiles: str, include_stereocenters=True) -> Optional[str]:
7
+ """
8
+ Canonicalize the SMILES strings with RDKit.
9
+ The algorithm is detailed under https://pubs.acs.org/doi/full/10.1021/acs.jcim.5b00543
10
+ Args:
11
+ smiles: SMILES string to canonicalize
12
+ include_stereocenters: whether to keep the stereochemical information in the canonical SMILES string
13
+ Returns:
14
+ Canonicalized SMILES string, None if the molecule is invalid.
15
+ """
16
+
17
+ if smiles is None:
18
+ return None
19
+
20
+ if len(smiles) > 0:
21
+ mol = Chem.MolFromSmiles(smiles)
22
+
23
+ if mol is not None:
24
+ return Chem.MolToSmiles(mol, isomericSmiles=include_stereocenters)
25
+ else:
26
+ return None
27
+ else:
28
+ return None
29
+
30
+ def remove_duplicates(list_with_duplicates):
31
+ """
32
+ Removes the duplicates and keeps the ordering of the original list.
33
+ For duplicates, the first occurrence is kept and the later occurrences are ignored.
34
+ Args:
35
+ list_with_duplicates: list that possibly contains duplicates
36
+ Returns:
37
+ A list with no duplicates.
38
+ """
39
+ unique_set: Set[Any] = set()
40
+ unique_list = []
41
+ for element in list_with_duplicates:
42
+ if element not in unique_set:
43
+ unique_set.add(element)
44
+ unique_list.append(element)
45
+
46
+ return unique_list
47
+
48
+
49
+ def rnn_start_token_vector(batch_size, device='cpu'):
50
+ """
51
+ Returns a vector of start tokens for LSTM.
52
+ This vector can be used to start sampling a batch of SMILES strings.
53
+
54
+ Args:
55
+ batch_size: how many SMILES will be generated at the same time in LSTM
56
+ device: cpu | cuda
57
+
58
+ Returns:
59
+ a tensor (batch_size x 1) containing the start token
60
+ """
61
+ sd = SmilesCharDictionary()
62
+ return torch.LongTensor(batch_size, 1).fill_(sd.begin_idx).to(device)
@@ -0,0 +1,3 @@
1
+ from .modeling_molgpt import MolGPTMolecularGenerator
2
+
3
+ __all__ = ['MolGPTMolecularGenerator']
@@ -0,0 +1,87 @@
1
+ from torch.utils.data import Dataset
2
+ import torch
3
+
4
+ class SmilesDataset(Dataset):
5
+ """Dataset for SMILES strings for MolGPT training"""
6
+
7
+ def __init__(self, data, regex, max_len, properties=None,
8
+ scaffolds=None, scaffold_maxlen=None):
9
+ """
10
+ Initialize the dataset.
11
+
12
+ Parameters
13
+ ----------
14
+ data : List[str]
15
+ List of SMILES strings
16
+ regex : str
17
+ Regex for tokenization
18
+ max_len : int
19
+ Maximum sequence length
20
+ properties : Optional[List[List[float]]]
21
+ Property values for conditional generation
22
+ scaffolds : Optional[List[str]]
23
+ Scaffold SMILES for conditional generation
24
+ scaffold_maxlen : Optional[int]
25
+ Maximum length of scaffold SMILES
26
+ """
27
+
28
+ # Generate content by decomposing all SMILES strings
29
+ content = set()
30
+ for smile in data:
31
+ tokens = regex.findall(smile.strip())
32
+ content.update(tokens)
33
+
34
+ # Add padding token
35
+ content.add('<')
36
+
37
+ chars = sorted(list(set(content)))
38
+ data_size, vocab_size = len(data), len(chars)
39
+
40
+ self.stoi = {ch: i for i, ch in enumerate(chars)}
41
+ self.itos = {i: ch for i, ch in enumerate(chars)}
42
+ self.max_len = max_len
43
+ self.vocab_size = vocab_size
44
+ self.data = data
45
+ self.properties = properties
46
+ self.scaffolds = scaffolds
47
+ self.scaffold_maxlen = scaffold_maxlen
48
+ self.regex = regex
49
+
50
+ def __len__(self):
51
+ return len(self.data)
52
+
53
+ def __getitem__(self, idx):
54
+ # Get SMILES and property
55
+ smiles = self.data[idx].strip()
56
+ prop = self.properties[idx] if self.properties is not None else [0.0]
57
+
58
+ # Get scaffold if available
59
+ if self.scaffolds is not None:
60
+ scaffold = self.scaffolds[idx].strip()
61
+ else:
62
+ scaffold = ""
63
+
64
+ # Tokenize SMILES
65
+ tokens = self.regex.findall(smiles)
66
+ # Pad with '<' if needed
67
+ tokens += ['<'] * (self.max_len - len(tokens))
68
+ # Convert to indices
69
+ indices = [self.stoi.get(t, 0) for t in tokens]
70
+
71
+ # Create input and target tensors
72
+ x = torch.tensor(indices[:-1], dtype=torch.long)
73
+ y = torch.tensor(indices[1:], dtype=torch.long)
74
+
75
+ # Create property tensor
76
+ prop_tensor = torch.tensor(prop, dtype=torch.float)
77
+
78
+ # Create scaffold tensor if needed
79
+ if self.scaffolds is not None:
80
+ scaffold_tokens = self.regex.findall(scaffold)
81
+ scaffold_tokens += ['<'] * (self.scaffold_maxlen - len(scaffold_tokens))
82
+ scaffold_indices = [self.stoi.get(t, 0) for t in scaffold_tokens]
83
+ scaffold_tensor = torch.tensor(scaffold_indices, dtype=torch.long)
84
+ else:
85
+ scaffold_tensor = torch.tensor([], dtype=torch.long)
86
+
87
+ return x, y, prop_tensor, scaffold_tensor
@@ -0,0 +1,251 @@
1
+ """
2
+ GPT model:
3
+ - the initial stem consists of a combination of token encoding and a positional encoding
4
+ - the meat of it is a uniform sequence of Transformer blocks
5
+ - each Transformer is a sequential combination of a 1-hidden-layer MLP block and a self-attention block
6
+ - all blocks feed into a central residual pathway similar to resnets
7
+ - the final decoder is a linear projection into a vanilla Softmax classifier
8
+ """
9
+
10
+ import math
11
+ import logging
12
+
13
+ import torch
14
+ import torch.nn as nn
15
+ from torch.nn import functional as F
16
+
17
+
18
+ class CausalSelfAttention(nn.Module):
19
+ """
20
+ A vanilla multi-head masked self-attention layer with a projection at the end.
21
+ It is possible to use torch.nn.MultiheadAttention here but I am including an
22
+ explicit implementation here to show that there is nothing too scary here.
23
+ """
24
+
25
+ def __init__(self, config):
26
+ super().__init__()
27
+ assert config.hidden_size % config.num_head == 0
28
+ # key, query, value projections for all heads
29
+ self.key = nn.Linear(config.hidden_size, config.hidden_size)
30
+ self.query = nn.Linear(config.hidden_size, config.hidden_size)
31
+ self.value = nn.Linear(config.hidden_size, config.hidden_size)
32
+ # regularization
33
+ self.attn_drop = nn.Dropout(config.attn_pdrop)
34
+ self.resid_drop = nn.Dropout(config.resid_pdrop)
35
+ # output projection
36
+ self.proj = nn.Linear(config.hidden_size, config.hidden_size)
37
+ # causal mask to ensure that attention is only applied to the left in the input sequence
38
+ num = int(bool(config.num_task)) + int(config.scaffold_maxlen) #int(config.lstm_layers) # int(config.use_scaffold)
39
+ # num = 1
40
+ self.register_buffer("mask", torch.tril(torch.ones(config.max_len + num, config.max_len + num))
41
+ .view(1, 1, config.max_len + num, config.max_len + num))
42
+
43
+ self.n_head = config.num_head
44
+
45
+ def forward(self, x, layer_past=None):
46
+ B, T, C = x.size()
47
+
48
+ # calculate query, key, values for all heads in batch and move head forward to be the batch dim
49
+ k = self.key(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
50
+ q = self.query(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
51
+ v = self.value(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
52
+
53
+ # causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)
54
+ att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
55
+ att = att.masked_fill(self.mask[:,:,:T,:T] == 0, float('-inf'))
56
+ att = F.softmax(att, dim=-1)
57
+ attn_save = att
58
+ att = self.attn_drop(att)
59
+ y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)
60
+ y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side
61
+
62
+ # output projection
63
+ y = self.resid_drop(self.proj(y))
64
+ return y, attn_save
65
+
66
+ class Block(nn.Module):
67
+ """ an unassuming Transformer block """
68
+
69
+ def __init__(self, config):
70
+ super().__init__()
71
+ self.ln1 = nn.LayerNorm(config.hidden_size)
72
+ self.ln2 = nn.LayerNorm(config.hidden_size)
73
+ self.attn = CausalSelfAttention(config)
74
+ self.mlp = nn.Sequential(
75
+ nn.Linear(config.hidden_size, 4 * config.hidden_size),
76
+ nn.GELU(),
77
+ nn.Linear(4 * config.hidden_size, config.hidden_size),
78
+ nn.Dropout(config.resid_pdrop),
79
+ )
80
+
81
+ def forward(self, x):
82
+ y, attn = self.attn(self.ln1(x))
83
+ x = x + y
84
+ x = x + self.mlp(self.ln2(x))
85
+ return x, attn
86
+
87
+ class GPTConfig:
88
+ """ base GPT config, params common to all GPT versions """
89
+ embd_pdrop = 0.1
90
+ resid_pdrop = 0.1
91
+ attn_pdrop = 0.1
92
+
93
+ def __init__(self, vocab_size, max_len, **kwargs):
94
+ self.vocab_size = vocab_size
95
+ self.max_len = max_len
96
+ for k,v in kwargs.items():
97
+ setattr(self, k, v)
98
+
99
+ class GPT(nn.Module):
100
+ """ the full GPT language model, with a context size of max_len """
101
+
102
+ def __init__(self, vocab_size, max_len, num_task, num_layer, num_head, hidden_size, use_scaffold, scaffold_maxlen, use_lstm, lstm_layers):
103
+ super().__init__()
104
+ config = GPTConfig(vocab_size, max_len, num_task = num_task, num_layer = num_layer, num_head = num_head, hidden_size = hidden_size, use_scaffold = use_scaffold, scaffold_maxlen = scaffold_maxlen, use_lstm = use_lstm, lstm_layers = lstm_layers)
105
+
106
+ # input embedding stem
107
+ self.config = config
108
+ self.tok_emb = nn.Embedding(config.vocab_size, config.hidden_size)
109
+ self.type_emb = nn.Embedding(2, config.hidden_size)
110
+ if config.num_task:
111
+ self.prop_nn = nn.Linear(config.num_task, config.hidden_size)
112
+
113
+ self.pos_emb = nn.Parameter(torch.zeros(1, config.max_len, config.hidden_size))
114
+ self.drop = nn.Dropout(config.embd_pdrop)
115
+ # transformer
116
+ self.blocks = nn.Sequential(*[Block(config) for _ in range(config.num_layer)])
117
+ # decoder head
118
+ self.ln_f = nn.LayerNorm(config.hidden_size)
119
+ self.head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
120
+
121
+ self.max_len = config.max_len
122
+
123
+ if config.use_lstm:
124
+ self.lstm = nn.LSTM(input_size = config.hidden_size, hidden_size = config.hidden_size, num_layers = config.lstm_layers, dropout = 0.3, bidirectional = False)
125
+
126
+ def get_max_len(self):
127
+ return self.max_len
128
+
129
+ def initialize_parameters(self, seed=None):
130
+ if seed is not None:
131
+ torch.manual_seed(seed)
132
+ self.apply(self._init_weights)
133
+
134
+ def _init_weights(self, module):
135
+ if isinstance(module, (nn.Linear, nn.Embedding)):
136
+ module.weight.data.normal_(mean=0.0, std=0.02)
137
+ if isinstance(module, nn.Linear) and module.bias is not None:
138
+ module.bias.data.zero_()
139
+ elif isinstance(module, nn.LayerNorm):
140
+ module.bias.data.zero_()
141
+ module.weight.data.fill_(1.0)
142
+
143
+ def configure_optimizers(self, train_config):
144
+ """
145
+ This long function is unfortunately doing something very simple and is being very defensive:
146
+ We are separating out all parameters of the model into two buckets: those that will experience
147
+ weight decay for regularization and those that won't (biases, and layernorm/embedding weights).
148
+ We are then returning the PyTorch optimizer object.
149
+ """
150
+
151
+ # separate out all parameters to those that will and won't experience regularizing weight decay
152
+ decay = set()
153
+ no_decay = set()
154
+ whitelist_weight_modules = (torch.nn.Linear, torch.nn.LSTM)
155
+ blacklist_weight_modules = (torch.nn.LayerNorm, torch.nn.Embedding)
156
+ for mn, m in self.named_modules():
157
+ for pn, p in m.named_parameters():
158
+ fpn = '%s.%s' % (mn, pn) if mn else pn # full param name
159
+
160
+ if pn.endswith('bias') or ('bias' in pn):
161
+ # all biases will not be decayed
162
+ no_decay.add(fpn)
163
+ elif (pn.endswith('weight') or ('weight' in pn)) and isinstance(m, whitelist_weight_modules):
164
+ # weights of whitelist modules will be weight decayed
165
+ decay.add(fpn)
166
+ elif pn.endswith('weight') and isinstance(m, blacklist_weight_modules):
167
+ # weights of blacklist modules will NOT be weight decayed
168
+ no_decay.add(fpn)
169
+
170
+ # special case the position embedding parameter in the root GPT module as not decayed
171
+ no_decay.add('pos_emb')
172
+
173
+ # validate that we considered every parameter
174
+ param_dict = {pn: p for pn, p in self.named_parameters()}
175
+ inter_params = decay & no_decay
176
+ union_params = decay | no_decay
177
+ assert len(inter_params) == 0, "parameters %s made it into both decay/no_decay sets!" % (str(inter_params), )
178
+ assert len(param_dict.keys() - union_params) == 0, "parameters %s were not separated into either decay/no_decay set!" \
179
+ % (str(param_dict.keys() - union_params), )
180
+
181
+ # create the pytorch optimizer object
182
+ optim_groups = [
183
+ {"params": [param_dict[pn] for pn in sorted(list(decay))], "weight_decay": train_config["weight_decay"]},
184
+ {"params": [param_dict[pn] for pn in sorted(list(no_decay))], "weight_decay": 0.0},
185
+ ]
186
+ optimizer = torch.optim.AdamW(optim_groups, lr=train_config["learning_rate"], betas=train_config["betas"])
187
+ return optimizer
188
+
189
+ def compute_loss(self, x, targets, prop = None, scaffold = None):
190
+ logits, attn_maps = self.forward(x, targets, prop, scaffold)
191
+ loss = F.cross_entropy(logits.reshape(-1, logits.size(-1)), targets.view(-1))
192
+ return loss
193
+
194
+ def forward(self, idx, targets=None, prop = None, scaffold = None):
195
+ b, t = idx.size()
196
+ assert t <= self.max_len, "Cannot forward, model max_len is exhausted."
197
+
198
+ if self.config.num_task:
199
+ assert prop.size(-1) == self.config.num_task, "num_task should be equal to last dim of property vector"
200
+
201
+ # forward the GPT model
202
+ token_embeddings = self.tok_emb(idx) # each index maps to a (learnable) vector
203
+ position_embeddings = self.pos_emb[:, :t, :] # each position maps to a (learnable) vector
204
+ type_embeddings = self.type_emb(torch.ones((b,t), dtype = torch.long, device = idx.device))
205
+ x = self.drop(token_embeddings + position_embeddings + type_embeddings)
206
+
207
+ if self.config.num_task:
208
+ type_embd = self.type_emb(torch.zeros((b, 1), dtype = torch.long, device = idx.device))
209
+ if prop.ndim == 2:
210
+ p = self.prop_nn(prop.unsqueeze(1)) # for single property
211
+ else:
212
+ p = self.prop_nn(prop) # for multiproperty
213
+ p += type_embd
214
+ x = torch.cat([p, x], 1)
215
+
216
+ if self.config.use_scaffold:
217
+ type_embd = self.type_emb(torch.zeros((b, 1), dtype = torch.long, device = idx.device))
218
+
219
+ scaffold_embeds = self.tok_emb(scaffold) # .mean(1, keepdim = True)
220
+ if self.config.use_lstm:
221
+ scaffold_embeds = self.lstm(scaffold_embeds.permute(1,0,2))[1][0]
222
+ # scaffold_embeds = scaffold_embeds.reshape(scaffold_embeds.shape[1], scaffold_embeds.shape[0], 2, self.config.hidden_size).mean(2)
223
+ scaffold_embeds = scaffold_embeds.permute(1,0,2) # mean(0, keepdim = True)
224
+ # scaffold_embeds = scaffold_embeds.reshape(self.config.lstm_layers, 1, -1, self.config.hidden_size)[-1].permute(1,0,2)
225
+ # scaffold_embeds = scaffold_embeds.reshape(scaffold_embeds.shape[1], scaffold_embeds.shape[0], self.config.hidden_size)
226
+ scaffold_embeds += type_embd
227
+ x = torch.cat([scaffold_embeds, x], 1)
228
+
229
+ # x = self.blocks(x)
230
+ attn_maps = []
231
+
232
+ for layer in self.blocks:
233
+ x, attn = layer(x)
234
+ attn_maps.append(attn)
235
+
236
+ x = self.ln_f(x)
237
+ logits = self.head(x)
238
+
239
+ # print(logits.shape)
240
+ if self.config.num_task and self.config.use_scaffold:
241
+ num = int(bool(self.config.num_task)) + int(self.config.scaffold_maxlen)
242
+ elif self.config.num_task:
243
+ num = int(bool(self.config.num_task))
244
+ elif self.config.use_scaffold:
245
+ num = int(self.config.scaffold_maxlen)
246
+ else:
247
+ num = 0
248
+
249
+ logits = logits[:, num:, :]
250
+
251
+ return logits, attn_maps # (num_layers, batch_size, num_heads, max_seq_len, max_seq_len)