LoRA微调教程

LoRA微调手把手教程

LoRA:高效微调的利器 LoRA(Low-Rank Adaptation)通过在原模型权重旁添加低秩矩阵,只需训练极少量参数即可实现有效的微调。一个7B模型的LoRA微调只需8GB显存,而全量微调需要56GB。 环境准备 pip install peft transformers accelerate datasets bitsandbytes 完整微调代码 import torch from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments from peft import LoraConfig, get_peft_model, TaskType from datasets import Dataset # 1. 加载模型和分词器 model_name = "Qwen/Qwen3-7B" tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16, device_map="auto", trust_remote_code=True, ) # 2. LoRA配置 lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, r=64, # LoRA秩,越大容量越大但训练越慢 lora_alpha=128, # 缩放因子,通常为r的2倍 lora_dropout=0.05, # Dropout防止过拟合 target_modules=[ # 应用LoRA的模块 "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj" ], bias="none", ) # 3. 应用LoRA model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 输出:trainable params: 39,321,600 || all params: 7,078,299,648 || trainable%: 0.556% # 4. 数据准备 def format_dataset(data): formatted = [] for item in data: text = f"<|im_start|>user\n{item['input']}<|im_end|>\n<|im_start|>assistant\n{item['output']}<|im_end|>" formatted.append({"text": text}) return formatted train_data = format_dataset(raw_train_data) val_data = format_dataset(raw_val_data) train_dataset = Dataset.from_list(train_data) val_dataset = Dataset.from_list(val_data) def tokenize_fn(examples): result = tokenizer( examples["text"], truncation=True, max_length=2048, padding=False, ) result["labels"] = result["input_ids"].copy() return result train_dataset = train_dataset.map(tokenize_fn, batched=True, remove_columns=["text"]) val_dataset = val_dataset.map(tokenize_fn, batched=True, remove_columns=["text"]) # 5. 训练参数 training_args = TrainingArguments( output_dir="./lora-output", num_train_epochs=3, per_device_train_batch_size=4, per_device_eval_batch_size=4, gradient_accumulation_steps=4, warmup_ratio=0.1, learning_rate=2e-4, lr_scheduler_type="cosine", logging_steps=10, eval_strategy="steps", eval_steps=100, save_strategy="steps", save_steps=100, save_total_limit=3, load_best_model_at_end=True, bf16=True, gradient_checkpointing=True, report_to="tensorboard", ) # 6. 训练 from transformers import Trainer trainer = Trainer( model=model, args=training_args, train_dataset=train_dataset, eval_dataset=val_dataset, data_collator=lambda features: { "input_ids": torch.nn.utils.rnn.pad_sequence( [torch.tensor(f["input_ids"]) for f in features], batch_first=True, padding_value=tokenizer.pad_token_id ), "labels": torch.nn.utils.rnn.pad_sequence( [torch.tensor(f["labels"]) for f in features], batch_first=True, padding_value=-100 ), "attention_mask": torch.nn.utils.rnn.pad_sequence( [torch.tensor([1] * len(f["input_ids"])) for f in features], batch_first=True, padding_value=0 ), }, ) trainer.train() # 7. 保存LoRA权重 model.save_pretrained("./lora-weights") tokenizer.save_pretrained("./lora-weights") 合并与部署 # 合并LoRA权重到基础模型 from peft import PeftModel base_model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16, device_map="auto" ) model = PeftModel.from_pretrained(base_model, "./lora-weights") merged_model = model.merge_and_unload() # 合并权重 # 保存合并后的完整模型 merged_model.save_pretrained("./merged-model") tokenizer.save_pretrained("./merged-model") # 导出为GGUF格式(用于Ollama部署) # python convert.py ./merged-model --outtype f16 QLoRA(量化LoRA) from transformers import BitsAndBytesConfig # 4-bit量化加载基础模型 bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.float16, bnb_4bit_use_double_quant=True, ) model = AutoModelForCausalLM.from_pretrained( model_name, quantization_config=bnb_config, device_map="auto", ) # 其余LoRA配置和训练流程相同 # QLoRA可以在单张8GB GPU上微调7B模型 超参数调优指南 参数 推荐值 说明 r 16-128 简单任务用小r,复杂任务用大r lora_alpha 2×r 通常为r的2倍 learning_rate 1e-4 ~ 5e-4 LoRA需要比全量微调更大的学习率 epochs 2-5 注意过拟合 batch_size 4-16 配合gradient_accumulation target_modules 全选 QKVO+FFN效果最好 常见问题 显存不足 使用QLoRA(4-bit量化) 减小batch_size,增加gradient_accumulation 启用gradient_checkpointing 减小max_length 过拟合 减少epochs 增加lora_dropout 增加训练数据 减小r 效果不好 检查数据质量 增大r 确保target_modules覆盖所有线性层 检查学习率是否合适 结语 LoRA是大模型微调的性价比之选——少量参数、少量显存、快速训练。通过合理的配置和高质量数据,LoRA微调可以达到接近全量微调的效果。掌握LoRA是LLM工程化的必备技能。 加入讨论 这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。 ...

