OpenAI GPT For Python Developers 第六章 微调和最佳实践 Few Shot Learning

发布于 2023年10月28日

GPT-3已经通过大量来自开放网络的数据进行了预训练,其中包含数十亿个单词。这使它能够迅速学习新任务,只需提供少量示例即可。这被称为“few-shot learning”,它在人工智能领域是一项革命性的发展。

毫无疑问,有许多强大的开源few-shot learning项目可用。要实现few-shot learning项目,有一些强大的Python库可以使用:

  • Pytorch–Torchmeta31:用于PyTorch32中的few-shot learning和meta-learning的扩展和数据加载器的集合

  • Few Shot33:包含清晰、易读且经过验证的代码,用于重现few-shot learning研究。

  • FewRel34:一个大规模的few-shot关系提取数据集,包含100多种关系和跨不同领域的数万个已标注实例。

  • Few-Shot Object Detection (FsDet):包含Simple Few-Shot Object Detection的官方few-shot对象检测实现。

  • Meta Transfer Learning:用于解决具有挑战性的few-shot learning设置的框架。该项目的目标是利用多个相似的低样本任务来学习如何修改基础学习器以应对新任务,只提供了少量已标记的样本。

  • Omniglot数据集上的原型网络:在Pytorch中的“Few-shot Learning”上的实现

  • 以及更多

“few-shot learning”的能力使GPT-3能够迅速理解提供给它的指令,即使只有少量数据。换句话说,GPT-3可以编程完成只提供少量示例的任务。这为基于AI的应用和领域开辟了新的无限可能性。

改进Few Shot Learning

微调原理是通过在比提示中能容纳的更多示例上进行训练来改进few-shot learning的过程。这种方法可以用于在许多任务上获得更好的结果:

  • 增加所使用的示例数量

  • 提高结果的准确性

  • 扩展可完成的任务范围

一旦模型经过调整和改进,您就不必在请求时提供示例。这可以节省金钱并加快请求速度。微调适用于主要的基础模型:davinci、curie、babbage和ada。实际微调让我们进入更实际的部分,看看真实的例子。

请注意,这一部分是一个介绍,我们接下来要做的是基础。继续阅读以掌握整体概念,但在本指南的后面,我们将深入探讨更高级的示例。

首先,激活用于开发的Python虚拟环境,然后导出您的OpenAI API密钥:

export OPENAI_API_KEY="<OPENAI_API_KEY>"

如果您使用Windows,可以使用set命令。

让我们创建一个JSONL文件。这是一个示例:

  {"prompt":"When do I have to start the heater?","completion":"Everyday in the morning at 7 AM.You should stop it at 2 PM"}
  {"prompt":"Where is the garage remote control?","completion":"Next to the yellow door,on the key ring"}
  {"prompt":"Is it necessary top rogram the scent diffuser everyday?","completion":"The scent diffuser is already programmed,you just need to recharge it when it sbattery is low"}

JSONL,也称为分行的JSON,是一种用于存储结构化数据的有用格式,可以逐个记录地进行处理。我们的训练数据应使用JSONL格式。

至少需要提供两百个示例进行训练是必要的。此外,将数据集大小加倍将线性提高模型质量。我们在JSONL文件中使用了3行,所以生成的模型性能不佳,但我们正在测试事物,并希望看看它在实践中的工作方式。

openai tools fine_tunes.prepare_data -f data.json
Analyzing...

基于分析,我们将执行以下操作:

  • [必要] 将您的格式`JSON`转换为`JSONL`

  • [推荐] 将所有完成添加后缀`\n`[Y/n]:Y

  • [推荐] 在完成的开头添加一个空白字符[Y/n]:Y

您的数据将写入新的JSONL文件。是否继续[Y/n]:Y

已将修改后的文件写入`data_prepared.jsonl`

随意查看!

现在,使用fine-tuning时,请执行以下步骤:

openai api fine_tunes.create -t "data_prepared.jsonl"

完成fine-tuning后,请记住您的提示必须以指示符字符串`?`结尾,以便模型开始生成完成,而不是继续使用提示。确保包括`stop=["\n"]`,以便生成的文本结束在预期位置。

