跳至主要内容

使用 OpenAI API 和 Python 微调 GPT-3

通过微调释放 GPT-3 的全部潜力。学习如何使用 OpenAI API 和 Python,将这一先进的神经网络模型针对您的特定用例进行改进。
· 12分钟 阅读

使用 AI 探索

GPT-3,即第三代生成式预训练变换模型,是由 OpenAI 创建的前沿神经网络深度学习模型。借助海量互联网数据,GPT-3 只需极少输入即可生成多样且稳健的机器文本。GPT-3 的应用范围广泛,不仅限于文本摘要、翻译、聊天机器人开发和内容生成。

尽管其性能已相当强大,但通过针对特定用例进行微调,仍能进一步提升 GPT-3 的表现。

那么,什么是微调?

这是一种在自定义用例数据集上对预训练的 GPT-3 进行再训练的过程。这样可以让模型更好地适应该用例或领域的细微差别,从而得到更准确的结果。

成功进行微调的前提条件是:(1) 具备 Python 编程的基础知识;(2) 熟悉机器学习和自然语言处理。

我们的文章 机器学习中的分类:入门 将帮助您了解机器学习中的分类,包含它是什么、如何使用以及一些分类算法示例。

我们将使用 OpenAI 提供的 openai Python 包,以便更方便地调用其 API 并访问 GPT-3 的能力。

本文将带您使用 Python 在用户自有数据上对 GPT-3 模型进行微调,涵盖从获取 API 凭据、准备数据、训练模型到验证模型的全部步骤。

哪些 GPT 模型可以进行微调?

可进行微调的 GPT 模型包括 Ada、Babbage、Curie 和 Davinci。这些模型隶属于 GPT-3 家族。此外,需要注意的是,较新的 GPT-3.5-turbo 模型或其他 GPT-4 目前不支持微调。

阅读我们的 GPT-3 初学者指南 以获取有关该模型的更多信息。

哪些用例适合对 GPT 进行微调?

分类和条件生成是两类能从对像 GPT-3 这样的语言模型进行微调中受益的问题。下面我们简要介绍这两类问题。

分类

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

  • 确保陈述真实:如果一家公司希望核实其网站上的广告是否提及正确的产品和公司,可以对分类器进行微调,以筛除不正确的广告,确保模型不会“编造”。
  • 情感分析:根据情感将文本分类,例如正面、负面或中性。
  • 邮件分拣分类:将收到的邮件归入多个预定义类别中的一个,这些类别可转换为数字,对于约 500 个类别以内的情况效果较好。

条件生成

此类问题涉及基于给定输入生成内容。应用包括改写、摘要、实体抽取、产品描述撰写、虚拟助手(聊天机器人)等。示例如下:

  • 从维基百科文章创建吸引人的广告。在此生成型用例中,请确保提供的样本质量高,因为微调后的模型会尝试模仿示例的风格(以及错误)。
  • 实体抽取。此任务类似于语言转换问题。可通过按字母顺序或按其在原文中出现的顺序对提取的实体进行排序来提升性能。
  • 客户支持聊天机器人。聊天机器人通常包含与对话相关的上下文(订单详情)、对目前对话的摘要以及最新消息。
  • 基于技术属性生成产品描述。将输入数据转换为自然语言,在该场景下可获得更佳表现。

GPT-3 微调的分步实现

必须先创建 OpenAI 开发者账号以获取 API 密钥,步骤如下:

首先,从 OpenAI 官方网站创建账号。

然后,选择右上角的用户头像,点击“View API Keys”,进入创建新 API 密钥或使用现有密钥的页面。

OpenAI Platform Landing Page

在“Create new secret key”选项卡中创建一个新的密钥,并提供一个有意义的名称(本例为 GPT3_fine_tuning_key),随后系统会自动生成 API 密钥。

Creating a secret API key

API Key Generated

如果您卡住或遇到困难,请务必查看包含全部代码的 DataLab 工作簿。

数据集

在此用例中,我们将针对问答场景对 GPT-3 模型进行微调,采用结构化的问答模式,帮助模型理解需要执行的任务。在整个训练和测试数据中,每组问答均保持一致的格式。

问答数据集中的一个样例如下所示:

{
"prompt": "my prompt ->",
"completion": "the answer of the prompt. \n"
}
  • “prompt”是模型读取并处理的输入文本。主要分隔符为箭头符号 (->),用于将提示与期望的响应分开。
  • “completion”是对提示的期望响应。使用反斜杠“\n”作为停止序列,表示答案的结束。

