chatgpt官方支持微调了!

news2024/10/7 2:24:11

前言

刚刚openai在官网宣布chatgpt支持微调了!具体支持微调的模型有:

并且GPT-3.5-Turbo-16k和GPT4在今年晚些也会支持微调。

其在官网也介绍了一些微调和准备数据的实战经验,可以学习~

官方文档:

https://platform.openai.com/docs/guides/fine-tuning/common-use-cases

需要微调吗

虽然微调可以更好的cover某一领域,但是毕竟训练需要耗费时间和精力,所以其建议先试试prompt engineering,有可能已经满足需要了,可以参考:

https://platform.openai.com/docs/guides/gpt-best-practices

微调可以处理哪些问题

通过微调可以处理以下场景:

(1)设定风格、语气、格式或其他质量方面的要求。

(2)提高产生所需输出的可靠性。

(3)纠正无法遵循复杂提示的错误。

(4)以特定方式处理许多边缘情况。

(5)执行难以用prompt准确表达的新技能或任务。

微调的另外一个好处就是可以解决时延或者减少花费,比如微调后就可以使用更短的prompt(训练的时候就用更少的prompt,inference自然就可以不用再写一堆prompt engineering的东西)来完成任务,这样就减少了token数;同时由于GPT4比较贵但是其效果又比较好,那么就可以用GPT4的结果做训练数据来微调chatgpt进而达到蒸馏GPT4。

准备微调训练数据

微调gpt-3.5-turbo需要准备的数据格式如下:

{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}

babbage-002和davinci-002需要准备的数据格式如下:

