Transformers 4.37 中文文档(六)(4)https://developer.aliyun.com/article/1564082
提示的基础知识
模型类型
现代 LLM 大多数是仅解码器的变压器。一些例子包括:LLaMA, Llama2, Falcon, GPT2。但是,您也可能遇到编码器-解码器变压器 LLM,例如 Flan-T5 和 BART。
编码器-解码器风格的模型通常用于生成任务,其中输出严重依赖于输入,例如翻译和总结。解码器模型用于所有其他类型的生成任务。
在使用管道生成 LLM 文本时,了解您正在使用的 LLM 类型很重要,因为它们使用不同的管道。
使用text-generation
管道运行仅解码器模型的推理:
>>> from transformers import pipeline >>> import torch >>> torch.manual_seed(0) >>> generator = pipeline('text-generation', model = 'gpt2') >>> prompt = "Hello, I'm a language model" >>> generator(prompt, max_length = 30) [{'generated_text': "Hello, I'm a language model expert, so I'm a big believer in the concept that I know very well and then I try to look into"}]
要使用编码器-解码器进行推理,请使用text2text-generation
管道:
>>> text2text_generator = pipeline("text2text-generation", model = 'google/flan-t5-base') >>> prompt = "Translate from English to French: I'm very happy to see you" >>> text2text_generator(prompt) [{'generated_text': 'Je suis très heureuse de vous rencontrer.'}]
基础版 vs 指导/聊天版模型
🤗 Hub 上提供的大多数最新 LLM 检查点都有两个版本:基础版和指导版(或聊天版)。例如,tiiuae/falcon-7b
和 tiiuae/falcon-7b-instruct
。
基础模型在给定初始提示时完成文本的能力非常出色,但是它们并不适合需要遵循指令或用于对话的 NLP 任务。这就是指导(聊天)版本的用武之地。这些检查点是在预训练基础版本上进一步微调指令和对话数据的结果。这种额外的微调使它们成为许多 NLP 任务的更好选择。
让我们举例说明一些简单的提示,您可以使用tiiuae/falcon-7b-instruct
来解决一些常见的 NLP 任务。
自然语言处理任务
首先,让我们设置环境:
pip install -q transformers accelerate
接下来,让我们使用适当的管道("text-generation"
)加载模型:
>>> from transformers import pipeline, AutoTokenizer >>> import torch >>> torch.manual_seed(0) >>> model = "tiiuae/falcon-7b-instruct" >>> tokenizer = AutoTokenizer.from_pretrained(model) >>> pipe = pipeline( ... "text-generation", ... model=model, ... tokenizer=tokenizer, ... torch_dtype=torch.bfloat16, ... device_map="auto", ... )
请注意,Falcon 模型是使用bfloat16
数据类型训练的,因此我们建议您也使用相同的数据类型。这需要一个最新版本的 CUDA,并且在现代显卡上效果最佳。
现在我们已经通过管道加载了模型,让我们探讨如何使用提示来解决 NLP 任务。
文本分类
文本分类中最常见的形式之一是情感分析,它为一段文本分配一个标签,比如“积极”、“消极”或“中性”。让我们编写一个提示,指示模型对给定的文本(电影评论)进行分类。我们将从给出指令开始,然后指定要分类的文本。请注意,我们不仅仅止步于此,还添加了响应的开头 - "情感:"
:
>>> torch.manual_seed(0) >>> prompt = """Classify the text into neutral, negative or positive. ... Text: This movie is definitely one of my favorite movies of its kind. The interaction between respectable and morally strong characters is an ode to chivalry and the honor code amongst thieves and policemen. ... Sentiment: ... """ >>> sequences = pipe( ... prompt, ... max_new_tokens=10, ... ) >>> for seq in sequences: ... print(f"Result: {seq['generated_text']}") Result: Classify the text into neutral, negative or positive. Text: This movie is definitely one of my favorite movies of its kind. The interaction between respectable and morally strong characters is an ode to chivalry and the honor code amongst thieves and policemen. Sentiment: Positive
因此,输出包含了我们在指令中提供的列表中的一个分类标签,而且是正确的!
您可能注意到,除了提示之外,我们还传递了一个max_new_tokens
参数。它控制模型应该生成的标记数量,这是您可以在文本生成策略指南中了解的许多文本生成参数之一。
命名实体识别
命名实体识别(NER)是在文本中找到命名实体的任务,比如人物、地点或组织。让我们修改提示中的指令,让 LLM 执行这个任务。在这里,我们还设置return_full_text = False
,这样输出就不包含提示了:
>>> torch.manual_seed(1) >>> prompt = """Return a list of named entities in the text. ... Text: The Golden State Warriors are an American professional basketball team based in San Francisco. ... Named entities: ... """ >>> sequences = pipe( ... prompt, ... max_new_tokens=15, ... return_full_text = False, ... ) >>> for seq in sequences: ... print(f"{seq['generated_text']}") - Golden State Warriors - San Francisco
正如您所看到的,模型正确识别了给定文本中的两个命名实体。
翻译
LLM 可以执行的另一个任务是翻译。您可以选择使用编码器-解码器模型来执行此任务,但是在这里,为了简化示例,我们将继续使用 Falcon-7b-instruct,它做得相当不错。再次,这是您如何编写一个基本提示,指示模型将一段文本从英语翻译成意大利语:
>>> torch.manual_seed(2) >>> prompt = """Translate the English text to Italian. ... Text: Sometimes, I've believed as many as six impossible things before breakfast. ... Translation: ... """ >>> sequences = pipe( ... prompt, ... max_new_tokens=20, ... do_sample=True, ... top_k=10, ... return_full_text = False, ... ) >>> for seq in sequences: ... print(f"{seq['generated_text']}") A volte, ho creduto a sei impossibili cose prima di colazione.
在这里,我们添加了do_sample=True
和top_k=10
,以允许模型在生成输出时更加灵活。
文本摘要
与翻译类似,文本摘要是另一个生成任务,输出严重依赖于输入,编码器-解码器模型可能是更好的选择。然而,解码器风格的模型也可以用于这个任务。以前,我们将指令放在提示的开头。然而,提示的最后也可以是一个合适的位置来放置指令。通常,最好将指令放在两端之一。
>>> torch.manual_seed(3) >>> prompt = """Permaculture is a design process mimicking the diversity, functionality and resilience of natural ecosystems. The principles and practices are drawn from traditional ecological knowledge of indigenous cultures combined with modern scientific understanding and technological innovations. Permaculture design provides a framework helping individuals and communities develop innovative, creative and effective strategies for meeting basic needs while preparing for and mitigating the projected impacts of climate change. ... Write a summary of the above text. ... Summary: ... """ >>> sequences = pipe( ... prompt, ... max_new_tokens=30, ... do_sample=True, ... top_k=10, ... return_full_text = False, ... ) >>> for seq in sequences: ... print(f"{seq['generated_text']}") Permaculture is an ecological design mimicking natural ecosystems to meet basic needs and prepare for climate change. It is based on traditional knowledge and scientific understanding.
问答
对于问答任务,我们可以将提示结构化为以下逻辑组件:指令、上下文、问题和引导词或短语("Answer:"
),以促使模型开始生成答案:
>>> torch.manual_seed(4) >>> prompt = """Answer the question using the context below. ... Context: Gazpacho is a cold soup and drink made of raw, blended vegetables. Most gazpacho includes stale bread, tomato, cucumbers, onion, bell peppers, garlic, olive oil, wine vinegar, water, and salt. Northern recipes often include cumin and/or pimentón (smoked sweet paprika). Traditionally, gazpacho was made by pounding the vegetables in a mortar with a pestle; this more laborious method is still sometimes used as it helps keep the gazpacho cool and avoids the foam and silky consistency of smoothie versions made in blenders or food processors. ... Question: What modern tool is used to make gazpacho? ... Answer: ... """ >>> sequences = pipe( ... prompt, ... max_new_tokens=10, ... do_sample=True, ... top_k=10, ... return_full_text = False, ... ) >>> for seq in sequences: ... print(f"Result: {seq['generated_text']}") Result: Modern tools are used, such as immersion blenders
推理
推理是 LLM 中最困难的任务之一,要取得良好的结果通常需要应用高级提示技术,比如思维链。
让我们尝试看看我们是否可以让模型通过一个基本提示来推理一个简单的算术任务:
>>> torch.manual_seed(5) >>> prompt = """There are 5 groups of students in the class. Each group has 4 students. How many students are there in the class?""" >>> sequences = pipe( ... prompt, ... max_new_tokens=30, ... do_sample=True, ... top_k=10, ... return_full_text = False, ... ) >>> for seq in sequences: ... print(f"Result: {seq['generated_text']}") Result: There are a total of 5 groups, so there are 5 x 4=20 students in the class.
正确!让我们稍微增加一点复杂性,看看我们是否仍然可以通过一个基本提示来完成:
>>> torch.manual_seed(6) >>> prompt = """I baked 15 muffins. I ate 2 muffins and gave 5 muffins to a neighbor. My partner then bought 6 more muffins and ate 2\. How many muffins do we now have?""" >>> sequences = pipe( ... prompt, ... max_new_tokens=10, ... do_sample=True, ... top_k=10, ... return_full_text = False, ... ) >>> for seq in sequences: ... print(f"Result: {seq['generated_text']}") Result: The total number of muffins now is 21
这是一个错误答案,应该是 12。在这种情况下,这可能是因为提示过于基础,或者是因为模型选择不当,毕竟我们选择了 Falcon 的最小版本。对于所有大小的模型来说,推理都是困难的,但更大的模型可能表现更好。
LLM 提示的最佳实践
在本指南的这一部分中,我们编制了一份倾向于改善提示结果的最佳实践清单:
- 在选择要使用的模型时,最新和最有能力的模型可能表现更好。
- 从一个简单而短的提示开始,然后逐步迭代。
- 将指令放在提示的开头或最后。在处理大量上下文时,模型会应用各种优化措施,以防止注意力复杂度呈二次方增长。这可能会使模型更加关注提示的开头或结尾,而不是中间部分。
- 将指令与其适用的文本清晰分开-更多内容请参见下一节。
- 对任务和期望结果进行具体和描述性的说明-其格式、长度、风格、语言等。
- 避免模棱两可的描述和指令。
- 更倾向于说“要做什么”而不是说“不要做什么”的指令。
- 通过编写第一个单词(甚至开始第一个句子)来“引导”输出朝着正确方向发展。
- 使用高级技术,如少样本提示和思维链
- 使用不同模型测试您的提示,以评估其稳健性。
- 版本和跟踪提示的性能。
高级提示技术
少样本提示
上述部分的基本提示是“零样本”提示的示例,这意味着模型已经获得了指令和上下文,但没有带有解决方案的示例。通常在指令数据集上进行微调的 LLM 在这种“零样本”任务上表现良好。然而,您可能会发现您的任务更加复杂或微妙,也许您对模型没有从指令中捕捉到的输出有一些要求。在这种情况下,您可以尝试称为少样本提示的技术。
在少样本提示中,我们在提示中提供示例,为模型提供更多上下文以提高性能。这些示例会让模型生成遵循示例模式的输出。
这里有一个例子:
>>> torch.manual_seed(0) >>> prompt = """Text: The first human went into space and orbited the Earth on April 12, 1961. ... Date: 04/12/1961 ... Text: The first-ever televised presidential debate in the United States took place on September 28, 1960, between presidential candidates John F. Kennedy and Richard Nixon. ... Date:""" >>> sequences = pipe( ... prompt, ... max_new_tokens=8, ... do_sample=True, ... top_k=10, ... ) >>> for seq in sequences: ... print(f"Result: {seq['generated_text']}") Result: Text: The first human went into space and orbited the Earth on April 12, 1961. Date: 04/12/1961 Text: The first-ever televised presidential debate in the United States took place on September 28, 1960, between presidential candidates John F. Kennedy and Richard Nixon. Date: 09/28/1960
在上面的代码片段中,我们使用了一个示例来向模型展示所需的输出,因此这可以称为“一次性”提示。然而,根据任务的复杂性,您可能需要使用多个示例。
少样本提示技术的局限性:
- 虽然 LLM 可以捕捉到示例中的模式,但这些技术在复杂的推理任务上效果不佳
- 少样本提示需要创建较长的提示。具有大量标记的提示可能会增加计算和延迟。提示的长度也有限制。
- 有时,当给定多个示例时,模型可能会学习您并非打算让它学习的模式,例如第三个电影评论总是负面的。
思维链
思维链(CoT)提示是一种技术,它促使模型产生中间推理步骤,从而提高复杂推理任务的结果。
有两种方法可以引导模型产生推理步骤:
- 通过用详细答案说明示例来进行少样本提示,向模型展示如何解决问题。
- 通过添加短语,如“让我们一步一步地思考”或“深呼吸,一步一步地解决问题”,指导模型进行推理。
如果我们将 CoT 技术应用于推理部分中的松饼示例,并使用更大的模型,例如(tiiuae/falcon-180B-chat
),您可以在HuggingChat中尝试,我们将在推理结果上获得显著的改进:
Let's go through this step-by-step: 1\. You start with 15 muffins. 2\. You eat 2 muffins, leaving you with 13 muffins. 3\. You give 5 muffins to your neighbor, leaving you with 8 muffins. 4\. Your partner buys 6 more muffins, bringing the total number of muffins to 14. 5\. Your partner eats 2 muffins, leaving you with 12 muffins. Therefore, you now have 12 muffins.
提示 vs 微调
通过优化您的提示,您可以取得出色的结果,但是您可能仍然在考虑是否微调模型对您的情况更有效。以下是一些微调较小模型可能是首选的情况:
- 您的领域与 LLMs 预先训练的领域大相径庭,广泛的提示优化并未产生足够的结果。
- 您需要您的模型在资源稀缺的语言中表现良好。
- 您需要训练模型的数据是受严格监管的敏感数据。
- 由于成本、隐私、基础设施或其他限制,您必须使用小型模型。
在上述所有示例中,您需要确保您已经拥有或可以轻松获得足够大的领域特定数据集,以合理的成本来微调模型。您还需要有足够的时间和资源来微调模型。
如果上述示例不适用于您,优化提示可能会更有益。
开发者指南
使用🤗 Tokenizers 中的分词器
原始文本:
huggingface.co/docs/transformers/v4.37.2/en/fast_tokenizers
PreTrainedTokenizerFast 依赖于 🤗 Tokenizers 库。从🤗 Tokenizers 库获得的分词器可以非常简单地加载到🤗 Transformers 中。
在进入具体内容之前,让我们首先通过几行代码创建一个虚拟的分词器:
>>> from tokenizers import Tokenizer >>> from tokenizers.models import BPE >>> from tokenizers.trainers import BpeTrainer >>> from tokenizers.pre_tokenizers import Whitespace >>> tokenizer = Tokenizer(BPE(unk_token="[UNK]")) >>> trainer = BpeTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"]) >>> tokenizer.pre_tokenizer = Whitespace() >>> files = [...] >>> tokenizer.train(files, trainer)
我们现在有一个在我们定义的文件上训练过的分词器。我们可以继续在该运行时中使用它,或者将其保存到一个 JSON 文件中以供将来重复使用。
直接从分词器对象加载
让我们看看如何在🤗 Transformers 库中利用这个分词器对象。PreTrainedTokenizerFast 类允许通过接受实例化的 tokenizer 对象作为参数来轻松实例化:
>>> from transformers import PreTrainedTokenizerFast >>> fast_tokenizer = PreTrainedTokenizerFast(tokenizer_object=tokenizer)
这个对象现在可以与🤗 Transformers 分词器共享的所有方法一起使用!请前往分词器页面获取更多信息。
从一个 JSON 文件加载
为了从一个 JSON 文件中加载一个分词器,让我们首先保存我们的分词器:
>>> tokenizer.save("tokenizer.json")
我们保存这个文件的路径可以通过 tokenizer_file
参数传递给 PreTrainedTokenizerFast 初始化方法:
>>> from transformers import PreTrainedTokenizerFast >>> fast_tokenizer = PreTrainedTokenizerFast(tokenizer_file="tokenizer.json")
这个对象现在可以与🤗 Transformers 分词器共享的所有方法一起使用!请前往分词器页面获取更多信息。
ng the total number of muffins to 14.
5. Your partner eats 2 muffins, leaving you with 12 muffins.
Therefore, you now have 12 muffins.
## 提示 vs 微调 通过优化您的提示,您可以取得出色的结果,但是您可能仍然在考虑是否微调模型对您的情况更有效。以下是一些微调较小模型可能是首选的情况: + 您的领域与 LLMs 预先训练的领域大相径庭,广泛的提示优化并未产生足够的结果。 + 您需要您的模型在资源稀缺的语言中表现良好。 + 您需要训练模型的数据是受严格监管的敏感数据。 + 由于成本、隐私、基础设施或其他限制,您必须使用小型模型。 在上述所有示例中,您需要确保您已经拥有或可以轻松获得足够大的领域特定数据集,以合理的成本来微调模型。您还需要有足够的时间和资源来微调模型。 如果上述示例不适用于您,优化提示可能会更有益。 # 开发者指南 # 使用🤗 Tokenizers 中的分词器 > 原始文本:[`huggingface.co/docs/transformers/v4.37.2/en/fast_tokenizers`](https://huggingface.co/docs/transformers/v4.37.2/en/fast_tokenizers) PreTrainedTokenizerFast 依赖于 [🤗 Tokenizers](https://huggingface.co/docs/tokenizers) 库。从🤗 Tokenizers 库获得的分词器可以非常简单地加载到🤗 Transformers 中。 在进入具体内容之前,让我们首先通过几行代码创建一个虚拟的分词器: ```py >>> from tokenizers import Tokenizer >>> from tokenizers.models import BPE >>> from tokenizers.trainers import BpeTrainer >>> from tokenizers.pre_tokenizers import Whitespace >>> tokenizer = Tokenizer(BPE(unk_token="[UNK]")) >>> trainer = BpeTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"]) >>> tokenizer.pre_tokenizer = Whitespace() >>> files = [...] >>> tokenizer.train(files, trainer)
我们现在有一个在我们定义的文件上训练过的分词器。我们可以继续在该运行时中使用它,或者将其保存到一个 JSON 文件中以供将来重复使用。
直接从分词器对象加载
让我们看看如何在🤗 Transformers 库中利用这个分词器对象。PreTrainedTokenizerFast 类允许通过接受实例化的 tokenizer 对象作为参数来轻松实例化:
>>> from transformers import PreTrainedTokenizerFast >>> fast_tokenizer = PreTrainedTokenizerFast(tokenizer_object=tokenizer)
这个对象现在可以与🤗 Transformers 分词器共享的所有方法一起使用!请前往分词器页面获取更多信息。
从一个 JSON 文件加载
为了从一个 JSON 文件中加载一个分词器,让我们首先保存我们的分词器:
>>> tokenizer.save("tokenizer.json")
我们保存这个文件的路径可以通过 tokenizer_file
参数传递给 PreTrainedTokenizerFast 初始化方法:
>>> from transformers import PreTrainedTokenizerFast >>> fast_tokenizer = PreTrainedTokenizerFast(tokenizer_file="tokenizer.json")
这个对象现在可以与🤗 Transformers 分词器共享的所有方法一起使用!请前往分词器页面获取更多信息。