lhallee commited on
Commit
59eb3c9
·
verified ·
1 Parent(s): 6fbf698

Update FastPLMs files

Browse files
README.md CHANGED
@@ -14,15 +14,16 @@ This checkpoint contains the FastPLMs `ESM3` implementation.
14
 
15
  Accepted inputs are sequence, structure, and function tracks prepared through
16
  the multimodal helpers.
17
- Supported Transformers entry points are `AutoConfig`, `AutoModel`.
 
18
 
19
  ## Capabilities
20
 
21
  | Feature | Status |
22
  | --- | --- |
23
- | Sequence classification | Unavailable: no advertised AutoClass |
24
- | Token classification | Unavailable: no advertised AutoClass |
25
- | PEFT fine-tuning | Supported pattern: attach LoRA to the pretrained model |
26
  | Embeddings | Supported: shared ordered embedding API |
27
  | Test-time training | Supported: low-rank masked-residue adaptation |
28
  | Attention variants | Supported: `eager`, `sdpa`, `flex_attention` |
@@ -95,6 +96,45 @@ Set `output` and `format="safetensors"` or `"sqlite"` for transactional,
95
  bounded-memory storage. Resume checks input order, model state, tokenizer
96
  policy, backend, dtype, and pooling configuration before it appends data.
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  ## PEFT fine-tuning
99
 
100
  Install the training dependencies. Then attach LoRA to the loaded checkpoint:
@@ -104,20 +144,22 @@ python -m pip install "datasets>=4.8,<5" "peft>=0.19,<0.20"
104
  ```
105
 
106
  ```python
107
- from peft import LoraConfig, get_peft_model
108
 
109
  peft_model = get_peft_model(
110
- model,
111
  LoraConfig(
 
112
  r=8,
113
  lora_alpha=16,
114
  target_modules="all-linear",
 
115
  ),
116
  )
117
  ```
118
 
119
- This checkpoint has no advertised classifier. Supply the task objective and
120
- preserve any new head through `modules_to_save`.
121
  All FastPLMs checkpoints follow the Transformers `PreTrainedModel` contract and
122
  can use PEFT. The ESM2-specific shipped CLI is an example, not a
123
  support boundary. Record the target modules, base revision, data identity, and
@@ -193,8 +235,8 @@ not experimental measurements of structure or function.
193
  ## Runtime contract
194
 
195
  - Public input: Sequence, structure, and function tracks prepared through the multimodal helpers
196
- - Advertised AutoClasses: `AutoConfig`, `AutoModel`
197
- - AutoClass weight status: `AutoConfig` = `FastPLMs extension`, `AutoModel` = `pretrained`
198
  - Attention implementations: `eager`, `sdpa`, `flex_attention`
199
  - Precision policies: `default`
200
  - BF16 execution: `fp32_parameters_autocast`
@@ -208,8 +250,8 @@ not experimental measurements of structure or function.
208
  ## Release record
209
 
210
  - FastPLMs weights: `Synthyra/ESM3_small`
211
- - Runtime revision: recorded in the built artifact and published commit
212
- - Source-tree and runtime-bundle SHA-256: recorded in the source record
213
  - Official checkpoint: `biohub/esm3-sm-open-v1`
214
  - Artifact source: `fast`
215
  - State transform: `esm3_to_fastplms_v1`
 
14
 
15
  Accepted inputs are sequence, structure, and function tracks prepared through
16
  the multimodal helpers.
17
+ Supported Transformers entry points are `AutoConfig`, `AutoModel`,
18
+ `AutoModelForSequenceClassification`, `AutoModelForTokenClassification`.
19
 
20
  ## Capabilities
21
 
22
  | Feature | Status |
23
  | --- | --- |
24
+ | Sequence classification | Supported: base weights with an untrained task head |
25
+ | Token classification | Supported: base weights with an untrained task head |
26
+ | PEFT fine-tuning | Supported pattern: preserve the separately trained `classifier` |
27
  | Embeddings | Supported: shared ordered embedding API |
28
  | Test-time training | Supported: low-rank masked-residue adaptation |
29
  | Attention variants | Supported: `eager`, `sdpa`, `flex_attention` |
 
96
  bounded-memory storage. Resume checks input order, model state, tokenizer
97
  policy, backend, dtype, and pooling configuration before it appends data.
98
 
99
+ ## Downstream prediction
100
+
101
+ The sequence and token prediction AutoClasses use the checkpoint backbone and
102
+ create a new, untrained `classifier`. Sequence labels have shape `(b,)`.
103
+ Residue labels have shape `(b, l)` and use `-100` outside biological positions.
104
+
105
+ ```python
106
+ import torch
107
+ from transformers import AutoTokenizer
108
+ from transformers import (
109
+ AutoModelForSequenceClassification,
110
+ AutoModelForTokenClassification,
111
+ )
112
+
113
+ model_id = "Synthyra/ESM3_small"
114
+ sequence_model = AutoModelForSequenceClassification.from_pretrained(
115
+ model_id, num_labels=2, trust_remote_code=True
116
+ ).eval()
117
+ token_model = AutoModelForTokenClassification.from_pretrained(
118
+ model_id, num_labels=3, trust_remote_code=True
119
+ ).eval()
120
+ tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
121
+ sequences = ["MSTNPKPQRKTKRNT", "MKTIIALSYIFCLVFA"]
122
+ batch = tokenizer(sequences, padding=True, return_tensors="pt")
123
+ biological = batch["attention_mask"].bool()
124
+ for special_id in tokenizer.all_special_ids:
125
+ biological &= batch["input_ids"].ne(special_id)
126
+
127
+ sequence_labels = torch.zeros(len(sequences), dtype=torch.long)
128
+ token_labels = torch.full_like(batch["input_ids"], -100)
129
+ token_labels[biological] = 0
130
+
131
+ with torch.inference_mode():
132
+ sequence_output = sequence_model(**batch, labels=sequence_labels)
133
+ token_output = token_model(**batch, labels=token_labels)
134
+ print(sequence_output.logits.shape) # (b, 2)
135
+ print(token_output.logits.shape) # (b, l, 3)
136
+ ```
137
+
138
  ## PEFT fine-tuning
139
 
140
  Install the training dependencies. Then attach LoRA to the loaded checkpoint:
 
144
  ```