理解数据集格式后,我们即可按如下方式生成训练集和验证集。这些 prompt 与 completion 是使用 ChatGPT 生成的。

我们的 数据科学版 ChatGPT 速查表 为用户提供 60+ 条用于数据科学任务的提示词。

training_data = [
	{
    	"prompt": "What is the capital of France?->",
    	"completion": """ The capital of France is Paris.\n"""
	},
	{
    	"prompt": "What is the primary function of the heart?->",
    	"completion": """ The primary function of the heart is to pump blood throughout the body.\n"""
	},
	{
    	"prompt": "What is photosynthesis?->",
    	"completion": """ Photosynthesis is the process by which green plants and some other organisms convert sunlight into chemical energy stored in the form of glucose.\n"""
	},
	{
    	"prompt": "Who wrote the play 'Romeo and Juliet'?->",
    	"completion": """ William Shakespeare wrote the play 'Romeo and Juliet'.\n"""
	},
	{
    	"prompt": "Which element has the atomic number 1?->",
    	"completion": """ Hydrogen has the atomic number 1.\n"""
	},
	{
    	"prompt": "What is the largest planet in our solar system?->",
    	"completion": """ Jupiter is the largest planet in our solar system.\n"""
	},
	{
    	"prompt": "What is the freezing point of water in Celsius?->",
    	"completion": """ The freezing point of water in Celsius is 0 degrees.\n"""
	},
	{
    	"prompt": "What is the square root of 144?->",
    	"completion": """ The square root of 144 is 12.\n"""
	},
	{
    	"prompt": "Who is the author of 'To Kill a Mockingbird'?->",
    	"completion": """ The author of 'To Kill a Mockingbird' is Harper Lee.\n"""
	},
	{
    	"prompt": "What is the smallest unit of life?->",
    	"completion": """ The smallest unit of life is the cell.\n"""
	}
]

validation_data = [
	{
    	"prompt": "Which gas do plants use for photosynthesis?->",
    	"completion": """ Plants use carbon dioxide for photosynthesis.\n"""
	},
	{
    	"prompt": "What are the three primary colors of light?->",
    	"completion": """ The three primary colors of light are red, green, and blue.\n"""
	},
	{
    	"prompt": "Who discovered penicillin?->",
    	"completion": """ Sir Alexander Fleming discovered penicillin.\n"""
	},
	{
    	"prompt": "What is the chemical formula for water?->",
    	"completion": """ The chemical formula for water is H2O.\n"""
	},
	{
    	"prompt": "What is the largest country by land area?->",
    	"completion": """ Russia is the largest country by land area.\n"""
	},
	{
    	"prompt": "What is the speed of light in a vacuum?->",
    	"completion": """ The speed of light in a vacuum is approximately 299,792 kilometers per second.\n"""
	},
	{
    	"prompt": "What is the currency of Japan?->",
    	"completion": """ The currency of Japan is the Japanese Yen.\n"""
	},
	{
    	"prompt": "What is the smallest bone in the human body?->",
    	"completion": """ The stapes, located in the middle ear, is the smallest bone in the human body.\n"""
	}
]

环境配置

在进入实现过程之前,我们需要通过安装必要的库来准备工作环境,尤其是 OpenAI 的 Python 库,如下所示:

pip install --upgrade openai

现在我们可以导入该库。

import os
from openai import OpenAI

client = OpenAI(
  api_key=os.environ['OPENAI_API_KEY'],
)

准备数据集

如上所示,使用列表格式处理对于小型数据集可能很方便。但将数据保存为 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")

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

training_file_id = client.files.create(
  file=open(training_file_name, "rb"),
  purpose="fine-tune"
)

validation_file_id = client.files.create(
  file=open(validation_file_name, "rb"),
  purpose="fine-tune"
)

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

成功执行上述代码后,将显示训练数据与验证数据的唯一标识符,如下所示。

至此,我们已具备继续进行微调所需的全部信息。

创建微调作业

该微调流程大量参考了在 Microsoft Azure 上进行微调的 openai-cookbook。

为执行微调,我们将采用以下两步:(1) 定义超参数;(2) 触发微调。

我们将对 davinci 模型进行微调,运行 15 个 epoch,批大小为 3,学习率倍率为 0.3,使用前述训练集和验证集。

response = client.fine_tuning.jobs.create(
  training_file=training_file_id.id, 
  validation_file=validation_file_id.id,
  model="davinci-002", 
  hyperparameters={
    "n_epochs": 15,
	"batch_size": 3,
	"learning_rate_multiplier": 0.3
  }
)
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}")

