esm 3.0.0__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.
- esm/__init__.py +0 -0
- esm/layers/attention.py +70 -0
- esm/layers/blocks.py +153 -0
- esm/layers/codebook.py +88 -0
- esm/layers/ffn.py +29 -0
- esm/layers/geom_attention.py +151 -0
- esm/layers/regression_head.py +24 -0
- esm/layers/rotary.py +221 -0
- esm/layers/structure_proj.py +68 -0
- esm/layers/transformer_stack.py +94 -0
- esm/models/esm3.py +799 -0
- esm/models/function_decoder.py +339 -0
- esm/models/vqvae.py +450 -0
- esm/pretrained.py +95 -0
- esm/sdk/api.py +327 -0
- esm/tokenization/__init__.py +63 -0
- esm/tokenization/function_tokenizer.py +404 -0
- esm/tokenization/residue_tokenizer.py +224 -0
- esm/tokenization/sasa_tokenizer.py +129 -0
- esm/tokenization/sequence_tokenizer.py +68 -0
- esm/tokenization/ss_tokenizer.py +109 -0
- esm/tokenization/structure_tokenizer.py +63 -0
- esm/tokenization/tokenizer_base.py +42 -0
- esm/utils/constants/esm3.py +127 -0
- esm/utils/constants/models.py +5 -0
- esm/utils/constants/physics.py +5 -0
- esm/utils/decoding.py +225 -0
- esm/utils/encoding.py +241 -0
- esm/utils/function/encode_decode.py +187 -0
- esm/utils/function/interpro.py +245 -0
- esm/utils/function/lsh.py +103 -0
- esm/utils/function/tfidf.py +56 -0
- esm/utils/generation.py +185 -0
- esm/utils/misc.py +256 -0
- esm/utils/noise_schedules.py +34 -0
- esm/utils/residue_constants.py +81 -0
- esm/utils/sampling.py +155 -0
- esm/utils/structure/affine3d.py +389 -0
- esm/utils/structure/aligner.py +88 -0
- esm/utils/structure/lddt.py +98 -0
- esm/utils/structure/normalize_coordinates.py +82 -0
- esm/utils/structure/predicted_aligned_error.py +108 -0
- esm/utils/structure/protein_chain.py +828 -0
- esm/utils/structure/protein_structure.py +263 -0
- esm/utils/types.py +27 -0
- esm-3.0.0.dist-info/LICENSE.md +80 -0
- esm-3.0.0.dist-info/METADATA +132 -0
- esm-3.0.0.dist-info/RECORD +50 -0
- esm-3.0.0.dist-info/WHEEL +5 -0
- esm-3.0.0.dist-info/top_level.txt +1 -0
esm/__init__.py
ADDED
|
File without changes
|
esm/layers/attention.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
import einops
|
|
4
|
+
import torch
|
|
5
|
+
import torch.nn.functional as F
|
|
6
|
+
from torch import nn
|
|
7
|
+
|
|
8
|
+
from esm.layers.rotary import RotaryEmbedding
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MultiHeadAttention(nn.Module):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
d_model: int,
|
|
15
|
+
n_heads: int,
|
|
16
|
+
bias: bool = False,
|
|
17
|
+
qk_layernorm: bool = True,
|
|
18
|
+
):
|
|
19
|
+
super().__init__()
|
|
20
|
+
|
|
21
|
+
self.d_model = d_model
|
|
22
|
+
self.n_heads = n_heads
|
|
23
|
+
|
|
24
|
+
self.d_head = self.d_model // self.n_heads
|
|
25
|
+
self.layernorm_qkv = nn.Sequential(
|
|
26
|
+
nn.LayerNorm(d_model), nn.Linear(d_model, d_model * 3, bias=bias)
|
|
27
|
+
)
|
|
28
|
+
self.out_proj = nn.Linear(d_model, d_model, bias=bias)
|
|
29
|
+
|
|
30
|
+
if qk_layernorm:
|
|
31
|
+
self.q_ln = nn.LayerNorm(d_model, bias=bias)
|
|
32
|
+
self.k_ln = nn.LayerNorm(d_model, bias=bias)
|
|
33
|
+
else:
|
|
34
|
+
self.q_ln = nn.Identity()
|
|
35
|
+
self.k_ln = nn.Identity()
|
|
36
|
+
|
|
37
|
+
self.rotary = RotaryEmbedding(d_model // n_heads)
|
|
38
|
+
|
|
39
|
+
def _apply_rotary(self, q: torch.Tensor, k: torch.Tensor):
|
|
40
|
+
q = q.unflatten(-1, (self.n_heads, self.d_head))
|
|
41
|
+
k = k.unflatten(-1, (self.n_heads, self.d_head))
|
|
42
|
+
q, k = self.rotary(q, k)
|
|
43
|
+
q = q.flatten(-2, -1)
|
|
44
|
+
k = k.flatten(-2, -1)
|
|
45
|
+
return q, k
|
|
46
|
+
|
|
47
|
+
def forward(self, x, seq_id):
|
|
48
|
+
qkv_BLD3 = self.layernorm_qkv(x)
|
|
49
|
+
query_BLD, key_BLD, value_BLD = torch.chunk(qkv_BLD3, 3, dim=-1)
|
|
50
|
+
query_BLD, key_BLD = self.q_ln(query_BLD), self.k_ln(key_BLD)
|
|
51
|
+
query_BLD, key_BLD = self._apply_rotary(query_BLD, key_BLD)
|
|
52
|
+
|
|
53
|
+
n_heads = self.n_heads
|
|
54
|
+
reshaper = functools.partial(
|
|
55
|
+
einops.rearrange, pattern="b s (h d) -> b h s d", h=n_heads
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
query_BHLD, key_BHLD, value_BHLD = map(
|
|
59
|
+
reshaper, (query_BLD, key_BLD, value_BLD)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Where True, enable participation in attention.
|
|
63
|
+
mask_BLL = seq_id.unsqueeze(-1) == seq_id.unsqueeze(-2)
|
|
64
|
+
mask_BHLL = mask_BLL.unsqueeze(1)
|
|
65
|
+
|
|
66
|
+
context_BHLD = F.scaled_dot_product_attention(
|
|
67
|
+
query_BHLD, key_BHLD, value_BHLD, mask_BHLL
|
|
68
|
+
)
|
|
69
|
+
context_BLD = einops.rearrange(context_BHLD, "b h s d -> b s (h d)")
|
|
70
|
+
return self.out_proj(context_BLD)
|
esm/layers/blocks.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
from esm.layers.attention import MultiHeadAttention
|
|
6
|
+
from esm.layers.geom_attention import (
|
|
7
|
+
GeometricReasoningOriginalImpl,
|
|
8
|
+
)
|
|
9
|
+
from esm.utils.structure.affine3d import Affine3D
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def swiglu_correction_fn(expansion_ratio: float, d_model: int) -> int:
|
|
13
|
+
# set hidden dimesion to nearest multiple of 256 after expansion ratio
|
|
14
|
+
return int(((expansion_ratio * d_model) + 255) // 256 * 256)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SwiGLU(nn.Module):
|
|
18
|
+
"""
|
|
19
|
+
SwiGLU activation function as an nn.Module, allowing it to be used within nn.Sequential.
|
|
20
|
+
This module splits the input tensor along the last dimension and applies the SiLU (Swish)
|
|
21
|
+
activation function to the first half, then multiplies it by the second half.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
super(SwiGLU, self).__init__()
|
|
26
|
+
|
|
27
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
28
|
+
x1, x2 = x.chunk(2, dim=-1)
|
|
29
|
+
return F.silu(x1) * x2
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def swiglu_ln_ffn(d_model: int, expansion_ratio: float, bias: bool):
|
|
33
|
+
return nn.Sequential(
|
|
34
|
+
nn.LayerNorm(d_model),
|
|
35
|
+
nn.Linear(
|
|
36
|
+
d_model, swiglu_correction_fn(expansion_ratio, d_model) * 2, bias=bias
|
|
37
|
+
),
|
|
38
|
+
SwiGLU(),
|
|
39
|
+
nn.Linear(swiglu_correction_fn(expansion_ratio, d_model), d_model, bias=bias),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def gelu_ln_ffn(d_model: int, expansion_ratio: float, bias: bool):
|
|
44
|
+
hidden_dim = int(expansion_ratio * d_model)
|
|
45
|
+
return nn.Sequential(
|
|
46
|
+
nn.LayerNorm(d_model),
|
|
47
|
+
nn.Linear(d_model, hidden_dim, bias=bias),
|
|
48
|
+
nn.GELU(),
|
|
49
|
+
nn.Linear(hidden_dim, d_model, bias=bias),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class UnifiedTransformerBlock(nn.Module):
|
|
54
|
+
"""
|
|
55
|
+
A unified transformer block that can optionally incorporate geometric attention.
|
|
56
|
+
|
|
57
|
+
This class defines a transformer block that can be configured to use geometric attention
|
|
58
|
+
alongside the standard multi-head attention mechanism. It is designed to be a flexible
|
|
59
|
+
component of transformer-based models, allowing for the integration of geometric reasoning.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
d_model : int
|
|
64
|
+
The dimensionality of the input and output features of the transformer block.
|
|
65
|
+
n_heads : int
|
|
66
|
+
The number of attention heads in the multi-head attention mechanism.
|
|
67
|
+
n_layers : int
|
|
68
|
+
The number of layers in the transformer block.
|
|
69
|
+
use_geom_attn : bool, optional
|
|
70
|
+
Whether to use geometric attention in addition to the standard multi-head attention. Defaults to False.
|
|
71
|
+
v_heads : int, optional
|
|
72
|
+
The number of heads to use for the geometric attention mechanism, if enabled. Must be specified if `use_geom_attn` is True.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
d_model: int,
|
|
78
|
+
n_heads: int,
|
|
79
|
+
use_geom_attn: bool = False,
|
|
80
|
+
use_plain_attn: bool = True,
|
|
81
|
+
v_heads: int | None = None,
|
|
82
|
+
bias: bool = False,
|
|
83
|
+
expansion_ratio: float = 4.0,
|
|
84
|
+
residue_scaling_factor: float = 1,
|
|
85
|
+
mask_and_zero_frameless: bool = False,
|
|
86
|
+
qk_layernorm: bool = True,
|
|
87
|
+
ffn_type: str = "swiglu", # swiglu | gelu
|
|
88
|
+
):
|
|
89
|
+
super().__init__()
|
|
90
|
+
self.use_plain_attn = use_plain_attn
|
|
91
|
+
if self.use_plain_attn:
|
|
92
|
+
self.attn = MultiHeadAttention(
|
|
93
|
+
d_model, n_heads, bias, qk_layernorm=qk_layernorm
|
|
94
|
+
)
|
|
95
|
+
self.use_geom_attn = use_geom_attn
|
|
96
|
+
if self.use_geom_attn:
|
|
97
|
+
if v_heads is None:
|
|
98
|
+
raise ValueError("v_heads must be specified when use_geom_attn is True")
|
|
99
|
+
self.geom_attn = GeometricReasoningOriginalImpl(
|
|
100
|
+
c_s=d_model,
|
|
101
|
+
v_heads=v_heads,
|
|
102
|
+
bias=bias,
|
|
103
|
+
mask_and_zero_frameless=mask_and_zero_frameless,
|
|
104
|
+
)
|
|
105
|
+
if ffn_type == "swiglu":
|
|
106
|
+
self.ffn = swiglu_ln_ffn(d_model, expansion_ratio, bias)
|
|
107
|
+
elif ffn_type == "gelu":
|
|
108
|
+
self.ffn = gelu_ln_ffn(d_model, expansion_ratio, bias)
|
|
109
|
+
else:
|
|
110
|
+
raise ValueError(f"Unknown ffn_type: {ffn_type}")
|
|
111
|
+
self.scaling_factor = residue_scaling_factor
|
|
112
|
+
|
|
113
|
+
def forward(
|
|
114
|
+
self,
|
|
115
|
+
x: torch.Tensor,
|
|
116
|
+
sequence_id: torch.Tensor,
|
|
117
|
+
frames: Affine3D,
|
|
118
|
+
frames_mask: torch.Tensor,
|
|
119
|
+
chain_id: torch.Tensor,
|
|
120
|
+
) -> torch.Tensor:
|
|
121
|
+
"""
|
|
122
|
+
Forward pass for the UnifiedTransformerBlock.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
x : torch.Tensor[float]
|
|
127
|
+
Input tensor to the transformer block, typically the output from the previous layer.
|
|
128
|
+
sequence_id : torch.Tensor[int]
|
|
129
|
+
Tensor containing sequence IDs for each element in the batch, used for attention masking.
|
|
130
|
+
frames : Affine3D
|
|
131
|
+
Affine3D containing geometric frame information for geometric attention.
|
|
132
|
+
frames_mask : torch.Tensor[bool]
|
|
133
|
+
Boolean mask tensor indicating valid frames for geometric attention.
|
|
134
|
+
chain_id : torch.Tensor[int]
|
|
135
|
+
Tensor containing chain IDs for each element, used for attention masking in geometric attention.
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
torch.Tensor[float]
|
|
140
|
+
The output tensor after applying the transformer block operations.
|
|
141
|
+
"""
|
|
142
|
+
if self.use_plain_attn:
|
|
143
|
+
r1 = self.attn(x, sequence_id)
|
|
144
|
+
x = x + r1 / self.scaling_factor
|
|
145
|
+
|
|
146
|
+
if self.use_geom_attn:
|
|
147
|
+
r2 = self.geom_attn(x, frames, frames_mask, sequence_id, chain_id)
|
|
148
|
+
x = x + r2 / self.scaling_factor
|
|
149
|
+
|
|
150
|
+
r3 = self.ffn(x) / self.scaling_factor
|
|
151
|
+
x = x + r3
|
|
152
|
+
|
|
153
|
+
return x
|
esm/layers/codebook.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import torch
|
|
3
|
+
import torch.distributed as dist
|
|
4
|
+
import torch.nn as nn
|
|
5
|
+
import torch.nn.functional as F
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EMACodebook(nn.Module):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
n_codes,
|
|
12
|
+
embedding_dim,
|
|
13
|
+
no_random_restart=True,
|
|
14
|
+
restart_thres=1.0,
|
|
15
|
+
ema_decay=0.99,
|
|
16
|
+
):
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.register_buffer("embeddings", torch.randn(n_codes, embedding_dim))
|
|
19
|
+
self.register_buffer("N", torch.zeros(n_codes))
|
|
20
|
+
self.register_buffer("z_avg", self.embeddings.data.clone())
|
|
21
|
+
|
|
22
|
+
self.n_codes = n_codes
|
|
23
|
+
self.embedding_dim = embedding_dim
|
|
24
|
+
self._need_init = True
|
|
25
|
+
self.no_random_restart = no_random_restart
|
|
26
|
+
self.restart_thres = restart_thres
|
|
27
|
+
self.freeze_codebook = False
|
|
28
|
+
self.ema_decay = ema_decay
|
|
29
|
+
|
|
30
|
+
def reset_parameters(self):
|
|
31
|
+
# For meta init
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
def _tile(self, x):
|
|
35
|
+
d, ew = x.shape
|
|
36
|
+
if d < self.n_codes:
|
|
37
|
+
n_repeats = (self.n_codes + d - 1) // d
|
|
38
|
+
std = 0.01 / np.sqrt(ew)
|
|
39
|
+
x = x.repeat(n_repeats, 1)
|
|
40
|
+
x = x + torch.randn_like(x) * std
|
|
41
|
+
return x
|
|
42
|
+
|
|
43
|
+
def _init_embeddings(self, z):
|
|
44
|
+
# z: [b, t, c]
|
|
45
|
+
self._need_init = False
|
|
46
|
+
flat_inputs = z.view(-1, self.embedding_dim)
|
|
47
|
+
y = self._tile(flat_inputs)
|
|
48
|
+
|
|
49
|
+
y.shape[0]
|
|
50
|
+
_k_rand = y[torch.randperm(y.shape[0])][: self.n_codes]
|
|
51
|
+
if dist.is_initialized():
|
|
52
|
+
dist.broadcast(_k_rand, 0)
|
|
53
|
+
self.embeddings.data.copy_(_k_rand)
|
|
54
|
+
self.z_avg.data.copy_(_k_rand)
|
|
55
|
+
self.N.data.copy_(torch.ones(self.n_codes))
|
|
56
|
+
|
|
57
|
+
def forward(self, z):
|
|
58
|
+
# z: [b, t, c]
|
|
59
|
+
if self._need_init and self.training and not self.freeze_codebook:
|
|
60
|
+
self._init_embeddings(z)
|
|
61
|
+
# z is of shape [batch_size, sequence length, channels]
|
|
62
|
+
flat_inputs = z.view(-1, self.embedding_dim)
|
|
63
|
+
distances = (
|
|
64
|
+
(flat_inputs**2).sum(dim=1, keepdim=True)
|
|
65
|
+
- 2 * flat_inputs @ self.embeddings.t()
|
|
66
|
+
+ (self.embeddings.t() ** 2).sum(dim=0, keepdim=True)
|
|
67
|
+
) # [bt, c]
|
|
68
|
+
|
|
69
|
+
encoding_indices = torch.argmin(distances, dim=1)
|
|
70
|
+
encoding_indices = encoding_indices.view(*z.shape[:2]) # [b, t, ncode]
|
|
71
|
+
|
|
72
|
+
embeddings = F.embedding(encoding_indices, self.embeddings) # [b, t, c]
|
|
73
|
+
|
|
74
|
+
commitment_loss = 0.25 * F.mse_loss(z, embeddings.detach())
|
|
75
|
+
|
|
76
|
+
# EMA codebook update
|
|
77
|
+
if self.training and not self.freeze_codebook:
|
|
78
|
+
assert False, "Not implemented"
|
|
79
|
+
embeddings_st = (embeddings - z).detach() + z
|
|
80
|
+
|
|
81
|
+
return embeddings_st, encoding_indices, commitment_loss
|
|
82
|
+
|
|
83
|
+
def dictionary_lookup(self, encodings):
|
|
84
|
+
embeddings = F.embedding(encodings, self.embeddings)
|
|
85
|
+
return embeddings
|
|
86
|
+
|
|
87
|
+
def soft_codebook_lookup(self, weights: torch.Tensor) -> torch.Tensor:
|
|
88
|
+
return weights @ self.embeddings
|
esm/layers/ffn.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import torch.nn as nn
|
|
2
|
+
import torch.nn.functional as F
|
|
3
|
+
from torch import Tensor
|
|
4
|
+
|
|
5
|
+
# NOT CURRENTLY USED
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SwiGLU(nn.Module):
|
|
9
|
+
def __init__(self) -> None:
|
|
10
|
+
super().__init__()
|
|
11
|
+
|
|
12
|
+
def forward(self, x: Tensor) -> Tensor:
|
|
13
|
+
x1, x2 = x.chunk(2, dim=-1)
|
|
14
|
+
hidden = F.silu(x1) * x2
|
|
15
|
+
return hidden
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FFN(nn.Module):
|
|
19
|
+
def __init__(self, in_proj, activation, out_proj) -> None:
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.in_proj = in_proj
|
|
22
|
+
self.activation = activation
|
|
23
|
+
self.out_proj = out_proj
|
|
24
|
+
|
|
25
|
+
def forward(self, x: Tensor) -> Tensor:
|
|
26
|
+
x = self.in_proj(x)
|
|
27
|
+
x = self.activation(x)
|
|
28
|
+
x = self.out_proj(x)
|
|
29
|
+
return x
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from math import sqrt
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from einops import rearrange
|
|
5
|
+
from torch import nn
|
|
6
|
+
from torch.nn import functional as F
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GeometricReasoningOriginalImpl(nn.Module):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
c_s: int,
|
|
13
|
+
v_heads: int,
|
|
14
|
+
num_vector_messages: int = 1,
|
|
15
|
+
mask_and_zero_frameless: bool = True,
|
|
16
|
+
divide_residual_by_depth: bool = False,
|
|
17
|
+
bias: bool = False,
|
|
18
|
+
):
|
|
19
|
+
"""Approximate implementation:
|
|
20
|
+
|
|
21
|
+
ATTN(A, v) := (softmax_j A_ij) v_j
|
|
22
|
+
make_rot_vectors(x) := R(i->g) Linear(x).reshape(..., 3)
|
|
23
|
+
make_vectors(x) := T(i->g) Linear(x).reshape(..., 3)
|
|
24
|
+
|
|
25
|
+
v <- make_rot_vectors(x)
|
|
26
|
+
q_dir, k_dir <- make_rot_vectors(x)
|
|
27
|
+
q_dist, k_dist <- make_vectors(x)
|
|
28
|
+
|
|
29
|
+
A_ij <- dot(q_dir_i, k_dir_j) -||q_dist_i - k_dist_j||^2
|
|
30
|
+
x <- x + Linear(T(g->i) ATTN(A, v))
|
|
31
|
+
"""
|
|
32
|
+
super().__init__()
|
|
33
|
+
self.c_s = c_s
|
|
34
|
+
self.v_heads = v_heads
|
|
35
|
+
self.num_vector_messages = num_vector_messages
|
|
36
|
+
self.mask_and_zero_frameless = mask_and_zero_frameless
|
|
37
|
+
|
|
38
|
+
self.s_norm = nn.LayerNorm(c_s, bias=bias)
|
|
39
|
+
dim_proj = (
|
|
40
|
+
4 * self.v_heads * 3 + self.v_heads * 3 * self.num_vector_messages
|
|
41
|
+
) # 2 x (q, k) * number of heads * (x, y, z) + number of heads * number of vector messages * (x, y, z)
|
|
42
|
+
self.proj = nn.Linear(c_s, dim_proj, bias=bias)
|
|
43
|
+
channels_out = self.v_heads * 3 * self.num_vector_messages
|
|
44
|
+
self.out_proj = nn.Linear(channels_out, c_s, bias=bias)
|
|
45
|
+
|
|
46
|
+
# The basic idea is for some attention heads to pay more or less attention to rotation versus distance,
|
|
47
|
+
# as well as to control the sharpness of the softmax (i.e., should this head only attend to those residues
|
|
48
|
+
# very nearby or should there be shallower dropoff in attention weight?)
|
|
49
|
+
self.distance_scale_per_head = nn.Parameter(torch.zeros((self.v_heads)))
|
|
50
|
+
self.rotation_scale_per_head = nn.Parameter(torch.zeros((self.v_heads)))
|
|
51
|
+
|
|
52
|
+
def forward(self, s, affine, affine_mask, sequence_id, chain_id):
|
|
53
|
+
attn_bias = sequence_id.unsqueeze(-1) == sequence_id.unsqueeze(-2)
|
|
54
|
+
attn_bias = attn_bias.unsqueeze(1).float()
|
|
55
|
+
attn_bias = attn_bias.masked_fill(
|
|
56
|
+
~affine_mask[:, None, None, :], torch.finfo(attn_bias.dtype).min
|
|
57
|
+
)
|
|
58
|
+
chain_id_mask = chain_id.unsqueeze(1) != chain_id.unsqueeze(2)
|
|
59
|
+
attn_bias = attn_bias.masked_fill(
|
|
60
|
+
chain_id_mask.unsqueeze(1), torch.finfo(s.dtype).min
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
ns = self.s_norm(s)
|
|
64
|
+
vec_rot, vec_dist = self.proj(ns).split(
|
|
65
|
+
[
|
|
66
|
+
self.v_heads * 2 * 3 + self.v_heads * 3 * self.num_vector_messages,
|
|
67
|
+
self.v_heads * 2 * 3,
|
|
68
|
+
],
|
|
69
|
+
dim=-1,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Rotate the queries and keys for the rotation term. We also rotate the values.
|
|
73
|
+
# NOTE(zeming, thayes): Values are only rotated, not translated. We may wish to change
|
|
74
|
+
# this in the future.
|
|
75
|
+
query_rot, key_rot, value = (
|
|
76
|
+
affine.rot[..., None]
|
|
77
|
+
.apply(rearrange(vec_rot, "... (h c) -> ... h c", c=3))
|
|
78
|
+
.split(
|
|
79
|
+
[
|
|
80
|
+
self.v_heads,
|
|
81
|
+
self.v_heads,
|
|
82
|
+
self.v_heads * self.num_vector_messages,
|
|
83
|
+
],
|
|
84
|
+
dim=-2,
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Rotate and translate the queries and keys for the distance term
|
|
89
|
+
# NOTE(thayes): a simple speedup would be to apply all rotations together, then
|
|
90
|
+
# separately apply the translations.
|
|
91
|
+
query_dist, key_dist = (
|
|
92
|
+
affine[..., None]
|
|
93
|
+
.apply(rearrange(vec_dist, "... (h c) -> ... h c", c=3))
|
|
94
|
+
.chunk(2, dim=-2)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
query_dist = rearrange(query_dist, "b s h d -> b h s 1 d")
|
|
98
|
+
key_dist = rearrange(key_dist, "b s h d -> b h 1 s d")
|
|
99
|
+
query_rot = rearrange(query_rot, "b s h d -> b h s d")
|
|
100
|
+
key_rot = rearrange(key_rot, "b s h d -> b h d s")
|
|
101
|
+
value = rearrange(
|
|
102
|
+
value, "b s (h m) d -> b h s (m d)", m=self.num_vector_messages
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
distance_term = (query_dist - key_dist).norm(dim=-1) / sqrt(3)
|
|
106
|
+
rotation_term = query_rot.matmul(key_rot) / sqrt(3)
|
|
107
|
+
distance_term_weight = rearrange(
|
|
108
|
+
F.softplus(self.distance_scale_per_head), "h -> h 1 1"
|
|
109
|
+
)
|
|
110
|
+
rotation_term_weight = rearrange(
|
|
111
|
+
F.softplus(self.rotation_scale_per_head), "h -> h 1 1"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
attn_weight = (
|
|
115
|
+
rotation_term * rotation_term_weight - distance_term * distance_term_weight
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if attn_bias is not None:
|
|
119
|
+
# we can re-use the attention bias from the transformer layers
|
|
120
|
+
# NOTE(thayes): This attention bias is expected to handle two things:
|
|
121
|
+
# 1. Masking attention on padding tokens
|
|
122
|
+
# 2. Masking cross sequence attention in the case of bin packing
|
|
123
|
+
s_q = attn_weight.size(2)
|
|
124
|
+
s_k = attn_weight.size(3)
|
|
125
|
+
_s_q = max(0, attn_bias.size(2) - s_q)
|
|
126
|
+
_s_k = max(0, attn_bias.size(3) - s_k)
|
|
127
|
+
attn_bias = attn_bias[:, :, _s_q:, _s_k:]
|
|
128
|
+
attn_weight = attn_weight + attn_bias
|
|
129
|
+
|
|
130
|
+
attn_weight = torch.softmax(attn_weight, dim=-1)
|
|
131
|
+
|
|
132
|
+
attn_out = attn_weight.matmul(value)
|
|
133
|
+
|
|
134
|
+
attn_out = (
|
|
135
|
+
affine.rot[..., None]
|
|
136
|
+
.invert()
|
|
137
|
+
.apply(
|
|
138
|
+
rearrange(
|
|
139
|
+
attn_out, "b h s (m d) -> b s (h m) d", m=self.num_vector_messages
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
attn_out = rearrange(
|
|
145
|
+
attn_out, "b s (h m) d -> b s (h m d)", m=self.num_vector_messages
|
|
146
|
+
)
|
|
147
|
+
if self.mask_and_zero_frameless:
|
|
148
|
+
attn_out = attn_out.masked_fill(~affine_mask[..., None], 0.0)
|
|
149
|
+
s = self.out_proj(attn_out)
|
|
150
|
+
|
|
151
|
+
return s
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import torch.nn as nn
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def RegressionHead(
|
|
5
|
+
d_model: int,
|
|
6
|
+
output_dim: int,
|
|
7
|
+
hidden_dim: int | None = None,
|
|
8
|
+
) -> nn.Module:
|
|
9
|
+
"""Single-hidden layer MLP for supervised output.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
d_model: input dimension
|
|
13
|
+
output_dim: dimensionality of the output.
|
|
14
|
+
hidden_dim: optional dimension of hidden layer, defaults to d_model.
|
|
15
|
+
Returns:
|
|
16
|
+
output MLP module.
|
|
17
|
+
"""
|
|
18
|
+
hidden_dim = hidden_dim if hidden_dim is not None else d_model
|
|
19
|
+
return nn.Sequential(
|
|
20
|
+
nn.Linear(d_model, hidden_dim),
|
|
21
|
+
nn.GELU(),
|
|
22
|
+
nn.LayerNorm(hidden_dim),
|
|
23
|
+
nn.Linear(hidden_dim, output_dim),
|
|
24
|
+
)
|