天天看點

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

作者:新智元

編輯:桃子 好困

【新智元導讀】今天,OpenAI正式開放GPT-3.5微調API,GPT-4版本也即将推出。這意味着,繼插件「APP Store」大爆發後,所有人皆可以打造個性化的專屬「類ChatGPT應用」。

終于來了!

剛剛,OpenAI正式宣布,所有開發者都可以對GPT-3.5 Turbo進行微調。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

初步結果表明,微調後的GPT-3.5 Turbo,在具體任務中,性能與GPT-4實力相當,甚至反超GPT-4。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

最讓人興奮的是,「地表最強」的GPT-4微調版本,也将在幾個月後正式上線。

這意味着,任何人可以根據需要,用「專有資料」對模型微調,标志着OpenAI開啟了AI商業應用的新紀元。

英偉達進階科學家Jim Fan稱,OpenAI釋出了自「插件應用商店」以來最大的産品更新:GPT-3.5的微調API。這将是有史以來最大的LoRA雲服務。預計将有一大堆新應用從各行各業湧現出來。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

提示量大減90%

自GPT-3.5 Turbo釋出以來,開發者和企業一直在尋求定制化的模型,以便為使用者建立獨特和差異化的體驗。

不負等待,現在,開發者終于可以進行監督式微調,讓該模型更好地滿足自己的使用需求。

OpenAI表示,在私人測試版中,客戶已經通過微調,顯著提升了模型在常見案例中的性能,具體包括:

- 提高可控性

AI模型面臨的經典挑戰之一,便是精确地遵循指令。而微調便可以讓模型做到這一點,比如輸出更加簡潔,或始終用特定的語言回複。

舉個栗子,開發者可以通過微調確定要求模型,當使用者在使用德語時,模型總是以德語回應。

- 可靠的輸出格式

微調提高了模型一緻格式化響應的能力,這對于需要特定響應格式的APP非常重要,比如代碼補全或編寫API調用。

想象一下,開發者可以通過微調将使用者提示可靠地轉化為高品質的JSON片段,這樣,就能與自己的系統一起使用,讓任務變得更加流暢。

- 自定義語調

微調可以優化模型,輸出可以反應特定語調,進而更好地代表适應企業品牌的聲音。

不同的品牌對外發出的聲音是不一樣的,從活躍創新初創公司到較為保守的企業,都可以通過模型微調讓語調與對外形象保持一緻。

- 更短的提示,相同的性能

GPT-3.5-Turbo微調可以處理多達4k個token,是之前微調模型的2倍。

早期測試者通過指令微調模型本身,将提示大小減少了高達90%,進而加快了每次API調用,并降低了成本。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

值得注意的是,當微調與提示工程、資訊檢索和函數調用等其他技術相結合時,會獲得最為強大的能力。

最後OpenAI表示,支援使用函數調用和gpt-3.5-turbo-16k的微調功能,也将在今年秋季推出。

價格x8

微調GPT-3.5的成本可以分為兩部分:初始訓練成本和使用成本。

訓練:0.008美元/1K token

使用輸入:0.012美元/1K token

使用輸出:0.016美元/1K token

例如,一個gpt-3.5-turbo微調任務的訓練檔案為100,000個token(約75,000個單詞),那麼訓練3個epoch的預期成本為2.40美元。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

從使用的價格上來看,微調後的GPT-3.5是原始版本的8倍。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

四步輕松搞定

第一步:準備資料

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

第二步:上傳檔案

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

第三步:建立微調任務

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

第四步:使用微調模型

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

GPT-3模型更新

7月,OpenAI曾宣布GPT-3基礎模型(ada、babbage、curie和davinci)将于2024年1月4日關閉。

同在今天,OpenAI再次更新GPT-3模型,并提供了babbage-002和davinci-002作為以上模型替代品,可以作為基礎模型或微調模型來使用。

客戶也可以通過查詢Completions API來通路這些模型。

另外,這些模型使用新的API端口/v1/fine_tuning/jobs進行微調。

該端口取代了/v1/fine-tunes舊端口(2024年1月4日關閉),提供了分頁和更多可擴充性,以支援微調API的未來發展。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

網友開啟新世界

衆多網友已經基于GPT-3.5 Turbo微調功能,開始創造新世界了。

剛剛在基于Marv示例的合成樣本資料上對GPT-3.5進行了微調,Marv太搞笑了。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

使用者:我怎樣才能交到女朋友?

助手:嘿Siri,給我找個女朋友。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

毋庸置疑,這将徹底改變遊戲規則,大大減少我們的API成本!

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

不過,也有網友表示十分疑惑,如果GPT-3.5 Turbo比GPT-4還強,那為什麼還要每月支付20美元???

對此其他網友解釋道,它隻是在「特定任務」上更勝一籌,在通用型任務上依然還是GPT-4的主場。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

官方微調指南

