掘金 人工智能 07月06日
在Mac上10分钟玩转LoRA微调模型
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了如何在Mac上使用LoRA技术微调Flan-T5小模型,实现无需显卡和云端资源即可训练AI模型。文章详细阐述了LoRA的优势,包括轻量、可插拔和免风险。通过环境准备、数据准备、脚本编写、模型训练和推理测试等步骤,带领读者一步步完成LoRA微调的实战过程。虽然由于资源限制,微调效果有限,但文章提供了宝贵的经验,并探讨了未来改进方向,为读者提供了在有限资源下进行AI模型训练的实践指南。

💡 LoRA技术通过仅训练少量新增参数,实现轻量级微调,速度快、显存占用低,并且可以灵活切换不同的LoRA适配器,实现一模多用,同时冻结基座模型,避免灾难性遗忘。

💻 训练环境搭建:文章详细介绍了在Mac上使用pyenv安装Python,创建虚拟环境,安装PyTorch (MPS)以及transformers、datasets、peft、accelerate等核心库,为后续的微调工作做好准备。

💾 数据准备与脚本编写:文章指导用户准备JSONL格式的训练数据,并提供了一个名为finetune_lora.py的脚本示例,该脚本使用Hugging Face的库,包括AutoModelForSeq2SeqLM、AutoTokenizer、TrainingArguments等,实现了从数据加载、文本预处理、模型加载、LoRA注入、设备选择、训练参数设置、模型训练到LoRA适配器保存的完整流程。

🚀 实战效果与问题:文章展示了微调后的模型在回答问题时的输出结果,并指出了由于资源限制,微调效果并不完美。同时,文章强调了数据集大小、显卡性能以及基座模型选择对微调效果的影响,并建议有条件的用户尝试更大的模型。

🔑 总结与思考:文章总结了LoRA、M系Mac和HuggingFace + PEFT的优势,并提出了一个开放性问题:将专业知识微调到基座模型中效果好,还是通过RAG技术+专业知识库+通用大模型效果好?引发读者思考。

原文

LoRA微调Flan T5小模型

“不用显卡、不用上云,只靠一台 Mac 就能训练属于自己的 AI 模型。”

1. 为什么选择 LoRA?

2. 环境准备

步骤命令
安装pyenvbrew install pyenv
安装python版本pyenv install 3.11.9
创建虚拟环境python3.9 -m venv .venv
进入虚拟环境source .venv/bin/activat
安装 PyTorch (MPS)pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
安装核心库pip install transformers datasets peft accelerate

3. 准备数据 (举例:本地JSONL)

4. 创建脚本finetune_lora.py