上述代码生成了作业 ID(ftjob-WsCjQMEmSvRMFCbhdTqxZzOf)、训练响应以及训练状态(pending)。

pending 状态并不提供有用信息。不过,我们可以通过运行以下代码更深入了解训练过程:

import signal
import datetime


def signal_handler(sig, frame):
    status = client.fine_tuning.jobs.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 = client.fine_tuning.jobs.list_events(fine_tuning_job_id=job_id)
try:
    for event in events:
        print(
            f'{datetime.datetime.fromtimestamp(event.created_at)} {event.message}'
        )
except Exception:
    print("Stream interrupted (client disconnected).")

如下所示,所有 epoch 及微调状态(succeeded)都会被显示出来。

检查微调作业状态

我们来验证操作是否成功,此外还可通过列表操作查看所有微调作业。

import time

status = client.fine_tuning.jobs.retrieve(job_id).status
if status not in ["succeeded", "failed"]:
    print(f"Job not in terminal status: {status}. Waiting.")
    while status not in ["succeeded", "failed"]:
        time.sleep(2)
        status = client.fine_tuning.jobs.retrieve(job_id).status
        print(f"Status: {status}")
else:
    print(f"Finetune job {job_id} finished with status: {status}")
print("Checking other finetune jobs in the subscription.")
result = client.fine_tuning.jobs.list()
print(f"Found {len(result.data)} finetune jobs.")

执行结果如下所示:

共计 2 个微调作业。

模型验证

最终,可从 “fine_tuned_model” 属性中获取微调后的模型。以下打印语句显示最终模型名称为:ft:davinci-002:personal::8gKnyxn3

# Retrieve the finetuned model
fine_tuned_model = result.data[0].fine_tuned_model
print(fine_tuned_model)

使用该模型,我们可以通过提供 prompt、模型名称,并使用 openai.Completion.create() 函数创建查询来验证其效果。结果可从返回的字典中按如下方式获取:

new_prompt = "Which part is the smallest bone in the entire human body?"
answer = client.completions.create(
  model=fine_tuned_model,
  prompt=new_prompt
)

print(answer.choices[0].text)

new_prompt = "Which type of gas is utilized by plants during the process of photosynthesis?"
answer = client.completions.create(
  model=fine_tuned_model,
  prompt=new_prompt
)

print(answer.choices[0].text)

即使这些提示词与验证数据集中的写法不完全相同,模型仍能将其映射到正确答案。上述请求的回答如下图所示。

image1.png

尽管训练样本很少,我们依然得到了一款表现尚可的微调模型。增大训练集规模可获得更好结果。

结论

本文探讨了 GPT-3 的潜力,并讨论了通过微调来提升其在特定用例中的表现的过程。我们概述了成功微调 GPT-3 的前提条件,包括对 Python 编程的基础理解以及对机器学习与自然语言处理的熟悉程度。

此外,我们介绍了 openai Python 包,用于通过 OpenAI 的 API 更便捷地访问 GPT-3 的能力。文章覆盖了使用 Python 和自定义数据集对 GPT-3 模型进行微调的全部步骤,从获取 API 凭据到准备数据、训练模型,再到验证模型。

通过强调微调的益处并提供全面的流程指南,本文旨在帮助数据科学家、开发者及其他相关方掌握创建更准确高效、并能贴合其特定需求与要求的 GPT-3 模型所需的工具与知识。

我们还有关于 什么是 GPT-4 以及它为何重要? 的文章,以及 Transformers 与 Hugging Face 入门 教程。务必阅读以进一步提升您的知识水平。

Zoumana Keita

作者

Zoumana Keita

一位多才多艺、乐于分享知识并回馈他人的数据科学家。Zoumana 是 YouTube 内容创作者,也是 Medium 上的顶尖科技写作者。他热爱演讲、编程与教学。Zoumana 拥有两个硕士学位:其一是在法国巴黎获得的计算机科学硕士,专注于机器学习;其二是在美国德州理工大学获得的数据科学硕士。他的职业生涯始于法国 Groupe OPEN 的软件开发工程师,随后加入 IBM 担任机器学习顾问,为保险公司构建端到端的 AI 解决方案。之后,Zoumana 加入了 Axionable——一家总部位于巴黎与蒙特利尔的首家“可持续 AI”初创公司。在那里,他担任数据科学家,为来自法国、蒙特利尔、新加坡和瑞士的客户交付 AI 产品,主要聚焦 NLP 场景。此外,他将 5% 的时间投入到研发工作。目前,他在 IFC(世界银行集团)担任高级数据科学家。

主题