如何去微調GPT-3.5 Turbo,官方已經給出了教程。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

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

首先,OpenAI介紹了通過微調,你可以API可用的模型中獲得更多資訊:

1. 比提示品質更高的結果

2. 能夠就提示中無法容納的更多示例進行訓練

3. 提示更簡潔,節省token使用

4. 降低延遲請求

GPT模型已經在大量文本上進行了預訓練。

為了有效使用模型,OpenAI在提示中加入指令,有時還包括幾個示例。通過示範,來展示模型如何執行任務通常被稱為「少樣本學習」。

微調可以通過訓練比提示中更多的示例,來改進少樣本學習,能夠讓模型在大量任務上獲得更好的結果。

一旦微調模型,你就不需要在提示符中提供更多的示例。這樣,既節省了成本,又降低了延遲的請求。

在高層次上,微調包含以下步驟:

1. 準備和上傳訓練資料

2. 訓練一個新的微調模型

3. 使用你的微調模型

目前,微調可用于以下模型:

- gpt-3.5-turbo-0613(推薦)

- babbage-002

- davinci-002

何時需要微調

微調GPT模型可以使其更适合特定APP,但這需要投入大量的時間和精力。

OpenAI建議,首先嘗試通過提示工程、提示鍊(将複雜任務分解為多個提示),以及函數調用,可以獲得良好的結果,主要原因是:

- 有許多任務,GPT模型最初可能表現不佳,但有了更好的提示,便可以獲得更好的結果,并且可能不需要微調。

- 疊代提示等其他政策,比使用微調疊代具有更快的回報循環,而微調需要建立資料集,并且訓練模型。

- 在仍需要微調的情況下,初始提示工程任務不會白費。OpenAI通常在微調資料中使用優秀的提示(或将提示鍊/工具使用與微調相結合),進而看到最佳結果。

前段時間,OpenAI釋出「GPT最佳實踐指南」中,提供一些有關提示有效的政策,無需微調即可獲得更好的性能。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

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

常見用例

微調可以改善結果的一些常見用例:

- 設定風格、語調、格式或其他定性方面

- 提高生産所需輸出的可靠性

- 糾正不按複雜提示操作的情況

- 以特定方式處理許多邊緣情況

- 執行一項難以用提示表達的新技能或任務

在接下來的部分中,OpenAI将探讨如何設定用于微調的資料,以及微調後提高基線模型性能的各種示例。

微調有效的另一個場景是,在不犧牲品質的情況下通過替換GPT-4,或使用更短的提示來降低成本和/或延遲。

如果可以用GPT-4取得良好的結果,那麼通過對GPT-4完成進行微調(可能使用更短的指令提示),而微調gpt-3.5-turbo後模型也能達到類似的效果。

準備資料集

當你确定微調是正确的解決方案,将需要準備訓練模型的資料。

這裡,你需要建立一組多樣化的示範對話,這些對話應與你在推理時要求模型響應的對話相似。

資料集中的每個示例都應該是,與Chat completions API相同的對話,具體來說,就是一個消息清單,每個消息都有角色、内容和可選名稱。

至少一些訓練示例,應該直接針對提示模型行為不符合預期的情況,并且資料中提供的助手消息,應該是你希望模型提供的理想響應。

- 示例格式

在這個例子中,目标是建立一個偶爾給出諷刺回應的聊天機器人。

對此,OpenAI為資料集建立了3個訓練示例(對話):

{"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."}]}           

微調gpt-3.5-turbo需要會話聊天格式。對于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>"}

           

- 編寫提示

建議在微調之前,用你認為對模型最有效的指令和提示,納入到每個訓練示例中。

這樣做可以獲得最佳和最普遍的結果,尤其是在訓練示例相對較少(不足100個)的情況下。

如果你想縮短每個示例中重複出現的指令或提示,以節省成本,請記住,模型的行為很可能包含這些指令,很難讓模型在推理時忽略這些「内置」指令。

可能需要更多的訓練示例才能獲得良好的結果,因為模型必須完全通過示範學習,而無需指令指導。

- 示例數量

要微調模型,你需要提供至少10個示例。

通常會看到,使用gpt-3.5-turbo對50-100個訓練示例進行微調,有着明顯改進。根據具體使用情況,同樣的的示例效果不一。

OpenAI建議,從50個精心制作示例開始,看看模型在微調後是否顯示出改進的迹象。

在某些情況下,這可能就足夠了,但即使模型尚未達到輸出品質,明确的改進也是一個好迹象,表明提供更多資料将繼續改進模型。

如果沒有改進,則表明你可能需要重新考慮如何為模型設定任務,或者在擴充到有限的示例集之前,重新調整資料結構。

- 拆分訓練和測試

收集初始資料集後,建議将其拆分為訓練和測試兩個部分。

