按频率重排GPT2Tokenizer Token ID引发未识别Token问题排查
我尝试通过按频率重排现有GPT2Tokenizer的Token ID创建新分词器,理论上Token ID顺序不影响性能和可用性,但实际出现部分Token无法识别的情况。因特定需求必须采用该方式,附上相关代码及输出,询问问题成因。
原分词器测试代码及输出
top_amt = 10000 old_tokenizer = GPT2Tokenizer.from_pretrained("EleutherAI/gpt-neo-125M") inputs = old_tokenizer("Hello, my dog is cute", return_tensors="pt") print("input ids: ", inputs['input_ids'])
输出:
input ids: tensor([[15496, 11, 616, 3290, 318, 13779]])
词频统计与Token重排代码及输出
# 统计词频 tokens_to_id = dict(list(tokenizer.get_vocab().items())) # 获取频率 frequencies = np.zeros(len(tokens_to_id)) for i, sample in enumerate(tokenized_val['input_ids']): for token in sample: frequencies[token] += 1 # 排序 sorted_indices = np.argsort(frequencies)[::-1] #toptenk = sorted_indices[:top_amt] print("Top 10 tokens: ", tokenizer.decode(toptenk[:10])) # 去重并重分配ID top_tokens = {tokenizer.decode([sorted_indices[i]]):i for i in range(len(sorted_indices))} top_keys = list(top_tokens.keys()) top_tokens = {top_keys[i]: i for i in range(len(top_keys))} print("Occurrences of least used token: ", top_keys[top_amt], frequencies[top_tokens[top_keys[top_amt]]])
输出:
Top 10 tokens: <|endoftext|>. and the, to a was it Occurrences of least used token: Often 477.0
新分词器创建与测试代码及输出
vocab_file_path = 'reduced_vocab.json' with open(vocab_file_path, 'w') as f: f.write(json.dumps(top_tokens)) print(f"Reduced vocabulary file saved to {vocab_file_path}") new_tokenizer = GPT2Tokenizer(vocab_file=vocab_file_path, merges_file="merges.txt", pad_token='<|endoftext|>') new_tokenizer.save_pretrained("TinyStoriesTokenizer") print("Tokenizer saved.") inputs = new_tokenizer("Hello, my dog is cute", return_tensors="pt") print("input ids: ", inputs['input_ids'])
输出:
Reduced vocabulary file saved to reduced_vocab.json Tokenizer saved. input ids: tensor([[564, 5, 0, 0, 0, 0]])
问题成因分析
1. BPE合并规则与新词汇表不匹配
GPT2系列分词器基于Byte-Pair Encoding(BPE),其merges.txt文件记录了BPE的合并步骤,这些步骤是和原分词器的Token ID直接绑定的。当你重排Token ID后,原merges.txt中的合并规则指向的是旧ID,与新词汇表的ID完全不对应,导致分词器无法按照正确的路径合并出"my"、"dog"这类Token,只能尝试拆分到更低层级的单元,甚至无法匹配到有效Token。
2. 词汇表构建时的去重操作导致Token丢失
你在构建top_tokens时,先通过{tokenizer.decode([sorted_indices[i]]):i}生成字典,再通过{top_keys[i]: i}去重。但部分Token解码后可能出现重复的字符串(比如某些控制字符、特殊编码的Token),去重会直接覆盖掉重复键对应的ID,导致这些Token在新词汇表中丢失。当分词时遇到对应序列,无法找到匹配的Token,最终输出0(GPT2Tokenizer默认无unk_token,会将无法匹配的内容映射到无效ID或默认值)。
3. 特殊Token的处理冲突
你将<|endoftext|>设为pad_token,但在重排Token ID时,该Token的ID被改变,而原merges.txt中可能包含与该特殊Token相关的合并规则,进一步加剧了分词逻辑的混乱,导致部分常规Token的合并路径完全失效。
内容的提问来源于stack exchange,提问作者Cade Harger

