BERT分词器在命名实体识别任务中的标点处理问题
解决BERT分词器拆分带点缩写人名的问题
BERT默认的WordPiece分词会把P.这类带点缩写拆成['p', '.'],和你训练数据里的完整P.token不匹配,直接影响NER模型识别这类实体。下面是几种实用的解决办法:
方法1:给分词器新增自定义token
把任务中常见的带点缩写加入分词器的词汇表,再调整模型embedding层适配新token,之后重新微调模型即可。
from transformers import BertTokenizer, BertModel path_pretrained_model='/model/bert/' tokenizer = BertTokenizer.from_pretrained(path_pretrained_model) # 加入你NER任务里常见的带点缩写,比如P.、M.等 new_abbr_tokens = ["p.", "P.", "m.", "M."] tokenizer.add_tokens(new_abbr_tokens) # 加载模型并调整embedding层大小,适配新token model = BertModel.from_pretrained(path_pretrained_model) model.resize_token_embeddings(len(tokenizer)) # 测试效果 print(tokenizer.tokenize("P. Adam is a scientist.")) # 输出: ['P.', 'adam', 'is', 'a', 'scientist', '.']
方法2:用never_split强制保留特定字符串
初始化分词器时,指定哪些字符串不允许被拆分,适合缩写范围固定的场景,不需要重新训练模型。
from transformers import BertTokenizer path_pretrained_model='/model/bert/' tokenizer = BertTokenizer.from_pretrained( path_pretrained_model, never_split=["p.", "P.", "m.", "M."] # 注意大小写,根据你的分词器是否大小写敏感调整 ) print(tokenizer.tokenize("P. Adam is a scientist.")) # 输出: ['P.', 'adam', 'is', 'a', 'scientist', '.']
方法3:预处理文本替换缩写(无需修改模型)
用正则把带点缩写替换成临时占位符,分词后再还原,适合缩写种类多、无法枚举的场景。
import re from transformers import BertTokenizer path_pretrained_model='/model/bert/' tokenizer = BertTokenizer.from_pretrained(path_pretrained_model) def preprocess_abbr(text): # 匹配单个字母+句号的缩写(比如P.、m.) return re.sub(r'\b([A-Za-z])\.', r'\1_DOT_', text) def restore_abbr(tokens): # 把占位符还原成带点形式 return [tok.replace('_DOT_', '.') if '_DOT_' in tok else tok for tok in tokens] # 测试流程 raw_text = "P. Adam is a scientist." processed_text = preprocess_abbr(raw_text) tokens = tokenizer.tokenize(processed_text) final_tokens = restore_abbr(tokens) print(final_tokens) # 输出: ['p.', 'adam', 'is', 'a', 'scientist', '.']
补充说明
SpaCy分词不会出现这个问题,是因为它的分词逻辑更偏向规则和统计层面的完整token保留,而BERT的WordPiece算法优先拆分不在预训练词汇表中的token。所以针对NER任务里的这类特定实体,必须保证输入token和训练数据的一致性,才能让模型有效识别。
内容的提问来源于stack exchange,提问作者Questioner
相关产品推荐
相关产品推荐

