Taykhoom commited on
Commit
b932f65
·
0 Parent(s):

Initial RNA-MSM 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,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ tags:
4
+ - RNA
5
+ - language-model
6
+ - MSA
7
+ license: mit
8
+ ---
9
+
10
+ # RNA-MSM
11
+
12
+ Multiple sequence alignment-based RNA language model trained on homologous RNA
13
+ sequence alignments from the RNAcmap pipeline.
14
+
15
+ ## Architecture
16
+
17
+ | Parameter | Value |
18
+ |---|---|
19
+ | Layers | 10 |
20
+ | Attention heads | 12 |
21
+ | Embedding dimension | 768 |
22
+ | FFN hidden dimension | 3072 (GELU) |
23
+ | Vocabulary size | 12 |
24
+ | Positional encoding | Learned (sequence) + learned scalar (alignment row) |
25
+ | Normalization | Pre-LayerNorm + final LayerNorm |
26
+ | Architecture | Axial MSA Transformer (row + column self-attention) |
27
+ | Max sequence length | 1024 tokens (including the prepended `<cls>`) |
28
+ | Max alignment depth | 1024 rows |
29
+
30
+ **Input format:** RNA-MSM takes 3D input `(batch, num_alignments, seqlen)`. Each
31
+ alignment is a set of homologous RNA sequences of equal length (an MSA). The model
32
+ applies row self-attention (across sequence positions) and column self-attention
33
+ (across alignment rows) at each of the 10 transformer layers.
34
+
35
+ ### Vocabulary
36
+
37
+ | Token | ID | Token | ID |
38
+ |---|---|---|---|
39
+ | `<cls>` | 0 | `U` | 7 |
40
+ | `<pad>` | 1 | `X` | 8 |
41
+ | `<eos>` | 2 | `N` | 9 |
42
+ | `<unk>` | 3 | `-` | 10 |
43
+ | `A` | 4 | `<mask>` | 11 |
44
+ | `G` | 5 | | |
45
+ | `C` | 6 | | |
46
+
47
+ Each sequence is prepended with `<cls>` (id 0). No `<eos>` token is appended.
48
+
49
+ ## Pretraining
50
+
51
+ - **Objective:** Masked language modeling on RNA MSAs (masking ~15% of tokens)
52
+ - **Data:** RNA homologous sequences searched by RNAcmap from non-redundant RNA
53
+ databases
54
+ - **Source checkpoint:** `RNA_MSM_pretrained.ckpt`
55
+ ([original Google Drive link](https://drive.google.com/file/d/11A-S13qAb5wiBi1YLs3EOrnixSDq7Q0q/view))
56
+
57
+ ### Checkpoint selection
58
+
59
+ There is one publicly released RNA-MSM pretrained checkpoint. This is that checkpoint,
60
+ converted from the original PyTorch Lightning `.ckpt` format.
61
+
62
+ ## Parity Verification
63
+
64
+ Hidden-state representations verified identical (max abs diff = 0.00, exact match) to
65
+ the reference implementation at all 11 representation levels (embedding + 10 transformer
66
+ layers), both on padded and unpadded batches. Verified on GPU with PyTorch 2.7 /
67
+ CUDA 12.9.
68
+
69
+ ## Related Models
70
+
71
+ See the full [RNA-MSM collection](https://huggingface.co/collections/Taykhoom/rna-msm-6a18b5c2b0181ebbc71ff777).
72
+
73
+ ## Usage
74
+
75
+ RNA-MSM is an **MSA model** -- it performs best when given multiple homologous
76
+ sequences as input. For single-sequence embedding, each sequence is treated as a
77
+ 1-row MSA.
78
+
79
+ ### Single-sequence embedding
80
+
81
+ ```python
82
+ import torch
83
+ from transformers import AutoTokenizer, AutoModel
84
+
85
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/RNA-MSM", trust_remote_code=True)
86
+ model = AutoModel.from_pretrained("Taykhoom/RNA-MSM", trust_remote_code=True)
87
+ model.eval()
88
+
89
+ sequences = ["AGCUAGCUAGCU", "GCUAGCUA"]
90
+ enc = tokenizer(sequences, return_tensors="pt", padding=True)
91
+ # enc["input_ids"]: (2, 1, seqlen) -- each sequence treated as 1-row MSA
92
+
93
+ with torch.no_grad():
94
+ out = model(**enc)
95
+
96
+ # last_hidden_state: (batch, num_alignments, seqlen, 768)
97
+ lhs = out.last_hidden_state # (2, 1, seqlen, 768)
98
+
99
+ # Per-token embeddings for the query sequence (row 0), excluding CLS
100
+ token_emb = lhs[:, 0, 1:, :] # (2, seqlen-1, 768)
101
+
102
+ # Mean-pool over non-padding positions for sequence-level embedding
103
+ mask = enc["attention_mask"][:, 0, 1:].unsqueeze(-1).float() # (2, seqlen-1, 1)
104
+ seq_emb = (token_emb * mask).sum(1) / mask.sum(1).clamp(min=1) # (2, 768)
105
+ ```
106
+
107
+ ### MSA embedding
108
+
109
+ ```python
110
+ import torch
111
+ from transformers import AutoTokenizer, AutoModel
112
+
113
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/RNA-MSM", trust_remote_code=True)
114
+ model = AutoModel.from_pretrained("Taykhoom/RNA-MSM", trust_remote_code=True)
115
+ model.eval()
116
+
117
+ # One MSA: 3 aligned homologous sequences of equal length
118
+ msa = [
119
+ "AGCUAGCUAGCU",
120
+ "AGCUAGCUAGC-",
121
+ "AGCU--CUAGCU",
122
+ ]
123
+ enc = tokenizer.encode_msa([msa], return_tensors="pt", padding=True)
124
+ # enc["input_ids"]: (1, 3, seqlen)
125
+
126
+ with torch.no_grad():
127
+ out = model(**enc)
128
+
129
+ # last_hidden_state: (1, 3, seqlen, 768)
130
+ # Use row 0 (query sequence) for downstream tasks
131
+ query_emb = out.last_hidden_state[:, 0, 1:, :] # (1, seqlen-1, 768)
132
+ ```
133
+
134
+ ### Intermediate layers
135
+
136
+ ```python
137
+ with torch.no_grad():
138
+ out = model(**enc, output_hidden_states=True)
139
+
140
+ # hidden_states: tuple of 11 tensors, each (batch, num_alignments, seqlen, 768)
141
+ # Index 0 = embedding, 1..10 = transformer layer outputs
142
+ layer5_emb = out.hidden_states[5][:, 0, :, :] # (batch, seqlen, 768)
143
+ ```
144
+
145
+ ### MLM logits
146
+
147
+ ```python
148
+ from transformers import AutoModelForMaskedLM
149
+
150
+ mlm = AutoModelForMaskedLM.from_pretrained("Taykhoom/RNA-MSM", trust_remote_code=True)
151
+ mlm.eval()
152
+
153
+ enc = tokenizer(["AGCU<mask>AGCU"], return_tensors="pt", padding=True)
154
+ with torch.no_grad():
155
+ logits = mlm(**enc).logits # (1, 1, seqlen, 12)
156
+ ```
157
+
158
+ ### Fine-tuning
159
+
160
+ For sequence-level downstream tasks (e.g., solvent accessibility), extract the
161
+ embedding from the query row (row 0) of the last hidden state, then apply a
162
+ prediction head. The model's attention maps (row attention) are also useful for
163
+ 2D structural tasks (e.g., secondary structure prediction).
164
+
165
+ ## Implementation Notes
166
+
167
+ RNA-MSM uses **axial attention**: each transformer layer applies row self-attention
168
+ (attending across sequence positions, summed over alignment rows) followed by column
169
+ self-attention (attending across alignment rows per position). This custom attention
170
+ pattern is not compatible with `attn_implementation="sdpa"` or
171
+ `attn_implementation="flash_attention_2"` -- only `"eager"` is supported.
172
+
173
+ `last_hidden_state` has shape `(batch, num_alignments, seqlen, embed_dim)` -- note
174
+ the 4D output, reflecting the MSA structure. For single-sequence use (1-row MSA),
175
+ this is `(batch, 1, seqlen, embed_dim)`.
176
+
177
+ ## Citation
178
+
179
+ ```bibtex
180
+ @article{zhang2024_rnamsm,
181
+ title = {Multiple sequence alignment-based {RNA} language model and its application to structural inference},
182
+ author = {Zhang, Yikun and Lang, Mei and Jiang, Jiuhong and Gao, Zhiqiang and Xu, Fan and Litfin, Thomas and Chen, Ke and Singh, Jaswinder and Huang, Xiansong and Song, Guoli and Tian, Yonghong and Zhan, Jian and Chen, Jie and Zhou, Yaoqi},
183
+ journal = {Nucleic Acids Research},
184
+ volume = {52},
185
+ number = {1},
186
+ pages = {e3},
187
+ year = {2024},
188
+ doi = {10.1093/nar/gkad1031}
189
+ }
190
+ ```
191
+
192
+ ## Credits
193
+
194
+ Original model and code by Zhang et al. Source: [GitHub](https://github.com/yikunpku/RNA-MSM).
195
+ The HF conversion code was authored primarily by [Claude Code](https://claude.ai/code)
196
+ and reviewed manually by Taykhoom Dalal.
197
+
198
+ ## License
199
+
200
+ MIT, following the original repository.
config.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "auto_map": {
3
+ "AutoConfig": "configuration_rnamsm.RNAMSMConfig",
4
+ "AutoModel": "modeling_rnamsm.RNAMSMModel",
5
+ "AutoModelForMaskedLM": "modeling_rnamsm.RNAMSMForMaskedLM"
6
+ },
7
+ "activation_dropout": 0.1,
8
+ "architectures": [
9
+ "RNAMSMForMaskedLM"
10
+ ],
11
+ "attention_dropout": 0.1,
12
+ "cls_idx": 0,
13
+ "dropout": 0.1,
14
+ "embed_dim": 768,
15
+ "embed_positions_msa": true,
16
+ "eos_idx": 2,
17
+ "ffn_embed_dim": 3072,
18
+ "mask_idx": 11,
19
+ "max_alignments": 1024,
20
+ "max_positions": 1024,
21
+ "max_tokens_per_msa": 16384,
22
+ "model_type": "rnamsm",
23
+ "num_attention_heads": 12,
24
+ "num_layers": 10,
25
+ "padding_idx": 1,
26
+ "transformers_version": "4.57.6",
27
+ "vocab_size": 12,
28
+ "model_max_length": 1024
29
+ }
configuration_rnamsm.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+
3
+
4
+ class RNAMSMConfig(PretrainedConfig):
5
+ model_type = "rnamsm"
6
+
7
+ auto_map = {
8
+ "AutoConfig": "configuration_rnamsm.RNAMSMConfig",
9
+ "AutoModel": "modeling_rnamsm.RNAMSMModel",
10
+ "AutoModelForMaskedLM": "modeling_rnamsm.RNAMSMForMaskedLM",
11
+ }
12
+
13
+ def __init__(
14
+ self,
15
+ vocab_size=12,
16
+ num_layers=10,
17
+ embed_dim=768,
18
+ num_attention_heads=12,
19
+ ffn_embed_dim=3072,
20
+ padding_idx=1,
21
+ mask_idx=11,
22
+ cls_idx=0,
23
+ eos_idx=2,
24
+ dropout=0.1,
25
+ attention_dropout=0.1,
26
+ activation_dropout=0.1,
27
+ max_positions=1024,
28
+ max_alignments=1024,
29
+ max_tokens_per_msa=16384,
30
+ embed_positions_msa=True,
31
+ **kwargs,
32
+ ):
33
+ super().__init__(padding_idx=padding_idx, **kwargs)
34
+ self.vocab_size = vocab_size
35
+ self.num_layers = num_layers
36
+ self.embed_dim = embed_dim
37
+ self.num_attention_heads = num_attention_heads
38
+ self.ffn_embed_dim = ffn_embed_dim
39
+ self.mask_idx = mask_idx
40
+ self.cls_idx = cls_idx
41
+ self.eos_idx = eos_idx
42
+ self.dropout = dropout
43
+ self.attention_dropout = attention_dropout
44
+ self.activation_dropout = activation_dropout
45
+ self.max_positions = max_positions
46
+ self.max_alignments = max_alignments
47
+ self.max_tokens_per_msa = max_tokens_per_msa
48
+ self.embed_positions_msa = embed_positions_msa
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3998fb8289d98cf53944fc14da4157a40c03dffecf0efefd7e76044ed16a0095
3
+ size 383678288
modeling_rnamsm.py ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from typing import Optional, Tuple
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+ import torch.nn.functional as F
7
+ from transformers import PreTrainedModel
8
+ from transformers.modeling_outputs import BaseModelOutput, MaskedLMOutput
9
+
10
+ try:
11
+ from .configuration_rnamsm import RNAMSMConfig
12
+ except ImportError:
13
+ from configuration_rnamsm import RNAMSMConfig
14
+
15
+
16
+ def gelu(x):
17
+ return x * 0.5 * (1.0 + torch.erf(x / math.sqrt(2.0)))
18
+
19
+
20
+ class RNAMSMLMHead(nn.Module):
21
+ def __init__(self, config: RNAMSMConfig, embed_tokens_weight: nn.Parameter):
22
+ super().__init__()
23
+ self.dense = nn.Linear(config.embed_dim, config.embed_dim)
24
+ self.layer_norm = nn.LayerNorm(config.embed_dim)
25
+ self.weight = embed_tokens_weight
26
+ self.bias = nn.Parameter(torch.zeros(config.vocab_size))
27
+
28
+ def forward(self, x):
29
+ x = self.dense(x)
30
+ x = gelu(x)
31
+ x = self.layer_norm(x)
32
+ return F.linear(x, self.weight) + self.bias
33
+
34
+
35
+ class LearnedPositionalEmbedding(nn.Embedding):
36
+ def __init__(self, num_embeddings: int, embedding_dim: int, padding_idx: int):
37
+ num_embeddings_ = num_embeddings + padding_idx + 1
38
+ super().__init__(num_embeddings_, embedding_dim, padding_idx)
39
+ self.max_positions = num_embeddings
40
+
41
+ def forward(self, tokens: torch.Tensor) -> torch.Tensor:
42
+ mask = tokens.ne(self.padding_idx).int()
43
+ positions = (torch.cumsum(mask, dim=1).type_as(mask) * mask).long() + self.padding_idx
44
+ return F.embedding(positions, self.weight, self.padding_idx,
45
+ self.max_norm, self.norm_type, self.scale_grad_by_freq, self.sparse)
46
+
47
+
48
+ class NormalizedResidualBlock(nn.Module):
49
+ def __init__(self, layer: nn.Module, embedding_dim: int, dropout: float):
50
+ super().__init__()
51
+ self.layer = layer
52
+ self.layer_norm = nn.LayerNorm(embedding_dim)
53
+ self.dropout_module = nn.Dropout(dropout)
54
+
55
+ def forward(self, x, *args, **kwargs):
56
+ residual = x
57
+ x = self.layer_norm(x)
58
+ outputs = self.layer(x, *args, **kwargs)
59
+ if isinstance(outputs, tuple):
60
+ x, *out = outputs
61
+ else:
62
+ x, out = outputs, None
63
+ x = self.dropout_module(x)
64
+ x = residual + x
65
+ if out is not None:
66
+ return (x,) + tuple(out)
67
+ return x
68
+
69
+
70
+ class FeedForwardNetwork(nn.Module):
71
+ def __init__(self, embedding_dim: int, ffn_embedding_dim: int,
72
+ activation_dropout: float, max_tokens_per_msa: int):
73
+ super().__init__()
74
+ self.fc1 = nn.Linear(embedding_dim, ffn_embedding_dim)
75
+ self.fc2 = nn.Linear(ffn_embedding_dim, embedding_dim)
76
+ self.activation_fn = nn.GELU()
77
+ self.activation_dropout_module = nn.Dropout(activation_dropout)
78
+ self.max_tokens_per_msa = max_tokens_per_msa
79
+
80
+ def forward(self, x):
81
+ x = self.activation_fn(self.fc1(x))
82
+ x = self.activation_dropout_module(x)
83
+ return self.fc2(x)
84
+
85
+
86
+ class RowSelfAttention(nn.Module):
87
+ """Self-attention across columns (sequence positions), summed over MSA rows."""
88
+
89
+ def __init__(self, embed_dim: int, num_heads: int, dropout: float, max_tokens_per_msa: int):
90
+ super().__init__()
91
+ self.num_heads = num_heads
92
+ self.dropout = dropout
93
+ self.head_dim = embed_dim // num_heads
94
+ self.scaling = self.head_dim ** -0.5
95
+ self.max_tokens_per_msa = max_tokens_per_msa
96
+ self.attn_shape = "hnij"
97
+ self.q_proj = nn.Linear(embed_dim, embed_dim)
98
+ self.k_proj = nn.Linear(embed_dim, embed_dim)
99
+ self.v_proj = nn.Linear(embed_dim, embed_dim)
100
+ self.out_proj = nn.Linear(embed_dim, embed_dim)
101
+ self.dropout_module = nn.Dropout(dropout)
102
+
103
+ def align_scaling(self, q):
104
+ return self.scaling / math.sqrt(q.size(0))
105
+
106
+ def compute_attention_weights(self, x, scaling, padding_mask=None):
107
+ num_rows, num_cols, batch_size, embed_dim = x.size()
108
+ q = self.q_proj(x).view(num_rows, num_cols, batch_size, self.num_heads, self.head_dim)
109
+ k = self.k_proj(x).view(num_rows, num_cols, batch_size, self.num_heads, self.head_dim)
110
+ q = q * scaling
111
+ if padding_mask is not None:
112
+ q = q * (1 - padding_mask.permute(1, 2, 0).unsqueeze(3).unsqueeze(4).to(q))
113
+ attn_weights = torch.einsum(f"rinhd,rjnhd->{self.attn_shape}", q, k)
114
+ if padding_mask is not None:
115
+ attn_weights = attn_weights.masked_fill(
116
+ padding_mask[:, 0].unsqueeze(0).unsqueeze(2), -10000.0)
117
+ return attn_weights
118
+
119
+ def compute_attention_update(self, x, attn_probs):
120
+ num_rows, num_cols, batch_size, embed_dim = x.size()
121
+ v = self.v_proj(x).view(num_rows, num_cols, batch_size, self.num_heads, self.head_dim)
122
+ context = torch.einsum(f"{self.attn_shape},rjnhd->rinhd", attn_probs, v)
123
+ context = context.contiguous().view(num_rows, num_cols, batch_size, embed_dim)
124
+ return self.out_proj(context)
125
+
126
+ def _batched_forward(self, x, padding_mask=None):
127
+ num_rows, num_cols, batch_size, _ = x.size()
128
+ max_rows = max(1, self.max_tokens_per_msa // num_cols)
129
+ scaling = self.align_scaling(x)
130
+ attns = 0
131
+ for start in range(0, num_rows, max_rows):
132
+ pm = padding_mask[:, start:start + max_rows] if padding_mask is not None else None
133
+ attns = attns + self.compute_attention_weights(x[start:start + max_rows], scaling, pm)
134
+ attn_probs = attns.softmax(-1)
135
+ attn_probs = self.dropout_module(attn_probs)
136
+ outputs = [self.compute_attention_update(x[start:start + max_rows], attn_probs)
137
+ for start in range(0, num_rows, max_rows)]
138
+ return torch.cat(outputs, 0), attn_probs
139
+
140
+ def forward(self, x, self_attn_mask=None, self_attn_padding_mask=None):
141
+ num_rows, num_cols, batch_size, _ = x.size()
142
+ if num_rows * num_cols > self.max_tokens_per_msa and not torch.is_grad_enabled():
143
+ return self._batched_forward(x, self_attn_padding_mask)
144
+ scaling = self.align_scaling(x)
145
+ attn_weights = self.compute_attention_weights(x, scaling, self_attn_padding_mask)
146
+ attn_probs = attn_weights.softmax(-1)
147
+ attn_probs = self.dropout_module(attn_probs)
148
+ output = self.compute_attention_update(x, attn_probs)
149
+ return output, attn_probs
150
+
151
+
152
+ class ColumnSelfAttention(nn.Module):
153
+ """Self-attention across MSA rows (alignment depth) per sequence position."""
154
+
155
+ def __init__(self, embed_dim: int, num_heads: int, dropout: float, max_tokens_per_msa: int):
156
+ super().__init__()
157
+ self.num_heads = num_heads
158
+ self.dropout = dropout
159
+ self.head_dim = embed_dim // num_heads
160
+ self.scaling = self.head_dim ** -0.5
161
+ self.max_tokens_per_msa = max_tokens_per_msa
162
+ self.q_proj = nn.Linear(embed_dim, embed_dim)
163
+ self.k_proj = nn.Linear(embed_dim, embed_dim)
164
+ self.v_proj = nn.Linear(embed_dim, embed_dim)
165
+ self.out_proj = nn.Linear(embed_dim, embed_dim)
166
+ self.dropout_module = nn.Dropout(dropout)
167
+
168
+ def compute_attention_update(self, x, self_attn_padding_mask=None):
169
+ num_rows, num_cols, batch_size, embed_dim = x.size()
170
+ if num_rows == 1:
171
+ attn_probs = torch.ones(self.num_heads, num_cols, batch_size, 1, 1,
172
+ device=x.device, dtype=x.dtype)
173
+ output = self.out_proj(self.v_proj(x))
174
+ else:
175
+ q = self.q_proj(x).view(num_rows, num_cols, batch_size, self.num_heads, self.head_dim)
176
+ k = self.k_proj(x).view(num_rows, num_cols, batch_size, self.num_heads, self.head_dim)
177
+ v = self.v_proj(x).view(num_rows, num_cols, batch_size, self.num_heads, self.head_dim)
178
+ q = q * self.scaling
179
+ attn_weights = torch.einsum("icnhd,jcnhd->hcnij", q, k)
180
+ if self_attn_padding_mask is not None:
181
+ attn_weights = attn_weights.masked_fill(
182
+ self_attn_padding_mask.permute(2, 0, 1).unsqueeze(0).unsqueeze(3), -10000.0)
183
+ attn_probs = attn_weights.softmax(-1)
184
+ attn_probs = self.dropout_module(attn_probs)
185
+ context = torch.einsum("hcnij,jcnhd->icnhd", attn_probs, v)
186
+ context = context.contiguous().view(num_rows, num_cols, batch_size, embed_dim)
187
+ output = self.out_proj(context)
188
+ return output, attn_probs
189
+
190
+ def _batched_forward(self, x, self_attn_padding_mask=None):
191
+ num_rows, num_cols, batch_size, _ = x.size()
192
+ max_cols = max(1, self.max_tokens_per_msa // num_rows)
193
+ outputs, attns = [], []
194
+ for start in range(0, num_cols, max_cols):
195
+ pm = (self_attn_padding_mask[:, :, start:start + max_cols]
196
+ if self_attn_padding_mask is not None else None)
197
+ out, attn = self.compute_attention_update(x[:, start:start + max_cols], pm)
198
+ outputs.append(out)
199
+ attns.append(attn)
200
+ return torch.cat(outputs, 1), torch.cat(attns, 1)
201
+
202
+ def forward(self, x, self_attn_mask=None, self_attn_padding_mask=None):
203
+ num_rows, num_cols, batch_size, _ = x.size()
204
+ if num_rows * num_cols > self.max_tokens_per_msa and not torch.is_grad_enabled():
205
+ return self._batched_forward(x, self_attn_padding_mask)
206
+ return self.compute_attention_update(x, self_attn_padding_mask)
207
+
208
+
209
+ class AxialTransformerLayer(nn.Module):
210
+ def __init__(self, config: RNAMSMConfig):
211
+ super().__init__()
212
+ self.row_self_attention = NormalizedResidualBlock(
213
+ RowSelfAttention(config.embed_dim, config.num_attention_heads,
214
+ config.attention_dropout, config.max_tokens_per_msa),
215
+ config.embed_dim, config.dropout,
216
+ )
217
+ self.column_self_attention = NormalizedResidualBlock(
218
+ ColumnSelfAttention(config.embed_dim, config.num_attention_heads,
219
+ config.attention_dropout, config.max_tokens_per_msa),
220
+ config.embed_dim, config.dropout,
221
+ )
222
+ self.feed_forward_layer = NormalizedResidualBlock(
223
+ FeedForwardNetwork(config.embed_dim, config.ffn_embed_dim,
224
+ config.activation_dropout, config.max_tokens_per_msa),
225
+ config.embed_dim, config.dropout,
226
+ )
227
+
228
+ def forward(self, x, padding_mask=None, output_attentions=False):
229
+ x, row_attn = self.row_self_attention(x, self_attn_padding_mask=padding_mask)
230
+ x, col_attn = self.column_self_attention(x, self_attn_padding_mask=padding_mask)
231
+ x = self.feed_forward_layer(x)
232
+ return x, row_attn, col_attn
233
+
234
+
235
+ class RNAMSMPreTrainedModel(PreTrainedModel):
236
+ config_class = RNAMSMConfig
237
+ base_model_prefix = "rnamsm"
238
+
239
+ def _init_weights(self, module):
240
+ if isinstance(module, nn.Linear):
241
+ nn.init.normal_(module.weight, std=0.02)
242
+ if module.bias is not None:
243
+ nn.init.zeros_(module.bias)
244
+ elif isinstance(module, nn.Embedding):
245
+ nn.init.normal_(module.weight, std=0.02)
246
+ if module.padding_idx is not None:
247
+ module.weight.data[module.padding_idx].zero_()
248
+ elif isinstance(module, nn.LayerNorm):
249
+ nn.init.ones_(module.weight)
250
+ nn.init.zeros_(module.bias)
251
+
252
+
253
+ class RNAMSMModel(RNAMSMPreTrainedModel):
254
+ """
255
+ RNA-MSM backbone: MSA Transformer that processes multiple-sequence-aligned RNA
256
+ sequences and produces per-position embeddings for each alignment row.
257
+
258
+ Input: input_ids of shape (batch, num_alignments, seqlen)
259
+ Output: last_hidden_state of shape (batch, num_alignments, seqlen, embed_dim)
260
+ """
261
+
262
+ def __init__(self, config: RNAMSMConfig):
263
+ super().__init__(config)
264
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.embed_dim,
265
+ padding_idx=config.padding_idx)
266
+ self.embed_positions = LearnedPositionalEmbedding(
267
+ config.max_positions, config.embed_dim, config.padding_idx)
268
+ if config.embed_positions_msa:
269
+ self.msa_position_embedding = nn.Parameter(
270
+ 0.01 * torch.randn(1, config.max_alignments, 1, 1))
271
+ else:
272
+ self.register_parameter("msa_position_embedding", None)
273
+ self.dropout_module = nn.Dropout(config.dropout)
274
+ self.emb_layer_norm_before = nn.LayerNorm(config.embed_dim)
275
+ self.emb_layer_norm_after = nn.LayerNorm(config.embed_dim)
276
+ self.layers = nn.ModuleList([AxialTransformerLayer(config)
277
+ for _ in range(config.num_layers)])
278
+ self.post_init()
279
+
280
+ def forward(
281
+ self,
282
+ input_ids: torch.Tensor,
283
+ attention_mask: Optional[torch.Tensor] = None,
284
+ output_hidden_states: Optional[bool] = None,
285
+ output_attentions: Optional[bool] = None,
286
+ return_dict: Optional[bool] = None,
287
+ ):
288
+ output_hidden_states = (output_hidden_states if output_hidden_states is not None
289
+ else self.config.output_hidden_states)
290
+ output_attentions = (output_attentions if output_attentions is not None
291
+ else self.config.output_attentions)
292
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
293
+
294
+ assert input_ids.ndim == 3, (
295
+ "RNA-MSM expects 3D input_ids of shape (batch, num_alignments, seqlen). "
296
+ "For single sequences, use tokenizer which produces (batch, 1, seqlen).")
297
+
298
+ batch_size, num_alignments, seqlen = input_ids.size()
299
+
300
+ # HF convention: attention_mask 1=attend, 0=pad -> padding_mask True=padding
301
+ if attention_mask is not None:
302
+ padding_mask = attention_mask.eq(0)
303
+ else:
304
+ padding_mask = input_ids.eq(self.config.padding_idx)
305
+
306
+ if not padding_mask.any():
307
+ padding_mask = None
308
+
309
+ # (B, R, C) -> embed: (B, R, C, D)
310
+ x = self.embed_tokens(input_ids)
311
+ x = x + self.embed_positions(
312
+ input_ids.view(batch_size * num_alignments, seqlen)
313
+ ).view(batch_size, num_alignments, seqlen, self.config.embed_dim)
314
+
315
+ if self.msa_position_embedding is not None:
316
+ if num_alignments > self.config.max_alignments:
317
+ raise RuntimeError(
318
+ f"MSA depth {num_alignments} exceeds max_alignments "
319
+ f"{self.config.max_alignments}.")
320
+ x = x + self.msa_position_embedding[:, :num_alignments]
321
+
322
+ x = self.emb_layer_norm_before(x)
323
+ x = self.dropout_module(x)
324
+
325
+ if padding_mask is not None:
326
+ x = x * (1 - padding_mask.unsqueeze(-1).to(x))
327
+
328
+ all_hidden_states = []
329
+ all_row_attentions = []
330
+ all_col_attentions = []
331
+
332
+ if output_hidden_states:
333
+ all_hidden_states.append(x)
334
+
335
+ # (B, R, C, D) -> (R, C, B, D) for axial attention
336
+ x = x.permute(1, 2, 0, 3)
337
+
338
+ for layer in self.layers:
339
+ x, row_attn, col_attn = layer(x, padding_mask=padding_mask,
340
+ output_attentions=output_attentions)
341
+ if output_hidden_states:
342
+ all_hidden_states.append(x.permute(2, 0, 1, 3))
343
+ if output_attentions:
344
+ all_row_attentions.append(row_attn)
345
+ all_col_attentions.append(col_attn)
346
+
347
+ x = self.emb_layer_norm_after(x)
348
+ x = x.permute(2, 0, 1, 3) # (R, C, B, D) -> (B, R, C, D)
349
+
350
+ if output_hidden_states:
351
+ all_hidden_states[-1] = x
352
+
353
+ if not return_dict:
354
+ return tuple(v for v in [
355
+ x,
356
+ tuple(all_hidden_states) if output_hidden_states else None,
357
+ tuple(all_row_attentions) if output_attentions else None,
358
+ ] if v is not None)
359
+
360
+ return BaseModelOutput(
361
+ last_hidden_state=x,
362
+ hidden_states=tuple(all_hidden_states) if output_hidden_states else None,
363
+ attentions=tuple(all_row_attentions) if output_attentions else None,
364
+ )
365
+
366
+
367
+ class RNAMSMForMaskedLM(RNAMSMPreTrainedModel):
368
+ _tied_weights_keys = ["lm_head.weight"]
369
+
370
+ def __init__(self, config: RNAMSMConfig):
371
+ super().__init__(config)
372
+ self.rnamsm = RNAMSMModel(config)
373
+ self.lm_head = RNAMSMLMHead(config, self.rnamsm.embed_tokens.weight)
374
+ self.post_init()
375
+
376
+ def get_output_embeddings(self):
377
+ return self.lm_head
378
+
379
+ def set_output_embeddings(self, new_embeddings):
380
+ self.lm_head = new_embeddings
381
+
382
+ def forward(
383
+ self,
384
+ input_ids: torch.Tensor,
385
+ attention_mask: Optional[torch.Tensor] = None,
386
+ labels: Optional[torch.Tensor] = None,
387
+ output_hidden_states: Optional[bool] = None,
388
+ output_attentions: Optional[bool] = None,
389
+ return_dict: Optional[bool] = None,
390
+ ):
391
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
392
+
393
+ out = self.rnamsm(
394
+ input_ids,
395
+ attention_mask=attention_mask,
396
+ output_hidden_states=output_hidden_states,
397
+ output_attentions=output_attentions,
398
+ return_dict=return_dict,
399
+ )
400
+
401
+ logits = self.lm_head(out[0] if not return_dict else out.last_hidden_state)
402
+
403
+ loss = None
404
+ if labels is not None:
405
+ loss = F.cross_entropy(
406
+ logits.view(-1, self.config.vocab_size),
407
+ labels.view(-1),
408
+ ignore_index=-100,
409
+ )
410
+
411
+ if not return_dict:
412
+ output = (logits,) + out[1:]
413
+ return ((loss,) + output) if loss is not None else output
414
+
415
+ return MaskedLMOutput(
416
+ loss=loss,
417
+ logits=logits,
418
+ hidden_states=out.hidden_states,
419
+ attentions=out.attentions,
420
+ )
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_rnamsm.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from typing import Dict, List, Optional, Union
4
+
5
+ import torch
6
+ from transformers import PreTrainedTokenizer
7
+ from transformers.tokenization_utils_base import BatchEncoding
8
+
9
+
10
+ _VOCAB = {
11
+ "<cls>": 0,
12
+ "<pad>": 1,
13
+ "<eos>": 2,
14
+ "<unk>": 3,
15
+ "A": 4,
16
+ "G": 5,
17
+ "C": 6,
18
+ "U": 7,
19
+ "X": 8,
20
+ "N": 9,
21
+ "-": 10,
22
+ "<mask>": 11,
23
+ }
24
+
25
+
26
+ class RNAMSMTokenizer(PreTrainedTokenizer):
27
+ """
28
+ Tokenizer for RNA-MSM.
29
+
30
+ Vocabulary: <cls>(0) <pad>(1) <eos>(2) <unk>(3) A(4) G(5) C(6) U(7) X(8) N(9) -(10) <mask>(11)
31
+
32
+ RNA-MSM is an MSA Transformer: it always expects 3D input
33
+ (batch, num_alignments, seqlen). This tokenizer treats each input string
34
+ as a single-sequence MSA (1 alignment row), so the standard __call__ API:
35
+
36
+ enc = tokenizer(["AGCU", "GAUC"], return_tensors="pt", padding=True)
37
+ # enc.input_ids: (2, 1, T) -- batch of 2 single-sequence MSAs
38
+
39
+ For real MSAs (multiple aligned sequences), use encode_msa():
40
+
41
+ enc = tokenizer.encode_msa([["AGCU--", "AGCUUU"]], return_tensors="pt")
42
+ # enc["input_ids"]: (1, 2, T) -- 1 MSA with 2 alignment rows
43
+ """
44
+
45
+ vocab_files_names = {"vocab_file": "vocab.json"}
46
+ model_input_names = ["input_ids", "attention_mask"]
47
+
48
+ def __init__(
49
+ self,
50
+ vocab_file=None,
51
+ cls_token="<cls>",
52
+ pad_token="<pad>",
53
+ eos_token="<eos>",
54
+ unk_token="<unk>",
55
+ mask_token="<mask>",
56
+ **kwargs,
57
+ ):
58
+ if vocab_file and os.path.isfile(vocab_file):
59
+ with open(vocab_file) as f:
60
+ self._vocab = json.load(f)
61
+ else:
62
+ self._vocab = dict(_VOCAB)
63
+ self._ids_to_tokens = {v: k for k, v in self._vocab.items()}
64
+ super().__init__(
65
+ cls_token=cls_token,
66
+ pad_token=pad_token,
67
+ eos_token=eos_token,
68
+ unk_token=unk_token,
69
+ mask_token=mask_token,
70
+ **kwargs,
71
+ )
72
+
73
+ @property
74
+ def vocab_size(self):
75
+ return len(self._vocab)
76
+
77
+ def get_vocab(self):
78
+ return dict(self._vocab)
79
+
80
+ def _tokenize(self, text):
81
+ return list(text)
82
+
83
+ def _convert_token_to_id(self, token):
84
+ return self._vocab.get(token, self._vocab["<unk>"])
85
+
86
+ def _convert_id_to_token(self, index):
87
+ return self._ids_to_tokens.get(index, "<unk>")
88
+
89
+ def save_vocabulary(self, save_directory, filename_prefix=None):
90
+ os.makedirs(save_directory, exist_ok=True)
91
+ fname = (filename_prefix + "-" if filename_prefix else "") + "vocab.json"
92
+ path = os.path.join(save_directory, fname)
93
+ with open(path, "w") as f:
94
+ json.dump(self._vocab, f, indent=2)
95
+ return (path,)
96
+
97
+ def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):
98
+ cls = [self.cls_token_id]
99
+ if token_ids_1 is None:
100
+ return cls + token_ids_0
101
+ return cls + token_ids_0 + cls + token_ids_1
102
+
103
+ def get_special_tokens_mask(self, token_ids_0, token_ids_1=None,
104
+ already_has_special_tokens=False):
105
+ if already_has_special_tokens:
106
+ return super().get_special_tokens_mask(
107
+ token_ids_0, token_ids_1, already_has_special_tokens=True)
108
+ mask = [1] + [0] * len(token_ids_0)
109
+ if token_ids_1 is not None:
110
+ mask += [1] + [0] * len(token_ids_1)
111
+ return mask
112
+
113
+ def create_token_type_ids_from_sequences(self, token_ids_0, token_ids_1=None):
114
+ if token_ids_1 is None:
115
+ return [0] + token_ids_0
116
+ return [0] + token_ids_0 + [0] + token_ids_1
117
+
118
+ def __call__(
119
+ self,
120
+ text,
121
+ text_pair=None,
122
+ add_special_tokens=True,
123
+ padding=False,
124
+ truncation=False,
125
+ max_length=None,
126
+ return_tensors=None,
127
+ **kwargs,
128
+ ):
129
+ """
130
+ Tokenize one or more sequences, each treated as a 1-row MSA.
131
+
132
+ text: str or List[str]
133
+ Returns dict with input_ids of shape (batch, 1, seqlen) and
134
+ attention_mask of shape (batch, 1, seqlen).
135
+ """
136
+ if isinstance(text, str):
137
+ sequences = [text]
138
+ else:
139
+ sequences = list(text)
140
+
141
+ encoded = []
142
+ for seq in sequences:
143
+ ids = self._tokenize_single(seq, add_special_tokens)
144
+ encoded.append(ids)
145
+
146
+ if padding and len(encoded) > 1:
147
+ max_len = max(len(ids) for ids in encoded)
148
+ pad_id = self.pad_token_id
149
+ encoded = [ids + [pad_id] * (max_len - len(ids)) for ids in encoded]
150
+
151
+ input_ids = [[ids] for ids in encoded]
152
+ attention_mask = [[[1 if t != self.pad_token_id else 0 for t in ids]]
153
+ for ids in encoded]
154
+
155
+ if return_tensors == "pt":
156
+ input_ids = torch.tensor(input_ids, dtype=torch.long)
157
+ attention_mask = torch.tensor(attention_mask, dtype=torch.long)
158
+ return BatchEncoding({"input_ids": input_ids, "attention_mask": attention_mask})
159
+
160
+ return BatchEncoding({"input_ids": input_ids, "attention_mask": attention_mask})
161
+
162
+ def _tokenize_single(self, sequence, add_special_tokens=True):
163
+ tokens = list(sequence)
164
+ ids = [self._convert_token_to_id(t) for t in tokens]
165
+ if add_special_tokens:
166
+ ids = [self.cls_token_id] + ids
167
+ return ids
168
+
169
+ def encode_msa(
170
+ self,
171
+ msas,
172
+ add_special_tokens=True,
173
+ padding=False,
174
+ return_tensors=None,
175
+ ):
176
+ """
177
+ Tokenize a batch of MSAs.
178
+
179
+ msas: List[List[str]]
180
+ Each inner list is one MSA (multiple aligned sequences of equal length).
181
+ All sequences within an MSA must have the same length.
182
+
183
+ Returns dict with:
184
+ input_ids: (batch, max_alignments, max_seqlen)
185
+ attention_mask: (batch, max_alignments, max_seqlen)
186
+ """
187
+ if isinstance(msas[0], str):
188
+ msas = [msas]
189
+
190
+ max_rows = max(len(msa) for msa in msas)
191
+ max_seqlen = max(
192
+ len(self._tokenize_single(seq, add_special_tokens))
193
+ for msa in msas for seq in msa
194
+ )
195
+
196
+ pad_id = self.pad_token_id
197
+ batch_ids = []
198
+ batch_mask = []
199
+
200
+ for msa in msas:
201
+ msa_ids = []
202
+ msa_mask = []
203
+ for seq in msa:
204
+ ids = self._tokenize_single(seq, add_special_tokens)
205
+ if padding:
206
+ pad_len = max_seqlen - len(ids)
207
+ mask = [1] * len(ids) + [0] * pad_len
208
+ ids = ids + [pad_id] * pad_len
209
+ else:
210
+ mask = [1] * len(ids)
211
+ msa_ids.append(ids)
212
+ msa_mask.append(mask)
213
+
214
+ if padding:
215
+ pad_row = [pad_id] * max_seqlen
216
+ pad_mask_row = [0] * max_seqlen
217
+ while len(msa_ids) < max_rows:
218
+ msa_ids.append(pad_row)
219
+ msa_mask.append(pad_mask_row)
220
+
221
+ batch_ids.append(msa_ids)
222
+ batch_mask.append(msa_mask)
223
+
224
+ if return_tensors == "pt":
225
+ batch_ids = torch.tensor(batch_ids, dtype=torch.long)
226
+ batch_mask = torch.tensor(batch_mask, dtype=torch.long)
227
+ return BatchEncoding({"input_ids": batch_ids, "attention_mask": batch_mask})
228
+
229
+ return BatchEncoding({"input_ids": batch_ids, "attention_mask": batch_mask})
230
+
231
+ def decode(self, token_ids, skip_special_tokens=False, **kwargs):
232
+ if isinstance(token_ids, torch.Tensor):
233
+ token_ids = token_ids.tolist()
234
+ tokens = [self._convert_id_to_token(i) for i in token_ids]
235
+ if skip_special_tokens:
236
+ special = {self.cls_token, self.pad_token, self.eos_token,
237
+ self.unk_token, self.mask_token}
238
+ tokens = [t for t in tokens if t not in special]
239
+ return "".join(tokens)
240
+
241
+ def num_special_tokens_to_add(self, pair=False):
242
+ return 1
tokenizer_config.json ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ "11": {
36
+ "content": "<mask>",
37
+ "lstrip": false,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": true
42
+ }
43
+ },
44
+ "auto_map": {
45
+ "AutoTokenizer": ["tokenization_rnamsm.RNAMSMTokenizer", null]
46
+ },
47
+ "clean_up_tokenization_spaces": false,
48
+ "cls_token": "<cls>",
49
+ "eos_token": "<eos>",
50
+ "extra_special_tokens": {},
51
+ "mask_token": "<mask>",
52
+ "model_max_length": 1024,
53
+ "pad_token": "<pad>",
54
+ "tokenizer_class": "RNAMSMTokenizer",
55
+ "unk_token": "<unk>"
56
+ }
vocab.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "<cls>": 0,
3
+ "<pad>": 1,
4
+ "<eos>": 2,
5
+ "<unk>": 3,
6
+ "A": 4,
7
+ "G": 5,
8
+ "C": 6,
9
+ "U": 7,
10
+ "X": 8,
11
+ "N": 9,
12
+ "-": 10,
13
+ "<mask>": 11
14
+ }