LengthTokenizer Rust (DP 最少 token / 最低 TPC) Python extension
Project description
Length-MAX Tokenizer
说明(中文补充):本仓库同时提供
- Rust 训练/统计实现(
length_tokenizer)- Rust 推理分词(DP 最少 token / 最低 TPC):Python 扩展
length_tokenizer_rs.DpTokenizer- HuggingFace remote code 导出(
train_to_hf*生成tokenizer_out/目录)
1) Install
pip install length-tokenizer-rs
If your corpus is parquet (or you want streaming reads via pyarrow):
pip install pyarrow
1.1 GitHub Actions / CI install
- Option A (recommended): publish via GitHub Actions (this repo) and install from PyPI.
- In GitHub repo settings → Secrets and variables → Actions, add:
PYPI_API_TOKEN: your PyPI token (project-scoped recommended).
- Bump version in
pyproject.toml/Cargo.toml, then create and push a tag likev0.1.9. - The workflow
/.github/workflows/publish_pypi.ymlwill build wheels (Linux/macOS/Windows) + sdist and publish. - Then in any CI (or locally):
- In GitHub repo settings → Secrets and variables → Actions, add:
pip install length-tokenizer-rs==0.1.9
- Option B: build-from-source install inside GitHub Actions (no PyPI needed).
- This compiles the Rust extension during CI.
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- uses: PyO3/maturin-action@v1
with:
command: develop
args: --release
- run: python -c "import length_tokenizer_rs; print(length_tokenizer_rs.__version__)"
2) Train a vocab from your corpus (export a local tokenizer directory)
After training you will get a directory (e.g. ./tokenizer_out/) containing:
vocab.json / tokenizer_config.json / special_tokens_map.json / tokenization_length_tokenizer.py / README.md
2.1 Text corpus (one sentence per line)
from length_tokenizer_rs import train_to_hf
train_to_hf(
corpus_file="corpus.txt", # one sentence per line
out_dir="./tokenizer_out",
num_merges=50000,
aim_token_num=20000,
n_max=6,
num_workers=8,
multi_process=False,
use_heap=False, # 默认关闭:更省内存(推荐大语料/大 n_max)
)
2.2 Parquet corpus (streaming read via pyarrow)
from length_tokenizer_rs import train_to_hf_parquet
train_to_hf_parquet(
parquet_path="/path/to/parquet_dir_or_file",
out_dir="./tokenizer_out",
text_column="text",
max_docs=0,
batch_size=8192,
recursive=True,
num_merges=50000,
aim_token_num=20000,
n_max=6,
num_workers=8,
multi_process=False,
use_heap=False, # 默认关闭:更省内存
chunk_size=4096,
)
2.3 推荐最高效设置(大语料 + n_max=9 + 32k vocab)
当你要对齐主流 Llama 系列(vocab_size≈32k)并且 n-gram 上限较大(如 n_max=9)时,内存峰值通常是最大瓶颈。
本实现新增了 use_heap 开关(默认关闭),用于避免候选堆复制一份 n-gram key 导致的额外内存。
-
推荐参数(经验值,适用于 2×GPU 训练前的 vocab 训练阶段):
aim_token_num=32000num_merges=40000(上限,不等于最终词表大小)n_max=9num_workers=64(按 CPU 资源调整)use_heap=False(默认,强烈建议保持)multi_process=True(推荐大语料:更稳定,也便于利用增量 apply 优化)
-
默认高效行为(无需额外设置):当
multi_process=True时,本实现会默认开启一组“高吞吐/低峰值”的策略:- 默认启用 增量模式(不再每步全量重算 stats)
- 默认将 diff 临时文件优先写到
/dev/shm(Linux 内存盘;若不存在则回退到系统 temp 目录) - 默认给每个 worker 设置一个“不过量”的线程数(避免 64 个 worker 各自开满 128 线程导致抖动)
- 默认使用较大的
MP_BUCKET_BATCH以提升主进程合并桶文件的并行度
-
仍可覆盖(仅在你需要 debug/保守模式时):
MP_FULL_RECOMPUTE=1或MP_NO_INCREMENTAL=1:强制每步全量重算(更慢)WORKER_THREADS=1:限制 worker 内部线程(更稳、但可能更慢)MP_BUCKET_BATCH=64:降低主进程并行读桶批大小(降低峰值内存)
-
日志重定向(很重要):训练日志默认写到 stderr,如果你用
tee记日志,需要把 stderr 合并到 stdout:
... 2>&1 | tee run.log
- CLI 示例(适合快速 bench / 复现实验):
cd tokenizers_rust
cargo run --release --bin length_tokenizer -- \
--corpus /path/to/train.txt \
--corpus-format txt \
--output token_table_32k.json \
--num-merges 40000 \
--aim-token-num 32000 \
--n-max 9 \
--num-workers 64 \
2>&1 | tee run_32k_n9.log
如你确实有充足内存并希望加速“找 best n-gram”,可以显式开启 heap:
--use-heap
3) Tokenize your corpus with the new vocab → write ids (data prep)
The examples below write ids.txt: one sample per line, space-separated token ids (high throughput: Rust DpTokenizer.encode_batch()).
3.1 Text corpus (one sentence per line) → ids.txt
import json
from pathlib import Path
from length_tokenizer_rs import DpTokenizer
TOKENIZER_DIR = Path("./tokenizer_out")
VOCAB = TOKENIZER_DIR / "vocab.json"
dp = DpTokenizer(str(VOCAB), "<unk>")
# Optional: add BOS/EOS (adjust to your training pipeline)
vocab = json.loads(VOCAB.read_text(encoding="utf-8"))
bos = vocab.get("<s>")
eos = vocab.get("</s>")
IN_TXT = Path("corpus.txt")
OUT_IDS = Path("corpus.ids.txt")
BATCH = 256
buf = []
with IN_TXT.open("r", encoding="utf-8", errors="ignore") as r, OUT_IDS.open("w", encoding="utf-8") as w:
for line in r:
s = line.strip()
if not s:
continue
buf.append(s)
if len(buf) >= BATCH:
for ids in dp.encode_batch(buf):
if bos is not None:
w.write(str(int(bos)) + " ")
w.write(" ".join(str(int(x)) for x in ids))
if eos is not None:
w.write(" " + str(int(eos)))
w.write("\n")
buf.clear()
if buf:
for ids in dp.encode_batch(buf):
if bos is not None:
w.write(str(int(bos)) + " ")
w.write(" ".join(str(int(x)) for x in ids))
if eos is not None:
w.write(" " + str(int(eos)))
w.write("\n")
3.2 Parquet corpus (streaming read) → ids.txt
import json
from pathlib import Path
import pyarrow.dataset as ds
from length_tokenizer_rs import DpTokenizer
PARQUET = "/path/to/parquet_dir_or_file"
TEXT_COL = "text"
TOKENIZER_DIR = Path("./tokenizer_out")
VOCAB = TOKENIZER_DIR / "vocab.json"
dp = DpTokenizer(str(VOCAB), "<unk>")
vocab = json.loads(VOCAB.read_text(encoding="utf-8"))
bos = vocab.get("<s>")
eos = vocab.get("</s>")
OUT_IDS = Path("parquet.ids.txt")
BATCH = 256
buf = []
dataset = ds.dataset(PARQUET, format="parquet")
scanner = dataset.scanner(columns=[TEXT_COL], batch_size=8192, use_threads=True)
with OUT_IDS.open("w", encoding="utf-8") as w:
for batch in scanner.to_batches():
col = batch.column(0)
for s in col.to_pylist():
if not s or not str(s).strip():
continue
buf.append(str(s))
if len(buf) >= BATCH:
for ids in dp.encode_batch(buf):
if bos is not None:
w.write(str(int(bos)) + " ")
w.write(" ".join(str(int(x)) for x in ids))
if eos is not None:
w.write(" " + str(int(eos)))
w.write("\n")
buf.clear()
if buf:
for ids in dp.encode_batch(buf):
if bos is not None:
w.write(str(int(bos)) + " ")
w.write(" ".join(str(int(x)) for x in ids))
if eos is not None:
w.write(" " + str(int(eos)))
w.write("\n")
4) Load the tokenizer for training (local directory)
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("./tokenizer_out", trust_remote_code=True)
assert getattr(tok, "_rust", None) is not None, "Rust extension not active"
5) 现代架构验证(Llama-style: RoPE + RMSNorm + SwiGLU)
审稿/对外说明时,“现代架构验证”通常指:在 非 GPT-2 的 decoder-only Transformer 上,从零训练(或至少跑通训练环节)并复现同方向的效率收益。
本仓库提供一个最小可复用脚本 validate_modern_arch_llama.py,用你导出的 Length-MAX tokenizer 直接训练一个小的 LlamaForCausalLM(RoPE + RMSNorm + SwiGLU)若干步,验证流程可跑通:
pip install -U torch transformers
python validate_modern_arch_llama.py \
--tokenizer_dir ./tokenizer_out \
--corpus_file corpus.txt \
--seq_len 256 \
--batch_size 8 \
--steps 100
建议用于 rebuttal 的正式实验:保持同语料/同 vocab size/同超参,只替换 tokenizer(BPE vs Length-MAX),并在该现代架构上报告 steps-to-target loss、latency/throughput、以及下游任务指标。
5.1 2×GPU(例如 2×5060Ti 16GB)推荐跑法:torchrun DDP
脚本已支持 DDP。示例(两张卡):
torchrun --standalone --nproc_per_node 2 validate_modern_arch_llama.py \
--tokenizer_dir ./tokenizer_out \
--corpus_file corpus.txt \
--max_lines 0 \
--device cuda \
--precision bf16 \
--grad_checkpointing \
--seq_len 1024 \
--batch_size 32 \
--grad_accum 4 \
--steps 2000 \
--lr 3e-4 \
--weight_decay 0.1 \
--print_every 50 \
--hidden_size 768 \
--num_layers 12 \
--num_heads 12 \
--num_kv_heads 12 \
--intermediate_size 2048
说明:
--batch_size是 global batch size(DDP 下会按 world size 自动切分到每张卡)。- 显存不够时,优先开
--grad_checkpointing,然后减小--seq_len或--batch_size,用--grad_accum把全局 batch 拉回去。
6) Publish to PyPI(推荐用 GitHub Actions)
这个仓库原本就带了 GitHub Actions 发版流程:tokenizers_rust/.github/workflows/publish_pypi.yml。
- GitHub 发版(推荐,跨平台 wheel):
- workflow 触发条件是 push tag,例如
v0.1.7 - 会构建 Linux/macOS/Windows 的 wheels(py3.10/3.11/3.12),并用
PYPI_API_TOKEN发布到 PyPI
- workflow 触发条件是 push tag,例如
示例(在你自己的 git repo 里执行):
git add tokenizers_rust
git commit -m "Release v0.1.7"
git tag v0.1.7
git push origin main --tags
- 本地发布(不推荐,通常只会上传当前平台 wheel):
注意:maturin publish 不需要也不支持 --release(默认就是 release 构建)。
cd tokenizers_rust
maturin publish
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file length_tokenizer_rs-0.1.9.tar.gz.
File metadata
- Download URL: length_tokenizer_rs-0.1.9.tar.gz
- Upload date:
- Size: 100.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89f37503f5d45f9b166f47011283a693b1011ecc74c665e7cc05a7007441886c
|
|
| MD5 |
4580b272e8de5007a4a3c1e811f89d80
|
|
| BLAKE2b-256 |
e93386497b79f9e097daa2100203114773a7ea4cbf467bb6b817a360fe7e0f24
|
File details
Details for the file length_tokenizer_rs-0.1.9-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: length_tokenizer_rs-0.1.9-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 599.3 kB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4453143a982999e94cecd17a3bec7091d1c3389279420ab1233c5761d38df335
|
|
| MD5 |
485f1f2250c8354cd2db642a2517e3dd
|
|
| BLAKE2b-256 |
3d278b929d14f173b1791f7120ce048b810fc7040f66782b949afbdaf8388384
|
File details
Details for the file length_tokenizer_rs-0.1.9-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: length_tokenizer_rs-0.1.9-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 646.4 kB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b66198c801a2ab71cfc497aac07bd29ab4e1ee750d788425f79b797ddb79cba7
|
|
| MD5 |
df3b487d05ef19243c0859816b64b124
|
|
| BLAKE2b-256 |
e5a4e8def13b371e2ce2253c372da77c1f1f17a4caddd83079aa26ef42a50198
|
File details
Details for the file length_tokenizer_rs-0.1.9-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: length_tokenizer_rs-0.1.9-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 598.6 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f95e8c89ee8e50326be150a66000bf50479009e39d26b2b20cb2c52bf0de2dd
|
|
| MD5 |
bd1264bcca016337db4c12fefb641bc6
|
|
| BLAKE2b-256 |
d7693ac53d7ca46e0576d6c26ca4310b1c599979b5f349890776531ac3eb9ff4
|
File details
Details for the file length_tokenizer_rs-0.1.9-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: length_tokenizer_rs-0.1.9-cp311-cp311-macosx_11_0_arm64.whl
- Upload date:
- Size: 646.6 kB
- Tags: CPython 3.11, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7dca31841bdef9623f984300a00ada68e78217291aba6e5091d64a9ad3c9adc3
|
|
| MD5 |
db0aa10ad07908d7221a3cabd28b35ce
|
|
| BLAKE2b-256 |
7f83958326ae4266a8e600c85fbfa4b216a63ea51ffb2ad1b33fe77895fbc82f
|
File details
Details for the file length_tokenizer_rs-0.1.9-cp310-cp310-win_amd64.whl.
File metadata
- Download URL: length_tokenizer_rs-0.1.9-cp310-cp310-win_amd64.whl
- Upload date:
- Size: 598.8 kB
- Tags: CPython 3.10, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a10068d5191796e7da9a8fc2bebb3cc96bb8c8ff1836903f97f65d313a89346
|
|
| MD5 |
2cd1efd3ec889889a36ebf8b43bdfaf8
|
|
| BLAKE2b-256 |
e00191caf336a04cd8f92d6e38cceb7415daa1f7a27b90c9d4aa68b3e347652a
|
File details
Details for the file length_tokenizer_rs-0.1.9-cp310-cp310-macosx_11_0_arm64.whl.
File metadata
- Download URL: length_tokenizer_rs-0.1.9-cp310-cp310-macosx_11_0_arm64.whl
- Upload date:
- Size: 646.8 kB
- Tags: CPython 3.10, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92f1da8a3a53fcc12d99661b459e545a5638cfb2eb353c0786d672cd8ecef08d
|
|
| MD5 |
8a39d032b467c521af3db2a09706464f
|
|
| BLAKE2b-256 |
7c4387a51c12c3da2ec9c5d0075cf32131b52a46debf120640a0134a71e39902
|
File details
Details for the file length_tokenizer_rs-0.1.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: length_tokenizer_rs-0.1.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 726.4 kB
- Tags: CPython 3.8, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ba242fcb99e3619bc3282ab3bd6aa1f8f13fe8e9390e46045f27d08fb0e1354
|
|
| MD5 |
5d87bf0875ba6f8ce4b15acb94a9cff4
|
|
| BLAKE2b-256 |
b2c478b2088f4f5eacd2534f61bae2c505628d7886a7f5b5453ff77409a64542
|