Skip to main content

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

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=32000
    • num_merges=40000(上限,不等于最终词表大小)
    • n_max=9
    • num_workers=64(按 CPU 资源调整)
    • use_heap=False(默认,强烈建议保持)
    • multi_process=False(默认;若你希望进一步隔离峰值内存/更稳定,可尝试 True
  • 日志重定向(很重要):训练日志默认写到 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_sizeglobal 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

示例(在你自己的 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 Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

length_tokenizer_rs-0.1.8-cp312-cp312-win_amd64.whl (588.2 kB view details)

Uploaded CPython 3.12Windows x86-64

length_tokenizer_rs-0.1.8-cp312-cp312-macosx_11_0_arm64.whl (637.3 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

length_tokenizer_rs-0.1.8-cp311-cp311-win_amd64.whl (587.7 kB view details)

Uploaded CPython 3.11Windows x86-64

length_tokenizer_rs-0.1.8-cp311-cp311-macosx_11_0_arm64.whl (637.8 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

length_tokenizer_rs-0.1.8-cp310-cp310-win_amd64.whl (587.9 kB view details)

Uploaded CPython 3.10Windows x86-64

length_tokenizer_rs-0.1.8-cp310-cp310-macosx_11_0_arm64.whl (638.0 kB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

length_tokenizer_rs-0.1.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (716.4 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

File details

Details for the file length_tokenizer_rs-0.1.8-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for length_tokenizer_rs-0.1.8-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 5e7aa644f32814b690afefa6f058cfe0f1a1aa1a72ed811f311d1b1fe7875935
MD5 ac5ec1b7d8e09cfd90e24e29eacfc2f5
BLAKE2b-256 20c8c72cbc89bbc061ad68d54d8ba10cbbbcdb41cb846a97ec23eeb3d50bf9ce

See more details on using hashes here.

File details

Details for the file length_tokenizer_rs-0.1.8-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for length_tokenizer_rs-0.1.8-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d5858f01d7f9bfb1151be0774d5b58f4da798cdea2678e459f35d09b7f45151d
MD5 0ed071d2f0f9d467a31ddad5ecbf178d
BLAKE2b-256 7abbd3fadcd4f90e2910a4fefc0ca3a2716ba2a9697ac29d95619d125c1e1c47

See more details on using hashes here.

File details

Details for the file length_tokenizer_rs-0.1.8-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for length_tokenizer_rs-0.1.8-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 ded0b1f36d32ef7a017c14a1eea42a20709f07b9a8cd7494e10908be025b9c76
MD5 5a476b64b1c98617d6b2a0658fdd1a39
BLAKE2b-256 73150eef4e68cdb53a325e5d22636f4a5fb3925c25ccdfe842616c5393cabc5a

See more details on using hashes here.

File details

Details for the file length_tokenizer_rs-0.1.8-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for length_tokenizer_rs-0.1.8-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 55f394ba357ead073357f26b040c7f04675f414a87bf8c8ee96b6f077c90bc02
MD5 17c14cd786f6da65c3a364494036cc8b
BLAKE2b-256 a0a1b3fa425b364b9681bd23c43d584f3c3e73bd3cdd4c89970324d5c218095f

See more details on using hashes here.

File details

Details for the file length_tokenizer_rs-0.1.8-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for length_tokenizer_rs-0.1.8-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 9625b01592f7b17b80239750a14823b7abdf7ab8b8b4a50e16401940b91aa260
MD5 171c9e4c01d8abb21d514f2fb29b0f8c
BLAKE2b-256 fce8bd9f7bd33db5dabffcfe30b22e08f81941bd5546d2fd70c40bf31a631e2b

See more details on using hashes here.

File details

Details for the file length_tokenizer_rs-0.1.8-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for length_tokenizer_rs-0.1.8-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d9e3b1ed535645869a315ebaa34708c2f4eee98c75b235d721d06e5515f9f14e
MD5 fd72931ebba3c73283bf49614e570183
BLAKE2b-256 843c97bc44636e8dd388021fc1288c3ed3756b5b6d1809f02125214ce4f128ab

See more details on using hashes here.

File details

Details for the file length_tokenizer_rs-0.1.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for length_tokenizer_rs-0.1.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9b06716e279d1e691e258a1bd025cb7bbaf536efb9ab760936e4910b7cb8b82f
MD5 18d12384ed1c781d3217fa354a5ed637
BLAKE2b-256 898c17f6533ad42cbd0016151de05e5b9b99350b390147f08cafa7f0a6b417de

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page