1. 引言
3天前,OpenAI公布了一个新特性,那就是宣布用户可以自定义微调GPT-3.5 Turbo了。这意味着什么呢?这就意味着很多任务都可以先拿GPT-3.5 Turbo进行一个微调作为基准系统了。但是,需要注意的是,这里能够微调GPT-3.5 Turbo应该和之前能够微调GPT-3使用的是相似的技术,也就是参数高效的微调,不然每个用户光保存模型大小,即使是传言中的10B左右的模型,也是很难做到的。
那么什么场景比较适合它呢?我个人看法是如果是用它进行对话微调,意义可能不是太大,因为现在的对话数据大部分也是蒸馏自它。因此,它更适合在一些特定任务上,使用少量的数据就可以微调出一个性能不错的模型。
2. 怎么操作?
这两天看到网上宣传这个事情的不少,但是真正动手做的还是比较少的。因此,特地亲自下场试验一下。上一个特性是Function Calling,错过了,这次可不能再错过了。下面看一下整个训练步骤吧,这次我们使用汉语语法纠错任务作为样例,进行一个完整的数据准备、上传文件、训练模型和测试结果流程。
2.1 准备文件
根据OpenAI官网的要求,微调GPT-3.5 Turbo的样例输入格式是和它原始的对话形式是一致的,例如:
{
"messages": [{
"role": "system", "content": "你是一个汉语语法纠错器。"}, {
"role": "user", "content": "检测这个句子的语法错误:这件事对我们大家当时震动很大。"}, {
"role": "assistant", "content": "这件事当时对我们大家震动很大。"}]}
如果原来的数据格式不是这样的,可以先写一个转换函数,使得其转换成上述的格式即可。
2.2 上传文件
上传文件的代码也非常容易,下面是一个上传文件的函数,openai.File的上传文件是异步操作,当你执行完create以后,它会先把文档传到云端,但是需要等待云端处理该数据,这里据OpenAI说,是包含了内容过滤的部分。
def upload_file(file_name):
# if a train_file is 400KB, it will take about 1 min to upload the file.
file_upload = openai.File.create(file=open(file_name, "rb"), purpose="fine-tune")
print("Uploaded file id", file_upload.id)
while True:
print("Waiting for file to process...")
file_handle = openai.File.retrieve(id=file_upload.id)
if len(file_handle) and file_handle.status == "processed":
print("File processed")
break
time.sleep(3)
return file_upload
当文档处理成功后,我们就可以利用这个文件进行微调GPT-3.5了。
2.3 训练模型
训练模型的代码非常简单,只有一句:
job = openai.FineTuningJob.create(training_file=file_upload.id, model="gpt-3.5-turbo")
print(job.id)
这里同样是一个异步操作,需要注意的是,这里一定要记得file_upload.id,这样才能够让OpenAI知道我们要训哪个文件。
另外需要注意的是,也要记得job.id,因为需要用它来查询这个模型训练的进度的。如果忘记了这个id也没有关系,你可以用下面的代码查询已有的job。
result = openai.FineTuningJob.list(limit=10)
print(result)
模型的训练需要时间,当你查询到job的状态是下面所示时,才表明这个模型已经训练成功了:
"status": "succeeded"
当然OpenAI也会在模型训练完成后邮件通知你的。
2.4 测试模型
模型测试和使用原版的GPT-3.5 Turbo方式相同,只不过model里需要填写model id,而model id则可以通过上面的查询和通知邮件中获取。当你获取到model id后,只需要执行下面的代码就可以测试了:
def test_by_case(model_id, message):
"""
Test the model with a given message.
:param model_id: ID of the model to test.
:param message: Message to test the model.
:return: Response from the model.
"""
completion = openai.ChatCompletion.create(
model=model_id,
messages=message
)
return completion.choices[0].message["content"]
整体上还是比较简单的。根据OpenAI的说法,一个账户一天可以训练12个模型,每次只能同时训练1个模型。
3. 实验结果
Model | # Param. | Data | Word-level (P/R/F) | Char-level(P/R/F) |
---|---|---|---|---|
S2S_BART | 375M | 1061 | 21.08/10.54/17.57 | 22.09/10.62/18.16 |
GrammarGPT | 7B | 1061 | 42.42/16.87/32.56 | 46.67/18.58/35.84 |
Fine-tuning GPT-3.5 Turbo | - | 1061 | 36.16/34.75/35.87 | 36.17/33.69/35.65 |
以上是我们在GrammarGPT的训练和测试集上进行的评估,可以看到模型的性能和7B全微调的性能差不多。
也不用担心上面的步骤看不懂,大家可以直接查看完整的代码:finetune_chatgpt。
4. 训练的规模和成本
由于我们使用的是之前的GrammarGPT的任务作为样例,大部分关键指标如下所示。
训练样本1061条,转换后的文件大小为400KB左右,上传文件花费了大约1分多钟,训练轮数是默认值,最后是3轮,训练时间大概为1个多小时,推理速度为1秒1个样本左右。整体上训练40W左右token,测试500个样例,共计花费9刀左右。而对比训练GrammarGPT的7B大小,大概训练时间也就在8卡A100几十分钟的消耗,按照1个小时100块左右的消耗,两者似乎也差不太多。
5. 小结
从体验上来看,能够微调GPT-3.5 Turbo的门槛还是要低了很多,尽管有人吐槽说,这个成本是使用原版的4倍以上,但是对于大多数没有拥有8卡A100又需要大模型能力的同学来说,无外乎是一个福音。正如我之前所说的那样,以后的论文中,zero-shot的GPT-3.5 Turbo不是baseline了,而是微调后的GPT-3.5 Turbo才是一个strong baseline。
这是一个好事,也是一个坏事。