Fill-Mask
Transformers
Safetensors
nucengram
feature-extraction
biology
genomics
dna
masked-lm
custom_code
Instructions to use FreakingPotato/NucEngram with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use FreakingPotato/NucEngram with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("fill-mask", model="FreakingPotato/NucEngram", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("FreakingPotato/NucEngram", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
| """ModernBERT-based encoder for genomic MLM, augmented with Engram injection. | |
| This wrapper adds Engram (conditional N-gram memory) deltas to specific | |
| ModernBERT encoder layers via forward pre-hooks. It does NOT modify HF's | |
| ModernBERT code, the ``engram`` module, or the ``model_modernbert`` module — | |
| all three are reused unchanged. | |
| Backbone parameter names match ``ModernBertWrapper`` exactly so a checkpoint | |
| trained as ``model_type='modernbert'`` can be warm-started into this class | |
| with ``strict=False``: only ``engram_blocks.*`` keys are missing. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import replace | |
| from typing import Sequence | |
| import torch | |
| import torch.nn as nn | |
| from .engram import Engram, EngramConfig | |
| from .model_modernbert import ( | |
| ModernBertGenomicConfig, | |
| build_modernbert, | |
| ) | |
| def _validate_layer_inject_ids(ids: Sequence[int], num_layers: int) -> list[int]: | |
| out: list[int] = [] | |
| for idx, i in enumerate(ids): | |
| if isinstance(i, bool) or not isinstance(i, int): | |
| raise ValueError( | |
| f"layer_inject_ids[{idx}] = {i!r} is not an int (bools are rejected too)" | |
| ) | |
| if i < 0 or i >= num_layers: | |
| raise ValueError( | |
| f"layer_inject_ids[{idx}] = {i} out of range [0, {num_layers})" | |
| ) | |
| out.append(i) | |
| if len(set(out)) != len(out): | |
| raise ValueError(f"layer_inject_ids contains duplicates: {list(ids)}") | |
| return out | |
| def _engram_key(i: int) -> str: | |
| return f"layer_{i:04d}" | |
| class ModernBertEngramWrapper(nn.Module): | |
| """ModernBERT encoder for MLM with Engram modules injected at chosen layers. | |
| The wrapper: | |
| * Constructs a stock ``ModernBertForMaskedLM`` via :func:`build_modernbert` | |
| and stores it at ``self.model`` (identical attribute layout to | |
| :class:`ModernBertWrapper`, so state_dict keys are exactly the same | |
| for every backbone tensor). | |
| * Adds ``self.engram_blocks: nn.ModuleDict`` keyed by ``layer_{i:04d}``, | |
| one :class:`Engram` per index in ``engram_cfg.layer_inject_ids``. | |
| * Registers a ``forward_pre_hook(with_kwargs=True)`` on each target | |
| encoder layer that adds ``engram_blocks[layer_{i:04d}](hidden, input_ids)`` | |
| to the layer's input ``hidden_states``. | |
| ``input_ids`` is stashed on ``self._current_input_ids`` at the start of | |
| each ``forward`` and never cleared inside the same call, so layers | |
| recomputed during backward (e.g. under ``torch.utils.checkpoint``) still | |
| see the right ids. | |
| """ | |
| def __init__(self, cfg: ModernBertGenomicConfig, engram_cfg: EngramConfig): | |
| super().__init__() | |
| ids = _validate_layer_inject_ids(engram_cfg.layer_inject_ids, cfg.num_hidden_layers) | |
| engram_cfg = replace( | |
| engram_cfg, | |
| hidden_size=cfg.hidden_size, | |
| vocab_size=cfg.vocab_size, | |
| pad_id=cfg.pad_token_id, | |
| ) | |
| self.cfg = cfg | |
| self.engram_cfg = engram_cfg | |
| # IDENTICAL outer attribute layout to ModernBertWrapper: | |
| self.model, self.hf_cfg = build_modernbert(cfg) | |
| # Sibling engram modules — NOT inside self.model, so backbone keys are unchanged. | |
| self.engram_blocks = nn.ModuleDict( | |
| {_engram_key(i): Engram(engram_cfg, layer_id=i) for i in ids} | |
| ) | |
| for i in ids: | |
| self.model.model.layers[i].register_forward_pre_hook( | |
| self._make_hook(i), with_kwargs=True | |
| ) | |
| self._current_input_ids: torch.Tensor | None = None | |
| def attn_implementation(self) -> str: | |
| impl = getattr(self.model.config, "_attn_implementation", None) | |
| if impl: | |
| return impl | |
| return getattr(self.model.config, "attn_implementation", "unknown") | |
| def num_params(self, only_trainable: bool = True) -> dict: | |
| backbone = 0 | |
| memory = 0 | |
| for name, p in self.named_parameters(): | |
| if only_trainable and not p.requires_grad: | |
| continue | |
| n = p.numel() | |
| if "engram" in name and "embedding.emb" in name: | |
| memory += n | |
| else: | |
| backbone += n | |
| return {"backbone": backbone, "memory": memory, "total": backbone + memory} | |
| def _make_hook(self, layer_id: int): | |
| key = _engram_key(layer_id) | |
| # Cache to avoid one dict lookup per call. | |
| block_ref = self.engram_blocks[key] | |
| wrapper_self = self | |
| def hook(module, args, kwargs): | |
| ids = wrapper_self._current_input_ids | |
| if ids is None: | |
| return None # cold-call safety (e.g. tracing); do nothing | |
| if args: | |
| hidden = args[0] | |
| delta = block_ref(hidden, input_ids=ids) | |
| new_args = (hidden + delta, *args[1:]) | |
| return new_args, kwargs | |
| hidden = kwargs["hidden_states"] | |
| delta = block_ref(hidden, input_ids=ids) | |
| kwargs = {**kwargs, "hidden_states": hidden + delta} | |
| return args, kwargs | |
| return hook | |
| def forward( | |
| self, | |
| input_ids: torch.Tensor, | |
| labels: torch.Tensor | None = None, | |
| attention_mask: torch.Tensor | None = None, | |
| ) -> dict: | |
| # Overwrite-only on every forward; do NOT clear before/after model() so | |
| # that any backward-time recomputed forward (e.g. gradient checkpointing) | |
| # still reads the same ids that the original forward used. | |
| self._current_input_ids = input_ids | |
| if attention_mask is not None: | |
| attention_mask = attention_mask.to(torch.long) | |
| out = self.model( | |
| input_ids=input_ids, | |
| attention_mask=attention_mask, | |
| labels=labels, | |
| output_hidden_states=False, | |
| ) | |
| return {"loss": out.loss, "logits": out.logits, "hidden": None} | |