让微调的Llama3.2:1b成为你的专属GPT
微调是否比提示词工程更具性价比!
前序
谁能更好地治疗婴儿的咳嗽——年轻的儿科医生还是著名的内科医生?
虽然这两者都是医生,都可以治疗孩子的咳嗽,但儿科医生是专门针对儿童健康的专家,能更好地诊断婴儿的病情,对吧?
这正是微调对小模型所起的作用。微调可以让那些较小、较弱的模型在解决特定问题时比那些声称可以做任何事情的大模型做得更好。
最近我也遇到了一个需要做出类似选择的情况。
我在构建一个查询路由机器人,该机器人负责将用户的查询引导至正确的部门,然后由人工客服继续对话。本质上,这是一个简单的文本分类任务。
GPT-4o(以及它的小版本)在这方面表现非常出色,但它既昂贵又缺乏灵活性。它是一个封闭模型,无法在你自己的基础设施上进行微调。虽然OpenAI提供了在其平台上微调的功能,但费用对我来说太高了。
GPT-4o的训练成本为每百万个token 25美元。而我的训练数据很快就达到了几百万个token。此外,使用微调后的模型还要比常规模型贵50%左右。
以这样的成本,我的小应用程序无法上线。我正处于预算紧张的时期!
我能做的备选方案就是训练一个开源模型。开源模型在分类任务中的表现非常好,但训练需要GPU,并且时间较长。
于是,我决定赌一把小模型。
小型语言模型(LLMs)是微调的唯一途径,能在较低的成本下取得不错的结果——这正是我所需要的。
小模型可以在廉价的硬件上运行,并且使用相对便宜的GPU进行微调。而且,它们的训练和推理时间比大模型快得多。
有几个候选模型——Phi3.5、DistillBERT和GPT-Neo,但我决定尝试Meta Llama 3.2的1B模型。这是一个随机选择,也许我受到最近围绕该模型的炒作的影响。
你需要付出多少?如果你打算自建GPT?
你需要更多GPU还是更现代的GPU?该如何做出基础设施决策?
在本文中,我将比较微调后的Llama 3.2–1B instruct模型与使用少样本提示的GPT-4o的表现。
以下是我的训练过程和结果。
微调Llama 3.2 1B(免费)
是的,我在训练上没有花一分钱。
微调可能会很昂贵,除非你选择了正确的策略。你可以选择重新训练所有参数、执行迁移学习,或者进行参数高效的微调。
第一个策略,即全参数训练,可能是最昂贵也最危险的。顾名思义,这种方法会重新训练模型中的所有10亿个参数。这将耗费大量时间和预算。此外,全参数微调还存在一个问题,叫做“灾难性遗忘”。这意味着当你微调模型时,它可能会丢失一些在预训练阶段学到的知识。
第二个策略,迁移学习,是一个不错的选择,但有些复杂。
最后一种方法,参数优化微调(PEFT),既便宜又高效。它的核心理念是只训练一部分参数。
具体来说,Low-Rank Adaptation(LORA)是所有微调策略中的最佳选择之一。在LORA中,我们只对少数选择的层中的部分参数进行微调,从而实现快速高效的训练。
另一个可以显著减少训练基础设施需求的关键决策是量化。简单来说,我们可以将模型参数存储为float16或更小的数据类型。更小的数据类型在内存中占用更少的空间,计算速度更快,但可能会以牺牲一些精度为代价。
现在,我们已经决定使用LORA作为微调策略。那么我们该去哪里找到免费的训练基础设施呢?
可以选择Colab或Kaggle Notebooks。
这些平台提供免费的GPU,通常足以微调这个小模型。
微调的Llama-3.2与OpenAI GPT-4o的多提示词对比
LORA微调非常流行,我不想再赘述另一个教程。
可以参考Unsloth提供的Colab笔记本,里面有非常详细的步骤指南。事实上,我也一直在使用这个笔记本进行我的微调任务。
让我告诉你需要做哪些更改。
首先,笔记本默认微调的是Llama-3.2的3B参数版本。如果你想测试更大模型,可以保持不变,或者你可以选择其他可用的模型。我选择了Llama-3.2–1B-Instruct,因为我想测试这个最小的模型是否足够应对我的任务。
然后,有一个单元格会将数据集转换为所需格式。我将其修改为使用我的微调数据集。
#之前
from unsloth.chat_templates import standardize_sharegpt
dataset = standardize_sharegpt(dataset)
dataset = dataset.map(formatting_prompts_func, batched=True)
#之后
from datasets import Dataset
dataset = Dataset.from_json("/content/insurance_training_data.json")
dataset = dataset.map(formatting_prompts_func, batched=True)最简单的方法是使用与笔记本初始使用情况相符的数据集,例如以下格式:
code{
"conversations": [
{"role": "user", "content": "<user_query>"},
{"role": "assistant", "content": "<department>"}
]
}
通过以上两处更改,便可以基于用户数据微调模型。
评估微调后的模型
评估是非常重要的一部分,通过以下代码进行经典的混淆矩阵评估:
1. 使用微调模型生成响应
def generate_response(user_query):
FastLanguageModel.for_inference(model)
messages = [
{"role": "system", "content": "You are a helpful assistant who can route the following query to the relevant department."},
{"role": "user", "content": user_query}
]
tokenized_input = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to("cuda")
generated_output = model.generate(input_ids=tokenized_input, max_new_tokens=64, use_cache=True, temperature=1.5, min_p=0.1)
decoded_response = tokenizer.batch_decode(generated_output, skip_special_tokens=True)[0]
assistant_response = decoded_response.split("\n\n")[-1]
return assistant_response
2. 生成OpenAI GPT-4o的响应
example_prompt_template = PromptTemplate.from_template("User Query: {user_query}\n{department}")
llm = ChatOpenAI(temperature=0, model="gpt-4o")
# 定义示例
examples = [
{"user_query": "I recently had an accident and need to file a claim for my vehicle. Can you guide me through the process?", "department": "Claims"}
# 省略更多示例
]
few_shot_prompt_template = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt_template,
prefix="You are an intelligent assistant for an insurance company. Your task is to route customer queries to the appropriate department.",
suffix="User Query: {user_query}",
input_variables=["user_query"]
)
class Department(BaseModel):
department: str
def predict_department(user_query):
structured_llm = llm.with_structured_output(Department)
prediction_chain = few_shot_prompt_template | structured_llm
result = prediction_chain.invoke(user_query)
return result.department
3. 读取评估数据并预测
import json
with open("/content/insurance_bot_evaluation_data.json", "r") as f:
eval_data = json.load(f)
for ix, item in enumerate(eval_data):
print(f"{ix+1} of {len(eval_data)}")
item['open_ai_response'] = generate_response(item['user_query'])
item['llama_response'] = item['open_ai_response']
4. 计算精确度、召回率、准确率和F1得分
使用 scikit-learn 来计算不同模型的表现:
from sklearn.metrics import precision_score, recall_score, accuracy_score, f1_score
# OpenAI
true_labels = [item['department'] for item in eval_data]
predicted_labels_openai = [item['open_ai_response'] for item in eval_data]
precision_openai = precision_score(true_labels, predicted_labels_openai, average='weighted')
recall_openai = recall_score(true_labels, predicted_labels_openai, average='weighted')
accuracy_openai = accuracy_score(true_labels, predicted_labels_openai)
f1_openai = f1_score(true_labels, predicted_labels_openai, average='weighted')
print("OpenAI Response Scores:")
print("Precision:", precision_openai)
print("Recall:", recall_openai)
print("Accuracy:", accuracy_openai)
print("F1 Score:", f1_openai)
# Llama 3.2 1B
predicted_labels_llama = [item['llama_response'] for item in eval_data]
precision_llama = precision_score(true_labels, predicted_labels_llama, average='weighted', zero_division=0)
recall_llama = recall_score(true_labels, predicted_labels_llama, average='weighted', zero_division=0)
accuracy_llama = accuracy_score(true_labels, predicted_labels_llama)
f1_llama = f1_score(true_labels, predicted_labels_llama, average='weighted', zero_division=0)
print("Llama Response Scores:")
print("Precision:", precision_llama)
print("Recall:", recall_llama)
print("Accuracy:", accuracy_llama)
print("F1 Score:", f1_llama)
通过这些步骤,你可以评估微调后的模型与OpenAI GPT-4o的性能对比。
然后,我们使用这些函数来生成评估数据集的响应。
评估数据集有预期的类别,现在我们有了生成的类别,这为计算指标提供了依据。我们在接下来的部分中进行这些计算。
以下是结果:
OpenAI 响应得分:
- 精确度(Precision):0.9
- 召回率(Recall):0.75
- 准确率(Accuracy):0.75
- F1 得分:0.818
Llama 响应得分:
- 精确度(Precision):0.88
- 召回率(Recall):0.73
- 准确率(Accuracy):0.79
- F1 得分:0.798
结果显示,微调后的模型结果几乎接近于 GPT-4o 的表现。这对一个只有 10 亿参数的小模型来说相当令人印象深刻。
当然,GPT-4o 的表现略好一些,但差距并不大。
另外,如果在 few-shot 提示中提供更多的例子,GPT-4o 可能会产生更好的结果。不过,我的示例有时是整段话,考虑到 OpenAI 对输入 tokens 收费,这会大大增加成本。
总结
我已经成为小型语言模型的粉丝。它们速度快、成本低,并且在大多数用例中已经足够了——如果它们不需要微调的话。
在本文中,我讨论了如何微调 Llama 3.2 1B,这是一款可以在相对便宜的硬件上运行并且免费进行微调的模型。我的任务是文本分类。
但这并不是说小型模型会始终超越像 GPT-4o 这样的大型模型——或者甚至 Meta Llama 自己的 8B、11B 和 90B 参数模型。更大的模型具有多语言理解、视觉指令和丰富的世界知识等超能力。
我的观点是,如果你不需要这些超能力,为什么不考虑使用一个小型的语言模型呢?