在机器学习中,我们过去总是依赖不同的模型来完成不同的任务。随着多模态和大型语言模型(LLMs)的引入,这种情况发生了变化。
过去,我们需要为分类、命名实体识别(NER)、问答(QA)和许多其他任务使用单独的模型的日子已经一去不复返。
随着变换器(transformers)和迁移学习(transfer learning)的引入,要使语言模型适应不同的任务,只需要在网络的末端(即头部)添加一些小的层次以及一些微调。
如今,甚至这种方法也已经过时了。为什么要更改这些最后几个模型层,并经过整个微调过程,当您可以直接向模型提出分类或问答的请求时。
大型语言模型(LLMs)可以执行所有这些任务以及更多。这些模型已经经过简单的概念训练,您输入一系列文本,模型输出一系列文本。唯一可变的是输入文本,也就是提示(prompt)。
在这个新时代的LLMs中,提示(prompt)至关重要。糟糕的提示会产生糟糕的输出,而好的提示则非常强大。构建良好的提示是那些使用LLMs进行构建的人的重要技能。
LangChain库认识到提示的强大之处,并为其构建了一整套对象。在本文中,我们将学习关于PromptTemplates以及如何有效实施它们的所有知识。
提示工程
在深入研究Langchain的PromptTemplate之前,我们需要更好地理解提示以及提示工程的学科。
一个提示通常由多个部分组成:
并不是所有的提示都使用这些组件,但一个好的提示通常会使用两个或更多。让我们更精确地定义它们。
- 指令(Instructions)告诉模型要做什么,如何使用提供的外部信息,如何处理查询,以及如何构建输出。
- 外部信息或上下文(External information or context(s))充当模型的额外知识来源。这些信息可以手动插入提示中,通过向量数据库检索(检索增强)获取,或通过其他方式(API、计算等)引入。
- 用户输入或查询(User input or query)通常是由人类用户(提示者)输入系统的查询(不过并不总是如此)。
- 输出指示符(Output indicator)标记了待生成文本的开头。如果要生成Python代码,我们可以使用import来告诉模型必须开始编写Python代码(因为大多数Python脚本都以import开头)。
通常,这些组件按照上述顺序放置在提示中,从指令开始,然后是外部信息(如果适用),然后是用户输入,最后是输出指示符。
让我们看看如何使用Langchain将这些内容输入到OpenAI模型中:
In[5]:
prompt = """Answer the question based on the context below. If the
question cannot be answered using the information provided answer
with "I don't know".
Context: Large Language Models (LLMs) are the latest models used in NLP.
Their superior performance over smaller models has made them incredibly
useful for developers building NLP enabled applications. These models
can be accessed via Hugging Face's `transformers` library, via OpenAI
using the `openai` library, and via Cohere using the `cohere` library.
Question: Which libraries and model providers offer LLMs?
Answer: """
In[6]:
from langchain.llms import OpenAI
# initialize the models
openai = OpenAI(
model_name="text-davinci-003",
openai_api_key="YOUR_API_KEY"
)
In[7]:
print(openai(prompt))
Out[7]:
Hugging Face's `transformers` library, OpenAI using the `openai` library, and Cohere using the `cohere` library.
实际上,我们不太可能硬编码上下文和用户问题。我们将通过一个模板来输入它们,这就是Langchain的PromptTemplate发挥作用的地方。
提示模版
Langchain中的提示模板类是为了更容易构建具有动态输入的提示而构建的。其中最简单的类是PromptTemplate。我们将通过向我们之前的提示添加一个动态输入,即用户查询,来测试它。
from langchain import PromptTemplate
template = """Answer the question based on the context below. If the
question cannot be answered using the information provided answer
with "I don't know".
Context: Large Language Models (LLMs) are the latest models used in NLP.
Their superior performance over smaller models has made them incredibly
useful for developers building NLP enabled applications. These models
can be accessed via Hugging Face's `transformers` library, via OpenAI
using the `openai` library, and via Cohere using the `cohere` library.
Question: {query}
Answer: """
prompt_template = PromptTemplate(
input_variables=["query"],
template=template
)
有了这个,我们可以使用format
方法来查看将查询传递给模板的效果。
In[9]:
print(
prompt_template.format(
query="Which libraries and model providers offer LLMs?"
)
)
Out[9]:
Answer the question based on the context below. If the
question cannot be answered using the information provided answer
with "I don't know".
Context: Large Language Models (LLMs) are the latest models used in NLP.
Their superior performance over smaller models has made them incredibly
useful for developers building NLP enabled applications. These models
can be accessed via Hugging Face's `transformers` library, via OpenAI
using the `openai` library, and via Cohere using the `cohere` library.
Question: Which libraries and model providers offer LLMs?
Answer:
当然,我们可以直接将这个输出传递给LLM对象,如下所示:
In[10]:
print(openai(
prompt_template.format(
query="Which libraries and model providers offer LLMs?"
)
))
Out[10]:
Hugging Face's `transformers` library, OpenAI using the `openai` library, and Cohere using the `cohere` library.
这只是一个简单的实现,可以轻松地用f-strings(例如f”插入一些自定义文本 ‘{custom_text}’ 等”) 来替代。然而,使用Langchain的PromptTemplate对象,我们可以规范这个过程,添加多个参数,并以面向对象的方式构建提示。
这些是Langchain提供的一些重要优势,但只是Langchain为帮助我们处理提示提供的部分功能。
Few Shot提示模板
LLMs的成功来自于它们的大尺寸以及在模型训练过程中学习到的“知识”能力,这些知识存储在模型参数中。然而,还有更多将知识传递给LLM的方法。主要的两种方法是:
- 参数化知识(Parametric knowledge)——上面提到的知识是在模型训练期间学到的,存储在模型权重(或参数)中。
- 来源知识(Source knowledge)——通过输入提示在推理时提供给模型的任何知识。
Langchain的FewShotPromptTemplate适用于源知识输入。其思想是通过少量示例来“训练”模型,我们称之为Few-shot学习,这些示例在提示中提供给模型。
Few-shot学习在我们的模型需要帮助理解我们要求它做什么时非常有效。我们可以在以下示例中看到这一点:
In[12]:
prompt = """The following is a conversation with an AI assistant.
The assistant is typically sarcastic and witty, producing creative
and funny responses to the users questions. Here are some examples:
User: What is the meaning of life?
AI: """
openai.temperature = 1.0 # increase creativity/randomness of output
print(openai(prompt))
Out[12]:
Life is like a box of chocolates, you never know what you're gonna get!
在这种情况下,我们提出了一个严肃的问题,希望得到一些有趣的回答,比如笑话。然而,即使将温度设置为1.0(增加随机性/创意性),我们仍然得到了严肃的回答。
为了帮助模型,我们可以给它一些我们想要的回答类型的示例:
In[13]:
prompt = """The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative and funny responses to the users questions. Here are some
examples:
User: How are you?
AI: I can't complain but sometimes I still do.
User: What time is it?
AI: It's time to get a watch.
User: What is the meaning of life?
AI: """
print(openai(prompt))
Out[13]:
42, of course!
通过示例来强化我们在提示中传递的指令,我们更有可能得到一个更有趣的回应。然后,我们可以使用Langchain的FewShotPromptTemplate来规范化这个过程:
from langchain import FewShotPromptTemplate
# create our examples
examples = [
{
"query": "How are you?",
"answer": "I can't complain but sometimes I still do."
}, {
"query": "What time is it?",
"answer": "It's time to get a watch."
}
]
# create a example template
example_template = """
User: {query}
AI: {answer}
"""
# create a prompt example from above template
example_prompt = PromptTemplate(
input_variables=["query", "answer"],
template=example_template
)
# now break our previous prompt into a prefix and suffix
# the prefix is our instructions
prefix = """The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative and funny responses to the users questions. Here are some
examples:
"""
# and the suffix our user input and output indicator
suffix = """
User: {query}
AI: """
# now create the few shot prompt template
few_shot_prompt_template = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix=prefix,
suffix=suffix,
input_variables=["query"],
example_separator="nn"
)
如果我们随后传递示例和用户查询,我们将得到这个结果:
In[15]:
query = "What is the meaning of life?"
print(few_shot_prompt_template.format(query=query))
Out[15]:
The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative and funny responses to the users questions. Here are some
examples:
User: How are you?
AI: I can't complain but sometimes I still do.
User: What time is it?
AI: It's time to get a watch.
User: What is the meaning of life?
AI:
这个过程可能看起来有些复杂。为什么要使用FewShotPromptTemplate对象、示例字典等等,当我们可以用几行代码和一个f-string来完成相同的任务呢?
再次强调,这种方法更加规范化,与Langchain中的其他功能很好地集成(比如chains——稍后会详细介绍),并且具备多个功能。其中之一是根据查询长度来变化包含的示例数量。
动态数量的示例很重要,因为我们的提示和完成输出的最大长度受到限制。这个限制是由最大上下文窗口来衡量的。
context window=input tokens+output tokenscontext window=input tokens+output tokens
同时,我们可以最大化提供给模型进行Few-shot学习的示例数量。
考虑到这一点,我们需要平衡包含的示例数量和我们的提示大小。我们的硬限制是最大上下文窗口大小,但我们还必须考虑通过LLM处理更多标记的成本。更少的标记意味着更便宜的服务和更快的LLM完成。
FewShotPromptTemplate允许我们根据这些变量来变化包含的示例数量。首先,我们创建一个更广泛的示例列表:
examples = [
{
"query": "How are you?",
"answer": "I can't complain but sometimes I still do."
}, {
"query": "What time is it?",
"answer": "It's time to get a watch."
}, {
"query": "What is the meaning of life?",
"answer": "42"
}, {
"query": "What is the weather like today?",
"answer": "Cloudy with a chance of memes."
}, {
"query": "What is your favorite movie?",
"answer": "Terminator"
}, {
"query": "Who is your best friend?",
"answer": "Siri. We have spirited debates about the meaning of life."
}, {
"query": "What should I do today?",
"answer": "Stop talking to chatbots on the internet and go outside."
}
]
在此之后,我们不是直接传递示例,而是使用LengthBasedExampleSelector,如下所示:
from langchain.prompts.example_selector import LengthBasedExampleSelector
example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
max_length=50 # this sets the max length that examples should be
)
值得注意的是,我们将max_length测量为通过空格和换行符分割字符串确定的单词数。具体的逻辑如下所示:
In[30]:
import re
some_text = "There are a total of 8 words here.nPlus 6 here, totaling 14 words."
words = re.split('[n ]', some_text)
print(words, len(words))
Out[30]:
['There', 'are', 'a', 'total', 'of', '8', 'words', 'here.', 'Plus', '6', 'here,', 'totaling', '14', 'words.'] 14
然后,我们将我们的example_selector传递给FewShotPromptTemplate,以创建一个新的、动态的提示模板:
# now create the few shot prompt template
dynamic_prompt_template = FewShotPromptTemplate(
example_selector=example_selector, # use example_selector instead of examples
example_prompt=example_prompt,
prefix=prefix,
suffix=suffix,
input_variables=["query"],
example_separator="n"
)
现在,如果我们传递一个较短或较长的查询,我们应该会看到包含的示例数量会变化。
In[32]:
print(dynamic_prompt_template.format(query="How do birds fly?"))
Out[32]:
The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative and funny responses to the users questions. Here are some
examples:
User: How are you?
AI: I can't complain but sometimes I still do.
User: What time is it?
AI: It's time to get a watch.
User: What is the meaning of life?
AI: 42
User: What is the weather like today?
AI: Cloudy with a chance of memes.
User: How do birds fly?
AI:
传递一个更长的问题将导致包含的示例数量减少:
In[34]:
query = """If I am in America, and I want to call someone in another country, I'm
thinking maybe Europe, possibly western Europe like France, Germany, or the UK,
what is the best way to do that?"""
print(dynamic_prompt_template.format(query=query))
Out[34]:
The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative and funny responses to the users questions. Here are some
examples:
User: How are you?
AI: I can't complain but sometimes I still do.
User: If I am in America, and I want to call someone in another country, I'm
thinking maybe Europe, possibly western Europe like France, Germany, or the UK,
what is the best way to do that?
AI:
有了这个,我们在提示变量中返回了更少的示例。这允许我们限制过多的标记使用,并避免超过LLM的最大上下文窗口引发错误。
自然地,提示是LLMs新世界的重要组成部分。值得探索Langchain提供的可用工具,并熟悉不同的提示工程技术。
在这里,我们只涵盖了Langchain中可用的提示工具的一些示例,以及它们如何有限地使用。在下一章中,我们将探讨Langchain的另一个重要部分——称为“chains”的部分,我们将看到更多关于提示模板的使用以及它们如何融入该库提供的更广泛工具的示例。