145
 
146
  ```python
147
+ from peft import LoraConfig, TaskType, get_peft_model
148
 
149
  peft_model = get_peft_model(
150
+ sequence_model,
151
  LoraConfig(
152
+ task_type=TaskType.SEQ_CLS,
153
  r=8,
154
  lora_alpha=16,
155
  target_modules="all-linear",
156
+ modules_to_save=["classifier"],
157
  ),
158
  )
159
  ```
160
 
161
+ This checkpoint advertises a classification head. Save the separately trained
162
+ `classifier` with the adapter.
163
  All FastPLMs checkpoints follow the Transformers `PreTrainedModel` contract and
164
  can use PEFT. The ESM2-specific shipped CLI is an example, not a
165
  support boundary. Record the target modules, base revision, data identity, and
 
235
  ## Runtime contract
236
 
237
  - Public input: Sequence, structure, and function tracks prepared through the multimodal helpers
238
+ - Advertised AutoClasses: `AutoConfig`, `AutoModel`, `AutoModelForSequenceClassification`, `AutoModelForTokenClassification`
239
+ - AutoClass weight status: `AutoConfig` = `FastPLMs extension`, `AutoModel` = `pretrained`, `AutoModelForSequenceClassification` = `base weights + untrained task head`, `AutoModelForTokenClassification` = `base weights + untrained task head`
240
  - Attention implementations: `eager`, `sdpa`, `flex_attention`
241
  - Precision policies: `default`
242
  - BF16 execution: `fp32_parameters_autocast`
 
250
  ## Release record
251
 
252
  - FastPLMs weights: `Synthyra/ESM3_small`
253
+ - Runtime revision: recorded separately in the built artifact and published commit
254
+ - Runtime source identities: recorded in `source-record.json`
255
  - Official checkpoint: `biohub/esm3-sm-open-v1`
256
  - Artifact source: `fast`
257
  - State transform: `esm3_to_fastplms_v1`
fastplms/models.toml CHANGED
@@ -199,7 +199,7 @@ representative = "esmc_small"
199
  documentation = "docs/models.md#esm-and-esmc"
200
  test_tiers = ["check", "compliance", "feature", "artifact", "benchmark"]
201
  runtime_paths = ["__init__.py", "registry.py", "runtime.py", "models.toml", "models/__init__.py", "attention", "embeddings", "models/esm_plusplus", "models/ttt.py"]
202
- auto_map = { AutoConfig = "fastplms.models.esm_plusplus.modeling_esm_plusplus.ESMplusplusConfig", AutoModel = "fastplms.models.esm_plusplus.modeling_esm_plusplus.ESMplusplusModel", AutoModelForMaskedLM = "fastplms.models.esm_plusplus.modeling_esm_plusplus.ESMplusplusForMaskedLM" }
203
 
204
  [families.esm3]
205
  architecture = "ESM3"
@@ -223,7 +223,7 @@ representative = "esm3_small"
223
  documentation = "docs/models.md#esm3"
224
  test_tiers = ["check", "compliance", "feature", "artifact", "benchmark"]
225
  runtime_paths = ["__init__.py", "registry.py", "runtime.py", "models.toml", "models/__init__.py", "attention", "embeddings", "models/esm3", "models/ttt.py"]
226
- auto_map = { AutoConfig = "fastplms.models.esm3.modeling_esm3.FastESM3Config", AutoModel = "fastplms.models.esm3.modeling_esm3.FastESM3Model" }
227
 
228
  [families.e1]
229
  architecture = "E1"
@@ -370,8 +370,8 @@ conversion_provenance = "Input: the pinned native Meta ESMFold checkpoint plus i
370
  representative = "esmfold"
371
  documentation = "docs/models.md#esmfold"
372
  test_tiers = ["check", "compliance", "structure", "feature", "artifact", "benchmark"]
373
- runtime_paths = ["__init__.py", "registry.py", "runtime.py", "models.toml", "models/__init__.py", "attention", "embeddings", "models/_esm_rotary.py", "models/esmfold"]
374
- auto_map = { AutoConfig = "fastplms.models.esmfold.modeling_fast_esmfold.FastEsmFoldConfig", AutoModel = "fastplms.models.esmfold.modeling_fast_esmfold.FastEsmForProteinFolding" }
375
 
