文章目录
- 文本生成
- 默认文本生成
- 更改文本生成配置
- 保存文本生成配置
- 通用参数
- 常见问题
- 输出长度
- 解码策略
- 填充
- 提示格式
Transformers是一个采用当下技术最新、表现最佳(State-of-the-art,SoTA)的模型和技术在预训练自然语言处理、计算机视觉、音频和多模态模型方面提供推理和训练的的开源库;旨在快速易用,以便每个人都可以开始使用transformer模型进行学习或构建。该库不仅包含Transformer模型,还包括用于计算机视觉任务的现代卷积网络等非Transformer模型。文本生成
文本生成(Text generation)是大型语言模型(Large Language Models,LLMs)最流行的应用。LLMs从给定一些初始文本(提示)从训练生成下一个词(token),并自动生成输出,直到预定义的长度或碰到序列结束符(end-of-sequence,EOS)。
在Transformers中, [~GenerationMixin.generate] API 处理文本生成,可用于所有具有文本生成功能的模型。
默认文本生成
Bitsandbytes是一个大数据模型,通过使用基于CUDA的 GPU,可以支持多种量化器后端。下面我们使用大数据模型[bitsandbytes]在默认配置下情况下生成文本作为案例。首先安装大数据模型:
!pip install -U transformers bitsandbytes通过 [~PreTrainedModel.from_pretrained] 加载大数据模型,并配置以下两个参数来降低对内存的需求:
device_map="auto":启用使用 [Big Model Inference]自动初始化模型框架,并在所有可用设备上加载和调度模型权重到最快的设备(GPU)上。quantization_config:是一个定义量化器的配置对象。这个例子使用bitsandbytes作为量化后端,加载4B模型。
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig quantization_config = BitsAndBytesConfig(load_in_4bit=True) model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", device_map="auto", quantization_config=quantization_config)标记输入,设置 [~PreTrainedTokenizer.padding_side] 为left,因为LLM没有被训练继续从填充token生成。
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1", padding_side="left") model_inputs = tokenizer(["A list of colors: red, blue"], return_tensors="pt").to("cuda")标记器返回包括tokenid和attention掩码。通过 [~GenerationMixin.generate] 将返回结果传入并生成token序列;最后通过 [~PreTrainedTokenizer.batch_decode] 将token序列转化为文本输出:
generated_ids = model.generate(**model_inputs) tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]结果:
"A list of colors: red, blue, green, yellow, orange, purple, pink,"更改文本生成配置
[GenerationConfig]包含了所有的生成设置。上例中, 文本生成设置是根据 [mistralai/Mistral-7B-v0.1] 模型下的generation_config.json文件派生出来的。当配置没有与模型一起保存时,将使用默认解码策略。
通过generation_config属性来检查配置。它只展示与默认配置不同的值,本例中为bos_token_id和eos_token_id。
from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", device_map="auto") model.generation_config GenerationConfig { "bos_token_id": 1, "eos_token_id": 2 }也可以通过设置 [~GenerationMixin.generate] 方法的输入参数来覆盖 [GenerationConfig]中已有的配置参数值。max_new_tokens,num_beams,do_sample, 和num_return_sequences这几个是最常调整的参数。
# enable beam search sampling strategy model.generate(**inputs, num_beams=4, do_sample=True)[~GenerationMixin.generate]也可以使用外部库或自定义代码进行扩展。logits_processor参数接受自定义 [LogitsProcessor]实例来计算下一个token概率分布;stopping_criteria参数接受自定义[StoppingCriteria] 实例来停止文本生成。
保存文本生成配置
下面代码演示通过创建 [GenerationConfig] 实例时传入解码参数来覆盖 [GenerationConfig]对应的既有参数值并保存:
from transformers import AutoModelForCausalLM, GenerationConfig model = AutoModelForCausalLM.from_pretrained("my_account/my_model") generation_config = GenerationConfig( max_new_tokens=50, do_sample=True, top_k=50, eos_token_id=model.config.eos_token_id )使用 [~GenerationConfig.save_pretrained]方法保存已修改的配置参数值到配置文件中,并通过设置传入参数push_to_hub为True将配置自动上传到Hub中:
generation_config.save_pretrained("my_account/my_model", push_to_hub=True)config_file_name参数用来设置配置文件名,当在单个目录中存储多个生成配置时,使用该参指定要加载哪个生成配置的方法。这样,可以为不同的生成任务(如带有采样的创造性文本生成,带有光束搜索的摘要等)使用不同的配置,以便与单个模型一起使用。
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer, GenerationConfig tokenizer = AutoTokenizer.from_pretrained("google-t5/t5-small") model = AutoModelForSeq2SeqLM.from_pretrained("google-t5/t5-small") translation_generation_config = GenerationConfig( num_beams=4, early_stopping=True, decoder_start_token_id=0, eos_token_id=model.config.eos_token_id, pad_token=model.config.pad_token_id, ) translation_generation_config.save_pretrained("/tmp", config_file_name="translation_generation_config.json", push_to_hub=True) generation_config = GenerationConfig.from_pretrained("/tmp", config_file_name="translation_generation_config.json") inputs = tokenizer("translate English to French: Configuration files are easy to use!", return_tensors="pt") outputs = model.generate(**inputs, generation_config=generation_config) print(tokenizer.batch_decode(outputs, skip_special_tokens=True))通用参数
[~GenerationMixin.generate] 是一个功能强大的工具,可以进行大量定制。在Transformers中,大多数文本生成工具中都可以自定定义这些参数如[~GenerationMixin.generate], [GenerationConfig],pipelines等。
| 参数名 | 类型 | 描述 |
|---|---|---|
max_new_tokens | int | 控制token的最大生成数量。一般默认值为定义的很小,所以最好设置整个参数。 |
do_sample | bool | 该参数设置采样的方式,如果参数值为True,表示生成时会采样下一个token;否则为贪婪式采样。 大多数用例应该将这个标志设置为“True”。 |
temperature | float | 高值(“>0.8”)适用于创造性任务,低值(如“<0.4”)适用于需要“思考”的任务。如果需要该值有效,需设置do_sample = True。 |
num_beams | int | 如果设置值大于1,则会激活并使用波束搜索算法。 |
repetition_penalty | float | 如果模型重复率太高,可将该参数值设置大于 `1.0。值越大,惩罚越大。 |
eos_token_id | List[int] | 设置结束符标识。通常情况下,使用默认值即可;如果需要,可以通过该参数指定为其他值。 |
常见问题
在文本生成过程中可能遇到的一些常见问题,这些问题可能是下面其中之一。
输出长度
[~GenerationMixin.generate] 默认返回最多20个token,除非在 [GenerationConfig]中另有指定。强烈建议使用 [max_new_tokens] 参数手动设置生成的token数量,以控制输出长度。
model_inputs = tokenizer(["A sequence of numbers: 1, 2"], return_tensors="pt").to("cuda")下面代码使用默认的token:
generated_ids = model.generate(**model_inputs) tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] 'A sequence of numbers: 1, 2, 3, 4, 5'下面代码通过max_new_tokens手工设置:
generated_ids = model.generate(**model_inputs, max_new_tokens=50) tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] 'A sequence of numbers: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,'解码策略
[~GenerationMixin.generate] 中使用的是默认解码策略即贪婪搜索算法(greedy search)。虽然这种解码策略适用于基于输入的任务(转录、翻译),但对于更具创造性的用例(故事写作、聊天应用程序)却不是最佳选择。如果需要其他策略,需通过在 [GenerationConfig]中另有指定。
model_inputs = tokenizer(["I am a cat."], return_tensors="pt").to("cuda") generated_ids = model.generate(**model_inputs) tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]例如,启用 [multinomial sampling] 策略来生成更多样化的输出。参考[生成策略](./generation_strategies)指南了解更多解码策略:
model_inputs = tokenizer(["I am a cat."], return_tensors="pt").to("cuda") generated_ids = model.generate(**model_inputs, do_sample=True) tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]填充
如果输入序列的长度不相同,则需要填充。但是LLMs并没有被训练来继续填充token生成,这意味着 [~PreTrainedTokenizer.padding_side] 参数需要设置填充为left或right。以下代码设置为右侧:
model_inputs = tokenizer( ["1, 2, 3", "A, B, C, D, E"], padding=True, return_tensors="pt" ).to("cuda") generated_ids = model.generate(**model_inputs) tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]结果:
'1, 2, 33333333333'以下代码设置为左侧:
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1", padding_side="left") tokenizer.pad_token = tokenizer.eos_token model_inputs = tokenizer( ["1, 2, 3", "A, B, C, D, E"], padding=True, return_tensors="pt" ).to("cuda") generated_ids = model.generate(**model_inputs) tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]结果:
'1, 2, 3, 4, 5, 6,'提示格式
一些模型和任务需要特定的输入提示格式,如果格式不正确,模型则会返回次优输出。
例如,聊天模型期望输入为[聊天模板]。你的提示应该包括 “角色” 和 “内容”,以表明谁参与了对话。如果您尝试将提示作为单个字符串传递,则模型并不一定返回预期的输出。
from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-alpha") model = AutoModelForCausalLM.from_pretrained( "HuggingFaceH4/zephyr-7b-alpha", device_map="auto", load_in_4bit=True )以下是将提示作为单一字符串传入的案例,该方式会导致返回不一定是预期的输出:
prompt = """How many cats does it take to change a light bulb? Reply as a pirate.""" model_inputs = tokenizer([prompt], return_tensors="pt").to("cuda") input_length = model_inputs.input_ids.shape[1] generated_ids = model.generate(**model_inputs, max_new_tokens=50) print(tokenizer.batch_decode(generated_ids[:, input_length:], skip_special_tokens=True)[0])结果:
"Aye, matey! 'Tis a simple task for a cat with a keen eye and nimble paws. First, the cat will climb up the ladder, carefully avoiding the rickety rungs. Then, with"下面案例将提示格式化后传入:
messages = [ { "role": "system", "content": "You are a friendly chatbot who always responds in the style of a pirate", }, {"role": "user", "content": "How many cats does it take to change a light bulb?"}, ] model_inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to("cuda") input_length = model_inputs.shape[1] generated_ids = model.generate(model_inputs, do_sample=True, max_new_tokens=50) print(tokenizer.batch_decode(generated_ids[:, input_length:], skip_special_tokens=True)[0])结果:
"Arr, matey! According to me beliefs, 'twas always one cat to hold the ladder and another to climb up it an’ change the light bulb, but if yer looking to save some catnip, maybe yer can