當送出包含訓練和測試檔案的微調時,OpenAI将在訓練過程中提供兩者的統計資料。

這些統計資料将是模型改程序度的初始信号。

此外,通過在測試集上生成樣本,盡早建構測試集将有助于確定你能夠在訓練後評估模型。

- token限制

每個訓練示例限制為4096個token。

訓練時,長度超過這一限制,将被截斷為4096個token。

是以,要確定整個訓練示例适合上下文,請檢查消息内容中的總token數是否低于4,000個。每個檔案目前限制為50 MB。

- 估算成本

為了估算微調成本,主要還是參考官方定價頁面,了解每1k token成本的詳細資訊。

在3個epoch内訓練了100,000個token的訓練檔案,預期成本約為2.4美元。

- 檢查資料格式

編譯完資料集後,在建立微調之前,檢查資料格式非常重要。

為此,OpenAI建立了一個簡單的Python腳本,你可以使用它來查找潛在錯誤、檢視token計數并估計微調的成本。

資料格式化腳本:

# 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'

)           

建立微調模型

在確定資料集的數量和結構正确并上傳檔案後,下一步是建立微調模型。

使用OpenAI SDK開始微調:

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

其中,model是初始的模型的名稱(gpt-3.5-turbo、babbage-002或davinci-002)。你可以使用字尾參數自定義微調模型的名稱。

開始微調後,可能需要一些時間才能完成。

根據模型和資料集的大小,訓練模型可能需要幾分鐘,或幾個小時。模型訓練完成後,建立微調模型的使用者将收到一封電子郵件确認。

除了建立微調模型之外,你還可以列出現有任務、檢索任務狀态或取消任務。

# 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字段填充了模型的名稱。

你現在可以将此模型指定為Chat完成(用于gpt-3.5-turbo)或舊Completions API(用于babbage-002和davinci-002)中的參數,并使用Playground向其送出請求。

所有步驟完成後,模型可以立即用于推理。

在某些情況下,你的模型可能需要幾分鐘才能準備好處理請求。如果對模型的請求逾時或找不到模型名稱,很可能是因為模型仍在加載中。如果發生這種情況,請在幾分鐘後重試。

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)           

微調示例

- 風格和語調

在這個例子中,将探讨如何建立一個微調模型,讓模型遵循特定的風格和語調指導,而不是僅僅依靠提示。

首先,建立一組示例對話,顯示模型應該是什麼,如下這個例子是「拼寫錯誤的單詞」。

{"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."}]}           

如果你想效仿并自己建立一個微調模型,至少需要10個示例。

在獲得可能改進模型的資料後,下一步是檢查資料是否滿足所有格式要求。

在對資料進行了格式化和驗證,最後的訓練步驟是開始建立微調模型。你可以通過OpenAI CLI或SDK之一執行此操作,如下所示:

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


openai.FineTuningJob.create(training_file="file-abc123", model="gpt-3.5-turbo")           

一旦訓練完成後,你就可以使用自己的微調模型。

- 結構化輸出

另一種與微調非常有效的用例是讓模型提供結構化資訊,如下例子是關于「體育新聞标題」:

{"messages": [{"role": "system", "content": "Given a sports headline, provide the following fields in a JSON dict, where applicable: "player" (full name)", "team", "sport", and "gender".},{"role": "user", "content": "Sources: Colts grant RB Taylor OK to seek trade"},
{"role": "assistant", "content": "{"player": "Jonathan Taylor", "team": "Colts", "sport": "football", "gender": "male" }"},]}
{"messages": [{"role": "system", "content": "Given a sports headline, provide the following fields in a JSON dict, where applicable: "player" (full name)", "team", "sport", and "gender".},{"role": "user", "content": "OSU 'split down middle' on starting QB battle"},
{"role": "assistant", "content": "{"player": null, "team": "OSU", "sport": "football", "gender": null }"},]}           

接下來的步驟,如上一個例子:

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


openai.FineTuningJob.create(training_file="file-abc123", model="gpt-3.5-turbo")           

訓練完成後,你可以使用微調模型,并提出一下請求:

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": "Given a sports headline, provide the following fields in a JSON dict, where applicable: player (full name), team, sport, and gender"},
    {"role": "user", "content": "Richardson wins 100m at worlds to cap comeback"}
  ]
)


print(completion.choices[0].message)           

根據格式化的訓練資料,響應應如下所示:

{"player": "Sha'Carri Richardson", "team": null", "sport": "track and field", "gender": "female"}           

除此之外,在GitHub上斬獲了近47k星的「OpenAI Cookbook」(OpenAI API的使用示例和指南),也于第一時間整理出了一份詳盡的微調教程。

OpenAI突發更新!GPT-3.5正式開放微調,人人可打造專屬ChatGPT

項目位址:https://github.com/openai/openai-cookbook/blob/main/examples/How_to_finetune_chat_models.ipynb

繼續閱讀