376
  [families.esmfold2]
377
  architecture = "ESMFold2"
@@ -396,8 +396,8 @@ conversion_provenance = "Input: each pinned Biohub ESMFold2 checkpoint and its s
396
  representative = "esmfold2"
397
  documentation = "docs/esmfold2.md"
398
  test_tiers = ["check", "compliance", "structure", "feature", "artifact", "benchmark"]
399
- runtime_paths = ["__init__.py", "registry.py", "runtime.py", "models.toml", "models/__init__.py", "attention", "embeddings", "models/esmfold2", "models/esm_plusplus", "models/ttt.py"]
400
- auto_map = { AutoConfig = "fastplms.models.esmfold2.configuration_esmfold2.ESMFold2Config", AutoModel = "fastplms.models.esmfold2.modeling_esmfold2.ESMFold2Model" }
401
 
402
  [[models]]
403
  id = "esm2_8m"
@@ -1215,7 +1215,7 @@ official_files = [
1215
  "config.json=git-sha1:79ed0dc0f867b8f09bfa004d6f77397c2ab9b38d",
1216
  "model.safetensors=sha256:01358c317428d38535e3db513cab177336fc0f7fab0d84002e64b7741d5181b3",
1217
  ]
1218
- auto_map = { AutoConfig = "fastplms.models.esmfold2.configuration_esmfold2.ESMFold2Config", AutoModel = "fastplms.models.esmfold2.modeling_esmfold2_experimental.ESMFold2ExperimentalModel" }
1219
 
1220
  [[models]]
1221
  id = "esmfold2_experimental_fast_cutoff2025"
@@ -1236,4 +1236,4 @@ official_files = [
1236
  "config.json=git-sha1:0333d68ddb12ed2f066741dcb801142f466c0a2c",
1237
  "model.safetensors=sha256:4e903b740ad6ad704ec60881bfd593e0d6c874a630ffa0f0838276e0b665088f",
1238
  ]
1239
- auto_map = { AutoConfig = "fastplms.models.esmfold2.configuration_esmfold2.ESMFold2Config", AutoModel = "fastplms.models.esmfold2.modeling_esmfold2_experimental.ESMFold2ExperimentalModel" }
 
199
  documentation = "docs/models.md#esm-and-esmc"
200
  test_tiers = ["check", "compliance", "feature", "artifact", "benchmark"]
201
  runtime_paths = ["__init__.py", "registry.py", "runtime.py", "models.toml", "models/__init__.py", "attention", "embeddings", "models/esm_plusplus", "models/ttt.py"]
202
+ auto_map = { AutoConfig = "fastplms.models.esm_plusplus.modeling_esm_plusplus.ESMplusplusConfig", AutoModel = "fastplms.models.esm_plusplus.modeling_esm_plusplus.ESMplusplusModel", AutoModelForMaskedLM = "fastplms.models.esm_plusplus.modeling_esm_plusplus.ESMplusplusForMaskedLM", AutoModelForSequenceClassification = "fastplms.models.esm_plusplus.modeling_esm_plusplus.ESMplusplusForSequenceClassification", AutoModelForTokenClassification = "fastplms.models.esm_plusplus.modeling_esm_plusplus.ESMplusplusForTokenClassification" }
203
 
204
  [families.esm3]
205
  architecture = "ESM3"
 
223
  documentation = "docs/models.md#esm3"
224
  test_tiers = ["check", "compliance", "feature", "artifact", "benchmark"]
225
  runtime_paths = ["__init__.py", "registry.py", "runtime.py", "models.toml", "models/__init__.py", "attention", "embeddings", "models/esm3", "models/ttt.py"]
226
+ auto_map = { AutoConfig = "fastplms.models.esm3.modeling_esm3.FastESM3Config", AutoModel = "fastplms.models.esm3.modeling_esm3.FastESM3Model", AutoModelForSequenceClassification = "fastplms.models.esm3.modeling_esm3.FastESM3ForSequenceClassification", AutoModelForTokenClassification = "fastplms.models.esm3.modeling_esm3.FastESM3ForTokenClassification" }
227
 
228
  [families.e1]
229
  architecture = "E1"
 
370
  representative = "esmfold"
371
  documentation = "docs/models.md#esmfold"
372
  test_tiers = ["check", "compliance", "structure", "feature", "artifact", "benchmark"]
373
+ runtime_paths = ["__init__.py", "registry.py", "runtime.py", "models.toml", "models/__init__.py", "attention", "embeddings", "models/_esm_rotary.py", "models/classification_probe.py", "models/esmfold"]
374
+ auto_map = { AutoConfig = "fastplms.models.esmfold.modeling_fast_esmfold.FastEsmFoldConfig", AutoModel = "fastplms.models.esmfold.modeling_fast_esmfold.FastEsmForProteinFolding", AutoModelForSequenceClassification = "fastplms.models.esmfold.modeling_fast_esmfold.FastEsmForSequenceClassification", AutoModelForTokenClassification = "fastplms.models.esmfold.modeling_fast_esmfold.FastEsmForTokenClassification" }
375
 
376
  [families.esmfold2]
377
  architecture = "ESMFold2"
 
