微信扫码
添加专属顾问
我要投稿
淘宝工程师实战分享:如何用LoRA技术低成本定制大模型,让AI真正懂你的业务。 核心内容: 1. LoRA微调技术的原理与优势解析 2. 本地实现与百炼平台两种实战方案对比 3. 小样本场景下的业务适配效果验证
本文主要介绍了大模型时代下,如何通过 LoRA(Low-Rank Adaptation)这一参数高效微调技术,实现对大模型的轻量级定制。文章从微调的基本概念出发,详细阐述了 LoRA 的原理、优势与局限性,并结合本地原生实现(Transformers + PEFT)和百炼平台两种方式,展示了在小样本、低资源场景下的实战流程。结果表明,LoRA 能以极低的计算成本让通用大模型有效学习业务知识,显著提升其在特定任务中的表现,真正实现“让大模型懂业务”,推动 AI 从“可用”走向“好用”。
如今大模型时代,深刻重塑了很多行业的业务形态与技术架构。从智能客服到内容生成,从代码辅助到推荐系统,大模型正以前所未有的速度渗透到互联网技术的各个领域,成为驱动创新的核心引擎。其强大的泛化能力和上下文理解水平,使得许多过去需要复杂工程与规则设计的任务,如今只需一次“提示”即可完成。
然而,尽管大模型展现出惊人的通用能力,对大多数开发者和业务方而言,它仍是一个“黑盒”——我们只能调用其预训练时所掌握的知识与行为模式,难以干预其内部逻辑,也无法直接引导模型适应特定场景的表达习惯、术语体系或业务规则。在生产环境中,这种 “能力固定”的特性带来了明显的局限:模型输出可能偏离业务预期,难以保证一致性,更无法随业务演进而持续进化。
模式 | 说明 | 特点 |
1. 推理时动态叠加(On-thefly) | 加载原始模型 + LoRA 权重,实时计算 W+ΔWW+ΔW | 可用,但慢 |
2. 合并后推理(MergedInference) | 将 LoRA 权重 合并到原始模型中,生成一个新模型,直接推理 | 推理更快、更稳定 |
pip install --upgrade torch==2.1.0+cpu torchvision==0.16.0+cpu --index-url https://download.pytorch.org/whl/cpupip install -U transformers==4.38.2 peft==0.10.0 datasets==2.18.0 \ accelerate sentencepiece safetensors tqdm
2. 模型准备
这里考虑到微调是在 mac
本地运行,算力有限,所以使用了较小的 DeepSeek-R1-Distill-Qwen-1.5B作为基座模型。使用 Hugging Face或 Modelscope等下载模型到本地即可。{"instrucAon":"埋点时主要记录哪些事件?","response":"使⽤的是xx埋点框架,埋点有分为各种事件类型,主要的有曝光事件和点击事件,可以根据曝光和点击事件来统计数据,如点击率等"}{"instrucAon":"xxxxxxxxxxxxxxxxxx?","response":"xxxxxxxxxxxxxxx"}
import json
from datasets import Dataset
from transformers import AutoTokenizer
# 正确读取jsonl
with open("lora_seckill_qa.jsonl", "r", encoding="utf-8") as f:
raw_data = [json.loads(line) for line in f if line.strip()]
# 如果是单轮格式可直接用
# dataset = Dataset.from_list(raw_data)
# 若是conversation 格式(如 [{"conversation":[...]}]),需展开
def conversation_to_list(item):
out = []
for turn in item["conversation"]:
instr = turn.get("system", "") + "\n" + turn["input"] if
turn.get("system") else turn["input"]
out.append({
"instruction": instr.strip(),
"response": turn["output"].strip()
})
return out
# 如果你的raw_data 已经是单轮格式就跳过这一段
all_samples = []
if "conversation" in raw_data[0]:
for d in raw_data:
all_samples.extend(conversation_to_list(d))
dataset = Dataset.from_list(all_samples)
else:
dataset = Dataset.from_list(raw_data)
# 保存分词器
tokenizer = AutoTokenizer.from_pretrained(
"/Users/shawn/Documents/AI-dev/models/deepseek/deepseek-ai/DeepSeek-R1-
Distill-Qwen-7B",
trust_remote_code=True,
local_files_only=True
)
tokenizer.save_pretrained("./tokenizer")
# 保存处理后的数据集
dataset.save_to_disk("./processed_dataset_ms")
print("预处理完成,已保存!")
import torch
from datasets import load_from_disk
from transformers import AutoTokenizer, AutoModelForCausalLM,
TrainingArguments, Trainer, default_data_collator
from peft import get_peft_model, LoraConfig
import random
# 1. 加载数据集
dataset = load_from_disk("./processed_dataset_ms")
# 2. 样本随机打乱
dataset = dataset.shuffle(seed=42)
# 3. 分词器
tokenizer = AutoTokenizer.from_pretrained("./tokenizer", local_files_only=True)
def generate_and_tokenize_prompt(batch):
texts = [
f"""<s>### Instruction:
{instruction}
### Response:
{response}
</s>"""
for instruction, response in zip(batch["instruction"],
batch["response"])
]
out = tokenizer(
texts,
max_length=256,
padding="max_length",
truncation=True,
add_special_tokens=False,
return_tensors=None,
)
# Loss 忽略paddingp.
out["labels"] = [
[tok if tok != tokenizer.pad_token_id else -100 for tok in label]
for label in out["input_ids"]
]
return out
tokenized_dataset = dataset.map(
generate_and_tokenize_prompt,
batched=True,
remove_columns=dataset.column_names,
desc="Tokenizing"
)
# 4. 加载基座模型(如设备有限可指定CPU/其他device)
model = AutoModelForCausalLM.from_pretrained(
"/Users/shawn/Documents/AI-dev/models/deepseek/deepseek-ai/DeepSeek-R1-
Distill-Qwen-7B",
torch_dtype=torch.bfloat16, # MPS、A100 等建议用bfloat16
local_files_only=True,
trust_remote_code=True
)
# 5. LoRA 配置
lora_config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05, # 或0, 稳定死记记忆;推荐小数据降dropout
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 6. 训练参数
training_args = TrainingArguments(
output_dir="./results_ms",
per_device_train_batch_size=2,
gradient_accumulation_steps=1,
learning_rate=3e-4,
num_train_epochs=8,
logging_dir="./logs_ms",
save_steps=100,
save_total_limit=3,
logging_steps=10,
overwrite_output_dir=True,
report_to=None,
fp16=False
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
data_collator=default_data_collator,
)
# 7. 训练与自动采样输出
trainer.train()
# 8. 保存模型与tokenizer
model.save_pretrained("./deepseek-7b-lora_ms")
tokenizer.save_pretrained("./deepseek-7b-lora_ms")
print("训练完成。")
场景 | 建议配置 |
小数据集微调 | r=8 , alpha=16 , dropout=0.1 , epochs=5~10 |
大数据集微调 | r=16~32 , alpha=32 , dropout=0.05 , lr=2e-4 |
显存受限 | 减小 batch_size,启用 fp16,使用 gradient_accumulation |
快速实验 | overwrite_output_dir=True , logging_steps=10 , save_steps=50 |
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
# 路径设置
BASE_PATH = "/Users/shawn/Documents/AI-dev/models/deepseek/deepseekai/
DeepSeek-R1-Distill-Qwen-7B"
LORA_PATH = "./deepseek-7b-lora_ms"
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained(
BASE_PATH,
local_files_only=True,
trust_remote_code=True
)
# === 严格分离模型实例 ===
# 原始模型
model_base = AutoModelForCausalLM.from_pretrained(
BASE_PATH,
device_map="mps",
torch_dtype=torch.bfloat16,
local_files_only=True,
trust_remote_code=True
)
# 微调后的模型(Base + LoRA 适配器)
model_base_for_lora = AutoModelForCausalLM.from_pretrained(
BASE_PATH,
device_map="mps",
torch_dtype=torch.bfloat16,
local_files_only=True,
trust_remote_code=True
)
model_lora = PeftModel.from_pretrained(
model_base_for_lora,
LORA_PATH
)
def format_prompt_one_round(user_input: str) -> str:
return f"<s>### Instruction:\n{user_input}\n### Response:\n"
def generate_single(model, prompt, tokenizer, max_new_tokens=200):
inputs = tokenizer(prompt, return_tensors="pt")
for k, v in inputs.items():
inputs[k] = v.to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=0.7,
top_p=0.9,
do_sample=True,
eos_token_id=tokenizer.eos_token_id
)
full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
reply = full_output[len(prompt):]
# 截断下一个分隔符,保证只输出新生成内容
for sep in ["<s>", "</s>", "###"]:
if sep in reply:
reply = reply.split(sep)[0]
return reply.strip()
def main():
print("="*40)
print("DeepSeek 模型微调前/后 单轮对话对比(严格模型物理分离版)")
print("="*40)
print("输入exit 退出。\n")
while True:
user_input = input("你说:").strip()
if user_input.lower() in {"exit", "quit"}:
print("对话结束~ 再见!")
break
prompt = format_prompt_one_round(user_input)
# 原始模型推理
base_reply = generate_single(model_base, prompt, tokenizer)
# LoRA 微调后模型推理
lora_reply = generate_single(model_lora, prompt, tokenizer)
print("\n-------------------------------")
print("【原模型 输出】↓\n" + base_reply)
print("\n【微调后输出】↓\n" + lora_reply)
print("-------------------------------")
if __name__ == "__main__":
main()
{“messages": [{"role": "system", "content": "You are a professional e-commercetitle analyst. Given a long product title, output ONLY the core product entityname (1-5 words) without any other text."}, {"role": "user", "content": "请从下列商品标题中提取最核心的商品主体,直接输出主体名,不要加其它词:【黑旗】心语肉松原味辣味1kg 烘焙面包蛋糕寿司原料商用肉松小贝"}, {"role": "assistant", "content": "肉松"}]}{"messages": [{"role": "system", "content": "You are a professional e-commercetitle analyst. Given a long product title, output ONLY the core product entityname (1-5 words) without any other text."}, {"role": "user", "content": "请从下列商品标题中提取最核心的商品主体,直接输出主体名,不要加其它词:夏季竹枕片成人藤凉席冰丝枕头套单人儿童竹枕席藤枕芯套一对拍2"}, {"role": "assistant", "content": "枕套"}]}
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2025-07-03
2025-07-28
2025-07-22
2025-07-09
2025-07-09
2025-07-31
2025-07-30
2025-07-20
2025-08-07
2025-07-16