譯者 | 朱先忠
審校 | 重樓
本文將通過具體的實戰代碼示例來探索谷歌開源Gemini Flash模型的學習曲線和采樣效率。
在大多數常見的機器學習和自然語言處理中,實現最佳性能通常需要在用于訓練的數據量和由此產生的模型準確性之間進行權衡。本文中,我們將以PII(個人識別信息)脫敏算法數據集為例,探討使用微調谷歌Gemini Flash模型的情況下樣本效率的概念。我們將研究隨著樣本數量的增加而進行的微調如何影響調整后的模型的功能。
何謂樣本效率,為什么它很重要?
樣本效率(Sample efficiency)是指模型在有限的訓練數據量下實現高精度的能力。這是機器學習開發的一個關鍵方面,尤其是在處理大型標記數據集可能稀缺或獲取成本高昂的任務或領域的情況下。一個樣本高效的模型可以從更少的樣本中有效地學習,減少與數據收集和訓練相關的時間、成本和精力。LLM被證明是非常有樣本效率的,甚至能夠在很少的樣本情況下進行情境學習,也能夠顯著提高性能。本文的主要目標是以谷歌開源的Gemini Flash模型為例來探討這方面的問題。我們將在不同設置條件下評估此LLM模型,然后繪制學習曲線,以便更直觀地了解訓練數據量是如何影響性能的。
顯示訓練得分和交叉驗證得分的學習曲線示例(來源:維基百科)
微調Gemini Flash模型實現PII脫敏實戰
為了展示樣本效率的影響,我們將進行一項實驗,重點是微調Gemini Flash模型,從而應用于個人識別信息脫敏。我們將使用Hugging Face公司的公開個人識別信息脫敏數據集,并評估模型在不同微調場景下的性能:
- 零樣本設置:評估預先訓練的Gemini Flash模型,無需任何微調。
- 小樣本設置(3-shot):在要求模型個人識別信息脫敏新文本之前,為模型提供3個樣本。
- 使用50|200|800|3200|6400個樣本進行微調:使用從小到大的“PII/Masked(個人識別信息明文/個人識別信息密文)”對數據集對模型進行微調。
對于每種設置,我們將在200個句子的固定測試集上評估模型的性能,使用BLEU(bilingual evaluation understudy:雙語替換測評)指標來衡量生成的脫敏文本的質量。該指標能夠評估模型輸出和脫敏句子之間的重疊性,提供脫敏準確性的定量衡量。
實驗限制
這個小實驗的結果可能不會直接推廣到其他使用場景或數據集情況下。另外,微調的最佳數據量取決于各種因素,包括任務的性質和復雜性、數據的質量以及基礎模型的具體特征等。
我的建議是,你可以從這篇文章提供的代碼中獲得一些靈感,或者:
- 如果你已經有了數據,可以直接將其應用于你的使用場景,這樣你就可以看到你的訓練曲線是否變慢了(這意味著,你的收益正在顯著下降)
- 或者,如果你沒有數據的話,那么可以為你所遇到的同類問題(分類、命名實體識別、摘要)和類似的難度級別找到一個數據集。這樣的話,你就可以通過繪制學習曲線來了解你自己的任務需要多少數據。
實驗數據
我們將使用在Huggingface上共享的PII(個人身份信息)掩蔽數據集。
該數據集中包含兩對文本,一對是帶有脫敏信息的原始文本,另一對文本隱藏了所有原始個人身份信息。
舉例
輸入:
A student’s assessment was found on device bearing IMEI: 06–184755–866851–3. The document falls under the various topics discussed in our Optimization curriculum. Can you please collect it?
目標:
A student’s assessment was found on device bearing IMEI: [PHONEIMEI]. The document falls under the various topics discussed in our [JOBAREA] curriculum. Can you please collect it?
注意,上面數據是經過合成處理的;所以,這里實際上沒有共享真正的個人身份信息。
我們的目標是建立從源文本到目標文本的映射,以自動隱藏所有個人身份信息。
數據許可:https://huggingface.co/datasets/ai4privacy/pii-masking-200k/blob/main/license.md。
編程實現
我們將提供一些編程代碼,以方便加速此實驗進程。編碼中,我們將利用Hugging Face數據集庫加載個人身份信息脫敏數據集,并利用google.generativeai庫與Gemini Flash交互,還將利用evaluate庫來計算BLEU(雙語替換測評)分數。
pip install transformers datasets evaluate google-generativeai python-dotenv sacrebleu
此代碼段實現安裝項目所需的庫,包括:
- 數據集:便于從Hugging Face加載和處理數據集。
- evaluate庫:允許使用SacreBLEU等評估指標。
- google-generativeai:允許與谷歌的Gemini API交互。
首先,我們進行數據加載和拆分:
#導入必需的庫
from datasets import load_dataset
from google.generativeai.types import HarmCategory, HarmBlockThreshold
# Define GOOGLE_API_KEY as a global variable
# 加載和分割數據集函數
def load_data(train_size: int, test_size: int):
"""
加載pii-masking-200k數據集并把它拆分成訓練與測試兩種類型。
參數:
train_size: 訓練數據集的大小
test_size: 測試數據集的大小
返回:
一個包含訓練數據集和測試數據集的元組。
"""
dataset = load_dataset("ai4privacy/pii-masking-200k")
dataset = dataset["train"].train_test_split(test_size=test_size, seed=42)
train_d = dataset["train"].select(range(train_size))
test_d = dataset["test"]
return train_d, test_d
接下來,我們嘗試為該任務提示零樣本。這意味著,我們向LLM解釋任務,并要求它從原始文本中生成個人身份信息脫敏數據。這是通過使用一個列出所有需要脫敏標簽的提示來完成的。
我們還將LLM API的調用并行化,以稍微加快速度。
最后,我們使用BLEU評分進行評估。它是一種基于精度的度量方案,通常用于機器翻譯,將模型輸出與參考句子進行比較。雖然這種方法存在一定局限性,但卻易于應用,適用于像我們手頭這樣的文本到文本任務。
import google.generativeai as genai
from google.generativeai.types.content_types import ContentDict
from google.generativeai.types import HarmCategory, HarmBlockThreshold
from concurrent.futures import ThreadPoolExecutor
import evaluate
safety_settings = {
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
}
SYS_PROMPT = (
"Substitute all PII in this text for a generic label like [FIRSTNAME] (Between square brackets)\n"
"Labels to substitute are PREFIX, FIRSTNAME, LASTNAME, DATE, TIME, "
"PHONEIMEI, USERNAME, GENDER, CITY, STATE, URL, JOBAREA, EMAIL, JOBTYPE, "
"COMPANYNAME, JOBTITLE, STREET, SECONDARYADDRESS, COUNTY, AGE, USERAGENT, "
"ACCOUNTNAME, ACCOUNTNUMBER, CURRENCYSYMBOL, AMOUNT, CREDITCARDISSUER, "
"CREDITCARDNUMBER, CREDITCARDCVV, PHONENUMBER, SEX, IP, ETHEREUMADDRESS, "
"BITCOINADDRESS, MIDDLENAME, IBAN, VEHICLEVRM, DOB, PIN, CURRENCY, "
"PASSWORD, CURRENCYNAME, LITECOINADDRESS, CURRENCYCODE, BUILDINGNUMBER, "
"ORDINALDIRECTION, MASKEDNUMBER, ZIPCODE, BIC, IPV4, IPV6, MAC, "
"NEARBYGPSCOORDINATE, VEHICLEVIN, EYECOLOR, HEIGHT, SSN, language"
)
#計算零樣本設置的函數
def evaluate_zero_shot(train_data, test_data, model_name="gemini-1.5-flash"):
"""
評估該模型的零樣本性能。
參數:
train_data: 訓練數據集(不用于零樣本情況)。
test_data: 測試數據集。
model_name: 要使用的模型的名稱。
返回值 :
零樣本設置的SacreBLEU分數
"""
model = genai.GenerativeModel(model_name)
def map_zero_shot(text):
messages = [
ContentDict(
role="user",
parts=[f"{SYS_PROMPT}\nText: {text}"],
),
]
response = model.generate_content(messages, safety_settings=safety_settings)
try:
return response.text
except ValueError:
print(response)
return ""
with ThreadPoolExecutor(max_workers=4) as executor:
predictions = list(
executor.map(
map_zero_shot,
[example["source_text"] for example in test_data],
)
)
references = [[example["target_text"]] for example in test_data]
sacrebleu = evaluate.load("sacrebleu")
sacrebleu_results = sacrebleu.compute(
predictions=predictions, references=references
)
print(f"Zero-shot SacreBLEU score: {sacrebleu_results['score']}")
return sacrebleu_results["score"]
現在,讓我們進一步探討有關提示的問題。除了向LLM解釋任務外,我們還將向它展示三個我們期望它做什么的例子。這種技巧通常有助于提高性能。
# 評估小樣本設置的函數
def evaluate_few_shot(train_data, test_data, model_name="gemini-1.5-flash"):
"""
評估模型的小樣本性能
參數:
train_data: 訓練數據集
test_data: 測試數據集
model_name: 要使用的模型的名稱。
返回值:
小樣本設置的SacreBLEU分數
"""
model = genai.GenerativeModel(model_name)
def map_few_shot(text, examples):
messages = [
ContentDict(
role="user",
parts=[SYS_PROMPT],
)
]
for example in examples:
messages.append(
ContentDict(role="user", parts=[f"Text: {example['source_text']}"]),
)
messages.append(
ContentDict(role="model", parts=[f"{example['target_text']}"])
)
messages.append(ContentDict(role="user", parts=[f"Text: {text}"]))
response = model.generate_content(messages, safety_settings=safety_settings)
try:
return response.text
except ValueError:
print(response)
return ""
few_shot_examples = train_data.select(range(3))
with ThreadPoolExecutor(max_workers=4) as executor:
predictions = list(
executor.map(
lambda example: map_few_shot(example["source_text"], few_shot_examples),
test_data,
)
)
references = [[example["target_text"]] for example in test_data]
sacrebleu = evaluate.load("sacrebleu")
sacrebleu_results = sacrebleu.compute(
predictions=predictions, references=references
)
print(f"3-shot SacreBLEU score: {sacrebleu_results['score']}")
return sacrebleu_results["score"]
最后,我們來嘗試一下使用微調方案。在這里,我們只使用Gemini API的托管服務。它現在是免費的,所以不妨使用一下。注意,我們將使用越來越多的數據,并比較每種數據的性能。
運行調優任務再簡單不過了:我們只需使用genai.create_tuned_model函數來處理數據、迭代次數、學習率和參數。
訓練任務是異步的,這意味著我們不必等待程序的運行結束。不過,程序的執行要排隊,通常在24小時內完成。
def finetune(train_data, finetune_size, model_name="gemini-1.5-flash"):
"""
調優模型
參數:
train_data: 訓練數據集。
finetune_size:用于微調的樣本數量。
model_name: 要用于進行微調的基本模型的名稱。
返回值:
已調優的模型的名稱。
"""
base_model = f"models/{model_name}-001-tuning"
tuning_data = [
{
"text_input": f"{SYS_PROMPT}\nText: {example['source_text']}",
"output": example["target_text"],
}
for example in train_data.select(range(finetune_size))
]
print(len(tuning_data))
operation = genai.create_tuned_model(
display_name=f"tuned-{finetune_size}",
source_model=base_model,
epoch_count=2,
batch_size=4,
learning_rate=0.0001,
training_data=tuning_data,
)
你可以使用以下代碼片段來檢查一下調優任務的狀態:
import google.generativeai as genai
for model_info in genai.list_tuned_models():
print(model_info.name)
print(model_info)
實驗結果對比分析
構建PII數據脫敏函數時不同設置方案結果比較
容易看出,PII數據脫敏算法通過添加更多用于微調的訓練數據提高了性能。
零樣本和小樣本情況
由上圖可知,零樣本方法獲得了83.85的令人尊敬的BLEU分數。這表明,即使沒有任何訓練樣本,模型也會對任務有一些基本的理解。然而,實驗中只提供三個樣本(3-shot)就可以將分數提高到87.59,這說明了即使是有限的樣本情況下,這些樣本在LLM情境學習中也是非常有效的。
微調情況
實驗一開始,我們使用50個樣本的小數據集進行微調,得出的BLEU得分為86.38,略低于3-shot方法。然而,隨著訓練數據的增加,性能顯著提高。在使用了200個樣本的情況下,BLEU得分躍升至90.97,而在使用了800個樣本時則達到了94.30。當使用最大數量達到6400個樣本測試的情況下達到最高BLEU分數97.52。
結論
我們最后得到的基本結論是:一切都在意料之中,隨著數據的增加,性能也會提高。雖然Gemini Flash模型的零樣本和小樣本功能令人印象深刻,證明了其具有推廣到新任務的能力,但是使用足夠大的數據進行微調時也會顯著提高其準確性。這里唯一出乎意料的是,如果訓練數據的數量太少或質量太低,那么小樣本提示結果的準確率有時會勝過經過微調后的準確率。
總之,我們可以得到如下幾條關鍵結論:
- 要實現高性能目標,微調可能是必要的:即使少量的微調數據也比零樣本和小樣本方法產生巨大的改進。
- 更多的數據通常會帶來更好的結果:隨著微調數據集大小的增加,調優后的模型的準確脫敏個人身份信息的能力也會提高,如上圖中BLEU分數的上升所表明的。
- 收益遞減:雖然更多的數據通常會產生更好的數據結果,但也可能會出現性能增長開始趨于平穩的時刻。確定這一點可以幫助我們更好地權衡標簽預算和調整模型質量之間的權衡。
還需要說明的是,在我們的例子中,穩定期從3200個樣本開始,任何高于這個水平的樣本都會產生正的但遞減的回報。
譯者介紹
朱先忠,51CTO社區編輯,51CTO專家博客、講師,濰坊一所高校計算機教師,自由編程界老兵一枚。
原文標題:How Much Data Do You Need to Fine-Tune Gemini?,作者:Youness Mansar