396
  representative = "esmfold2"
397
  documentation = "docs/esmfold2.md"
398
  test_tiers = ["check", "compliance", "structure", "feature", "artifact", "benchmark"]
399
+ runtime_paths = ["__init__.py", "registry.py", "runtime.py", "models.toml", "models/__init__.py", "attention", "embeddings", "models/classification_probe.py", "models/_esm_rotary.py", "models/esmfold2", "models/esm_plusplus", "models/ttt.py"]
400
+ auto_map = { AutoConfig = "fastplms.models.esmfold2.configuration_esmfold2.ESMFold2Config", AutoModel = "fastplms.models.esmfold2.modeling_esmfold2.ESMFold2Model", AutoModelForSequenceClassification = "fastplms.models.esmfold2.modeling_esmfold2_classification.ESMFold2ForSequenceClassification", AutoModelForTokenClassification = "fastplms.models.esmfold2.modeling_esmfold2_classification.ESMFold2ForTokenClassification" }
401
 
402
  [[models]]
403
  id = "esm2_8m"
 
1215
  "config.json=git-sha1:79ed0dc0f867b8f09bfa004d6f77397c2ab9b38d",
1216
  "model.safetensors=sha256:01358c317428d38535e3db513cab177336fc0f7fab0d84002e64b7741d5181b3",
1217
  ]
1218
+ auto_map = { AutoConfig = "fastplms.models.esmfold2.configuration_esmfold2.ESMFold2Config", AutoModel = "fastplms.models.esmfold2.modeling_esmfold2_experimental.ESMFold2ExperimentalModel", AutoModelForSequenceClassification = "fastplms.models.esmfold2.modeling_esmfold2_classification.ESMFold2ExperimentalForSequenceClassification", AutoModelForTokenClassification = "fastplms.models.esmfold2.modeling_esmfold2_classification.ESMFold2ExperimentalForTokenClassification" }
1219
 
1220
  [[models]]
1221
  id = "esmfold2_experimental_fast_cutoff2025"
 
1236
  "config.json=git-sha1:0333d68ddb12ed2f066741dcb801142f466c0a2c",
1237
  "model.safetensors=sha256:4e903b740ad6ad704ec60881bfd593e0d6c874a630ffa0f0838276e0b665088f",
1238
  ]
1239
+ auto_map = { AutoConfig = "fastplms.models.esmfold2.configuration_esmfold2.ESMFold2Config", AutoModel = "fastplms.models.esmfold2.modeling_esmfold2_experimental.ESMFold2ExperimentalModel", AutoModelForSequenceClassification = "fastplms.models.esmfold2.modeling_esmfold2_classification.ESMFold2ExperimentalForSequenceClassification", AutoModelForTokenClassification = "fastplms.models.esmfold2.modeling_esmfold2_classification.ESMFold2ExperimentalForTokenClassification" }
fastplms/models/esm3/__init__.py CHANGED
@@ -1,4 +1,14 @@
1
- from fastplms.models.esm3.modeling_esm3 import FastESM3Config, FastESM3Model
 
 
 
 
 
2
 
3
 
4
- __all__ = ["FastESM3Config", "FastESM3Model"]
 
 
 
 
 
 
1
+ from fastplms.models.esm3.modeling_esm3 import (
2
+ FastESM3Config,
3
+ FastESM3ForSequenceClassification,
4
+ FastESM3ForTokenClassification,
5
+ FastESM3Model,
6
+ )
7
 
8
 
9
+ __all__ = [
10
+ "FastESM3Config",
11
+ "FastESM3ForSequenceClassification",
12
+ "FastESM3ForTokenClassification",
13
+ "FastESM3Model",
14
+ ]
fastplms/models/esm3/modeling_esm3.py CHANGED
@@ -16,21 +16,26 @@ import os
16
  import shutil
17
  import stat
18
  import tempfile
19
- import einops
20
- import torch
21
- import torch.nn as nn
22
- import torch.nn.functional as F
23
  from contextlib import contextmanager
24
  from dataclasses import dataclass
25
  from pathlib import Path, PurePosixPath
26
  from typing import ClassVar
27
  from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
 
 
 
 
 
28
  from einops import rearrange
29
  from tokenizers import Tokenizer
30
  from tokenizers.models import BPE
31
  from tokenizers.processors import TemplateProcessing
32
  from transformers import PretrainedConfig, PreTrainedModel, PreTrainedTokenizerFast
33
- from transformers.modeling_outputs import ModelOutput
 
 
 
 
34
 
35
 
36
  try:
@@ -652,6 +657,8 @@ def _render_saved_runtime_bridge(archive_hash: str, tree_hash: str) -> str:
652
  '_modeling = _import_without_bytecode("fastplms.models.esm3.modeling_esm3")',
653
  "FastESM3Config = _modeling.FastESM3Config",
654
  "FastESM3Model = _modeling.FastESM3Model",
 
 
655
  "",
656
  ]
657
  return "\n".join(lines)
@@ -707,6 +714,9 @@ def _validate_saved_runtime_destination(save_directory: Path) -> None:
707
  def _write_saved_runtime(
708
  save_directory: Path,
709
  prepared_runtime: tuple[bytes, dict[str, object], str] | None = None,
 
 
 
710
  ) -> None:
