GPT模型微调教程:打造您专属的ChatGPT模型

一、前言

通过前面对大语言模型的介绍,我们了解到,其实像目前已经发布的一些主流大语言模型如OpenAI公司发布的的GPT-3.5、GPT-4、Google发布的Bard和PaLM 2多模态模型、Anthropic最近推出的Claude 2、亦或是国内清华大学联合智普AI发布的ChatGLM 2等都是属于通用型的 LLM 模型,基于海量的互联网数据进行训练推理而来,因为涉及领域广泛,你会发现这些模型或多或少都会存在对某些知识不熟悉或者回答错误的情况,加之很多涉密性质的数据无法获取,所以在一定程度来讲,虽然可以解决大部分的问题。但对于某些特定的领域,现有的模型能力依然会存在一定的局限性,这个时候我们就需要针对模型未摄取的知识进行有效的补充和微调来释放模型的能力,使得模型能够更好地适应特定用例或领域的细微差别,从而获得更准确的结果。

除了模型微调之外,前面我们也介绍过使用 LangChain 结合 LLM 来构建自定义知识库的应用案例,这种方式不需要做任何微调,主要是将我们本地的数据通过 Embedding 构建向量索引,然后结合大语言模型的语义理解和向量检索结果进行检索,也能解决扩充模型知识库的目的,来解决企业一些实际的问题。这种解决方案相较于模型微调训练属于是成本非常低了,所以前一阵由于GPT模型的火热和基于GPT构建知识库的刚性需求,一度将向量数据库推向了高潮,各种顶尖AI公司和独角兽都推出了各自的向量数据库,对于历史基于PostgreSQL开发的应用也提供了轻量化实现向量检索的解决方案,连MySQL似乎都没那么香了。

并不是因为有了向量相似度检索技术,加之模型微调成本高,这种方案就没有意义了,其实不然,真正想在某一特定领域推出具备自己核心竞争力的AI产品,最合适的路依然还是基于目前开源或者闭源的一些 LLM 来结合自己的业务数据进行微调,训练出更智能,更人性化的模型产品,前一段时间我们介绍了针对本地开源模型的微调方案《开源LLM微调训练指南:如何打造属于自己的LLM模型》,今天我们主要介绍基于OpenAI的GPT模型进行微调构建属于你个人的模型。

二、什么是模型微调?

在具体介绍如何进行模型微调之前,我们需要先弄清楚模型微调和模型开发训练是完全不同的概念,在很多大语言模型发布的论文上会介绍模型训练的数据集规模,训练环境和推理时间,这些是说明模型从0训练推理到评估成熟的完整过程需要耗费的时间和成本,一般的个人或者公司几乎是很难承担这种费用投入的,而模型微调其实是指在已经训练好的模型基础上,通过进一步在自定义用例数据集上训练 GPT-3 的过程。通常用于提高模型的性能,这使得模型能够更好地适应特定用例或领域的细微差别,从而获得更准确的结果。

如果你需要对GPT模型进行微调,首先你需要了解GPT模型是一个自然语言处理模型,用于生成文本。它在大规模的语料库上进行了预训练,学习了语言的结构和规律。但是,由于每个任务或数据集都有自己的特点和要求,所以通常需要对GPT模型进行微调,使其更适合你的具体任务。

微调GPT模型的步骤通常包括以下几个步骤:

  1. 准备数据集:首先,你需要准备一个与你的任务相关的数据集。这个数据集应该包括输入文本和对应的目标输出。例如,如果你的任务是生成电影评论,那么你的数据集应该包括电影评论和相应的情感标签。
  2. 调整模型结构:GPT模型通常由多层的Transformer网络组成。你可以根据任务的需要,对模型的结构进行微调。例如,添加一些任务特定的层或调整模型的层数。
  3. 重置模型权重:将GPT模型的权重初始化为预训练的权重,并冻结这些权重。这样做是为了保留模型在预训练阶段学到的知识,并防止在微调过程中过度调整模型。
  4. 微调模型:使用准备好的数据集,通过反向传播算法,更新模型的权重。在微调过程中,你可以调整学习率、批次大小等超参数,以优化模型的性能。
  5. 评估和优化:在微调的过程中,你可以周期性地使用验证集来评估模型的性能。根据评估结果,你可以对模型进行进一步的优化,如调整超参数、增加训练轮次等。

通过这样的微调过程,你可以使GPT模型更好地适应你的任务,并获得更好的性能。需要注意的是,微调过程需要大量的计算资源和时间,因此在实际操作中需要仔细权衡时间和资源的成本。

三、哪些GPT模型可以微调?

可以微调的GPT模型包括Ada、Babbage、Curie和Davinci。这些模型属于 GPT-3 系列。另外,值得注意的是,微调目前不适用于较新的 GPT-3.5-turbo 型号或其他 GPT-4

据OpenAI发布的最新消息,计划于2024年1月4日将关闭Ada、Babbage、Curie和Davinci模型,而基于最新的GPT-3.5 Turbo和GPT-4 的微调推出时间未明确,预计在今年下半年发布。如果短期急用或者希望学习一下的可以关注了解,不着急的也可以等新模型的微调版本发布之后再考虑。

2023 年 7 月 6 日,我们宣布弃用 ada、babbage、curie 和 davinci 模型。这些模型(包括微调版本)将于 2024 年 1 月 4 日关闭。我们正在积极致力于对升级的基础 GPT-3 模型以及 GPT-3.5 Turbo 和 GPT-4 进行微调,我们建议等待这些新选项可用,而不是基于即将弃用的模型进行微调。

四、哪些应用场景适合微调?

对于Fine-Tuning GPT,有以下几种好的应用场景:文本分类和条件生成。

4.1、文本分类

对于分类问题,每个输入都被分配到预定义的类别之一,以下是一些示例:

  • 确保真实陈述:如果一家公司想要验证其网站上的广告是否提及了正确的产品和公司,可以对分类器进行Fine-Tuning,以过滤掉不正确的广告,确保模型不会胡编乱造。
  • 情感分析:这涉及根据情感对文本进行分类,如积极、消极或中性。
  • 电子邮件分类:将传入的电子邮件分成多个预定义的类别之一,可以将这些类别转换为数字,对于多达~500个类别来说效果良好。

4.2、条件生成

这类问题涉及根据给定的输入生成内容。应用包括改写、总结、实体提取、产品描述编写、虚拟助手(聊天机器人)等。以下是一些示例:

  • 从维基百科文章中创建引人注目的广告。在这种生成性的应用场景中,确保提供的样本是高质量的,因为Fine-Tuned模型将尝试模仿样本的风格(和错误)。
  • 实体提取。这个任务类似于语言转换问题。通过按字母顺序或按照原始文本中出现的顺序对提取的实体进行排序,可以提高性能。
  • 客户支持聊天机器人。聊天机器人通常包含有关对话的相关上下文(订单详情),对话的摘要以及最近的消息。
  • 基于技术属性的产品描述。将输入数据转换为自然语言,以在这个环境中实现更好的性能。

五、如何对GPT进行模型微调?

模型微调是因为ChatGPT和GPT-4等模型并不是全能的AI,它们在许多垂直领域的回答可能会出错。其中一个主要原因是缺乏特定领域的训练数据。如果我们有丰富的特定领域数据,我们可以利用这些数据来对模型进行"微调",使其在该领域表现出色。完成微调后,我们可以直接向模型提问,而无需像之前那样使用嵌入查询相关资料,然后将找到的资料一并提交给OpenAI以获取答案。

OpenAI的模型微调过程并不复杂。您只需要提供数据给OpenAI,微调过程将在云端的"黑盒子"中进行。需要提供的数据格式是一个文本文件,每一行都是一个Prompt,以及对应该Prompt生成的Completion接口的内容。

本文将介绍使用 Python 对用户自己的数据进行 GPT-3 模型的微调过程,涵盖从获取 API 凭证到准备数据、训练模型和验证模型的所有步骤。

5.1、数据集标准

我们将针对问答场景微调 GPT-3 模型,其中包含结构化问答模式,旨在帮助模型理解模型需要执行的任务。在整个训练和测试数据中,每对问题和答案都保持一致的格式。

您的数据必须是JSONL文档,其中每一行都是与训练示例相对应的提示-完成对。可以使用OpenAI提供的CLI 数据准备工具轻松将数据转换为此文件格式。

训练示例越多越好。按官方的建议至少有几百个例子。一般来说,数据集大小每增加一倍都会导致模型质量线性增加。

问答数据集的实例遵循以下格式:

{
    "prompt": "my prompt text ->",
    "completion": "the answer of the prompt. \n"
}

{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
...
  • “prompt”:是模型读取和处理的输入文本。主要分隔符是箭头符号 (->),用于将提示与预期响应区分开来。
  • “completion”:是对提示的预期响应。反斜杠“\n”符号用作停止序列来指示答案的结束。

了解数据集格式后,我们可以生成训练数据集和验证数据集,如下所示。这些prompt和completion是使用 ChatGPT 生成的。

training_data = [
    {
        "prompt": "中国的首都是哪里?->",
        "completion": """中国的首都是北京。\n"""
    },
    {
        "prompt": "中国的最长河流是哪条?->",
        "completion": """中国的最长河流是长江。\n"""
    },
    {
        "prompt": "中国的最高山峰是哪座?->",
        "completion": """中国的最高山峰是珠穆朗玛峰。\n"""
    },
    {
        "prompt": "中国的国花是什么?->",
        "completion": """中国的国花是牡丹。\n"""
    },
    {
        "prompt": "中国的四大发明包括哪些?->",
        "completion": """中国的四大发明包括指南针、造纸术、火药和印刷术。\n"""
    },
    {
        "prompt": "中国的传统节日春节是在哪个月份?->",
        "completion": """中国的传统节日春节是在农历正月。\n"""
    },
    {
        "prompt": "中国的长城是世界上最长的人造建筑物吗?->",
        "completion": """是的,中国的长城是世界上最长的人造建筑物。\n"""
    },
    {
        "prompt": "中国的国旗由几颗星组成?->",
        "completion": """中国的国旗由五颗星组成。\n"""
    },
    {
        "prompt": "中国的国歌是什么?->",
        "completion": """中国的国歌是《义勇军进行曲》。\n"""
    },
    {
        "prompt": "中国的面积排名世界第几?->",
        "completion": """中国的面积排名世界第三。\n"""
    }
]
validation_data = [
    {
        "prompt": "中国的第一位主席是谁?->",
        "completion": """中国的第一位主席是毛泽东。\n"""
    },
    {
        "prompt": "中国的最长高速公路是哪条?->",
        "completion": """中国的最长高速公路是京藏高速公路。\n"""
    },
    {
        "prompt": "中国的最大湖泊是哪个?->",
        "completion": """中国的最大湖泊是青海湖。\n"""
    },
    {
        "prompt": "中国的省级行政区划有多少个?->",
        "completion": """中国的省级行政区划共有34个。\n"""
    },
    {
        "prompt": "中国的古代丝绸之路起点是哪里?->",
        "completion": """中国的古代丝绸之路起点是西安。\n"""
    },
    {
        "prompt": "中国的十二生肖中有哪些动物?->",
        "completion": """中国的十二生肖包括鼠、牛、虎、兔、龙、蛇、马、羊、猴、鸡、狗和猪。\n"""
    },
    {
        "prompt": "中国的国家体育馆被称为什么?->",
        "completion": """中国的国家体育馆被称为鸟巢。\n"""
    },
    {
        "prompt": "中国的国家图书馆位于哪个城市?->",
        "completion": """中国的国家图书馆位于北京。\n"""
    }
]

5.2、准备基础环境

我们需要准备工作环境,建议使用 OpenAI 命令行界面 (CLI)。要安装它,请运行

pip install --upgrade openai

(以下说明适用于0.9.4及更高版本。此外,OpenAI CLI 需要 python 3。)

OPENAI_API_KEY通过将以下行添加到 shell 初始化脚本(例如 .bashrc、zshrc 等)中或在微调命令之前在命令行中运行它来设置环境变量:

export OPENAI_API_KEY="<OPENAI_API_KEY>"

5.3、准备数据集

如上所示,处理列表格式对于小型数据集可能很方便。但是,以 JSONL(JSON Lines)格式保存数据有几个好处。好处包括可扩展性、互操作性、简单性以及与 OpenAI API 的兼容性,后者在创建微调作业时需要 JSONL 格式的数据。

以下代码利用辅助函数prepare_data以JSONL格式创建训练和验证数据:

import json

training_file_name = "training_data.jsonl"
validation_file_name = "validation_data.jsonl"

def prepare_data(dictionary_data, final_file_name):
    with open(final_file_name, 'w') as outfile:
        for entry in dictionary_data:
            json.dump(entry, outfile)
            outfile.write('\n')

prepare_data(training_data, "training_data.jsonl")
prepare_data(validation_data, "validation_data.jsonl")

在Google Colab笔记本中,可以使用以下针对训练和验证数据的语句来完成数据集的准备。

!openai tools fine_tunes.prepare_data -f "training_data.jsonl"
!openai tools fine_tunes.prepare_data -f "validation_data.jsonl"
Analyzing...

- Your file contains 22 prompt-completion pairs. In general, we recommend having at least a few hundred examples. We've found that performance tends to linearly increase for every doubling of the number of examples
- More than a third of your `prompt` column/key is uppercase. Uppercase prompts tends to perform worse than a mixture of case encountered in normal language. We recommend to lower case the data if that makes sense in your domain. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details
- More than a third of your `completion` column/key is uppercase. Uppercase completions tends to perform worse than a mixture of case encountered in normal language. We recommend to lower case the data if that makes sense in your domain. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details
- All prompts end with suffix `? ->`
- All prompts start with prefix `《`
- All completions end with suffix `\n`
- The completion should start with a whitespace character (` `). This tends to produce better results due to the tokenization we use. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details

Based on the analysis we will perform the following actions:
- [Recommended] Lowercase all your data in column/key `prompt` [Y/n]: Y
- [Recommended] Lowercase all your data in column/key `completion` [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 `training_data_prepared.jsonl`
Feel free to take a look!

Now use that file when fine-tuning:
> openai api fine_tunes.create -t "training_data_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'll approximately take 2.75 minutes to train a `curie` model, and less for `ada` and `babbage`. Queue will approximately take half an hour per job ahead of you.

最后,我们将两个数据集上传到OpenAI开发者账户,如下:

training_file_id = upload_data_to_OpenAI(training_file_name)
validation_file_id = upload_data_to_OpenAI(validation_file_name)

print(f"Training File ID: {training_file_id}")
print(f"Validation File ID: {validation_file_id}")

训练数据上传完成功后会返回唯一的文件标识ID及文件相关信息。

<File file id=file-hKjjDPr885IqmsfcDJvEsidl at 0x7f17f5725f80> JSON: {
  "object": "file",
  "id": "file-hKjjDPr885IqmsfcDJvEsidl",
  "purpose": "fine-tune",
  "filename": "file",
  "bytes": 15103,
  "created_at": 1690133676,
  "status": "uploaded",
  "status_details": null
}

5.4、创建微调模型

为了进行微调,我们需要遵循以下两个步骤:(1)定义超参数,(2)开始微调过程。

每个微调工作都从基本模型开始,默认为Curie模型。模型的选择会直接影响模型的性能和运行微调模型的成本。可以选择微调的模型可以是以下之一:ada、babbage、curie或davinci。

开始进行微调工作后,可能需要一些时间才能完成。创建完的微调任务需要进入队列排队,训练我们的模型可能需要几分钟或几小时,具体取决于模型和数据集大小。

接下来我们将使用训练和验证数据集对davinci模型进行微调。我们将设置batch_size为3和learning_rate_multiplier为0.3,在15个epochs中运行微调。

create_args = {
        "training_file": training_file_id,
        "validation_file": validation_file_id,
        "model": "davinci",
        "n_epochs": 4,
        "batch_size": 3,
        "learning_rate_multiplier": 0.3
}

response = openai.FineTune.create(**create_args)
job_id = response["id"]
status = response["status"]

print(f'Fine-tunning model with jobID: {job_id}.')
print(f"Training Response: {response}")
print(f"Training Status: {status}")

上面的代码生成 jobID ( ft-ByTj1ZNnSn6Fl7rvm8MhzcVV)、训练响应和训练状态(待处理)的以下信息。

<FineTune fine-tune id=ft-ByTj1ZNnSn6Fl7rvm8MhzcVV at 0x7f17f576d760> JSON: {
  "object": "fine-tune",
  "id": "ft-ByTj1ZNnSn6Fl7rvm8MhzcVV",
  "hyperparams": {
    "n_epochs": 4,
    "batch_size": 3,
    "prompt_loss_weight": 0.01,
    "learning_rate_multiplier": 0.3
  },
  "organization_id": "org-MplDvRDOWGVmqFOhYs8cLCMd",
  "model": "davinci",
  "training_files": [
    {
      "object": "file",
      "id": "file-hKjjDPr885IqmsfcDJvEsidl",
      "purpose": "fine-tune",
      "filename": "file",
      "bytes": 15103,
      "created_at": 1690133676,
      "status": "processed",
      "status_details": null
    }
  ],
  "validation_files": [],
  "result_files": [],
  "created_at": 1690133686,
  "updated_at": 1690133686,
  "status": "pending",
  "fine_tuned_model": null,
  "events": [
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Created fine-tune: ft-ByTj1ZNnSn6Fl7rvm8MhzcVV",
      "created_at": 1690133686
    }
  ]
}

微调任务在pedding状态不输出任何相关信息。但是我们可以通过运行以下代码来更深入地了解训练过程:

import signal
import datetime

def signal_handler(sig, frame):
        status = openai.FineTune.retrieve(job_id).status
        print(f"Stream interrupted. Job is still {status}.")
        return

print(f'Streaming events for the fine-tuning job: {job_id}')
signal.signal(signal.SIGINT, signal_handler)

events = openai.FineTune.stream_events(job_id)
try:
        for event in events:
            print(f'{datetime.datetime.fromtimestamp(event["created_at"])} {event["message"]}')

except Exception:
        print("Stream interrupted (client disconnected).")

下面生成了所有epochs,以及微调的状态,微调已成功。

Streaming events for the fine-tuning job:ft-ByTj1ZNnSn6Fl7rvm8MhzcVV
2023-07-23 15:11:54 Created fine-tune: ft-ByTj1ZNnSn6Fl7rvm8MhzcVV
2023-07-23 15:12:30 Fine-tne costs $0.12
2023-07-23 15:12:30 Fine-tune enqueued.Queue number:0
2023-07-23 15:12:31 Fine-tunestarted
2023-07-23 15:14:38 Completedepoch 1/4
2023-07-23 15:14:41 Completedepoch 2/4
2023-07-23 15:14:45 Completedepoch 3/4
2023-07-23 15:14:49 Completedepoch 4/4
2023-07-23 15:16:07 Uploaded model:davinci:ft-personal-2023-07-23-20-16-06
2023-07-23 15:16:08 Uploaded result file:file-hKjjDPr885IqmsfcDJvEsidl
2023-07-2315:16:08 Fine-tune succeeded

5.5、检查微调进度

可以使用两个openai提供的函数来检查模型微调的进度。

5.5.1、列出微调任务所有事件

我们可以使用openai.FineTune.list_events()并传入fine_tune_response id来列出微调任务相关的所有当前事件列表:

fine_tune_events = openai.FineTune.list_events(id=fine_tune_response.id)

输出结果:

<OpenAIObject list at 0x785e5604fe20> JSON: {
  "object": "list",
  "data": [
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Created fine-tune: ft-ByTj1ZNnSn6Fl7rvm8MhzcVV",
      "created_at": 1690133686
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune costs $0.06",
      "created_at": 1690142386
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune enqueued. Queue number: 1",
      "created_at": 1690142386
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune is in the queue. Queue number: 0",
      "created_at": 1690142453
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune started",
      "created_at": 1690142457
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Completed epoch 1/4",
      "created_at": 1690142523
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Completed epoch 2/4",
      "created_at": 1690142527
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Completed epoch 3/4",
      "created_at": 1690142531
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Completed epoch 4/4",
      "created_at": 1690142535
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Uploaded model: curie:ft-personal-2023-07-23-20-02-38",
      "created_at": 1690142558
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Uploaded result file: file-CjPz8kFeL0r3Al140byoI02c",
      "created_at": 1690142559
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune succeeded",
      "created_at": 1690142559
    }
  ]
}

5.5.2、根据条件检索微调任务

还可以使用openai.FineTune.retrieve(id=fine_tune_response.id)检索指定的微调任务,其中包含有关微调任务的详细信息:

retrieve_response = openai.FineTune.retrieve(id=fine_tune_response.id)

输出结果:

<FineTune fine-tune id=ft-ByTj1ZNnSn6Fl7rvm8MhzcVV at 0x785e2c3fddf0> JSON: {
  "object": "fine-tune",
  "id": "ft-ByTj1ZNnSn6Fl7rvm8MhzcVV",
  "hyperparams": {
    "n_epochs": 4,
    "batch_size": 1,
    "prompt_loss_weight": 0.01,
    "learning_rate_multiplier": 0.1
  },
  "organization_id": "org-MplDvRDOWGVmqFOhYs8cLCMd",
  "model": "curie",
  "training_files": [
    {
      "object": "file",
      "id": "file-hKjjDPr885IqmsfcDJvEsidl",
      "purpose": "fine-tune",
      "filename": "file",
      "bytes": 15103,
      "created_at": 1690133676,
      "status": "processed",
      "status_details": null
    }
  ],
  "validation_files": [],
  "result_files": [
    {
      "object": "file",
      "id": "file-CjPz8kFeL0r3Al140byoI02c",
      "purpose": "fine-tune-results",
      "filename": "compiled_results.csv",
      "bytes": 4754,
      "created_at": 1690142559,
      "status": "processed",
      "status_details": null
    }
  ],
  "created_at": 1690133686,
  "updated_at": 1690142559,
  "status": "succeeded",
  "fine_tuned_model": "curie:ft-personal-2023-07-23-20-02-38",
  "events": [
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Created fine-tune: ft-ByTj1ZNnSn6Fl7rvm8MhzcVV",
      "created_at": 1690133686
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune costs $0.06",
      "created_at": 1690142386
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune enqueued. Queue number: 1",
      "created_at": 1690142386
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune is in the queue. Queue number: 0",
      "created_at": 1690142453
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune started",
      "created_at": 1690142457
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Completed epoch 1/4",
      "created_at": 1690142523
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Completed epoch 2/4",
      "created_at": 1690142527
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Completed epoch 3/4",
      "created_at": 1690142531
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Completed epoch 4/4",
      "created_at": 1690142535
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Uploaded model: curie:ft-personal-2023-07-23-20-02-38",
      "created_at": 1690142558
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Uploaded result file: file-CjPz8kFeL0r3Al140byoI02c",
      "created_at": 1690142559
    },
    {
      "object": "fine-tune-event",
      "level": "info",
      "message": "Fine-tune succeeded",
      "created_at": 1690142559
    }
  ]
}

5.5.3、查询所有微调任务列表

通过调用OpenAI的FineTune API的list()方法,可以获取到已经进行过fine-tuning的模型的列表,以便进一步分析和处理。

openai.FineTune.list()

5.6、保存微调后的模型

对fine_tuned_model 为 null 进行故障排除 在微调过程中,该fine_tuned_model可能无法立即在 openai.FineTune.create() 返回的 fine_tune_response 对象中可用。

要检查微调过程的状态,我们可以调用该openai.FineTune.retrieve()函数并传入fine_tune_response.id。该函数将返回一个 JSON 对象,其中包含有关训练状态的信息,例如当前 epoch、当前批次、训练损失和验证损失。

微调过程完成后,您可以通过调用 openai.FineTune.list()来检查所有微调模型的状态。这将列出您的所有微调及其当前状态。

微调过程完成后,您可以通过调用 opena.FineTune.retrieve() 来检索 fine_tuned_model 密钥。再次运行并传入fine_tune_response.id。这将返回一个 JSON 对象,其中包含 fine_tuned_model 键和可用于进一步完成的微调模型的 ID。

微调完成后,继续保存微调模型的名称:

# 如果response.fine_tuned_model不为空
fine_tuned_model = fine_tune_response.fine_tuned_model
fine_tuned_model