2026-07-02 · 2 min · 354 words · 硅基 AGI 探索者
LoRA vs QLoRA 2026对比

LoRA vs QLoRA 2026对比:参数高效微调的两种路径

引言 参数高效微调(PEFT)技术让普通开发者也能微调大模型。其中LoRA和QLoRA是最流行的两种方案。2026年,随着模型规模进一步扩大,这两种技术的差异和选择变得更加重要。 一、LoRA原理 LoRA(Low-Rank Adaptation)通过低秩矩阵分解来近似模型权重的更新: 原始: Y = W·X, W ∈ R^(d×k) LoRA: Y = (W + ΔW)·X, ΔW = A·B, A ∈ R^(d×r), B ∈ R^(r×k), r << d 只训练A和B两个小矩阵,原参数W冻结。 from peft import LoraConfig, get_peft_model config = LoraConfig( r=8, # 秩 lora_alpha=16, target_modules=["q_proj", "v_proj", "k_proj", "o_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" ) model = get_peft_model(base_model, config) 优势 训练参数减少99%+ 不增加推理延迟(训练后可合并权重) 效果接近全参数微调 劣势 仍需要模型以全精度加载到GPU 大模型仍需要大量GPU内存 二、QLoRA原理 QLoRA在LoRA基础上增加了4位量化: 基础模型: 4-bit量化存储 → 大幅减少内存 LoRA适配器: 全精度训练 → 保持训练精度 梯度: 通过4-bit基础模型反传 → 计算LoRA梯度 from transformers import BitsAndBytesConfig import torch bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16 ) model = AutoModelForCausalLM.from_pretrained( "meta-llama/Llama-3-70B", quantization_config=bnb_config, device_map="auto" ) # 然后应用LoRA model = get_peft_model(model, lora_config) 优势 70B模型只需18GB GPU内存(LoRA需要140GB) 普通消费级GPU就能微调大模型 效果接近LoRA 劣势 训练速度稍慢(量化/反量化开销) 推理时如果保持量化,精度略有下降 三、2026年对比 3.1 内存对比 模型大小 全参微调 LoRA QLoRA 7B ~56GB ~28GB ~6GB 13B ~104GB ~52GB ~10GB 70B ~560GB ~280GB ~18GB 175B ~1400GB ~700GB ~40GB 3.2 性能对比 指标 LoRA QLoRA 差距 训练速度 1.0x 0.85x QLoRA慢15% 最终精度 100% 98-99% QLoRA略低 推理速度 1.0x 1.0x* 合并后相同 训练稳定性 高 中高 QLoRA偶有梯度异常 *合并权重后推理速度相同;如果不合并,QLoRA推理更慢 ...

2026-07-02 · 2 min · 289 words · 硅基 AGI 探索者
lora finetuning 2026 data to deployment

LoRA 微调实战 2026:从数据准备到部署的完整流程

为什么选择 LoRA 全参数微调一个 70B 模型需要数百 GB 显存,而 LoRA(Low-Rank Adaptation)通过冻结原始权重、只训练低秩适配矩阵,将可训练参数减少到原来的 0.1%-1%,在消费级 GPU 上即可完成微调。 方法 可训练参数 显存需求 (7B) 显存需求 (70B) 全参数微调 100% 120GB 1200GB LoRA 0.1-1% 16GB 80GB QLoRA 0.1-1% 8GB 40GB 完整流程概览 数据准备 → 格式转换 → 训练配置 → LoRA训练 → 评估 → 合并 → 部署 1. 数据准备 数据格式 # 推荐格式:ShareGPT / OpenAI Messages { "messages": [ {"role": "system", "content": "你是一个专业的技术顾问。"}, {"role": "user", "content": "解释一下 RAG 的工作原理"}, {"role": "assistant", "content": "RAG(检索增强生成)是一种..."} ] } 数据构建脚本 import json from pathlib import Path class SFTDataBuilder: def __init__(self, output_dir: str): self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) def build_from_qa_pairs(self, qa_pairs: list, system_prompt: str): """从问答对构建训练数据""" samples = [] for qa in qa_pairs: sample = { "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": qa["question"]}, {"role": "assistant", "content": qa["answer"]} ] } samples.append(sample) # 划分训练/验证集 split = int(len(samples) * 0.95) train_path = self.output_dir / "train.jsonl" val_path = self.output_dir / "val.jsonl" with open(train_path, 'w', encoding='utf-8') as f: for s in samples[:split]: f.write(json.dumps(s, ensure_ascii=False) + '\n') with open(val_path, 'w', encoding='utf-8') as f: for s in samples[split:]: f.write(json.dumps(s, ensure_ascii=False) + '\n') print(f"训练集: {split} 条 → {train_path}") print(f"验证集: {len(samples) - split} 条 → {val_path}") def build_from_conversations(self, conversations: list): """从多轮对话构建训练数据""" samples = [] for conv in conversations: messages = [] for turn in conv: messages.append({"role": turn["role"], "content": turn["content"]}) samples.append({"messages": messages}) return samples 数据质量检查 class DataQualityChecker: def check(self, data_path: str): issues = [] with open(data_path, 'r', encoding='utf-8') as f: lines = f.readlines() for i, line in enumerate(lines): sample = json.loads(line) # 1. 检查消息格式 if "messages" not in sample: issues.append(f"Line {i}: 缺少 messages 字段") continue # 2. 检查角色顺序 roles = [m["role"] for m in sample["messages"]] if roles[-1] != "assistant": issues.append(f"Line {i}: 最后一条消息不是 assistant") # 3. 检查内容长度 for msg in sample["messages"]: if len(msg["content"]) < 5: issues.append(f"Line {i}: 消息内容过短") if len(msg["content"]) > 8000: issues.append(f"Line {i}: 消息内容过长 ({len(msg['content'])} chars)") # 4. 检查 assistant 回复质量 assistant_msgs = [m for m in sample["messages"] if m["role"] == "assistant"] for msg in assistant_msgs: if msg["content"].startswith("我是一个AI"): issues.append(f"Line {i}: assistant 回复包含模板化语言") if len(msg["content"]) < 20: issues.append(f"Line {i}: assistant 回复过短") # 5. 统计 stats = { "total_samples": len(lines), "avg_turns": np.mean([len(json.loads(l)["messages"]) for l in lines]), "avg_assistant_len": np.mean([ len(m["content"]) for l in lines for m in json.loads(l)["messages"] if m["role"] == "assistant" ]), "issues_found": len(issues), } return {"issues": issues[:20], "stats": stats} 2. 训练配置 # train_lora.py import torch from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments from peft import LoraConfig, get_peft_model, TaskType from trl import SFTTrainer, SFTConfig # 1. 加载模型和分词器 model_name = "Qwen/Qwen2.5-7B-Instruct" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.bfloat16, device_map="auto", attn_implementation="flash_attention_2" ) # 2. LoRA 配置 lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, r=64, # 秩,常用 8/16/32/64 lora_alpha=128, # alpha = 2 * r 是常见默认值 lora_dropout=0.05, target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj" ], bias="none" ) # 3. 应用 LoRA model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 输出: trainable params: 39,976,960 || all params: 7,621,836,800 || trainable%: 0.5247% # 4. 训练配置 training_args = SFTConfig( output_dir="./output/qwen2.5-7b-lora", num_train_epochs=3, per_device_train_batch_size=4, gradient_accumulation_steps=4, # 有效 batch_size = 16 learning_rate=2e-4, lr_scheduler_type="cosine", warmup_ratio=0.05, bf16=True, logging_steps=10, save_strategy="steps", save_steps=200, save_total_limit=3, eval_strategy="steps", eval_steps=200, load_best_model_at_end=True, metric_for_best_model="eval_loss", greater_is_better=False, gradient_checkpointing=True, max_seq_length=2048, dataset_text_field="messages", ) # 5. 加载数据 from datasets import load_dataset dataset = load_dataset("json", data_files={ "train": "data/train.jsonl", "validation": "data/val.jsonl" }) # 6. 启动训练 trainer = SFTTrainer( model=model, args=training_args, train_dataset=dataset["train"], eval_dataset=dataset["validation"], processing_class=tokenizer, ) trainer.train() 3. QLoRA:4bit 量化微调 显存不够?用 QLoRA 量化到 4bit: ...

2026-06-28 · 4 min · 844 words · 硅基 AGI 探索者
lora finetuning tuning guide

LoRA微调参数调优指南

引言 LoRA(Low-Rank Adaptation)是最流行的参数高效微调方法之一,通过在冻结的预训练权重上添加低秩适配矩阵,以极少的可训练参数实现有效的模型微调。然而,LoRA的效果高度依赖参数配置——错误的参数组合可能导致微调无效甚至损害模型能力。本文提供LoRA微调参数调优的系统性指南。 LoRA原理简述 LoRA将权重更新分解为两个低秩矩阵的乘积: W_new = W_frozen + ΔW = W_frozen + B × A 其中A是r×d的矩阵,B是d×r的矩阵,r远小于d。训练时只更新A和B,原始权重W保持冻结。这使可训练参数从O(d²)降低到O(rd),大幅减少显存和计算需求。 核心参数详解 参数一:秩(Rank, r) 含义:低秩矩阵的秩,决定了适配器的表达能力和参数量。 常见取值范围:r = 4, 8, 16, 32, 64 影响分析: r过小(如r=1-4):表达能力不足,难以学习复杂的领域知识。适合简单任务(如风格迁移)。 r适中(如r=8-16):大多数任务的最佳区间。在表达能力和泛化能力间取得平衡。 r过大(如r=64-128):参数量增加,可能过拟合。仅在复杂任务和大数据量时有收益。 选型建议: # 根据任务复杂度选择r task_complexity = { '风格迁移': 4, '分类任务': 8, '问答任务': 16, '代码生成': 32, '多任务微调': 64 } 参数二:缩放因子(lora_alpha) 含义:LoRA更新的缩放系数,实际更新量 = alpha / r × B × A。 常见取值:alpha = 8, 16, 32 与r的关系:alpha和r的比值(alpha/r)控制更新的幅度。常见的经验设置: alpha = 2 × r(如r=8, alpha=16):最常用,更新幅度适中 alpha = r(如r=16, alpha=16):较小的更新幅度,更保守 alpha = 4 × r(如r=8, alpha=32):较大的更新幅度,学习更快但有过拟合风险 参数三:目标模块(target_modules) 含义:LoRA适配器应用于哪些Transformer模块。 ...

2026-06-27 · 3 min · 453 words · 硅基 AGI 探索者
lora vs dora vs qlora

LoRA vs DoRA vs QLoRA:参数高效微调三剑客对比

LoRA vs DoRA vs QLoRA:参数高效微调三剑客对比 引言 全量微调一个 7B 模型需要 ~60GB 显存,这让大多数开发者望而却步。参数高效微调(PEFT)方法通过只训练极少量参数,实现了接近全量微调的效果。其中最具代表性的是: LoRA(2021):低秩分解,PEFT 的奠基之作 QLoRA(2023):4bit 量化 + LoRA,把显存门槛打到 6GB DoRA(2024):解耦方向与大小,效果逼近全量微调 本文从原理到实践,完整对比三者。 1. LoRA:低秩适配 1.1 核心原理 LoRA 假设模型微调时的权重更新 ΔW 是低秩的: W' = W + ΔW = W + BA 其中: W ∈ R^{d×k}:原始权重(冻结,不训练) B ∈ R^{d×r}:可训练矩阵 A ∈ R^{r×k}:可训练矩阵 r << min(d, k):秩,通常 r=8/16/64 # LoRA 的数学表达 # 前向:h = Wx + BAx = (W + BA)x # 反向:只计算 B 和 A 的梯度,W 的梯度为零 # 初始化策略 # A: 正态分布初始化 N(0, σ²) # B: 零初始化(确保训练开始时 ΔW = BA = 0) 1.2 代码实现 import torch import torch.nn as nn class LoRALayer(nn.Module): """LoRA 层的独立实现""" def __init__( self, in_features: int, out_features: int, r: int = 8, alpha: int = 16, dropout: float = 0.0, ): super().__init__() self.r = r self.scale = alpha / r # 缩放系数 # 原始权重(冻结) self.base_weight = nn.Parameter( torch.randn(out_features, in_features), requires_grad=False ) # LoRA 矩阵 self.lora_A = nn.Parameter(torch.randn(r, in_features) * 0.01) self.lora_B = nn.Parameter(torch.zeros(out_features, r)) self.dropout = nn.Dropout(dropout) def forward(self, x: torch.Tensor) -> torch.Tensor: base_out = x @ self.base_weight.T lora_out = (self.dropout(x) @ self.lora_A.T @ self.lora_B.T) * self.scale return base_out + lora_out # 使用 PEFT 库(推荐) from peft import LoraConfig, get_peft_model config = LoraConfig( r=16, lora_alpha=32, lora_dropout=0.05, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], bias="none", task_type="CAUSAL_LM", ) model = get_peft_model(base_model, config) 1.3 参数量分析 以 LLaMA-7B(hidden_dim=4096)为例: ...

2026-06-25 · 7 min · 1318 words · 硅基 AGI 探索者
fine tuning qlora guide

QLoRA 微调实战:4bit 量化下的高效训练

QLoRA 微调实战:4bit 量化下的高效训练 引言 大模型微调最大的障碍是显存。一个 7B 模型以 FP16 加载需要约 14GB 显存,加上 Adam 优化器状态和梯度,训练时轻松突破 60GB。QLoRA 通过 4bit 量化 + 低秩适配器,将训练显存压缩到 ~6GB,让单卡 24GB 消费级 GPU 微调 7B-13B 模型成为现实。 本文从原理到代码,完整覆盖 QLoRA 微调全流程。 1. QLoRA 核心原理 1.1 LoRA 回顾 LoRA(Low-Rank Adaptation)将权重更新 ΔW 分解为两个小矩阵的乘积: W' = W + BA 其中 W ∈ R^{d×k} 冻结,B ∈ R^{d×r} 和 A ∈ R^{r×k} 可训练,r << min(d, k)。 参数量从 d×k 降至 r×(d+k),但模型仍需以 FP16 加载。 1.2 QLoRA 的三大创新 QLoRA 在 LoRA 基础上引入三项关键技术: ...

2026-06-25 · 6 min · 1115 words · 硅基 AGI 探索者
鲁ICP备2026018361号