ChatGPT大模型极简应用开发-CH4-GPT-4 和 ChatGPT 的高级技巧
文章目录
- 4.1 提示工程
- 4.1.1 设计有效的提示词
- 4.1.2 逐步思考
- 4.1.3 实现少样本学习
- 4.1.4 改善提示效果
- 4.2 微调
- 4.2.1 开始微调
- 4.2.2 使用 OpenAI API 进行微调
- 4.2.3 微调的应用
- 4.2.4 生成和微调电子邮件营销活动的合成数据
- 4.2.5 微调的成本
- 4.3 小结
4.1 提示工程
回顾聊天模型的chat_completion 函数:
def chat_completion(prompt, model="gpt-4", temperature=0):
res = openai.ChatCompletion.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=temperature,
)
print(res["choices"][0]["message"]["content"])
该函数接收提示词并在终端中显示补全结果。model
和 temperature
是两个可选特征,分别被默认设置为 gpt-4 和 0。
为了说明提示工程的原理,我们将再次使用示例文本“As Descartes said, I think therefore”(正如笛卡儿所说,我思故)。如果将此文本 输入 GPT-4,那么模型自然会通过迭代式地添加最可能出现的标记来补全句子:
chat_completion("As Descartes said, I think therefore")
模型的输出消息如下所示:
I am. This famous philosophical statement, also known as "Cogito, ergo sum," emphasizes the existence of the self through the act of thinking or doubting. Descartes used this statement as a foundational principle in his philosophy, arguing that one's own existence is the most certain and indubitable fact that can be known.
提示工程是一门新兴的学科,专注于以最佳实践构建 LLM 的最佳输入,从而尽可能以程序化方式生成目标输出。AI 工程师必须知道如何与 AI 进行交互,以获取可用于应用程序的有利结果。此外,AI 工程师还必须知道如何正确提问和编写高质量的提示词。
应该考虑在 openai 库的方法中使用不同的参数,因为如果使用 temperature、top_p 和 max_tokens 等参数,那么即使使用相同的提示词,你也可能得到截然不同的结果。
4.1.1 设计有效的提示词
很多任务可以通过提示词来完成,包括摘要、文本分类、情感分析和问题回答。在所有这些任务中,我们通常需要在提示词中定义三大要素:角色、上下文和任务。
这三大要素并非都是必需的,它们的顺序也不是固定的。不过,如果能够构造好提示词的结构并明确定义这三大要素,你就能够得到不错的结果。即使使用了这三大要素,对于复杂的任务,也可能需要采用更高级的技巧,如零样本学习、少样本学习和微调。
- 上下文
提示词的第一大要素是上下文。在为 LLM 编写输入文本时,必须尽可能详细地描述上下文。为了说明这一点,假设我们想使用 GPT-4 创建一个应用程序,用于推荐午餐的主菜。接下来,将比较在两个上下文中获得的结果。第一个上下文提供较少的细节,第二个上下文则提供较多的细节。
假设使用以下较短的提示词:
chat_completion("Give me a suggestion for the main course for today's lunch.")
得到以下输出消息:
Grilled chicken with roasted vegetables and quinoa.
在提示词中加入更多上下文信息:
prompt = """
Context: I do 2 hours of sport a day. I am vegetarian, and I don't
like green vegetables. I am conscientious about eating healthily.
Task: Give me a suggestion for a main course for today's lunch."""
chat_completion(prompt)
得到以下输出消息:
For today's lunch, you can try a Chickpea and Sweet Potato Curry served with Quinoa. This dish is packed with protein, fiber, and essential nutrients, while also being vegetarian and not relying on green vegetables. The curry can be made with chickpeas, sweet potatoes, tomatoes, onions, garlic, and a blend of spices like turmeric, cumin, and coriander. Cook the quinoa separately and serve it alongside the curry for a delicious and healthy meal.
在第二个例子中,由于提示词含有更多上下文,因此模型给出的句子更详细:它推荐了一道富含蛋白质的健康素食菜肴。
上下文通过引导 GPT 模型进行思考来使其输出更有价值的回答。构建含有良好上下文的提示词是一个迭代过程,通常需要试错。确定上下文必须包含的关键点有时会有些棘手。为了找到可改进之处,可以请 GPT-4 提些意见。
使用以下提示词:
prompt = """
Context: I do 2 hours of sport a day. I am vegetarian and I don't
like green vegetables. I am very careful to eat healthily.
Task: Give me a suggestion for a main course for today's lunch? Do not perform the
requested task! Instead, can you ask me questions about the context so that when I answer, you can perform the requested task more efficiently?
"""
chat_completion(prompt)
GPT-4 向我们提出以下问题:
Sure, I have a few questions to help me suggest a suitable main course for you:
1. What type of sport do you do, and do you have any specific nutritional requirements related to it?
2. Are there any other vegetables or ingredients that you particularly enjoy or dislike?
3. Do you prefer a hot or cold meal for lunch?
4. Are you open to trying new recipes or do you prefer sticking to familiar dishes?
模型提出的问题通常与提示词相关。根据这些问题,可以在提示词中更新上下文。总之,建议在构建提示词时经常使用这个技巧。
- 任务
任务定义了你对 GPT-4 模型的用法,并且应该明确且具体。你应该提供足够的任务信息,并在提示词中使用合适的短语来引导模型给出你所期望的结果。
在前面的例子中,任务是让模型为午餐推荐一道主菜。可以为该任务添加细节,以说明想要的内容。在本例中,要求模型添加一张表格,列出每种食材所需的克数:
prompt = """
Context: I do 2 hours of sport a day. I am vegetarian, and I don't like green vegetables. I am conscientious about eating healthily.
Task: Give me a suggestion for a main course for today's lunch. With this suggestion, I also want a table with two columns where each row contains an ingredient from the main course. The first column of the table is the name of the ingredient. The second column of the table is the number of grams of that ingredient needed for one person. Do not give the recipe for preparing the main course.
"""
chat_completion(prompt)
模型给出以下输出结果:
Suggestion for today's lunch: Stuffed Bell Peppers with Quinoa and
Black Beans
| Ingredient | Grams per person |
|---------------------|------------------|
| Bell peppers | 200g |
| Quinoa | 60g |
| Black beans | 75g |
| Onion | 50g |
| Garlic | 5g |
| Olive oil | 10g |
| Tomato sauce | 50g |
| Cumin | 1g |
| Chili powder | 1g |
| Salt | 1g |
| Pepper | 1g |
| Shredded cheese | 30g |
OpenAI API 示例页面列出了 48 个任务示例。这些示例展示了 GPT模型可以执行的任务,其中每个示例都配有相关的提示词和演示。这里给几个例子:
- 语法纠正
纠正句子中的语病,并将其修改为标准的英语句子。提示词示例如下:
Correct this to standard English: She no went to the market.
- 给二年级学生概括一下
将复杂的文本概括为简单的概念。提示词示例如下:
Summarize this for a second-grade student: Jupiter is the fifth planet [...]
- TL;DR 概要
TL;DR 是“too long; didn’t read”的首字母缩写,意为“太长了,没读”。有人发现,只需在文本末尾添加 Tl;dr,即可请求模型对文本进行总结。提示词示例如下:
A neutron star [...] atomic nuclei. Tl;dr
- Python转自然语言
用自然语言解释一段 Python 代码,提示词示例如下:
# Python 3
def hello(x):
print('hello '+str(x))
# Explanation of what the code does
- 计算时间复杂度
计算一个函数的时间复杂度。提示词示例如下:
# Python 3
def hello(x, n):
for i in range(n):
print('hello '+str(x))
# The time complexity of this function is
- 修复 Python bug
修复含有 bug 的 Python 代码。提示词示例如下
### Buggy Python
def hello(x, n):
for i in rang(n):
print('hello '+str(x))
### Fixed Python
- SQL 请求
构建简单的 SQL 查询。提示词示例如下:
Create a SQL request to find all users who live in California and have over 1000 credits.
- 类比制造器
对两个词进行类比。提示词示例如下:
Create an analogy for this phrase. Questions are arrows in that:
- 摘要笔记
总结会议纪要。提示词示例如下:
Convert my shorthand into a first-hand account of the meeting:
Tom: Profits up 50%
Jane: New servers are online
Kjel: Need more time to fix software
- 角色
在撰写提示词时,影响模型的最后一种方式是赋予其一个角色。角色和上下文可以独立使用,但同时使用可以加强对模型输出的控制。以下示例中赋予模型运动营养专家的角色:
prompt = """
Role: You are a nutritionist designing healthy diets for highperformance athletes. You take into account the nutrition needed
for a good recovery.
Context: I do 2 hours of sport a day. I am vegetarian, and I don't
like green vegetables. I am conscientious about eating healthily.
Task: Based on your expertise defined in your role, give me a suggestion for a main course for today's lunch. With this suggestion, I also want a table with two columns where each row in the table contains an ingredient from the main course. The first
column of the table is the name of the ingredient. The second column of the table is the number of grams of that ingredient needed for one person. Do not give the recipe for preparing the main course.
"""
chat_completion(prompt)
得到以下结果:
Main Course Suggestion: Quinoa and Chickpea Stuffed Bell Peppers
Ingredient Table:
| Ingredient | Grams per Person |
|---------------------|------------------|
| Quinoa | 60 |
| Chickpeas | 100 |
| Bell Peppers | 200 |
| Olive Oil | 10 |
| Onion | 50 |
| Garlic | 5 |
| Tomato | 100 |
| Feta Cheese | 30 |
| Kalamata Olives | 20 |
| Fresh Basil | 5 |
| Salt | 2 |
| Black Pepper | 1 |
提示词可用于调整像 GPT 模型这样的 LLM 的概率分布集。它们可以被视为模型指南,引导模型生成特定类型的结果。虽然没有必须遵守的提示词设计结构,但不妨考虑结合使用上下文、角色和任务。
4.1.2 逐步思考
GPT-4 不擅长计算。比如,它无法计算 369 × 1235
。该模型从左侧开始,通过依次预测答案中的每个标记来给出完整的答案。这意味着 GPT-4 首先生成最左侧的数字,然后将其作为上下文的一部分生成下一个数字,以此类推,直到形成完整的答案。挑战在于,每个数字都是独立预测的,与最终的正确值无关。
在提示词的末尾添加“让我们逐步思考”这样的话,已被证明可以使模型解决更复杂的推理问题。这种技术称为零样本思维链策略(zero-shot-CoTstrategy),由 Takeshi Kojima 等人在 2022 年发表的论文“Large Language Models are Zero-Shot Reasoners”中提出。
思维链是指使用提示词鼓励模型逐步模仿推理的技术。零样本这个术语意味着模型不依赖于特定任务的示例来执行这种推理,它已经准备好根据其一般的训练结果来处理新任务,零样本学习试图在不需要特定任务示例的情况下进行泛化。
在提示词末尾添加逐步思考的字样(比如“Let’s think step by step”)后,模型开始通过拆分问题来进行推理。它可能需要一些时间来进行推理,从而解决之前无法在一次尝试中解决的问题。
prompt = "How much is 369 * 1235? Let's think step by step."
chat_completion(prompt)
输出:
Step 1: Multiply 369 by 5 (the last digit of 1235).
369 * 5 = 1845
Step 2: Multiply 369 by 30 (the second last digit of 1235 is 3,
so multiply by 3 and add a 0 at the end).
369 * 30 = 11070
Step 3: Multiply 369 by 200 (the third digit of 1235 is 2,
so multiply by 2 and add two 0s at the end).
369 * 200 = 73800
Step 4: Multiply 369 by 1000 (the first digit of 1235 is 1,
so multiply by 1 and add three 0s at the end).
369 * 1000 = 369000
Step 5: Add the results from steps 1 to 4.
1845 + 11070 + 73800 + 369000 = 455715
So, 369 * 1235 = 455,715.
模型所用的方法发生了显著的改变。现在它将主要问题分解为更小、更容易管理的步骤,而不是试图直接解决问题。
论文“Large Language Models are Zero-Shot Reasoners”的作者发现,它对于多步算术问题、涉及符号推理的问题、涉 及策略的问题和其他涉及推理的问题非常有效。然而,它对于模型回答常识性问题没有显著效果。
4.1.3 实现少样本学习
少样本学习(few-shot learning)是由 Tom B. Brown 等人在论文“Language Models Are Few-Shot Learners”中提出的,它指的是 LLM仅通过提示词中的几个示例就能进行概括并给出有价值的结果。
在本例中,要求 LLM 将特定的单词转换成表情符号。很难想象如何通过提示词给模型下达这种“指令”。但是通过少样本学习,这变得很容易。给模型一些例子,它将自动尝试复制它们的模式:
得到以下输出消息:
少样本学习技巧提供了具有目标输出的输入示例。然后,在最后一行,提供了想让模型完成的提示词。这个提示词与之前的示例具有相同的形式。模型将根据给定示例的模式执行操作。
仅凭几个示例,模型就能够复现模式。通过利用在训练阶段所获得的海量知识,LLM 可以根据少量例子迅速适应并生成准确的答案。
少样本学习是 LLM 的一个强大的特点,因为它使得 LLM 高度灵活且适应性强,只需有限的额外信息即可执行各种任务。
指导 LLM 的另一种方法是单样本学习(one-shot learning)。顾名思义,在单样本学习中,只提供一个示例来帮助模型执行任务。尽管这种方法提供的指导比少样本学习要少,但对于简单的任务或 LLM 已经具备丰富背景知识的主题,它可能很有效。单样本学习的优点是更简单、生成速度更快、计算成本更低(因而 API 使用成本更低)。然而,对于复杂的任务或需要更深入理解所需结果的情况,少样本学习的效果可能更好。
4.1.4 改善提示效果
可以引导 GPT 模型的行为,以使模型给出的结果更好地满足我们的需求:
- 指示模型提出更多问题
在提示词的末尾,询问模型是否理解问题并指示模型提出更多问题。比如在构建机器人的时候,可以在提示词的末尾添加如下文本:
你清楚地理解我的请求了吗?如果没有,请问我关于上下文的问题。这样一来,当我回答时,你就能够更高效地执行我所请求的任务。
- 格式化输出
有时可能希望在一个较长的过程中使用 LLM 的输出。在这种情况下,输出格式很重要。比如想要json输出或者markdown格式输出。这里以json为例:
prompt = """
Give a JSON output with 5 names of animals. The output must be accepted by json.loads.
"""
chat_completion(prompt, model='gpt-4')
输出得到以下json代码块:
"animals": [
"lion",
"tiger",
"elephant",
"giraffe",
"zebra"
]
}
- 重复指示
经验表明,重复指示会取得良好的效果,尤其是当提示词很长时。基本思路是,在提示词中多次添加相同的指令,但每次采用不同的表述方式。
- 使用负面提示
在文本生成场景中,负面提示是指通过指定不希望在输出中看到的内容来引导模型。负面提示作为约束或指南,用于滤除某些类型的回答。对于复杂任务,这种技巧特别有用:当以不同的表述方式多次重复指令时,模型往往能够更准确地遵循指令。
比如可以在提示词中指示模型不要在 JSON 代码块之前或之后添加任何内容。
Extract the keywords from the following question: {user_question}. Do not answer anything else, only the keywords.
- 添加长度限制
限制长度通常是不错的做法。如果你只希望模型回答 1 个词或者 10个句子,那么不妨将要求添加到提示词中。
比如:
指示模型用 100 个单词生成一篇内容翔实的新闻稿;
如果你能回答问题,回答 ANSWER;如果你需要更多信息,回答 MORE;如果你无法回答,回答 OTHER。只回答一个词
如果没有最后一句话,模型往往会生成句子,而不会遵循指示。
4.2 微调
针对特定任务或上下文对它们进行微调,可以进一步提高它们的性能。
4.2.1 开始微调
假设想为公司创建一个电子邮件自动回复生成器。由于公司所在的行业使用专有词汇,因此你希望生成器给出的电子邮件回复保持一定的写作风格。要做到这一点,有两种策略:
- 要么使用之前介绍的提示工程技巧来强制模型输出你想要的文本
- 要么对现有模型进行微调
需要收集大量电子邮件,其中包含关于特定业务领域的数据、客户咨询及针对这些咨询的回复。可以使用这些数据微调现有模型,以使模型学习公司所用的语言模式和词汇。微调后的模型本质上是基于 OpenAI 提供的原始模型构建的新模型,其中模型的内部权重被调整,以适应特定问题,从而能够在相关任务上提高准确性。
使用特定领域的数据集来更新现有 GPT模型的内部权重。微调的目标是使新模型能够在特定领域中做出比原始GPT 模型更好的预测。
即使使用自己的数据对 LLM 进行了微调,新模型也仍然保存在 OpenAI 的服务器上。需要通过 OpenAI API 与新模型进行交互,而不是在本地使用它。
针对特定领域的需求调整 GPT 基础模型
目前,微调仅适用于 davinci、curie、babbage 和 ada 这几个基础模型 。这些模型都在准确性和所需资源之间做出了权衡。开发人员可以为应用程序选择最合适的模型:
- **较小的模型(ada 和 babbage)**可能在简单任务或资源有限的应用程序中更快且更具成本效益
- **较大的模型(curie 和 davinci)**则提供了更强的语言处理和生成能力,从而适用于需要更高准确性的复杂任务
上述模型不属于 InstructGPT 系列,它们没有经过 RLHF 阶段。通过微调这些基础模型,比如根据自定义数据集调整它们的内部权重,可以针对特定的任务或领域定制模型。在微调时,必须使用基础模型,而不能使用 InstructGPT系列中的模型。
对比微调和少样本学习
微调是指针对特定任务在一组数据上重新训练现有模型,以提高模型的性能并使其回答更准确。在微调过程中,模型的内部参数得到更新。
少样本学习则是通过提示词向模型提供有限数量的好例子,以指导模型根据这些例子给出目标结果。在少样本学习过程中,模型的内部参数不会被修改。
**微调非常适合有大量数据可用的场景。这种定制化确保模型生成的内容更符合目标领域的特定语言模式、词汇和语气。**微调除了文中提到的确保模型生成内容更符合目标领域的特定语言模式、词汇和语气,还有一个优势:**可以通过微调缩短每一次提示中重复的指令或提示词以节省成本或降低延迟,模型会记住通过微调获得的“内置”指令。**因此,微调后,你可以在不牺牲提示质量的前提下,每次输入更短的提示词
少样本学习是一种更灵活的方法,其数据使用率也更高,因为它不需要重新训练模型。当只有有限的示例可用或需要快速适应不同任务时,这种技巧非常有益。少样本学习让开发人员能够快速设计原型并尝试各种任务,这使其成为许多用例的实用选择。
微调通常需要用到大量数据 。可用示例的缺乏往往限制了使用这种技巧。为了了解微调所需的数据量,可以假设对于相对简单的任务或仅需稍微调整的模型,通过几百个提示词示例才能获得相应的目标结果。
提供 50 ~ 100 个训练示例进行微调,就会看到明显的改进效果。
4.2.2 使用 OpenAI API 进行微调
准备数据
更新 LLM 需要提供一个包含示例的数据集。该数据集应该是一个JSONL 文件,其中每一行对应一个提示词−补全文本对:
{"prompt": "<prompt text>", "completion": "<completion text>"}
{"prompt": "<prompt text>", "completion": "<completion text>"}
{"prompt": "<prompt text>", "completion": "<completion text>"}
...
OpenAI 提供了一个工具,可以帮助生成此训练文件。该工具接受各种文件格式(CSV、TSV、XLSX、JSON 或 JSONL)作为输入,只要它们包含提示词和文本补全列 /键,并且输出可直接用于微调过程的 JSONL 文件。该工具还会验证数据,并提供改进数据质量的建议。
在终端中使用以下代码行运行此工具:
$ openai tools fine_tunes.prepare_data -f <LOCAL_FILE>
该工具将提出一系列建议来改善最终文件的结果。你既可以接受这些建议,也可以不接受,还可以指定选项 -q,从而自动接受所有建议。
如果有足够的数据,那么该工具会询问是否要将数据分为训练集和验证集。这是一种推荐的做法。算法将使用训练集来微调模型参数。验证集则用于衡量模型在未用于更新参数的数据上的性能。
对 LLM 的微调受益于高质量示例,最好由专家审核。当使用已有数据集进行微调时,请确保对数据进行筛查,以排除具有冒犯性的内容或不准确的内容。如果数据集过大而无法手动审核所有内容,则可以检查随机样本
上传数据
准备好数据后,需要将其上传到 OpenAI 服务器。OpenAI API 提供了不同的函数来操作文件。以下是一些重要函数。上传文件:
openai.File.create(
file=open("out_openai_completion_prepared.jsonl", "rb"),
purpose='fine-tune'
)
两个参数是必需的:file 和 purpose。在微调时,将 purpose 设置为 fine-tune。这将验证用于微调的下载文件格式。此函数的输出是一个字典,你可以在 id 字段中检索文件 ID。
删除文件
openai.File.delete("file-z5mGg(...)")
file_id 参数是必需的。
列出所有已上传的文件
openai.File.list()
检索文件 ID 可能会有帮助,比如在开始微调过程时。
- 创建经过微调的模型
微调已上传文件是一个简单的过程。端点openai.FineTune.create 在 OpenAI 服务器上创建一个作业,以根据给定的数据集优化指定的模型。该函数的响应包含排队作业的详细信息,如作业的状态、fine_tune_id,以及过程结束时模型的名称。
openai.FineTune.create 的一些输入参数
- 列出微调作业
可以通过以下函数获取 OpenAI 服务器上的所有微调作业:
openai.FineTune.list()
结果是一个字典,包含所有微调模型的信息。
- 取消微调作业
可以通过以下函数立即中断在 OpenAI 服务器上运行的作业:
openai.FineTune.cancel()
该函数只有一个必需的参数:fine_tune_id。该参数是以 ft- 开头的字符串,例如 ft-Re12otqdRaJ(…)。它是在使用openai.FineTune.create 创建作业后获得的。
4.2.3 微调的应用
- 法律文本分析
在这个案例中,LLM 被用来处理法律文本并提取有价值的信息。通过微调可以使模型专门针对特定领域完成任务,或协助非专业人士参与法律程序。通过在特定主题的法律语料库上对 LLM 进行微调,或者针对特定类型的最终用户,该模型可以更好地处理法律文本,并在执行与该特定类型的最终用户相关的任务时变得更加熟练。
- 自动代码审查
微调可以帮助模型分析开发人员编写的代码,并提出改进建议。这需要在包含代码片段和注释的数据集上训练模型,使模型能够处理特定语言的语法、语义和最佳实践。比如,JSONL 文件可以包含代码及相应的审查评论。这将帮助模型学习如何提供准确且相关的代码质量反馈:
{"prompt": "Review the following Python code:
'def sum(a, b): return a + b\n result = sum(5, '5')'",
"completion": "Type error: The 'sum' function is adding an integer and a string. Consider converting the string to an integer using int() before passing it to the function."}
- 财务文档摘要
针对收益报告、财务报表和分析师报告等财务文档,经过微调的 LLM可以生成简明、准确的摘要。在与财务记录明确相关的数据集上微调后,LLM 可以更准确地理解财务文档的术语和上下文。要调整现有模型以获得更好的财务文档摘要,你需要拥有大量样本摘要。这样一来,你便可以给模型提供一个包含财务文档及其理想摘要的 JSONL 文件,举例如下:
{"prompt": "The central bank announced a 0.25% increase in the benchmark interest rate, citing concerns about inflationary pressures. This move is expected to affect borrowing costs for consumers and businesses.",
"completion": "Bank increases rate 0.25% for inflation worry. May affect people, business borrowing costs."}
- 技术文档翻译
与少样本学习相比,使用微调后的模型来翻译技术文档可以显著地改善翻译效果。主要原因是,技术文档通常包含专业词汇和复杂的句子结构,少样本学习无法有效处理这种复杂性。基础模型是未经调整的GPT-3 模型,它们采用了 RLHF 技术。在使用前,这些模型需要进行微调。要微调现有的基础模型,你需要准备一个包含训练数据的JSONL 文件。对于技术文档翻译的用例,该文件应该包括将技术文本翻译为目标语言的翻译内容。
- 为专业领域生成内容
一个经过微调的模型可以针对高度专业化的主题生成高质量、引人入胜且与上下文相关的内容。对于这种任务,基础模型可能没有足够的训练数据来保持准确性。与其他所有用例一样,你需要创建一个训练数据集,以使模型专门用于生成专业内容。
这些数据将用于创建包含“提示词−补全文本对”的 JSONL 文件。以客户服务领域为例,提供迅速、准确且友好的响应对于提升客户满意度至关重要。
微调模型可以显著提高客户服务聊天机器人的性能,使模型更好地理解和响应特定领域的客户查询。客户服务场景具备天然的优势,即容易收集典型客户服务对话的高质量问答集,通过用户反馈形成回路,再通过微调持续改进模型的响应质量。这使模型能够更准确地识别客户问题的本质,并提供合适的解决方案。比如,模型可以学习如何处理账户查询、故障排除或产品推荐等具体问题。
4.2.4 生成和微调电子邮件营销活动的合成数据
为一家电子邮件营销机构制作一个文本生成工具,利用定向内容为企业创建个性化的电子邮件营销活动。这些电子邮件旨在吸引受众并推广产品或服务。
假设机构有一个来自支付处理行业的客户,机构的工作人员请求举办一场直接面向客户的电子邮件营销活动,为商店提供一项新的在线支付服务。电子邮件营销机构决定在这个项目中使用微调技术,因而需要大量的数据来进行微调。出于演示目的需要合成数据,通常最好的结果是通过人类专家给出的数据获得的,但在某些情况下,合成数据也可以帮助我们获得有用的解决方案。
- 创建合成数据集
在下面的示例中,利用 GPT-3.5 Turbo 创建合成数据。为了做到这一点,将在提示词中指定,希望使用促销话术向特定的商店推广在线支付服务。商店的特征包括商店类型、商店所在城市和商店的规模。为了获得促销话术,通过之前定义的chat_completion 函数将提示词发送给 GPT-3.5 Turbo。
在脚本的开头,定义 3 个列表,分别对应商店类型、商店所在城市和商店的规模:
l_sector = ['Grocery Stores', 'Restaurants', 'Fast Food Restaurants', 'Pharmacies', 'Service Stations (Fuel)', 'Electronics Stores']
l_city = ['Brussels', 'Paris', 'Berlin']
l_size = ['small', 'medium', 'large']
然后在一个字符串中定义第一个提示词。使用介绍的提示工程技巧,将在该提示词中明确定义角色、上下文和任务。在这个字符串中,大括号中的 3 个值将在后面被替换为相应的值。以下是用于生成合成数据的第一个提示词:
f_prompt = """
Role: You are an expert content writer with extensive direct marketing experience. You have strong writing skills, creativity, adaptability to different tones and styles, and a deep understanding of audience needs and preferences for effective direct campaigns.
Context: You have to write a short message in no more than 2 sentences for a direct marketing campaign to sell a new e-commerce payment service to stores.
The target stores have the following three characteristics:
- The sector of activity: {sector}
- The city where the stores are located: {city}
- The size of the stores: {size}
Task: Write a short message for the direct marketing campaign. Use the skills defined in your role to write this message! It is important that the message you create takes into account the product you are selling and the characteristics of the store you are writing to.
"""
以下提示词只包含 3 个变量的值,用逗号分隔。它不用于创建合成数据,只用于微调:
f_sub_prompt = "{sector}, {city}, {size}"
下面是代码的主要部分,它遍历之前定义的 3 个列表,循环中的代码块很简单。用适当的值替换大括号中的值。变量 prompt 与函数 chat_completion 一起使用,生成的广告保存在 response_txt 中。然后将变量 sub_prompt 和response_txt 添加到 out_openai_completion.csv 文件中,作为用于微调的训练集:
df = pd.DataFrame()
for sector in l_sector:
for city in l_city:
for size in l_size:
for i in range(3): # 每个重复 3 次
prompt = f_prompt.format(sector=sector, city=city, size=size)
sub_prompt = f_sub_prompt.format(sector=sector, city=city, size=size)
response_txt = chat_completion(prompt, model="gpt-3.5-turbo", temperature=1)
new_row = {"prompt": sub_prompt, "completion": response_txt}
new_row = pd.DataFrame([new_row])
df = pd.concat([df, new_row], axis=0, ignore_index=True)
df.to_csv("out_openai_completion.csv", index=False)
对于每种特征的组合都生成 3 个示例。为了充分发挥模型的创造力,将温度值设置为 1。在这个脚本的最后将一张 Pandas 表存储在 out_openai_completion.csv 文件中。它包含162(6x3x3x3) 个观测值,其中两列分别包含提示词和相应的文本补全内容。
"Grocery Stores, Brussels, small",Introducing our new e-commerce payment service - the perfect solution for small Brussels-based grocery stores to easily and securely process online transactions.
"Grocery Stores, Brussels, small", Looking for a hassle-free payment solution for your small grocery store in Brussels? Our new e-commerce payment service is here to simplify your transactions and increase your revenue. Try it now!
现在,可以调用工具来根据 out_openai_completion.csv 文件生成训练文件:
$ openai tools fine_tunes.prepare_data -f out_openai_completion.csv
**这个工具为“提示词−补全文本对”提供了改进建议。**在文本的末尾,它甚至指导继续微调并告诉如何在微调过程完成后使用模型进行预测:
Analyzing...
- Based on your file extension, your file is formatted as a CSV file
- Your file contains 162 prompt-completion pairs
- Your data does not contain a common separator at the end of your prompts.
Having a separator string appended to the end of the prompt makes it clearer to the fine-tuned model where the completion should begin. If you intend to do open-ended generation, then you should leave the prompts empty
- Your data does not contain a common ending at the end of your completions. Having a common ending string appended to the end of the completion makes it clearer to the fine-tuned model where the completion should end.
- The completion should start with a whitespace character (` `). This tends to produce better results due to the tokenization we use.
Based on the analysis we will perform the following actions:
- [Necessary] Your format `CSV` will be converted to `JSONL`
- [Recommended] Add a suffix separator `->` to all prompts [Y/n]: Y
- [Recommended] Add a suffix ending `\n` to all completions [Y/n]: Y
- [Recommended] Add a whitespace character to the beginning of the completion [Y/n]: Y
Your data will be written to a new JSONL file. Proceed [Y/n]: Y
Wrote modified file to `out_openai_completion_prepared.jsonl` Feel free to take a look! Now use that file when fine-tuning:
> openai api fine_tunes.create -t "out_openai_completion_prepared.jsonl" After you’ve fine-tuned a model, remember that your prompt has to end with the indicator string ` ->` for the model to start generating completions, rather than continuing with the prompt. Make sure to include `stop=["\n"]`
so that the generated texts ends at the expected place. Once your model starts training, it will approximately take 4.67 minutes to train a `curie` model, and less for `ada` and `babbage`. Queue will approximately take half an hour per job ahead of you.
在这个过程完成后,将得到一个名为out_openai_completion_prepared. jsonl 的新文件,可以把它发送给 OpenAI 服务器以运行微调过程。请注意,如函数中的消息所解释的那样,提示词的末尾已经添加了字符串 ->,并且所有的补全文本都添加了以 \n 结尾的后缀。
- 使用合成数据集微调模型
以下代码上传文件并进行微调。在本例中,将使用 davinci 作为基础模型,并为生成的模型添加后缀 direct_marketing:
ft_file = openai.File.create(
file=open("out_openai_completion_prepared.jsonl", "rb"),
purpose="fine-tune"
)
openai.FineTune.create(
training_file=ft_file["id"], model="davinci",
suffix="direct_marketing"
)
当微调完成时,将拥有一个适用于任务的新模型。微调所需的时间主要取决于数据集中可用的示例数量、示例中的标记数量,以及所选择的基础模型。
$ openai api fine_tunes.create -t out_openai_completion_prepared.jsonl -m davinci --suffix "direct_marketing"
Upload progress: 100%|| 40.8k/40.8k [00:00<00:00, 65.5Mit/s]
Uploaded file from out_openai_completion_prepared.jsonl:
file-z5mGg(...)
Created fine-tune: ft-mMsm(...)
Streaming events until fine-tuning is complete...
(Ctrl-C will interrupt the stream, but not cancel the fine-tune)
[] Created fine-tune: ft-mMsm(...)
[] Fine-tune costs $0.84
[] Fine-tune enqueued. Queue number: 0
[] Fine-tune started
[] Completed epoch 1/4
[] Completed epoch 2/4
[] Completed epoch 3/4
[] Completed epoch 4/4
通过在命令行中输入 Ctrl+C,可以断开与 OpenAI 服务器的连接,但这样做不会中断微调过程。若要重新连接服务器并获取正在运行的微调作业的状态,可以像下面这样执行命令 fine_tunes.follow
,其中 fine_tune_id
是微调作业的 ID:
$ openai api fine_tunes.follow -i fine_tune_id
如果丢失了fine_tune_id
,则可以通过以下方式显示所有模型:
$ openai api fine_tunes.list
要立即取消微调作业,请执行以下命令:
$ openai api fine_tunes.cancel -i fine_tune_id
要删除微调作业,请执行以下命令:
$ openai api fine_tunes.delete -i fine_tune_id
- 使用经过微调的模型进行文本补全
一旦构建完新模型,便可以通过不同的方式使用它。最简单的测试方法是使用 OpenAI Playground。界面右侧的下拉菜单中搜索它,如图 4-4 所示。所有经过微调的模型都在此列表的底部。选择模型后,可以使用它进行预测
使用经过微调的模型,提示词为 Hotel, NewYork, small ->。在没有进一步指示的情况下,模型自动生成了一则广告,用于推广纽约的一家小型酒店的在线支付服务。
已经通过一个只有 162 个例子的小型数据集获得了出色的结果。对于微调任务,我们通常建议提供几百个例子,最好提供几千个。此外,本例中的训练集是合成的。在理想情况下,训练集应该由专家编写。
使用 OpenAI API 时,我们按照之前的方式使用Completion.create
,只不过需要将新模型的名称作为输入参数。不要忘记以->
结束所有提示词,并将 \n
设置为停止词:
openai.Completion.create(
model="davinci:ft-book:direct-marketing-2023-05-01-15-20-35",
prompt="Hotel, New York, small ->",
max_tokens=100,
temperature=0,
stop="\n"
)
得到以下答案:
<OpenAIObject text_completion id=cmpl-7BTkrdo(...) at
0x7f2(4ca5c220>
JSON: {
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"text": " \"Upgrade your hotel's payment system with our new \
e-commerce service, designed for small businesses.
}
],
"created": 1682970309,
"id": "cmpl-7BTkrdo(...)",
"model": "davinci:ft-book:direct-marketing-2023-05-01-15-20-35",
"object": "text_completion",
"usage": {
"completion_tokens": 37,
"prompt_tokens": 8,
"total_tokens": 45
}
}
Python 开发人员可以使用微调技术根据具体的业务需求来定制 LLM,特别是在电子邮件营销等动态领域中。这种强大的方法可用于定制应用程序所需的语言模型,最终帮助你更好地为客户提供服务并推动业务增长
4.2.5 微调的成本
使用微调模型的成本不低。不仅需要支付模型训练费用,而且在模型准备好后,每次进行预测时需要支付的费用也会比使用 OpenAI 提供的基础模型略高一些。
作为比较,gpt-3.5-turbo 模型的定价是每千个输出标记 0.0020 美元。可见,gpt-3.5-turbo 模型的性价比最高。要了解最新的模型定价,请访问 OpenAI 的 Pricing 页面。
4.3 小结
开发人员可以通过了解提示工程、零样本学习、少样本学习 和微调来创建更高效、针对性更强的应用程序。通过考虑上下文、任务和角色,开发人员可以创建有效的提示词,从而更精确地与模型交互。通过逐步推理,开发人员可以鼓励模型更有效地推理和处理复杂任务。
零样本学习和少样本学习都属于提示工程中的具体技术。提示工程作为一门快速发展的学科,旨在为生成式 AI 模型设计和优化提示词,以获得更高质量的模型响应。截至 2023 年 12 月 2日,除了本书提到的这几种方法,提示工程已经发展出了更多方法,如自洽性(self consistency)、思维树(tree of thoughts)、推理与行动(ReAct)等
总结
- 默认使用零样本学习
- 如果零样本学习因输出要求而不起作用,则使用少样本学习
- 如果零样本学习因任务过于复杂而不起作用,请尝试使用提示工程技巧
- 如果拥有非常专业的大型数据集,并且其他解决方案无法提供足够好的结果,则应将微调作为最后的手段
为了确保成功构建 LLM 驱动型应用程序,开发人员应该尝试其他技术并评估模型的响应准确性和相关性。此外,开发人员应该意识到 LLM 的计算限制,并相应地调整提示词以取得更好的结果。通过整合这些先进技术并不断完善方法,开发人员可以构建强大且富有创意的应用程序,真正释放GPT-4 和 ChatGPT 的潜力。