import torchfrom transformers import (    AutoModelForSeq2SeqLM,   # —— Seq2Seq 架构模型(这里选用 google/flan-t5-small)    AutoTokenizer,           # —— 与模型配套的分词器    TrainingArguments,       # —— Trainer 的超参数容器    Trainer,                 # —— HuggingFace 训练循环封装器    DataCollatorForSeq2Seq   # —— 动态批量 Padding + Label 处理器)from datasets import load_dataset      # —— 轻松读取/流式加载各类数据集from peft import (                     # —— PEFT = Parameter-Efficient Fine-Tuning    LoraConfig, TaskType, get_peft_model, PeftModel)# ---------- Step 1: 读取 JSONL 数据 ----------# data.jsonl 每行形如 {"instruction": "...", "output": "..."}dataset       = load_dataset("json", data_files="data.jsonl")train_dataset = dataset["train"]# ---------- Step 2: 文本预处理 ----------model_name"google/flan-t5-small"tokenizer  = AutoTokenizer.from_pretrained(model_name)def preprocess(example):    # 将 “指令” 编码为 input_ids    model_input = tokenizer(        example["instruction"],        max_length=512,        truncation=True,        padding="max_length"    )    # 将 “答案” 编码为 labels(Teacher forcing)    labels = tokenizer(        example["output"],        max_length=128,        truncation=True,        padding="max_length"    )    model_input["labels"] = labels["input_ids"]    return model_inputtrain_dataset = train_dataset.map(preprocess)# ---------- Step 3: 加载基座模型 + 注入 LoRA ----------base_model = AutoModelForSeq2SeqLM.from_pretrained(model_name)lora_config = LoraConfig(    r=8,                     # 低秩矩阵秩    lora_alpha=16,           # 缩放因子    lora_dropout=0.1,        # dropout    bias="none",    task_type=TaskType.SEQ_2_SEQ_LM)model = get_peft_model(base_model, lora_config)  # 返回仅新增数万参数的可训练模型# ---------- Step 4: 设备 ----------device"mps"if torch.backends.mps.is_available() else"cpu"model.to(device)# ---------- Step 5: 训练参数 ----------training_args = TrainingArguments(    output_dir="./lora_finetune_output",    per_device_train_batch_size=2,     # 真实 batch size = 2 × gradient_accumulation    gradient_accumulation_steps=4,    num_train_epochs=5,    learning_rate=1e-4,    logging_steps=1,    save_strategy="no",    report_to="none",    fp16=False                         # Apple Silicon 上使用 float32 更稳定)data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)# ---------- Step 6: 开始训练 ----------trainer = Trainer(    model=model,    args=training_args,    train_dataset=train_dataset,    data_collator=data_collator,    tokenizer=tokenizer)trainer.train()# ---------- Step 7: 保存 LoRA 适配器 ----------model.save_pretrained("lora_adapter")# ---------- Step 8: 推理测试 ----------print("🎯 推理测试:")base        = AutoModelForSeq2SeqLM.from_pretrained(model_name).to(device)lora_model  = PeftModel.from_pretrained(base, "lora_adapter").to(device)def infer(prompt: str) -> str:    """单条推理:传入指令,返回模型生成的文本"""    inputs = tokenizer(prompt, return_tensors="pt").to(device)    outputs = lora_model.generate(**inputs, max_new_tokens=1)    return tokenizer.decode(outputs[0], skip_special_tokens=True)test_prompts = [    "what is the capital of China?",    "what is the capital of France?",    "what is the airplane?"]for p in test_prompts:    print(f"🧠 Prompt: {p}")    print(f"📝 Answer: {infer(p)}")    print("-" * 40)
    训练日志打印
(.venv)   aigc python finetune_lora.pyGenerating train split: 1000 examples [00:00, 452655.30 examples/s]Map: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:00<00:00, 9258.42 examples/s]{'loss': 41.4961'grad_norm': 3.4582340717315674'learning_rate': 0.0001'epoch': 0.01}{'loss': 45.12'grad_norm': 4.7143425941467285'learning_rate': 9.984e-05'epoch': 0.02}{'loss': 43.707'grad_norm': 3.819638252258301'learning_rate': 9.968000000000001e-05'epoch': 0.02}{'loss': 40.973'grad_norm': 3.7596452236175537'learning_rate': 9.952e-05'epoch': 0.03}{'loss': 45.6711'grad_norm': 4.67157506942749'learning_rate': 9.936000000000001e-05'epoch': 0.04}{'loss': 42.3758'grad_norm': 4.384180068969727'learning_rate': 9.92e-05'epoch': 0.05}{'loss': 36.1399'grad_norm': 3.7017316818237305'learning_rate': 9.904e-05'epoch': 0.06}{'loss': 44.6688'grad_norm': 4.85175085067749'learning_rate': 9.888e-05'epoch': 0.06}{'loss': 44.2394'grad_norm': 4.683821201324463'learning_rate': 9.872e-05'epoch': 0.07}{'loss': 41.7887'grad_norm': 4.8903913497924805'learning_rate': 9.856e-05'epoch': 0.08}{'loss': 44.2073'grad_norm': 4.807693004608154'learning_rate': 9.84e-05'epoch': 0.09}{'loss': 42.7997'grad_norm': 4.235534191131592'learning_rate': 9.824000000000001e-05'epoch': 0.1}{'loss': 39.6141'grad_norm': 4.627796173095703'learning_rate': 9.808000000000001e-05'epoch': 0.1}{'loss': 41.5859'grad_norm': 4.959679126739502'learning_rate': 9.792e-05'epoch': 0.11}{'loss': 44.5831'grad_norm': 5.254530429840088'learning_rate': 9.776000000000001e-05'epoch': 0.12}{'loss': 43.8274'grad_norm': 5.325198173522949'learning_rate': 9.76e-05'epoch': 0.13}{'loss': 41.8024'grad_norm': 5.138743877410889'learning_rate': 9.744000000000002e-05'epoch': 0.14}{'loss': 41.099'grad_norm': 5.499021530151367'learning_rate': 9.728e-05'epoch': 0.14}{'loss': 41.2216'grad_norm': 11.285901069641113'learning_rate': 9.712e-05'epoch': 0.15}{'loss': 37.4023'grad_norm': 4.781173229217529'learning_rate': 9.696000000000001e-05'epoch': 0.16}{'loss': 41.2696'grad_norm': 5.610023021697998'learning_rate': 9.680000000000001e-05'epoch': 0.17}{'loss': 37.7613'grad_norm': 3.9915575981140137'learning_rate': 9.664000000000001e-05'epoch': 0.18}....

可以看到损失值每轮递减,在M3 MacBook上,5 epoch≈3分钟,所以非常快就能看到结果。

6. 实战效果

这是我的问题:test_prompts = [    "what is the capital of China?",    "what is the capital of France?",    "what is the airplane?"]

loRa微调之后的模型输出如下:

那么从结果来看回答还可以,但是你如果仔细看就会发现,答案是错的,很明显中国的首都是北京不是上海,法国的首都是巴黎不是悉尼。

因为我们只是拿google的t5基座小模型做微调演示,受各方面因素影响,比如数据集少,显卡不够,基座模型也一般等,所以微调出来的模型质量并不是很好。如果大家有更多的显卡,内存,那么建议尝试一下更大的模型,比如7B或者7B以上的,这样微调出来的模型应该效果不错。

7. 小结

最后我给大家抛一个问题:将专业知识微调到基座模型中效果好,还是通过RAG技术+专业知识库+通用大模型效果好?

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

LoRA Flan-T5 微调 Mac AI模型
相关文章