711
  """Make one ESM3 ``save_pretrained`` directory independently loadable."""
712
 
@@ -729,10 +739,12 @@ def _write_saved_runtime(
729
  raise RuntimeError("Saved ESM3 config.json is missing or invalid.") from error
730
  if not isinstance(config, dict):
731
  raise RuntimeError("Saved ESM3 config.json must contain a JSON object.")
732
- config["auto_map"] = {
733
  "AutoConfig": "modeling_fastplms.FastESM3Config",
734
  "AutoModel": "modeling_fastplms.FastESM3Model",
735
  }
 
 
736
  config_payload = (json.dumps(config, indent=2, sort_keys=True) + "\n").encode("utf-8")
737
  _replace_saved_runtime_file(config_path, config_payload)
738
 
@@ -2255,7 +2267,12 @@ class FastESM3Model(FastPLMTestTimeTrainingMixin, FastESM3PreTrainedModel, Embed
2255
  _validate_saved_runtime_destination(save_path)
2256
  prepared_runtime = _build_saved_runtime_archive(Path(__file__).resolve().parents[2])
2257
  super().save_pretrained(save_directory, *args, **kwargs)
2258
- _write_saved_runtime(save_path, prepared_runtime)
 
 
 
 
 
2259
 
2260
  def tokenize_sequences(
2261
  self,
@@ -2576,3 +2593,298 @@ class FastESM3Model(FastPLMTestTimeTrainingMixin, FastESM3PreTrainedModel, Embed
2576
  if not return_dict:
2577
  return result.to_tuple()
2578
  return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  import shutil
17
  import stat
18
  import tempfile
 
 
 
 
19
  from contextlib import contextmanager
20
  from dataclasses import dataclass
21
  from pathlib import Path, PurePosixPath
22
  from typing import ClassVar
23
  from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
24
+
25
+ import einops
26
+ import torch
27
+ import torch.nn as nn
28
+ import torch.nn.functional as F
29
  from einops import rearrange
30
  from tokenizers import Tokenizer
31
  from tokenizers.models import BPE
32
  from tokenizers.processors import TemplateProcessing
33
  from transformers import PretrainedConfig, PreTrainedModel, PreTrainedTokenizerFast
34
+ from transformers.modeling_outputs import (
35
+ ModelOutput,
36
+ SequenceClassifierOutput,
37
+ TokenClassifierOutput,
38
+ )
39
 
40
 
41
  try:
 
657
  '_modeling = _import_without_bytecode("fastplms.models.esm3.modeling_esm3")',
658
  "FastESM3Config = _modeling.FastESM3Config",
659
  "FastESM3Model = _modeling.FastESM3Model",
660
+ "FastESM3ForSequenceClassification = _modeling.FastESM3ForSequenceClassification",
661
+ "FastESM3ForTokenClassification = _modeling.FastESM3ForTokenClassification",
662
  "",
663
  ]
664
  return "\n".join(lines)
 
714
  def _write_saved_runtime(
715
  save_directory: Path,
716
  prepared_runtime: tuple[bytes, dict[str, object], str] | None = None,
717
+ *,
718
+ auto_class: str = "AutoModel",
719
+ model_class: str = "FastESM3Model",
720
  ) -> None:
721
  """Make one ESM3 ``save_pretrained`` directory independently loadable."""
722
 
 
739
  raise RuntimeError("Saved ESM3 config.json is missing or invalid.") from error
740
  if not isinstance(config, dict):
741
  raise RuntimeError("Saved ESM3 config.json must contain a JSON object.")
742
+ auto_map = {
743
  "AutoConfig": "modeling_fastplms.FastESM3Config",
744
  "AutoModel": "modeling_fastplms.FastESM3Model",
745
  }
746
+ auto_map[auto_class] = f"modeling_fastplms.{model_class}"
747
+ config["auto_map"] = auto_map
748
  config_payload = (json.dumps(config, indent=2, sort_keys=True) + "\n").encode("utf-8")
749
  _replace_saved_runtime_file(config_path, config_payload)
750
 
 
2267
  _validate_saved_runtime_destination(save_path)
2268
  prepared_runtime = _build_saved_runtime_archive(Path(__file__).resolve().parents[2])
2269
  super().save_pretrained(save_directory, *args, **kwargs)
2270
+ _write_saved_runtime(
2271
+ save_path,
2272
+ prepared_runtime,
2273
+ auto_class=self._auto_class,
2274
+ model_class=type(self).__name__,
2275
+ )
2276
 
2277
  def tokenize_sequences(
2278
  self,
 
2593
  if not return_dict:
2594
  return result.to_tuple()
2595
  return result
2596
+
2597
+
2598
+ def _esm3_classifier_attention_mask(
2599
+ attention_mask: torch.Tensor | None,
2600
+ input_ids: torch.Tensor | None,
2601
+ sequence_tokens: torch.Tensor | None,
2602
+ ) -> torch.Tensor | None:
2603
+ """Infer padding attention for classifier calls that provide sequence tokens."""
2604
+
2605
+ if attention_mask is not None:
2606
+ return attention_mask
2607
+ tokens = sequence_tokens if sequence_tokens is not None else input_ids
2608
+ return None if tokens is None else tokens.ne(SEQUENCE_PAD_TOKEN)
2609
+
2610
+
2611
+ def _esm3_residue_mask(
2612
+ hidden_states: torch.Tensor,
2613
+ attention_mask: torch.Tensor | None,
2614
+ input_ids: torch.Tensor | None,
2615
+ sequence_tokens: torch.Tensor | None,
2616
+ ) -> torch.Tensor:
2617
+ """Select biological residue positions from one ESM3 token-aligned output."""
2618
+
2619
+ tokens = sequence_tokens if sequence_tokens is not None else input_ids
2620
+ if attention_mask is None:
2621
+ mask = torch.ones(hidden_states.shape[:2], dtype=torch.bool, device=hidden_states.device)
2622
+ else:
2623
+ mask = attention_mask.to(device=hidden_states.device, dtype=torch.bool)
2624
+ if tokens is not None:
2625
+ tokens = tokens.to(hidden_states.device)
2626
+ special = (
2627
+ tokens.eq(SEQUENCE_BOS_TOKEN)
2628
+ | tokens.eq(SEQUENCE_PAD_TOKEN)
2629
+ | tokens.eq(SEQUENCE_EOS_TOKEN)
2630
+ | tokens.eq(SEQUENCE_CHAINBREAK_TOKEN)
2631
+ )
2632
+ mask = mask & ~special
2633
+ return mask
2634
+
2635
+
2636
+ def _esm3_problem_type(
2637
+ config: FastESM3Config,
2638
+ num_labels: int,
2639
+ labels: torch.Tensor,
2640
+ ) -> str:
2641
+ if config.problem_type is None:
2642
+ if num_labels == 1:
2643
+ config.problem_type = "regression"
2644
+ elif labels.dtype in (torch.long, torch.int):
2645
+ config.problem_type = "single_label_classification"
2646
+ else:
2647
+ config.problem_type = "multi_label_classification"
2648
+ if config.problem_type not in {
2649
+ "regression",
2650
+ "single_label_classification",
2651
+ "multi_label_classification",
2652
+ }:
2653
+ raise ValueError(f"Unsupported problem_type: {config.problem_type!r}.")
2654
+ return config.problem_type
2655
+
2656
+
2657
+ def _esm3_sequence_classification_loss(
2658
+ logits: torch.Tensor,
2659
+ labels: torch.Tensor,
2660
+ problem_type: str,
2661
+ num_labels: int,
2662
+ ) -> torch.Tensor:
2663
+ labels = labels.to(logits.device)
2664
+ if problem_type == "regression":
2665
+ if num_labels == 1:
2666
+ return F.mse_loss(logits.reshape(-1), labels.reshape(-1))
2667
+ return F.mse_loss(logits, labels)
2668
+ if problem_type == "single_label_classification":
2669
+ return F.cross_entropy(logits.view(-1, num_labels), labels.view(-1))
2670
+ return F.binary_cross_entropy_with_logits(logits, labels)
2671
+
2672
+
2673
+ def _esm3_token_classification_loss(
2674
+ logits: torch.Tensor,
2675
+ labels: torch.Tensor,
2676
+ residue_mask: torch.Tensor,
2677
+ problem_type: str,
2678
+ num_labels: int,
2679
+ ) -> torch.Tensor:
2680
+ labels = labels.to(logits.device)
2681
+ if problem_type == "single_label_classification":
2682
+ if labels.shape != logits.shape[:2]:
2683
+ raise ValueError(
2684
+ "Single-label token targets must have shape (batch, sequence); "
2685
+ f"received {tuple(labels.shape)} for logits {tuple(logits.shape)}."
2686
+ )
2687
+ targets = labels.masked_fill(~residue_mask, -100)
2688
+ return F.cross_entropy(
2689
+ logits.reshape(-1, num_labels),
2690
+ targets.reshape(-1),
2691
+ ignore_index=-100,
2692
+ )
2693
+
2694
+ if problem_type == "regression" and num_labels == 1 and labels.ndim == 2:
2695
+ labels = labels.unsqueeze(-1)
2696
+ if labels.shape != logits.shape:
2697
+ raise ValueError(
2698
+ "Token regression and multilabel targets must match the logits shape; "
2699
+ f"received {tuple(labels.shape)} and {tuple(logits.shape)}."
2700
+ )
2701
+ valid = residue_mask.unsqueeze(-1) & labels.ne(-100)
2702
+ if not bool(valid.any()):
2703
+ raise ValueError("Token labels do not contain a supervised biological residue.")
2704
+ if problem_type == "regression":
2705
+ return F.mse_loss(logits[valid], labels[valid])
2706
+ return F.binary_cross_entropy_with_logits(logits[valid], labels[valid])
2707
+
2708
+
2709
+ class FastESM3ForSequenceClassification(FastESM3Model):
2710
+ """ESM3 with a padding-aware classifier over final residue embeddings."""
2711
+
2712
+ _auto_class = "AutoModelForSequenceClassification"
2713
+
2714
+ def __init__(self, config: FastESM3Config, **kwargs) -> None:
2715
+ super().__init__(config, **kwargs)
2716
+ self.num_labels = config.num_labels
2717
+ dropout = getattr(config, "classifier_dropout", 0.0)
2718
+ self.dropout = nn.Dropout(0.0 if dropout is None else float(dropout))
2719
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
2720
+ self.classifier.apply(self._init_weights)
2721
+
2722
+ def forward(
2723
+ self,
2724
+ input_ids: torch.Tensor | None = None,
2725
+ attention_mask: torch.Tensor | None = None,
2726
+ sequence_tokens: torch.Tensor | None = None,
2727
+ structure_tokens: torch.Tensor | None = None,
2728
+ ss8_tokens: torch.Tensor | None = None,
2729
+ sasa_tokens: torch.Tensor | None = None,
2730
+ function_tokens: torch.Tensor | None = None,
2731
+ residue_annotation_tokens: torch.Tensor | None = None,
2732
+ average_plddt: torch.Tensor | None = None,
2733
+ per_res_plddt: torch.Tensor | None = None,
2734
+ structure_coords: torch.Tensor | None = None,
2735
+ chain_id: torch.Tensor | None = None,
2736
+ sequence_id: torch.Tensor | None = None,
2737
+ labels: torch.Tensor | None = None,
2738
+ output_attentions: bool | None = None,
2739
+ output_hidden_states: bool | None = None,
2740
+ return_dict: bool | None = None,
2741
+ **kwargs,
2742
+ ) -> SequenceClassifierOutput | tuple[torch.Tensor, ...]:
2743
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
2744
+ classifier_attention_mask = _esm3_classifier_attention_mask(
2745
+ attention_mask,
2746
+ input_ids,
2747
+ sequence_tokens,
2748
+ )
2749
+ output = super().forward(
2750
+ input_ids=input_ids,
2751
+ attention_mask=classifier_attention_mask,
2752
+ sequence_tokens=sequence_tokens,
2753
+ structure_tokens=structure_tokens,
2754
+ ss8_tokens=ss8_tokens,
2755
+ sasa_tokens=sasa_tokens,
2756
+ function_tokens=function_tokens,
2757
+ residue_annotation_tokens=residue_annotation_tokens,
2758
+ average_plddt=average_plddt,
2759
+ per_res_plddt=per_res_plddt,
2760
+ structure_coords=structure_coords,
2761
+ chain_id=chain_id,
2762
+ sequence_id=sequence_id,
2763
+ labels=None,
2764
+ output_attentions=output_attentions,
2765
+ output_hidden_states=output_hidden_states,
2766
+ return_dict=True,
2767
+ **kwargs,
2768
+ )
2769
+ hidden_states = output.last_hidden_state
2770
+ if hidden_states is None:
2771
+ raise RuntimeError("ESM3 did not return final residue embeddings.")
2772
+ residue_mask = _esm3_residue_mask(
2773
+ hidden_states,
2774
+ classifier_attention_mask,
2775
+ input_ids,
2776
+ sequence_tokens,
2777
+ )
2778
+ residue_counts = residue_mask.sum(dim=1, keepdim=True)
2779
+ if not bool(residue_counts.all()):
2780
+ raise ValueError("Sequence classification requires one biological residue per row.")
2781
+ pooled = (hidden_states * residue_mask.unsqueeze(-1)).sum(dim=1)
2782
+ pooled = pooled / residue_counts.to(hidden_states.dtype)
2783
+ logits = self.classifier(self.dropout(pooled))
2784
+
2785
+ loss = None
2786
+ if labels is not None:
2787
+ problem_type = _esm3_problem_type(self.config, self.num_labels, labels)
2788
+ loss = _esm3_sequence_classification_loss(
2789
+ logits,
2790
+ labels,
2791
+ problem_type,
2792
+ self.num_labels,
2793
+ )
2794
+ result = SequenceClassifierOutput(
2795
+ loss=loss,
2796
+ logits=logits,
2797
+ hidden_states=output.hidden_states,
2798
+ attentions=output.attentions,
2799
+ )
2800
+ return result if return_dict else result.to_tuple()
2801
+
2802
+
2803
+ class FastESM3ForTokenClassification(FastESM3Model):
2804
+ """ESM3 with a token task head over final residue embeddings."""
2805
+
2806
+ _auto_class = "AutoModelForTokenClassification"
2807
+
2808
+ def __init__(self, config: FastESM3Config, **kwargs) -> None:
2809
+ super().__init__(config, **kwargs)
2810
+ self.num_labels = config.num_labels
2811
+ dropout = getattr(config, "classifier_dropout", 0.0)
2812
+ self.dropout = nn.Dropout(0.0 if dropout is None else float(dropout))
2813
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
2814
+ self.classifier.apply(self._init_weights)
2815
+
2816
+ def forward(
2817
+ self,
2818
+ input_ids: torch.Tensor | None = None,
2819
+ attention_mask: torch.Tensor | None = None,
2820
+ sequence_tokens: torch.Tensor | None = None,
2821
+ structure_tokens: torch.Tensor | None = None,
2822
+ ss8_tokens: torch.Tensor | None = None,
2823
+ sasa_tokens: torch.Tensor | None = None,
2824
+ function_tokens: torch.Tensor | None = None,
2825
+ residue_annotation_tokens: torch.Tensor | None = None,
2826
+ average_plddt: torch.Tensor | None = None,
2827
+ per_res_plddt: torch.Tensor | None = None,
2828
+ structure_coords: torch.Tensor | None = None,
2829
+ chain_id: torch.Tensor | None = None,
2830
+ sequence_id: torch.Tensor | None = None,
2831
+ labels: torch.Tensor | None = None,
2832
+ output_attentions: bool | None = None,
2833
+ output_hidden_states: bool | None = None,
2834
+ return_dict: bool | None = None,
2835
+ **kwargs,
2836
+ ) -> TokenClassifierOutput | tuple[torch.Tensor, ...]:
2837
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
2838
+ classifier_attention_mask = _esm3_classifier_attention_mask(
2839
+ attention_mask,
2840
+ input_ids,
2841
+ sequence_tokens,
2842
+ )
2843
+ output = super().forward(
2844
+ input_ids=input_ids,
2845
+ attention_mask=classifier_attention_mask,
2846
+ sequence_tokens=sequence_tokens,
2847
+ structure_tokens=structure_tokens,
2848
+ ss8_tokens=ss8_tokens,
2849
+ sasa_tokens=sasa_tokens,
2850
+ function_tokens=function_tokens,
2851
+ residue_annotation_tokens=residue_annotation_tokens,
2852
+ average_plddt=average_plddt,
2853
+ per_res_plddt=per_res_plddt,
2854
+ structure_coords=structure_coords,
2855
+ chain_id=chain_id,
2856
+ sequence_id=sequence_id,
2857
+ labels=None,
2858
+ output_attentions=output_attentions,
2859
+ output_hidden_states=output_hidden_states,
2860
+ return_dict=True,
2861
+ **kwargs,
2862
+ )
2863
+ hidden_states = output.last_hidden_state
2864
+ if hidden_states is None:
2865
+ raise RuntimeError("ESM3 did not return final residue embeddings.")
2866
+ residue_mask = _esm3_residue_mask(
2867
+ hidden_states,
2868
+ classifier_attention_mask,
2869
+ input_ids,
2870
+ sequence_tokens,
2871
+ )
2872
+ logits = self.classifier(self.dropout(hidden_states))
2873
+
2874
+ loss = None
2875
+ if labels is not None:
2876
+ problem_type = _esm3_problem_type(self.config, self.num_labels, labels)
2877
+ loss = _esm3_token_classification_loss(
2878
+ logits,
2879
+ labels,
2880
+ residue_mask,
2881
+ problem_type,
2882
+ self.num_labels,
2883
+ )
2884
+ result = TokenClassifierOutput(
2885
+ loss=loss,
2886
+ logits=logits,
2887
+ hidden_states=output.hidden_states,
2888
+ attentions=output.attentions,
2889
+ )
2890
+ return result if return_dict else result.to_tuple()
fastplms_bundle.py CHANGED
The diff for this file is too large to render. See raw diff
 
modeling_fastplms.py CHANGED
@@ -8,11 +8,12 @@ import sys
8
  import tempfile
9
  from io import BytesIO
10
  from pathlib import Path
 
11
  from zipfile import ZIP_DEFLATED, ZipFile
12
 
13
  from .fastplms_bundle import RUNTIME_DATA, RUNTIME_HASH
14
 
15
- if RUNTIME_HASH != "6fc80802a28c93c5200cbe876afa1e1f7984cba094062417774f0cfaa00eb89e":
16
  raise RuntimeError("FastPLMs runtime identity differs from the bridge.")
17
 
18
  _RUNTIME_TEMPORARIES = []
@@ -179,8 +180,12 @@ def _install_runtime():
179
  return package
180
 
181
  _install_runtime()
182
- _module_181 = _import_without_bytecode("fastplms.models.esm3.modeling_esm3")
183
- FastESM3Config = _module_181.FastESM3Config
184
  FastESM3Config.__module__ = __name__
185
- FastESM3Model = _module_181.FastESM3Model
 
 
 
 
186
  FastESM3Model.__module__ = __name__
 
8
  import tempfile
9
  from io import BytesIO
10
  from pathlib import Path
11
+ from typing import ClassVar
12
  from zipfile import ZIP_DEFLATED, ZipFile
13
 
14
  from .fastplms_bundle import RUNTIME_DATA, RUNTIME_HASH
15
 
16
+ if RUNTIME_HASH != "3471ed179d0b65b2145d6ee0069ad26a013096034f66661efa5a7e52aecba71d":
17
  raise RuntimeError("FastPLMs runtime identity differs from the bridge.")
18
 
19
  _RUNTIME_TEMPORARIES = []
 
180
  return package
181
 
182
  _install_runtime()
183
+ _module_182 = _import_without_bytecode("fastplms.models.esm3.modeling_esm3")
184
+ FastESM3Config = _module_182.FastESM3Config
185
  FastESM3Config.__module__ = __name__
186
+ FastESM3ForSequenceClassification = _module_182.FastESM3ForSequenceClassification
187
+ FastESM3ForSequenceClassification.__module__ = __name__
188
+ FastESM3ForTokenClassification = _module_182.FastESM3ForTokenClassification
189
+ FastESM3ForTokenClassification.__module__ = __name__
190
+ FastESM3Model = _module_182.FastESM3Model
191
  FastESM3Model.__module__ = __name__