一旦您的模型开始训练,训练`curie`模型大约需要2.48分钟,而`ada`和`babbage`则需要更少的时间。队列将大约需要每个任务提前半小时。

CLI将创建一个名为“<your_filename>_prepared.jsonl”的文件。我们将在下一步中使用它:

openai api fine_tunes.create -t "data_prepared.jsonl" -m curie

您可以使用curie,或任何其他基础模型(davinci、babbage或ada)。

当fine-tuning完成时,可能需要一些时间,CLI将打印您创建的新模型的名称。

示例:

Job complete!

Status: succeeded

尝试您的微调模型:

openai api completions.create -m curie:ft-learninggpt-2023-02-18-08-38-08 -p <YOUR_PROMPT>

请注意,由于某种原因,操作可能会被中断,在这种情况下,您需要使用以下命令恢复它:

openai api fine_tunes.follow-i <YOUR_FINE_TUNE_JOB_ID> 

您现在可以使用以下命令列出您的微调模型:

openai api fine_tunes.list 

您现在可以使用该模型:

export FINE_TUNED_MODEL="<FINE_TUNED_MODEL>"
openai api completions.create -m $FINE_TUNED_MODEL -p <YOUR_PROMPT>

如果您要使用Python,与我们之前学到的内容相比,唯一不同的是模型ID(FINE_TUNED_MODEL)

openai.Completion.create(
  model=FINE_TUNED_MODEL,
  prompt=YOUR_PROMPT
  # additional parameters
  # temperature,
  # frequency_penalty,
  # presence_penalty
  # ..etc 
)

或使用cURL:

curl https://api.openai.com/v1/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": YOUR_PROMPT, "model": FINE_TUNED_MODEL}'

如果您要分析模型,请运行:

openai api fine_tunes.results -i <YOUR_FINE_TUNE_JOB_ID>

输出应为CSV格式。

示例:

步骤,经过的令牌数,经过的示例数,训练损失,训练序列准确性,训练令牌准确性

1,25,1,1.6863485659162203,0.0,0.36363636363636365

2,58,2,1.4631860547722317,0.0,0.55

3,91,3,1.7541891464591026,0.0,0.23529411764705882

4,124,4,1.6673923087120057,0.0,0.23529411764705882

5,157,5,1.2660537725454195,0.0,0.55

6,182,6,1.440378345052401,0.0,0.45454545454545453

7,215,7,1.1451897841482424,0.0,0.6

8,240,8,1.3461188264936208,0.0,0.45454545454545453

9,273,9,1.3577824272681027,0.0,0.35294117647058826

10,298,10,1.2882517070074875,0.0,0.45454545454545453

11,331,11,1.0392776031675748,0.0,0.65

12,364,12,1.3298884843569247,0.0,0.35294117647058826

13,397,13,1.0371532278927043,0.0,0.65

请注意,可以使用suffix39参数向您的微调模型名称添加后缀(最多40个字符):

openai api fine_tunes.create -t data.jsonl -m <engine> --suffix "my_model_name"

最后,您可以删除模型:

#CLI
openai api models.delete-i <FINE_TUNED_MODEL>
#Python

openai.Model.delete(FINE_TUNED_MODEL)
#cURL
curl -X "DELETE" https://api.openai.com/v1/models/<FINE_TUNED_MODEL> -H "Authorization: Bearer $OPENAI_API_KEY"

这只是一个小而快速的测试,以了解一切是如何工作的。微调模型之所以性能不佳,有两个原因:首先,数据集很小,其次,我们没有应用最佳实践。

我们将在本指南的后面看到更高级的示例,但我们需要首先了解最佳实践。

数据集、提示和完成:最佳实践是什么?

每个提示都应以固定的分隔符结尾

为了使模型能够准确识别提示的结尾和完成文本的开始,包括固定的分隔符非常重要。常用的分隔符是==\n\n###\n\n==,不应在提示的其他地方使用。这将有助于确保模型生成所期望的输出。在我们的高级示例中,我们将看到如何使用分隔符来改进模型。

每个完成都应以空格开始

