尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

7B模型显存告急?LoRA微调技术让你轻松搞定,16GB显存也能玩转大模型!

7B模型显存告急?LoRA微调技术让你轻松搞定,16GB显存也能玩转大模型! 全参数微调一个7B模型需要多少显存模型本身fp16要14GB优化器状态fp32要28GB梯度要14GB加起来56GB起步。一张A100 40GB都装不下。LoRALow-Rank Adaptation把微调参数量降到原来的0.1%显存需求直接砍到1/5。16GB显存的V100也能微调7B模型。这篇从原理到手写实现到参数调优把LoRA讲透。LoRA原理为什么低秩就够了核心观察预训练模型的权重矩阵W在微调时的变化量ΔW是低秩的——不需要完整的d×d矩阵来表达调整用两个小矩阵A和B就够了。原权重: W ∈ R^{d×d} 参数量: d²LoRA: ΔW B × A 参数量: 2 × d × r (r d) B ∈ R^{d×r}, A ∈ R^{r×d}前向传播h Wx ΔWx Wx BAxr就是LoRA的rank秩通常取8、16、64。当d4096、r8时原来一个矩阵要4096×409616M参数LoRA只要2×4096×865K参数降了246倍。为什么低秩有效微调时模型只需要做小幅调整不是从头学。小幅调整不需要满秩矩阵来表达。好比给你一张照片做微调——改亮度对比度几个旋钮就够了不用重画整张照片。手写LoRA实现不用任何库20行代码实现LoRAimport torchimport torch.nn as nnimport mathclass LoRALinear(nn.Module): LoRA适配的线性层 def __init__( self, original_linear: nn.Linear, rank: int 8, alpha: float 16.0, dropout: float 0.0, ): super().__init__() self.original original_linear self.rank rank self.alpha alpha self.scaling alpha / rank # 缩放因子 d_in original_linear.in_features d_out original_linear.out_features # LoRA矩阵 self.lora_A nn.Parameter(torch.empty(d_in, rank)) self.lora_B nn.Parameter(torch.zeros(d_out, rank)) # Dropout self.dropout nn.Dropout(dropout) if dropout 0 else nn.Identity() # 初始化A用KaimingB用零 # 这样训练开始时BA0模型行为和原始一样 nn.init.kaiming_uniform_(self.lora_A, amath.sqrt(5)) # B已经初始化为零 # 冻结原始权重 self.original.weight.requires_grad False if self.original.bias is not None: self.original.bias.requires_grad False def forward(self, x: torch.Tensor) - torch.Tensor: # 原始路径 LoRA路径 original_output self.original(x) lora_output self.dropout(x) self.lora_A self.lora_B.t() * self.scaling return original_output lora_output几个关键设计1. B初始化为零训练开始时ΔWBA0模型输出和预训练完全一样。这是一个优雅的起点——你从已知的良好状态开始微调不会因为随机初始化导致训练初期输出混乱。2. 缩放因子scalingalpha/rankalpha是一个超参数和rank一起控制LoRA的强度。alpha固定时rank越大每个参数的影响越小被rank除。这样调rank时不用同步调学习率。3. 原始权重冻结requires_gradFalse不计算梯度、不更新参数。只有A和B是可训练的。把LoRA应用到模型上手动替换每个线性层太麻烦写一个自动化函数def apply_lora_to_model( model: nn.Module, rank: int 8, alpha: float 16.0, dropout: float 0.0, target_modules: list[str] | None None,) - nn.Module: 自动将模型中的线性层替换为LoRA版本 if target_modules is None: # 默认对注意力的Q/V投影做LoRA target_modules [w_q, w_v] for name, module in model.named_modules(): # 找到目标模块 if not any(t in name for t in target_modules): continue if not isinstance(module, nn.Linear): continue # 获取父模块和属性名 parts name.split(.) parent model for part in parts[:-1]: parent getattr(parent, part) attr_name parts[-1] # 替换为LoRA版本 lora_layer LoRALinear( module, rankrank, alphaalpha, dropoutdropout ) setattr(parent, attr_name, lora_layer) return modeldef get_lora_params(model: nn.Module) - list[nn.Parameter]: 只获取LoRA参数 return [p for n, p in model.named_parameters() if lora_ in n and p.requires_grad]def get_lora_state_dict(model: nn.Module) - dict: 只保存LoRA参数 return { k: v for k, v in model.state_dict().items() if lora_ in k }使用示例# 加载预训练模型model GPTModel(vocab_size32000, d_model4096, n_heads32, n_layers32)# 应用LoRAmodel apply_lora_to_model( model, rank16, alpha32.0, target_modules[w_q, w_k, w_v, w_o], # 对所有注意力投影做LoRA)# 只训练LoRA参数optimizer torch.optim.AdamW(get_lora_params(model), lr1e-4)# 正常训练for batch in dataloader: optimizer.zero_grad() output model(batch[input_ids], labelsbatch[labels]) output[loss].backward() optimizer.step()# 保存只保存LoRA权重几MB就够torch.save(get_lora_state_dict(model), lora_weights.pt)显存对比7B模型d_model4096, 32层, 32头的参数分布组件全参数LoRA (r16, QKVO)注意力权重201M201M (冻结)注意力LoRA016.8M (可训练)FFN权重6.7B6.7B (冻结)总可训练参数6.9B16.8M训练显存(fp16)~56GB~18GB16.8M vs 6.9B——可训练参数少了400倍。显存从56GB降到18GB一张V100 16GB勉强能跑开gradient checkpointing后。rank和alpha怎么设这是LoRA最常被问的问题。我直接给结论再解释原因。rank选择任务复杂度推荐rank原因简单指令跟随4-8调整量小低秩够用风格迁移/对话8-16需要学一些风格特征代码/数学/多语言16-64需要较大的调整空间领域知识注入64-128知识量大需要更高秩alpha选择alpha 2 × rank是一个不错的起点。alpha/rank就是实际的缩放系数alpha16, rank8 → scaling2.0alpha32, rank16 → scaling2.0alpha16, rank16 → scaling1.0我的经验rank从8或16开始试效果不够再加别上来就64alpha固定为rank的2倍调rank就够了只对Q/V做LoRA和对QKVO都做效果差不了多少但后者参数多一倍。省资源就只做Q/V对FFN也做LoRAtarget_modules加w1,w2,w3可以进一步提升效果但参数量翻3倍一个容易踩的坑rank太大效果反而变差。因为低秩约束本身就是一种正则化——rank小LoRA只能学最重要的方向rank大了开始学噪声。我做过对比实验rank4: accuracy78.3% (欠拟合)rank8: accuracy82.1% (最佳)rank16: accuracy81.7% (略降)rank64: accuracy79.5% (过拟合)这是在一个小型指令微调任务上的结果。rank8反而最好64最差。QLoRA显存再砍一半QLoRA在LoRA基础上加了3个优化让4-bit模型也能微调4-bit NormalFloat量化新的数据类型比普通INT4更精确双重量化量化常数本身也量化省一点显存分页优化器优化器状态用CPU内存分页防止OOMfrom transformers import BitsAndBytesConfig# QLoRA配置bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_quant_typenf4, # NormalFloat4 bnb_4bit_compute_dtypetorch.bfloat16, # 计算时反量化到bf16 bnb_4bit_use_double_quantTrue, # 双重量化)# 加载4-bit模型from transformers import AutoModelForCausalLMmodel AutoModelForCausalLM.from_pretrained( meta-llama/Llama-2-7b-hf, quantization_configbnb_config, device_mapauto,)# 然后正常应用LoRAfrom peft import LoraConfig, get_peft_modellora_config LoraConfig( r16, lora_alpha32, target_modules[q_proj, v_proj], lora_dropout0.05, task_typeCAUSAL_LM,)model get_peft_model(model, lora_config)model.print_trainable_parameters()# 输出: trainable params: 4,194,304 || all params: 6,738,415,616 || trainable%: 0.0622QLoRA的显存占用7B模型项目全参数fp16LoRA fp16QLoRA 4-bit模型权重14GB14GB (冻结)3.5GBLoRA参数-0.03GB0.03GB优化器状态28GB0.06GB0.06GB梯度14GB0.03GB0.03GB激活值~5GB~5GB~5GB合计~61GB~19GB~9GB9GB跑7B微调消费级显卡都够用。LoRA的合并和部署训练完的LoRA权重怎么用两种方式方式1运行时合并# 加载原始模型 LoRA权重model load_base_model()lora_state torch.load(lora_weights.pt)# 把LoRA权重合并到原始权重for name, param in model.named_parameters(): if name in lora_state: # 找到对应的A和B # ΔW B A^T * scaling # W_new W ΔW ...方式2权重预合并推荐def merge_lora_weights(model: nn.Module) - nn.Module: 将LoRA权重合并到原始权重然后移除LoRA模块 for name, module in model.named_modules(): if isinstance(module, LoRALinear): # 合并: W_new W B A^T * scaling with torch.no_grad(): delta_w (module.lora_B module.lora_A.t()) * module.scaling module.original.weight.data delta_w.t() # 用原始线性层替换LoRA层 parts name.split(.) parent model for part in parts[:-1]: parent getattr(parent, part) setattr(parent, parts[-1], module.original) return model# 合并后保存完整模型model merge_lora_weights(model)torch.save(model.state_dict(), merged_model.pt)合并后的模型和全参数微调的模型格式完全一样推理时没有任何额外开销。LoRA的零推理代价就是这么来的。多LoRA切换如果同一基座模型训了多个LoRA比如不同领域可以只保存基座多个小LoRA文件# 基座模型加载一次base_model load_base_model()# 切换到领域A的LoRAlora_a torch.load(lora_domain_a.pt)model_a apply_lora_weights(base_model, lora_a)# 切换到领域B的LoRAlora_b torch.load(lora_domain_b.pt)model_b apply_lora_weights(base_model, lora_b)基座14GB每个LoRA才几十MB。存一个基座10个LoRA比存10个全量微调模型省140GB。LoRA的常见问题QLoRA效果比全参数微调差多少A在大多数场景下差距很小。微软原论文的实验显示LoRA在多数任务上和全参数微调持平甚至更好。但也有反例——比如微调数据量和预训练数据量差好几个数量级时全参数微调可能更好。QLoRA能叠加吗A能。先训一个LoRA-1再在LoRA-1的基础上训LoRA-2。但注意学习率要更小因为初始状态不再是预训练权重了。也有DyLoRA这种动态调rank的方法不过工程上复杂度太高不推荐新手用。QLoRA的dropout设多少A0.05-0.1。比全参数微调的0.1-0.3小因为LoRA本身参数少正则化已经够强了。rank越小dropout可以越小。QLoRA和Adapter、Prefix-Tuning有什么区别ALoRA是修改权重加ΔWAdapter是加额外层插在网络中间Prefix-Tuning是加额外输入可学习的prompt前缀。LoRA的优势是不增加推理延迟权重可以合并Adapter多了一层计算Prefix-Tuning占了输入长度。最后唠两句为什么AI大模型成为越来越多程序员转行就业、升职加薪的首选很简单这些岗位缺人且高薪智联招聘的最新数据给出了最直观的印证2025年2月AI领域求职人数同比增幅突破200% 远超其他行业平均水平整个人工智能行业的求职增速达到33.4%位居各行业榜首其中人工智能工程师岗位的求职热度更是飙升69.6%。AI产业的快速扩张也让人才供需矛盾愈发突出。麦肯锡报告明确预测到2030年中国AI专业人才需求将达600万人人才缺口可能高达400万人这一缺口不仅存在于核心技术领域更蔓延至产业应用的各个环节。那0基础普通人如何学习大模型 深耕科技一线十二载亲历技术浪潮变迁。我见证那些率先拥抱AI的同行如何建立起效率与薪资的代际优势。如今我将积累的大模型面试真题、独家资料、技术报告与实战路线系统整理分享于此为你扫清学习困惑共赴AI时代新程。我整理出这套 AI 大模型突围资料包【允许白嫖】✅从入门到精通的全套视频教程✅AI大模型学习路线图0基础到项目实战仅需90天✅大模型书籍与技术文档PDF✅各大厂大模型面试题目详解✅640套AI大模型报告合集✅大模型入门实战训练这份完整版的大模型 AI 学习和面试资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】①从入门到精通的全套视频教程包含提示词工程、RAG、Agent等技术点② AI大模型学习路线图0基础到项目实战仅需90天全过程AI大模型学习路线③学习电子书籍和技术文档市面上的大模型书籍确实太多了这些是我精选出来的④各大厂大模型面试题目详解⑤640套AI大模型报告合集⑥大模型入门实战训练如果说你是以下人群中的其中一类都可以来智泊AI学习人工智能找到高薪工作一次小小的“投资”换来的是终身受益应届毕业生‌无工作经验但想要系统学习AI大模型技术期待通过实战项目掌握核心技术。零基础转型‌非技术背景但关注AI应用场景计划通过低代码工具实现“AI行业”跨界‌。业务赋能 ‌突破瓶颈传统开发者Java/前端等学习Transformer架构与LangChain框架向AI全栈工程师转型‌。获取方式有需要的小伙伴可以保存图片到wx扫描二v码免费领取【保证100%免费】
返回列表