背景
在LLM处理中有很多关于tokenizer的方式,方便记忆理解,在此特意整理一下。
本次以为qwen/Qwen2-7B-Instruct
模型的tokenizer为例。
bert-base-chinese
模型略有不同,可以参考这里。
transformers 版本 2.1.1
加载tokenizer
from transformers import AutoTokenizer
model_path = "qwen/Qwen2-7B-Instruct"
# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False, trust_remote_code=True)
tokenizer
# Qwen2Tokenizer(name_or_path='qwen/Qwen2-7B-Instruct', vocab_size=151643, model_max_length=131072, is_fast=False, padding_side='right', truncation_side='right',
# special_tokens={'eos_token': '<|im_end|>', 'pad_token': '<|endoftext|>', 'additional_special_tokens': ['<|im_start|>', '<|im_end|>']},
# clean_up_tokenization_spaces=False), added_tokens_decoder={
# 151643: AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
# 151644: AddedToken("<|im_start|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
# 151645: AddedToken("<|im_end|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
# }
可以看到qwen/Qwen2-7B-Instruct
分词器的一些基础信息,比如词表大小vocab_size=151643,还有3个特殊符号151643、151644和151645等信息。
最基础encode和decode,将文本转化为词表的id或者id序列转化为可阅读的文本字符串。
text = "This is a test"
text_encode = tokenizer.encode(text)
print(text_encode)
# [1986, 374, 264, 1273]
text_decode = tokenizer.decode(text_encode)
print(text_decode)
# This is a test
构造LLM模型可以识别的输入格式,直接调用tokenizer
对象。
texts = ["This is a test", "Another test", "天气不错"]
tokenizer(texts)
# {'input_ids': [[1986, 374, 264, 1273], [14037, 1273], [104307, 100832]], 'attention_mask': [[1, 1, 1, 1], [1, 1], [1, 1]]}
# 一般来说会加上一些参数
# truncation=True时,超过max_length的文本会被截断
# padding=True时,文本会被填充到max_length,不足会填充0
# attention_mask 会根据input_ids生成 1表示真实有效,0表示填充字符
tokenizer(texts, truncation=True, padding=True, max_length=4)
# {'input_ids': [[1986, 374, 264, 1273], [14037, 1273, 151643, 151643], [104307, 100832, 151643, 151643]], 'attention_mask': [[1, 1, 1, 1], [1, 1, 0, 0], [1, 1, 0, 0]]}
apply_chat_template
函数可以构造带有提示词模式的模型输入。
一般来说,LLM中可以设置两种 Prompt:一种是 System Prompt,该种 Prompt 内容会在整个会话过程中持久地影响模型的回复,且相比于普通 Prompt 具有更高的重要性;另一种是 User Prompt,这更偏向于我们平时提到的 Prompt,即需要模型做出回复的输入。
prompt = "帮我实现一个快排算法"
messages = [{"role": "system", "content": "现在你是一个pyhon代码高手"}, {"role": "user", "content": prompt}]
# 构造LLM prrompt,tokenize=False不立即执行 tokenizer
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, return_tensors="pt")
print(text)
# <|im_start|>system
# 现在你是一个pyhon代码高手<|im_end|>
# <|im_start|>user
# 帮我实现一个快排算法<|im_end|>
# <|im_start|>assistant
# 转为为LLM可识别的数据格式
model_inputs = tokenizer(text, truncation=True, padding=True, max_length=512, return_tensors="pt")
结果如下:
{'input_ids': tensor([[151644, 8948, 198, 99601, 56568, 101909, 3288, 81549, 46100,
105862, 151645, 198, 151644, 872, 198, 108965, 101884, 46944,
99234, 59956, 107018, 151645, 198, 151644, 77091, 198]]),
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1]])
}
将model_inputs 结果重新转化为文本如下:
tokenizer.decode(model_inputs["input_ids"][0])
# '<|im_start|>system\n现在你是一个pyhon代码高手<|im_end|>\n<|im_start|>user\n帮我实现一个快排算法<|im_end|>\n<|im_start|>assistant\n'