Instructions to use RomanCohort/RNAQwen with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use RomanCohort/RNAQwen with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("RomanCohort/RNAQwen", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download scripts/35_check_rna_tokenization.py from RomanCohort/RNAQwen: direct link, hf CLI and curl.
- Browser
- Download file 3.48 kB
-
https://huggingface.co/RomanCohort/RNAQwen/resolve/main/scripts/35_check_rna_tokenization.py
- Command line
-
hf download hf://RomanCohort/RNAQwen/scripts/35_check_rna_tokenization.py
-
curl -L -o 35_check_rna_tokenization.py https://huggingface.co/RomanCohort/RNAQwen/resolve/main/scripts/35_check_rna_tokenization.py
3.48 kB
| """Qwen3 的 tokenizer 怎么切 RNA 序列?这决定"原生文本路线"是不是几乎零代价。 | |
| 要看三件事: | |
| 1. 压缩比:一条 1022 nt 的序列变成多少 token | |
| 2. 切分方式:是逐碱基、还是 BPE 把常见 k-mer 合成一个 token | |
| 3. 有没有"奇怪"的切分:碱基被吸进非核酸 token,或前后缀被污染 | |
| 如果压缩比接近 1:1(一碱基一 token),1022 nt 就是 1022 token —— 仍然可接受 | |
| (Qwen3 上下文 32K)。如果 BPE 能合成 k-mer,那更好。 | |
| 真正要警惕的是第 3 类:切得"很奇怪",那会破坏序列的局部结构。 | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import sys | |
| from collections import Counter | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / "src")) | |
| import rnaqwen # noqa: F401,E402 | |
| from transformers import AutoTokenizer # noqa: E402 | |
| TOK = ROOT / "models/Qwen3-8B" | |
| DATA = ROOT / "data/processed/rnaqa_n6_merged.jsonl" | |
| def main() -> int: | |
| tk = AutoTokenizer.from_pretrained(str(TOK)) | |
| print(f" Qwen3 vocab size = {len(tk)}") | |
| recs = [] | |
| with open(DATA, encoding="utf-8") as f: | |
| for line in f: | |
| if line.strip(): | |
| recs.append(json.loads(line)) | |
| # 按长度分层取 8 条 | |
| recs.sort(key=lambda r: len(r.get("sequence") or "")) | |
| idx = [0, len(recs)//7, len(recs)*2//7, len(recs)*3//7, len(recs)*4//7, | |
| len(recs)*5//7, len(recs)*6//7, len(recs)-1] | |
| pick = [recs[i] for i in idx if recs[i].get("sequence")] | |
| print() | |
| print(" %8s %8s %8s %9s" % ("nt", "tokens", "ratio", "示例切分")) | |
| ratios = [] | |
| for r in pick: | |
| s = r["sequence"] | |
| ids = tk(s, add_special_tokens=False)["input_ids"] | |
| toks = tk.convert_ids_to_tokens(ids) | |
| ratios.append(len(ids) / max(len(s), 1)) | |
| head = "|".join(str(t).replace("Ġ", "_") for t in toks[:8]) | |
| print(" %8d %8d %8.3f %s" % (len(s), len(ids), len(ids)/len(s), head[:50])) | |
| print() | |
| print(f" 平均压缩比 tokens/nt = {sum(ratios)/len(ratios):.3f}") | |
| print(" (1.0 = 逐碱基;<1 = BPE 把 k-mer 合成了)") | |
| # 逐碱基切分的占比:单字符 token 有多少 | |
| s = pick[len(pick)//2]["sequence"] | |
| ids = tk(s, add_special_tokens=False)["input_ids"] | |
| toks = tk.convert_ids_to_tokens(ids) | |
| lens = Counter(len(str(t).lstrip("Ġ")) for t in toks) | |
| print() | |
| print(" token 长度分布(字符数):", dict(sorted(lens.items()))) | |
| single = sum(v for k, v in lens.items() if k == 1) / max(len(toks), 1) | |
| print(f" 单字符 token 占比 {100*single:.1f}%") | |
| # 关键检查:把序列大写/加空格,token 数会不会变(说明切分不稳定) | |
| s2 = " ".join(s) | |
| n2 = len(tk(s2, add_special_tokens=False)["input_ids"]) | |
| print(f" 同一序列加空格后 token 数: {n2}(原 {len(ids)})" | |
| f" {'→ 切分对分隔符敏感' if abs(n2-len(ids))>len(ids)*0.2 else '→ 基本不变'}") | |
| print() | |
| if sum(ratios)/len(ratios) > 0.95: | |
| print(" -> **逐碱基**。1022 nt ≈ 1022 token,上下文装得下,路线可行。") | |
| else: | |
| print(" -> BPE 有合并,比逐碱基更省。") | |
| print(" 下一步:看切分是否**稳定且局部**(相邻碱基的 token 边界要一致),") | |
| print(" 否则继续预训练学到的是切分噪声而不是序列结构。") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |