
A generative DNA foundation model from the Carbon family.
Carbon-3B is a 3B-parameter decoder-only autoregressive genomic foundation model trained on DNA and RNA sequences, with a primary focus on eukaryotes. It has a native context length of 32,768 6-mer tokens (β 197k DNA base pairs) and extends to 65,536 tokens (β 393 kbp) at inference time via YaRN. Carbon-3B is designed to be both strong and efficient: on generative tasks (sequence recovery), variant-effect prediction, and motif-perturbation discrimination, it matches the capability of substantially larger single-nucleotide baselines such as Evo2-7B while running several times faster.
Carbon-3B is the flagship model of the Carbon family. We also release Carbon-8B for users who need additional capability at higher inference cost, and Carbon-500M β a small generative model intended for speculative decoding alongside Carbon-3B (or Carbon-8B).
Across our zero-shot evaluation suite, sequence recovery, four variant-effect-prediction (VEP) benchmarks (ClinVar coding, ClinVar non-coding, BRCA2, TraitGym Mendelian), and two sequence-level perturbation tasks (nucleotide triplet-expansion and synonymous codon replacement), Carbon-3B is competitive with Evo2-7B. It additionally works well on long context and retrieves needles reliably from up to β 393 kbp of distal context on the Genomic-NIAH long-context benchmark, while remaining several times faster than Evo2-7B. For full design rationale and ablations, see the Carbon technical report and the Carbon GitHub repository.
Carbon-3B is a standard Hugging Face causal LM. The custom DNA tokenizer requires trust_remote_code=True on the tokenizer; the model itself is stock LlamaForCausalLM and does not require it.
pip install -U transformers
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
repo = "HuggingFaceBio/Carbon-3B"
tok = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
repo,
dtype=torch.bfloat16,
).cuda().eval()
# Wrap a DNA prompt with the <dna> tag (the model is trained with this format).
# DNA length should be a multiple of 6 β see the Tokenizer section below.
dna_prompt = "ATGCGCTAGCTACGATCGATCGTAGCTAGCTAGCTAGCTACG" # 42 bp = 7 Γ 6-mer
prompt = f"<dna>{dna_prompt}"
inputs = tok(prompt, return_tensors="pt", add_special_tokens=False).to("cuda")
out = model.generate(
**inputs,
max_new_tokens=64,
do_sample=False,
)
print(tok.decode(out[0][inputs.input_ids.shape[1]:], skip_special_tokens=True))
The Carbon tokenizer is a hybrid of BPE (for English text) and a fixed 6-mer scheme (for DNA). 6-mer tokenization is the central DNA-modeling design choice in Carbon β we found it works substantially better than BPE for DNA (see the Carbon technical report for the analysis) β but it comes with a few practical constraints when feeding input to the model. The tokenizer only switches into 6-mer mode when it sees the <dna> tag and emits <oov> token for any token with letters not in [ATCG].
<dna> β this is criticalIf you pass a raw DNA sequence without the <dna> tag, the tokenizer treats it as English text and applies BPE. BPE-tokenized DNA is essentially a different language for Carbon-3B, and performance collapses across every benchmark. Always prepend <dna> before any DNA content. Use </dna> to close the DNA block if you intend to follow it with non-DNA tokens.
# β Wrong β tokenized as BPE, performance collapses
prompt = "ATGCGCTAGCTACGATCGATCGTAGCTAGCTAG"
# β
Correct β `<dna>` flips the tokenizer into 6-mer mode, for generation
prompt = "<dna>ATGCGCTAGCTACGATCGATCGTAGCTAGCTAG"
# β
Also correct β explicitly close the DNA block
prompt = "<dna>ATGCGCTAGCTACGATCGATCGTAGCTAGCTAG</dna>"
The <dna> / </dna> tell the tokenizer where to switch modes.
Anything else inside a <dna>... block β lowercase bases, IUPAC ambiguity codes (N, Y, R, β¦), or any other character β is mapped to the <oov> token. Filter to canonical uppercase ACGT before passing input if you don't want <oov>.
Each DNA token encodes 6 nucleotides, so the tokenizer groups input in non-overlapping 6-mer blocks. If the sequence is not a multiple of 6, the current tokenizer right-pads the trailing partial block with As (e.g. ...CTAG β token TAGAAA). We recommend truncating to a multiple of 6 before passing the sequence in:
def truncate_to_6mer(seq: str) -> str:
return seq[: (len(seq) // 6) * 6]
prompt = f"<dna>{truncate_to_6mer(seq)}"
For variant-effect or perturbation tasks, score sequences with the model's per-token log-probabilities. A minimal single-sequence helper:
import torch
import torch.nn.functional as F
@torch.no_grad()
def score(seq: str) -> float:
"""Mean log-prob per DNA token of `seq` (single sequence, no padding)."""
text = f"<dna>{seq}</dna>"
ids = tok(text, return_tensors="pt", add_special_tokens=False).input_ids.to(model.device)
logits = model(ids).logits[:, :-1, :]
targets = ids[:, 1:]
logp = F.log_softmax(logits.float(), dim=-1).gather(-1, targets.unsqueeze(-1)).squeeze(-1)
return logp.mean().item()
For batched scoring with attention masking and full reproducible evaluation pipelines (sequence recovery, ClinVar / BRCA2 / TraitGym VEP, triplet-expansion / synonymous codon replacement, Genomic-NIAH), use the official scripts in the Carbon evaluation directory β see perturbation_tasks.py for the canonical score_hf implementation and README.md for run instructions across all tasks.
The released config.json is configured for the native 32 k context. To extend to 65,536 tokens (β 393 kbp) at inference time, override max_position_embeddings and add a YaRN rope_scaling block. We recommend a YaRN factor of 4, which we observed gives better retrieval quality at 64 k than a tighter factor of 2:
from transformers import AutoConfig, AutoModelForCausalLM
config = AutoConfig.from_pretrained(repo, trust_remote_code=True)
config.max_position_embeddings = 65536 # 65,536 tokens β 393 kbp
config.rope_scaling = {
"type": "yarn",
"factor": 4.0,
"original_max_position_embeddings": 32768,
}
model = AutoModelForCausalLM.from_pretrained(
repo, config=config, dtype=torch.bfloat16
).cuda().eval()
We do not recommend pushing beyond 64 k tokens: retrieval quality degrades sharply at 128 k context in our benchmarks.
Carbon-3B and Carbon-500M share the same tokenizer and DNA template format, so Carbon-500M can be used as a draft model for speculative decoding with Carbon-3B (or Carbon-8B) as the target model, reducing wall-clock generation cost at no quality loss.
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
draft = AutoModelForCausalLM.from_pretrained(
"HuggingFaceBio/Carbon-500M",
dtype=torch.bfloat16,
).cuda().eval()
target = model # Carbon-3B, loaded above
inputs = tok(f"<dna>{dna_prompt}", return_tensors="pt", add_special_tokens=False).to("cuda")
out = target.generate(
**inputs,
max_new_tokens=256,
do_sample=False,
assistant_model=draft,
)
The model is trained with a mixed-template objective; some examples are prefixed with species-type and/or gene-type metadata tokens. Generation can be conditioned on these by prepending the corresponding tokens:
prompt = "<vertebrate_mammalian><protein_coding_region><dna>ATGCGCTAG..."
The unconditional <dna>SEQUENCE</dna> format remains supported and is the default. See the Carbon technical report for the full list of supported metadata tags.
The fns branch loads custom modeling code for Factorized Nucleotide Supervision (FNS). Carbon still uses its efficient 6-mer tokenizer, but during generation each selected 6-mer is assembled from six per-position nucleotide distributions, giving base-pair-level control over decoded DNA. Use this branch when you need exact base-pair counts, per-position masks, or temperature/top-p behavior applied at the nucleotide level rather than over the 4,096-way 6-mer distribution:
import math
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "HuggingFaceBio/Carbon-3B"
revision = "fns"
device = "cuda"
tokenizer = AutoTokenizer.from_pretrained(model_id, revision=revision, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_id,
revision=revision,
trust_remote_code=True,
dtype=torch.bfloat16,
).to(device).eval()
context = "ATGCGCTAGCTACGATCGATCGTAGCTAGCTAGCTAGCTACG"
n_bp = 60
inputs = tokenizer(f"<dna>{context}", return_tensors="pt", add_special_tokens=False).to(device)
with torch.no_grad():
output_ids = model.generate(
**inputs,
max_new_tokens=math.ceil(n_bp / tokenizer.k),
do_sample=False,
pad_token_id=tokenizer.eos_token_id,
)
generated_ids = output_ids[0, inputs.input_ids.shape[1]:]
generated_dna = tokenizer.decode(generated_ids, skip_special_tokens=True)[:n_bp]
print(generated_dna)
The same per-base marginals are exposed through score_sequence(), which returns the probability assigned to the observed base at each position. Taking the mean log probability gives a base-pair-level sequence score, where higher values indicate higher model likelihood:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "HuggingFaceBio/Carbon-3B"
revision = "fns"
device = "cuda"
tokenizer = AutoTokenizer.from_pretrained(model_id, revision=revision, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_id,
revision=revision,
trust_remote_code=True,
dtype=torch.bfloat16,
).to(device).eval()
reference = "GGGCTATAAAGGCCATCGATCGATCGATCGATCGATCGATCG"
perturbed = "GGGCGCGCGCGGCCATCGATCGATCGATCGATCGATCGATCG"
with torch.no_grad():
bp_probs, actual_probs = model.score_sequence([reference, perturbed])
scores = [torch.log(p.clamp_min(1e-12)).mean().item() for p in actual_probs]
print(f"reference mean bp logp: {scores[0]:.4f}")
print(f"perturbed mean bp logp: {scores[1]:.4f}")
print(f"reference preferred: {scores[0] > scores[1]}")
All evaluations are zero-shot and use the public Carbon evaluation pipeline. The suite covers seven tasks across four capability families:
| Family | Task | Metric |
|---|---|---|
| Generative | Sequence recovery (eukaryote, bacteria, others splits) | Per-base accuracy on the next 30 bp |
| Variant-effect prediction (VEP) | ClinVar coding | AUROC, AUPRC (right-end / next-token scoring) |
| ClinVar non-coding | AUROC, AUPRC | |
| BRCA2 | AUROC, AUPRC, Spearman Ο (centered 8 kb window, full-LL delta) | |
| TraitGym Mendelian | AUROC, AUPRC, Spearman Ο | |
| Sequence-level perturbation | Nucleotide triplet-expansion (insert 10 consecutive CAG triplets into a CDS; model should prefer the natural reference) | Pairwise discrimination accuracy |
| Synonymous codon replacement (replace every codon with the highest-frequency synonym for the target species; model should prefer the natural reference) | Pairwise discrimination accuracy | |
| Long-context retrieval | Genomic-NIAH (4 task variants Γ 6 context lengths, up to 786 kbp) | gen_exact_match, ll_correct |
Below we highlight the three short-context probes for which we report headline numbers in this card. Full results, including all VEP benchmarks and Genomic-NIAH heatmaps, are in the Carbon technical report.
| Category | Metric (%) | Carbon 3B | GENERator-v2 3B | Evo2 7B |
|---|---|---|---|---|
| Generative | Sequence Recovery eukaryote | 61.54 | 58.56 | 59.86 |
| Variant effect prediction | BRCA2 | 84.63 | 81.93 | 83.52 |
| TraitGym Mendelian | 33.65 | 27.91 | 37.78 | |
| ClinVar coding (24 kb) | 92.89 | 91.55 | 93.33 | |
| ClinVar non-coding (24 kb) | 91.14 | 90.13 | 89.79 | |
| Perturbation | Nucleotide triplet-expansion | 85.20 | 83.06 | 88.43 |
| Synonymous codon | 88.89 | 87.03 | 91.59 |
Carbon-3B is competitive with Evo2-7B while being much faster to run.
Genomic-NIAH is a long context benchmark, inspired from NIAH and RULER benchmarks for English. The model needs to retrieves a random 24 bp VALUE planted in a real-genome haystack at one of five depths, evaluated at six context lengths from 24 kbp to 786 kbp. The benchmark contains 500 examples per (task, context) cell.
Below are the scores on niah:
| Context length | Carbon 3B 32k (native / YaRN 4Γ) | GENERator-v2 3B | Evo2-7B |
|---|---|---|---|
| 16 k tokens (98 kbp) | 0.73 / β | 0.74 | 0.97 |
| 32 k tokens (196 kbp) | 0.55 / 0.90 | β | 0.95 |
| 64 k tokens (393 kbp) | β / 0.79 | β | 0.80 |
Sample sizes: Carbon & GENERator n=500. Evo2-7B n=150 at 16k, n=100 at 32k, n=100 at 64k due to the slow inference speed.
niah at 98 kbp.niah at 393 kbp (64 k tokens) under YaRN, despite being substantially smaller.Carbon models run natively in vLLM and thus generate DNA sequences over 150 times faster than the Evo2 family of models. Below we show the results of a throughput benchmark, where 1080 base-pairs are used for prefill and decode with increasing number of input sequences. All models except Evo2 40B were run on a H100 GPU, with the batch size of the Evo2 models tuned to the largest possible size that fits in VRAM.

Carbon-3B is pre-trained for 1T 6-mer tokens (β 6T DNA base pairs) at sequence length 8 192, with a global batch size of 256 sequences (β 2 M tokens / step). The optimizer is AdamW throughout.
The data mixture during the stable phases of pre-training (Phase 1 and the stable portion of Phase 2) is the one documented on the HuggingFaceBio/carbon-pretraining-corpus dataset card: β 70 % Generator-style eukaryotic genomic DNA, with mRNA, splice-enriched mRNA, and GTDB bacterial genomes alongside metadata-conditioned templates.
The training uses a staged objective and learning-rate schedule:
See the Carbon technical report for the full pre-training recipe.
After pre-training, the model undergoes continued training for 50B additional tokens at sequence length 32,768, with the rotary base shifted from 5 Γ 10^5 to 5 Γ 10^6. The long-context training mixture is:
| Component | Fraction |
|---|---|
| Gener-style annotated genes (metadata-conditioned) | 35.0 % |
| Concatenated annotated genes (long-context-data) | 13.8 % |
| mRNA transcripts | 25.0 % |
| Splice-enriched mRNA | 10.0 % |
| GTDB bacterial genomes | 15.0 % |
| Promoter sequences | 1.2 % |
The optimizer is AdamW (Ξ²β = 0.9, Ξ²β = 0.95, Ξ΅ = 1e-8, weight decay = 0.1, gradient clipping = 1.0), with a WSD learning-rate schedule: 2,000 steps linear warmup from 0 to 3e-5, stable phase, then 4,000-step linear decay to 3e-6. Global batch size: 64 sequences Γ 32,768 tokens.
factor=4. Pushing further to 128 k tokens (β 786 kbp) causes retrieval quality to drop sharply in our long-context benchmarks.Apache 2.0.
Carbon is a joint collaboration between the research teams at Hugging Face, Zhongguancun Academy, and TIGEM/University of Naples βFederico IIβ.
@article{allal2026carbon,
title={Carbon: Decoding the Language of Life},
author={Allal, Loubna Ben and Li, Qiuyi and Fiusco, Maurizio and Tunstall, Lewis and Rasul, Kashif and Beeching, Ed and Aubakirova, Dana and Pati{\~n}o, Carlos and Frere, Thibaud and Lozhkov, Anton and others},
journal={bioRxiv},
pages={2026--05},
year={2026},
publisher={Cold Spring Harbor Laboratory}
}

A generative DNA foundation model from the Carbon family.
Carbon-3B is a 3B-parameter decoder-only autoregressive genomic foundation model trained on DNA and RNA sequences, with a primary focus on eukaryotes. It has a native context length of 32,768 6-mer tokens (β 197k DNA base pairs) and extends to 65,536 tokens (β 393 kbp) at inference time via YaRN. Carbon-3B is designed to be both strong and efficient: on generative tasks (sequence recovery), variant-effect prediction, and motif-perturbation discrimination, it matches the capability of substantially larger single-nucleotide baselines such as Evo2-7B while running several times faster.
Carbon-3B is the flagship model of the Carbon family. We also release Carbon-8B for users who need additional capability at higher inference cost, and Carbon-500M β a small generative model intended for speculative decoding alongside Carbon-3B (or Carbon-8B).
Across our zero-shot evaluation suite, sequence recovery, four variant-effect-prediction (VEP) benchmarks (ClinVar coding, ClinVar non-coding, BRCA2, TraitGym Mendelian), and two sequence-level perturbation tasks (nucleotide triplet-expansion and synonymous codon replacement), Carbon-3B is competitive with Evo2-7B. It additionally works well on long context and retrieves needles reliably from up to β 393 kbp of distal context on the Genomic-NIAH long-context benchmark, while remaining several times faster than Evo2-7B. For full design rationale and ablations, see the Carbon technical report and the Carbon GitHub repository.
Carbon-3B is a standard Hugging Face causal LM. The custom DNA tokenizer requires trust_remote_code=True on the tokenizer; the model itself is stock LlamaForCausalLM and does not require it.
pip install -U transformers
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
repo = "HuggingFaceBio/Carbon-3B"
tok = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
repo,
dtype=torch.bfloat16,
).cuda().eval()
# Wrap a DNA prompt with the <dna> tag (the model is trained with this format).
# DNA length should be a multiple of 6 β see the Tokenizer section below.
dna_prompt = "ATGCGCTAGCTACGATCGATCGTAGCTAGCTAGCTAGCTACG" # 42 bp = 7 Γ 6-mer
prompt = f"<dna>{dna_prompt}"
inputs = tok(prompt, return_tensors="pt", add_special_tokens=False).to("cuda")
out = model.generate(
**inputs,
max_new_tokens=64,
do_sample=False,
)
print(tok.decode(out[0][inputs.input_ids.shape[1]:], skip_special_tokens=True))
The Carbon tokenizer is a hybrid of BPE (for English text) and a fixed 6-mer scheme (for DNA). 6-mer tokenization is the central DNA-modeling design choice in Carbon β we found it works substantially better than BPE for DNA (see the Carbon technical report for the analysis) β but it comes with a few practical constraints when feeding input to the model. The tokenizer only switches into 6-mer mode when it sees the <dna> tag and emits <oov> token for any token with letters not in [ATCG].
<dna> β this is criticalIf you pass a raw DNA sequence without the <dna> tag, the tokenizer treats it as English text and applies BPE. BPE-tokenized DNA is essentially a different language for Carbon-3B, and performance collapses across every benchmark. Always prepend <dna> before any DNA content. Use </dna> to close the DNA block if you intend to follow it with non-DNA tokens.
# β Wrong β tokenized as BPE, performance collapses
prompt = "ATGCGCTAGCTACGATCGATCGTAGCTAGCTAG"
# β
Correct β `<dna>` flips the tokenizer into 6-mer mode, for generation
prompt = "<dna>ATGCGCTAGCTACGATCGATCGTAGCTAGCTAG"
# β
Also correct β explicitly close the DNA block
prompt = "<dna>ATGCGCTAGCTACGATCGATCGTAGCTAGCTAG</dna>"
The <dna> / </dna> tell the tokenizer where to switch modes.
Anything else inside a <dna>... block β lowercase bases, IUPAC ambiguity codes (N, Y, R, β¦), or any other character β is mapped to the <oov> token. Filter to canonical uppercase ACGT before passing input if you don't want <oov>.
Each DNA token encodes 6 nucleotides, so the tokenizer groups input in non-overlapping 6-mer blocks. If the sequence is not a multiple of 6, the current tokenizer right-pads the trailing partial block with As (e.g. ...CTAG β token TAGAAA). We recommend truncating to a multiple of 6 before passing the sequence in:
def truncate_to_6mer(seq: str) -> str:
return seq[: (len(seq) // 6) * 6]
prompt = f"<dna>{truncate_to_6mer(seq)}"
For variant-effect or perturbation tasks, score sequences with the model's per-token log-probabilities. A minimal single-sequence helper:
import torch
import torch.nn.functional as F
@torch.no_grad()
def score(seq: str) -> float:
"""Mean log-prob per DNA token of `seq` (single sequence, no padding)."""
text = f"<dna>{seq}</dna>"
ids = tok(text, return_tensors="pt", add_special_tokens=False).input_ids.to(model.device)
logits = model(ids).logits[:, :-1, :]
targets = ids[:, 1:]
logp = F.log_softmax(logits.float(), dim=-1).gather(-1, targets.unsqueeze(-1)).squeeze(-1)
return logp.mean().item()
For batched scoring with attention masking and full reproducible evaluation pipelines (sequence recovery, ClinVar / BRCA2 / TraitGym VEP, triplet-expansion / synonymous codon replacement, Genomic-NIAH), use the official scripts in the Carbon evaluation directory β see perturbation_tasks.py for the canonical score_hf implementation and README.md for run instructions across all tasks.
The released config.json is configured for the native 32 k context. To extend to 65,536 tokens (β 393 kbp) at inference time, override max_position_embeddings and add a YaRN rope_scaling block. We recommend a YaRN factor of 4, which we observed gives better retrieval quality at 64 k than a tighter factor of 2:
from transformers import AutoConfig, AutoModelForCausalLM
config = AutoConfig.from_pretrained(repo, trust_remote_code=True)
config.max_position_embeddings = 65536 # 65,536 tokens β 393 kbp
config.rope_scaling = {
"type": "yarn",
"factor": 4.0,
"original_max_position_embeddings": 32768,
}
model = AutoModelForCausalLM.from_pretrained(
repo, config=config, dtype=torch.bfloat16
).cuda().eval()
We do not recommend pushing beyond 64 k tokens: retrieval quality degrades sharply at 128 k context in our benchmarks.
Carbon-3B and Carbon-500M share the same tokenizer and DNA template format, so Carbon-500M can be used as a draft model for speculative decoding with Carbon-3B (or Carbon-8B) as the target model, reducing wall-clock generation cost at no quality loss.
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
draft = AutoModelForCausalLM.from_pretrained(
"HuggingFaceBio/Carbon-500M",
dtype=torch.bfloat16,
).cuda().eval()
target = model # Carbon-3B, loaded above
inputs = tok(f"<dna>{dna_prompt}", return_tensors="pt", add_special_tokens=False).to("cuda")
out = target.generate(
**inputs,
max_new_tokens=256,
do_sample=False,
assistant_model=draft,
)
The model is trained with a mixed-template objective; some examples are prefixed with species-type and/or gene-type metadata tokens. Generation can be conditioned on these by prepending the corresponding tokens:
prompt = "<vertebrate_mammalian><protein_coding_region><dna>ATGCGCTAG..."
The unconditional <dna>SEQUENCE</dna> format remains supported and is the default. See the Carbon technical report for the full list of supported metadata tags.
The fns branch loads custom modeling code for Factorized Nucleotide Supervision (FNS). Carbon still uses its efficient 6-mer tokenizer, but during generation each selected 6-mer is assembled from six per-position nucleotide distributions, giving base-pair-level control over decoded DNA. Use this branch when you need exact base-pair counts, per-position masks, or temperature/top-p behavior applied at the nucleotide level rather than over the 4,096-way 6-mer distribution:
import math
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "HuggingFaceBio/Carbon-3B"
revision = "fns"
device = "cuda"
tokenizer = AutoTokenizer.from_pretrained(model_id, revision=revision, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_id,
revision=revision,
trust_remote_code=True,
dtype=torch.bfloat16,
).to(device).eval()
context = "ATGCGCTAGCTACGATCGATCGTAGCTAGCTAGCTAGCTACG"
n_bp = 60
inputs = tokenizer(f"<dna>{context}", return_tensors="pt", add_special_tokens=False).to(device)
with torch.no_grad():
output_ids = model.generate(
**inputs,
max_new_tokens=math.ceil(n_bp / tokenizer.k),
do_sample=False,
pad_token_id=tokenizer.eos_token_id,
)
generated_ids = output_ids[0, inputs.input_ids.shape[1]:]
generated_dna = tokenizer.decode(generated_ids, skip_special_tokens=True)[:n_bp]
print(generated_dna)
The same per-base marginals are exposed through score_sequence(), which returns the probability assigned to the observed base at each position. Taking the mean log probability gives a base-pair-level sequence score, where higher values indicate higher model likelihood:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "HuggingFaceBio/Carbon-3B"
revision = "fns"
device = "cuda"
tokenizer = AutoTokenizer.from_pretrained(model_id, revision=revision, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_id,
revision=revision,
trust_remote_code=True,
dtype=torch.bfloat16,
).to(device).eval()
reference = "GGGCTATAAAGGCCATCGATCGATCGATCGATCGATCGATCG"
perturbed = "GGGCGCGCGCGGCCATCGATCGATCGATCGATCGATCGATCG"
with torch.no_grad():
bp_probs, actual_probs = model.score_sequence([reference, perturbed])
scores = [torch.log(p.clamp_min(1e-12)).mean().item() for p in actual_probs]
print(f"reference mean bp logp: {scores[0]:.4f}")
print(f"perturbed mean bp logp: {scores[1]:.4f}")
print(f"reference preferred: {scores[0] > scores[1]}")
All evaluations are zero-shot and use the public Carbon evaluation pipeline. The suite covers seven tasks across four capability families:
| Family | Task | Metric |
|---|---|---|
| Generative | Sequence recovery (eukaryote, bacteria, others splits) | Per-base accuracy on the next 30 bp |
| Variant-effect prediction (VEP) | ClinVar coding | AUROC, AUPRC (right-end / next-token scoring) |
| ClinVar non-coding | AUROC, AUPRC | |
| BRCA2 | AUROC, AUPRC, Spearman Ο (centered 8 kb window, full-LL delta) | |
| TraitGym Mendelian | AUROC, AUPRC, Spearman Ο | |
| Sequence-level perturbation | Nucleotide triplet-expansion (insert 10 consecutive CAG triplets into a CDS; model should prefer the natural reference) | Pairwise discrimination accuracy |
| Synonymous codon replacement (replace every codon with the highest-frequency synonym for the target species; model should prefer the natural reference) | Pairwise discrimination accuracy | |
| Long-context retrieval | Genomic-NIAH (4 task variants Γ 6 context lengths, up to 786 kbp) | gen_exact_match, ll_correct |
Below we highlight the three short-context probes for which we report headline numbers in this card. Full results, including all VEP benchmarks and Genomic-NIAH heatmaps, are in the Carbon technical report.
| Category | Metric (%) | Carbon 3B | GENERator-v2 3B | Evo2 7B |
|---|---|---|---|---|
| Generative | Sequence Recovery eukaryote | 61.54 | 58.56 | 59.86 |
| Variant effect prediction | BRCA2 | 84.63 | 81.93 | 83.52 |
| TraitGym Mendelian | 33.65 | 27.91 | 37.78 | |
| ClinVar coding (24 kb) | 92.89 | 91.55 | 93.33 | |
| ClinVar non-coding (24 kb) | 91.14 | 90.13 | 89.79 | |
| Perturbation | Nucleotide triplet-expansion | 85.20 | 83.06 | 88.43 |
| Synonymous codon | 88.89 | 87.03 | 91.59 |
Carbon-3B is competitive with Evo2-7B while being much faster to run.
Genomic-NIAH is a long context benchmark, inspired from NIAH and RULER benchmarks for English. The model needs to retrieves a random 24 bp VALUE planted in a real-genome haystack at one of five depths, evaluated at six context lengths from 24 kbp to 786 kbp. The benchmark contains 500 examples per (task, context) cell.
Below are the scores on niah:
| Context length | Carbon 3B 32k (native / YaRN 4Γ) | GENERator-v2 3B | Evo2-7B |
|---|---|---|---|
| 16 k tokens (98 kbp) | 0.73 / β | 0.74 | 0.97 |
| 32 k tokens (196 kbp) | 0.55 / 0.90 | β | 0.95 |
| 64 k tokens (393 kbp) | β / 0.79 | β | 0.80 |
Sample sizes: Carbon & GENERator n=500. Evo2-7B n=150 at 16k, n=100 at 32k, n=100 at 64k due to the slow inference speed.
niah at 98 kbp.niah at 393 kbp (64 k tokens) under YaRN, despite being substantially smaller.Carbon models run natively in vLLM and thus generate DNA sequences over 150 times faster than the Evo2 family of models. Below we show the results of a throughput benchmark, where 1080 base-pairs are used for prefill and decode with increasing number of input sequences. All models except Evo2 40B were run on a H100 GPU, with the batch size of the Evo2 models tuned to the largest possible size that fits in VRAM.

Carbon-3B is pre-trained for 1T 6-mer tokens (β 6T DNA base pairs) at sequence length 8 192, with a global batch size of 256 sequences (β 2 M tokens / step). The optimizer is AdamW throughout.
The data mixture during the stable phases of pre-training (Phase 1 and the stable portion of Phase 2) is the one documented on the HuggingFaceBio/carbon-pretraining-corpus dataset card: β 70 % Generator-style eukaryotic genomic DNA, with mRNA, splice-enriched mRNA, and GTDB bacterial genomes alongside metadata-conditioned templates.
The training uses a staged objective and learning-rate schedule:
See the Carbon technical report for the full pre-training recipe.
After pre-training, the model undergoes continued training for 50B additional tokens at sequence length 32,768, with the rotary base shifted from 5 Γ 10^5 to 5 Γ 10^6. The long-context training mixture is:
| Component | Fraction |
|---|---|
| Gener-style annotated genes (metadata-conditioned) | 35.0 % |
| Concatenated annotated genes (long-context-data) | 13.8 % |
| mRNA transcripts | 25.0 % |
| Splice-enriched mRNA | 10.0 % |
| GTDB bacterial genomes | 15.0 % |
| Promoter sequences | 1.2 % |
The optimizer is AdamW (Ξ²β = 0.9, Ξ²β = 0.95, Ξ΅ = 1e-8, weight decay = 0.1, gradient clipping = 1.0), with a WSD learning-rate schedule: 2,000 steps linear warmup from 0 to 3e-5, stable phase, then 4,000-step linear decay to 3e-6. Global batch size: 64 sequences Γ 32,768 tokens.
factor=4. Pushing further to 128 k tokens (β 786 kbp) causes retrieval quality to drop sharply in our long-context benchmarks.Apache 2.0.
Carbon is a joint collaboration between the research teams at Hugging Face, Zhongguancun Academy, and TIGEM/University of Naples βFederico IIβ.
@article{allal2026carbon,
title={Carbon: Decoding the Language of Life},
author={Allal, Loubna Ben and Li, Qiuyi and Fiusco, Maurizio and Tunstall, Lewis and Rasul, Kashif and Beeching, Ed and Aubakirova, Dana and Pati{\~n}o, Carlos and Frere, Thibaud and Lozhkov, Anton and others},
journal={bioRxiv},
pages={2026--05},
year={2026},
publisher={Cold Spring Harbor Laboratory}
}