将学习怎样为你的应用定制一模型
1 介绍
微调让你获得更多的可用模型通过API提供:
- 比提示工程返回更高质量的结果
- 能够训练更多的样本,而不只是一个提示工程
- 较短的提示工程从而节省了token
- 更低的延时请求
OpenAI的文本生成模型已经在一个巨大的文本上经过预训练。为了高效的使用模型,我们引入了一些指令,有时是一些样本在提示词中。使用案例展示怎样执行一个任务,也就是学说的"少量样本学习"
微调通过训练比提示符所能容纳的更多的示例来改进少样本学习,让你在完成大量的任务中获得更好的结果。一旦一个模型经过微调,你就无需在提示词中提供那么多的样本。既节约了token成本又降低了延时请求
在更高一个级别的微调中,包含以下步骤:
- 准备并上传训练数据
- 训练一个新的微调模型
- 评估结果,如果需要返回第1步重做
- 应用中使用你的微调模型
2 什么样的模型可以微调?
微调可用于下面的模型:gpt-3.5-turbo-0125 (推荐), gpt-3.5-turbo-1106, gpt-3.5-turbo-0613, babbage-002, davinci-002, and gpt-4-0613 (实验). 您还可以微调微调模型,如果您获得额外的数据并且不想重复前面的训练步骤,这将非常有用。我们期望gpt-3.5 turbo在结果和易用性方面成为大多数用户的正确模型。
3 使用微调的时机
微调OpenAI文本生成模型对于特殊应用可以使用它们变的更强,但对于投入的时间和努力要谨慎。我们推荐为得到想要的结果先尝试,使用提示工程、提示链(把复杂的任务分成多个提示词)和函数调用,主要原因如下:
- 有许多任务我们的模型一开始表现不是很好,但如果使用恰当的提示词结果是可以改善的,因此微调可能并不需要
- 迭代所有的提示词和其它的别的一些策略要比迭代微调快的多,微调需要创建数据集和进行训练
- 在使用微调的情况下仍然需要,最初的提示工程并不是在浪费时间。当在微调数据中使用一个好的提示词,我们通常也会得到一个好的结果。(结合提示链和微调工具一起使用) 我们的提示工程指南提供了一些最有效的策略和方法的背景,这些策略和战术可以在不进行微调的情况下获得更好的表现。您可能会发现在我们的后台中快速迭代提示很有帮助。
4 常用案例
使用微调改善结果的一些常用案例:
- 设定风格、语气、格式或其他特性方面
- 为获得想要的输出结果提供可靠性
- 纠正无法遵循复杂提示的错误
- 用特殊的方式处理很多边缘案例
- 执行一个很难用提示词进行表述的技能或任务
当一个任务容易展示但不容易复述的情况下,在更高层面考虑这些案例。在接下来的章节中,我们将探讨如何设置用于微调的数据,以及各种微调可以提高基准模型性能的示例。在不牺牲质量的情况下,通过替换GPT-4或使用更短的提示来降低成本和/或延迟是有效的微调的另一个场景。如果使用GPT-4可以获得良好的效果,那么通过对GPT-4进行微调,可以缩短指令提示时间,使用经过微调的gpt-3.5 turbo模型也可以达到类似的效果。
5 准备数据集
一旦你下决心只有微调才能正确的解决问题(例如:你已经优化了你的提示词,但模型使用依然存在问题),你需要准备训练模型的数据。您应该创建一组不同的演示对话,这些对话类似于您将要求模型在生产中的推理时响应的对话。 在数据集里每一个样本要和我们的聊天实例API格式相同,特别是每一个消息列表要有一个角色,内容和可选的名称。至少其中的一些训练样本应该是目标案例,提示模型的行为不符合预期的,并且在数据中提供助理消息,是你想让模型提供的理想的反馈。 例子格式 在这个盒子中,我们的目标是创建一个聊天机器人,有时给出滑稽的回答,我们为数据集创建3个训练样本:
json
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}
会话的聊天格式要符合gpt-3.5-turbo的要求。
json
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
多轮会话的例子 聊天格式的案例可以具有助手角色的多条消息。在默认的微调过程中在一个单独的案例会训练所有的助手消息。为了跳过微调中特殊的助手消息,可加weight关键字屏蔽微调中的消息,允许你控制哪一条消息可学习。weight的值为0-1之间。下面是一些案例:
json
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris", "weight": 0}, {"role": "user", "content": "Can you be more sarcastic?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already.", "weight": 1}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "William Shakespeare", "weight": 0}, {"role": "user", "content": "Can you be more sarcastic?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?", "weight": 1}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "384,400 kilometers", "weight": 0}, {"role": "user", "content": "Can you be more sarcastic?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters.", "weight": 1}]}
制作提示 我们通常推荐对模型作用最好的指令或提示词优先提供给微调,并在每个训练样本都包含他们,这使用你能获得最好和最能用的结果,特别是你拥有相当少的样本时(例如:在100以下)
如果你想缩短在每一个样本中指令和提示词以节约花费,记忆模型可能的行为,如果它已经包含这些指令或提示词,并且当在推理的时候很难使模型忽略这些内置的指定
可能需要更多的训练样本使模型达到一个好的结果,因为模型必须完全通过演示来学习,而不需要指导。
样本数量推荐 对于微调一个模型,你最少需要提供10个样本。在gpt-3.5-turbo中使用50-100训练样本,我们通常可以看到一个明显的改进,但真正的数量非常依赖样本的正确使用。
我们推荐一开始用50个精心设计的演示案例,观察模型在微调之后是否有改善,在某些情况下案例可能不够,但即使模型仍然没有达到生产质量,清晰的结果改善是一个好的迹象,提供更多的样本以持续改善模型。没有任何改进表明您可能需要重新考虑如何为模型设置任务或在扩展到有限的示例集之前重组数据。
分割训练集和测试集 在收集初始的数据集后,我们推荐将数据集分割为训练集和测试集两部分,当把训练集和测试集提交给一个微调任务时,我们会提供它们训练过程的统计信息,这些统计将作为模型有多少改善的依据,另外,一开始就构造一个测试数据集是很有必要的,使你可以在训练结束后对模型进行评估。
Token限制 Token限制依赖于模型的选择,对于gpt-3.5-turbo-0125,最大上下文长度为16,385,因此每个训练示例也限制为16,385个Tokens。对于gpt-3.5-turbo-0613,每个训练示例限制为4,096个Tokens。超过默认值的示例将被截断为最大上下文长度,从而从训练示例的末尾删除Tokens。为了确保您的整个训练示例符合上下文,请考虑检查消息内容中的总Tokens计数是否低于限制。
估算成本 请参阅定价页面了解每1k输入和输出代币的详细成本(我们不收取作为验证数据一部分的代币)。要估算特定微调工作的成本,请使用以下公式:
1k Tokens 价格 * Token数量 * 训练次数
对于在3个epoch中训练了100,000个令牌的训练文件,预期成本约为2.40美元。
检查数据格式 一旦你已经编排了一个数据集,在创建一个微调任务之前,需要着重检查数据格式。为了这点,我们创建了一个简单的Python脚本,以排查一些基本的错误,检查token数量并评估微调成本
上传训练文件 一旦你已经有了验证集,为了开始微调,验证集需要使用以下API进行上传:
python
from openai import OpenAI
client = OpenAI()
client.files.create(
file=open("mydata.jsonl", "rb"),
purpose="fine-tune"
)
在上传文件之后,还需要花一些时间进行后台处理。当文件在处理中,你仍然可以创建一个微调任务,但不会马上开始,直到上传的文件处理完成。
上传文件最大为1G,然而我们不建议使用这么多数据进行微调,因为你不大可能需要这么大的数据才看到模型改善。
6 创建微调模型
在确认你有足够的数量和正确的结构的数据集,并上传之后,接下来的一步是创建微调任务。我们支持通过fine-tuning UI和编程的方式创建微调任务: 使用OpenAI SDK:
python
from openai import OpenAI
client = OpenAI()
client.fine_tuning.jobs.create(
training_file="file-abc123",
model="gpt-3.5-turbo"
)
在这个例子中,model是你想要微调的模型的名字(gpt-3.5-turbo, babbage-002, davincii -002,或者一个现有的微调模型),training_file是训练文件上传到OpenAI API时返回的文件ID。您可以使用后缀参数定制您的微调模型的名称。 要设置额外的微调参数,如validation_file或超参数,请参考API规范进行微调。
在你开始一个微调任务后,需要花费一些时间来完成。在我们的系统中你的任务被放在队列中,可能排在其它任务之后,训练一个模型可能花费几分钟或几小时,它依赖于你选择的模型和数据集的大小。在模型被训练完成后,会收到一封邮件。
除了创建微调任务外,你也可以查看所有任务列表,查看某一个任务的状态或取消任务等
python
from openai import OpenAI
client = OpenAI()
# List 10 fine-tuning jobs
client.fine_tuning.jobs.list(limit=10)
# Retrieve the state of a fine-tune
client.fine_tuning.jobs.retrieve("ftjob-abc123")
# Cancel a job
client.fine_tuning.jobs.cancel("ftjob-abc123")
# List up to 10 events from a fine-tuning job
client.fine_tuning.jobs.list_events(fine_tuning_job_id="ftjob-abc123", limit=10)
# Delete a fine-tuned model (must be an owner of the org the model was created in)
client.models.delete("ft:gpt-3.5-turbo:acemeco:suffix:abc123")