{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
  • 构建指令

构建指令最关键的就是要确保训练数据是最好的,最满足自己当前需求的;要把这部分高质量的数据尽可能都加到训练集中,尤其是自己手头准备的训练样本并不是很多的时候,这样可以尽最大可能让训练出的模型满足自己的需求。

另外就是前面说的如果你想通过缩短prompt来减少inference成本,那么模型就需要更多的训练样本去学习到被“省略”部分的指令。

为了便于理解,笔者这里举个例子:假设当前在做一个根据小说内容生成一句评论的场景,目前的要求有(1)评论必须有搞笑风格(2)评论必须带有表情符号(3)评论字数必须在10-20个字(4)评论必须要口语化一些。那么我们调用GPT4的时候prompt就可以这么写:

假设你是一个小说评论生成器,生成的评论必须满足以下要求:
(1)评论必须有搞笑风格
(2)评论必须带有表情符号
(3)评论字数必须在10-20个字
(4)评论必须要口语化一些
小说内容如下:
****

通过使用这个指令调用GPT4我们可以很好的得到满足自己需求的response(假设调用chatgpt不能满足需求),这样我们就有了训练样本啦,然后用这里的样本去微调chatgpt就可以蒸馏了GPT4啦。

同时看到当前的场景每个样本开头都有这一段相似的指令:

假设你是一个小说评论生成器,生成的评论必须满足以下要求:
(1)评论必须有搞笑风格
(2)评论必须带有表情符号
(3)评论字数必须在10-20个字
(4)评论必须要口语化一些

如果我们在微调的时候把这段去掉同样可以达到需求的话,那么在inference的时候就可以大大减少token数了(尤其是当指令非常多的时候),极端下我们在微调的时候训练样本的prompt就只是小说内容,response还是GPT4的回复。但是这种情况就需要准备更多的样本,让模型去学习到那些被忽略的隐式“指令”。

  • 需要多少条训练样本

最少需要10条训练样本。openai观察到通常使用50-100条样本来微调后,chatgpt就可以发生明显的变化。

官方建议精心准备50条样本来微调,然后观察模型是否显示出改善的迹象。一般来说可能已经能用了,但即使模型还不满足需求,如果看到已经明显改变的迹象了,那剩下的就是沿着当前准备数据的思路提供更多数据即可。如果没有看到改变的迹象,这可能意味着需要重新考虑如何为模型设置任务或重新构建指令数据。

  • 划分训练&测试集

官方要求划分训练和测试集,这样在微调过程中其会帮统计一些指标变化,方便辅助看训练的效果

  • token 限制

每条训练token总数最大为4096,也就是说需要确保构建的样本最好在4000以内,如果超出会被自动截断,可以使用以下脚本来计算样本的token数:

https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb

  • 微调成本

  • 检查格式

在准备完数据后,最后可以再double check一下数据格式等等准备的是否正确,以免真真启动训练的时候发生错误,为此官方也给了一个check脚本:

# We start by importing the required packages

import json
import os
import tiktoken
import numpy as np
from collections import defaultdict

# Next, we specify the data path and open the JSONL file

data_path = "<YOUR_JSON_FILE_HERE>"

# Load dataset
with open(data_path) as f:
    dataset = [json.loads(line) for line in f]

# We can inspect the data quickly by checking the number of examples and the first item

# Initial dataset stats
print("Num examples:", len(dataset))
print("First example:")
for message in dataset[0]["messages"]:
    print(message)

# Now that we have a sense of the data, we need to go through all the different examples and check to make sure the formatting is correct and matches the Chat completions message structure

# Format error checks
format_errors = defaultdict(int)

for ex in dataset:
    if not isinstance(ex, dict):
        format_errors["data_type"] += 1
        continue

    messages = ex.get("messages", None)
    if not messages:
        format_errors["missing_messages_list"] += 1
        continue

    for message in messages:
        if "role" not in message or "content" not in message:
            format_errors["message_missing_key"] += 1

        if any(k not in ("role", "content", "name") for k in message):
            format_errors["message_unrecognized_key"] += 1

        if message.get("role", None) not in ("system", "user", "assistant"):
            format_errors["unrecognized_role"] += 1

        content = message.get("content", None)
        if not content or not isinstance(content, str):
            format_errors["missing_content"] += 1

    if not any(message.get("role", None) == "assistant" for message in messages):
        format_errors["example_missing_assistant_message"] += 1

if format_errors:
    print("Found errors:")
    for k, v in format_errors.items():
        print(f"{k}: {v}")
else:
    print("No errors found")

# Beyond the structure of the message, we also need to ensure that the length does not exceed the 4096 token limit.

# Token counting functions
encoding = tiktoken.get_encoding("cl100k_base")

# not exact!
# simplified from https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
def num_tokens_from_messages(messages, tokens_per_message=3, tokens_per_name=1):
    num_tokens = 0
    for message in messages:
        num_tokens += tokens_per_message
        for key, value in message.items():
            num_tokens += len(encoding.encode(value))
            if key == "name":
                num_tokens += tokens_per_name
    num_tokens += 3
    return num_tokens

def num_assistant_tokens_from_messages(messages):
    num_tokens = 0
    for message in messages:
        if message["role"] == "assistant":
            num_tokens += len(encoding.encode(message["content"]))
    return num_tokens

def print_distribution(values, name):
    print(f"\n#### Distribution of {name}:")
    print(f"min / max: {min(values)}, {max(values)}")
    print(f"mean / median: {np.mean(values)}, {np.median(values)}")
    print(f"p5 / p95: {np.quantile(values, 0.1)}, {np.quantile(values, 0.9)}")

# Last, we can look at the results of the different formatting operations before proceeding with creating a fine-tuning job:

# Warnings and tokens counts
n_missing_system = 0
n_missing_user = 0
n_messages = []
convo_lens = []
assistant_message_lens = []

for ex in dataset:
    messages = ex["messages"]
    if not any(message["role"] == "system" for message in messages):
        n_missing_system += 1
    if not any(message["role"] == "user" for message in messages):
        n_missing_user += 1
    n_messages.append(len(messages))
    convo_lens.append(num_tokens_from_messages(messages))
    assistant_message_lens.append(num_assistant_tokens_from_messages(messages))

print("Num examples missing system message:", n_missing_system)
print("Num examples missing user message:", n_missing_user)
print_distribution(n_messages, "num_messages_per_example")
print_distribution(convo_lens, "num_total_tokens_per_example")
print_distribution(assistant_message_lens, "num_assistant_tokens_per_example")
n_too_long = sum(l > 4096 for l in convo_lens)
print(f"\n{n_too_long} examples may be over the 4096 token limit, they will be truncated during fine-tuning")

# Pricing and default n_epochs estimate
MAX_TOKENS_PER_EXAMPLE = 4096

MIN_TARGET_EXAMPLES = 100
MAX_TARGET_EXAMPLES = 25000
TARGET_EPOCHS = 3
MIN_EPOCHS = 1
MAX_EPOCHS = 25

n_epochs = TARGET_EPOCHS
n_train_examples = len(dataset)
if n_train_examples * TARGET_EPOCHS < MIN_TARGET_EXAMPLES:
    n_epochs = min(MAX_EPOCHS, MIN_TARGET_EXAMPLES // n_train_examples)
elif n_train_examples * TARGET_EPOCHS > MAX_TARGET_EXAMPLES:
    n_epochs = max(MIN_EPOCHS, MAX_TARGET_EXAMPLES // n_train_examples)

n_billing_tokens_in_dataset = sum(min(MAX_TOKENS_PER_EXAMPLE, length) for length in convo_lens)
print(f"Dataset has ~{n_billing_tokens_in_dataset} tokens that will be charged for during training")
print(f"By default, you'll train for {n_epochs} epochs on this dataset")
print(f"By default, you'll be charged for ~{n_epochs * n_billing_tokens_in_dataset} tokens")
print("See pricing page to estimate total costs")

在检查没问题后就可以上传数据用于后续训练了

openai.File.create(
  file=open("mydata.jsonl", "rb"),
  purpose='fine-tune'
)

创建训练模型

创建的模型:

import os
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")
openai.FineTuningJob.create(training_file="file-abc123", model="gpt-3.5-turbo")

在开启微调后,任务就排队开始训练了,通常需要等几分钟或者几个小时训练,等完成训练后,用户就会收到一份确认邮件了。

当然也可以查看当前任务列表,取消某个任务等等

# List 10 fine-tuning jobs
openai.FineTuningJob.list(limit=10)

# Retrieve the state of a fine-tune
openai.FineTuningJob.retrieve("ft-abc123")

# Cancel a job
openai.FineTuningJob.cancel("ft-abc123")

# List up to 10 events from a fine-tuning job
openai.FineTuningJob.list_events(id="ft-abc123", limit=10)

# Delete a fine-tuned model (must be an owner of the org the model was created in)
import openai
openai.Model.delete("ft-abc123")

使用微调好的模型

一旦训练结束,就可以通过查看任务细节看到模型名字“fine_tuned_model”,然后就可以调用使用了,如果请求错误,可以再等一会,因为其可能在正在加载。

import os
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")

completion = openai.ChatCompletion.create(
  model="ft:gpt-3.5-turbo:my-org:custom_suffix:id",
  messages=[
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Hello!"}
  ]
)

print(completion.choices[0].message)
  • 评估模型

官方会提供一些常规的量化指标比如training loss, training token accuracy, test loss, and test token accuracy。

另外直接看生成的case可能更直观,可以对比chatgpt和微调后的chatgpt的效果,如果想自动化评估也可以使用openai官方提供的evals框架(GPT4自动打分)

https://github.com/openai/evals

  • 迭代训练数据质量

如果模型结果不尽人意,那么首先可以看当前训练数据的质量是否过关:

(1) 收集当前的badcase,然后针对性的加对应的数据来纠正模型

(2) 如果模型在语法、风格等方面不满足需要,那需要查看当前的训练样本中是否就已经包含了对应的脏数据,需要剔除或者修正。

(3) 平衡数据:假设训练数据中有60%是"I cannot answer this",但是inference的时候只有5%是这种情况,那么需要平衡好这个比例

(4) 确保模型包含所有信息,比如有个需求是希望模型根据用户的个人特征来赞美他们,而训练数据中包含了与之前对话中未提及的特征相关的赞美,那么模型可能会学会产生虚构的信息即幻觉。

(5) 确保高的一致性:如果当前需求的训练集是多个人协同创建的,那么模型的性能可能会受到多人之间的一致性和一致性水平的限制。例如,在一个文本提取任务中,如果多人只在提取的片段上达成了70%的一致,那么模型可能天花板就是70%。

(6) 训练集确保和官方要求的格式一样。

  • 迭代训练数据数量

当确保了当前训练集质量后,就可以考虑增加训练集的数量来更进一步提高性能。增加量级通常有助于模型更好地学习任务,特别是处理一些边缘case。预计每当增加一倍量级时,都会有类似客观的改进程度。可以通过以下方式粗略估计通过增加训练数据大小所带来的预期效果:
(1)在当前数据集上进行微调
(2)在当前数据集的一半上进行微调
(3)观察两者之间的质量差距

一般来说,如果必须进行数据量权衡,较少数量的高质量数据通常比大量低质量数据更有效。

  • 迭代训练超参

在第一次试水训练的时候,官方不建议指定epoch,而是由官方根据样本量大小设置一个默认值,然后训练完看效果后再决定怎么调整epoch:

(1)当需求是偏单一场景的时候(比如分类、实体抽取等等),可以适当尝试增加1-2个epoch

(2)当需求是偏多任务场景的时候或者说发现模型缺乏多样性的时候可以尝试减少1-2个epoch

微调示例

这里笔者就不再重述了,感兴趣的可以看原博客,官方给了两个具体的微调示例。

总结

openai这一波实属厉害,它的基座模型天花板本来就很高,而且我们可以看到官方介绍的微调样本数量其实需要的很少就可以得到不错的效果,在这种情况下,其他模型至少目前应该还是很难竞争的。

不过还有一个老问题没解决就是数据的安全性,不论是调用api还是微调,用户数据都是要最终流向openai的,这对安全性要求高的用户来说还是不可行,这个时候训练自己的本地私有化模型还是有必要的。

另外我们也可以借鉴学习一下其提高的迭代数据质量&数据数量等实战经验用以自己的训练开发中。

关注

欢迎关注,下期再见啦~

知乎,csdn,github,微信公众号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/916258.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常用的数据可视化工具有哪些?要操作简单的

随着数据量的剧增&#xff0c;对分析效率和数据信息传递都带来了不小的挑战&#xff0c;于是数据可视化工具应运而生&#xff0c;通过直观形象的图表来展现、传递数据信息&#xff0c;提高数据分析报表的易读性。那么&#xff0c;常用的操作简单数据可视化工具有哪些&#xff1…

高并发保证接口幂等性方案

接口幂等的解决方案 什么是接口幂等性 接口幂等性是指无论调用多少次相同的接口请求&#xff0c;对系统的状态和数据产生的影响都是一致的。简而言之&#xff0c;幂等性保证了对同一个接口请求的重复调用不会产生额外的副作用或改变系统的状态。 在设计和实现接口时&#xf…

SQL Server 执行报错: “minus“ 附近有语法错误。

sql server 执行带 minus 的语句一直报错&#xff0c;如下图&#xff1a; 找了好久才知道minus是Oracle里面的语法&#xff0c;SQL server 应用 EXCEPT。

PCL中的ISS特征点检测

ISS是基于内部形态描述子(ISS) 的特征点。 算法检测流程(参考论文:基于ISS 特征点结合改进ICP 的点云配准算法): PCL中的实现: template<typename PointInT, typename PointOutT, typename NormalT> void pcl::ISSKeypoint3D<PointInT, PointOutT, NormalT>…

斯坦福大学医学院教授:几年内ChatGPT之类的AI将纳入日常医学实践

注意&#xff1a;本信息仅供参考&#xff0c;分享此内容旨在传递更多信息之目的&#xff0c;并不意味着赞同其观点或证实其说法。 在一项新研究中&#xff0c;斯坦福大学研究人员发现&#xff0c;ChatGPT在复杂临床护理考试题中可以胜过一、二年级的医学生。此项研究显示&#…

组件库的使用和自定义组件

目录 一、组件库介绍 1、什么是组件 2、组件库介绍 3、arco.design 二、组件库的使用 1、快速上手 2、主题定制 3、暗黑模式 4、语言国际化 5、业务常见问题 三、自定义组件 2、组件开发规范 3、示例实践guide-tip 4、业务组件快速托管 一、组件库介绍 1、什么是…

allegro输出.IPC文件

1、ipc文件的导出 板厂会使用cam软件生产一个网表文件&#xff1b;如果传递给板厂的数据中也有一个IPC文件&#xff0c;板厂将对两个网表文件进行对比&#xff1b;提高生产的安全性&#xff0c;准确性&#xff1b; 1&#xff0c;PCB软件输出的光绘文件&#xff0c;有时会变异&a…

从LeakCanary看如何生成内存快照

前面我们已经完成了生命周期监控并且可以通过ReferenceQueue和WeakHashMap的比较确定哪些对象发生泄漏了&#xff0c;那么接下来需要考虑的就是如何确定这个对象是被谁持有导致泄漏的呢&#xff1f; 从内存泄漏一文中可知&#xff0c;当我们使用Android Studio或MAT分析内存泄…

【从零学习python 】75. TCP协议:可靠的面向连接的传输层通信协议

文章目录 TCP协议TCP通信的三个步骤TCP特点TCP与UDP的区别TCP通信模型进阶案例 TCP协议 TCP协议&#xff0c;传输控制协议&#xff08;英语&#xff1a;Transmission Control Protocol&#xff0c;缩写为 TCP&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议…

java八股文面试[数据结构]——List和Set的区别

List和Set是用来存放集合的接口&#xff0c;并且二者都继承自接接口Collection List 中的元素存放是有序的&#xff0c;可以存放重复的元素&#xff0c;检索效率较高&#xff0c;插入删除效率较低。 Set 没有存放顺序不能存放重复元素检索效率较低&#xff0c;插入删除效率较…

【前端】深入理解CSS盒子模型与浮动

目录 一、前言二、盒子模型1、盒子模型组成1.1、border边框1.1.1、边框的三部分组成1.1.2、边框复合简写1.1.3、边框分开写1.1.4、表格的细线边框 1.2、padding内边距1.3、margin外边距1.3.1、外边距水平居中1.3.2、外边距合并1.3.3、嵌套块元素垂直 外边距的塌陷1.3.3.1、解决…

全流程R语言Meta分析核心技术应用

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

LMLCCS_UPDATEFO2 LCL DB 方法 get_normvector 头寸 A 中RC 1 内部错误,过账时报错<转载>

原文链接&#xff1a;https://blog.csdn.net/XFYBB/article/details/129174579 物料的成本中心&#xff0c;作业价格没有维护 再用FCMLHELP&#xff0c;重新创建一下 se37&#xff0c;FCMLHELP_CHECK_TESTFLAG&#xff0c;打断点&#xff0c;跳过PW

外围信息收集

一、查询域名信息 1、安装whois sudo apt update sudo apt install whois2、使用 whois [域名]也可以通过在线网站进行查询网站 3、反查 4、网站在线查询 4.1、网站一 通过使用网站去查询&#xff1a;网址 &#xff0c;这个网站只会记录他所知道的域名&#xff0c;不全 4.…

网络综合布线实训室方案(2023版)

综合布线实训室概述 随着智慧城市的蓬勃发展,人工智能、物联网、云计算、大数据等新兴行业也随之崛起,网络布线系统作为现代智慧城市、智慧社区、智能建筑、智能家居、智能工厂和现代服务业的基础设施和神经网络,发挥着重要作用。实践表明,网络系统故障的70%发生在布线系统,直接…

centos7 忘记密码需要重置密码

第一步进入系统加载条之前 按 e 键 第二步到了这个界面 找到 linux16 开头的 将 ro 改成 rw init/sysroot/bin/sh 修改完之后 按下按键 ctrlx 或者是 F10 第三步输入 命令 chroot /sysroot第四步 重置root用户密码 这里重置密码是叫你输入两遍密码 passwd root第五步 更…

常量变量习题答案

基础题目: 第一题 按步骤编写代码&#xff0c;效果如图所示&#xff1a; 编写步骤&#xff1a; 定义类 Test1定义 main方法控制台输出5行字符串类型常量值控制台输出5行字符类型常量值 参考答案&#xff1a; public class Test1 {public static void main(String[] args) {/…

【负载均衡】Nacos简单入门

Nacos简单入门 快速安装 在Nacos的GitHub页面&#xff0c;提供有下载链接&#xff0c;可以下载编译好的Nacos服务端或者源代码&#xff1a; 下载完压缩包之后&#xff0c;放在任意目录下面进行解压&#xff1a; GitHub主页&#xff1a;https://github.com/alibaba/nacos G…

Java日志框架概览

SLF4J 提供统一的日志门面API&#xff0c;即图中紫色部分&#xff0c;实现中立的日志记录API 桥接功能&#xff0c;蓝色部分&#xff0c;把各种日志框架API&#xff08;绿色部分&#xff09;桥接到SLF4J API。这样即便你的程序中使用各种日志API记录日志&#xff0c;最终都可桥…

伊斯特曼E1D和VEAZEN费森S88这两款全单吉他怎么样?优缺点综合对比评测,哪一款更出众?

3000-4000元价位预算有什么适合初学或者进阶换琴的吉他品牌推荐&#xff1f;VEAZEN费森S88系列和Eastman伊斯特曼E1D系列一直也是深受初学者和进阶者喜欢的热销全单吉他型号&#xff0c;那么&#xff0c;今天就以它们为本期的评测主角&#xff0c;从五个方面深度剖析介绍和对比…