当使用OpenAI的分词器时,重要的是要注意每个完成都必须以空格开头。这是因为分词器主要使用空格来对大多数单词进行分词。因此,以空格开头可以确保分词器能够准确地对文本进行分词。牢记这一点对于确保分词器正常运行并产生所期望的结果非常重要。

每个完成都应以固定的停止序列结尾

与提示一样,每个完成都应标记有停止序列,以指示完成的结束。停止序列可以是在完成中未使用的任何令牌,例如==\n或###==。对于推理,您应该以与创建训练数据集时相同的方式格式化提示,包括相同的分隔符。还要指定相同的停止序列以正确截断完成文本。

使用更多高质量示例的微调效果更好

要通过使用高质量提示使模型优于基础模型,您需要提供至少几百个高质量示例,最好由人类专家评估,这是OpenAI在其官方文档中推荐的做法。

根据OpenAI进行的测试,随着示例数量呈指数增加,性能往往呈线性改善趋势。因此,增加示例数量是提高模型性能最可靠和有效的方法之一。

编写更好的提示和完成

根据OpenAI的说法,以下训练数据集:

{"prompt":"Productisalaptop.BrandisDell.ModelisInspiron.RAM is 8GB.Hard\ drive is 256GB SSD.->","completion":"The Dell Inspiron laptop comes equipped with 8GB of RAM and a 256GB SSD, making it a fast and reliable option for all your computing needs.END"} {"prompt":"The book is a thriller.Author is James Patterson.Main character is a detective.Setting is NewYork City.->","completion":"If you're a fan of thrillers,you'll love this book by James Patterson featuring a detective in the heart of New York City. Get ready for a heart-pounding read. END"} {"prompt":"The recipe is for spaghetti carbonara.Ingredients are spaghetti,bacon,eggs, parmesan cheese. Difficulty level is easy.->", "completion": " Looking for an easy and delicious dinner option? Try this spaghetti carbonara recipe that features crispy bacon, creamy eggs, and freshly grated parmesan cheese. You won't be disappointed!END"} 

比以下示例效果更好:

