| |
| |
| |
| |
|
|
| |
| |
|
|
| from typing import Union |
| import mlx.core as mx |
| import mlx.nn as nn |
|
|
| from .simplefold.mlx.esm_modules import ( |
| ContactPredictionHead, |
| ESM1bLayerNorm, |
| RobertaLMHead, |
| TransformerLayer, |
| ) |
| from onescience.datapipes.esm import Alphabet |
|
|
|
|
| def masked_fill_mlx(x, mask, value): |
| return mx.where(mask, value, x) |
|
|
|
|
| class ESM2(nn.Module): |
| def __init__( |
| self, |
| num_layers: int = 33, |
| embed_dim: int = 1280, |
| attention_heads: int = 20, |
| alphabet: Union[Alphabet, str] = "ESM-1b", |
| token_dropout: bool = True, |
| ): |
| super().__init__() |
| self.num_layers = num_layers |
| self.embed_dim = embed_dim |
| self.attention_heads = attention_heads |
| if not isinstance(alphabet, Alphabet): |
| alphabet = Alphabet.from_architecture(alphabet) |
| self.alphabet = alphabet |
| self.alphabet_size = len(alphabet) |
| self.padding_idx = alphabet.padding_idx |
| self.mask_idx = alphabet.mask_idx |
| self.cls_idx = alphabet.cls_idx |
| self.eos_idx = alphabet.eos_idx |
| self.prepend_bos = alphabet.prepend_bos |
| self.append_eos = alphabet.append_eos |
| self.token_dropout = token_dropout |
|
|
| self._init_submodules() |
|
|
| def _init_submodules(self): |
| self.embed_scale = 1 |
| self.embed_tokens = mx.zeros((self.alphabet_size, self.embed_dim)) |
|
|
| self.layers = [ |
| TransformerLayer( |
| self.embed_dim, |
| 4 * self.embed_dim, |
| self.attention_heads, |
| add_bias_kv=False, |
| use_esm1b_layer_norm=True, |
| use_rotary_embeddings=True, |
| ) |
| for _ in range(self.num_layers) |
| ] |
|
|
| self.contact_head = ContactPredictionHead( |
| self.num_layers * self.attention_heads, |
| self.prepend_bos, |
| self.append_eos, |
| eos_idx=self.eos_idx, |
| ) |
| self.emb_layer_norm_after = ESM1bLayerNorm(self.embed_dim) |
|
|
| self.lm_head = RobertaLMHead( |
| embed_dim=self.embed_dim, |
| output_dim=self.alphabet_size, |
| weight=self.embed_tokens, |
| ) |
|
|
| def __call__( |
| self, tokens, repr_layers=[], need_head_weights=False, return_contacts=False |
| ): |
| if return_contacts: |
| need_head_weights = True |
|
|
| assert tokens.ndim == 2 |
| padding_mask = mx.equal(tokens, self.padding_idx) |
|
|
| x = self.embed_scale * self.embed_tokens[tokens, :] |
|
|
| if self.token_dropout: |
| x = masked_fill_mlx(x, (tokens == self.mask_idx)[..., None], 0.0) |
| |
| mask_ratio_train = 0.15 * 0.8 |
| src_lengths = (~padding_mask).sum(axis=-1) |
| mask_ratio_observed = (tokens == self.mask_idx).sum(axis=-1).astype( |
| x.dtype |
| ) / src_lengths |
| x = x * (1 - mask_ratio_train) / (1 - mask_ratio_observed)[:, None, None] |
|
|
| if padding_mask is not None: |
| x = x * (1 - padding_mask[..., None].astype(x.dtype)) |
|
|
| repr_layers = set(repr_layers) |
| hidden_representations = {} |
| if 0 in repr_layers: |
| hidden_representations[0] = x |
|
|
| if need_head_weights: |
| attn_weights = [] |
|
|
| |
| x = mx.swapaxes(x, axis1=0, axis2=1) |
|
|
| if not padding_mask.any(): |
| padding_mask = None |
|
|
| for layer_idx, layer in enumerate(self.layers): |
| x, attn = layer( |
| x, |
| self_attn_padding_mask=padding_mask, |
| need_head_weights=need_head_weights, |
| ) |
|
|
| if (layer_idx + 1) in repr_layers: |
| hidden_representations[layer_idx + 1] = mx.swapaxes(x, axis1=0, axis2=1) |
| if need_head_weights: |
| |
| attn_weights.append(mx.swapaxes(attn, axis1=1, axis2=0)) |
|
|
| x = self.emb_layer_norm_after(x) |
| x = mx.swapaxes(x, axis1=0, axis2=1) |
|
|
| |
| if (layer_idx + 1) in repr_layers: |
| hidden_representations[layer_idx + 1] = x |
| x = self.lm_head(x) |
|
|
| result = {"logits": x, "representations": hidden_representations} |
| if need_head_weights: |
| |
| attentions = mx.stack(attn_weights, axis=1) |
| if padding_mask is not None: |
| attention_mask = 1 - padding_mask.astype(attentions.dtype) |
| attention_mask = ( |
| attention_mask[:, None, ...] * attention_mask[:, :, None, ...] |
| ) |
| attentions = attentions * attention_mask[:, None, None, :, :] |
| result["attentions"] = attentions |
| if return_contacts: |
| contacts = self.contact_head(tokens, attentions) |
| result["contacts"] = contacts |
|
|
| return result |
|
|
| def predict_contacts(self, tokens): |
| return self(tokens, return_contacts=True)["contacts"] |
|
|