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, built on HuggingFace. | |
| We construct ``ModernBertForMaskedLM`` from scratch with our 9-token DNA | |
| vocabulary and configurable max_position. Supports both ``sdpa`` and | |
| ``flash_attention_2`` attention implementations. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Literal | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| # ─── Patch HF ModernBertConfig for global_attn_every_n_layers=1 ─────────── | |
| # transformers 5.7.0 has a bug: when all layers are "full_attention", the | |
| # original convert_rope_params_to_dict unconditionally injects a | |
| # "sliding_attention" entry into rope_parameters, then standardize_rope_params | |
| # takes its Case-1 (single-rope) path and mutates rope_parameters with | |
| # top-level "rope_type"/"rope_theta" keys — the strict-dataclass validator | |
| # then rejects the result because those keys aren't in the Literal subset. | |
| # Fix: only add "sliding_attention" rope params when layer_types actually has | |
| # at least one sliding_attention layer. Drop it otherwise so standardize takes | |
| # the Case-2 (per-layer-type) path with only "full_attention". | |
| def _patch_modernbert_rope_params(): | |
| from transformers import ModernBertConfig | |
| if getattr(ModernBertConfig, "_nucengram_patched", False): | |
| return | |
| def patched(self, **kwargs): | |
| rope_scaling = kwargs.pop("rope_scaling", None) | |
| default_rope_params = { | |
| "sliding_attention": {"rope_type": "default"}, | |
| "full_attention": {"rope_type": "default"}, | |
| } | |
| self.rope_parameters = (self.rope_parameters | |
| if self.rope_parameters is not None | |
| else default_rope_params) | |
| if rope_scaling is not None: | |
| if "full_attention" in self.rope_parameters: | |
| self.rope_parameters["full_attention"].update(rope_scaling) | |
| if "sliding_attention" in self.rope_parameters: | |
| self.rope_parameters["sliding_attention"].update(rope_scaling) | |
| needs_sliding = (self.layer_types is not None | |
| and "sliding_attention" in set(self.layer_types)) | |
| if self.rope_parameters.get("full_attention") is None: | |
| self.rope_parameters["full_attention"] = {"rope_type": "default"} | |
| self.rope_parameters["full_attention"].setdefault( | |
| "rope_theta", kwargs.pop("global_rope_theta", self.default_theta["global"])) | |
| if needs_sliding: | |
| if self.rope_parameters.get("sliding_attention") is None: | |
| self.rope_parameters["sliding_attention"] = {"rope_type": "default"} | |
| self.rope_parameters["sliding_attention"].setdefault( | |
| "rope_theta", kwargs.pop("local_rope_theta", self.default_theta["local"])) | |
| else: | |
| # No sliding-attention layers → drop the entry so standardize_rope_params | |
| # takes Case-2 (per-layer-type) with only "full_attention" key. | |
| self.rope_parameters.pop("sliding_attention", None) | |
| kwargs.pop("local_rope_theta", None) | |
| self.standardize_rope_params() | |
| return kwargs | |
| ModernBertConfig.convert_rope_params_to_dict = patched | |
| ModernBertConfig._nucengram_patched = True | |
| _patch_modernbert_rope_params() | |
| from .tokenizer import VOCAB_SIZE, PAD_ID, BOS_ID, EOS_ID, MASK_ID | |
| AttentionImpl = Literal["sdpa", "flash_attention_2", "eager"] | |
| class ModernBertGenomicConfig: | |
| vocab_size: int = VOCAB_SIZE | |
| hidden_size: int = 384 | |
| intermediate_size: int = 1024 | |
| num_hidden_layers: int = 8 | |
| num_attention_heads: int = 6 | |
| max_position_embeddings: int = 8192 | |
| local_attention: int = 128 # span for local attention layers | |
| global_attn_every_n_layers: int = 3 # =3 alternating GLOBAL+2×LOCAL; =1 every layer GLOBAL | |
| rope_theta_global: float = 160000.0 # bigger theta for long context | |
| rope_theta_local: float = 10000.0 | |
| attn_implementation: AttentionImpl = "sdpa" | |
| pad_token_id: int = PAD_ID | |
| bos_token_id: int = BOS_ID | |
| eos_token_id: int = EOS_ID | |
| cls_token_id: int = BOS_ID | |
| sep_token_id: int = EOS_ID | |
| mask_token_id: int = MASK_ID | |
| norm_eps: float = 1e-5 | |
| embedding_dropout: float = 0.0 | |
| attention_dropout: float = 0.0 | |
| mlp_dropout: float = 0.0 | |
| initializer_range: float = 0.02 | |
| tie_word_embeddings: bool = True | |
| sparse_prediction: bool = False | |
| deterministic_flash_attn: bool = False | |
| bf16: bool = True | |
| def build_modernbert(cfg: ModernBertGenomicConfig): | |
| """Construct a ModernBertForMaskedLM with the given config.""" | |
| from transformers import ModernBertConfig, ModernBertForMaskedLM | |
| # When global_attn_every_n_layers=1 every layer is full_attention — no | |
| # sliding rope params needed. The HF patch above keeps strict validation happy. | |
| if cfg.global_attn_every_n_layers == 1: | |
| rope_parameters = {"full_attention": {"rope_theta": cfg.rope_theta_global}} | |
| else: | |
| rope_parameters = { | |
| "full_attention": {"rope_theta": cfg.rope_theta_global}, | |
| "sliding_attention": {"rope_theta": cfg.rope_theta_local}, | |
| } | |
| hf_cfg = ModernBertConfig( | |
| vocab_size=cfg.vocab_size, | |
| hidden_size=cfg.hidden_size, | |
| intermediate_size=cfg.intermediate_size, | |
| num_hidden_layers=cfg.num_hidden_layers, | |
| num_attention_heads=cfg.num_attention_heads, | |
| hidden_activation="gelu", | |
| max_position_embeddings=cfg.max_position_embeddings, | |
| norm_eps=cfg.norm_eps, | |
| norm_bias=False, | |
| pad_token_id=cfg.pad_token_id, | |
| bos_token_id=cfg.bos_token_id, | |
| eos_token_id=cfg.eos_token_id, | |
| cls_token_id=cfg.cls_token_id, | |
| sep_token_id=cfg.sep_token_id, | |
| attention_bias=False, | |
| attention_dropout=cfg.attention_dropout, | |
| local_attention=cfg.local_attention, | |
| global_attn_every_n_layers=cfg.global_attn_every_n_layers, | |
| rope_parameters=rope_parameters, | |
| embedding_dropout=cfg.embedding_dropout, | |
| mlp_bias=False, | |
| mlp_dropout=cfg.mlp_dropout, | |
| decoder_bias=True, | |
| classifier_pooling="mean", | |
| deterministic_flash_attn=cfg.deterministic_flash_attn, | |
| sparse_prediction=cfg.sparse_prediction, | |
| tie_word_embeddings=cfg.tie_word_embeddings, | |
| initializer_range=cfg.initializer_range, | |
| ) | |
| # Use FA2 only if we asked for it AND the install supports it. | |
| impl = cfg.attn_implementation | |
| if impl == "flash_attention_2": | |
| try: | |
| import flash_attn # noqa: F401 | |
| except ImportError as e: # pragma: no cover | |
| raise RuntimeError( | |
| "attn_implementation='flash_attention_2' requested but " | |
| "flash-attn is not importable. Install flash-attn or use 'sdpa'." | |
| ) from e | |
| model = ModernBertForMaskedLM(hf_cfg) | |
| # HuggingFace stores the chosen attention impl on the config as well as on | |
| # individual modules; we set it explicitly on the loaded model so it takes | |
| # effect for our from-scratch instantiation. | |
| model.config._attn_implementation = impl | |
| if hasattr(model, "set_attn_implementation"): | |
| model.set_attn_implementation(impl) | |
| return model, hf_cfg | |
| # --------------------------------------------------------------------------- | |
| # common adapter so the training loop can swap models | |
| # --------------------------------------------------------------------------- | |
| class ModernBertWrapper(nn.Module): | |
| """Thin adapter that gives a ModernBertForMaskedLM the same forward | |
| signature as our in-house ``NucEngramModel.forward`` (returns dict with | |
| ``loss``, ``logits``, ``hidden``). | |
| """ | |
| def __init__(self, cfg: ModernBertGenomicConfig): | |
| super().__init__() | |
| self.cfg = cfg | |
| self.model, self.hf_cfg = build_modernbert(cfg) | |
| 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 = sum(p.numel() for p in self.parameters() | |
| if p.requires_grad or not only_trainable) | |
| return {"backbone": backbone, "memory": 0, "total": backbone} | |
| def forward(self, | |
| input_ids: torch.Tensor, | |
| labels: torch.Tensor | None = None, | |
| attention_mask: torch.Tensor | None = None) -> dict: | |
| # ModernBert wants attention_mask in {0, 1} (1 = keep). | |
| 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, | |
| } | |