问题背景
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("Llama-3.2") # 替换实际模型路径
model = AutoModelForCausalLM.from_pretrained("Llama-3.2") # 替换实际模型路径
text = "禁止为出售、购买、利用野生动物或者禁止使用的猎捕工具发布广告。将这段话生成一个关键词"
#使用分词器将文本和前缀转换为输入ID和注意力掩码
input_ids = tokenizer.encode(text, return_tensors="pt", padding=True)
attention_mask = input_ids != tokenizer.pad_token_id
# 使用模型生成摘要输出ID
output_ids = model.generate(input_ids, max_new_tokens=256, attention_mask=attention_mask, pad_token_id=tokenizer.pad_token_id)
# 使用分词器将摘要输出ID转换为文本
summary = tokenizer.decode(output_ids[0], skip_special_tokens=True)
执行上述代码将报错:
ValueError: Asking to pad but the tokenizer does not have a padding token. Please select a token to use as `pad_token` `(tokenizer.pad_token = tokenizer.eos_token e.g.)` or add a new pad token via `tokenizer.add_special_tokens({'pad_token': '[PAD]'})`
问题分析
报错提示的比较明显了意思就是我们想在分词器进行填充,但是我们没有告诉分词器用什么填充。甚至将如何操作已经提示在报错中了。
在自然语言处理(NLP)任务中,我们经常需要对一批输入进行处理,例如文本分类、情感分析、机器翻译等。为了便于模型的处理,我们通常需要保证每个输入具有相同的长度。然而,实际的输入文本长度可能不一致。因此,为了使输入具有固定长度,我们会选择一种方式对输入进行填充。
在使用PyTorch TRANSFORMERS库时,我们通常会首先使用tokenizer对输入进行分词。然而,有些情况下,我们希望对输入进行填充,但是tokenizer并没有提供专门的填充标记。
第一种方案:tokenizer.pad_token = tokenizer.eos_token
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("Llama-3.2") # 替换实际模型路径
model = AutoModelForCausalLM.from_pretrained("Llama-3.2") # 替换实际模型路径
text = "禁止为出售、购买、利用野生动物或者禁止使用的猎捕工具发布广告。将这段话生成一个关键词"
# 在输入文本到分词器前执行该行代码
tokenizer.pad_token = tokenizer.eos_token
#使用分词器将文本和前缀转换为输入ID和注意力掩码
input_ids = tokenizer.encode(text, return_tensors="pt", padding=True)
attention_mask = input_ids != tokenizer.pad_token_id
# 使用模型生成摘要输出ID
output_ids = model.generate(input_ids, max_new_tokens=256, attention_mask=attention_mask, pad_token_id=tokenizer.pad_token_id)
# 使用分词器将摘要输出ID转换为文本
summary = tokenizer.decode(output_ids[0], skip_special_tokens=True)
tokenizer.pad_token = tokenizer.eos_token
这行代码的意思是将分词器(tokenizer)的填充令牌(pad token)设置为等于它的结束序列令牌(eos token)。
通过设置tokenizer.pad_token = tokenizer.eos_token
,实际上是在告诉分词器,当需要对序列进行填充时,使用EOS令牌作为填充令牌。这在某些情况下可能是有用的,比如希望模型在处理填充部分时能够识别出序列的实际结束。
**第二种方案:**add a new pad token via tokenizer.add_special_tokens({'pad_token': '[PAD]'})
相当于使用了默认的特殊字符来充当填充物
'[PAD]'
是自定义的填充符号,在这段代码中被定义为 pad_token
。这个填充符号通常是一个字符串,可以是任意定义的标记符号,但在很多情况下,像 [PAD]
这样的符号是一个通用的约定。
[PAD]
:用于表示被填充的 token,它并不代表有意义的语言内容,而仅仅用于让模型在处理不同长度的输入时,忽略这些填充的部分。
添加的特殊字符有什么用?
在问答训练时会添加特殊字符框架协同训练,经过微调模型就知道这样的框架是在做问答,在推理时,只要将文本加入特殊字符组成的问答框架就可以进行回答。
同时,使用特殊字符,也能帮助模型训练时区分每一个句子间的层次关系。
总结
为什么有时候分词器会要求设置tokenizer.pad_token = tokenizer.eos_token?
这样处理往往是针对batch操作的,generate是允许batch处理,因此无论如何必须先设置好pad_token
pad_token
的作用
pad_token
是在自然语言处理任务中用于填充(padding)输入序列的特殊符号。在许多情况下,模型的输入需要具有相同的长度,因此需要对较短的序列进行填充。pad_token
的作用就是在输入序列不足时,补充这个填充符号以达到相同长度,从而使模型能够并行处理多个输入。