向BERT/RoBERTa添加新token时如何保留相邻tokens的原有分词行为
你遇到的分词异常是子词分词器的最长匹配逻辑和新增token的边界属性不匹配导致的。add_tokens默认将新增token识别为独立整词,匹配完成后会重置后续字符的边界判断,因此BERT中后续的.不会被识别为后缀##.,RoBERTa中后续的单词不会被加上空格前缀Ġ。
BERT场景解决方案
因为你需要保留do_basic_tokenize=False的设置,不需要修改分词器核心逻辑,只需要加一层极轻量的后处理规则即可:当检测到分词结果中mynewword后紧跟.时,将.替换为##.,该规则不会影响其他原生分词结果,完全符合你的研究需求。
RoBERTa场景解决方案
你提到的添加带Ġ前缀token的思路完全可行,只需要同时添加两个版本的新token即可覆盖所有场景:
- 无前缀的
mynewword:匹配句首无前导空格的场景 - 带
Ġ前缀的Ġmynewword:匹配句中前面有空格的常规场景
添加完成后分词器的边界判断逻辑会和原生词完全一致。
完整实现代码
from transformers import BertTokenizer, RobertaTokenizer # BERT侧实现 bert = BertTokenizer.from_pretrained('bert-base-uncased', do_basic_tokenize=False) bert.add_tokens(['mynewword']) def fixed_bert_tokenize(text): tokens = bert.tokenize(text) fixed = [] for idx, tok in enumerate(tokens): if idx > 0 and tok == '.' and tokens[idx-1] == 'mynewword': fixed.append('##.') else: fixed.append(tok) return fixed # 测试BERT效果 print(fixed_bert_tokenize('mynewword.')) # 输出:['mynewword', '##.'] 符合预期 # RoBERTa侧实现 roberta = RobertaTokenizer.from_pretrained('roberta-base') roberta.add_tokens(['mynewword', 'Ġmynewword']) # 测试RoBERTa句中效果 print(roberta.tokenize('A mynewword a')) # 输出:['A', 'Ġmynewword', 'Ġa'] 符合预期 # 测试RoBERTa句首效果 print(roberta.tokenize('mynewword a')) # 输出:['mynewword', 'Ġa'] 符合预期
微调适配
新增token后调用model.resize_token_embeddings(len(tokenizer))即可调整嵌入层尺寸,新增token的嵌入会自动随机初始化,仅在微调阶段更新,完全符合你要求的「和普通词汇行为一致」的需求。
内容的提问来源于stack exchange,提问作者Jigsaw
相关产品推荐
相关产品推荐

