譯者 | 朱先忠
審校 | 重樓
本文將通過具體的實戰(zhàn)代碼示例來探索谷歌開源Gemini Flash模型的學習曲線和采樣效率。
在大多數(shù)常見的機器學習和自然語言處理中,實現(xiàn)最佳性能通常需要在用于訓練的數(shù)據(jù)量和由此產(chǎn)生的模型準確性之間進行權(quán)衡。本文中,我們將以PII(個人識別信息)脫敏算法數(shù)據(jù)集為例,探討使用微調(diào)谷歌Gemini Flash模型的情況下樣本效率的概念。我們將研究隨著樣本數(shù)量的增加而進行的微調(diào)如何影響調(diào)整后的模型的功能。
何謂樣本效率,為什么它很重要?
樣本效率(Sample efficiency)是指模型在有限的訓練數(shù)據(jù)量下實現(xiàn)高精度的能力。這是機器學習開發(fā)的一個關(guān)鍵方面,尤其是在處理大型標記數(shù)據(jù)集可能稀缺或獲取成本高昂的任務或領(lǐng)域的情況下。一個樣本高效的模型可以從更少的樣本中有效地學習,減少與數(shù)據(jù)收集和訓練相關(guān)的時間、成本和精力。LLM被證明是非常有樣本效率的,甚至能夠在很少的樣本情況下進行情境學習,也能夠顯著提高性能。本文的主要目標是以谷歌開源的Gemini Flash模型為例來探討這方面的問題。我們將在不同設(shè)置條件下評估此LLM模型,然后繪制學習曲線,以便更直觀地了解訓練數(shù)據(jù)量是如何影響性能的。
顯示訓練得分和交叉驗證得分的學習曲線示例(來源:維基百科)
微調(diào)Gemini Flash模型實現(xiàn)PII脫敏實戰(zhàn)
為了展示樣本效率的影響,我們將進行一項實驗,重點是微調(diào)Gemini Flash模型,從而應用于個人識別信息脫敏。我們將使用Hugging Face公司的公開個人識別信息脫敏數(shù)據(jù)集,并評估模型在不同微調(diào)場景下的性能:
- 零樣本設(shè)置:評估預先訓練的Gemini Flash模型,無需任何微調(diào)。
- 小樣本設(shè)置(3-shot):在要求模型個人識別信息脫敏新文本之前,為模型提供3個樣本。
- 使用50|200|800|3200|6400個樣本進行微調(diào):使用從小到大的“PII/Masked(個人識別信息明文/個人識別信息密文)”對數(shù)據(jù)集對模型進行微調(diào)。
對于每種設(shè)置,我們將在200個句子的固定測試集上評估模型的性能,使用BLEU(bilingual evaluation understudy:雙語替換測評)指標來衡量生成的脫敏文本的質(zhì)量。該指標能夠評估模型輸出和脫敏句子之間的重疊性,提供脫敏準確性的定量衡量。
實驗限制
這個小實驗的結(jié)果可能不會直接推廣到其他使用場景或數(shù)據(jù)集情況下。另外,微調(diào)的最佳數(shù)據(jù)量取決于各種因素,包括任務的性質(zhì)和復雜性、數(shù)據(jù)的質(zhì)量以及基礎(chǔ)模型的具體特征等。
我的建議是,你可以從這篇文章提供的代碼中獲得一些靈感,或者:
- 如果你已經(jīng)有了數(shù)據(jù),可以直接將其應用于你的使用場景,這樣你就可以看到你的訓練曲線是否變慢了(這意味著,你的收益正在顯著下降)
- 或者,如果你沒有數(shù)據(jù)的話,那么可以為你所遇到的同類問題(分類、命名實體識別、摘要)和類似的難度級別找到一個數(shù)據(jù)集。這樣的話,你就可以通過繪制學習曲線來了解你自己的任務需要多少數(shù)據(jù)。
實驗數(shù)據(jù)
我們將使用在Huggingface上共享的PII(個人身份信息)掩蔽數(shù)據(jù)集。
該數(shù)據(jù)集中包含兩對文本,一對是帶有脫敏信息的原始文本,另一對文本隱藏了所有原始個人身份信息。
舉例
輸入:
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?
注意,上面數(shù)據(jù)是經(jīng)過合成處理的;所以,這里實際上沒有共享真正的個人身份信息。
我們的目標是建立從源文本到目標文本的映射,以自動隱藏所有個人身份信息。
數(shù)據(jù)許可:https://huggingface.co/datasets/ai4privacy/pii-masking-200k/blob/main/license.md。
編程實現(xiàn)
我們將提供一些編程代碼,以方便加速此實驗進程。編碼中,我們將利用Hugging Face數(shù)據(jù)集庫加載個人身份信息脫敏數(shù)據(jù)集,并利用google.generativeai庫與Gemini Flash交互,還將利用evaluate庫來計算BLEU(雙語替換測評)分數(shù)。
pip install transformers datasets evaluate google-generativeai python-dotenv sacrebleu
此代碼段實現(xiàn)安裝項目所需的庫,包括:
- 數(shù)據(jù)集:便于從Hugging Face加載和處理數(shù)據(jù)集。
- evaluate庫:允許使用SacreBLEU等評估指標。
- google-generativeai:允許與谷歌的Gemini API交互。
首先,我們進行數(shù)據(jù)加載和拆分:
#導入必需的庫
from datasets import load_dataset
from google.generativeai.types import HarmCategory, HarmBlockThreshold
# Define GOOGLE_API_KEY as a global variable
# 加載和分割數(shù)據(jù)集函數(shù)
def load_data(train_size: int, test_size: int):
"""
加載pii-masking-200k數(shù)據(jù)集并把它拆分成訓練與測試兩種類型。
參數(shù):
train_size: 訓練數(shù)據(jù)集的大小
test_size: 測試數(shù)據(jù)集的大小
返回:
一個包含訓練數(shù)據(jù)集和測試數(shù)據(jù)集的元組。
"""
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解釋任務,并要求它從原始文本中生成個人身份信息脫敏數(shù)據(jù)。這是通過使用一個列出所有需要脫敏標簽的提示來完成的。
我們還將LLM API的調(diào)用并行化,以稍微加快速度。
最后,我們使用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"
)
#計算零樣本設(shè)置的函數(shù)
def evaluate_zero_shot(train_data, test_data, model_name="gemini-1.5-flash"):
"""
評估該模型的零樣本性能。
參數(shù):
train_data: 訓練數(shù)據(jù)集(不用于零樣本情況)。
test_data: 測試數(shù)據(jù)集。
model_name: 要使用的模型的名稱。
返回值 :
零樣本設(shè)置的SacreBLEU分數(shù)
"""
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"]
現(xiàn)在,讓我們進一步探討有關(guān)提示的問題。除了向LLM解釋任務外,我們還將向它展示三個我們期望它做什么的例子。這種技巧通常有助于提高性能。
# 評估小樣本設(shè)置的函數(shù)
def evaluate_few_shot(train_data, test_data, model_name="gemini-1.5-flash"):
"""
評估模型的小樣本性能
參數(shù):
train_data: 訓練數(shù)據(jù)集
test_data: 測試數(shù)據(jù)集
model_name: 要使用的模型的名稱。
返回值:
小樣本設(shè)置的SacreBLEU分數(shù)
"""
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"]
最后,我們來嘗試一下使用微調(diào)方案。在這里,我們只使用Gemini API的托管服務。它現(xiàn)在是免費的,所以不妨使用一下。注意,我們將使用越來越多的數(shù)據(jù),并比較每種數(shù)據(jù)的性能。
運行調(diào)優(yōu)任務再簡單不過了:我們只需使用genai.create_tuned_model函數(shù)來處理數(shù)據(jù)、迭代次數(shù)、學習率和參數(shù)。
訓練任務是異步的,這意味著我們不必等待程序的運行結(jié)束。不過,程序的執(zhí)行要排隊,通常在24小時內(nèi)完成。
def finetune(train_data, finetune_size, model_name="gemini-1.5-flash"):
"""
調(diào)優(yōu)模型
參數(shù):
train_data: 訓練數(shù)據(jù)集。
finetune_size:用于微調(diào)的樣本數(shù)量。
model_name: 要用于進行微調(diào)的基本模型的名稱。
返回值:
已調(diào)優(yōu)的模型的名稱。
"""
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,
)
你可以使用以下代碼片段來檢查一下調(diào)優(yōu)任務的狀態(tài):
import google.generativeai as genai
for model_info in genai.list_tuned_models():
print(model_info.name)
print(model_info)
實驗結(jié)果對比分析
構(gòu)建PII數(shù)據(jù)脫敏函數(shù)時不同設(shè)置方案結(jié)果比較
容易看出,PII數(shù)據(jù)脫敏算法通過添加更多用于微調(diào)的訓練數(shù)據(jù)提高了性能。
零樣本和小樣本情況
由上圖可知,零樣本方法獲得了83.85的令人尊敬的BLEU分數(shù)。這表明,即使沒有任何訓練樣本,模型也會對任務有一些基本的理解。然而,實驗中只提供三個樣本(3-shot)就可以將分數(shù)提高到87.59,這說明了即使是有限的樣本情況下,這些樣本在LLM情境學習中也是非常有效的。
微調(diào)情況
實驗一開始,我們使用50個樣本的小數(shù)據(jù)集進行微調(diào),得出的BLEU得分為86.38,略低于3-shot方法。然而,隨著訓練數(shù)據(jù)的增加,性能顯著提高。在使用了200個樣本的情況下,BLEU得分躍升至90.97,而在使用了800個樣本時則達到了94.30。當使用最大數(shù)量達到6400個樣本測試的情況下達到最高BLEU分數(shù)97.52。
結(jié)論
我們最后得到的基本結(jié)論是:一切都在意料之中,隨著數(shù)據(jù)的增加,性能也會提高。雖然Gemini Flash模型的零樣本和小樣本功能令人印象深刻,證明了其具有推廣到新任務的能力,但是使用足夠大的數(shù)據(jù)進行微調(diào)時也會顯著提高其準確性。這里唯一出乎意料的是,如果訓練數(shù)據(jù)的數(shù)量太少或質(zhì)量太低,那么小樣本提示結(jié)果的準確率有時會勝過經(jīng)過微調(diào)后的準確率。
總之,我們可以得到如下幾條關(guān)鍵結(jié)論:
- 要實現(xiàn)高性能目標,微調(diào)可能是必要的:即使少量的微調(diào)數(shù)據(jù)也比零樣本和小樣本方法產(chǎn)生巨大的改進。
- 更多的數(shù)據(jù)通常會帶來更好的結(jié)果:隨著微調(diào)數(shù)據(jù)集大小的增加,調(diào)優(yōu)后的模型的準確脫敏個人身份信息的能力也會提高,如上圖中BLEU分數(shù)的上升所表明的。
- 收益遞減:雖然更多的數(shù)據(jù)通常會產(chǎn)生更好的數(shù)據(jù)結(jié)果,但也可能會出現(xiàn)性能增長開始趨于平穩(wěn)的時刻。確定這一點可以幫助我們更好地權(quán)衡標簽預算和調(diào)整模型質(zhì)量之間的權(quán)衡。
還需要說明的是,在我們的例子中,穩(wěn)定期從3200個樣本開始,任何高于這個水平的樣本都會產(chǎn)生正的但遞減的回報。
譯者介紹
朱先忠,51CTO社區(qū)編輯,51CTO專家博客、講師,濰坊一所高校計算機教師,自由編程界老兵一枚。
原文標題:How Much Data Do You Need to Fine-Tune Gemini?,作者:Youness Mansar