微软最近发布的Phi-2模型虽然只有27亿参数,但其性能表现却令人惊艳。作为一名长期从事模型微调的实践者,我想分享一个完整的项目案例:如何将一个简单的谜语数据集转化为多轮对话数据,并使用QLoRA技术对Phi-2进行高效微调。
这个项目特别适合那些想要:
整个过程分为两个主要阶段:数据集构建和模型微调。我们先从数据集开始。
我们使用的原始数据集是Hypersniper/riddles_v1,包含约470个谜语及其解答。每个样本结构如下:
json复制{
"instruction": "What is pronounced like one letter...",
"output": "The answer to this question is \"eye\"...",
"answer": "eye"
}
这个数据集有两个主要限制:
提示:在实际项目中,原始数据质量直接影响最终模型效果。建议至少准备1000+样本,且覆盖目标场景的多样性。
我们采用Mistral-7B-Instruct模型来生成更多谜语。核心思路是:
关键代码实现:
python复制prompt_template = """Below are 10 riddles. Come up with 10 more...
Riddles:{questions}"""
for _ in range(300):
random.shuffle(questions)
q10_sample = questions[0:10]
prompt = prompt_template.format(questions="\n".join(q10_sample))
messages = [{"role":"user","content": prompt}]
input_tokens = tokenizer.apply_chat_template(messages, return_tensors="pt").to("cuda")
output_tokens = model.generate(input_tokens, max_new_tokens=500)
# 后续处理...
实际生成中会遇到两个主要问题:
经过清洗后,我们最终获得1682个高质量的新谜语。
单轮问答不足以训练出好的对话模型。我们设计了一个四步流程:
python复制prompt_template = """{riddle}
Think step-by-step, keep your explanations simple..."""
python复制prompt_template = """Please continue the conversation below...
Imitate a curious 10 year old kid..."""
生成追问回答:再次使用Mistral回答这些追问
格式统一:将所有对话转为ChatML格式
code复制<|im_start|>user
谜语问题<|im_end|>
<|im_start|>assistant
谜语解答<|im_end|>
经验分享:让AI模拟儿童提问是个实用技巧,能生成更自然、简单的问题,避免过于复杂的追问破坏对话流畅性。
我们使用4-bit量化的Phi-2模型:
python复制model = AutoModelForCausalLM.from_pretrained(
"microsoft/phi-2",
device_map="auto",
quantization_config=BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_quant_type="nf4"
),
torch_dtype=torch.bfloat16
)
特别注意:
我们采用以下LoRA配置:
python复制lora_config = LoraConfig(
r=32,
lora_alpha=32,
target_modules=["q_proj","k_proj","v_proj","dense"],
modules_to_save=["lm_head","embed_tokens"],
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM"
)
参数选择考量:
r=32:在模型适配能力和计算开销间取得平衡target_modules:选择注意力机制相关层,覆盖约9.2%的参数lora_alpha=32:与rank值保持一致,这是常见做法由于数据集较小(1682个对话),我们采用以下策略:
训练配置示例:
python复制args = TrainingArguments(
output_dir="out",
per_device_train_batch_size=1,
gradient_accumulation_steps=16,
num_train_epochs=20,
learning_rate=2e-5,
lr_scheduler_type="constant",
bf16=True,
# 其他参数...
)
避坑指南:Phi-2对学习率很敏感。测试发现大于4e-5容易导致训练不稳定,而小于1e-5则收敛太慢。
虽然设置了20个epoch,但由于数据集小,实际训练时间约2.5小时(单卡3090)。观察发现:
微调后的Phi-2能够进行连贯的两轮对话:
code复制用户:什么东西越洗越脏?
助手:答案是水。当你用水清洗其他物品时,水本身会变得越来越脏...
用户:那为什么我们还要用水洗手呢?
助手:虽然水会变脏,但它能带走我们手上的污垢和细菌...
数据层面:
模型层面:
训练策略:
这个项目最让我惊讶的是,即使只有1682个对话样本,Phi-2也能展现出不错的对话能力。这证明了小模型在特定任务上的潜力,以及QLoRA技术的高效性。在实际应用中,建议先从小数据集开始验证思路,再逐步扩展数据规模和质量。