作者 | 崔皓
審校 | 重樓
通用模型雖好,但微調(diào)訓(xùn)練得到一個(gè)自己的專(zhuān)屬大模型更能讓技術(shù)人心動(dòng)。最近,GPT-3.5 Turbo最近推出了一項(xiàng)全新的微調(diào)功能,該功能允許開(kāi)發(fā)者和企業(yè)精準(zhǔn)定制模型,以滿(mǎn)足特定應(yīng)用場(chǎng)景的需求。
微調(diào)GPT,不僅可以提高模型的可操控性、輸出格式的可靠性和語(yǔ)氣的一致性,還使企業(yè)能夠縮短提示長(zhǎng)度,從而加速API調(diào)用并降低成本。
本文就帶領(lǐng)諸位見(jiàn)證微調(diào)大模型的魅力,了解GPT-3.5 Turbo,并實(shí)現(xiàn)一個(gè)關(guān)于天氣的調(diào)優(yōu)模型。
1、GPT-3.5 Turbo微調(diào)功能好在哪里
GPT-3.5 Turbo,一款業(yè)界領(lǐng)先的大型語(yǔ)言模型,最近推出了一項(xiàng)令人振奮的微調(diào)功能。該功能允許開(kāi)發(fā)者和企業(yè)能夠根據(jù)特定用例定制模型,從而實(shí)現(xiàn)更高的性能和更佳的用戶(hù)體驗(yàn)。
首先,微調(diào)已經(jīng)顯著提高了模型的“可操控性”。模型現(xiàn)在可以更準(zhǔn)確地按照用戶(hù)的指示進(jìn)行操作,無(wú)論是生成更簡(jiǎn)潔的輸出,還是針對(duì)特定語(yǔ)言進(jìn)行響應(yīng)。
其次,微調(diào)還可以改善模型的輸出格式。開(kāi)發(fā)者可以將用戶(hù)的輸入轉(zhuǎn)換為高質(zhì)量的JSON片段,從而完成與其他系統(tǒng)的集成。
此外,微調(diào)還增強(qiáng)了模型輸出的“語(yǔ)氣”,使其更符合企業(yè)的調(diào)性,從而增加企業(yè)品牌的識(shí)別度和一致性。
2、微調(diào)的成本和定價(jià)
在介紹GPT-3.5 Turbo版本的微調(diào)之前,我先跟大家說(shuō)下安全和定價(jià)問(wèn)題。
微調(diào)是一個(gè)復(fù)雜但重要的過(guò)程。確保安全性是首要任務(wù)。據(jù)OpenAI官方解釋?zhuān)?xùn)練數(shù)據(jù)會(huì)通過(guò)審核API和GPT-4審核系統(tǒng)進(jìn)行篩選,確保符合安全標(biāo)準(zhǔn)。
成本方面,微調(diào)分為訓(xùn)練和使用兩個(gè)環(huán)節(jié)。訓(xùn)練成本按令牌數(shù)量計(jì)算,每1000個(gè)令牌的價(jià)格為$0.008。使用成本也按令牌計(jì)算,輸入和輸出分別是每1000個(gè)令牌$0.012和$0.016。
以100,000個(gè)令牌和3個(gè)訓(xùn)練周期為例,預(yù)計(jì)微調(diào)成本為$2.40。這一點(diǎn)對(duì)于預(yù)算有限的開(kāi)發(fā)者或企業(yè)來(lái)說(shuō),提供了明確的費(fèi)用預(yù)測(cè)。
總體而言,微調(diào)提供了性能和安全性的平衡,同時(shí)給出了明確的成本結(jié)構(gòu)。這些因素都是在進(jìn)行模型微調(diào)時(shí)需要考慮的關(guān)鍵要素。
3、為什么微調(diào)GPT3.5更劃算
模型微調(diào)的目的是為了增加性能和效率。微調(diào)現(xiàn)在能夠生成比提示更高質(zhì)量的結(jié)果,并且能夠在提示中容納更多的示例,從而提高了所謂的“少量樣本學(xué)習(xí)”的效果。
一旦模型經(jīng)過(guò)微調(diào),提示可以更短,從而節(jié)省令牌和成本。這也意味著請(qǐng)求將具有更低的延遲,從而提供更快的響應(yīng)。
微調(diào)涉及幾個(gè)關(guān)鍵步驟,包括準(zhǔn)備和上傳訓(xùn)練數(shù)據(jù),訓(xùn)練新的微調(diào)模型,以及模型的實(shí)際使用。具體的定價(jià)信息已在OpenAI的定價(jià)頁(yè)面上公布。
目前,推薦使用的微調(diào)模型是gpt-3.5-turbo-0613,但也支持其他模型如babbage-002和davinci-002。OpenAI預(yù)計(jì)今年晚些時(shí)候?qū)镚PT-4啟用微調(diào)功能。
4、何時(shí)進(jìn)行微調(diào)
雖然GPT模型進(jìn)行微調(diào)確實(shí)能夠提高其在特定任務(wù)上的性能,但這并不應(yīng)是首選方案。在微調(diào)之前,開(kāi)發(fā)者可以先嘗試通過(guò)提示工程、提示鏈接和函數(shù)調(diào)用來(lái)優(yōu)化模型性能。與微調(diào)相比,這些策略具有更快的反饋循環(huán),允許開(kāi)發(fā)者在不創(chuàng)建數(shù)據(jù)集和運(yùn)行訓(xùn)練作業(yè)的情況下迅速迭代和改進(jìn)。事實(shí)上,許多初步表現(xiàn)不佳的任務(wù)可以通過(guò)更精確的提示而得到改善,從而避免了微調(diào)的需要。
即使在微調(diào)成為必要的情況下,早期的提示工程工作也不會(huì)浪費(fèi)。實(shí)際上,最佳的微調(diào)效果通常可在使用了良好提示的數(shù)據(jù)中觀(guān)察到。就是使用更加廉價(jià),高效的方式來(lái)優(yōu)化模型,在效果不佳的情況下再考慮對(duì)模型進(jìn)行微調(diào),畢竟微調(diào)需要付出更高的時(shí)間和資金成本。
微調(diào)主要用于改善模型在風(fēng)格、語(yǔ)調(diào)、格式和特定任務(wù)方面的性能。同時(shí),它也能有效地降低成本和延遲,特別是當(dāng)從GPT-4遷移到gpt-3.5-turbo時(shí)。
廢話(huà)不多說(shuō),我們開(kāi)始微調(diào)GPT-3.5 Trubo模型。
5、準(zhǔn)備數(shù)據(jù)集
我們想把GPT-3.5 Turbo模型調(diào)優(yōu)成一個(gè)天氣預(yù)報(bào)員,不過(guò)這個(gè)預(yù)報(bào)員會(huì)通過(guò)說(shuō)冷笑話(huà)的方式來(lái)預(yù)報(bào)天氣。第一步,我們需要準(zhǔn)備一些數(shù)據(jù)集。
數(shù)據(jù)集中的每個(gè)示例都需要與Chat completions API相同格式的對(duì)話(huà),具體來(lái)說(shuō),每條消息都有角色、內(nèi)容和可選名稱(chēng)。
下面是官方提供的樣例:
{"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."}]}
JSON對(duì)象是用于微調(diào)GPT模型的訓(xùn)練數(shù)據(jù)示例。每個(gè)JSON對(duì)象代表一個(gè)對(duì)話(huà)實(shí)例,由三個(gè)部分組成:
(1)系統(tǒng)消息(System Message):定義了聊天機(jī)器人(在這里是名為“Marv”的機(jī)器人)的基本特性。在這個(gè)例子中,系統(tǒng)消息標(biāo)明Marv是一個(gè)提供準(zhǔn)確信息但也具有諷刺性的聊天機(jī)器人。
(2)用戶(hù)消息(User Message):這是用戶(hù)向聊天機(jī)器人提出的問(wèn)題或命令。
(3)助理消息(Assistant Message):這是聊天機(jī)器人對(duì)用戶(hù)消息的響應(yīng)。在這個(gè)例子中,由于Marv是一個(gè)諷刺性的聊天機(jī)器人,所以它的回應(yīng)是準(zhǔn)確但帶有諷刺色彩的。
具體地,每條消息都有兩個(gè)字段:
role:指定消息的發(fā)送者角色,可以是system、user或assistant。
content:包含消息內(nèi)容的字段。
要微調(diào)模型,需要至少提供10個(gè)示例。如果能夠準(zhǔn)備50到100個(gè)訓(xùn)練示例,對(duì)gpt-3.5-turbo進(jìn)行微調(diào)會(huì)有明顯的改善。官方建議可以準(zhǔn)備50個(gè)示例進(jìn)行微調(diào),在觀(guān)察微調(diào)結(jié)果后再尋求改善。需要說(shuō)明的是,每個(gè)訓(xùn)練示例限制為4096個(gè)令牌。超過(guò)此長(zhǎng)度的示例將在訓(xùn)練時(shí)截?cái)酁榍?096個(gè)令牌。為確保整個(gè)訓(xùn)練示例適合上下文,請(qǐng)考慮檢查消息內(nèi)容中的總令牌計(jì)數(shù)是否低于4000。每個(gè)文件目前限制為50 MB。
為了估算微調(diào)作業(yè)的成本,可以使用以下公式:對(duì)于訓(xùn)練了3周期的100,000個(gè)令牌的訓(xùn)練文件,預(yù)計(jì)成本將為~$2.40。
在清楚上面的情況以后,我們來(lái)創(chuàng)建自己的數(shù)據(jù)集合。我們想讓GPT-3.5 Turbo成為一個(gè)會(huì)講笑話(huà)的天氣助理。并且通過(guò)不同的問(wèn)答的方式,告訴它如何“幽默”地回答人類(lèi)關(guān)于天氣的問(wèn)題。
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "今天北京的天氣怎么樣?"}, {"role": "assistant", "content": "今天北京的天氣晴朗,就像我的心情一樣明朗!"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "上海明天會(huì)下雨嗎?"}, {"role": "assistant", "content": "上海明天不會(huì)下雨,但我的幽默感可能會(huì)淋濕你!"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "廣州的濕度怎么樣?"}, {"role": "assistant", "content": "廣州的濕度很高,就像我的幽默感一樣無(wú)處不在!"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "成都的溫度是多少?"}, {"role": "assistant", "content": "成都的溫度適中,就像一杯剛剛好的茶!"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "杭州周末會(huì)晴天嗎?"}, {"role": "assistant", "content": "杭州周末會(huì)是晴天,完美的野餐天氣!別忘了帶上笑容。"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "深圳冬天冷嗎?"}, {"role": "assistant", "content": "深圳的冬天不冷,但我的冷笑話(huà)可能會(huì)讓你打寒顫!"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "武漢夏天熱嗎?"}, {"role": "assistant", "content": "武漢的夏天非常熱,就像我的幽默感一樣火辣!"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "西安有沙塵暴嗎?"}, {"role": "assistant", "content": "西安偶爾有沙塵暴,但不要擔(dān)心,我的笑話(huà)會(huì)吹走沙塵!"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "重慶的天氣如何?"}, {"role": "assistant", "content": "重慶的天氣多變,就像我的幽默感一樣難以捉摸!"}]}
{"messages": [{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"}, {"role": "user", "content": "南京的春天怎么樣?"}, {"role": "assistant", "content": "南京的春天溫暖宜人,就像一首美麗的詩(shī)歌!"}]}
6、檢查數(shù)據(jù)格式
前面我們創(chuàng)建了數(shù)據(jù)集合,雖然有一些需要注意的地方,但是整個(gè)過(guò)程還是比較方便的。如果你的訓(xùn)練集合比較大,要處理的數(shù)據(jù)比較多,就需要謹(jǐn)慎一些,畢竟每次調(diào)優(yōu)都是在花真金白銀的。因此,如果數(shù)據(jù)量比較大的話(huà),我建議對(duì)數(shù)據(jù)格式進(jìn)行檢查。這里創(chuàng)建了一個(gè)簡(jiǎn)單的Python腳本,您可以使用它來(lái)查找潛在錯(cuò)誤、查看令牌計(jì)數(shù)并估算微調(diào)作業(yè)的成本。這個(gè)檢查并非必選項(xiàng),各位可以酌情選擇。
如下代碼首先加載一個(gè)包含聊天消息的數(shù)據(jù)集,并對(duì)其進(jìn)行初步檢查。然后,它遍歷數(shù)據(jù)集,檢查每個(gè)示例的格式是否正確,并記錄任何格式錯(cuò)誤。最后,它使用tiktoken庫(kù)來(lái)計(jì)算數(shù)據(jù)集中的令牌數(shù)量,并打印一些統(tǒng)計(jì)信息,例如示例數(shù)量、消息數(shù)量、對(duì)話(huà)長(zhǎng)度和助理消息長(zhǎng)度的分布。此外,它還計(jì)算了訓(xùn)練的預(yù)計(jì)成本和時(shí)期數(shù)量。
# 導(dǎo)入必要的包
import json
import os
import tiktoken
import numpy as np
from collections import defaultdict
# 指定數(shù)據(jù)路徑
data_path = "<YOUR_JSON_FILE_HERE>"
# 通過(guò)讀取文件中的每一行并解析為JSON對(duì)象來(lái)加載數(shù)據(jù)集
with open(data_path) as f:
dataset = [json.loads(line) for line in f]
# 打印數(shù)據(jù)集的統(tǒng)計(jì)信息,例如示例數(shù)量和第一個(gè)示例
print("Num examples:", len(dataset))
print("First example:")
for message in dataset[0]["messages"]:
print(message)
# 初始化格式錯(cuò)誤計(jì)數(shù)器
format_errors = defaultdict(int)
# 遍歷數(shù)據(jù)集,檢查每個(gè)示例的格式
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:
# 檢查消息是否包含“role”和“content”鍵
if "role" not in message or "content" not in message:
format_errors["message_missing_key"] += 1
# 檢查消息是否包含未識(shí)別的鍵
if any(k not in ("role", "content", "name") for k in message):
format_errors["message_unrecognized_key"] += 1
# 檢查角色是否被識(shí)別
if message.get("role", None) not in ("system", "user", "assistant"):
format_errors["unrecognized_role"] += 1
# 檢查內(nèi)容是否存在且為字符串
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
# 打印找到的錯(cuò)誤
if format_errors:
print("Found errors:")
for k, v in format_errors.items():
print(f"{k}: {v}")
else:
print("No errors found")
# 使用tiktoken庫(kù)來(lái)獲取編碼
encoding = tiktoken.get_encoding("cl100k_base")
# 定義計(jì)算消息中令牌數(shù)量的函數(shù)
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
# 定義計(jì)算助理消息中令牌數(shù)量的函數(shù)
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
# 定義打印值分布的函數(shù)
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)}")
# 初始化警告和令牌計(jì)數(shù)
n_missing_system = 0
n_missing_user = 0
n_messages = []
convo_lens = []
assistant_message_lens = []
# 遍歷數(shù)據(jù)集,計(jì)算警告和令牌計(jì)數(shù)
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))
# 打印缺少的系統(tǒng)和用戶(hù)消息數(shù)量,以及消息、對(duì)話(huà)和助理消息的長(zhǎng)度分布
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")
# 定義最大令牌數(shù)量和目標(biāo)示例數(shù)量
MAX_TOKENS_PER_EXAMPLE = 4096
MIN_TARGET_EXAMPLES = 100
MAX_TARGET_EXAMPLES = 25000
TARGET_EPOCHS = 3
MIN_EPOCHS = 1
MAX_EPOCHS = 25
# 計(jì)算時(shí)期數(shù)量
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)
# 計(jì)算數(shù)據(jù)集中的計(jì)費(fèi)令牌數(shù)量
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")
開(kāi)始調(diào)優(yōu)
數(shù)據(jù)集建立好了, 并且也對(duì)它進(jìn)行了檢查。接下來(lái)就開(kāi)始調(diào)優(yōu)了。
上傳數(shù)據(jù)集
將數(shù)據(jù)集保存到 "gpt-3.5-turbo-ft-file.jsonl"文件中。執(zhí)行如下代碼:
import openai
openai.File.create(
file=open("gpt-3.5-turbo-ft-file.jsonl", "rb"),
purpose='fine-tune'
)
import openai:這行代碼導(dǎo)入了 OpenAI 的 Python 庫(kù),以便使用其 API 功能。
openai.File.create(...): 這個(gè)函數(shù)用于在 OpenAI 服務(wù)器上創(chuàng)建一個(gè)新的文件??梢詫?duì)上傳文件做后續(xù)處理或操作(在這種情況下,是為了微調(diào)模型)。
file=open("gpt-3.5-turbo-ft-file.jsonl", "rb"): 這里,file 參數(shù)指定了要上傳的文件。函數(shù) open("gpt-3.5-turbo-ft-file.jsonl", "rb") 打開(kāi)了一個(gè)名為 gpt-3.5-turbo-ft-file.jsonl 的文件,以二進(jìn)制讀取模式("rb")。
purpose='fine-tune':這個(gè) purpose 參數(shù)標(biāo)記了文件上傳的目的。在這里,目的是“微調(diào)”(fine-tune)模型。
執(zhí)行上述代碼之后得到如下結(jié)果:
<File file id=file-F8Gh75F2A5R0gWlq5KADZdZG at 0x78f25bdc1df0> JSON: {
"object": "file",
"id": "file-F8Gh75F2A5R0gWlq5KADZdZG",
"purpose": "fine-tune",
"filename": "file",
"bytes": 2545,
"created_at": 1692886089,
"status": "uploaded",
"status_details": null }
我們來(lái)逐一解釋一下返回的結(jié)果。
"object": "file":指示這個(gè) JSON 對(duì)象代表一個(gè)“文件”。
"id": "file-F8Gh75F2A5R0gWlq5KADZdZG": 這是文件的唯一標(biāo)識(shí)符(ID)。在后續(xù)的調(diào)優(yōu)中用到它,也就是針對(duì)這個(gè)上傳文件進(jìn)行調(diào)優(yōu)。
"purpose": "fine-tune": 這表示文件的用途是用于微調(diào)模型,這與你在 openai.File.create() 函數(shù)中設(shè)置的 purpose='fine-tune' 是一致的。
"filename": "file": 這是上傳文件的名稱(chēng)。在這個(gè)例子中,它被簡(jiǎn)單地命名為 "file"。
"bytes": 2545": 這表示文件的大小是 2545 字節(jié)。
"created_at": 1692886089":這是文件創(chuàng)建(或上傳)時(shí)間的 Unix 時(shí)間戳。
"status": "uploaded": 這表示文件的當(dāng)前狀態(tài)是“已上傳”。
"status_details": null:這里提供了關(guān)于文件狀態(tài)的額外細(xì)節(jié)。在這個(gè)例子中,沒(méi)有提供額外的狀態(tài)細(xì)節(jié)(null)。
7、進(jìn)行調(diào)優(yōu)
好了,文件上傳之后接著執(zhí)行調(diào)優(yōu)的代碼如下:
openai.FineTuningJob.create(training_file="file-F8Gh75F2A5R0gWlq5KADZdZG", model="gpt-3.5-turbo")
代碼比較簡(jiǎn)單,看上去也比較好理解:
training_file="file-F8Gh75F2A5R0gWlq5KADZdZG": training_file參數(shù)指定了用于微調(diào)的訓(xùn)練數(shù)據(jù)文件的 ID。這個(gè) ID 應(yīng)該是你之前上傳文件時(shí)獲得的。
model="gpt-3.5-turbo": model 參數(shù)指定了你想微調(diào)的模型版本。在這個(gè)例子中,選擇的是 GPT-3.5 Turbo。
8、查看調(diào)優(yōu)進(jìn)度
調(diào)優(yōu)并不是一蹴而就的,整個(gè)過(guò)程會(huì)有OpenAI服務(wù)器完成,因此需要等待一段時(shí)間。在這段時(shí)間里面,我們會(huì)通過(guò)代碼檢查調(diào)優(yōu)的狀態(tài)和進(jìn)度。
# List 10 fine-tuning jobs
openai.FineTuningJob.list(limit=10)
# Retrieve the state of a fine-tune
response = openai.FineTuningJob.retrieve("ftjob-OJAXmjzlYT0TKbrHA9p2TWro")
print(response)
# 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"
一起來(lái)看看上面的代碼做了什么,注釋掉的部分雖然在本例中沒(méi)有用到,但是在一些場(chǎng)景會(huì)使用,因此一并放了進(jìn)來(lái):
(1)查看微調(diào)作業(yè)列表:`openai.FineTuningJob.list(limit=10)`**: 這一行列出了最近的 10 個(gè)微調(diào)作業(yè)。`limit=10` 表示最多列出 10 個(gè)作業(yè)。這對(duì)于跟蹤多個(gè)微調(diào)任務(wù)或查看歷史作業(yè)非常有用。
(2)獲取微調(diào)作業(yè)的狀態(tài):`response = openai.FineTuningJob.retrieve("ftjob-OJAXmjzlYT0TKbrHA9p2TWro")`**: 這一行通過(guò)微調(diào)作業(yè)的唯一 ID(在這里是 `"ftjob-OJAXmjzlYT0TKbrHA9p2TWro"`)來(lái)檢索特定微調(diào)作業(yè)的狀態(tài)和信息。
(3)取消微調(diào)作業(yè)(注釋掉了):`#openai.FineTuningJob.cancel("ft-abc123")`**:可以用來(lái)取消一個(gè)指定 ID 的微調(diào)作業(yè)。在這里,作業(yè) ID 是 `"ft-abc123"`。
(4)列出微調(diào)作業(yè)的事件(注釋掉了):`#openai.FineTuningJob.list_events(id="ft-abc123", limit=10)`**:用于列出一個(gè)特定微調(diào)作業(yè)的最多 10 個(gè)事件。這些事件可能包括作業(yè)開(kāi)始、進(jìn)度更新或作業(yè)完成等。
(5)刪除微調(diào)模型(注釋掉了):`#openai.Model.delete("ft-abc123")`**:用于刪除一個(gè)已經(jīng)微調(diào)過(guò)的模型。注意,只有模型所屬組織的所有者才能刪除它。
運(yùn)行上面代碼就可以看到詳細(xì)的調(diào)優(yōu)過(guò)程了,如下:
{
"object": "fine_tuning.job",
"id": "ftjob-OJAXmjzlYT0TKbrHA9p2TWro",
"model": "gpt-3.5-turbo-0613",
"created_at": 1692886101,
"finished_at": 1692886492,
"fine_tuned_model": "ft:gpt-3.5-turbo-0613:personal::7r5OjUmx",
"organization_id": "org-4P7htKo6DejPTQxfu3rExc7D",
"result_files": [
"file-9mLgEz2wKpHGoKtkZ0I3O8Yk"
],
"status": "succeeded",
"validation_file": null,
"training_file": "file-F8Gh75F2A5R0gWlq5KADZdZG",
"hyperparameters": {
"n_epochs": 10
},
"trained_tokens": 6810
}
雖然返回的信息很多,但是還是要耐心對(duì)其進(jìn)行分析。把幾個(gè)重點(diǎn)字段列出如下:
"object": "fine_tuning.job"`**: 指定這個(gè) JSON 對(duì)象代表一個(gè)微調(diào)作業(yè)。
`"id": "ftjob-OJAXmjzlYT0TKbrHA9p2TWro"`**: 微調(diào)作業(yè)的唯一標(biāo)識(shí)符。
`"model": "gpt-3.5-turbo-0613"`**: 表示用于微調(diào)的基礎(chǔ)模型。
`"created_at": 1692886101"`**: 作業(yè)創(chuàng)建時(shí)間的 Unix 時(shí)間戳。
`"finished_at": 1692886492"`**: 作業(yè)完成時(shí)間的 Unix 時(shí)間戳。
`"fine_tuned_model": "ft:gpt-3.5-turbo-0613:personal::7r5OjUmx"`**: 微調(diào)后生成的模型的唯一標(biāo)識(shí)符。
`"result_files": ["file-9mLgEz2wKpHGoKtkZ0I3O8Yk"]`**: 包含微調(diào)結(jié)果的文件的 ID。
`"status": "succeeded"`**: 微調(diào)作業(yè)的狀態(tài),這里是“成功”。
`"training_file": "file-F8Gh75F2A5R0gWlq5KADZdZG"`**: 用于訓(xùn)練的文件 ID。
`"hyperparameters": {"n_epochs": 10}`**: 微調(diào)作業(yè)使用的超參數(shù),這里只設(shè)置了訓(xùn)練周期(`n_epochs`)為 10。
`"trained_tokens": 6810"`**: 在微調(diào)過(guò)程中訓(xùn)練的令牌(tokens)數(shù)量。
9、測(cè)試微調(diào)之后的模型
執(zhí)行如下代碼,讓我們問(wèn)問(wèn)微調(diào)之后的GPT-3.5 Turbo天氣的問(wèn)題。
fine_tuned_model_id = response["fine_tuned_model"]
completion = openai.ChatCompletion.create(
model=fine_tuned_model_id, # 請(qǐng)確保使用您微調(diào)后的模型ID
temperature=0.7,
max_tokens=500,
messages=[
{"role": "system", "content": "你是一個(gè)會(huì)講笑話(huà)的天氣助理。"},
{"role": "user", "content": "今年武漢的冬天冷不冷?"}
]
)
print(completion.choices[0].message['content'])
fine_tuned_model_id = response["fine_tuned_model"]: 從之前獲取的微調(diào)作業(yè)響應(yīng)(response)中提取出微調(diào)后的模型 ID,并存儲(chǔ)在 fine_tuned_model_id 變量中。
completion = openai.ChatCompletion.create(...): 調(diào)用 OpenAI 的 ChatCompletion.create 方法來(lái)生成聊天回應(yīng)。
model=fine_tuned_model_id: 指定使用微調(diào)后的模型 ID。這確保了生成的回應(yīng)基于你的微調(diào)模型。
微調(diào)之后的GPT-3.5 Turbo說(shuō)出的冷笑話(huà),不知道是不是夠冷?
10、總結(jié)
GPT-3.5 Turbo 的微調(diào)功能為開(kāi)發(fā)者和企業(yè)提供了一種有效的方式,以定制大語(yǔ)言模型以適應(yīng)特定的應(yīng)用需求。通過(guò)微調(diào),模型在執(zhí)行任務(wù)時(shí)不僅更可操控、輸出更可靠,而且可以更準(zhǔn)確地反映企業(yè)的品牌語(yǔ)氣。此外,微調(diào)還有助于減少API調(diào)用的時(shí)間和成本。
本文深入解析了這一全新功能,涵蓋了從安全性和成本到準(zhǔn)備和驗(yàn)證數(shù)據(jù)集的全方位內(nèi)容。文章還通過(guò)代碼示例詳細(xì)演示了如何進(jìn)行模型微調(diào),從上傳數(shù)據(jù)集到測(cè)試微調(diào)結(jié)果,提供了一條明確的操作路徑??偟膩?lái)說(shuō),微調(diào)作為一個(gè)強(qiáng)大的工具,極大地?cái)U(kuò)展了GPT-3.5 Turbo 在各種應(yīng)用場(chǎng)景中的可能性。
作者介紹:崔皓,51CTO社區(qū)編輯,資深架構(gòu)師,擁有18年的軟件開(kāi)發(fā)和架構(gòu)經(jīng)驗(yàn),10年分布式架構(gòu)經(jīng)驗(yàn)。