微调分类
本篇文章讲述了使用GPT-3对文本进行分类的最佳实践,并分享了微调GPT-3以进行文本分类的方法。文章包括了数据探索和数据准备等环节,并介绍了如何使用Sklearn和Pandas等工具对数据集进行操作。同时还介绍了数据准备工具的使用方法。本文适合对GPT-3文本分类感兴趣的读者学习参考。
正文
我们将微调 ada 分类器以区分两种运动:棒球和曲棍球。
from sklearn.datasets import fetch_20newsgroups import pandas as pd import openai categories = ['rec.sport.baseball', 'rec.sport.hockey'] sports_dataset = fetch_20newsgroups(subset='train', shuffle=True, random_state=42, categories=categories)
数据探索
可以使用 sklearn 加载新闻组数据集。 首先,我们将查看数据本身:
print(sports_dataset['data'][0])
来自:dougb@comm.mot.com(道格银行) 主题:回复:克利夫兰门票所需的信息 回复:dougb@ecs.comm.mot.com 组织机构:摩托罗拉陆地移动产品部门 分布:美国 Nntp 发布主机:145.1.146.35 线路:17 在文章 <1993Apr1.234031.4950@leland.Stanford.EDU> 中,bohnert@leland.Stanford.EDU (matthew bohnert) 写道: |> 我将于 4 月 15 日星期四至 4 月 18 日星期日在克利夫兰。 |> 有谁知道部落是否会在那些日期进城,以及 |> 如果是这样,他们在和谁比赛,是否有门票? 该部落将于 4 月 16 日至 19 日进城。 总是有可用的门票! (虽然他们在玩多伦多, 许多多伦多球迷会前往克利夫兰,因为那里更容易 在克利夫兰买票比在多伦多买票。 不管怎样,我是认真的 怀疑他们会在本季结束前售罄。) -- 道格银行私人系统部 dougb@ecs.comm.mot.com 摩托罗拉通信部门 dougb@nwu.edu 伊利诺伊州绍姆堡 dougb@casbah.acns.nwu.edu 708-576-8207
sports_dataset.target_names[sports_dataset['target'][0]]
'rec.sport.baseball'
len_all, len_baseball, len_hockey = len(sports_dataset.data), len([e for e in sports_dataset.target if e == 0]), len([e for e in sports_dataset.target if e == 1]) print(f"Total examples: {len_all}, Baseball examples: {len_baseball}, Hockey examples: {len_hockey}")
示例总数:1197,棒球示例:597,曲棍球示例:600
上面可以看到棒球类别的一个示例。 它是一封发送到邮件列表的电子邮件。 我们可以观察到我们总共有 1197 个例子,在这两项运动之间平均分配。
数据准备
我们将数据集转换为 pandas 数据框,其中有一列用于提示和完成。 提示包含来自邮件列表的电子邮件,完成是运动的名称,曲棍球或棒球。 仅出于演示目的和微调速度,我们仅采用 300 个示例。 在实际用例中,示例越多性能越好。
import pandas as pd labels = [sports_dataset.target_names[x].split('.')[-1] for x in sports_dataset['target']] texts = [text.strip() for text in sports_dataset['data']] df = pd.DataFrame(zip(texts, labels), columns = ['prompt','completion']) #[:300] df.head()
提示 | 完成 | |
---|---|---|
0 | From: dougb@comm.mot.com (Doug Bank)\nSubject:… | 棒球 |
1 | From: gld@cunixb.cc.columbia.edu (Gary L Dare)… | 曲棍球 |
2 | From: rudy@netcom.com (Rudy Wade)\nSubject: Re… | 棒球 |
3 | From: monack@helium.gas.uug.arizona.edu (david… | 曲棍球 |
4 | Subject: Let it be Known\nFrom: <ISSBTL@BYUVM…. | 棒球 |
棒球和曲棍球都是单一代币。 我们将数据集保存为 jsonl 文件。
df.to_json("sport2.jsonl", orient='records', lines=True)
数据准备工具
我们现在可以使用数据准备工具,它会在微调之前对我们的数据集提出一些改进建议。 在启动该工具之前,我们更新了 openai 库以确保我们使用的是最新的数据准备工具。 我们另外指定 -q 自动接受所有建议。
!pip install --upgrade openai
!openai tools fine_tunes.prepare_data -f sport2.jsonl -q
分析... - 您的文件包含 1197 个提示完成对 - 根据您的数据,您似乎正在尝试微调分类模型 - 对于分类,我们建议您尝试一种更快、更便宜的模型,例如`ada` - 对于分类,您可以通过保留一个未用于训练的数据集来估计预期的模型性能 - 有 11 个例子很长。 这些是行:[134、200、281、320、404、595、704、838、1113、1139、1174] 对于条件生成和分类,示例不应超过 2048 个标记。 - 您的数据在提示末尾不包含公共分隔符。 在提示的末尾附加一个分隔符字符串可以使微调模型更清楚应该从哪里开始完成。 有关更多详细信息和示例,请参阅 https://beta.openai.com/docs/guides/fine-tuning/preparing-your-dataset。 如果你打算做开放式生成,那么你应该将提示留空 - 完成应以空白字符 (` `) 开头。 由于我们使用的标记化,这往往会产生更好的结果。 有关详细信息,请参阅 https://beta.openai.com/docs/guides/fine-tuning/preparing-your-dataset 根据分析,我们将执行以下操作: - [推荐] 删除 11 个长示例 [Y/n]: Y - [推荐] 为所有提示添加后缀分隔符 `\n\n###\n\n` [Y/n]: Y - [推荐] 补全开头加一个空白字符 [Y/n]: Y - [推荐] 是否要拆分成训练集和验证集? [是/否]:是 您的数据将写入新的 JSONL 文件。 继续 [是/否]:是 将修改后的文件写入 `sport2_prepared_train.jsonl` 和 `sport2_prepared_valid.jsonl` 随便看看吧! 现在在微调时使用该文件: > openai api fine_tunes.create -t "sport2_prepared_train.jsonl" -v "sport2_prepared_valid.jsonl" --compute_classification_metrics --classification_positive_class "棒球" 微调模型后,请记住,您的提示必须以指示符字符串"\n\n###\n\n"结束,模型才能开始生成补全,而不是继续提示。 一旦您的模型开始训练,训练 `curie` 模型大约需要 30.8 分钟,而 `ada` 和 `babbage` 则更少。 每个工作在您前面排队大约需要半小时。 该工具有助于对数据集提出一些改进建议,并将数据集拆分为训练集和验证集。 提示和完成之间的后缀是必要的,以告诉模型输入文本已停止,现在需要预测类别。 由于我们在每个示例中使用相同的分隔符,因此该模型能够了解它是为了预测分隔符后的棒球或曲棍球。 补全中的空格前缀很有用,因为大多数单词标记都是用空格前缀标记的。 该工具还认识到这可能是一项分类任务,因此它建议将数据集拆分为训练数据集和验证数据集。 这将使我们能够轻松衡量新数据的预期性能。
微调
该工具建议我们运行以下命令来训练数据集。 由于这是一项分类任务,我们想知道我们的分类用例在提供的验证集上的泛化性能如何。 该工具建议添加 --compute_classification_metrics --classification_positive_class
” baseball
” 以计算分类指标。
我们可以简单地从 CLI 工具中复制建议的命令。 我们特别添加 -m ada
来微调更便宜和更快的 ada 模型,该模型在性能上通常与分类用例中更慢和更昂贵的模型相当。
!openai api fine_tunes.create -t "sport2_prepared_train.jsonl" -v "sport2_prepared_valid.jsonl" --compute_classification_metrics --classification_positive_class " baseball" -m ada
上传进度:100%|████████████████████| 1.52M/1.52M [00:00<00:00, 1.81Mit/s] 从 sport2_prepared_train.jsonl 上传文件:file-Dxx2xJqyjcwlhfDHpZdmCXlF 上传进度:100%|███████████████████████| 388k/388k [00:00<00:00, 507kit/s] 从 sport2_prepared_valid.jsonl 上传文件:file-Mvb8YAeLnGdneSAFcfiVcgcN 创建微调:ft-2zaA7qi0rxJduWQpdvOvmGn3 流式传输事件直到微调完成... (Ctrl-C 会中断流,但不会取消微调) [2021-07-30 13:15:50] 创建微调:ft-2zaA7qi0rxJduWQpdvOvmGn3 [2021-07-30 13:15:52]微调入队。 队列号:0 [2021-07-30 13:15:56]微调开始 [2021-07-30 13:18:55] 完成纪元 1/4 [2021-07-30 13:20:47] 完成纪元 2/4 [2021-07-30 13:22:40] 完成纪元 3/4 [2021-07-30 13:24:31] 完成纪元 4/4 [2021-07-30 13:26:22] 上传模型:ada:ft-openai-2021-07-30-12-26-20 [2021-07-30 13:26:27] 上传结果文件:file-6Ki9RqLQwkChGsr9CHcr1ncg [2021-07-30 13:26:28]微调成功 工作完成! 状态:成功🎉 试用您的微调模型: openai api completions.create -m ada:ft-openai-2021-07-30-12-26-20 -p <YOUR_PROMPT> 模型在十分钟左右训练成功。 我们可以看到模型名称是 ada:ft-openai-2021-07-30-12-26-20,我们可以使用它来进行推理。
[高级] 结果和预期的模型性能
我们现在可以下载结果文件以观察在保留的验证集上的预期性能。
!openai api fine_tunes.results -i ft-2zaA7qi0rxJduWQpdvOvmGn3 > result.csv
results = pd.read_csv('result.csv') results[results['classification/accuracy'].notnull()].tail(1)
step | elapsed_tokens | elapsed_examples | training_loss | training_sequence_accuracy | training_token_accuracy | classification/accuracy | classification/precision | classification/recall | classification/auroc | classification/auprc | classification/f1.0 | validation_loss | validation_sequence_accuracy | validation_token_accuracy | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
929 | 930 | 3027688 | 3720 | 0.044408 | 1.0 | 1.0 | 0.991597 | 0.983471 | 1.0 | 1.0 | 1.0 | 0.991667 | NaN | NaN | NaN |
results[results['classification/accuracy'].notnull()]['classification/accuracy'].plot()
输入:
<AxesSubplot:>
使用模型
我们现在可以调用模型来获得预测。
test = pd.read_json('sport2_prepared_valid.jsonl', lines=True) test.head()
prompt | completion | |
---|---|---|
0 | From: gld@cunixb.cc.columbia.edu (Gary L Dare)… | hockey |
1 | From: smorris@venus.lerc.nasa.gov (Ron Morris … | hockey |
2 | From: golchowy@alchemy.chem.utoronto.ca (Geral… | hockey |
3 | From: krattige@hpcc01.corp.hp.com (Kim Krattig… | baseball |
4 | From: warped@cs.montana.edu (Doug Dolven)\nSub… | baseball |
我们需要按照我们在微调期间使用的提示使用相同的分隔符。 在这种情况下,它是 \n\n###\n\n
。 由于我们关心的是分类,所以我们希望温度尽可能低,我们只需要一个令牌完成来确定模型的预测。
ft_model = 'ada:ft-openai-2021-07-30-12-26-20' res = openai.Completion.create(model=ft_model, prompt=test['prompt'][0] + '\n\n###\n\n', max_tokens=1, temperature=0) res['choices'][0]['text']
' hockey'
要获取对数概率,我们可以在完成请求中指定 logprobs 参数。
res = openai.Completion.create(model=ft_model, prompt=test['prompt'][0] + '\n\n###\n\n', max_tokens=1, temperature=0, logprobs=2) res['choices'][0]['logprobs']['top_logprobs'][0]
<OpenAIObject at 0x7fe114e435c8> JSON: { " baseball": -7.6311407, " hockey": -0.0006307676 }
我们可以看到该模型预测曲棍球的可能性比棒球大得多,这是正确的预测。 通过请求 log_probs,我们可以看到每个类别的预测(对数)概率。
概括
有趣的是,我们的微调分类器非常通用。 尽管接受了针对不同邮件列表的电子邮件的训练,它也成功地预测了推文。
sample_hockey_tweet = """Thank you to the @Canes and all you amazing Caniacs that have been so supportive! You guys are some of the best fans in the NHL without a doubt! Really excited to start this new chapter in my career with the @DetroitRedWings !!""" res = openai.Completion.create(model=ft_model, prompt=sample_hockey_tweet + '\n\n###\n\n', max_tokens=1, temperature=0, logprobs=2) res['choices'][0]['text']
' hockey'
sample_baseball_tweet="""BREAKING: The Tampa Bay Rays are finalizing a deal to acquire slugger Nelson Cruz from the Minnesota Twins, sources tell ESPN.""" res = openai.Completion.create(model=ft_model, prompt=sample_baseball_tweet + '\n\n###\n\n', max_tokens=1, temperature=0, logprobs=2) res['choices'][0]['text']
' baseball'
评论 (0)