Taykhoom commited on
Commit
3a5238d
·
0 Parent(s):

Initial RiNALMo-mega Hugging Face port

Browse files
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ tags:
4
+ - RNA
5
+ - language-model
6
+ - biology
7
+ license: cc-by-4.0
8
+ ---
9
+
10
+ # RiNALMo-mega
11
+
12
+ Minimal HuggingFace port of the **mega** (148.1M-parameter) variant of
13
+ [RiNALMo](https://doi.org/10.1038/s41467-025-60872-5) -- a general-purpose RNA language model
14
+ pre-trained on 36 million non-coding RNA sequences.
15
+
16
+ ## Architecture
17
+
18
+ | Parameter | Value |
19
+ |---|---|
20
+ | Layers | 30 |
21
+ | Attention heads | 20 |
22
+ | Embedding dimension | 640 |
23
+ | FFN hidden dimension | 1706 (SwiGLU, floor(2/3 x 4 x embed)) |
24
+ | Parameters | 148,056,972 |
25
+ | Vocabulary size | 22 |
26
+ | Positional encoding | RoPE (base=10000, non-interleaved) |
27
+ | Normalization | LayerNorm (eps=1e-5) |
28
+ | Architecture | Pre-LN Transformer with SwiGLU FFN |
29
+ | Max sequence length | ~8192 (practical; RoPE has no hard limit) |
30
+
31
+ **Vocabulary** (index order): `<cls>` (0), `<pad>` (1), `<eos>` (2), `<unk>` (3),
32
+ `<mask>` (4), A (5), C (6), G (7), T (8), I (9), R (10), Y (11), K (12), M (13),
33
+ S (14), W (15), B (16), D (17), H (18), V (19), N (20), `-` (21).
34
+
35
+ Note: the tokenizer converts **U -> T** before encoding (the model was trained on T).
36
+
37
+ ## Pretraining
38
+
39
+ - **Objective:** Masked language modeling (BERT-style, 15% mask rate)
40
+ - **Data:** 36 million non-coding RNA sequences from multiple databases
41
+ - **Source checkpoint:** `rinalmo_mega_pretrained.pt` from [Zenodo 15043668](https://zenodo.org/records/15043668)
42
+
43
+ ### Checkpoint selection
44
+
45
+ The mega variant (148.1M params) offers a strong quality/cost tradeoff. Use micro for
46
+ fast inference; use giga for maximum representation quality.
47
+
48
+ ## Parity Verification
49
+
50
+ All 31 representation levels (embedding + 30 transformer layers) verified to be
51
+ bit-exact (max abs diff = 0.00) against a pure-PyTorch reference that loads the
52
+ original weights. Weight mapping verified for all 390 per-block tensors.
53
+ Eager and SDPA implementations agree within 4e-6 on padded batches.
54
+
55
+ ## Related Models
56
+
57
+ See the full [RiNALMo collection](https://huggingface.co/collections/Taykhoom/rinalmo-6a17c182db99be3f7a0d7bac).
58
+
59
+ | Model | Parameters | Notes |
60
+ |---|---|---|
61
+ | [RiNALMo-micro](https://huggingface.co/Taykhoom/RiNALMo-micro) | 33.5M | Smallest variant |
62
+ | **[RiNALMo-mega](https://huggingface.co/Taykhoom/RiNALMo-mega)** | 148.1M | This model |
63
+ | [RiNALMo-giga](https://huggingface.co/Taykhoom/RiNALMo-giga) | 650.9M | Full model |
64
+
65
+ ## Usage
66
+
67
+ ### Embedding generation
68
+
69
+ ```python
70
+ import torch
71
+ from transformers import AutoTokenizer, AutoModel
72
+
73
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/RiNALMo-mega", trust_remote_code=True)
74
+ model = AutoModel.from_pretrained("Taykhoom/RiNALMo-mega", trust_remote_code=True)
75
+ model.eval()
76
+
77
+ sequences = ["ACUUUGGCCA", "CCCGGU"]
78
+ enc = tokenizer(sequences, return_tensors="pt", padding=True)
79
+
80
+ with torch.no_grad():
81
+ out = model(**enc)
82
+
83
+ cls_emb = out.last_hidden_state[:, 0, :] # (batch, 640) -- CLS token
84
+ token_emb = out.last_hidden_state # (batch, seq_len, 640)
85
+
86
+ # Intermediate layers
87
+ out_all = model(**enc, output_hidden_states=True)
88
+ layer15_emb = out_all.hidden_states[15] # after block 15
89
+ ```
90
+
91
+ ### MLM logits
92
+
93
+ ```python
94
+ from transformers import AutoTokenizer, AutoModelForMaskedLM
95
+
96
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/RiNALMo-mega", trust_remote_code=True)
97
+ model = AutoModelForMaskedLM.from_pretrained("Taykhoom/RiNALMo-mega", trust_remote_code=True)
98
+ model.eval()
99
+
100
+ enc = tokenizer(["ACU<mask>UGGCCA"], return_tensors="pt")
101
+ with torch.no_grad():
102
+ logits = model(**enc).logits # (1, seq_len, 22)
103
+ ```
104
+
105
+ ### Faster attention backends
106
+
107
+ ```python
108
+ # SDPA (PyTorch 2.0+)
109
+ model = AutoModel.from_pretrained("Taykhoom/RiNALMo-mega", trust_remote_code=True,
110
+ attn_implementation="sdpa")
111
+
112
+ # Flash Attention 2 (requires flash-attn package)
113
+ model = AutoModel.from_pretrained("Taykhoom/RiNALMo-mega", trust_remote_code=True,
114
+ attn_implementation="flash_attention_2",
115
+ dtype=torch.bfloat16)
116
+ ```
117
+
118
+ ### Fine-tuning
119
+
120
+ Standard HF conventions. For sequence-level tasks, pool over non-padding positions
121
+ or use the CLS token embedding as input to a prediction head.
122
+
123
+ ## Implementation Notes
124
+
125
+ The original RiNALMo uses Flash Attention 2.3.2 during training. This HF port exposes
126
+ eager (standard PyTorch), SDPA, and Flash Attention 2 through HuggingFace's
127
+ `attn_implementation` dispatch. SDPA and the dispatch interface are additions; the
128
+ Flash backend preserves the original non-causal Flash Attention design.
129
+
130
+ The model uses a non-standard Pre-LN residual: the attention residual connection is
131
+ taken from the *normalized* input (i.e., `x = attn_ln(x); x = x + attn(x)`) rather
132
+ than the original. The FFN uses standard Pre-LN.
133
+
134
+ TokenDropout rescales embeddings by `(1 - mask_ratio_train) / (1 - mask_ratio_observed)`
135
+ even at inference, consistent with the original training code.
136
+
137
+ ## Citation
138
+
139
+ ```bibtex
140
+ @article{penic2025_rinalmo,
141
+ title = {RiNALMo: general-purpose {RNA} language models can generalize well on structure prediction tasks},
142
+ author = {Penić, Rafael Josip and Vlašić, Tin and Huber, Roland G. and Wan, Yue and Šikić, Mile},
143
+ journal = {Nature Communications},
144
+ volume = {16},
145
+ number = {1},
146
+ pages = {5671},
147
+ year = {2025},
148
+ doi = {10.1038/s41467-025-60872-5}
149
+ }
150
+ ```
151
+
152
+ ## Credits
153
+
154
+ Original model and code by Penić et al. Source: [GitHub lbcb-sci/RiNALMo](https://github.com/lbcb-sci/RiNALMo).
155
+ The HF conversion code was authored primarily by [Claude Code](https://claude.ai/code)
156
+ and reviewed manually by Taykhoom Dalal.
157
+
158
+ ## License
159
+
160
+ Apache 2.0 (code) / CC BY 4.0 (model weights), following the original repository.
config.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "RiNALMoForMaskedLM"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_rinalmo.RiNALMoConfig",
7
+ "AutoModel": "modeling_rinalmo.RiNALMoModel",
8
+ "AutoModelForMaskedLM": "modeling_rinalmo.RiNALMoForMaskedLM"
9
+ },
10
+ "attention_dropout": 0.1,
11
+ "cls_idx": 0,
12
+ "dtype": "float32",
13
+ "embed_dim": 640,
14
+ "eos_idx": 2,
15
+ "mask_idx": 4,
16
+ "mask_ratio": 0.15,
17
+ "mask_tkn_prob": 0.8,
18
+ "model_max_length": 8192,
19
+ "model_type": "rinalmo",
20
+ "num_heads": 20,
21
+ "num_layers": 30,
22
+ "padding_idx": 1,
23
+ "residual_dropout": 0.1,
24
+ "rope_base": 10000,
25
+ "token_dropout_active": true,
26
+ "transformers_version": "4.57.6",
27
+ "transition_dropout": 0.0,
28
+ "transition_factor": 4,
29
+ "unk_idx": 3,
30
+ "use_rot_emb": true,
31
+ "vocab_size": 22
32
+ }
configuration_rinalmo.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+
3
+
4
+ class RiNALMoConfig(PretrainedConfig):
5
+ model_type = "rinalmo"
6
+
7
+ auto_map = {
8
+ "AutoConfig": "configuration_rinalmo.RiNALMoConfig",
9
+ "AutoModel": "modeling_rinalmo.RiNALMoModel",
10
+ "AutoModelForMaskedLM": "modeling_rinalmo.RiNALMoForMaskedLM",
11
+ }
12
+
13
+ def __init__(
14
+ self,
15
+ vocab_size: int = 22,
16
+ embed_dim: int = 1280,
17
+ num_layers: int = 33,
18
+ num_heads: int = 20,
19
+ transition_factor: int = 4,
20
+ padding_idx: int = 1,
21
+ mask_idx: int = 4,
22
+ cls_idx: int = 0,
23
+ eos_idx: int = 2,
24
+ unk_idx: int = 3,
25
+ use_rot_emb: bool = True,
26
+ rope_base: int = 10000,
27
+ attention_dropout: float = 0.1,
28
+ transition_dropout: float = 0.0,
29
+ residual_dropout: float = 0.1,
30
+ token_dropout_active: bool = True,
31
+ mask_ratio: float = 0.15,
32
+ mask_tkn_prob: float = 0.8,
33
+ model_max_length: int = 8192,
34
+ **kwargs,
35
+ ):
36
+ super().__init__(padding_idx=padding_idx, **kwargs)
37
+ self.vocab_size = vocab_size
38
+ self.embed_dim = embed_dim
39
+ self.num_layers = num_layers
40
+ self.num_heads = num_heads
41
+ self.transition_factor = transition_factor
42
+ self.mask_idx = mask_idx
43
+ self.cls_idx = cls_idx
44
+ self.eos_idx = eos_idx
45
+ self.unk_idx = unk_idx
46
+ self.use_rot_emb = use_rot_emb
47
+ self.rope_base = rope_base
48
+ self.attention_dropout = attention_dropout
49
+ self.transition_dropout = transition_dropout
50
+ self.residual_dropout = residual_dropout
51
+ self.token_dropout_active = token_dropout_active
52
+ self.mask_ratio = mask_ratio
53
+ self.mask_tkn_prob = mask_tkn_prob
54
+ self.model_max_length = model_max_length
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bcde6c45200027908bf2caf902edf0d114528dcedea917e0f382d6d52806b8ef
3
+ size 592274840
modeling_rinalmo.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+
6
+ from transformers import PreTrainedModel
7
+ from transformers.modeling_outputs import BaseModelOutput, MaskedLMOutput
8
+
9
+ try:
10
+ from .configuration_rinalmo import RiNALMoConfig
11
+ except ImportError:
12
+ from configuration_rinalmo import RiNALMoConfig
13
+
14
+
15
+ def _rotate_half(x):
16
+ x1, x2 = x.chunk(2, dim=-1)
17
+ return torch.cat((-x2, x1), dim=-1)
18
+
19
+
20
+ def _apply_rotary_pos_emb(q, k, cos, sin):
21
+ cos = cos.to(q.dtype)
22
+ sin = sin.to(q.dtype)
23
+ return (q * cos) + (_rotate_half(q) * sin), (k * cos) + (_rotate_half(k) * sin)
24
+
25
+
26
+ class RotaryPositionEmbedding(nn.Module):
27
+ def __init__(self, dim: int, base: int = 10000):
28
+ super().__init__()
29
+ inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
30
+ self.register_buffer("inv_freq", inv_freq)
31
+ self._seq_len_cached = None
32
+ self._cos_cached = None
33
+ self._sin_cached = None
34
+
35
+ def _update_cache(self, seq_len: int, device, dtype):
36
+ if seq_len != self._seq_len_cached:
37
+ self._seq_len_cached = seq_len
38
+ t = torch.arange(seq_len, device=device).type_as(self.inv_freq)
39
+ freqs = torch.einsum("i,j->ij", t, self.inv_freq)
40
+ emb = torch.cat((freqs, freqs), dim=-1)
41
+ self._cos_cached = emb.cos()[None, None, :, :]
42
+ self._sin_cached = emb.sin()[None, None, :, :]
43
+
44
+ def forward(self, q, k):
45
+ self._update_cache(q.shape[-2], q.device, q.dtype)
46
+ return _apply_rotary_pos_emb(q, k, self._cos_cached, self._sin_cached)
47
+
48
+
49
+ class RiNALMoAttention(nn.Module):
50
+ def __init__(self, config: RiNALMoConfig):
51
+ super().__init__()
52
+ self.embed_dim = config.embed_dim
53
+ self.num_heads = config.num_heads
54
+ self.head_dim = config.embed_dim // config.num_heads
55
+
56
+ self.qkv_proj = nn.Linear(config.embed_dim, 3 * config.embed_dim, bias=False)
57
+ self.out_proj = nn.Linear(config.embed_dim, config.embed_dim, bias=False)
58
+ self.attn_dropout = nn.Dropout(p=config.attention_dropout)
59
+
60
+ if config.use_rot_emb:
61
+ self.rotary_emb = RotaryPositionEmbedding(self.head_dim, base=config.rope_base)
62
+ else:
63
+ self.rotary_emb = None
64
+
65
+ def forward(self, x, key_padding_mask=None, output_attentions=False):
66
+ B, T, _ = x.shape
67
+
68
+ qkv = self.qkv_proj(x)
69
+ q, k, v = qkv.chunk(3, dim=-1)
70
+
71
+ q = q.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
72
+ k = k.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
73
+ v = v.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
74
+
75
+ if self.rotary_emb is not None:
76
+ q, k = self.rotary_emb(q, k)
77
+
78
+ scale = math.sqrt(self.head_dim)
79
+ attn = torch.matmul(q, k.transpose(-1, -2)) / scale
80
+
81
+ if key_padding_mask is not None:
82
+ attn = attn.masked_fill(key_padding_mask.unsqueeze(1).unsqueeze(2), float("-inf"))
83
+
84
+ attn = attn.softmax(dim=-1)
85
+ attn_weights = attn if output_attentions else None
86
+ attn = self.attn_dropout(attn)
87
+
88
+ out = torch.matmul(attn, v)
89
+ out = out.transpose(1, 2).contiguous().view(B, T, self.embed_dim)
90
+ out = self.out_proj(out)
91
+ return out, attn_weights
92
+
93
+
94
+ class RiNALMoSdpaAttention(RiNALMoAttention):
95
+ def forward(self, x, key_padding_mask=None, output_attentions=False):
96
+ if output_attentions:
97
+ return super().forward(x, key_padding_mask, output_attentions=True)
98
+
99
+ B, T, _ = x.shape
100
+
101
+ qkv = self.qkv_proj(x)
102
+ q, k, v = qkv.chunk(3, dim=-1)
103
+
104
+ q = q.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
105
+ k = k.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
106
+ v = v.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
107
+
108
+ if self.rotary_emb is not None:
109
+ q, k = self.rotary_emb(q, k)
110
+
111
+ attn_mask = None
112
+ if key_padding_mask is not None:
113
+ attn_mask = torch.zeros(B, 1, 1, T, dtype=q.dtype, device=q.device)
114
+ attn_mask = attn_mask.masked_fill(key_padding_mask.unsqueeze(1).unsqueeze(2), float("-inf"))
115
+
116
+ out = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask, dropout_p=0.0)
117
+ out = out.transpose(1, 2).contiguous().view(B, T, self.embed_dim)
118
+ out = self.out_proj(out)
119
+ return out, None
120
+
121
+
122
+ class RiNALMoFlashAttention2(RiNALMoAttention):
123
+ def forward(self, x, key_padding_mask=None, output_attentions=False):
124
+ if output_attentions:
125
+ return super().forward(x, key_padding_mask, output_attentions=True)
126
+
127
+ try:
128
+ from flash_attn import flash_attn_func, flash_attn_varlen_func
129
+ from flash_attn.bert_padding import pad_input, unpad_input
130
+ except ImportError as e:
131
+ raise ImportError(
132
+ "flash_attn is required for attn_implementation='flash_attention_2'. "
133
+ "Install with: pip install flash-attn --no-build-isolation"
134
+ ) from e
135
+
136
+ B, T, _ = x.shape
137
+
138
+ qkv = self.qkv_proj(x)
139
+ q, k, v = qkv.chunk(3, dim=-1)
140
+
141
+ q = q.view(B, T, self.num_heads, self.head_dim)
142
+ k = k.view(B, T, self.num_heads, self.head_dim)
143
+ v = v.view(B, T, self.num_heads, self.head_dim)
144
+
145
+ if self.rotary_emb is not None:
146
+ q_t = q.transpose(1, 2)
147
+ k_t = k.transpose(1, 2)
148
+ q_t, k_t = self.rotary_emb(q_t, k_t)
149
+ q = q_t.transpose(1, 2)
150
+ k = k_t.transpose(1, 2)
151
+
152
+ orig_dtype = q.dtype
153
+ if q.dtype not in (torch.float16, torch.bfloat16):
154
+ q = q.to(torch.bfloat16)
155
+ k = k.to(torch.bfloat16)
156
+ v = v.to(torch.bfloat16)
157
+
158
+ if key_padding_mask is not None and key_padding_mask.any():
159
+ attend_mask = ~key_padding_mask
160
+ q_unpad, indices, cu_seqlens, max_seqlen, _ = unpad_input(q, attend_mask)
161
+ k_unpad, *_ = unpad_input(k, attend_mask)
162
+ v_unpad, *_ = unpad_input(v, attend_mask)
163
+
164
+ out_unpad = flash_attn_varlen_func(
165
+ q_unpad, k_unpad, v_unpad,
166
+ cu_seqlens_q=cu_seqlens, cu_seqlens_k=cu_seqlens,
167
+ max_seqlen_q=max_seqlen, max_seqlen_k=max_seqlen,
168
+ causal=False,
169
+ )
170
+ out = pad_input(out_unpad.view(-1, self.embed_dim), indices, B, T)
171
+ else:
172
+ out = flash_attn_func(q, k, v, causal=False)
173
+ out = out.view(B, T, self.embed_dim)
174
+
175
+ out = out.to(orig_dtype)
176
+ out = self.out_proj(out)
177
+ return out, None
178
+
179
+
180
+ RINALMO_ATTENTION_CLASSES = {
181
+ "eager": RiNALMoAttention,
182
+ "sdpa": RiNALMoSdpaAttention,
183
+ "flash_attention_2": RiNALMoFlashAttention2,
184
+ }
185
+
186
+
187
+ class RiNALMoSwiGLU(nn.Module):
188
+ def __init__(self, embed_dim: int, ffn_dim: int):
189
+ super().__init__()
190
+ self.linear = nn.Linear(embed_dim, ffn_dim, bias=True)
191
+ self.linear_gate = nn.Linear(embed_dim, ffn_dim, bias=True)
192
+ self.beta = nn.Parameter(torch.ones(1))
193
+
194
+ def forward(self, x):
195
+ gate = self.linear(x)
196
+ swish = gate * torch.sigmoid(self.beta * gate)
197
+ return swish * self.linear_gate(x)
198
+
199
+
200
+ class TokenDropout(nn.Module):
201
+ def __init__(self, active: bool, mask_ratio: float, mask_tkn_prob: float,
202
+ mask_idx: int, padding_idx: int):
203
+ super().__init__()
204
+ self.active = active
205
+ self.mask_ratio_train = mask_ratio * mask_tkn_prob
206
+ self.mask_idx = mask_idx
207
+ self.padding_idx = padding_idx
208
+
209
+ def forward(self, x, tokens):
210
+ if not self.active:
211
+ return x
212
+ pad_mask = tokens.eq(self.padding_idx)
213
+ src_lens = (~pad_mask).sum(dim=-1).to(x.dtype)
214
+ x = torch.where((tokens == self.mask_idx).unsqueeze(-1), torch.zeros_like(x), x)
215
+ mask_ratio_obs = (tokens == self.mask_idx).sum(dim=-1).to(x.dtype) / src_lens
216
+ scale = (1.0 - self.mask_ratio_train) / (1.0 - mask_ratio_obs)
217
+ x = x * scale[:, None, None]
218
+ return x
219
+
220
+
221
+ class RiNALMoLayer(nn.Module):
222
+ def __init__(self, config: RiNALMoConfig):
223
+ super().__init__()
224
+ ffn_dim = int(2 / 3 * config.transition_factor * config.embed_dim)
225
+ attn_cls = RINALMO_ATTENTION_CLASSES[getattr(config, "_attn_implementation", "eager")]
226
+
227
+ self.attn_layer_norm = nn.LayerNorm(config.embed_dim)
228
+ self.attn = attn_cls(config)
229
+ self.out_layer_norm = nn.LayerNorm(config.embed_dim)
230
+ self.ffn = RiNALMoSwiGLU(config.embed_dim, ffn_dim)
231
+ self.ffn_dropout = nn.Dropout(p=config.transition_dropout)
232
+ self.ffn_down = nn.Linear(ffn_dim, config.embed_dim, bias=True)
233
+ self.residual_dropout_1 = nn.Dropout(p=config.residual_dropout)
234
+ self.residual_dropout_2 = nn.Dropout(p=config.residual_dropout)
235
+
236
+ def forward(self, x, key_padding_mask=None, output_attentions=False):
237
+ x = self.attn_layer_norm(x)
238
+ attn_out, attn_weights = self.attn(x, key_padding_mask=key_padding_mask,
239
+ output_attentions=output_attentions)
240
+ x = x + self.residual_dropout_1(attn_out)
241
+
242
+ residual = x
243
+ x = self.out_layer_norm(x)
244
+ x = residual + self.residual_dropout_2(self.ffn_down(self.ffn_dropout(self.ffn(x))))
245
+
246
+ return x, attn_weights
247
+
248
+
249
+ class RiNALMoPreTrainedModel(PreTrainedModel):
250
+ config_class = RiNALMoConfig
251
+ base_model_prefix = "model"
252
+ _supports_sdpa = True
253
+ _supports_flash_attn_2 = True
254
+
255
+ def _init_weights(self, module):
256
+ if isinstance(module, (nn.Linear, nn.Embedding)):
257
+ module.weight.data.normal_(mean=0.0, std=0.02)
258
+ if isinstance(module, nn.Linear) and module.bias is not None:
259
+ module.bias.data.zero_()
260
+ elif isinstance(module, nn.LayerNorm):
261
+ module.bias.data.zero_()
262
+ module.weight.data.fill_(1.0)
263
+
264
+
265
+ class RiNALMoModel(RiNALMoPreTrainedModel):
266
+ def __init__(self, config: RiNALMoConfig):
267
+ super().__init__(config)
268
+ self.embedding = nn.Embedding(config.vocab_size, config.embed_dim, padding_idx=config.padding_idx)
269
+ self.token_dropout = TokenDropout(
270
+ active=config.token_dropout_active,
271
+ mask_ratio=config.mask_ratio,
272
+ mask_tkn_prob=config.mask_tkn_prob,
273
+ mask_idx=config.mask_idx,
274
+ padding_idx=config.padding_idx,
275
+ )
276
+ self.layers = nn.ModuleList([RiNALMoLayer(config) for _ in range(config.num_layers)])
277
+ self.final_layer_norm = nn.LayerNorm(config.embed_dim)
278
+ self.post_init()
279
+
280
+ def forward(
281
+ self,
282
+ input_ids,
283
+ attention_mask=None,
284
+ output_hidden_states=None,
285
+ output_attentions=None,
286
+ return_dict=None,
287
+ ):
288
+ output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
289
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
290
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
291
+
292
+ if attention_mask is not None:
293
+ key_padding_mask = attention_mask.eq(0)
294
+ else:
295
+ key_padding_mask = input_ids.eq(self.config.padding_idx)
296
+
297
+ x = self.embedding(input_ids)
298
+ x = self.token_dropout(x, input_ids)
299
+
300
+ all_hidden_states = []
301
+ all_attentions = []
302
+ if output_hidden_states:
303
+ all_hidden_states.append(x)
304
+
305
+ for layer in self.layers:
306
+ x, attn_weights = layer(x, key_padding_mask=key_padding_mask,
307
+ output_attentions=output_attentions)
308
+ if output_hidden_states:
309
+ all_hidden_states.append(x)
310
+ if output_attentions:
311
+ all_attentions.append(attn_weights)
312
+
313
+ x = self.final_layer_norm(x)
314
+
315
+ return BaseModelOutput(
316
+ last_hidden_state=x,
317
+ hidden_states=tuple(all_hidden_states) if output_hidden_states else None,
318
+ attentions=tuple(all_attentions) if output_attentions else None,
319
+ )
320
+
321
+
322
+ class RiNALMoForMaskedLM(RiNALMoPreTrainedModel):
323
+ def __init__(self, config: RiNALMoConfig):
324
+ super().__init__(config)
325
+ self.model = RiNALMoModel(config)
326
+ self.lm_head = RiNALMoLMHead(config)
327
+ self.post_init()
328
+
329
+ def forward(
330
+ self,
331
+ input_ids,
332
+ attention_mask=None,
333
+ labels=None,
334
+ output_hidden_states=None,
335
+ output_attentions=None,
336
+ return_dict=None,
337
+ ):
338
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
339
+ out = self.model(input_ids, attention_mask=attention_mask,
340
+ output_hidden_states=output_hidden_states,
341
+ output_attentions=output_attentions, return_dict=return_dict)
342
+ logits = self.lm_head(out.last_hidden_state)
343
+ loss = None
344
+ if labels is not None:
345
+ loss = F.cross_entropy(logits.view(-1, self.config.vocab_size),
346
+ labels.view(-1), ignore_index=-100)
347
+ return MaskedLMOutput(loss=loss, logits=logits,
348
+ hidden_states=out.hidden_states,
349
+ attentions=out.attentions)
350
+
351
+
352
+ class RiNALMoLMHead(nn.Module):
353
+ def __init__(self, config: RiNALMoConfig):
354
+ super().__init__()
355
+ self.linear1 = nn.Linear(config.embed_dim, config.embed_dim)
356
+ self.layer_norm = nn.LayerNorm(config.embed_dim)
357
+ self.linear2 = nn.Linear(config.embed_dim, config.vocab_size)
358
+
359
+ def forward(self, x):
360
+ x = self.linear1(x)
361
+ x = F.gelu(x)
362
+ x = self.layer_norm(x)
363
+ x = self.linear2(x)
364
+ return x
special_tokens_map.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "cls_token": "<cls>",
3
+ "eos_token": "<eos>",
4
+ "mask_token": "<mask>",
5
+ "pad_token": "<pad>",
6
+ "unk_token": "<unk>"
7
+ }
tokenization_rinalmo.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from typing import Dict, List, Optional
4
+
5
+ from transformers import PreTrainedTokenizer
6
+
7
+
8
+ _VOCAB = {
9
+ "<cls>": 0,
10
+ "<pad>": 1,
11
+ "<eos>": 2,
12
+ "<unk>": 3,
13
+ "<mask>": 4,
14
+ "A": 5,
15
+ "C": 6,
16
+ "G": 7,
17
+ "T": 8,
18
+ "I": 9,
19
+ "R": 10,
20
+ "Y": 11,
21
+ "K": 12,
22
+ "M": 13,
23
+ "S": 14,
24
+ "W": 15,
25
+ "B": 16,
26
+ "D": 17,
27
+ "H": 18,
28
+ "V": 19,
29
+ "N": 20,
30
+ "-": 21,
31
+ }
32
+
33
+
34
+ class RiNALMoTokenizer(PreTrainedTokenizer):
35
+ """
36
+ Tokenizer for RiNALMo. Character-level over a 22-token RNA alphabet.
37
+ Converts U->T before tokenizing (the model was trained on T, not U).
38
+ Wraps sequences as <cls> ... <eos>.
39
+ """
40
+
41
+ vocab_files_names = {"vocab_file": "vocab.json"}
42
+ model_input_names = ["input_ids", "attention_mask"]
43
+
44
+ def __init__(
45
+ self,
46
+ vocab_file: Optional[str] = None,
47
+ cls_token: str = "<cls>",
48
+ pad_token: str = "<pad>",
49
+ eos_token: str = "<eos>",
50
+ unk_token: str = "<unk>",
51
+ mask_token: str = "<mask>",
52
+ **kwargs,
53
+ ):
54
+ if vocab_file is not None and os.path.isfile(vocab_file):
55
+ with open(vocab_file) as f:
56
+ self._vocab = json.load(f)
57
+ else:
58
+ self._vocab = dict(_VOCAB)
59
+ self._ids_to_tokens = {v: k for k, v in self._vocab.items()}
60
+ super().__init__(
61
+ cls_token=cls_token,
62
+ pad_token=pad_token,
63
+ eos_token=eos_token,
64
+ unk_token=unk_token,
65
+ mask_token=mask_token,
66
+ **kwargs,
67
+ )
68
+
69
+ @property
70
+ def vocab_size(self) -> int:
71
+ return len(self._vocab)
72
+
73
+ def get_vocab(self) -> Dict[str, int]:
74
+ return dict(self._vocab)
75
+
76
+ def _tokenize(self, text: str) -> List[str]:
77
+ text = text.upper().replace("U", "T")
78
+ return list(text)
79
+
80
+ def _convert_token_to_id(self, token: str) -> int:
81
+ return self._vocab.get(token, self._vocab["<unk>"])
82
+
83
+ def _convert_id_to_token(self, index: int) -> str:
84
+ return self._ids_to_tokens.get(index, "<unk>")
85
+
86
+ def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None):
87
+ os.makedirs(save_directory, exist_ok=True)
88
+ fname = (filename_prefix + "-" if filename_prefix else "") + "vocab.json"
89
+ path = os.path.join(save_directory, fname)
90
+ with open(path, "w") as f:
91
+ json.dump(self._vocab, f, indent=2)
92
+ return (path,)
93
+
94
+ def build_inputs_with_special_tokens(
95
+ self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
96
+ ) -> List[int]:
97
+ cls = [self.cls_token_id]
98
+ eos = [self.eos_token_id]
99
+ if token_ids_1 is None:
100
+ return cls + token_ids_0 + eos
101
+ return cls + token_ids_0 + eos + cls + token_ids_1 + eos
102
+
103
+ def get_special_tokens_mask(
104
+ self,
105
+ token_ids_0: List[int],
106
+ token_ids_1: Optional[List[int]] = None,
107
+ already_has_special_tokens: bool = False,
108
+ ) -> List[int]:
109
+ if already_has_special_tokens:
110
+ return super().get_special_tokens_mask(token_ids_0, token_ids_1, True)
111
+ mask = [1] + [0] * len(token_ids_0) + [1]
112
+ if token_ids_1 is not None:
113
+ mask += [1] + [0] * len(token_ids_1) + [1]
114
+ return mask
115
+
116
+ def create_token_type_ids_from_sequences(
117
+ self,
118
+ token_ids_0: List[int],
119
+ token_ids_1: Optional[List[int]] = None,
120
+ ) -> List[int]:
121
+ if token_ids_1 is None:
122
+ return [0] * (len(token_ids_0) + 2)
123
+ return [0] * (len(token_ids_0) + 2) + [0] * (len(token_ids_1) + 2)
tokenizer_config.json ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "<cls>",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "1": {
12
+ "content": "<pad>",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "2": {
20
+ "content": "<eos>",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "3": {
28
+ "content": "<unk>",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": true
34
+ },
35
+ "4": {
36
+ "content": "<mask>",
37
+ "lstrip": false,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": true
42
+ }
43
+ },
44
+ "clean_up_tokenization_spaces": false,
45
+ "cls_token": "<cls>",
46
+ "eos_token": "<eos>",
47
+ "extra_special_tokens": {},
48
+ "mask_token": "<mask>",
49
+ "model_max_length": 8192,
50
+ "pad_token": "<pad>",
51
+ "tokenizer_class": "RiNALMoTokenizer",
52
+ "unk_token": "<unk>",
53
+ "auto_map": {
54
+ "AutoTokenizer": [
55
+ "tokenization_rinalmo.RiNALMoTokenizer",
56
+ null
57
+ ]
58
+ }
59
+ }
vocab.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "<cls>": 0,
3
+ "<pad>": 1,
4
+ "<eos>": 2,
5
+ "<unk>": 3,
6
+ "<mask>": 4,
7
+ "A": 5,
8
+ "C": 6,
9
+ "G": 7,
10
+ "T": 8,
11
+ "I": 9,
12
+ "R": 10,
13
+ "Y": 11,
14
+ "K": 12,
15
+ "M": 13,
16
+ "S": 14,
17
+ "W": 15,
18
+ "B": 16,
19
+ "D": 17,
20
+ "H": 18,
21
+ "V": 19,
22
+ "N": 20,
23
+ "-": 21
24
+ }