Text Generation
Transformers
Safetensors
English
qwen3
dna
genomics
biology
variant-effect-prediction
conversational
text-generation-inference
Instructions to use HuggingFaceBio/Carbon-X with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use HuggingFaceBio/Carbon-X with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="HuggingFaceBio/Carbon-X") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("HuggingFaceBio/Carbon-X") model = AutoModelForCausalLM.from_pretrained("HuggingFaceBio/Carbon-X", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use HuggingFaceBio/Carbon-X with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "HuggingFaceBio/Carbon-X" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "HuggingFaceBio/Carbon-X", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/HuggingFaceBio/Carbon-X
- SGLang
How to use HuggingFaceBio/Carbon-X with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "HuggingFaceBio/Carbon-X" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "HuggingFaceBio/Carbon-X", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "HuggingFaceBio/Carbon-X" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "HuggingFaceBio/Carbon-X", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use HuggingFaceBio/Carbon-X with Docker Model Runner:
docker model run hf.co/HuggingFaceBio/Carbon-X
Commit ·
1fac52c
0
Parent(s):
Carbon-X
Browse filesCo-authored-by: loubnabnl <loubnabnl@users.noreply.huggingface.co>
Co-authored-by: danaaubakirova <danaaubakirova@users.noreply.huggingface.co>
- .gitattributes +38 -0
- README.md +320 -0
- chat_template.jinja +91 -0
- config.json +75 -0
- dna_config.json +12 -0
- examples/agent_tool.py +65 -0
- examples/inspect_agent.py +67 -0
- figures/carbon-x-banner.png +3 -0
- figures/partner-logos.png +3 -0
- figures/score-vs-cost.png +0 -0
- generation_config.json +14 -0
- model-00001-of-00002.safetensors +3 -0
- model-00002-of-00002.safetensors +3 -0
- model.safetensors.index.json +451 -0
- skills/carbon-dna/SKILL.md +42 -0
- skills/carbon-dna/scripts/carbon.py +105 -0
- tokenizer.json +3 -0
- tokenizer.py +835 -0
- tokenizer_config.json +39 -0
.gitattributes
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 36 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
figures/carbon-x-banner.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
figures/partner-logos.png filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: transformers
|
| 3 |
+
license: apache-2.0
|
| 4 |
+
base_model: Qwen/Qwen3-14B
|
| 5 |
+
language:
|
| 6 |
+
- en
|
| 7 |
+
tags:
|
| 8 |
+
- dna
|
| 9 |
+
- genomics
|
| 10 |
+
- biology
|
| 11 |
+
- variant-effect-prediction
|
| 12 |
+
- conversational
|
| 13 |
+
pipeline_tag: text-generation
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+

|
| 17 |
+
|
| 18 |
+
<p align="center">
|
| 19 |
+
<a href="https://huggingface.co/spaces/HuggingFaceBio/Carbon-X-Release"><b>Blog</b></a> |
|
| 20 |
+
<a href="https://huggingface.co/HuggingFaceBio"><b>Hugging Face Bio</b></a> |
|
| 21 |
+
<a href="https://github.com/huggingface/carbon/tree/main"><b>Code</b></a>
|
| 22 |
+
</p>
|
| 23 |
+
|
| 24 |
+
# Carbon-X
|
| 25 |
+
|
| 26 |
+
An open 14B language model for DNA understanding and generation, from the **Carbon** family.
|
| 27 |
+
|
| 28 |
+
## Table of Contents
|
| 29 |
+
|
| 30 |
+
1. [Model Summary](#model-summary)
|
| 31 |
+
2. [How to use](#how-to-use)
|
| 32 |
+
3. [Evaluation](#evaluation)
|
| 33 |
+
4. [Training](#training)
|
| 34 |
+
5. [Using Carbon-X as a tool for an agent](#using-carbon-x-as-a-tool-for-an-agent)
|
| 35 |
+
6. [Limitations](#limitations)
|
| 36 |
+
7. [License](#license)
|
| 37 |
+
8. [Acknowledgements](#acknowledgements)
|
| 38 |
+
|
| 39 |
+
## Model Summary
|
| 40 |
+
|
| 41 |
+
<p align="center">
|
| 42 |
+
<img src="figures/score-vs-cost.png" alt="Average DNA benchmark score versus cost per problem relative to Carbon-X" width="800">
|
| 43 |
+
</p>
|
| 44 |
+
|
| 45 |
+
<p align="center"><em>Each comparison model is shown at the reasoning effort with its highest average score. Costs use standard API pricing; for Carbon-X, measured throughput per H200-hour at $5/hour.</em></p>
|
| 46 |
+
|
| 47 |
+
**Carbon-X** is a 14.8B-parameter decoder-only language model trained jointly on DNA sequences and natural-language text. You give it raw DNA inside an ordinary chat conversation and it answers in English: it predicts whether mutations are benign or pathogenic, identifies regions that control gene activity, classifies sequences by organism group, predicts the next bases of a sequence, and explains its reasoning. Responses can contain both DNA and natural language.
|
| 48 |
+
|
| 49 |
+
DNA and text share a single token stream in a single decoder. There is no separate DNA encoder. A hybrid tokenizer keeps a BPE vocabulary for text and encodes DNA as non-overlapping 6-mers, so one DNA token covers 6 base pairs.
|
| 50 |
+
|
| 51 |
+
Across five DNA benchmarks, which cover variant-effect prediction, sequence understanding and next-base prediction, Carbon-X averages **66.7%** with thinking disabled. That is 27 points above Qwen3-14B, the model it starts from, and ahead of every other open model we evaluated. It is close to GPT-6.1 Sol (67.5%) at about 1/1,800 of the inference cost per problem, and it leads every model we tested on coding-variant effects and next-base prediction ([Evaluation](#evaluation)). Carbon-X answers directly, without extended reasoning, so each problem costs few output tokens. We estimate that analysing the 2.4 million variants of uncertain significance in ClinVar would cost about $300 with Carbon-X, compared with $32,000 to $4 million with the frontier models we evaluated. The weights fit on a single 80 GB GPU in bfloat16, so researchers can keep sensitive or unpublished sequences on their own infrastructure.
|
| 52 |
+
|
| 53 |
+
### Key features
|
| 54 |
+
|
| 55 |
+
- **14.8B parameters**: 40 layers, hidden size 5120, GQA (40 query heads, 8 KV heads), SwiGLU, RMSNorm with QK-norm.
|
| 56 |
+
- **Hybrid tokenizer**: the Qwen3 BPE vocabulary for text plus 5,515 DNA tokens. These are 4,096 6-mers, 1,364 tail tokens for sequences whose length is not a multiple of 6, and the `<dna>`, `</dna>` and `<oov>` markers. DNA in a prompt is detected and tagged automatically.
|
| 57 |
+
- **Chat model**: post-trained and evaluated with thinking disabled.
|
| 58 |
+
- **Context window**: 40,960 tokens.
|
| 59 |
+
- **Training**: 302B tokens of mid-training on DNA, biology text, English and instruction data, then supervised fine-tuning and reinforcement learning with verifiable rewards on DNA tasks.
|
| 60 |
+
- **Efficient inference**: serves with vLLM on a single GPU.
|
| 61 |
+
|
| 62 |
+
## How to use
|
| 63 |
+
|
| 64 |
+
Carbon-X uses a standard causal LM architecture. The DNA tokenizer is custom code, so pass `trust_remote_code=True` when you load the tokenizer. Carbon-X requires Transformers v5 or later.
|
| 65 |
+
|
| 66 |
+
```bash
|
| 67 |
+
pip install -U transformers
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
```python
|
| 71 |
+
import torch
|
| 72 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 73 |
+
|
| 74 |
+
model_id = "HuggingFaceBio/Carbon-X"
|
| 75 |
+
|
| 76 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
|
| 77 |
+
model = AutoModelForCausalLM.from_pretrained(model_id, dtype=torch.bfloat16, device_map="cuda")
|
| 78 |
+
|
| 79 |
+
messages = [
|
| 80 |
+
{
|
| 81 |
+
"role": "user",
|
| 82 |
+
"content": (
|
| 83 |
+
"Reference: ATGGTGCATCTGACTCCTGAGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGG\n"
|
| 84 |
+
"Variant: ATGGTGCATCTGACTCCTGTGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGG\n\n"
|
| 85 |
+
"Is this variant likely to be benign or pathogenic? Explain your reasoning."
|
| 86 |
+
),
|
| 87 |
+
}
|
| 88 |
+
]
|
| 89 |
+
inputs = tokenizer.apply_chat_template(
|
| 90 |
+
messages,
|
| 91 |
+
add_generation_prompt=True,
|
| 92 |
+
enable_thinking=False,
|
| 93 |
+
return_tensors="pt",
|
| 94 |
+
return_dict=True,
|
| 95 |
+
).to(model.device)
|
| 96 |
+
|
| 97 |
+
outputs = model.generate(**inputs, max_new_tokens=1024)
|
| 98 |
+
print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
Carbon-X was post-trained and evaluated with `enable_thinking=False`, and we recommend keeping it that way. `generate()` uses the sampling defaults from `generation_config.json` (temperature 0.6, top-p 0.95, top-k 20), which are also the settings we used for every reported result.
|
| 102 |
+
|
| 103 |
+
### Serving with vLLM
|
| 104 |
+
|
| 105 |
+
```bash
|
| 106 |
+
pip install -U vllm
|
| 107 |
+
vllm serve HuggingFaceBio/Carbon-X --trust-remote-code
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
```python
|
| 111 |
+
from openai import OpenAI
|
| 112 |
+
|
| 113 |
+
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
|
| 114 |
+
response = client.chat.completions.create(
|
| 115 |
+
model="HuggingFaceBio/Carbon-X",
|
| 116 |
+
messages=[{"role": "user", "content": "What does this sequence encode?\n\nATGGTGCATCTGACTCCTGAGGAGAAGTCTGCCGTT"}],
|
| 117 |
+
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
|
| 118 |
+
)
|
| 119 |
+
print(response.choices[0].message.content)
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
vLLM also applies the sampling defaults from `generation_config.json` unless you override them in the request.
|
| 123 |
+
|
| 124 |
+
### Tokenizer: working with DNA inputs
|
| 125 |
+
|
| 126 |
+
The tokenizer switches between BPE for text and 6-mers for DNA. You can paste sequences directly into a message:
|
| 127 |
+
|
| 128 |
+
```python
|
| 129 |
+
>>> ids = tokenizer("Describe this sequence: ACGTACGATTCA")["input_ids"]
|
| 130 |
+
>>> tokenizer.convert_ids_to_tokens(ids)
|
| 131 |
+
['Describe', 'Ġthis', 'Ġsequence', ':', 'Ġ', '<dna>', 'ACGTAC', 'GATTCA', '</dna>']
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
- **DNA is tagged automatically.** Runs of at least 6 A/C/G/T bases are wrapped in `<dna>…</dna>` and tokenized as 6-mers. Runs of at least 12 bases with up to 20% IUPAC ambiguity codes (`N`, `R`, `Y`, …) are tagged too. Lowercase bases are uppercased, and sequences displayed in space-separated blocks are joined. Text you have already wrapped in `<dna>…</dna>` is always tokenized as DNA. To turn off detection for a call, pass `auto_dna_tags=False` to the tokenizer or to `apply_chat_template()`.
|
| 135 |
+
- **Only A, C, G and T form 6-mers.** A 6-mer that contains any other character, such as `N`, becomes a single `<oov>` token. To keep full resolution, filter to canonical bases first.
|
| 136 |
+
- **Any length works.** When a sequence's length is not a multiple of 6, its last 1–5 bases are encoded with dedicated tail tokens, and decoding returns the original sequence exactly.
|
| 137 |
+
- **DNA in responses is tagged.** The model writes DNA inside `<dna>…</dna>`. Decoding with `skip_special_tokens=True` removes the tags.
|
| 138 |
+
- **First load needs Hub access.** On first load, the tokenizer downloads the pinned BPE vocabulary it builds on.
|
| 139 |
+
|
| 140 |
+
### Prompting tips
|
| 141 |
+
|
| 142 |
+
- Put the sequence and the question in the same user message.
|
| 143 |
+
- Include any context you already have, such as the gene, strand, coordinates or annotations. Without it, the model may guess at a sequence's identity or function.
|
| 144 |
+
- For variant questions, give the reference and alternate sequences, or a reference sequence with the position and the base change.
|
| 145 |
+
|
| 146 |
+
## Evaluation
|
| 147 |
+
|
| 148 |
+
We evaluate Carbon-X on five benchmarks that test whether a model can use DNA knowledge to predict mutation effects, identify biological features and recover DNA bases. Carbon-X results use thinking disabled, the default sampling settings and one sample per problem. Each comparison model is shown at the reasoning effort with its highest average score. The best score on each benchmark is in bold.
|
| 149 |
+
|
| 150 |
+
| Task | Benchmark | Carbon-X<br><sub>No reasoning</sub> | GPT-6.1 Sol<br><sub>Max</sub> | Opus 5.5<br><sub>Max</sub> | Gemini 4 Argon<br><sub>Low</sub> | GLM-5.3<br><sub>Low</sub> | Kimi K3<br><sub>Low</sub> | DeepSeek V4.1 Flash<br><sub>Low</sub> |
|
| 151 |
+
|---|---|---:|---:|---:|---:|---:|---:|---:|
|
| 152 |
+
| Coding variant effects | BioReason Coding | **74.9** | 71.0 | 70.3 | 6.5 | 52.1 | 53.9 | 51.4 |
|
| 153 |
+
| Complex variant effects | BioReason Non-SNV | 85.1 | 91.7 | **94.8** | 90.3 | 60.1 | 65.3 | 55.9 |
|
| 154 |
+
| DNA understanding | GenomeQA | 63.6 | **67.2** | 62.2 | 63.1 | 44.6 | 52.0 | 49.7 |
|
| 155 |
+
| DNA prediction | NextBase | **39.4** | 32.9 | 1.1 | 31.2 | 9.7 | 30.6 | 27.2 |
|
| 156 |
+
| Single-base variant effects | FrontierVEP | 70.4 | 74.9 | **76.2** | 58.0 | 32.7 | 50.1 | 51.3 |
|
| 157 |
+
| **Average** | | 66.7 | **67.5** | 60.9 | 49.8 | 39.8 | 50.4 | 47.1 |
|
| 158 |
+
|
| 159 |
+
BioReason and GenomeQA come from the literature; we built FrontierVEP and NextBase.
|
| 160 |
+
|
| 161 |
+
- **BioReason Coding** (1,018 variants, balanced accuracy): given original and mutated sequences from protein-coding regions, predict whether the mutation is benign or pathogenic. The gene name is removed from the prompt, so the model must work from the sequence alone.
|
| 162 |
+
- **BioReason Non-SNV** (873 variants, balanced accuracy): predict whether insertions, deletions and other changes that are not single-nucleotide variants are benign or pathogenic.
|
| 163 |
+
- **GenomeQA** (2,600 questions, accuracy averaged over six tasks): multiple-choice questions about sequence features: enhancer vs. promoter, splice sites, organism group, histone marks, transcription-factor binding sites and motifs.
|
| 164 |
+
- **NextBase** (600 sequences, per-base accuracy): predict the next DNA bases of a sequence. The score is the fraction of bases predicted correctly.
|
| 165 |
+
- **FrontierVEP** (1,000 variants, accuracy): given a long reference sequence (up to 8,192 bases) and a single-nucleotide variant, predict whether the variant is benign or pathogenic (500 benign, 500 pathogenic).
|
| 166 |
+
|
| 167 |
+
<details>
|
| 168 |
+
<summary>GenomeQA per-task accuracy</summary>
|
| 169 |
+
|
| 170 |
+
| Task | Accuracy |
|
| 171 |
+
|---|---:|
|
| 172 |
+
| Enhancer vs. promoter | 70.0 |
|
| 173 |
+
| Splice sites | 29.8 |
|
| 174 |
+
| Organism group (eukaryote classification) | 93.2 |
|
| 175 |
+
| Histone marks | 34.6 |
|
| 176 |
+
| Transcription-factor binding sites | 64.0 |
|
| 177 |
+
| Motifs | 90.0 |
|
| 178 |
+
|
| 179 |
+
</details>
|
| 180 |
+
|
| 181 |
+
Carbon-X also remains a capable assistant. On IFEval it reaches 79.3% prompt-level strict accuracy, or 84.0% averaged over the four IFEval metrics.
|
| 182 |
+
|
| 183 |
+
All training data was decontaminated against these benchmarks with three checks: whole-span DNA overlap, text n-gram overlap and sequence homology search.
|
| 184 |
+
|
| 185 |
+
## Training
|
| 186 |
+
|
| 187 |
+
### Model
|
| 188 |
+
|
| 189 |
+
- **Architecture:** decoder-only Transformer with 40 layers, hidden size 5120, FFN size 17408, 40 attention heads with GQA (8 KV heads), head dimension 128, SwiGLU, RMSNorm with QK-norm and untied embeddings.
|
| 190 |
+
- **Positional embedding:** RoPE with base θ = 1 × 10^6. The context window is 40,960 tokens.
|
| 191 |
+
- **Vocabulary:** 157,184 tokens: 151,669 BPE tokens and 5,515 DNA tokens, including padding to a multiple of 128.
|
| 192 |
+
- **Precision:** bfloat16.
|
| 193 |
+
|
| 194 |
+
### Vocabulary extension
|
| 195 |
+
|
| 196 |
+
Carbon-X starts from [Qwen3-14B](https://huggingface.co/Qwen/Qwen3-14B). We add the DNA tokens to its vocabulary with randomly initialised embeddings. We then train for about 10B tokens with the transformer body frozen, so that only the new DNA embeddings learn, before unfreezing the whole model.
|
| 197 |
+
|
| 198 |
+
### Mid-training
|
| 199 |
+
|
| 200 |
+
The full model is then trained for **302B tokens** with a sequence length of 8,192 and a global batch size of 512 sequences (≈ 4.2M tokens per step, 72,000 steps). The learning rate follows a cosine schedule from 5e-5 to 5e-6 after 2,000 warmup steps. In our ablations, learning rate was the dominant knob at this stage. A low learning rate preserves English but leaves DNA at chance, and a high one learns DNA but forgets English. The data mixture is:
|
| 201 |
+
|
| 202 |
+
| Component | Share |
|
| 203 |
+
|---|---:|
|
| 204 |
+
| **Raw DNA** | **55%** |
|
| 205 |
+
| Eukaryotic genomes: other vertebrates 16.2%, mammals 10.3%, invertebrates 7.6%, plants 3.1%, fungi 1.1%, protozoa 0.2% | 38.5% |
|
| 206 |
+
| mRNA (OpenGenome2) | 8.8% |
|
| 207 |
+
| Prokaryotic genomes (OpenGenome2) | 5.5% |
|
| 208 |
+
| mRNA with splice sites and promoters (OpenGenome2) | 2.2% |
|
| 209 |
+
| **English** | **20%** |
|
| 210 |
+
| FineWeb-Edu | 8% |
|
| 211 |
+
| DCLM | 8% |
|
| 212 |
+
| FinePDFs-Edu | 4% |
|
| 213 |
+
| **Biology text** | **15%** |
|
| 214 |
+
| Documents with long DNA sequences (FinePDFs, PubMed, bioRxiv, FineWeb, Biostars) | 7% |
|
| 215 |
+
| Other biology documents from the same sources | 7% |
|
| 216 |
+
| Synthetic descriptions of annotated RefSeq sequences | 1% |
|
| 217 |
+
| **Instruction data** | **10%** |
|
| 218 |
+
| Instruction following and chat | 10% |
|
| 219 |
+
|
| 220 |
+
### Supervised fine-tuning
|
| 221 |
+
|
| 222 |
+
The mid-trained model is fine-tuned for 3 epochs on a mixture of 20 dataset subsets. The learning rate follows a cosine schedule from 1e-5 to 1e-6, with a global batch size of 32, examples of up to 9,984 tokens and the loss computed on assistant tokens only. The mixture covers:
|
| 223 |
+
|
| 224 |
+
- Variant-effect reasoning over several views of the same variant (about 30% of the mixture)
|
| 225 |
+
- Sequence classification questions: enhancers vs. promoters, organism groups, transcription-factor motifs and binding sites
|
| 226 |
+
- Basic DNA operations, plus procedurally generated DNA skills such as counting, searching and editing sequences (about 10%)
|
| 227 |
+
- Sequence recovery and next-base prediction
|
| 228 |
+
- Biology questions from community Q&A forums
|
| 229 |
+
- Instruction-following, format-following and general chat data, which keep the model a good assistant
|
| 230 |
+
|
| 231 |
+
### Reinforcement learning
|
| 232 |
+
|
| 233 |
+
The fine-tuned model is then trained with asynchronous GRPO and verifiable rewards in two rounds, with thinking disabled:
|
| 234 |
+
|
| 235 |
+
1. One epoch over a blend of 98,433 prompts, selected from the fine-tuned model's own pass rates, for 150 steps.
|
| 236 |
+
2. A second round from that checkpoint, on 58,249 prompts re-selected from its rollouts. The released weights come after about 2.2 passes over the blend.
|
| 237 |
+
|
| 238 |
+
Both rounds use 16 samples per prompt, completions of up to 4,096 tokens, a learning rate of 1e-6, asymmetric clipping (ε = 0.2 / 0.28) and a maximum gradient norm of 0.2. Rewards are task-specific, code-checked verifiers. They cover variant-effect prediction (with a penalty for ungrounded rationales), sequence-feature classification, basic DNA operations, DNA skills, locating positions within sequences, biology multiple-choice questions and instruction following.
|
| 239 |
+
|
| 240 |
+
### Software
|
| 241 |
+
|
| 242 |
+
- **Post-training:** [TRL](https://github.com/huggingface/trl) for supervised fine-tuning and asynchronous GRPO, with [vLLM](https://github.com/vllm-project/vllm) for generation.
|
| 243 |
+
|
| 244 |
+
## Using Carbon-X as a tool for an agent
|
| 245 |
+
|
| 246 |
+
A general-purpose assistant can call Carbon-X when a question involves DNA. Serve Carbon-X with vLLM
|
| 247 |
+
([Serving with vLLM](#serving-with-vllm)) and wrap it in a function that takes a question and named DNA sequences:
|
| 248 |
+
|
| 249 |
+
```python
|
| 250 |
+
from openai import OpenAI
|
| 251 |
+
|
| 252 |
+
carbon = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
def ask_carbon(question: str, sequences: dict[str, str], context: str = "") -> str:
|
| 256 |
+
"""Ask Carbon-X a question about one or more named DNA sequences."""
|
| 257 |
+
parts = [context, question] if context else [question]
|
| 258 |
+
parts += [f"{name}: <dna>{seq}</dna>" for name, seq in sequences.items()]
|
| 259 |
+
response = carbon.chat.completions.create(
|
| 260 |
+
model="HuggingFaceBio/Carbon-X",
|
| 261 |
+
messages=[{"role": "user", "content": "\n\n".join(parts)}],
|
| 262 |
+
max_tokens=4096,
|
| 263 |
+
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
|
| 264 |
+
)
|
| 265 |
+
return response.choices[0].message.content
|
| 266 |
+
```
|
| 267 |
+
|
| 268 |
+
The snippet above is a core call. We provide the options below as examples, each one is self-contained, so pick the one that fits your agent:
|
| 269 |
+
|
| 270 |
+
- **Any agent with tool calling:** [`examples/agent_tool.py`](examples/agent_tool.py) describes `ask_carbon` as an
|
| 271 |
+
OpenAI-style tool and runs a minimal agent loop. The agent refers to sequences by name and your code looks up the DNA,
|
| 272 |
+
because language models often miscopy long sequences.
|
| 273 |
+
- **Inspect:** [`examples/inspect_agent.py`](examples/inspect_agent.py) wraps Carbon-X in an `@tool` for an
|
| 274 |
+
[Inspect](https://inspect.aisi.org.uk/) `react()` agent, with each sample's DNA kept in metadata.
|
| 275 |
+
- **Coding agents (Claude Code, Codex CLI):** [`skills/carbon-dna/`](skills/carbon-dna/) is a ready-made skill. Its
|
| 276 |
+
script needs only the Python standard library and has two commands: `ask` (a question about named DNA files) and
|
| 277 |
+
`compare` (rank equal-length variants by sequence likelihood). You need a GPU with about 80 GB of memory and vLLM
|
| 278 |
+
(`pip install -U vllm`); the skill itself needs no extra packages or API keys. Start Carbon-X, install the skill and
|
| 279 |
+
point it at the server:
|
| 280 |
+
|
| 281 |
+
```bash
|
| 282 |
+
vllm serve HuggingFaceBio/Carbon-X --trust-remote-code # one 80 GB GPU
|
| 283 |
+
hf download HuggingFaceBio/Carbon-X --include "skills/*" --local-dir carbon-x
|
| 284 |
+
cp -r carbon-x/skills/carbon-dna ~/.claude/skills/ # Codex CLI: ~/.codex/skills/
|
| 285 |
+
export CARBON_BASE_URL=http://localhost:8000/v1 # or the address of your vLLM server
|
| 286 |
+
```
|
| 287 |
+
|
| 288 |
+
Carbon-X runs with vLLM or Transformers. llama.cpp and other GGUF runtimes are not supported: the DNA tokenizer is
|
| 289 |
+
custom Python code that they cannot load, so DNA would be tokenized incorrectly.
|
| 290 |
+
|
| 291 |
+
### Tips for agent use
|
| 292 |
+
|
| 293 |
+
- **Pass DNA by reference.** Keep sequences in files or a lookup table and let the agent name them.
|
| 294 |
+
- **Ask for Carbon-X explicitly.** Agents often answer DNA questions on their own instead of calling a tool. If you want Carbon-X's view, say so in the system prompt.
|
| 295 |
+
- **Leave exact operations to code.** Counting, coordinates and edits are more reliable in Python than in either model. For variants, compute the position and the reference and alternate bases in code and pass them as `context`.
|
| 296 |
+
- **Cap generation length.** For next-base and continuation questions, set `max_tokens` to what you need and a client timeout, since long DNA generations are slow (see [Limitations](#limitations)).
|
| 297 |
+
|
| 298 |
+
## Limitations
|
| 299 |
+
|
| 300 |
+
Carbon-X is a starting point for research on connecting DNA and language. It has **not been clinically validated**, and it should not be used to make medical decisions.
|
| 301 |
+
|
| 302 |
+
- ⚠️ **Genetic data is highly sensitive.** Depending on how you use this model (local download, inference API or endpoints, third-party inference providers, Spaces demos or others), input and output data may be processed or handled differently by different providers or Space owners. Make sure you understand and agree with how your data is handled before using the model.
|
| 303 |
+
- **Exact sequence operations.** The model can miscount bases, confuse coordinates or make incorrect edits, particularly when it locates exact positions in long sequences. For tasks that need exact operations, pair it with tools that perform them.
|
| 304 |
+
- **Missing context and hallucinations.** The model may invent a sequence's identity or function when the prompt does not give enough information. Include known annotations and context where available, and verify its biological claims.
|
| 305 |
+
- **Uneven task coverage.** Splice-site and histone-mark questions remain weak (29.8% and 34.6% on GenomeQA). Carbon-X is also less reliable on short DNA exercises and textbook biology than on its core variant-effect and sequence-recovery tasks.
|
| 306 |
+
- **Long sequence generation.** Quality degrades when the model is asked to generate long DNA sequences. Carbon-X reads and explains existing sequences. It was not trained to design new ones.
|
| 307 |
+
- **Long inputs.** Mid-training used 8,192-token sequences (up to ≈ 49 kbp of DNA), and post-training examples were shorter than 16k tokens. The model accepts up to 40,960 tokens, but we have not evaluated inputs much longer than those seen in training.
|
| 308 |
+
- **Thinking mode.** The chat template supports `enable_thinking=True`, but Carbon-X was post-trained and evaluated with thinking disabled.
|
| 309 |
+
|
| 310 |
+
## License
|
| 311 |
+
|
| 312 |
+
Apache 2.0.
|
| 313 |
+
|
| 314 |
+
## Acknowledgements
|
| 315 |
+
|
| 316 |
+
Carbon-X is a joint collaboration between the research teams at [Hugging Face](https://huggingface.co), [Zhongguancun Academy](https://www.bza.edu.cn/en/), and the [Telethon Institute of Genetics and Medicine](https://www.tigem.it/)/[University of Naples “Federico II”](https://www.dicmapi.unina.it/).
|
| 317 |
+
|
| 318 |
+
<p align="center">
|
| 319 |
+
<img src="figures/partner-logos.png" alt="Hugging Face, Zhongguancun Academy, Telethon Institute of Genetics and Medicine and University of Naples Federico II" width="800">
|
| 320 |
+
</p>
|
chat_template.jinja
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{#- Training variant of the Qwen3 chat template (see qwen3.jinja for the original).
|
| 2 |
+
Modifications vs the original:
|
| 3 |
+
- Removed the loop.index0 > ns.last_query_index conditional; always include thinking block.
|
| 4 |
+
This makes the template prefix-preserving for the [user, assistant] → [user, assistant, tool] transition.
|
| 5 |
+
- Added {% generation %} / {% endgeneration %} around assistant message output to support
|
| 6 |
+
assistant-only loss masking in SFT training.
|
| 7 |
+
-#}
|
| 8 |
+
{%- if tools %}
|
| 9 |
+
{{- '<|im_start|>system\n' }}
|
| 10 |
+
{%- if messages[0].role == 'system' %}
|
| 11 |
+
{{- messages[0].content + '\n\n' }}
|
| 12 |
+
{%- endif %}
|
| 13 |
+
{{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
|
| 14 |
+
{%- for tool in tools %}
|
| 15 |
+
{{- "\n" }}
|
| 16 |
+
{{- tool | tojson }}
|
| 17 |
+
{%- endfor %}
|
| 18 |
+
{{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
|
| 19 |
+
{%- else %}
|
| 20 |
+
{%- if messages[0].role == 'system' %}
|
| 21 |
+
{{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- endif %}
|
| 24 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 25 |
+
{%- for message in messages[::-1] %}
|
| 26 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 27 |
+
{%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
|
| 28 |
+
{%- set ns.multi_step_tool = false %}
|
| 29 |
+
{%- set ns.last_query_index = index %}
|
| 30 |
+
{%- endif %}
|
| 31 |
+
{%- endfor %}
|
| 32 |
+
{%- for message in messages %}
|
| 33 |
+
{%- if message.content is string %}
|
| 34 |
+
{%- set content = message.content %}
|
| 35 |
+
{%- else %}
|
| 36 |
+
{%- set content = '' %}
|
| 37 |
+
{%- endif %}
|
| 38 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 39 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 40 |
+
{%- elif message.role == "assistant" %}
|
| 41 |
+
{%- set reasoning_content = '' %}
|
| 42 |
+
{%- if message.reasoning_content is string %}
|
| 43 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 44 |
+
{%- else %}
|
| 45 |
+
{%- if '</think>' in content %}
|
| 46 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 47 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 48 |
+
{%- endif %}
|
| 49 |
+
{%- endif %}
|
| 50 |
+
{{- '<|im_start|>' + message.role + '\n' }}
|
| 51 |
+
{%- generation %}
|
| 52 |
+
{{- '<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 53 |
+
{%- if message.tool_calls %}
|
| 54 |
+
{%- for tool_call in message.tool_calls %}
|
| 55 |
+
{%- if (loop.first and content) or (not loop.first) %}
|
| 56 |
+
{{- '\n' }}
|
| 57 |
+
{%- endif %}
|
| 58 |
+
{%- if tool_call.function %}
|
| 59 |
+
{%- set tool_call = tool_call.function %}
|
| 60 |
+
{%- endif %}
|
| 61 |
+
{{- '<tool_call>\n{"name": "' }}
|
| 62 |
+
{{- tool_call.name }}
|
| 63 |
+
{{- '", "arguments": ' }}
|
| 64 |
+
{%- if tool_call.arguments is string %}
|
| 65 |
+
{{- tool_call.arguments }}
|
| 66 |
+
{%- else %}
|
| 67 |
+
{{- tool_call.arguments | tojson }}
|
| 68 |
+
{%- endif %}
|
| 69 |
+
{{- '}\n</tool_call>' }}
|
| 70 |
+
{%- endfor %}
|
| 71 |
+
{%- endif %}
|
| 72 |
+
{{- '<|im_end|>\n' }}
|
| 73 |
+
{%- endgeneration %}
|
| 74 |
+
{%- elif message.role == "tool" %}
|
| 75 |
+
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
|
| 76 |
+
{{- '<|im_start|>user' }}
|
| 77 |
+
{%- endif %}
|
| 78 |
+
{{- '\n<tool_response>\n' }}
|
| 79 |
+
{{- content }}
|
| 80 |
+
{{- '\n</tool_response>' }}
|
| 81 |
+
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
|
| 82 |
+
{{- '<|im_end|>\n' }}
|
| 83 |
+
{%- endif %}
|
| 84 |
+
{%- endif %}
|
| 85 |
+
{%- endfor %}
|
| 86 |
+
{%- if add_generation_prompt %}
|
| 87 |
+
{{- '<|im_start|>assistant\n' }}
|
| 88 |
+
{%- if enable_thinking is defined and enable_thinking is false %}
|
| 89 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 90 |
+
{%- endif %}
|
| 91 |
+
{%- endif %}
|
config.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3ForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": null,
|
| 8 |
+
"dtype": "bfloat16",
|
| 9 |
+
"eos_token_id": 151643,
|
| 10 |
+
"head_dim": 128,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 5120,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 17408,
|
| 15 |
+
"layer_types": [
|
| 16 |
+
"full_attention",
|
| 17 |
+
"full_attention",
|
| 18 |
+
"full_attention",
|
| 19 |
+
"full_attention",
|
| 20 |
+
"full_attention",
|
| 21 |
+
"full_attention",
|
| 22 |
+
"full_attention",
|
| 23 |
+
"full_attention",
|
| 24 |
+
"full_attention",
|
| 25 |
+
"full_attention",
|
| 26 |
+
"full_attention",
|
| 27 |
+
"full_attention",
|
| 28 |
+
"full_attention",
|
| 29 |
+
"full_attention",
|
| 30 |
+
"full_attention",
|
| 31 |
+
"full_attention",
|
| 32 |
+
"full_attention",
|
| 33 |
+
"full_attention",
|
| 34 |
+
"full_attention",
|
| 35 |
+
"full_attention",
|
| 36 |
+
"full_attention",
|
| 37 |
+
"full_attention",
|
| 38 |
+
"full_attention",
|
| 39 |
+
"full_attention",
|
| 40 |
+
"full_attention",
|
| 41 |
+
"full_attention",
|
| 42 |
+
"full_attention",
|
| 43 |
+
"full_attention",
|
| 44 |
+
"full_attention",
|
| 45 |
+
"full_attention",
|
| 46 |
+
"full_attention",
|
| 47 |
+
"full_attention",
|
| 48 |
+
"full_attention",
|
| 49 |
+
"full_attention",
|
| 50 |
+
"full_attention",
|
| 51 |
+
"full_attention",
|
| 52 |
+
"full_attention",
|
| 53 |
+
"full_attention",
|
| 54 |
+
"full_attention",
|
| 55 |
+
"full_attention"
|
| 56 |
+
],
|
| 57 |
+
"max_position_embeddings": 40960,
|
| 58 |
+
"max_window_layers": 40,
|
| 59 |
+
"model_type": "qwen3",
|
| 60 |
+
"num_attention_heads": 40,
|
| 61 |
+
"num_hidden_layers": 40,
|
| 62 |
+
"num_key_value_heads": 8,
|
| 63 |
+
"pad_token_id": 151643,
|
| 64 |
+
"rms_norm_eps": 1e-06,
|
| 65 |
+
"rope_parameters": {
|
| 66 |
+
"rope_theta": 1000000,
|
| 67 |
+
"rope_type": "default"
|
| 68 |
+
},
|
| 69 |
+
"sliding_window": null,
|
| 70 |
+
"tie_word_embeddings": false,
|
| 71 |
+
"transformers_version": "5.13.0.dev0",
|
| 72 |
+
"use_cache": false,
|
| 73 |
+
"use_sliding_window": false,
|
| 74 |
+
"vocab_size": 157184
|
| 75 |
+
}
|
dna_config.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"k": 6,
|
| 3 |
+
"dna_start_id": 151669,
|
| 4 |
+
"dna_vocab_size": 5515,
|
| 5 |
+
"dna_special_tokens": [
|
| 6 |
+
"<dna>",
|
| 7 |
+
"</dna>",
|
| 8 |
+
"<oov>"
|
| 9 |
+
],
|
| 10 |
+
"auto_dna_tags": true,
|
| 11 |
+
"tail": "pad_n"
|
| 12 |
+
}
|
examples/agent_tool.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Use Carbon-X as a tool for any chat model with OpenAI-style tool calling.
|
| 2 |
+
|
| 3 |
+
Serve Carbon-X first: vllm serve HuggingFaceBio/Carbon-X --trust-remote-code
|
| 4 |
+
Then set the agent client and model below and run: python agent_tool.py
|
| 5 |
+
"""
|
| 6 |
+
import json
|
| 7 |
+
|
| 8 |
+
from openai import OpenAI
|
| 9 |
+
|
| 10 |
+
carbon = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def ask_carbon(question: str, sequences: dict[str, str], context: str = "") -> str:
|
| 14 |
+
"""Ask Carbon-X a question about one or more named DNA sequences."""
|
| 15 |
+
parts = [context, question] if context else [question]
|
| 16 |
+
parts += [f"{name}: <dna>{seq}</dna>" for name, seq in sequences.items()]
|
| 17 |
+
response = carbon.chat.completions.create(
|
| 18 |
+
model="HuggingFaceBio/Carbon-X",
|
| 19 |
+
messages=[{"role": "user", "content": "\n\n".join(parts)}],
|
| 20 |
+
max_tokens=4096,
|
| 21 |
+
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
|
| 22 |
+
)
|
| 23 |
+
return response.choices[0].message.content
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
sequences = {"reference.fa": "ATGGTGCATCTGACTCCTGAGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGG",
|
| 27 |
+
"variant.fa": "ATGGTGCATCTGACTCCTGTGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGG"}
|
| 28 |
+
|
| 29 |
+
carbon_tool = {
|
| 30 |
+
"type": "function",
|
| 31 |
+
"function": {
|
| 32 |
+
"name": "ask_carbon",
|
| 33 |
+
"description": (
|
| 34 |
+
"Ask Carbon-X, a DNA language model, a question about DNA sequences: variant effects, "
|
| 35 |
+
"regulatory elements, organism of origin, sequence features. Refer to sequences by name; "
|
| 36 |
+
"do not paste DNA. Its answers are model predictions, not verified facts."
|
| 37 |
+
),
|
| 38 |
+
"parameters": {
|
| 39 |
+
"type": "object",
|
| 40 |
+
"properties": {
|
| 41 |
+
"question": {"type": "string", "description": "One self-contained question."},
|
| 42 |
+
"sequences": {"type": "array", "items": {"type": "string", "enum": list(sequences)}},
|
| 43 |
+
"context": {"type": "string", "description": "Known gene, coordinates or annotations, if any."},
|
| 44 |
+
},
|
| 45 |
+
"required": ["question", "sequences"],
|
| 46 |
+
},
|
| 47 |
+
},
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
agent = OpenAI() # the agent model's provider
|
| 51 |
+
messages = [
|
| 52 |
+
{"role": "system", "content": "For questions about DNA, consult Carbon-X with the ask_carbon tool before answering."},
|
| 53 |
+
{"role": "user", "content": "variant.fa differs from reference.fa by one base. Is the change likely benign or pathogenic?"},
|
| 54 |
+
]
|
| 55 |
+
while True:
|
| 56 |
+
reply = agent.chat.completions.create(model="<agent model>", messages=messages, tools=[carbon_tool])
|
| 57 |
+
message = reply.choices[0].message
|
| 58 |
+
messages.append(message)
|
| 59 |
+
if not message.tool_calls:
|
| 60 |
+
break
|
| 61 |
+
for call in message.tool_calls:
|
| 62 |
+
args = json.loads(call.function.arguments)
|
| 63 |
+
answer = ask_carbon(args["question"], {name: sequences[name] for name in args["sequences"]}, args.get("context", ""))
|
| 64 |
+
messages.append({"role": "tool", "tool_call_id": call.id, "content": answer})
|
| 65 |
+
print(message.content)
|
examples/inspect_agent.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Use Carbon-X as a tool for an Inspect react() agent.
|
| 2 |
+
|
| 3 |
+
Serve Carbon-X first: vllm serve HuggingFaceBio/Carbon-X --trust-remote-code
|
| 4 |
+
Then run: python inspect_agent.py
|
| 5 |
+
"""
|
| 6 |
+
from inspect_ai import Task, eval, task
|
| 7 |
+
from inspect_ai.agent import as_solver, react
|
| 8 |
+
from inspect_ai.dataset import Sample
|
| 9 |
+
from inspect_ai.scorer import includes
|
| 10 |
+
from inspect_ai.solver import solver
|
| 11 |
+
from inspect_ai.tool import ToolError, tool
|
| 12 |
+
from inspect_ai.util import store
|
| 13 |
+
from openai import AsyncOpenAI
|
| 14 |
+
|
| 15 |
+
carbon = AsyncOpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@tool
|
| 19 |
+
def ask_carbon():
|
| 20 |
+
async def execute(question: str, sequences: list[str], context: str = "") -> str:
|
| 21 |
+
"""Ask Carbon-X, a DNA language model, about this task's DNA sequences. Its answers are predictions.
|
| 22 |
+
|
| 23 |
+
Args:
|
| 24 |
+
question: One self-contained question.
|
| 25 |
+
sequences: Names of the sequences to send, as given in the task.
|
| 26 |
+
context: Known facts, such as the gene, positions or alleles.
|
| 27 |
+
"""
|
| 28 |
+
dna = store().get("sequences")
|
| 29 |
+
if missing := [name for name in sequences if name not in dna]:
|
| 30 |
+
raise ToolError(f"Unknown sequence names: {missing}. Available: {list(dna)}")
|
| 31 |
+
parts = [context, question] if context else [question]
|
| 32 |
+
parts += [f"{name}: <dna>{dna[name]}</dna>" for name in sequences]
|
| 33 |
+
response = await carbon.chat.completions.create(
|
| 34 |
+
model="HuggingFaceBio/Carbon-X",
|
| 35 |
+
messages=[{"role": "user", "content": "\n\n".join(parts)}],
|
| 36 |
+
max_tokens=4096,
|
| 37 |
+
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
|
| 38 |
+
)
|
| 39 |
+
return response.choices[0].message.content
|
| 40 |
+
|
| 41 |
+
return execute
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@solver
|
| 45 |
+
def load_sequences():
|
| 46 |
+
async def solve(state, generate):
|
| 47 |
+
store().set("sequences", state.metadata["sequences"]) # the tool reads DNA from here
|
| 48 |
+
return state
|
| 49 |
+
|
| 50 |
+
return solve
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
@task
|
| 54 |
+
def carbon_agent():
|
| 55 |
+
sample = Sample(
|
| 56 |
+
input="Sequence `variant` differs from `reference` at position 20 (A>T). Is the variant benign or pathogenic?",
|
| 57 |
+
target="pathogenic",
|
| 58 |
+
metadata={"sequences": {
|
| 59 |
+
"reference": "ATGGTGCATCTGACTCCTGAGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGG",
|
| 60 |
+
"variant": "ATGGTGCATCTGACTCCTGTGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGG",
|
| 61 |
+
}},
|
| 62 |
+
)
|
| 63 |
+
agent = react(prompt="Consult Carbon-X with the ask_carbon tool before you answer.", tools=[ask_carbon()])
|
| 64 |
+
return Task(dataset=[sample], solver=[load_sequences(), as_solver(agent)], scorer=includes())
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
eval(carbon_agent(), model="openai/gpt-5") # any Inspect model with tool calling as the agent
|
figures/carbon-x-banner.png
ADDED
|
Git LFS Details
|
figures/partner-logos.png
ADDED
|
Git LFS Details
|
figures/score-vs-cost.png
ADDED
|
generation_config.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"do_sample": true,
|
| 3 |
+
"eos_token_id": [
|
| 4 |
+
151643,
|
| 5 |
+
151645,
|
| 6 |
+
151643
|
| 7 |
+
],
|
| 8 |
+
"pad_token_id": 151643,
|
| 9 |
+
"temperature": 0.6,
|
| 10 |
+
"top_k": 20,
|
| 11 |
+
"top_p": 0.95,
|
| 12 |
+
"transformers_version": "5.13.0.dev0",
|
| 13 |
+
"trust_remote_code": true
|
| 14 |
+
}
|
model-00001-of-00002.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c7858d72aaf5d22e45e75196c4f7352b7423127329c7d1ece67ec072c980cfe4
|
| 3 |
+
size 24956844000
|
model-00002-of-00002.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9a9f69350110babbfab047b6f18358bdf7b0e1f4790b584c57063ed1d2e1f8d7
|
| 3 |
+
size 4687301152
|
model.safetensors.index.json
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"metadata": {
|
| 3 |
+
"total_parameters": 14822046720,
|
| 4 |
+
"total_size": 29644093440
|
| 5 |
+
},
|
| 6 |
+
"weight_map": {
|
| 7 |
+
"lm_head.weight": "model-00001-of-00002.safetensors",
|
| 8 |
+
"model.embed_tokens.weight": "model-00001-of-00002.safetensors",
|
| 9 |
+
"model.layers.0.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 10 |
+
"model.layers.0.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 11 |
+
"model.layers.0.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 12 |
+
"model.layers.0.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 13 |
+
"model.layers.0.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 14 |
+
"model.layers.0.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 15 |
+
"model.layers.0.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 16 |
+
"model.layers.0.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 17 |
+
"model.layers.0.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 18 |
+
"model.layers.0.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 19 |
+
"model.layers.0.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 20 |
+
"model.layers.1.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 21 |
+
"model.layers.1.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 22 |
+
"model.layers.1.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 23 |
+
"model.layers.1.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 24 |
+
"model.layers.1.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 25 |
+
"model.layers.1.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 26 |
+
"model.layers.1.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 27 |
+
"model.layers.1.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 28 |
+
"model.layers.1.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 29 |
+
"model.layers.1.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 30 |
+
"model.layers.1.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 31 |
+
"model.layers.10.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 32 |
+
"model.layers.10.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 33 |
+
"model.layers.10.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 34 |
+
"model.layers.10.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 35 |
+
"model.layers.10.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 36 |
+
"model.layers.10.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 37 |
+
"model.layers.10.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 38 |
+
"model.layers.10.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 39 |
+
"model.layers.10.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 40 |
+
"model.layers.10.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 41 |
+
"model.layers.10.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 42 |
+
"model.layers.11.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 43 |
+
"model.layers.11.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 44 |
+
"model.layers.11.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 45 |
+
"model.layers.11.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 46 |
+
"model.layers.11.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 47 |
+
"model.layers.11.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 48 |
+
"model.layers.11.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 49 |
+
"model.layers.11.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 50 |
+
"model.layers.11.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 51 |
+
"model.layers.11.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 52 |
+
"model.layers.11.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 53 |
+
"model.layers.12.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 54 |
+
"model.layers.12.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 55 |
+
"model.layers.12.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 56 |
+
"model.layers.12.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 57 |
+
"model.layers.12.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 58 |
+
"model.layers.12.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 59 |
+
"model.layers.12.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 60 |
+
"model.layers.12.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 61 |
+
"model.layers.12.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 62 |
+
"model.layers.12.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 63 |
+
"model.layers.12.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 64 |
+
"model.layers.13.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 65 |
+
"model.layers.13.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 66 |
+
"model.layers.13.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 67 |
+
"model.layers.13.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 68 |
+
"model.layers.13.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 69 |
+
"model.layers.13.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 70 |
+
"model.layers.13.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 71 |
+
"model.layers.13.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 72 |
+
"model.layers.13.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 73 |
+
"model.layers.13.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 74 |
+
"model.layers.13.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 75 |
+
"model.layers.14.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 76 |
+
"model.layers.14.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 77 |
+
"model.layers.14.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 78 |
+
"model.layers.14.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 79 |
+
"model.layers.14.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 80 |
+
"model.layers.14.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 81 |
+
"model.layers.14.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 82 |
+
"model.layers.14.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 83 |
+
"model.layers.14.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 84 |
+
"model.layers.14.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 85 |
+
"model.layers.14.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 86 |
+
"model.layers.15.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 87 |
+
"model.layers.15.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 88 |
+
"model.layers.15.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 89 |
+
"model.layers.15.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 90 |
+
"model.layers.15.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 91 |
+
"model.layers.15.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 92 |
+
"model.layers.15.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 93 |
+
"model.layers.15.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 94 |
+
"model.layers.15.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 95 |
+
"model.layers.15.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 96 |
+
"model.layers.15.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 97 |
+
"model.layers.16.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 98 |
+
"model.layers.16.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 99 |
+
"model.layers.16.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 100 |
+
"model.layers.16.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 101 |
+
"model.layers.16.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 102 |
+
"model.layers.16.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 103 |
+
"model.layers.16.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 104 |
+
"model.layers.16.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 105 |
+
"model.layers.16.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 106 |
+
"model.layers.16.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 107 |
+
"model.layers.16.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 108 |
+
"model.layers.17.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 109 |
+
"model.layers.17.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 110 |
+
"model.layers.17.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 111 |
+
"model.layers.17.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 112 |
+
"model.layers.17.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 113 |
+
"model.layers.17.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 114 |
+
"model.layers.17.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 115 |
+
"model.layers.17.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 116 |
+
"model.layers.17.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 117 |
+
"model.layers.17.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 118 |
+
"model.layers.17.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 119 |
+
"model.layers.18.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 120 |
+
"model.layers.18.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 121 |
+
"model.layers.18.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 122 |
+
"model.layers.18.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 123 |
+
"model.layers.18.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 124 |
+
"model.layers.18.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 125 |
+
"model.layers.18.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 126 |
+
"model.layers.18.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 127 |
+
"model.layers.18.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 128 |
+
"model.layers.18.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 129 |
+
"model.layers.18.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 130 |
+
"model.layers.19.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 131 |
+
"model.layers.19.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 132 |
+
"model.layers.19.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 133 |
+
"model.layers.19.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 134 |
+
"model.layers.19.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 135 |
+
"model.layers.19.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 136 |
+
"model.layers.19.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 137 |
+
"model.layers.19.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 138 |
+
"model.layers.19.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 139 |
+
"model.layers.19.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 140 |
+
"model.layers.19.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 141 |
+
"model.layers.2.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 142 |
+
"model.layers.2.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 143 |
+
"model.layers.2.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 144 |
+
"model.layers.2.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 145 |
+
"model.layers.2.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 146 |
+
"model.layers.2.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 147 |
+
"model.layers.2.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 148 |
+
"model.layers.2.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 149 |
+
"model.layers.2.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 150 |
+
"model.layers.2.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 151 |
+
"model.layers.2.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 152 |
+
"model.layers.20.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 153 |
+
"model.layers.20.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 154 |
+
"model.layers.20.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 155 |
+
"model.layers.20.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 156 |
+
"model.layers.20.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 157 |
+
"model.layers.20.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 158 |
+
"model.layers.20.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 159 |
+
"model.layers.20.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 160 |
+
"model.layers.20.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 161 |
+
"model.layers.20.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 162 |
+
"model.layers.20.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 163 |
+
"model.layers.21.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 164 |
+
"model.layers.21.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 165 |
+
"model.layers.21.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 166 |
+
"model.layers.21.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 167 |
+
"model.layers.21.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 168 |
+
"model.layers.21.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 169 |
+
"model.layers.21.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 170 |
+
"model.layers.21.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 171 |
+
"model.layers.21.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 172 |
+
"model.layers.21.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 173 |
+
"model.layers.21.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 174 |
+
"model.layers.22.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 175 |
+
"model.layers.22.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 176 |
+
"model.layers.22.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 177 |
+
"model.layers.22.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 178 |
+
"model.layers.22.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 179 |
+
"model.layers.22.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 180 |
+
"model.layers.22.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 181 |
+
"model.layers.22.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 182 |
+
"model.layers.22.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 183 |
+
"model.layers.22.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 184 |
+
"model.layers.22.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 185 |
+
"model.layers.23.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 186 |
+
"model.layers.23.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 187 |
+
"model.layers.23.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 188 |
+
"model.layers.23.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 189 |
+
"model.layers.23.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 190 |
+
"model.layers.23.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 191 |
+
"model.layers.23.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 192 |
+
"model.layers.23.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 193 |
+
"model.layers.23.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 194 |
+
"model.layers.23.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 195 |
+
"model.layers.23.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 196 |
+
"model.layers.24.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 197 |
+
"model.layers.24.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 198 |
+
"model.layers.24.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 199 |
+
"model.layers.24.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 200 |
+
"model.layers.24.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 201 |
+
"model.layers.24.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 202 |
+
"model.layers.24.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 203 |
+
"model.layers.24.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 204 |
+
"model.layers.24.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 205 |
+
"model.layers.24.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 206 |
+
"model.layers.24.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 207 |
+
"model.layers.25.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 208 |
+
"model.layers.25.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 209 |
+
"model.layers.25.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 210 |
+
"model.layers.25.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 211 |
+
"model.layers.25.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 212 |
+
"model.layers.25.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 213 |
+
"model.layers.25.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 214 |
+
"model.layers.25.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 215 |
+
"model.layers.25.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 216 |
+
"model.layers.25.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 217 |
+
"model.layers.25.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 218 |
+
"model.layers.26.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 219 |
+
"model.layers.26.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 220 |
+
"model.layers.26.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 221 |
+
"model.layers.26.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 222 |
+
"model.layers.26.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 223 |
+
"model.layers.26.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 224 |
+
"model.layers.26.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 225 |
+
"model.layers.26.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 226 |
+
"model.layers.26.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 227 |
+
"model.layers.26.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 228 |
+
"model.layers.26.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 229 |
+
"model.layers.27.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 230 |
+
"model.layers.27.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 231 |
+
"model.layers.27.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 232 |
+
"model.layers.27.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 233 |
+
"model.layers.27.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 234 |
+
"model.layers.27.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 235 |
+
"model.layers.27.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 236 |
+
"model.layers.27.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 237 |
+
"model.layers.27.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 238 |
+
"model.layers.27.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 239 |
+
"model.layers.27.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 240 |
+
"model.layers.28.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 241 |
+
"model.layers.28.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 242 |
+
"model.layers.28.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 243 |
+
"model.layers.28.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 244 |
+
"model.layers.28.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 245 |
+
"model.layers.28.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 246 |
+
"model.layers.28.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 247 |
+
"model.layers.28.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 248 |
+
"model.layers.28.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 249 |
+
"model.layers.28.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 250 |
+
"model.layers.28.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 251 |
+
"model.layers.29.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 252 |
+
"model.layers.29.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 253 |
+
"model.layers.29.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 254 |
+
"model.layers.29.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 255 |
+
"model.layers.29.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 256 |
+
"model.layers.29.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 257 |
+
"model.layers.29.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 258 |
+
"model.layers.29.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 259 |
+
"model.layers.29.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 260 |
+
"model.layers.29.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 261 |
+
"model.layers.29.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 262 |
+
"model.layers.3.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 263 |
+
"model.layers.3.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 264 |
+
"model.layers.3.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 265 |
+
"model.layers.3.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 266 |
+
"model.layers.3.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 267 |
+
"model.layers.3.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 268 |
+
"model.layers.3.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 269 |
+
"model.layers.3.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 270 |
+
"model.layers.3.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 271 |
+
"model.layers.3.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 272 |
+
"model.layers.3.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 273 |
+
"model.layers.30.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 274 |
+
"model.layers.30.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 275 |
+
"model.layers.30.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 276 |
+
"model.layers.30.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 277 |
+
"model.layers.30.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 278 |
+
"model.layers.30.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 279 |
+
"model.layers.30.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 280 |
+
"model.layers.30.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 281 |
+
"model.layers.30.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 282 |
+
"model.layers.30.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 283 |
+
"model.layers.30.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 284 |
+
"model.layers.31.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 285 |
+
"model.layers.31.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 286 |
+
"model.layers.31.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 287 |
+
"model.layers.31.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 288 |
+
"model.layers.31.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 289 |
+
"model.layers.31.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 290 |
+
"model.layers.31.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 291 |
+
"model.layers.31.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 292 |
+
"model.layers.31.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 293 |
+
"model.layers.31.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 294 |
+
"model.layers.31.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 295 |
+
"model.layers.32.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 296 |
+
"model.layers.32.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 297 |
+
"model.layers.32.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 298 |
+
"model.layers.32.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 299 |
+
"model.layers.32.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 300 |
+
"model.layers.32.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 301 |
+
"model.layers.32.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 302 |
+
"model.layers.32.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 303 |
+
"model.layers.32.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 304 |
+
"model.layers.32.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
|
| 305 |
+
"model.layers.32.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
|
| 306 |
+
"model.layers.33.input_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 307 |
+
"model.layers.33.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
|
| 308 |
+
"model.layers.33.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
|
| 309 |
+
"model.layers.33.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
|
| 310 |
+
"model.layers.33.post_attention_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 311 |
+
"model.layers.33.self_attn.k_norm.weight": "model-00002-of-00002.safetensors",
|
| 312 |
+
"model.layers.33.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
|
| 313 |
+
"model.layers.33.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
|
| 314 |
+
"model.layers.33.self_attn.q_norm.weight": "model-00002-of-00002.safetensors",
|
| 315 |
+
"model.layers.33.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
|
| 316 |
+
"model.layers.33.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
|
| 317 |
+
"model.layers.34.input_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 318 |
+
"model.layers.34.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
|
| 319 |
+
"model.layers.34.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
|
| 320 |
+
"model.layers.34.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
|
| 321 |
+
"model.layers.34.post_attention_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 322 |
+
"model.layers.34.self_attn.k_norm.weight": "model-00002-of-00002.safetensors",
|
| 323 |
+
"model.layers.34.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
|
| 324 |
+
"model.layers.34.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
|
| 325 |
+
"model.layers.34.self_attn.q_norm.weight": "model-00002-of-00002.safetensors",
|
| 326 |
+
"model.layers.34.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
|
| 327 |
+
"model.layers.34.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
|
| 328 |
+
"model.layers.35.input_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 329 |
+
"model.layers.35.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
|
| 330 |
+
"model.layers.35.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
|
| 331 |
+
"model.layers.35.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
|
| 332 |
+
"model.layers.35.post_attention_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 333 |
+
"model.layers.35.self_attn.k_norm.weight": "model-00002-of-00002.safetensors",
|
| 334 |
+
"model.layers.35.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
|
| 335 |
+
"model.layers.35.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
|
| 336 |
+
"model.layers.35.self_attn.q_norm.weight": "model-00002-of-00002.safetensors",
|
| 337 |
+
"model.layers.35.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
|
| 338 |
+
"model.layers.35.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
|
| 339 |
+
"model.layers.36.input_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 340 |
+
"model.layers.36.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
|
| 341 |
+
"model.layers.36.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
|
| 342 |
+
"model.layers.36.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
|
| 343 |
+
"model.layers.36.post_attention_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 344 |
+
"model.layers.36.self_attn.k_norm.weight": "model-00002-of-00002.safetensors",
|
| 345 |
+
"model.layers.36.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
|
| 346 |
+
"model.layers.36.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
|
| 347 |
+
"model.layers.36.self_attn.q_norm.weight": "model-00002-of-00002.safetensors",
|
| 348 |
+
"model.layers.36.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
|
| 349 |
+
"model.layers.36.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
|
| 350 |
+
"model.layers.37.input_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 351 |
+
"model.layers.37.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
|
| 352 |
+
"model.layers.37.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
|
| 353 |
+
"model.layers.37.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
|
| 354 |
+
"model.layers.37.post_attention_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 355 |
+
"model.layers.37.self_attn.k_norm.weight": "model-00002-of-00002.safetensors",
|
| 356 |
+
"model.layers.37.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
|
| 357 |
+
"model.layers.37.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
|
| 358 |
+
"model.layers.37.self_attn.q_norm.weight": "model-00002-of-00002.safetensors",
|
| 359 |
+
"model.layers.37.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
|
| 360 |
+
"model.layers.37.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
|
| 361 |
+
"model.layers.38.input_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 362 |
+
"model.layers.38.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
|
| 363 |
+
"model.layers.38.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
|
| 364 |
+
"model.layers.38.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
|
| 365 |
+
"model.layers.38.post_attention_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 366 |
+
"model.layers.38.self_attn.k_norm.weight": "model-00002-of-00002.safetensors",
|
| 367 |
+
"model.layers.38.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
|
| 368 |
+
"model.layers.38.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
|
| 369 |
+
"model.layers.38.self_attn.q_norm.weight": "model-00002-of-00002.safetensors",
|
| 370 |
+
"model.layers.38.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
|
| 371 |
+
"model.layers.38.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
|
| 372 |
+
"model.layers.39.input_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 373 |
+
"model.layers.39.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
|
| 374 |
+
"model.layers.39.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
|
| 375 |
+
"model.layers.39.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
|
| 376 |
+
"model.layers.39.post_attention_layernorm.weight": "model-00002-of-00002.safetensors",
|
| 377 |
+
"model.layers.39.self_attn.k_norm.weight": "model-00002-of-00002.safetensors",
|
| 378 |
+
"model.layers.39.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
|
| 379 |
+
"model.layers.39.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
|
| 380 |
+
"model.layers.39.self_attn.q_norm.weight": "model-00002-of-00002.safetensors",
|
| 381 |
+
"model.layers.39.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
|
| 382 |
+
"model.layers.39.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
|
| 383 |
+
"model.layers.4.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 384 |
+
"model.layers.4.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 385 |
+
"model.layers.4.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 386 |
+
"model.layers.4.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 387 |
+
"model.layers.4.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 388 |
+
"model.layers.4.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 389 |
+
"model.layers.4.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 390 |
+
"model.layers.4.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 391 |
+
"model.layers.4.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 392 |
+
"model.layers.4.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 393 |
+
"model.layers.4.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 394 |
+
"model.layers.5.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 395 |
+
"model.layers.5.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 396 |
+
"model.layers.5.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 397 |
+
"model.layers.5.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 398 |
+
"model.layers.5.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 399 |
+
"model.layers.5.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 400 |
+
"model.layers.5.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 401 |
+
"model.layers.5.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 402 |
+
"model.layers.5.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 403 |
+
"model.layers.5.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 404 |
+
"model.layers.5.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 405 |
+
"model.layers.6.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 406 |
+
"model.layers.6.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 407 |
+
"model.layers.6.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 408 |
+
"model.layers.6.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 409 |
+
"model.layers.6.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 410 |
+
"model.layers.6.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 411 |
+
"model.layers.6.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 412 |
+
"model.layers.6.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 413 |
+
"model.layers.6.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 414 |
+
"model.layers.6.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 415 |
+
"model.layers.6.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 416 |
+
"model.layers.7.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 417 |
+
"model.layers.7.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 418 |
+
"model.layers.7.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 419 |
+
"model.layers.7.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 420 |
+
"model.layers.7.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 421 |
+
"model.layers.7.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 422 |
+
"model.layers.7.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 423 |
+
"model.layers.7.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 424 |
+
"model.layers.7.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 425 |
+
"model.layers.7.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 426 |
+
"model.layers.7.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 427 |
+
"model.layers.8.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 428 |
+
"model.layers.8.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 429 |
+
"model.layers.8.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 430 |
+
"model.layers.8.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 431 |
+
"model.layers.8.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 432 |
+
"model.layers.8.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 433 |
+
"model.layers.8.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 434 |
+
"model.layers.8.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 435 |
+
"model.layers.8.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 436 |
+
"model.layers.8.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 437 |
+
"model.layers.8.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 438 |
+
"model.layers.9.input_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 439 |
+
"model.layers.9.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
|
| 440 |
+
"model.layers.9.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
|
| 441 |
+
"model.layers.9.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
|
| 442 |
+
"model.layers.9.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
|
| 443 |
+
"model.layers.9.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
|
| 444 |
+
"model.layers.9.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
|
| 445 |
+
"model.layers.9.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
|
| 446 |
+
"model.layers.9.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
|
| 447 |
+
"model.layers.9.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
|
| 448 |
+
"model.layers.9.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
|
| 449 |
+
"model.norm.weight": "model-00002-of-00002.safetensors"
|
| 450 |
+
}
|
| 451 |
+
}
|
skills/carbon-dna/SKILL.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: carbon-dna
|
| 3 |
+
description: "Consult Carbon-X, a DNA language model, about DNA sequences in files: variant effects, regulatory elements, organism of origin, sequence features, and likelihood comparisons between variants."
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Consult Carbon-X about DNA
|
| 7 |
+
|
| 8 |
+
Carbon-X is a DNA language model. Use it for biological interpretation of DNA alongside your own analysis. Its answers
|
| 9 |
+
are model predictions, not verified facts or clinical classifications.
|
| 10 |
+
|
| 11 |
+
`scripts/carbon.py` (next to this file) talks to a server running Carbon-X. It needs only Python 3.10+. The user
|
| 12 |
+
configures the server with `CARBON_BASE_URL` (default `http://localhost:8000/v1`), `CARBON_MODEL` and, if needed,
|
| 13 |
+
`CARBON_API_KEY`. Resolve the script's absolute path from this skill's directory.
|
| 14 |
+
|
| 15 |
+
Put each sequence in its own file (plain DNA or one FASTA record) and refer to files by name. Do not retype DNA into
|
| 16 |
+
commands: copying long sequences by hand introduces errors.
|
| 17 |
+
|
| 18 |
+
Ask a question about one or more sequences:
|
| 19 |
+
|
| 20 |
+
```sh
|
| 21 |
+
python3 /path/to/carbon-dna/scripts/carbon.py ask \
|
| 22 |
+
--sequence reference=reference.fa --sequence variant=variant.fa \
|
| 23 |
+
--question "variant differs from reference at one base. Is the change likely benign or pathogenic?" \
|
| 24 |
+
--context "Position 20 (1-based): A>T. Gene and coordinates, if known."
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
Rank equal-length variants by how typical Carbon-X finds each sequence (a likelihood, not a pathogenicity score):
|
| 28 |
+
|
| 29 |
+
```sh
|
| 30 |
+
python3 /path/to/carbon-dna/scripts/carbon.py compare --reference reference.fa --alternative variant=variant.fa
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
From Python, add `scripts/` to `sys.path` and call `ask(question, {name: dna}, context)` or
|
| 34 |
+
`compare(reference_dna, {name: dna})`.
|
| 35 |
+
|
| 36 |
+
Guidance:
|
| 37 |
+
|
| 38 |
+
- Work out exact facts in code first (positions, reference and alternate bases, reading frame, lengths) and pass them
|
| 39 |
+
as `--context`. Carbon-X can miscount positions in long sequences.
|
| 40 |
+
- Ask one self-contained question per call, and keep any answer choices from the user's question.
|
| 41 |
+
- Carbon-X reads A/C/G/T only. The script trims `N` padding at the ends and refuses other characters.
|
| 42 |
+
- Check `truncated` in the result; for next-base or continuation questions, ask for only the length you need.
|
skills/carbon-dna/scripts/carbon.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Ask Carbon-X about DNA files, or compare DNA variants by model likelihood.
|
| 2 |
+
|
| 3 |
+
Talks to any OpenAI-compatible server running Carbon-X, such as `vllm serve HuggingFaceBio/Carbon-X --trust-remote-code`.
|
| 4 |
+
Configure it with environment variables:
|
| 5 |
+
CARBON_BASE_URL server URL including /v1 (default http://localhost:8000/v1)
|
| 6 |
+
CARBON_MODEL served model name (default HuggingFaceBio/Carbon-X)
|
| 7 |
+
CARBON_API_KEY bearer token, if the server needs one (for example an HF token for an Inference Endpoint)
|
| 8 |
+
|
| 9 |
+
Usage:
|
| 10 |
+
python carbon.py ask --question "..." --sequence reference=ref.fa --sequence variant=alt.fa [--context "..."]
|
| 11 |
+
python carbon.py compare --reference ref.fa --alternative alt1=alt1.fa [--alternative alt2=alt2.fa]
|
| 12 |
+
Only the Python standard library is needed. Output is JSON.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
import argparse
|
| 16 |
+
import json
|
| 17 |
+
import os
|
| 18 |
+
import re
|
| 19 |
+
import sys
|
| 20 |
+
import urllib.request
|
| 21 |
+
from pathlib import Path
|
| 22 |
+
|
| 23 |
+
BASE_URL = os.environ.get("CARBON_BASE_URL", "http://localhost:8000/v1").rstrip("/")
|
| 24 |
+
MODEL = os.environ.get("CARBON_MODEL", "HuggingFaceBio/Carbon-X")
|
| 25 |
+
API_KEY = os.environ.get("CARBON_API_KEY", "")
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def read_dna(path: str) -> str:
|
| 29 |
+
"""Read plain DNA or one FASTA record; keep only A/C/G/T after trimming N padding at the ends."""
|
| 30 |
+
lines = [line.strip() for line in Path(path).read_text().splitlines() if line.strip()]
|
| 31 |
+
if lines and lines[0].startswith(">"):
|
| 32 |
+
lines = lines[1:]
|
| 33 |
+
if any(line.startswith(">") for line in lines):
|
| 34 |
+
sys.exit(f"{path}: one sequence per file, please (multi-record FASTA found).")
|
| 35 |
+
dna = "".join(lines).upper().strip("N")
|
| 36 |
+
if not re.fullmatch("[ACGT]+", dna):
|
| 37 |
+
sys.exit(f"{path}: Carbon-X reads A/C/G/T; remove other characters first.")
|
| 38 |
+
return dna
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def post(route: str, payload: dict) -> dict:
|
| 42 |
+
request = urllib.request.Request(f"{BASE_URL}/{route}", json.dumps({"model": MODEL, **payload}).encode(),
|
| 43 |
+
{"Content-Type": "application/json", **({"Authorization": f"Bearer {API_KEY}"} if API_KEY else {})})
|
| 44 |
+
with urllib.request.urlopen(request, timeout=600) as response:
|
| 45 |
+
return json.load(response)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def ask(question: str, sequences: dict[str, str], context: str = "", max_tokens: int = 4096) -> dict:
|
| 49 |
+
"""Ask a question about named DNA sequences. Returns Carbon-X's answer, a model prediction."""
|
| 50 |
+
parts = [context, question] if context else [question]
|
| 51 |
+
parts += [f"{name}: <dna>{dna}</dna>" for name, dna in sequences.items()]
|
| 52 |
+
response = post("chat/completions", {"messages": [{"role": "user", "content": "\n\n".join(parts)}],
|
| 53 |
+
"max_tokens": max_tokens, "chat_template_kwargs": {"enable_thinking": False}})
|
| 54 |
+
choice = response["choices"][0]
|
| 55 |
+
return {"answer": choice["message"]["content"], "truncated": choice["finish_reason"] == "length"}
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def log_likelihood(dna: str) -> float:
|
| 59 |
+
"""Total log-likelihood (nats) of the DNA tokens of `<dna>{dna}</dna>` under Carbon-X."""
|
| 60 |
+
response = post("completions", {"prompt": f"<dna>{dna}</dna>", "max_tokens": 1, "temperature": 1.0,
|
| 61 |
+
"prompt_logprobs": 0, "add_special_tokens": False, "return_token_ids": True})
|
| 62 |
+
choice = response["choices"][0]
|
| 63 |
+
ids, logprobs = choice["prompt_token_ids"], choice["prompt_logprobs"]
|
| 64 |
+
n = (len(dna) + 5) // 6 # one token per 6 bases; a shorter tail gets its own token
|
| 65 |
+
if len(ids) != n + 2: # <dna>, n DNA tokens, </dna>
|
| 66 |
+
sys.exit("The server did not tokenize the DNA as expected; is it serving Carbon-X with --trust-remote-code?")
|
| 67 |
+
return sum(logprobs[i][str(ids[i])]["logprob"] for i in range(1, n + 1))
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def compare(reference: str, alternatives: dict[str, str]) -> dict:
|
| 71 |
+
"""Rank equal-length alternatives by log-likelihood relative to the reference (higher is more typical DNA).
|
| 72 |
+
|
| 73 |
+
This is sequence likelihood, not a calibrated pathogenicity score.
|
| 74 |
+
"""
|
| 75 |
+
if any(len(dna) != len(reference) for dna in alternatives.values()):
|
| 76 |
+
sys.exit("compare needs equal-length sequences; for insertions or deletions, use ask.")
|
| 77 |
+
base = log_likelihood(reference)
|
| 78 |
+
rows = [{"name": name, "delta_log_likelihood": log_likelihood(dna) - base,
|
| 79 |
+
"substitutions": sum(a != b for a, b in zip(reference, dna))} for name, dna in alternatives.items()]
|
| 80 |
+
return {"reference_log_likelihood": base, "alternatives": sorted(rows, key=lambda r: -r["delta_log_likelihood"])}
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def named(items: list[str]) -> dict[str, str]:
|
| 84 |
+
pairs = [item.split("=", 1) for item in items]
|
| 85 |
+
if any(len(pair) != 2 for pair in pairs):
|
| 86 |
+
sys.exit("Use NAME=FILE for each sequence.")
|
| 87 |
+
return {name: read_dna(path) for name, path in pairs}
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
if __name__ == "__main__":
|
| 91 |
+
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 92 |
+
sub = parser.add_subparsers(dest="command", required=True)
|
| 93 |
+
a = sub.add_parser("ask")
|
| 94 |
+
a.add_argument("--question", required=True)
|
| 95 |
+
a.add_argument("--sequence", action="append", default=[], metavar="NAME=FILE")
|
| 96 |
+
a.add_argument("--context", default="")
|
| 97 |
+
c = sub.add_parser("compare")
|
| 98 |
+
c.add_argument("--reference", required=True, metavar="FILE")
|
| 99 |
+
c.add_argument("--alternative", action="append", required=True, metavar="NAME=FILE")
|
| 100 |
+
args = parser.parse_args()
|
| 101 |
+
if args.command == "ask":
|
| 102 |
+
result = ask(args.question, named(args.sequence), args.context)
|
| 103 |
+
else:
|
| 104 |
+
result = compare(read_dna(args.reference), named(args.alternative))
|
| 105 |
+
print(json.dumps(result, indent=2))
|
tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:be75606093db2094d7cd20f3c2f385c212750648bd6ea4fb2bf507a6a4c55506
|
| 3 |
+
size 11422650
|
tokenizer.py
ADDED
|
@@ -0,0 +1,835 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
HybridDNATokenizer: Combines Qwen3 BPE tokenization with DNA 6-mer tokenization.
|
| 3 |
+
|
| 4 |
+
DNA spans are detected automatically or supplied in <dna>...</dna> tags and
|
| 5 |
+
tokenized as 6-mers. All remaining text uses Qwen3's BPE tokenization.
|
| 6 |
+
|
| 7 |
+
Supports token_mask for Fine-grained Nucleotide Supervision (FNS):
|
| 8 |
+
-2: padding token
|
| 9 |
+
-1: text token (BPE)
|
| 10 |
+
0: DNA special token (<dna>, </dna>, <oov>)
|
| 11 |
+
1-5: partial 6-mer token — valid_length real bases at positions [0, valid_length),
|
| 12 |
+
right-padded with 'A' at positions [valid_length, k) so loss can supervise
|
| 13 |
+
positions 0..valid_len-1 via pos_mask = (valid_len > pos)
|
| 14 |
+
6: full 6-mer
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
import os
|
| 18 |
+
import json
|
| 19 |
+
import itertools
|
| 20 |
+
import re
|
| 21 |
+
from typing import List, Optional, Tuple, Dict, Union, Any
|
| 22 |
+
|
| 23 |
+
from transformers import PreTrainedTokenizer, AutoTokenizer, BatchEncoding
|
| 24 |
+
|
| 25 |
+
# The BPE vocabulary comes from this tokenizer, and its size sets where the DNA ids start. Pin it so that a change on the
|
| 26 |
+
# Hub cannot silently shift the ids that checkpoints were trained with.
|
| 27 |
+
BASE_TOKENIZER = "Qwen/Qwen3-4B-Base"
|
| 28 |
+
BASE_TOKENIZER_REVISION = "906bfd4b4dc7f14ee4320094d8b41684abff8539"
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class HybridDNATokenizer(PreTrainedTokenizer):
|
| 32 |
+
"""
|
| 33 |
+
Hybrid tokenizer combining Qwen3 BPE with DNA 6-mer tokenization.
|
| 34 |
+
|
| 35 |
+
Explicit <dna>...</dna> regions are always tokenized as 6-mers. By default,
|
| 36 |
+
untagged DNA spans embedded in text are detected and tagged automatically;
|
| 37 |
+
pass auto_dna_tags=False to encode() or __call__() to disable detection for
|
| 38 |
+
one call.
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
model_input_names = ["input_ids", "attention_mask"]
|
| 42 |
+
|
| 43 |
+
# Single-nucleotide alphabet for tail="char": standard bases (A C T G U) +
|
| 44 |
+
# IUPAC ambiguity codes (R Y S W K M B D H V N). These 16 chars match
|
| 45 |
+
# microsoft/NatureLM-8x7B nlm_tokenizer.py's single-char DNA set and the
|
| 46 |
+
# NatureLM paper's stated DNA vocabulary size of 16 (arXiv:2502.07527, S2.1).
|
| 47 |
+
SINGLE_BASE_ALPHABET = "ACTGURYSWKMBDHVN"
|
| 48 |
+
IUPAC_DNA_ALPHABET = "ACGTRYSWKMBDHVN"
|
| 49 |
+
STANDARD_DNA_ALPHABET = frozenset("ACGT")
|
| 50 |
+
IUPAC_RUN_RE = re.compile(f"[{IUPAC_DNA_ALPHABET}]+", re.IGNORECASE | re.ASCII)
|
| 51 |
+
SPACED_DNA_RE = re.compile(
|
| 52 |
+
r"(?<![A-Za-z])(?:[ACGT]{1,5}[ \t]+){5,}[ACGT]{1,5}(?![A-Za-z])"
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
def __init__(
|
| 56 |
+
self,
|
| 57 |
+
base_tokenizer_path: Optional[str] = None,
|
| 58 |
+
k: int = 6,
|
| 59 |
+
auto_dna_tags: bool = True,
|
| 60 |
+
tail: str = "pad_a",
|
| 61 |
+
**kwargs
|
| 62 |
+
):
|
| 63 |
+
if tail not in ("pad_a", "pad_n", "char"):
|
| 64 |
+
raise ValueError(f"tail must be 'pad_a', 'pad_n' or 'char', got {tail!r}")
|
| 65 |
+
self.tail = tail
|
| 66 |
+
self.pad_char = {"pad_a": "A", "pad_n": "N"}.get(tail)
|
| 67 |
+
self.k = k
|
| 68 |
+
|
| 69 |
+
# Load base tokenizer (Qwen3-4B-Base) at the pinned revision
|
| 70 |
+
self._base_tokenizer = AutoTokenizer.from_pretrained(BASE_TOKENIZER, revision=BASE_TOKENIZER_REVISION)
|
| 71 |
+
|
| 72 |
+
# Get base vocabulary
|
| 73 |
+
self._base_vocab = self._base_tokenizer.get_vocab()
|
| 74 |
+
self._base_vocab_size = len(self._base_vocab)
|
| 75 |
+
|
| 76 |
+
# Initialize DNA vocabulary
|
| 77 |
+
self._init_dna_vocab()
|
| 78 |
+
|
| 79 |
+
# Build combined vocabulary
|
| 80 |
+
self._build_combined_vocab()
|
| 81 |
+
|
| 82 |
+
# Set special tokens
|
| 83 |
+
self._eos_token = kwargs.pop('eos_token', None) or "<|endoftext|>"
|
| 84 |
+
self._pad_token = kwargs.pop('pad_token', None) or self._base_tokenizer.pad_token or "<|endoftext|>"
|
| 85 |
+
|
| 86 |
+
# Initialize parent class
|
| 87 |
+
super().__init__(
|
| 88 |
+
eos_token=self._eos_token,
|
| 89 |
+
pad_token=self._pad_token,
|
| 90 |
+
**kwargs
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
# Preserve the base tokenizer's AddedToken metadata. These tokens are
|
| 94 |
+
# already present in the combined vocabulary at their original IDs, so
|
| 95 |
+
# this only registers their metadata and does not grow the vocabulary.
|
| 96 |
+
self.add_tokens(list(self._base_tokenizer.added_tokens_decoder.values()))
|
| 97 |
+
|
| 98 |
+
self.special_tokens = self.dna_special_tokens + [self._eos_token, self._pad_token]
|
| 99 |
+
self.auto_dna_tags = auto_dna_tags
|
| 100 |
+
|
| 101 |
+
def _init_dna_vocab(self):
|
| 102 |
+
"""Initialize DNA vocabulary (special tokens + k-mers + padding for 128 alignment)."""
|
| 103 |
+
bases = ['A', 'T', 'C', 'G']
|
| 104 |
+
|
| 105 |
+
# DNA special tokens
|
| 106 |
+
self.dna_special_tokens = ["<dna>", "</dna>", "<oov>"]
|
| 107 |
+
|
| 108 |
+
# Generate all k-mer combinations (4^k = 4096 for k=6)
|
| 109 |
+
self.kmers = [''.join(kmer) for kmer in itertools.product(bases, repeat=self.k)]
|
| 110 |
+
|
| 111 |
+
# tail="pad_n": dedicated tokens for the N-padded tail ("CT" -> "CTNNNN"),
|
| 112 |
+
# one per real prefix of length 1..k-1, i.e. 4^1+...+4^(k-1) = 1364.
|
| 113 |
+
self.npad_tokens = []
|
| 114 |
+
if self.tail == "pad_n":
|
| 115 |
+
for prefix_len in range(1, self.k):
|
| 116 |
+
for prefix in itertools.product(bases, repeat=prefix_len):
|
| 117 |
+
self.npad_tokens.append(''.join(prefix) + 'N' * (self.k - prefix_len))
|
| 118 |
+
|
| 119 |
+
# tail="char": single-base tokens for the trailing < k bases (NatureLM
|
| 120 |
+
# IUPAC alphabet), so N / ambiguity codes fall back to a token, not <oov>.
|
| 121 |
+
self.base_tokens = []
|
| 122 |
+
self.dna_base_token = {} # base -> token, e.g. "A" -> "<b_A>"
|
| 123 |
+
self.dna_base_token_to_base = {} # token -> base
|
| 124 |
+
if self.tail == "char":
|
| 125 |
+
for b in self.SINGLE_BASE_ALPHABET:
|
| 126 |
+
tok = f"<b_{b}>"
|
| 127 |
+
self.base_tokens.append(tok)
|
| 128 |
+
self.dna_base_token[b] = tok
|
| 129 |
+
self.dna_base_token_to_base[tok] = b
|
| 130 |
+
|
| 131 |
+
# DNA tokens start after base vocabulary
|
| 132 |
+
self.dna_start_id = self._base_vocab_size
|
| 133 |
+
|
| 134 |
+
# All DNA tokens get new IDs (no reuse of base vocab IDs, even for
|
| 135 |
+
# overlapping tokens like CCCCCC — they have different semantics in
|
| 136 |
+
# DNA context vs BPE context, per Qiuyi's recommendation). Tail-variant
|
| 137 |
+
# tokens are listed AFTER the 4^k k-mers so the k-mer ids are unchanged.
|
| 138 |
+
base_dna_tokens = self.dna_special_tokens + self.kmers + self.npad_tokens + self.base_tokens
|
| 139 |
+
|
| 140 |
+
# Calculate padding for 128 alignment
|
| 141 |
+
total_vocab_unpadded = self._base_vocab_size + len(base_dna_tokens)
|
| 142 |
+
target_vocab_size = ((total_vocab_unpadded + 127) // 128) * 128
|
| 143 |
+
num_padding_tokens = target_vocab_size - total_vocab_unpadded
|
| 144 |
+
|
| 145 |
+
# Add unused padding tokens
|
| 146 |
+
self.padding_tokens = [f"<unused_{i}>" for i in range(num_padding_tokens)]
|
| 147 |
+
|
| 148 |
+
# Create DNA token mappings — all get sequential new IDs
|
| 149 |
+
self.dna_token_to_id = {}
|
| 150 |
+
self.dna_id_to_token = {}
|
| 151 |
+
|
| 152 |
+
current_id = self.dna_start_id
|
| 153 |
+
for token in base_dna_tokens:
|
| 154 |
+
self.dna_token_to_id[token] = current_id
|
| 155 |
+
self.dna_id_to_token[current_id] = token
|
| 156 |
+
current_id += 1
|
| 157 |
+
|
| 158 |
+
# Add padding tokens
|
| 159 |
+
for token in self.padding_tokens:
|
| 160 |
+
self.dna_token_to_id[token] = current_id
|
| 161 |
+
self.dna_id_to_token[current_id] = token
|
| 162 |
+
current_id += 1
|
| 163 |
+
|
| 164 |
+
self.dna_vocab_size = len(base_dna_tokens) + len(self.padding_tokens)
|
| 165 |
+
|
| 166 |
+
# Set DNA special token IDs
|
| 167 |
+
self.dna_begin_token_id = self.dna_token_to_id["<dna>"]
|
| 168 |
+
self.dna_end_token_id = self.dna_token_to_id["</dna>"]
|
| 169 |
+
self.oov_token_id = self.dna_token_to_id["<oov>"]
|
| 170 |
+
|
| 171 |
+
def _build_combined_vocab(self):
|
| 172 |
+
"""Build combined vocabulary (base + DNA)."""
|
| 173 |
+
self._vocab = self._base_vocab.copy()
|
| 174 |
+
|
| 175 |
+
for token, token_id in self.dna_token_to_id.items():
|
| 176 |
+
if token not in self._vocab:
|
| 177 |
+
self._vocab[token] = token_id
|
| 178 |
+
|
| 179 |
+
self._id_to_token = {v: k for k, v in self._vocab.items()}
|
| 180 |
+
for token_id, token in self.dna_id_to_token.items():
|
| 181 |
+
if token_id not in self._id_to_token:
|
| 182 |
+
self._id_to_token[token_id] = token
|
| 183 |
+
|
| 184 |
+
@property
|
| 185 |
+
def vocab_size(self) -> int:
|
| 186 |
+
return max(self._vocab.values()) + 1
|
| 187 |
+
|
| 188 |
+
def get_vocab(self) -> Dict[str, int]:
|
| 189 |
+
return self._vocab.copy()
|
| 190 |
+
|
| 191 |
+
def __len__(self):
|
| 192 |
+
# Override default (len(get_vocab())) because get_vocab() deduplicates
|
| 193 |
+
# CCCCCC which exists as both BPE (ID 91443) and DNA 6-mer (ID 154402).
|
| 194 |
+
return self.vocab_size
|
| 195 |
+
|
| 196 |
+
@classmethod
|
| 197 |
+
def _auto_tag_text_segment(cls, text: str) -> str:
|
| 198 |
+
"""Tag conservative DNA candidates in a text-only segment.
|
| 199 |
+
|
| 200 |
+
Each maximal IUPAC nucleotide run is considered independently. Pure
|
| 201 |
+
A/C/G/T runs need at least six bases. Runs containing IUPAC ambiguity
|
| 202 |
+
codes need at least twelve bases and may contain at most 20% ambiguity.
|
| 203 |
+
Rejected ambiguous runs stay wholly on the BPE path rather than having
|
| 204 |
+
any A/C/G/T substring tagged.
|
| 205 |
+
"""
|
| 206 |
+
parts = []
|
| 207 |
+
previous_end = 0
|
| 208 |
+
|
| 209 |
+
for match in cls.IUPAC_RUN_RE.finditer(text):
|
| 210 |
+
candidate = match.group(0)
|
| 211 |
+
normalized = candidate.upper()
|
| 212 |
+
ambiguous_count = sum(base not in cls.STANDARD_DNA_ALPHABET for base in normalized)
|
| 213 |
+
is_standard_dna = ambiguous_count == 0 and len(normalized) >= 6
|
| 214 |
+
is_conservative_iupac = (
|
| 215 |
+
ambiguous_count > 0
|
| 216 |
+
and len(normalized) >= 12
|
| 217 |
+
and ambiguous_count / len(normalized) <= 0.2
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
if not (is_standard_dna or is_conservative_iupac):
|
| 221 |
+
continue
|
| 222 |
+
|
| 223 |
+
parts.append(text[previous_end:match.start()])
|
| 224 |
+
parts.append(f"<dna>{normalized}</dna>")
|
| 225 |
+
previous_end = match.end()
|
| 226 |
+
|
| 227 |
+
if not parts:
|
| 228 |
+
return text
|
| 229 |
+
|
| 230 |
+
parts.append(text[previous_end:])
|
| 231 |
+
return "".join(parts)
|
| 232 |
+
|
| 233 |
+
@classmethod
|
| 234 |
+
def _normalize_spaced_dna(cls, text: str) -> str:
|
| 235 |
+
"""Join uppercase DNA displays with at least six chunks and twelve bases.
|
| 236 |
+
|
| 237 |
+
Chunks contain one to five A/C/G/T bases, separated only by spaces or
|
| 238 |
+
tabs. Alphabetic boundaries prevent joining fragments of words.
|
| 239 |
+
"""
|
| 240 |
+
def normalize(match):
|
| 241 |
+
sequence = "".join(match.group(0).split())
|
| 242 |
+
return sequence if len(sequence) >= 12 else match.group(0)
|
| 243 |
+
|
| 244 |
+
return cls.SPACED_DNA_RE.sub(normalize, text)
|
| 245 |
+
|
| 246 |
+
def _add_auto_dna_tags(self, text: str) -> str:
|
| 247 |
+
"""Tag DNA outside existing <dna>...</dna> regions."""
|
| 248 |
+
return "".join(
|
| 249 |
+
segment if is_dna else self._auto_tag_text_segment(self._normalize_spaced_dna(segment))
|
| 250 |
+
for segment, is_dna in self._split_by_dna_tags(text)
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
def _split_by_dna_tags(self, text: str) -> List[Tuple[str, bool]]:
|
| 254 |
+
segments = []
|
| 255 |
+
i = 0
|
| 256 |
+
n = len(text)
|
| 257 |
+
|
| 258 |
+
while i < n:
|
| 259 |
+
start_pos = text.find('<dna>', i)
|
| 260 |
+
end_pos = text.find('</dna>', i)
|
| 261 |
+
|
| 262 |
+
if start_pos == -1 and end_pos == -1:
|
| 263 |
+
remaining = text[i:]
|
| 264 |
+
if remaining:
|
| 265 |
+
segments.append((remaining, False))
|
| 266 |
+
break
|
| 267 |
+
|
| 268 |
+
if start_pos == -1 and end_pos != -1:
|
| 269 |
+
dna_region = text[i:end_pos + 6]
|
| 270 |
+
if dna_region:
|
| 271 |
+
segments.append((dna_region, True))
|
| 272 |
+
i = end_pos + 6
|
| 273 |
+
continue
|
| 274 |
+
|
| 275 |
+
if start_pos != -1 and end_pos == -1:
|
| 276 |
+
if i < start_pos:
|
| 277 |
+
normal_text = text[i:start_pos]
|
| 278 |
+
if normal_text:
|
| 279 |
+
segments.append((normal_text, False))
|
| 280 |
+
dna_region = text[start_pos:]
|
| 281 |
+
if dna_region:
|
| 282 |
+
segments.append((dna_region, True))
|
| 283 |
+
break
|
| 284 |
+
|
| 285 |
+
if start_pos < end_pos:
|
| 286 |
+
if i < start_pos:
|
| 287 |
+
normal_text = text[i:start_pos]
|
| 288 |
+
if normal_text:
|
| 289 |
+
segments.append((normal_text, False))
|
| 290 |
+
dna_region = text[start_pos:end_pos + 6]
|
| 291 |
+
if dna_region:
|
| 292 |
+
segments.append((dna_region, True))
|
| 293 |
+
i = end_pos + 6
|
| 294 |
+
else:
|
| 295 |
+
dna_region = text[i:end_pos + 6]
|
| 296 |
+
if dna_region:
|
| 297 |
+
segments.append((dna_region, True))
|
| 298 |
+
i = end_pos + 6
|
| 299 |
+
|
| 300 |
+
return segments
|
| 301 |
+
|
| 302 |
+
def _parse_dna_region(self, dna_region: str) -> Tuple[str, bool, bool]:
|
| 303 |
+
if dna_region == '<dna>':
|
| 304 |
+
return '', True, False
|
| 305 |
+
elif dna_region == '</dna>':
|
| 306 |
+
return '', False, True
|
| 307 |
+
|
| 308 |
+
has_start = dna_region.startswith('<dna>')
|
| 309 |
+
has_end = dna_region.endswith('</dna>')
|
| 310 |
+
|
| 311 |
+
content = dna_region
|
| 312 |
+
if has_start:
|
| 313 |
+
content = content[5:]
|
| 314 |
+
if has_end and content.endswith('</dna>'):
|
| 315 |
+
content = content[:-6]
|
| 316 |
+
|
| 317 |
+
return content.strip(), has_start, has_end
|
| 318 |
+
|
| 319 |
+
def _process_dna_sequence(self, dna_seq: str) -> Dict:
|
| 320 |
+
k = self.k
|
| 321 |
+
dna_seq = dna_seq.upper()
|
| 322 |
+
|
| 323 |
+
kmer_tokens = []
|
| 324 |
+
valid_bases = set('ATCG')
|
| 325 |
+
|
| 326 |
+
def is_valid_kmer(kmer):
|
| 327 |
+
return len(kmer) == k and all(base in valid_bases for base in kmer)
|
| 328 |
+
|
| 329 |
+
if self.tail == "char":
|
| 330 |
+
# Greedy NatureLM tokenization (NatureLM, arXiv:2502.07527), ported
|
| 331 |
+
# from microsoft/NatureLM-8x7B nlm_tokenizer.py::_tokenize_entity:
|
| 332 |
+
# take a full k-mer when the next k chars form one, else emit a single
|
| 333 |
+
# base token and advance by 1 (also re-aligns after any non-base char).
|
| 334 |
+
i = 0
|
| 335 |
+
n = len(dna_seq)
|
| 336 |
+
while i < n:
|
| 337 |
+
window = dna_seq[i:i + k]
|
| 338 |
+
if is_valid_kmer(window):
|
| 339 |
+
kmer_tokens.append(window)
|
| 340 |
+
i += k
|
| 341 |
+
else:
|
| 342 |
+
base = dna_seq[i]
|
| 343 |
+
kmer_tokens.append(self.dna_base_token.get(base, "<oov>"))
|
| 344 |
+
i += 1
|
| 345 |
+
return {"kmer_tokens": kmer_tokens, "padding_length": 0, "valid_length": k}
|
| 346 |
+
|
| 347 |
+
for i in range(0, len(dna_seq) - k + 1, k):
|
| 348 |
+
kmer = dna_seq[i:i+k]
|
| 349 |
+
if is_valid_kmer(kmer):
|
| 350 |
+
kmer_tokens.append(kmer)
|
| 351 |
+
else:
|
| 352 |
+
kmer_tokens.append("<oov>")
|
| 353 |
+
|
| 354 |
+
processed_length = len(kmer_tokens) * k
|
| 355 |
+
remaining = dna_seq[processed_length:]
|
| 356 |
+
padding_length = 0
|
| 357 |
+
valid_length = k
|
| 358 |
+
|
| 359 |
+
if remaining:
|
| 360 |
+
padding_needed = k - len(remaining)
|
| 361 |
+
# Right-pad up to a full k-mer: real bases occupy positions
|
| 362 |
+
# [0, valid_length). The hybrid BP loss supervises positions
|
| 363 |
+
# 0..valid_len-1 via pos_mask = (valid_len > pos), so padding must be
|
| 364 |
+
# at the END. pad_char="A" reuses a real k-mer id ("CT"->"CTAAAA");
|
| 365 |
+
# "N" maps to a dedicated tail token ("CT"->"CTNNNN").
|
| 366 |
+
padded = remaining + self.pad_char * padding_needed
|
| 367 |
+
|
| 368 |
+
if all(base in valid_bases for base in remaining):
|
| 369 |
+
kmer_tokens.append(padded)
|
| 370 |
+
else:
|
| 371 |
+
kmer_tokens.append("<oov>")
|
| 372 |
+
|
| 373 |
+
padding_length = padding_needed
|
| 374 |
+
valid_length = len(remaining)
|
| 375 |
+
|
| 376 |
+
return {
|
| 377 |
+
"kmer_tokens": kmer_tokens,
|
| 378 |
+
"padding_length": padding_length,
|
| 379 |
+
"valid_length": valid_length,
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
def _tokenize(self, text: str, **kwargs) -> List[str]:
|
| 383 |
+
return list(text)
|
| 384 |
+
|
| 385 |
+
def _convert_token_to_id(self, token: str) -> int:
|
| 386 |
+
if token in self.dna_token_to_id:
|
| 387 |
+
return self.dna_token_to_id[token]
|
| 388 |
+
return self._base_vocab.get(token, self._base_tokenizer.unk_token_id or 0)
|
| 389 |
+
|
| 390 |
+
def _convert_id_to_token(self, index: int) -> str:
|
| 391 |
+
if index in self.dna_id_to_token:
|
| 392 |
+
return self.dna_id_to_token[index]
|
| 393 |
+
return self._id_to_token.get(index, "<oov>")
|
| 394 |
+
|
| 395 |
+
def convert_tokens_to_string(self, tokens: List[str]) -> str:
|
| 396 |
+
ids = [self._convert_token_to_id(t) for t in tokens]
|
| 397 |
+
return self.decode(ids, skip_special_tokens=False)
|
| 398 |
+
def encode(
|
| 399 |
+
self,
|
| 400 |
+
text: str,
|
| 401 |
+
add_special_tokens: bool = False,
|
| 402 |
+
return_token_mask: bool = False,
|
| 403 |
+
auto_dna_tags: Optional[bool] = None,
|
| 404 |
+
**kwargs
|
| 405 |
+
) -> Union[List[int], Tuple[List[int], List[int]]]:
|
| 406 |
+
use_auto = self.auto_dna_tags if auto_dna_tags is None else auto_dna_tags
|
| 407 |
+
if use_auto:
|
| 408 |
+
text = self._add_auto_dna_tags(text)
|
| 409 |
+
|
| 410 |
+
segments = self._split_by_dna_tags(text)
|
| 411 |
+
|
| 412 |
+
token_ids = []
|
| 413 |
+
token_mask = [] if return_token_mask else None
|
| 414 |
+
|
| 415 |
+
for segment_content, is_dna in segments:
|
| 416 |
+
if is_dna:
|
| 417 |
+
dna_content, has_start, has_end = self._parse_dna_region(segment_content)
|
| 418 |
+
|
| 419 |
+
if has_start:
|
| 420 |
+
token_ids.append(self.dna_begin_token_id)
|
| 421 |
+
if return_token_mask:
|
| 422 |
+
token_mask.append(0)
|
| 423 |
+
|
| 424 |
+
if dna_content:
|
| 425 |
+
result = self._process_dna_sequence(dna_content)
|
| 426 |
+
|
| 427 |
+
for idx, kmer in enumerate(result["kmer_tokens"]):
|
| 428 |
+
token_id = self.dna_token_to_id.get(kmer, self.oov_token_id)
|
| 429 |
+
token_ids.append(token_id)
|
| 430 |
+
|
| 431 |
+
if return_token_mask:
|
| 432 |
+
if kmer == "<oov>":
|
| 433 |
+
token_mask.append(0)
|
| 434 |
+
elif kmer in self.dna_base_token_to_base:
|
| 435 |
+
token_mask.append(1) # char-fallback single base
|
| 436 |
+
elif idx == len(result["kmer_tokens"]) - 1 and result["padding_length"] > 0:
|
| 437 |
+
token_mask.append(result["valid_length"])
|
| 438 |
+
else:
|
| 439 |
+
token_mask.append(self.k)
|
| 440 |
+
|
| 441 |
+
if has_end:
|
| 442 |
+
token_ids.append(self.dna_end_token_id)
|
| 443 |
+
if return_token_mask:
|
| 444 |
+
token_mask.append(0)
|
| 445 |
+
else:
|
| 446 |
+
base_ids = self._base_tokenizer.encode(
|
| 447 |
+
segment_content,
|
| 448 |
+
add_special_tokens=False
|
| 449 |
+
)
|
| 450 |
+
token_ids.extend(base_ids)
|
| 451 |
+
if return_token_mask:
|
| 452 |
+
token_mask.extend([-1] * len(base_ids))
|
| 453 |
+
|
| 454 |
+
# Do NOT append EOS when add_special_tokens=True. Qwen3 doesn't add
|
| 455 |
+
# BOS/EOS either, and appending EOS here breaks lighteval's
|
| 456 |
+
# tok_encode_pair: it relies on
|
| 457 |
+
# len(encode(ctx)) + len(encode(answer)) == len(encode(ctx + answer))
|
| 458 |
+
# which the extra EOS violates by shifting the split by 1.
|
| 459 |
+
|
| 460 |
+
if return_token_mask:
|
| 461 |
+
return token_ids, token_mask
|
| 462 |
+
return token_ids
|
| 463 |
+
|
| 464 |
+
def _decode_dna_token(self, token_str: str) -> str:
|
| 465 |
+
# Surface text for a DNA-vocab token. Baseline (pad_a) returns it
|
| 466 |
+
# unchanged; char maps "<b_A>"->"A"; pad_n strips the N-pad tail fill
|
| 467 |
+
# ("CTNNNN"->"CT"). Real k-mers contain no N, so rstrip is a no-op there.
|
| 468 |
+
if token_str in self.dna_base_token_to_base:
|
| 469 |
+
return self.dna_base_token_to_base[token_str]
|
| 470 |
+
if self.tail == "pad_n" and len(token_str) == self.k and token_str[0] in "ATCG":
|
| 471 |
+
return token_str.rstrip("N")
|
| 472 |
+
return token_str
|
| 473 |
+
|
| 474 |
+
def decode(
|
| 475 |
+
self,
|
| 476 |
+
token_ids: Union[int, List[int]],
|
| 477 |
+
skip_special_tokens: bool = False,
|
| 478 |
+
**kwargs
|
| 479 |
+
) -> str:
|
| 480 |
+
# A raw tensor/ndarray must become plain ints: dna_id_to_token is keyed by
|
| 481 |
+
# python ints and a 0-dim tensor scalar hashes by identity, so DNA lookups
|
| 482 |
+
# silently miss and the bases are dropped (e.g. decode(generate(...)[0])).
|
| 483 |
+
if hasattr(token_ids, "tolist"):
|
| 484 |
+
token_ids = token_ids.tolist()
|
| 485 |
+
if isinstance(token_ids, int):
|
| 486 |
+
token_ids = [token_ids]
|
| 487 |
+
|
| 488 |
+
if skip_special_tokens:
|
| 489 |
+
special_ids = {self.eos_token_id, self.pad_token_id}
|
| 490 |
+
token_ids = [tid for tid in token_ids if tid not in special_ids]
|
| 491 |
+
|
| 492 |
+
parts = []
|
| 493 |
+
i = 0
|
| 494 |
+
|
| 495 |
+
while i < len(token_ids):
|
| 496 |
+
tid = token_ids[i]
|
| 497 |
+
|
| 498 |
+
if tid == self.dna_begin_token_id:
|
| 499 |
+
dna_tokens = []
|
| 500 |
+
i += 1
|
| 501 |
+
|
| 502 |
+
while i < len(token_ids) and token_ids[i] != self.dna_end_token_id:
|
| 503 |
+
if token_ids[i] in self.dna_id_to_token:
|
| 504 |
+
dna_tokens.append(self._decode_dna_token(self.dna_id_to_token[token_ids[i]]))
|
| 505 |
+
i += 1
|
| 506 |
+
|
| 507 |
+
dna_seq = ''.join(dna_tokens)
|
| 508 |
+
|
| 509 |
+
if skip_special_tokens:
|
| 510 |
+
parts.append(dna_seq)
|
| 511 |
+
else:
|
| 512 |
+
parts.append(f"<dna>{dna_seq}")
|
| 513 |
+
if i < len(token_ids) and token_ids[i] == self.dna_end_token_id:
|
| 514 |
+
parts.append("</dna>")
|
| 515 |
+
i += 1
|
| 516 |
+
|
| 517 |
+
elif tid in self.dna_id_to_token:
|
| 518 |
+
# This branch handles k-mer tokens that appear without a <dna>
|
| 519 |
+
# wrapper — the common generation case where <dna> was in the
|
| 520 |
+
# prompt but only the generated portion is being decoded.
|
| 521 |
+
# K-mer tokens are content, not special tokens, so always decode
|
| 522 |
+
# them. Only drop true DNA special tokens (<dna>, </dna>, <oov>)
|
| 523 |
+
# when skip_special_tokens=True.
|
| 524 |
+
is_dna_special = tid in (self.dna_begin_token_id, self.dna_end_token_id, self.oov_token_id)
|
| 525 |
+
if not (skip_special_tokens and is_dna_special):
|
| 526 |
+
parts.append(self._decode_dna_token(self.dna_id_to_token[tid]))
|
| 527 |
+
i += 1
|
| 528 |
+
|
| 529 |
+
else:
|
| 530 |
+
text_ids = []
|
| 531 |
+
while i < len(token_ids):
|
| 532 |
+
curr_id = token_ids[i]
|
| 533 |
+
if curr_id in self.dna_id_to_token or curr_id == self.dna_begin_token_id:
|
| 534 |
+
break
|
| 535 |
+
text_ids.append(curr_id)
|
| 536 |
+
i += 1
|
| 537 |
+
|
| 538 |
+
if text_ids:
|
| 539 |
+
decoded = self._base_tokenizer.decode(text_ids, skip_special_tokens=skip_special_tokens)
|
| 540 |
+
parts.append(decoded)
|
| 541 |
+
|
| 542 |
+
return ''.join(parts)
|
| 543 |
+
|
| 544 |
+
def batch_decode(
|
| 545 |
+
self,
|
| 546 |
+
sequences: Union[List[int], List[List[int]], "torch.Tensor"],
|
| 547 |
+
skip_special_tokens: bool = False,
|
| 548 |
+
**kwargs
|
| 549 |
+
) -> List[str]:
|
| 550 |
+
return [
|
| 551 |
+
self.decode(
|
| 552 |
+
seq.tolist() if hasattr(seq, 'tolist') else list(seq),
|
| 553 |
+
skip_special_tokens=skip_special_tokens,
|
| 554 |
+
**kwargs
|
| 555 |
+
)
|
| 556 |
+
for seq in sequences
|
| 557 |
+
]
|
| 558 |
+
|
| 559 |
+
def __call__(
|
| 560 |
+
self,
|
| 561 |
+
text: Union[str, List[str]],
|
| 562 |
+
add_special_tokens: bool = False,
|
| 563 |
+
padding: bool = False,
|
| 564 |
+
truncation: bool = False,
|
| 565 |
+
max_length: Optional[int] = None,
|
| 566 |
+
return_tensors: Optional[str] = None,
|
| 567 |
+
return_token_mask: bool = False,
|
| 568 |
+
auto_dna_tags: Optional[bool] = None,
|
| 569 |
+
**kwargs
|
| 570 |
+
) -> Dict[str, Any]:
|
| 571 |
+
is_batch = isinstance(text, list)
|
| 572 |
+
texts = text if is_batch else [text]
|
| 573 |
+
|
| 574 |
+
all_ids = []
|
| 575 |
+
all_masks = [] if return_token_mask else None
|
| 576 |
+
|
| 577 |
+
for t in texts:
|
| 578 |
+
if return_token_mask:
|
| 579 |
+
ids, mask = self.encode(t, add_special_tokens=add_special_tokens, return_token_mask=True, auto_dna_tags=auto_dna_tags)
|
| 580 |
+
all_ids.append(ids)
|
| 581 |
+
all_masks.append(mask)
|
| 582 |
+
else:
|
| 583 |
+
ids = self.encode(t, add_special_tokens=add_special_tokens, return_token_mask=False, auto_dna_tags=auto_dna_tags)
|
| 584 |
+
all_ids.append(ids)
|
| 585 |
+
|
| 586 |
+
if padding:
|
| 587 |
+
max_len = max(len(ids) for ids in all_ids)
|
| 588 |
+
if max_length:
|
| 589 |
+
max_len = min(max_len, max_length)
|
| 590 |
+
|
| 591 |
+
padded_ids = []
|
| 592 |
+
attention_masks = []
|
| 593 |
+
padded_token_masks = [] if return_token_mask else None
|
| 594 |
+
|
| 595 |
+
for idx, ids in enumerate(all_ids):
|
| 596 |
+
pad_len = max_len - len(ids)
|
| 597 |
+
|
| 598 |
+
if pad_len > 0:
|
| 599 |
+
ids = ids + [self.pad_token_id] * pad_len
|
| 600 |
+
attn = [1] * (max_len - pad_len) + [0] * pad_len
|
| 601 |
+
if return_token_mask:
|
| 602 |
+
mask = all_masks[idx] + [-2] * pad_len
|
| 603 |
+
else:
|
| 604 |
+
ids = ids[:max_len]
|
| 605 |
+
attn = [1] * max_len
|
| 606 |
+
if return_token_mask:
|
| 607 |
+
mask = all_masks[idx][:max_len]
|
| 608 |
+
|
| 609 |
+
padded_ids.append(ids)
|
| 610 |
+
attention_masks.append(attn)
|
| 611 |
+
if return_token_mask:
|
| 612 |
+
padded_token_masks.append(mask)
|
| 613 |
+
|
| 614 |
+
all_ids = padded_ids
|
| 615 |
+
all_masks = padded_token_masks
|
| 616 |
+
else:
|
| 617 |
+
attention_masks = [[1] * len(ids) for ids in all_ids]
|
| 618 |
+
|
| 619 |
+
result = {
|
| 620 |
+
"input_ids": all_ids if is_batch else all_ids[0],
|
| 621 |
+
"attention_mask": attention_masks if is_batch else attention_masks[0],
|
| 622 |
+
}
|
| 623 |
+
|
| 624 |
+
if return_token_mask:
|
| 625 |
+
result["token_mask"] = all_masks if is_batch else all_masks[0]
|
| 626 |
+
|
| 627 |
+
if return_tensors == "pt":
|
| 628 |
+
import torch
|
| 629 |
+
if is_batch:
|
| 630 |
+
result["input_ids"] = torch.tensor(result["input_ids"])
|
| 631 |
+
result["attention_mask"] = torch.tensor(result["attention_mask"])
|
| 632 |
+
if return_token_mask:
|
| 633 |
+
result["token_mask"] = torch.tensor(result["token_mask"])
|
| 634 |
+
else:
|
| 635 |
+
result["input_ids"] = torch.tensor([result["input_ids"]])
|
| 636 |
+
result["attention_mask"] = torch.tensor([result["attention_mask"]])
|
| 637 |
+
if return_token_mask:
|
| 638 |
+
result["token_mask"] = torch.tensor([result["token_mask"]])
|
| 639 |
+
|
| 640 |
+
return BatchEncoding(result, tensor_type=return_tensors)
|
| 641 |
+
|
| 642 |
+
# Markers delimiting an assistant turn, used to build the assistant-token mask.
|
| 643 |
+
# Qwen chat format; override on the instance for other bases (e.g. Gemma uses
|
| 644 |
+
# "<start_of_turn>model\n" / "<end_of_turn>").
|
| 645 |
+
ASSISTANT_HEADER = "<|im_start|>assistant\n"
|
| 646 |
+
TURN_END = "<|im_end|>\n"
|
| 647 |
+
|
| 648 |
+
def apply_chat_template(self, conversation, *args, return_assistant_tokens_mask: bool = False, **kwargs):
|
| 649 |
+
"""Completion-only assistant mask WITHOUT a fast tokenizer.
|
| 650 |
+
|
| 651 |
+
HF derives the assistant mask from ``{% generation %}`` char-spans via
|
| 652 |
+
``BatchEncoding.char_to_token()``, which only fast (Rust) tokenizers implement.
|
| 653 |
+
HybridDNATokenizer is a slow Python tokenizer (the DNA k-mer logic can't be a
|
| 654 |
+
``tokenizer.json``), so that path raises
|
| 655 |
+
``char_to_token() is not available when using Python based tokenizers``.
|
| 656 |
+
|
| 657 |
+
Instead we tokenize normally and mark the tokens of every assistant turn -- from
|
| 658 |
+
just after ``ASSISTANT_HEADER`` up to and including the next ``TURN_END``. This
|
| 659 |
+
makes ``return_assistant_tokens_mask=True`` (and thus TRL ``assistant_only_loss``)
|
| 660 |
+
work unchanged, and needs no ``{% generation %}`` markers in the template.
|
| 661 |
+
"""
|
| 662 |
+
auto_dna_tags = kwargs.pop("auto_dna_tags", None)
|
| 663 |
+
if auto_dna_tags is not None:
|
| 664 |
+
tokenizer_kwargs = dict(kwargs.pop("tokenizer_kwargs", None) or {})
|
| 665 |
+
tokenizer_kwargs["auto_dna_tags"] = auto_dna_tags
|
| 666 |
+
kwargs["tokenizer_kwargs"] = tokenizer_kwargs
|
| 667 |
+
|
| 668 |
+
if not return_assistant_tokens_mask:
|
| 669 |
+
return super().apply_chat_template(conversation, *args, **kwargs)
|
| 670 |
+
kwargs.pop("return_assistant_tokens_mask", None)
|
| 671 |
+
want_tensors = kwargs.pop("return_tensors", None)
|
| 672 |
+
kwargs["tokenize"] = True
|
| 673 |
+
kwargs["return_dict"] = True
|
| 674 |
+
enc = super().apply_chat_template(conversation, *args, return_tensors=None, **kwargs)
|
| 675 |
+
seqs = enc["input_ids"]
|
| 676 |
+
batched = len(seqs) > 0 and isinstance(seqs[0], (list, tuple))
|
| 677 |
+
batch = seqs if batched else [seqs]
|
| 678 |
+
hdr = self(self.ASSISTANT_HEADER, add_special_tokens=False)["input_ids"]
|
| 679 |
+
end = self(self.TURN_END, add_special_tokens=False)["input_ids"]
|
| 680 |
+
masks = []
|
| 681 |
+
for ids in batch:
|
| 682 |
+
ids = list(ids)
|
| 683 |
+
m = [0] * len(ids)
|
| 684 |
+
i = 0
|
| 685 |
+
while i <= len(ids) - len(hdr):
|
| 686 |
+
if ids[i:i + len(hdr)] == hdr:
|
| 687 |
+
j = i + len(hdr)
|
| 688 |
+
while j < len(ids) and ids[j:j + len(end)] != end:
|
| 689 |
+
m[j] = 1
|
| 690 |
+
j += 1
|
| 691 |
+
for k in range(len(end)): # include the turn-end token(s)
|
| 692 |
+
if j + k < len(ids):
|
| 693 |
+
m[j + k] = 1
|
| 694 |
+
i = j + len(end)
|
| 695 |
+
else:
|
| 696 |
+
i += 1
|
| 697 |
+
masks.append(m)
|
| 698 |
+
enc["assistant_masks"] = masks if batched else masks[0]
|
| 699 |
+
if want_tensors is not None:
|
| 700 |
+
enc = enc.convert_to_tensors(tensor_type=want_tensors)
|
| 701 |
+
return enc
|
| 702 |
+
|
| 703 |
+
def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]:
|
| 704 |
+
vocab_file = os.path.join(
|
| 705 |
+
save_directory,
|
| 706 |
+
(filename_prefix + "-" if filename_prefix else "") + "vocab.json"
|
| 707 |
+
)
|
| 708 |
+
|
| 709 |
+
with open(vocab_file, "w", encoding="utf-8") as f:
|
| 710 |
+
json.dump(self._vocab, f, ensure_ascii=False, indent=2)
|
| 711 |
+
|
| 712 |
+
return (vocab_file,)
|
| 713 |
+
|
| 714 |
+
def save_pretrained(self, save_directory: str, **kwargs):
|
| 715 |
+
os.makedirs(save_directory, exist_ok=True)
|
| 716 |
+
|
| 717 |
+
# Save base tokenizer files with the wrapper's effective chat template.
|
| 718 |
+
self._base_tokenizer.chat_template = self.chat_template
|
| 719 |
+
self._base_tokenizer.save_pretrained(save_directory)
|
| 720 |
+
|
| 721 |
+
# Save DNA config
|
| 722 |
+
dna_config = {
|
| 723 |
+
"k": self.k,
|
| 724 |
+
"dna_start_id": self.dna_start_id,
|
| 725 |
+
"dna_vocab_size": self.dna_vocab_size,
|
| 726 |
+
"dna_special_tokens": self.dna_special_tokens,
|
| 727 |
+
"auto_dna_tags": self.auto_dna_tags,
|
| 728 |
+
"tail": self.tail,
|
| 729 |
+
}
|
| 730 |
+
|
| 731 |
+
dna_config_path = os.path.join(save_directory, "dna_config.json")
|
| 732 |
+
with open(dna_config_path, "w", encoding="utf-8") as f:
|
| 733 |
+
json.dump(dna_config, f, indent=2)
|
| 734 |
+
|
| 735 |
+
# Update tokenizer_config.json with auto_map
|
| 736 |
+
config_path = os.path.join(save_directory, "tokenizer_config.json")
|
| 737 |
+
|
| 738 |
+
if os.path.exists(config_path):
|
| 739 |
+
with open(config_path, "r") as f:
|
| 740 |
+
config = json.load(f)
|
| 741 |
+
else:
|
| 742 |
+
config = {}
|
| 743 |
+
|
| 744 |
+
config.update({
|
| 745 |
+
"tokenizer_class": "HybridDNATokenizer",
|
| 746 |
+
"auto_map": {
|
| 747 |
+
"AutoTokenizer": ["tokenizer.HybridDNATokenizer", None]
|
| 748 |
+
},
|
| 749 |
+
"k": self.k,
|
| 750 |
+
"auto_dna_tags": self.auto_dna_tags,
|
| 751 |
+
"tail": self.tail,
|
| 752 |
+
})
|
| 753 |
+
|
| 754 |
+
with open(config_path, "w", encoding="utf-8") as f:
|
| 755 |
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
| 756 |
+
|
| 757 |
+
# Copy this tokenizer.py to save directory
|
| 758 |
+
import shutil
|
| 759 |
+
src_py = os.path.abspath(__file__)
|
| 760 |
+
dst_py = os.path.join(save_directory, "tokenizer.py")
|
| 761 |
+
if os.path.exists(src_py) and src_py != dst_py:
|
| 762 |
+
shutil.copy2(src_py, dst_py)
|
| 763 |
+
|
| 764 |
+
return (save_directory,)
|
| 765 |
+
|
| 766 |
+
@classmethod
|
| 767 |
+
def from_pretrained(
|
| 768 |
+
cls,
|
| 769 |
+
pretrained_model_name_or_path,
|
| 770 |
+
*args,
|
| 771 |
+
cache_dir=None,
|
| 772 |
+
force_download=False,
|
| 773 |
+
local_files_only=False,
|
| 774 |
+
token=None,
|
| 775 |
+
revision="main",
|
| 776 |
+
trust_remote_code=False,
|
| 777 |
+
**kwargs,
|
| 778 |
+
):
|
| 779 |
+
# Custom loader: PreTrainedTokenizer.from_pretrained is incompatible with this
|
| 780 |
+
# tokenizer's __init__ (it re-passes bos_token etc.), so construct directly from
|
| 781 |
+
# bio_config/dna_config. That bypasses standard metadata loading, so also read
|
| 782 |
+
# chat_template from tokenizer_config.json (vLLM / apply_chat_template need it).
|
| 783 |
+
from transformers.utils.hub import cached_file
|
| 784 |
+
|
| 785 |
+
proxies = kwargs.pop("proxies", None)
|
| 786 |
+
subfolder = kwargs.pop("subfolder", None)
|
| 787 |
+
commit_hash = kwargs.pop("_commit_hash", None)
|
| 788 |
+
hub_kwargs = {
|
| 789 |
+
"cache_dir": cache_dir,
|
| 790 |
+
"force_download": force_download,
|
| 791 |
+
"local_files_only": local_files_only,
|
| 792 |
+
"proxies": proxies,
|
| 793 |
+
"revision": revision,
|
| 794 |
+
"subfolder": subfolder,
|
| 795 |
+
"token": token,
|
| 796 |
+
"_commit_hash": commit_hash,
|
| 797 |
+
"_raise_exceptions_for_missing_entries": False,
|
| 798 |
+
}
|
| 799 |
+
|
| 800 |
+
cfg = {}
|
| 801 |
+
for _name in ("bio_config.json", "dna_config.json"):
|
| 802 |
+
_p = cached_file(pretrained_model_name_or_path, _name, **hub_kwargs)
|
| 803 |
+
if _p is not None:
|
| 804 |
+
with open(_p, encoding="utf-8") as _f:
|
| 805 |
+
cfg = json.load(_f)
|
| 806 |
+
break
|
| 807 |
+
|
| 808 |
+
_init = {"base_tokenizer_path": pretrained_model_name_or_path}
|
| 809 |
+
for _key in ("k", "tail", "auto_dna_tags"):
|
| 810 |
+
if _key in cfg:
|
| 811 |
+
_init[_key] = cfg[_key]
|
| 812 |
+
|
| 813 |
+
tokenizer_config_path = cached_file(pretrained_model_name_or_path, "tokenizer_config.json", **hub_kwargs)
|
| 814 |
+
if tokenizer_config_path is not None:
|
| 815 |
+
with open(tokenizer_config_path, encoding="utf-8") as _f:
|
| 816 |
+
tokenizer_config = json.load(_f)
|
| 817 |
+
if tokenizer_config.get("chat_template"):
|
| 818 |
+
_init["chat_template"] = tokenizer_config["chat_template"]
|
| 819 |
+
|
| 820 |
+
chat_template_path = cached_file(pretrained_model_name_or_path, "chat_template.jinja", **hub_kwargs)
|
| 821 |
+
if chat_template_path is not None:
|
| 822 |
+
with open(chat_template_path, encoding="utf-8") as _f:
|
| 823 |
+
_init["chat_template"] = _f.read()
|
| 824 |
+
|
| 825 |
+
_init.update(kwargs)
|
| 826 |
+
tokenizer = cls(**_init)
|
| 827 |
+
# DNA ids start right after the base vocabulary, so a layout that differs from the saved one would misalign every
|
| 828 |
+
# DNA embedding. Fail instead of tokenizing with shifted ids.
|
| 829 |
+
for _key in ("dna_start_id", "dna_vocab_size"):
|
| 830 |
+
if _key in cfg and cfg[_key] != getattr(tokenizer, _key):
|
| 831 |
+
raise ValueError(
|
| 832 |
+
f"{pretrained_model_name_or_path} was saved with {_key}={cfg[_key]}, but the base tokenizer gives "
|
| 833 |
+
f"{getattr(tokenizer, _key)}, so its DNA token ids would not match the model."
|
| 834 |
+
)
|
| 835 |
+
return tokenizer
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"bos_token": null,
|
| 5 |
+
"clean_up_tokenization_spaces": false,
|
| 6 |
+
"eos_token": "<|endoftext|>",
|
| 7 |
+
"errors": "replace",
|
| 8 |
+
"extra_special_tokens": [
|
| 9 |
+
"<|im_start|>",
|
| 10 |
+
"<|im_end|>",
|
| 11 |
+
"<|object_ref_start|>",
|
| 12 |
+
"<|object_ref_end|>",
|
| 13 |
+
"<|box_start|>",
|
| 14 |
+
"<|box_end|>",
|
| 15 |
+
"<|quad_start|>",
|
| 16 |
+
"<|quad_end|>",
|
| 17 |
+
"<|vision_start|>",
|
| 18 |
+
"<|vision_end|>",
|
| 19 |
+
"<|vision_pad|>",
|
| 20 |
+
"<|image_pad|>",
|
| 21 |
+
"<|video_pad|>"
|
| 22 |
+
],
|
| 23 |
+
"is_local": false,
|
| 24 |
+
"local_files_only": false,
|
| 25 |
+
"model_max_length": 131072,
|
| 26 |
+
"pad_token": "<|endoftext|>",
|
| 27 |
+
"split_special_tokens": false,
|
| 28 |
+
"tokenizer_class": "HybridDNATokenizer",
|
| 29 |
+
"unk_token": null,
|
| 30 |
+
"auto_map": {
|
| 31 |
+
"AutoTokenizer": [
|
| 32 |
+
"tokenizer.HybridDNATokenizer",
|
| 33 |
+
null
|
| 34 |
+
]
|
| 35 |
+
},
|
| 36 |
+
"k": 6,
|
| 37 |
+
"auto_dna_tags": true,
|
| 38 |
+
"tail": "pad_n"
|
| 39 |
+
}
|