# 如果response.fine_tuned_model为空
retrieve_response = openai.FineTune.retrieve(fine_tune_response.id)
fine_tuned_model = retrieve_response.fine_tuned_model
fine_tuned_model

至此,我们经过微调就完成了一个新的模型,接下来用新的模型来测试一下。

5.7、测试新模型

当微调任务执行成功后,fine_tuned_model字段将会填充模型的名称。然后我们就可以可以将此模型指定为我们的Completions API 的参数并发起请求。

微调任务首次完成后,训练的模型可能需要几分钟才能准备好处理请求。如果对模型的请求超时,可能是因为模型正在加载。如果发生这种情况,请在几分钟后重试。

使用此模型,我们可以通过提供提示、模型名称并使用函数创建查询来运行查询来验证其结果openai.Completion.create()。从答案字典中检索结果如下:

new_prompt = "新中国的创始人是谁?"
answer = openai.Completion.create(
  model=fine_tuned_model,
  prompt=new_prompt
)

print(answer['choices'][0]['text'])

new_prompt = """古代丝绸之路的起点在深圳哪个城市?"""
answer = openai.Completion.create(
  model=fine_tuned_model,
  prompt=new_prompt
)

print(answer['choices'][0]['text'])

尽管提示的写法与验证数据集中的不完全相同,但模型仍然设法将它们映射到正确的答案。通过很少的训练样本,我们成功地建立了一个不错的微调模型。更大的训练规模可以获得更好的结果。

