LLM 提示指南¶
大型语言模型,比如Falcon和LLaMA,其实是预先训练好的Transformer模型。它们的初衷是预测给定一段文本后,下一个字词会是什么。这些模型拥有数十亿个参数,并且在海量的文本数据上训练了很长时间。正因为如此,它们变得非常强大和多功能。你可以通过用自然语言给出提示,来引导这些模型解决各种自然语言处理(NLP)任务。
为了让这些模型给出最佳结果,设计合适的提示非常重要,这被称为“提示工程”。提示工程是一个不断尝试和改进的过程。自然语言比编程语言更灵活、表达力更强,但也更容易产生歧义。而且,自然语言提示对细节非常敏感,哪怕小小的改动都可能导致完全不同的输出。
虽然没有什么万能的提示方法,但研究人员已经总结出一些最佳实践,帮助大家更稳定地获得好结果。
这本指南会介绍这些提示工程的最佳实践,帮助你制作更有效的LLM提示,解决各种NLP任务。你将学到:
- 激励的基础:了解如何通过自然语言提示激发大型语言模型的潜力,使其更有效地完成任务。
- LLM提示的最佳实践:掌握设计和优化提示词的基本原则和技巧,确保模型输出符合预期。
- 高级提示技巧:少样本提示和思维链,深入探讨少样本提示和思维链等高级技巧,提升模型在复杂任务中的表现。
- 何时进行微调而不是提示:明确在什么情况下选择微调模型而不是使用提示词,以获得最佳效果。
提示工程只是优化大型语言模型(LLM)输出的一部分。另一个关键部分是选择最合适的文本生成策略。你可以自定义LLM在生成文本时如何选择每个后续的字词,而无需改动任何可训练的参数。通过调整文本生成参数,你可以减少生成文本中的重复内容,使其更加连贯,听起来也更自然。关于文本生成策略和参数的详细内容,本指南不展开讨论,但你可以在以下指南中了解更多信息:
from transformers import pipeline
import torch
torch.manual_seed(0)
generator = pipeline('text-generation', model = 'openai-community/gpt2')
prompt = "Hello, I'm a language model"
generator(prompt, max_length = 30)
要使用编码器-解码器模型进行推理,请使用text2text-generationpipeline:
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)
基本模型 vs 指令/聊天模型¶
在🤗 Hub上提供的最近大型语言模型(LLM)checkpoints 大多有两个版本:基本版和指令版(或聊天版)。例如,tiiuae/falcon-7b和tiiuae/falcon-7b-instruct。
基本模型在给定初始提示时,非常擅长完成文本,但它们在需要遵循指令的NLP任务或会话使用中并不理想。这时,指令(聊天)版本就发挥作用了。这些 checkpoints 是对预训练的基本版本进行进一步微调,使用了指令和会话数据。这种额外的微调使它们成为许多NLP任务的更佳选择。
让我们通过一些简单的提示示例,展示如何使用tiiuae/falcon-7b-instruct来解决一些常见的NLP任务。
NLP任务¶
首先,我们来设置环境:
pip install -q transformers accelerate
接下来,使用合适的 pipeline("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任务。
文本分类¶
文本分类中最常见的一种形式是情感分析,它会给一段文本分配一个标签,比如“积极”、“消极”或“中性”。
让我们编写一个提示,指导模型对给定的文本(电影评论)进行分类。我们首先给出指令,然后指定要分类的文本。注意,我们不仅仅停留在这里,还添加了响应的开头——Sentiment::
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']}")
结果中包含了我们在指令中提供的分类标签列表中的一个,而且这个标签是正确的!
你可能会注意到,除了提示之外,我们还传递了一个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']}")
正如你所看到的,模型正确地从给定文本中识别出了两个命名实体。
翻译¶
另一个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']}")
在这里,我们添加了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']}")
问题回答¶
对于问题回答任务,我们可以将提示结构化为以下逻辑组件:指令、上下文、问题,以及引导词或短语(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']}")
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']}")
结果正确!让我们稍微增加一些复杂性,看看是否还能用基本提示解决问题:
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']}")
这个答案是错误的,正确答案应该是12。这种情况可能是由于提示过于简单,也可能是因为模型选择的原因,毕竟我们选择了Falcon的最小版本。推理对所有不同大小的模型来说都很困难,但更大的模型可能会表现得更好。
LLM提示的最佳实践¶
在本指南的这一部分,我们整理了一些通常能提高提示效果的最佳实践:
- 选择模型:选择最新和最强大的模型,它们很可能表现更好。
- 从简开始:从一个简单且简短的提示开始,然后逐步迭代。
- 指令位置:将指令放在提示的开头或结尾。处理大上下文时,模型会应用各种优化以防止注意力复杂度呈二次方增长。这可能导致模型更关注提示的开头或结尾,而不是中间。
- 明确分隔:清晰地将指令与它们应用的文本分开——下一节将详细介绍。
- 具体描述:对任务和期望结果进行具体和详细的描述,包括格式、长度、风格、语言等。
- 避免歧义:避免使用模糊的描述和指令。
- 正面指令:优先使用“做什么”的指令,而不是“不要做什么”的指令。
- 引导输出:通过写出第一个词(甚至开始第一句话)来引导输出方向。
- 高级技术:使用如少样本提示(Few-shot prompting)和思维链(Chain-of-thought)等高级技术。
- 测试模型:用不同模型测试你的提示,以评估其鲁棒性。
- 版本控制:对提示进行版本控制和性能跟踪。
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']}")
思维链(Chain-of-thought)¶
思维链(CoT)提示是一种技术,它促使模型生成中间推理步骤,从而在复杂推理任务上提升结果。
有两种方法引导模型生成推理步骤:
- 少样本提示:通过展示带有详细答案的问题示例,向模型展示如何逐步解决问题。
- 指令引导:通过添加如“让我们一步步思考”或“深呼吸,一步步解决问题”等短语来指导模型进行推理。
如果我们将CoT技术应用到推理部分的示例中,并使用一个更大的模型,比如(tiiuae/falcon-180B-chat),你可以在HuggingChat中尝试,我们将在推理结果上看到显著改进:
让我们一步步来看:
- 你一开始有15个松饼。
- 你吃了2个松饼,剩下13个。
- 你给了邻居5个松饼,剩下8个。
- 你的伴侣又买了6个松饼,总数增加到14个。
- 你的伴侣吃了2个松饼,剩下12个。
所以,你现在有12个松饼。
提示优化与微调对比¶
通过优化提示,你可以取得很好的结果,但你可能仍在思考是否微调模型对你的情况更有效。以下是一些微调较小模型可能是更佳选择的场景:
- 领域差异大:你的领域与LLM预训练的内容差异很大,且广泛的提示优化未能带来足够好的结果。
- 低资源语言:你需要模型在低资源语言中表现良好。
- 敏感数据训练:你需要模型在受严格监管的敏感数据上进行训练。
- 使用小模型:由于成本、隐私、基础设施或其他限制,你必须使用小模型。
在上述所有例子中,你需要确保你已经拥有或能够以合理成本轻松获得足够大的领域特定数据集来微调模型。你还需要有足够的时间和资源来微调模型。如果上述情况不符合你的情况,优化提示可能会更有益。