1.概述
今天我們將深入探索ChatGPT的兩項核心技術:Fine-tuning(微調)和Embeddings(嵌入)。這些技術在現代自然語言處理領域扮演着至關重要的角色,爲模型的性能提升和適應特定任務需求提供了關鍵支持。ChatGPT作爲GPT家族的一員,已經在多個領域展現了出色的表現,而其背後的Fine-tuning和Embeddings技術則是其成功的關鍵因素之一。
2.內容
2.1 什麼是Fine-tuning
Fine-tuning,又稱微調,是指在預訓練模型(如GPT-3)的基礎上,通過在特定任務上繼續訓練模型,使其適應特定任務的需求。GPT-3在大規模文本語料上進行了預訓練,學會了豐富的語言知識和模式。然而,要使模型在特定任務上表現出色,就需要對其進行進一步的微調。
ChatGPT的Fine-tuning涉及到將模型暴露在各種對話和語境中,以便它能夠更好地理解並生成自然對話。舉個例子,如果我們想要讓ChatGPT用於醫療諮詢,Fine-tuning的過程將包括讓模型學習醫學知識、專業術語和與患者交流的最佳實踐。這種Fine-tuning使得ChatGPT能夠根據任務的特定上下文作出更準確的迴應。
微調可讓你通過 API 提供以下功能,從而更充分地利用可用模型:
- 比提示更高質量的結果
- 能夠訓練超出提示範圍的示例
- 由於提示較短而節省了代幣
- 更低的延遲請求
GPT 模型已經過大量文本的預訓練。爲了有效地使用模型,在提示中包含說明,有時還包含幾個示例。使用演示來展示如何執行任務通常稱爲“小樣本學習”。
微調通過訓練超出提示範圍的更多示例來改進小樣本學習,讓你在大量任務上取得更好的結果。一旦模型經過微調,你就不需要在提示中提供那麼多示例。這可以節省成本並實現更低延遲的請求。
在較高層面上,微調涉及以下步驟:
- 準備並上傳訓練數據
- 訓練新的微調模型
- 使用你的微調模型
2.2 哪些模型可以進行微調
目前可對以下型號進行微調:
- gpt-3.5-turbo-0613(推薦)
- babbage-002
- davinci-002
目前gpt-3.5-turbo在結果和易用性方面成爲大多數用戶的正確模型。
2.3 何時使用微調
微調 GPT 模型可以使其更好地適應特定應用,但這需要仔細投入時間和精力。建議首先嚐試通過 Prompt 工程、Prompt Chaining(將複雜的任務分解爲多個Prompt)和函數調用來獲得良好的結果,主要原因是:
- 對於許多任務,模型最初可能表現不佳,但通過更好的 Prompt,我們可以取得更好的結果,並且可能不需要進行微調
- 迭代 Prompt 和其他策略比微調迭代具有更快的反饋循環,後者需要創建數據集並運行訓練作業
- 在仍然需要微調的情況下,最初的 Prompt 工程工作不會浪費 - 在微調數據中使用良好的 Prompt(或將 Prompt Chaining /工具使用與微調相結合)時,我們通常會看到最佳結果
GPT 最佳實踐指南提供了一些最有效的策略和策略的背景知識,無需微調即可獲得更好的性能。
2.4 常見用例
微調可以改善結果的一些常見用例:
- 設置風格、基調、格式或其他定性方面
- 提高產生所需輸出的可靠性
- 糾正未能遵循複雜提示的問題
- 以特定方式處理許多邊緣情況
- 執行難以在提示中闡明的新技能或任務
思考這些案例的一種高級方法是“展示而不是講述”更容易。在接下來的部分中,我們將探討如何設置用於微調的數據以及微調可以提高基準模型性能的各種示例。
微調有效的另一種情況是通過替換 GPT-4 或使用較短的 Prompt 來降低成本和/或延遲,而不犧牲質量。gpt-3.5-turbo如果您可以使用 GPT-4 獲得良好的結果,那麼您通常可以通過對 GPT-4 補全進行微調(可能還需要縮短指令提示)來使用微調模型達到類似的質量。
2.5 準備數據集
一旦你確定微調是正確的解決方案(即已經儘可能優化了 Prompt 並確定了模型仍然存在的問題),你將需要準備數據來訓練模型。你應該創建一組多樣化的演示對話,這些對話類似於您要求模型在生產中的推理時響應的對話。
數據集中的每個示例都應該是與我們的聊天完成 API 格式相同的對話,特別是消息列表,其中每條消息都有角色、內容和可選名稱。至少一些訓練示例應該直接針對提示模型未按預期運行的情況,並且數據中提供的輔助消息應該是您希望模型提供的理想響應。
2.6 格式示例
在此示例中,我們的目標是創建一個偶爾給出諷刺性響應的聊天機器人,這是我們可以爲數據集創建的三個訓練示例(對話):
{"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>"}
2.7 示例計數
要微調模型,你需要提供至少 10 個示例。我們通常會看到對 50 到 100 個訓練示例進行微調會帶來明顯的改進,gpt-3.5-turbo但正確的數量根據具體的用例而有很大差異。
建議從 50 個精心設計的演示開始,看看模型在微調後是否顯示出改進的跡象。在某些情況下,這可能就足夠了,但即使模型尚未達到生產質量,明顯的改進也是一個好兆頭,表明提供更多數據將繼續改進模型。沒有任何改進表明你可能需要重新考慮如何在超出有限示例集之前設置模型任務或重組數據。
2.8 檢查數據格式
編譯數據集後,在創建微調作業之前,檢查數據格式非常重要。爲此,我們創建了一個簡單的 Python 腳本,您可以使用它來查找潛在錯誤、檢查令牌計數並估計微調作業的成本。
驗證數據後,需要上傳文件才能用於微調作業:
openai.File.create( file=open("mydata.jsonl", "rb"), purpose='fine-tune' )
2.9 創建微調模型
確保數據集的數量和結構正確並上傳文件後,下一步是創建微調作業。
使用 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")
2.10 迭代數據質量
如果微調作業的結果不如預期,請考慮以下方式調整訓練數據集:
- 收集示例以解決剩餘問題
- 如果模型在某些方面仍然不擅長,請添加訓練示例,直接向模型展示如何正確地完成這些方面
- 仔細檢查現有示例是否存在問題
- 如果您的模型存在語法、邏輯或樣式問題,請檢查您的數據是否存在任何相同的問題。例如,如果模型現在說“我將爲您安排這次會議”(實際上不應該如此),請查看現有示例是否教導模型說它可以做它不能做的新事情
- 考慮數據的平衡性和多樣性
- 如果數據中 60% 的助理回答說“我無法回答這個問題”,但在推理時只有 5% 的回答應該這麼說,那麼您可能會得到過多的拒絕
- 確保您的訓練示例包含響應所需的所有信息
- 如果我們希望模型根據用戶的個人特徵來讚美用戶,並且訓練示例包括對先前對話中未找到的特徵的輔助讚美,則模型可能會學習產生幻覺信息
- 查看訓練示例中的一致性/一致性
- 如果多個人創建了訓練數據,則模型性能可能會受到人們之間的一致性/一致性程度的限制。例如,在文本提取任務中,如果人們僅同意 70% 的提取片段,則模型可能無法做得更好
- 確保所有訓練示例都採用相同的格式,正如推理所預期的那樣
3.Embeddings
3.1 什麼是Embeddings?
OpenAI 的文本嵌入衡量文本字符串的相關性。嵌入通常用於:
- 搜索(結果按與查詢字符串的相關性排名)
- 聚類(文本字符串按相似性分組)
- 推薦(推薦具有相關文本字符串的項目)
- 異常檢測(識別出相關性很小的異常值)
- 多樣性測量(分析相似性分佈)
- 分類(文本字符串按最相似的標籤進行分類)
嵌入是浮點數的向量(列表)。兩個向量之間的距離衡量它們的相關性。距離小表明相關性高,距離大表明相關性低。
3.2 如何獲得Embeddings
要獲得Embeddings,請將文本字符串以及嵌入模型 ID 的選擇(例如)發送到嵌入 API 端點text-embedding-ada-002。響應將包含一個嵌入,你可以提取、保存和使用它。
請求示例:
curl https://api.openai.com/v1/embeddings \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d '{ "input": "Your text string goes here", "model": "text-embedding-ada-002" }'
響應示例:
{ "data": [ { "embedding": [ -0.006929283495992422, -0.005336422007530928, ... -4.547132266452536e-05, -0.024047505110502243 ], "index": 0, "object": "embedding" } ], "model": "text-embedding-ada-002", "object": "list", "usage": { "prompt_tokens": 5, "total_tokens": 5 } }
3.3 Embeddings模型
OpenAI 提供了一個第二代嵌入模型(-002在模型 ID 中用 表示)和 16 個第一代模型(-001在模型 ID 中用 表示)。
建議對幾乎所有用例使用 text-embedding-ada-002。它更好、使用更簡單。
在Github中獲取數據集,該數據集包含截至 2012 年 10 月亞馬遜用戶留下的總共 568,454 條食品評論。出於說明目的,我們將使用 1,000 條最新評論的子集。評論是英文的,往往是正面的或負面的。每條評論都有 ProductId、UserId、Score、評論標題(摘要)和評論正文(文本)。例如:
def get_embedding(text, model="text-embedding-ada-002"): text = text.replace("\n", " ") return openai.Embedding.create(input = [text], model=model)['data'][0]['embedding'] df['ada_embedding'] = df.combined.apply(lambda x: get_embedding(x, model='text-embedding-ada-002')) df.to_csv('output/embedded_1k_reviews.csv', index=False)
要從保存的文件加載數據,你可以運行以下命令:
import pandas as pd df = pd.read_csv('output/embedded_1k_reviews.csv') df['ada_embedding'] = df.ada_embedding.apply(eval).apply(np.array)
3.4 Embeddings彙總常見問題
3.4.1 在嵌入字符串之前如何知道它有多少個標記?
在 Python 中,您可以使用 OpenAI 的 tokenizer 將字符串拆分爲標記tiktoken。
示例代碼:
import tiktoken def num_tokens_from_string(string: str, encoding_name: str) -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_name) num_tokens = len(encoding.encode(string)) return num_tokens num_tokens_from_string("tiktoken is great!", "cl100k_base")
對於像文本嵌入ada-002這樣的第二代嵌入模型,請使用cl100k_base編碼。
3.4.2 如何快速檢索 K 個最近的嵌入向量?
爲了快速搜索許多向量,我們建議使用向量數據庫。您可以在 GitHub 上的 Cookbook 中找到使用矢量數據庫和 OpenAI API 的示例。
矢量數據庫選項包括:
- Chroma,一個開源嵌入商店
- Milvus,一個爲可擴展的相似性搜索而構建的矢量數據庫
- Pinecone,一個完全託管的矢量數據庫
- Qdrant,矢量搜索引擎
- Redis作爲向量數據庫
- Typesense,快速開源矢量搜索
- Weaviate,一個開源矢量搜索引擎
- Zilliz,數據基礎設施,由 Milvus 提供支持
3.4.3 應該使用哪個距離函數?
推薦餘弦相似度。距離函數的選擇通常並不重要。
OpenAI 嵌入標準化爲長度 1,這意味着:
- 僅使用點積即可稍微更快地計算餘弦相似度
- 餘弦相似度和歐氏距離將導致相同的排名
4.Fine-tuning和Embeddings的挑戰
儘管Fine-tuning和嵌入技術在提高ChatGPT性能方面起到了關鍵作用,但也存在一些挑戰。其中之一是過擬合的問題。在Fine-tuning過程中,如果模型過於關注訓練數據中的噪聲或特定示例,可能導致模型在其他情況下表現不佳。因此,合適的正則化和數據清洗是確保模型泛化能力的重要因素。
另一個挑戰是平衡預訓練和Fine-tuning的關係。預訓練階段使模型具備了廣泛的語言知識,但Fine-tuning階段又需要在特定任務上進行調整。如何在這兩者之間取得平衡,以及如何在Fine-tuning過程中保留預訓練模型的有益特性,都需要深入研究和實驗。
5.總結
ChatGPT的成功背後離不開Fine-tuning和Embeddings技術的支持。Fine-tuning使得模型能夠適應特定任務,而嵌入技術則使模型能夠更好地理解語義和上下文。然而,這些技術也面臨着挑戰,需要持續的研究和改進。隨着人工智能領域的不斷髮展,我們可以期待看到更多關於Fine-tuning、嵌入技術以及模型個性化的創新和突破。