- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
DeBERTa-v2/v3 系列模型采用 SentencePiece 子词分词方案,其分词器与 BERT 时代基于 WordPiece 的实现有本质区别。本文以 PaddleNLP 官方 API 文档 paddlenlp.transformers.deberta_v2.tokenizer 为骨架,结合 tokenizer.py 的完整源码与 test_tokenizer.py 测试用例,系统讲解DebertaV2Tokenizer的加载方式、全部构造参数、核心编码解码流程与底层SPMTokenizer实现细节。读完本文,你将能够独立完成 DeBERTa-v2/v3 系列模型的加载、分词、特殊 token 拼装与保存复用,并理解其与 WordPiece 分词器在行为上的关键差异。
一、从 API 文档到实现:DebertaV2Tokenizer 概览
PaddleNLP 中文文档中的 deberta_v2 模块页 通过 Sphinxautomodule指令自动生成,其中tokenizer子页即本文对应的 paddlenlp.transformers.deberta_v2.tokenizer.rst,它渲染的正是paddlenlp.transformers.deberta_v2.tokenizer模块的完整文档内容。该模块位于 paddlenlp/transformers/deberta_v2/,包含:
configuration.py:DebertaV2Config配置类(vocab_size 为 128100,max_position_embeddings 为 512,type_vocab_size 为 0);modeling.py:DeBERTa-v2 模型结构;tokenizer.py:本文核心,定义了DebertaV2Tokenizer与内部辅助类SPMTokenizer。
模块对外只暴露一个类:__all__ = ["DebertaV2Tokenizer"],并且已通过 paddlenlp/transformers/init.py 导出,因此可以直接使用:
from paddlenlp.transformers import DebertaV2Tokenizer二、快速上手:从预训练模型加载分词器
DebertaV2Tokenizer继承自PretrainedTokenizer,词表资源类型为 SentencePiece 模型文件(.spm后缀)。源码中通过三个类属性声明资源映射(见 tokenizer.py):
resource_files_names = {"sentencepiece_model_file": "spm.model"} pretrained_resource_files_map = PRETRAINED_VOCAB_FILES_MAP pretrained_init_configuration = PRETRAINED_INIT_CONFIGURATION max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES其中PRETRAINED_VOCAB_FILES_MAP定义了 4 个开箱即用的官方权重(tokenizer.py):
| 模型名 | 说明 |
|---|---|
microsoft/deberta-v2-xlarge | DeBERTa-v2 xlarge,24 层、1024 隐藏维 |
microsoft/deberta-v3-base | DeBERTa-v3 base,12 层、768 隐藏维 |
microsoft/deberta-v3-large | DeBERTa-v3 large,24 层、1024 隐藏维 |
deepset/deberta-v3-large-squad2 | 在 SQuAD 2.0 上微调过的 v3-large |
这些模型对应的 SentencePiece 词表由 PaddleNLP 托管(spm.model文件),PRETRAINED_INIT_CONFIGURATION统一将do_lower_case预设为False,PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES将最大输入长度统一设为 512。
最标准的加载方式是通过AutoTokenizer.from_pretrained(这也是源码在词表文件缺失时报错信息中推荐的用法):
from paddlenlp.transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("microsoft/deberta-v3-base")也可以直接实例化DebertaV2Tokenizer,此时需要传入本地.spm词表文件路径:
from paddlenlp.transformers import DebertaV2Tokenizer tokenizer = DebertaV2Tokenizer("./spm.model")如果传入的vocab_file路径不存在,构造函数会抛出ValueError,并提示改用AutoTokenizer.from_pretrained(PRETRAINED_MODEL_NAME)从预训练模型加载(tokenizer.py)。测试用例 test_tokenizer.py 使用仓库自带的测试词表tests/fixtures/spiece.model进行验证。
三、构造函数参数完全解析
DebertaV2Tokenizer.__init__的完整签名(tokenizer.py):
def __init__( self, sentencepiece_model_file, vocab_file=None, do_lower_case=False, split_by_punct=False, bos_token="[CLS]", eos_token="[SEP]", unk_token="[UNK]", sep_token="[SEP]", pad_token="[PAD]", cls_token="[CLS]", mask_token="[MASK]", sp_model_kwargs: Optional[Dict[str, Any]] = None, **kwargs, ) -> None:各参数含义与注意事项:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
sentencepiece_model_file | str | 必填 | SentencePiece 模型文件(通常以.spm结尾),内含词表与合并规则 |
vocab_file | str | None | 词表文件路径;为None时直接复用sentencepiece_model_file |
do_lower_case | bool | False | 分词前是否将输入转为小写。注意 DeBERTa 官方预训练配置为False,与 BERT 不同 |
split_by_punct | bool | False | 是否在标点处预先切分后再交给 SentencePiece 编码 |
bos_token | str | "[CLS]" | 序列开始 token,DeBERTa 中实际承担该角色的是cls_token |
eos_token | str | "[SEP]" | 序列结束 token,实际承担该角色的是sep_token |
unk_token | str | "[UNK]" | 词表外 token 的替换符 |
sep_token | str | "[SEP]" | 分隔 token,用于拼接双序列 |
pad_token | str | "[PAD]" | 填充 token,用于 batch 内长度对齐 |
cls_token | str | "[CLS]" | 分类 token,序列首位置 |
mask_token | str | "[MASK]" | 掩码 token,用于掩码语言建模 |
sp_model_kwargs | dict | None | 透传给SentencePieceProcessor.__init__()的关键字参数,见第五节 |
构造过程中的关键行为(tokenizer.py):
- 所有特殊 token 字符串都会被包装为
AddedToken(token, lstrip=False, rstrip=False),即默认不触发左右空白剥离; - 通过
_build_special_tokens_map_extended注册完整特殊 token 表; - 校验词表文件存在后,实例化内部
SPMTokenizer完成底层加载。
四、核心 API 与编码解码流程
4.1 词表与基础转换
vocab_size属性返回词表大小(tokenizer.py),底层为 SentencePiece piece 数。测试夹具tests/fixtures/spiece.model的vocab_size为 30000(见 test_tokenizer.py);get_vocab()返回原始词表与新增词表的合并副本;_convert_token_to_id(token)直接调用spm.PieceToId(token);_convert_id_to_token(index)在索引越界时回退到unk_token(tokenizer.py)。
测试 test_convert_token_and_id 验证了"<pad>"与 id0的双向转换;test_get_vocab则断言词表首项为<pad>、第二项为<unk>、末项为[PAD],共 30001 个键(含特殊 token)。
4.2 分词入口_tokenize
def _tokenize(self, text: str) -> List[str]: if self.do_lower_case: text = text.lower() return self._tokenizer.tokenize(text)即:先按需小写化,再交给SPMTokenizer.tokenize→_encode_as_pieces。后者(tokenizer.py)会先将输入统一为 Unicode(convert_to_unicode,兼容 bytes 输入),然后:
- 若
split_by_punct=True:先用_run_split_on_punc按标点切词,再对每个词片段分别spm.encode(w, out_type=str); - 否则:直接
spm.encode(text, out_type=str)返回 piece 列表。
测试用例覆盖了do_lower_case与split_by_punct的四种组合(test_tokenizer.py)。例如do_lower_case=True时," \tHeLLo!how \n Are yoU? "被切为["▁hello", "!", "how", "▁are", "▁you", "?"],其中▁是 SentencePiece 表示词首空格的专用字符(UTF-8 字节\xe2\x96\x81)。
4.3 特殊 token 拼装格式
build_inputs_with_special_tokens(tokenizer.py)定义了 DeBERTa 的标准输入格式:
- 单序列:
[CLS] X [SEP] - 双序列:
[CLS] A [SEP] B [SEP]
get_special_tokens_mask(tokenizer.py)返回与输入等长的 0/1 掩码:1 表示特殊 token。单序列为[1, 0...0, 1],双序列为[1, 0...0, 1, 0...0, 1];若输入已含特殊 token,则回退到基类逻辑。
create_token_type_ids_from_sequences(tokenizer.py)为双序列任务生成 token type ids:第一段全部为 0,第二段全部为 1。注意 DeBERTa-v2/v3 的配置中type_vocab_size为 0(见 configuration.py),模型结构上并不依赖该区分,但方法仍保留以便与 BERT 系接口兼容。
build_offset_mapping_with_special_tokens(tokenizer.py)为序列标注任务构建偏移映射,特殊 token 的偏移固定为(0, 0):
- 单序列:
(0,0) X (0,0) - 双序列:
(0,0) A (0,0) B (0,0)
4.4 预处理与持久化
prepare_for_tokenization(text, is_split_into_words=False, **kwargs)(tokenizer.py):当is_split_into_words=True或add_prefix_space=True时,在文本前补一个空格,这符合 SentencePiece 以▁标记词首的惯例;save_vocabulary(save_directory, filename_prefix=None)(tokenizer.py):委托给内部SPMTokenizer.save_pretrained,将.spm文件写入目标目录,可配合tokenizer.save_pretrained(...)完成整套保存;save_resources(save_directory)(tokenizer.py):按resource_files_names遍历,将sentencepiece_model_file复制到目标目录(源目标相同时跳过),保证资源文件随模型一并落盘。
五、sp_model_kwargs:SentencePiece 采样参数详解
DebertaV2Tokenizer与SPMTokenizer都将sp_model_kwargs原样透传给sentencepiece.SentencePieceProcessor(**sp_model_kwargs)(tokenizer.py)。官方文档明确支持的三个关键参数:
| 参数 | 作用 | 取值说明 |
|---|---|---|
enable_sampling | 开启子词正则化(subword regularization) | True/False |
nbest_size | unigram 采样参数(对 BPE-Dropout 无效) | 0或1:不采样,取最优切分;> 1:从 nbest_size 个候选中采样;< 0:视为无穷大,使用 forward-filtering-and-backward-sampling 算法在整张格(lattice)上采样 |
alpha | unigram 采样的平滑参数;对 BPE 而言是合并操作的 dropout 概率 | 浮点数,通常取0.1附近的小值 |
典型用法——推理时保持确定性切分,训练时启用子词正则化:
# 推理:确定性切分 tokenizer = DebertaV2Tokenizer("./spm.model") # 训练/数据增强:子词正则化 tokenizer = DebertaV2Tokenizer( "./spm.model", sp_model_kwargs={"enable_sampling": True, "nbest_size": -1, "alpha": 0.1}, )六、内部实现:SPMTokenizer 类
SPMTokenizer(tokenizer.py)是DebertaV2Tokenizer的底层引擎,负责与 SentencePiece C++ 处理器交互。理解它能解释许多"黑盒"行为。
6.1 词表加载与 id 布局
spm = sp.SentencePieceProcessor(**self.sp_model_kwargs) spm.load(vocab_file) bpe_vocab_size = spm.GetPieceSize() self.vocab = {spm.IdToPiece(i): i for i in range(bpe_vocab_size)} self.ids_to_tokens = [spm.IdToPiece(i) for i in range(bpe_vocab_size)]词表直接来自spm.IdToPiece。源码注释揭示了典型 SentencePiece 模型开头三个 piece 的约定:<unk>、<s>、</s>(分别对应 id 0、1、2)。测试 test_get_vocab 验证了tests/fixtures/spiece.model的键顺序:首项<pad>、次项<unk>、末项[PAD]。也就是说,[PAD]、[CLS]、[SEP]、[MASK]等 DeBERTa 特殊 token 是被追加到 SentencePiece 词表末尾的,而不是像 BERT 那样占据 id 0-4。
6.2 特殊 token 保护机制
decode(tokenizer.py)在还原文本时会跳过特殊 token——[MASK]、[CLS]等不会被 SentencePiece 的 piece 解码器处理,而是在普通片段解码结果后原样拼接,并在相邻特殊 token 之间插入空格。add_special_token则支持运行时向词表追加新特殊 token。
part_of_whole_word(token, is_bos=False)(tokenizer.py)用于判断 token 是否属于一个完整词的中间部分:以▁开头的 piece 表示新词起点,返回False;单个空白/控制/标点字符或特殊 token 也返回False。这对需要按词边界恢复整词的序列标注任务很有用。
6.3 标点切分与字符分类
_run_split_on_punc(tokenizer.py)将文本按标点拆成独立片段,标点自身保留;_is_punctuation将 ASCII 中所有非字母/数字字符(33–47、58–64、91–96、123–126 区间)以及 UnicodeP*类别视为标点;_is_whitespace与_is_control共同构成字符分类工具,其中\t、\n、\r被当作空白而非控制字符处理。
6.4 序列化支持
SPMTokenizer实现了__getstate__/__setstate__(tokenizer.py):pickle 序列化时把底层spm处理器置为None,反序列化时用sp.SentencePieceProcessor(**self.sp_model_kwargs)重新加载词表文件,并兼容旧版本缺少sp_model_kwargs属性的场景。这使得 tokenizer 可以安全地随训练脚本跨进程/跨设备传输。
七、测试验证:行为基准与注意事项
仓库在 test_tokenizer.py 中通过TokenizerTesterMixin基类 + 自定义用例对DebertaV2Tokenizer做了全面验证,可作为复现基准:
from_pretrained_kwargs = {"add_prefix_space": True},即从预训练加载时默认启用前缀空格;from_pretrained_vocab_key = "sentencepiece_model_file",与类属性resource_files_names对应;- 端到端测试 test_full_tokenizer:对
"This is a test"得到 ids[13, 1, 4398, 25, 21, 1289]、tokens["▁", "T", "his", "▁is", "▁a", "▁test"]。其中单字符"T"反向转换回<unk>(back_tokens_target中第 2 项),直观展示了 SentencePiece 词表粒度的行为差异。
需要特别注意的两点:
do_lower_case默认关闭:DeBERTa-v3 官方预训练即采用大小写敏感分词,这与 BERT 系do_lower_case=True的默认习惯相反,加载后请勿盲目套用小写化预处理;- 慢/快分词器一致性已知问题:测试中
test_sentencepiece_tokenize_and_convert_tokens_to_string与test_sentencepiece_tokenize_and_decode两个用例被unittest.skip跳过,源码注释明确说明"fast tokenizer 中存在一个 bug 导致与 slow tokenizer 不一致"。这意味着在部署 fast tokenizer 路径时,应针对实际语料做一致性回归验证。
八、实战:完整加载与编码示例
将上述内容串成一条可运行的完整流程(基于microsoft/deberta-v3-base):
from paddlenlp.transformers import AutoTokenizer # 1. 加载(自动下载 spm.model 词表) tokenizer = AutoTokenizer.from_pretrained("microsoft/deberta-v3-base") # 2. 单序列编码:自动拼装 [CLS] X [SEP] text = "DeBERTa improves the BERT design with disentangled attention." inputs = tokenizer(text, max_length=512, truncation=True) print(inputs["input_ids"][:10]) # 首 token 为 cls_token_id,末 token 为 sep_token_id # 3. 双序列编码(问答/文本对任务):[CLS] A [SEP] B [SEP] pair = tokenizer("What is DeBERTa?", "A model with disentangled attention.") print(pair["token_type_ids"][:5]) # 第一段为 0 # 4. 解码还原 print(tokenizer.decode(inputs["input_ids"])) # 5. 保存与复用 tokenizer.save_pretrained("./my_deberta_v3") reloaded = DebertaV2Tokenizer.from_pretrained("./my_deberta_v3")结语
DebertaV2Tokenizer是 PaddleNLP 中融合 SentencePiece 子词能力与 BERT 系特殊 token 约定的一类典型实现:它以.spm模型为唯一词表资源,通过SPMTokenizer完成 piece 编码、特殊 token 保护、标点切分与可序列化封装,并借助AutoTokenizer无缝接入 4 个官方预训练权重。理解其构造参数(do_lower_case、split_by_punct、sp_model_kwargs)与底层行为(▁词首标记、特殊 token 追加至词表末尾、采样式切分),是在 DeBERTa-v2/v3 上进行微调、推理与数据增强的前提。更多细节可继续阅读 configuration.py、modeling.py 以及 test_tokenizer.py 中的全部测试用例。
- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
相关推荐
PaddleNLP UnifiedTransformerTokenizer 源码级解析:基于 SentencePiece 的对话式分词器
PaddleNLP UnifiedTransformerTokenizer 源码级解析:基于 SentencePiece 的对话式分词器 本文围绕 Paddle
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLPQbot 银行股配对交易实战:协整检验、z-score 价差信号与 backtrader 多数据回测
Qbot 银行股配对交易实战:协整检验、z score 价差信号与 backtrader 多数据回测 本文基于 Qbot 仓库 量化三 配对交易 https:/
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLPLeetCode-Go 题解 | 1670. Design Front Middle Back Queue:用 container/list 与中位指针实现前中后队列
LeetCode Go 题解 | 1670. Design Front Middle Back Queue:用 container/list 与中位指针实现前中
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考