对于这些微调模型的请求,依然可以继续使用所有其他Completions参数,例如temperature、frequency_penalty、等。presence_penalty。

5.8、删除微调模型

openai.Model.delete(FINE_TUNED_MODEL)

六、模型微调的成本

我们这里选用的基础模型是 Curie,而不是效果最好的 Davinci。之所以做出这样的选择,是出于成本的考虑。下图为各个模型有关微调费率的详细信息。

图片来源:https://openai.com/pricing#language-models

使用微调模型的成本要远远高于使用 OpenAI 内置的模型。以 Davinci 为基础微调的模型,使用的时候,每 1000 个 Token 的成本是 0.12 美元,是使用内置的 text-davinci-003 的 6 倍,是我们最常用的 gpt-3.5-turbo 的 60 倍。所以,如果只是一般很基础的应用,这个成本实在是太高了。就算是我们选择基于 Curie 微调,1000 个 Token 的使用成本也在 0.012 美元,虽然比 text-davinci-003 要便宜,但也是 gpt-3.5-turbo 的 6 倍。

6.1、增量训练,优化模型

虽然微调模型比较高昂的价格,限制了它的使用。不过,微调模型还有一个能力,就是我们可以在已经微调了的模型上根据新数据做进一步地微调。这个在很多垂直领域是非常有用,比如在医学、金融这样的领域,我们就可以不断收集新的数据,不断在前一个微调模型的基础之上继续微调我们的模型,让模型的效果越来越好。而这些领域往往也能承受更高一些的成本。进一步地微调其实操作起来并不复杂,就是再准备一些数据,以之前已经微调好的模型为基础模型来操作就好了。

在原有的模型上微调的时候,我们要修改两个参数。

  • 第一个是 model 参数,我们把 Curie 换成了我们刚才微调之后的模型 curie:ft-personal-2023-07-23-20-02-38。
  • 第二个是 learning_rate_multiplier,这个参数的默认值是根据你的样本数量在 0.05 到 0.2 不等。如果你继续微调的样本数要比之前微调的数据量小很多,你就可以调得大一点。

微调更新之后,模型的名称没有变,老的模型就被更新成了微调后的新模型。

七、结论

今天我们主要介绍了如何使用OpenAI的API对GPT模型进行微调。模型微调给我们提供了一个非常实用的能力,我们可以利用自己的数据,在 OpenAI 的基础模型上,调整模型参数生成一个新模型。这样我们就能够根据自己专有的垂直领域的数据,来生产一个专属于我们自己的模型。而且,我们可以根据新收集到的数据,不断在这个模型上继续微调迭代。不过,微调后的模型使用成本比较高,你需要自己核算一下,究竟是微调模型 ROI 比较高,还是使用前面的外部知识库的方式更划算一些。

八、推荐阅读

1、OpenAI 的模型微调,其实还有很多更丰富的用法,比如可以拿来做分类,或者命名实体的提取。你可以去官网的 Specific Guidelines 部分看一看,来试着微调一个模型。

  • https://platform.openai.com/docs/guides/fine-tuning

2、OpenAI 在自己的官方文档里,推荐了 Weight & Bias 这个公司的产品,可以来追踪微调后的模型的实验、模型与数据集。Weight & Bias 也在自己的文档里,提供了一个对 WIT 数据集进行模型微调的 Notebook,你有兴趣的话也可以去看一下。

  • https://platform.openai.com/docs/guides/fine-tuning/weights-biases
  • https://docs.wandb.ai/guides/integrations/openai?utm_source=wandb_docs&utm_medium=code&utm_campaign=OpenAI+API

3、有些使用微软Azure云服务的用户,对于如何基于Azure OpenAI来微调自己的模型,可以参考微软的官方文档

  • https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/fine-tuning?pivots=programming-language-studio

如果你对这篇文章感兴趣,而且你想要了解更多关于AI领域的实战技巧,可以关注「技术狂潮AI」公众号。在这里,你可以看到最新最热的AIGC领域的干货文章和案例实战教程。

猜你喜欢

转载自blog.csdn.net/FrenzyTechAI/article/details/131918889
今日推荐