可以,但“不用 BPE”≠“什么都不做”,而是把 tokenizer 退化成字符级(character-level)或者SMILES 字符级。
这样做确实能跑,但代价远远不只是“比较耗资源”那么简单,基本属于不可接受。给你个量化对比就明白了:
| 方案 | vocab 大小 | 序列长度 | 显存占用 | 训练时间 | 收敛难度 | 备注 |
|---|---|---|---|---|---|---|
| BPE(53 k) | ≈ 5×10⁴ | 200 token | 1× | 1× | 正常 | DrugGPT 原方案 |
| 字符级 | ≈ 60 | 1200–1500 字符 | 6–8× | 6–8× | 难 | 同样 batch 显存直接炸 |
| 原子级 | ≈ 30 | 2000+ | 10×+ | 10×+ | 极难 | 还要自己写 tokenizer |
显存/时间都是线性以上增长,因为 Transformer 的内存 ∝ (序列长度)²。
结论:
- 玩具级 demo(<1 M 样本、短序列)可以字符级,图个方便;
- 正经用 Zinc20 / 百万级蛋白-配体对,必须上 BPE 或 SentencePiece,否则 8×A100 都喂不饱;
- 千帆平台单卡 40 GB 环境,字符级 768 长度 batch=1 都可能 OOM,实际等于跑不动。
所以:
“不用 BPE”理论上可行,工程上基本等于放弃。
除非你只是本地几十条分子做着玩,否则还是把 BPE 训好,序列压到 200 token 再上千帆。
一句话区分
- BPE(Byte-Pair Encoding)和SentencePiece都不是“模型”,而是子词分段算法;
- SentencePiece是 Google 出品的“全家桶”工具,它内部既可以跑 BPE,也可以跑 Unigram 或 CHAR,还能把空格当成普通符号、不必预先分词;
- 所以:
“用 SentencePiece” ≠ “不用 BPE”;
“用 BPE” 可以用 HuggingFace 的tokenizers库,也可以用 SentencePiece 来实现。
下面给你一张“家谱图”+ 横向对比,一眼就能看懂。
一、家谱关系
子词分段算法家族 ├─ BPE(最早来自 90 年代压缩领域) │ ├─ HuggingFace 实现:ByteLevelBPETokenizer │ └─ SentencePiece 实现:--model_type=bpe ├─ Unigram Language Model(SentencePiece 默认) └─ WordPiece(BERT 用,和 BPE 类似但合并策略不同)二、横向对比(只看工程落地最关心的 6 项)
| 维度 | HF-BPE | SentencePiece-BPE | SentencePiece-Unigram |
|---|---|---|---|
| 空格处理 | 需自己替换成 Ġ/▁ 或先分词 | 自动把空格当普通符号 ▁ | 同上 |
| 开源库 | tokenizers(Rust 后端,极快) | sentencepiece(C++) | 同上 |
| 速度 | 训练+编码都最快 | 训练略慢,编码差不多 | 训练更慢 |
| vocab 大小控制 | 精确到 n 次 merge | 精确 | 精确 |
| 跨语言 | 需手动预处理 | 直接扔整句即可 | 同上 |
| 与 HF 兼容 | 原生保存tokenizer.json,无缝 | 需PreTrainedTokenizerFast.from_file()转一下 | 同上 |
三、一句话选型建议
- 追求“最快训练 + 最简代码”→ 直接用 HuggingFace 的
ByteLevelBPETokenizer(就是 DrugGPT 原文做法)。 - 多语言混排、不想管空格、还要给下游 TensorFlow Lite/ONNX 留接口→ 用 SentencePiece,选
--model_type=bpe即可,仍然是 BPE,只是换了实现。 - 想压榨更小 vocab、不介意训练慢→ SentencePiece 切到 Unigram,往往比 BPE 再少 10-20% 词表。
四、最小可运行示例(SentencePiece 跑 BPE)
# 1. 安装pipinstallsentencepiece# 2. 准备纯文本(一行一条 SMILES 或蛋白-配体整句)cat>corpus.txt<<EOF <|startoftext|><L>CCO<|endoftext|> <|startoftext|><P>MKLVL<L>CC(=O)O<|endoftext|> EOF# 3. 训练(BPE 模式)spm_train --input=corpus.txt\--model_prefix=sp_bpe\--vocab_size=53080\--model_type=bpe\--character_coverage=1.0\--pad_id=0--unk_id=1--bos_id=2--eos_id=3\--user_defined_symbols="<|startoftext|>,<|endoftext|>,<P>,<L>"# 4. 导出给 HF 用from tokenizersimportSentencePieceUnigramTokenizer tok=SentencePieceUnigramTokenizer("sp_bpe.model")tok.save("sp_bpe_hf.json")# 得到 HF 格式的 tokenizer.json总结
- BPE 是算法,SentencePiece 是工具箱;
- 用 SentencePiece 跑 BPE仍然叫 BPE,只是多了“空格即字符”“跨语言友好”两个buff;
- DrugGPT 这种纯英文+SMILES、已手动拼好特殊标记的场景,HF-BPE 最快最轻;
- 以后做多语言、端到端、还要给移动端部署,再换 SentencePiece 不迟。