{"prompt":"Product:laptop.Brand:Dell.Model:Inspiron.RAM:8GB.Harddrive:256GBSSD.->","completion":"The Dell Inspironlaptopcomesequipped with 8GB of
RAM and a 256GB SSD, making it a fast and reliable option for all your computing ne\
eds.END"}
{"prompt":"Thebook:thriller.Author:JamesPatterson.Maincharacter:detectiv\
e.Setting:NewYorkCity.->","completion":"Ifyou'reafanofthrillers,you'll\
love this book by James Patterson featuring a detective in the heart of New York Ci\
ty.Getreadyforaheart-poundingread.END"}
{"prompt":"Therecipe:forspaghetticarbonara.Ingredients:spaghetti,bacon,eg\
 gs,parmesancheese.Difficultylevel:easy.->","completion":"Lookingforaneas\
 yanddeliciousdinneroption?Trythisspaghetticarbonararecipethatfeaturescri\
 spybacon,creamyeggs,andfreshlygratedparmesancheese.Youwon't be disappointed!END"}

将输入数据转化为自然语言很可能会获得更好的性能。这在构建生成模型时更加明显。

审查数据以查找冒犯性内容

在微调现有数据集而不是从头开始创建提示时,重要的是手动审查数据,以查找冒犯性或不准确的内容,特别是如果您正在构建一个公开使用的应用程序。

如果由于某种原因您无法审查整个数据集,仍然可以采取措施确保对代表性样本进行评估。一种方法是尽可能多地审查随机样本。通过这样做,您仍然可以了解数据并识别可能存在的任何模式或趋势。

审查数据集的类型和结构

关键是要确保用于微调的数据集在结构和任务类型上与模型将用于的情况相似,显然,它应包含足够数量的相关数据以提高模型的性能。

使用不相似或不足的数据集对模型进行微调可能会导致次优的结果。

分析您的模型

我们使用命令openai api fine_tunes.results -i <YOUR_FINE_TUNE_- JOB_ID>来分析模型。结果以CSV格式呈现,就像我们之前看到的那样。以下是每列的说明:

1. “step”:此列显示训练步骤编号或训练过程的迭代次数。

2. “elapsed_tokens”:显示到目前为止训练过程处理的令牌数量。令牌是文本的单位,例如单词或标点符号。

3. “elapsed_examples”:这是到目前为止训练过程处理的示例(即文本片段)数量。

4. “training_loss”:此数字显示训练期间损失函数的值。(损失函数是衡量模型性能的方式,较低的值表示更好的性能。)

5. “training_sequence_accuracy”:模型在预测下一个令牌序列方面的准确性。(序列是一组形成有意义的单元的令牌,例如句子或段落。)

6. “training_token_accuracy”:此值告诉我们模型在预测个别令牌方面的准确性。

一般来说,训练过程的目标是最小化训练损失,同时最大化训练序列和令牌的准确性。这些措施显示模型能够生成出色的自然语言文本。

一些第三方工具,如wandb,也可以用于分析结果。

如果需要,使用验证数据

您可以考虑将一些数据留出用于验证。这样,您可以在训练模型的同时监视其性能。

在OpenAI CLI中,您可以创建一个与训练文件具有相同格式的验证文件,并在使用“openai api fine_tunes.create”命令创建微调作业时包括它。

例如,您可以创建一个名为“validation_data.jsonl”的验证文件,并在创建微调作业时使用以下命令包括它:

openai api fine_tunes.create -t train_data.jsonl -v validation_data.jsonl -m <engine>

在训练期间,OpenAI CLI将定期计算验证数据批次的指标,并将它们包含在结果文件中。生成的验证指标包括:

  • validation_loss:验证批次的损失

  • validation_sequence_accuracy:验证批次中完成的百分比,其中模型预测的令牌与真实的完成令牌完全匹配

  • validation_token_accuracy:模型正确预测的验证批次中的令牌百分比

微调超参数

在机器学习中,超参数是控制学习过程的参数,而其他参数的值(通常是节点权重)是通过训练确定的。

OpenAI配置了适用于各种用例的默认超参数值。

然而,在微调期间调整超参数可以使模型产生更高质量的输出。

以下是其中一些:

  • n_epochs:这代表了训练算法将循环遍历整个数据集的次数。使用更多的周期可以提高模型的性能,但也可能导致过度拟合训练数据。一般来说,过度拟合发生在模型过于专注于训练数据中的特定细节和变化时,导致在提供新数据时性能下降。本质上,模型最终会将训练数据中的噪音和其他不相关因素视为实际概念。

  • batch_size:更大的批量大小可以加快训练速度,但也可能需要更多内存,并降低模型的泛化性能。默认的OpenAI批量大小约为训练集中示例数量的0.2%,上限为256。批处理大小是模型在每次迭代(单个前向和反向传播)中训练的示例数量。如果您对此概念不熟悉,以下示例提供了一个简单的说明:想象一下,您正在学习如何解决数学问题。批处理大小就像您的老师一次给您几个问题来解决,然后再检查答案。如果您有很多问题要解决,一次性解决可能会很难,所以您的老师可能会一次给您几个问题。训练机器学习模型也是如此。批处理大小是模型在检查是否正确的之前一次处理的示例数量。如果您有很多示例,模型可能需要一次处理几个示例,就像您可能一次解决几个数学问题一样。

  • learning_rate_multiplier:此参数确定微调学习率,即用于预训练的原始学习率与此乘数相乘。OpenAI API根据批量大小设置此参数,默认值为0.05、0.1或0.2。

为了更好地理解这个参数,让我们首先考虑其他概念。

在机器学习中,“最小值”是用于衡量模型性能的数学函数的最低点。学习算法的目标是调整模型的参数,使其可以达到这一最小点并实现最佳性能。然而,如果学习率设置得太高,模型可能会超越最小值,最终在最小值周围振荡,甚至偏离最小值。这可能导致模型进行不正确的预测,并降低其整体性能。为了防止模型超越最小值,选择平衡学习速度和模型稳定性的最佳学习率非常重要。测试是您的朋友,因为不同的学习率可能对不同类型的数据集或模型效果更好。

  • compute_classification_metrics:此指标特定于用于分类任务的微调。如果设置为True,它会计算验证集上的分类特定指标(如准确率和F-1分数)。这些指标可以帮助您评估模型的性能并进行必要的调整。

值得注意的是,训练和测试模型所需的时间可能会受到超参数选择的影响,因为超参数的选择可能会影响模型的复杂性以及其泛化到新数据的能力。

使用Ada

在解决分类问题时,Ada模型是一个不错的选择。它在微调后的性能仅略低于Davinci,同时速度更快、更经济。

使用单令牌类别

假设您正在对句子进行分类,并将其分到某些类别中。考虑以下示例:

{prompt:"TheLosAngelesLakerswontheNBAchampionshiplastyear.",completion:"s\
portsandentertainment"}
{prompt:"AppleissettoreleaseanewiPhonemodelinthecomingmonths.",completi\
on:"technologyandscience"}
{prompt:"TheUnitedStatesCongresspasseda$1.9trillionCOVID-19reliefbill.",c\
ompletion:"politicsandgovernment"}
{prompt:"TheTokyoOlympicswerepostponedto2021duetotheCOVID-19pandemic.",c\
ompletion:"sportsandentertainment"}
{prompt:"Tesla'smarketcapitalizationsurpassedthatofToyotain2020.",completio\
n:"technologyandscience"}
{prompt:"JoeBidenwasinauguratedasthe46thPresidentoftheUnitedStatesonJan\
uary20,2021.",completion:"politicsandgovernment"}
{prompt:"NovakDjokovicwontheAustralianOpentennistournamentfortheninthtime\
in 2021.", completion: "sports and entertainment"}
{prompt:"Facebookwasfined$5billionbytheUSFederalTradeCommissionforprivac\
yviolations.",completion:"technologyandscience"}
{prompt:"TheUKofficiallylefttheEuropeanUniononJanuary31,2020.",completion\
:"politicsandgovernment"}
{prompt:"TheTampaBayBuccaneerswontheSuperBowlin2021,ledbyquarterbackTom\
Brady.", completion: "sports and entertainment"}

您可以注意到这里有3个类别:

  • 体育和娱乐

  • 科技和科学

  • 政治和政府

与将它们用作类别名称不同,更明智的做法是使用单个令牌,如:

  • 1(代表体育和娱乐)

  • 2(代表科技和科学)

  • 3(代表政治和政府)

{prompt:"TheLosAngelesLakerswontheNBAchampionshiplastyear.",completion:"1"}
{prompt:"AppleissettoreleaseanewiPhonemodelinthecomingmonths.",completion:"2"}
{prompt:"TheUnitedStatesCongresspasseda$1.9trillionCOVID-19reliefbill.",completion:"3"}
{prompt:"TheTokyoOlympicswerepostponedto2021duetotheCOVID-19pandemic.",completion:"1"}
{prompt:"Tesla'smarketcapitalizationsurpassedthatofToyotain2020.",completion:"2"}
{prompt:"JoeBidenwasinauguratedasthe46thPresidentoftheUnitedStateson January20,2021.",completion:"3"}
{prompt:"Novak Djokovicwonthe Australian Open tennistournament for the ninth time in 2021.", completion: "1"}
{prompt:"Facebookwasfined$5billionbytheUSFederalTradeCommissionforprivacyviolations.",completion:"2"}
{prompt:"TheUKofficiallylefttheEuropeanUniononJanuary31,2020.",completion:"3"}
{prompt:"TheTampaBayBuccaneerswontheSuperBowlin2021,ledbyquarterback Tom Brady.", completion: "1"}

这样做不仅会减少训练数据,而且在推断时令牌的数量只有1。

openai.Completion.create(
  engine=<engine>,
  max_tokens=1,
)

分类的其他考虑因素

  • 确保提示和完成组合不超过2048个令牌,包括分隔符。

  • 尽量为每个类别提供至少100个示例。

  • 分隔符不应在提示文本内使用。如果有这种情况,应从提示文本中删除它。例如:如果您的分隔符是`!#!`,则应预处理提示文本以删除任何`!#!`。



评论