ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大模型训练显存不够?从梯度累积到LoRA的四级省显存策略

大模型训练显存不够?从梯度累积到LoRA的四级省显存策略 训练大模型最让人头疼的问题是什么不是模型效果不够好而是“改一行代码就要被显存警告支配”的恐惧。你精心设计好网络结构满怀信心地启动训练脚本几秒钟之后就看到显存 OOM 的通红报错刷满整块屏幕。这种情况在实验室里反复重演在公司内部同样屡见不鲜。但显存不足这件事很多时候不是硬件不够强而是我们对显存去向一无所知。想要解决 OOM不是无脑买更贵的显卡也不是一味把 batch size 调小然后祈祷模型能收敛。显存优化的核心在于先弄清楚显存到底被谁吃掉了然后才能从四个不同层级制定省显存策略缩 batch、梯度检查点、ZeRO、LoRA。这四级策略从最基础到最彻底是一条完整的省显存阶梯。这篇文章我会先带你搞懂训练时显存花在哪再逐个拆解这四个省显存策略的原理、适用场景、代码写法以及它们的边界和坑。读完你能自己判断你的任务到底适合哪一级优化以及怎么用最少的内存跑同一个模型。1. 显存到底花在哪先看账本再谈省钱很多人以为显存是给模型参数住的模型越大显存越大。这句话对了一半但在训练场景里模型参数只是显存消耗的一部分甚至可能不是最大的部分。训练一次前向传播和反向传播显存里至少要住下这几类数据1.1 参数与梯度模型本身的权重参数以及反向传播计算出来的梯度这是最直观的开销。通常我们说的“7B 模型”光模型权重在 fp16 精度下就有约 14GB梯度又是几乎同样大小的一份。算到这里很多人就已经开始皱眉了一张 24GB 的卡光权重和梯度就快占满了还没算其它开销。1.2 优化器状态这一项常常被初学者忽略却在全量微调中占据显存大头。以最常用的 AdamW 优化器为例它会为每个参数保存两份状态一阶动量 m 和二阶动量 v。在 fp32 精度下这两个状态每个参数各占 4 字节也就是说 7B 模型仅仅优化器状态就需要7B * 4B * 2 56GB。加上 fp16 的模型参数 14GB 和梯度 14GB总计接近 84GB。所以为什么训练 7B 全参模型至少需要 80GB 以上的显存账就是这么算出来的。这里反复强调一个容易混淆的点训练时模型权重、梯度、优化器状态往往需要同时驻留在显存中而不是像推理那样算完一个层就可以释放一部分。1.3 激活值激活值是前向传播过程中每一层输出的中间结果。反向传播计算梯度时这些激活值会被重新使用所以不能提前丢掉。对于长序列、大 batch 的任务激活值可能比模型参数还占显存。很多大模型训练时报 OOM报错位置根本不在模型定义处而是在某个 transformer 层的激活计算处这就是激活值失控的典型信号。1.4 临时缓存与通信开销框架自身的临时张量、CUDA context、分布式训练的通信缓冲区也都会占用显存。这部分虽然零碎但累计起来同样不容小觑。以上四项叠加才是完整训练显存占用。理解这一点后我们会发现省显存有不同的切入口减 batch降低单次迭代的激活值峰值但不能减少参数、梯度和优化器状态。梯度检查点用计算换显存专门压缩激活值。ZeRO重新切分参数、梯度、优化器状态从分布式角度消灭冗余。LoRA干脆不训练原模型参数从源头把优化器状态和梯度规模降下去。这就是我要说的核心判断显存优化不是单点技巧而是组合策略。你缺哪一块就用对应层级的方案去补。下面的内容会把这四级逐一展开。2. 第一级缩减 batch size 与梯度累积2.1 为什么减 batch 能省显存batch size 直接影响的是激活值和 batch 维度的中间张量大小。在数据并行训练中每个 GPU 需要处理一份完整的 mini-batch 数据。batch 越大前向传播时的中间特征图就越多占用的显存自然越高。所以当训练刚遇到 OOM 时绝大多数人的第一反应就是“把 batch 调小一点”。这个思路没错但它有一个隐藏问题batch size 太小batch normalization 的统计量不稳定梯度估计噪声变大模型收敛变慢甚至不收敛。这不是玄学这是随机梯度下降的统计性质。我们需要一个办法既要住进小 batch 的显存容量又要保留大 batch 的梯度稳定性。梯度累积就是为解决这个矛盾而来的。2.2 梯度累积用时间换显存梯度累积的思路很简单把一个大 batch 拆成 N 个小 batch依次完成前向和反向计算但先不更新参数把梯度累加到一起。等跑了 N 个小 batch 之后再对累积的梯度做一次优化器更新。从数学上看累积 N 次梯度再更新等效于用 N 倍大小的 batch 计算了一次梯度。从显存上看每个小 batch 的激活值仍然是“小 batch”级别不会因为累积而增加显存开销。下面是一段最简的 PyTorch 梯度累积写法# 文件路径train_with_grad_accum.py import torch # 模拟一个最简单的模型、损失函数和优化器 model torch.nn.Linear(1024, 1024) optimizer torch.optim.AdamW(model.parameters(), lr1e-4) # 假设你原本想用 batch_size64 训练但显存只够 batch_size16 accum_steps 4 # 累积步数 micro_batch_size 16 # 每个 micro batch 的大小 optimizer.zero_grad() for step, (inputs, labels) in enumerate(dataloader): # 每个 micro batch 单独前向 反向 loss model(inputs, labels).mean() loss loss / accum_steps # 关键要除以累积步数保持总梯度量级不变 loss.backward() # 每累积够 accum_steps 次才更新一次参数 if (step 1) % accum_steps 0: optimizer.step() optimizer.zero_grad()这里真正的关键点也是新手最容易踩坑的地方是loss loss / accum_steps这一步。如果不做除法N 次反向传播的梯度累加后等效梯度会变成原来的 N 倍。虽然 AdamW 这类自适应优化器对梯度量级有一定鲁棒性但等价于暗中改变了学习率收敛行为会偏离预期。用这个方案你只需要付出训练时间变长的代价就可以在大 batch 语义下完成训练。它属于显存优化的“基础操作”几乎所有框架都原生支持。2.3 常见框架中的等效配置Hugging Face Transformers 的Trainer里它直接封装好了这个逻辑。你只需要设置per_device_train_batch_size为显存可承受的小值再设置gradient_accumulation_steps为累积步数。比如你的显存只支持batch_size2但想达到batch_size16的效果就在配置里写{ per_device_train_batch_size: 2, gradient_accumulation_steps: 8, learning_rate: 2e-5, num_train_epochs: 3 }那么实际等效 batch size 是2 * 8 16。从这里可以提炼出第一级省显存策略的判断它只解决激活值峰值问题解决不了权重、梯度、优化器状态的固定开销。3. 第二级梯度检查点Gradient Checkpointing3.1 为什么只减 batch 还是不够假设你用一个较大的模型做全参微调。把 batch 调到 1激活值确实降下去了但模型参数、梯度和优化器状态仍然不动如山。对于 7B 模型权重加梯度加优化器状态大约是 84GBbatch 怎么调都装不进 24GB 显卡。所以 batch 是显存优化的“第一层防线”但它的优化上限很低。面对大模型我们需要削弱激活值和参数两者的占用梯度检查点首先解决激活值的问题。3.2 原理前向传播不存激活反向再算一遍正常前向传播会把每一层激活值都保存到显存里用于反向传播。梯度检查点的思想是前向传播时不保存中间激活值只保存每一层的输入。等到反向传播需要某个中间梯度时再从保存的输入重新执行一次前向得到激活值。这本质上是典型的空间换时间策略。显存省了但额外增加了一次重计算训练时间大约会上升 20%~30%。不过这个代价在显存不足时是值得的。Hugging Face Transformers 里开启非常方便# 文件路径enable_checkpointing.py from transformers import AutoModelForCausalLM model AutoModelForCausalLM.from_pretrained(your/model-name) # 开启梯度检查点 model.gradient_checkpointing_enable() # 训练代码保持不变即可 # 训练完成后如果还要做推理建议关闭 # model.gradient_checkpointing_disable()如果是自己手写 PyTorch 模型可以用torch.utils.checkpoint对某个模块做重计算包装。需要注意使用梯度检查点后模型的 forward 函数应该保持纯粹的计算逻辑不要包含任何有状态的外部副作用比如把中间结果写进列表或者修改全局变量。因为 checkpoint 会调用 forward 多次副作用会被重复执行容易踩坑。3.3 视频说明gradient_checkpointing和batch_size不是二选一而是可以叠加使用。先减 batch 到显存可承受再开启梯度检查点进一步压掉激活值。在训练大模型时这两个操作通常是同时启用的。这一级的判断是梯度检查点省的是激活显存代价是训练速度下降。它不改变参数、梯度、优化器状态所以单独用它也无法训练超大模型但它能帮你把有限显存里的“空间”腾出来给更大的 batch。4. 第三级显存不够硬盘来凑ZeRO 与优化器状态切分4.1 从一个反直觉的问题说起为什么一张 80GB 的 A100 训练 7B 模型还是吃力按照前面的账本7B 全参 fp16 AdamW 的显存需求大约是 84GB。A100 单卡 80GB 已经很接近上限稍微加一些激活值和输入数据就直接 OOM。哪怕把 batch 设为 1也几乎跑不动。这时候再强大的单卡也救不了你我们必须换思路不改变模型结构改变数据在设备之间的分布方式。4.2 ZeRO 到底是什么ZeRO 是 DeepSpeed 提出的分布式显存优化方案它的核心是消除数据并行训练中的显存冗余。在普通数据并行DDP中每张卡都保存一份完整的模型参数、梯度和优化器状态。4 张卡就有 4 份冗余这显然很浪费。ZeRO 把这些状态按维度切分到不同设备上每张卡只保存一份分片用的时候再通过通信把数据聚合起来。ZeRO Stage 1切分优化器状态。ZeRO Stage 2切分优化器状态 梯度。ZeRO Stage 3切分优化器状态 梯度 模型参数。当显存不足以加载 7B 模型的完整参数时Stage 3 可能是唯一能在有限显存上启动训练的方案。更进一步的 ZeRO-Offload则把优化器状态和参数卸载到 CPU 内存甚至 NVMe 固态硬盘上。热搜词里提到的“显存不够硬盘来凑”说的就是这个方向。但要强调一点把数据放到 CPU 内存或 NVMe 硬盘速度代价非常明显。训练时每一轮参数更新都会涉及 CPU 与 GPU 之间的数据搬运如果数据量大训练会变得极慢。这个方法适合跨不过显存门槛时的“应急方案”不适合追求长期训练效率的场景。4.3 配置示例使用 DeepSpeed 时在 Hugging Face Transformers 的Trainer中通过配置文件启用。下面是一个 ZeRO Stage 2 的配置示例// 文件路径ds_config_zero2.json { zero_optimization: { stage: 2, allgather_partitions: true, allgather_bucket_size: 2e8, overlap_comm: true, reduce_scatter: true, reduce_bucket_size: 2e8, contiguous_gradients: true }, fp16: { enabled: auto }, train_batch_size: auto, train_micro_batch_size_per_gpu: auto }命令行或代码里带上这个配置即可deepspeed --num_gpus4 train.py --deepspeed ds_config_zero2.jsonZeRO 的部署复杂度明显高于前两级。它需要多卡环境、合适的通信带宽还要考虑通信开销对训练速度的影响。如果只有一张卡单纯用 DeepSpeed 的 ZeRO Stage 1/2 很多时候并没有额外收益因为切分是为了多卡协作这时更值得关注的是 ZeRO-Offload或者直接考虑下一级的 LoRA。4.4 与 offload 搭配时的判断如果日常使用场景是多卡集群ZeRO 几乎是省显存的第一选择它可以在不牺牲 batch 语义的前提下容纳更大模型。如果你只有单卡 16G 或 24GZeRO 的收益有限真正能“无中生有”地把模型塞进小显存的技术还得看优化器状态和参数的量级能不能降下来——这就迎来了 LoRA。5. 第四级LoRA——不训练原模型参数5.1 全量微调的问题全量微调Full Fine-tuning意味着要更新所有模型参数因此必须为每个参数保存梯度与优化器状态。前面的账本已经说明7B 模型在这种模式下至少需要 80GB 左右显存。这让绝大多数个人开发者和中小团队只能望而却步。为了让模型适配下游任务又不想付出这么高的显存成本LoRA 提供了一种新的思路冻结原模型权重在每一层旁边新增一个低秩矩阵只训练这个新增的小矩阵。LoRA 这个名字经常与 LoRa 通信协议混淆后者是物联网远距离通信技术而这里讨论的是 Low-Rank Adaptation一种参数高效微调方法。在做技术交流时提到 LoRA要先确认对方讨论的是模型微调方向还是硬件通信方向两者完全是两回事。5.2 核心原理低秩矩阵为什么可行一个训练好的大模型其权重在高维空间中是高度冗余的。也就是说模型微调时真正需要的参数更新可能只需要分布在低维子空间里。LoRA 的做法是保持原始权重W不动在旁边加上两个低秩矩阵A和B让前向传播变成Wx BAx。训练时只更新A和B。假设原始层的维度是2048 * 2048需要训练的参数量约为 400 万。使用 LoRA 后设置秩r8新增参数量只有2048 * 8 8 * 2048 32768个。训练成本直接降低两个数量级以上。优化器状态、梯度、反向传播的计算量都只需要针对这一小部分新增参数处理。这就是为什么 LoRA 能让 16G 显卡跑 7B 甚至更大的模型微调。它没有改变原模型的容量而是改变了你需要“更新”的范围。5.3 LoRA 微调示例使用 Hugging Face PEFT 库LoRA 的加载和配置非常简洁# 文件路径lora_finetune_example.py from transformers import AutoModelForCausalLM, AutoTokenizer from peft import LoraConfig, get_peft_model model AutoModelForCausalLM.from_pretrained(your/base-model) tokenizer AutoTokenizer.from_pretrained(your/base-model) # 配置 LoRA 参数 lora_config LoraConfig( r8, lora_alpha32, target_modules[q_proj, v_proj], lora_dropout0.1, biasnone, task_typeCAUSAL_LM ) # 包装为 PeftModel原模型参数被冻结 model get_peft_model(model, lora_config) model.print_trainable_parameters() # 通常会输出trainable params: ~8M || all params: ~7B || trainable%: 0.1这段代码里的r和lora_alpha是最容易让人困惑的两个参数。r是低秩矩阵的秩直接决定了新增参数量lora_alpha是缩放因子控制 LoRA 分支对原输出的影响程度一般建议设置为r的两倍左右。调整时不要一上来就调大r先固定r8做基准实验再按需扩大。LoRA 并不保证效果和全量微调完全一致。对于某些任务低秩假设可能不够充分当训练数据量极大、任务与原模型分布差异极大时全量微调的天花板仍然更高。但 LoRA 的价值在于它把“能不能跑”变成了“跑得多好”。大多数个人开发者、业务场景下的适配任务LoRA 已经能覆盖绝大部分需求。5.4 LoRA 文件格式问题训练完 LoRA 之后得到的是一个很小的模型产物。这个文件里只有新增的低秩矩阵和对应的配置信息不包含原始大模型的权重。使用时需要先加载基础模型再加载 LoRA 权重合并或叠加。相关文件通常以adapter_model.safetensors和adapter_config.json的形式存在这就是很多人问的“lora 文件格式是什么”的答案。它的优点是可组合、可插拔一个基础模型可以挂载多套不同任务的 LoRA 适配器随用随切换。6. 四级组合策略与决策表前面四段分别介绍了四种省显存手段下面把它们的适用边界放在一起对比优化层级省掉哪部分显存主要代价单卡是否可用适用场景缩减 batch 梯度累积激活值峰值训练时间变长是所有训练任务最先尝试的基础优化梯度检查点激活值存储大幅增加重计算训练速度下降 20%~30%是大模型或长序列训练batch 已缩到极限仍 OOMZeRO Stage 1/2优化器状态、梯度通信开销需要多卡环境部分Stage 3 可单卡配 offload但速度极慢多卡数据并行训练追求单卡容量扩展ZeRO Stage 3 / Offload参数 梯度 优化器状态CPU/硬盘带宽瓶颈训练极慢可以但效率低显存缺口很大的应急训练场景LoRA梯度 优化器状态 反向传播计算量效果可能略低于全量微调需要额外超参调整是单卡小显存微调大模型最推荐的做法在实际项目中我见过很多错误的选型有的团队只有 24G 单卡却一门心思搞 ZeRO Stage 3 NVMe offload结果训练速度慢到难以接受有的团队明明有 8 张 A100却坚持 LoRA 微调反而因为低秩假设限制了模型在新任务上的上限。这说明选型应该由“你有多少卡、多少显存、任务需要多高的效果”三者共同决定。一个比较通用的组合公式是先评估全量微调是否可行把 batch 缩到 1 看显存还差多少。如果只差激活值就开梯度检查点。如果多卡且差距不大用 ZeRO Stage 2。如果单卡且差距巨大直接切 LoRA。有一种很容易误导人的观点是既然 LoRA 这么好为什么不所有任务都用 LoRA我的答案是显存够用的时候全量微调仍然是最省心、效果上限最高的选择LoRA 的意义在于让资源不足的人也能参与大模型微调是一种“性价比”方案不应神化。7. 完整示例把四级策略串进同一个训练脚本下面这段示例演示了一个比较完整的训练入口。这里不限定具体模型但逻辑适用于大多数 Hugging Face 生态的任务。# 文件路径run_optimized_training.py import torch from transformers import ( AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments ) from peft import LoraConfig, get_peft_model import deepspeed MODEL_NAME your/base-model model AutoModelForCausalLM.from_pretrained( MODEL_NAME, torch_dtypetorch.float16 ) # 第一级数据相关配置 # per_device_train_batch_size 根据显存实测来填 # gradient_accumulation_steps 用来恢复等效大 batch training_args TrainingArguments( output_dir./output, per_device_train_batch_size2, gradient_accumulation_steps8, learning_rate2e-5, num_train_epochs3, fp16True, # 第二级梯度检查点 gradient_checkpointingTrue, # 第三级ZeRO 配置 deepspeedds_config_zero2.json, logging_steps10, save_steps500, ) # 第六节LoRA 方案可选项 USE_LORA True if USE_LORA: lora_config LoraConfig( r8, lora_alpha32, target_modules[q_proj, v_proj], lora_dropout0.1, biasnone, task_typeCAUSAL_LM, ) model get_peft_model(model, lora_config) # 普通训练入口 trainer Trainer( modelmodel, argstraining_args, train_datasettrain_dataset, tokenizertokenizer, ) trainer.train() trainer.save_model(./final_model)这段代码里同时出现了四级策略。但不是每个项目都需要全开。如果显存充足gradient_checkpointing和deepseek可以不启用LoRA 也可以关掉。全开的意义更像一张“极限压缩到极致的对照表”先看哪些配置导致显存告急再决定保留哪几项。真正常见的组合是“小 batch 梯度检查点 LoRA”也就是在不拆模型的情况下把显存压到最低。8. 验证效果与常见问题排查8.1 怎么判断优化生效了训练过程中可以用nvidia-smi查看显存使用变化但这只能看到整体占用。想要更准确地看到是哪部分显存被优化了建议用 PyTorch 的显存统计工具# 文件路径memory_debug.py import torch # 在训练循环中打印显存占用详情 print(torch.cuda.memory_summary(deviceNone, abbreviatedFalse)) # 或者只取关键指标 print(allocated: , torch.cuda.memory_allocated() / 1024**3, GB) print(reserved: , torch.cuda.memory_reserved() / 1024**3, GB)memory_summary会列出缓存的分配细节能帮助你判断当前的优化策略有没有生效。如果显存降了但训练速度慢到离谱大概率是梯度检查点或 ZeRO-Offload 的重计算/搬运开销太大。8.2 常见问题排查清单问题现象可能原因排查方式解决方案启动后立刻 OOMbatch 仍然过大或激活值峰值很高查看报错栈确认 OOM 发生在模型哪一层先减 batch开启梯度检查点再看是否还需 LoRA梯度累积后 loss 震荡严重没有除以累积步数或学习率偏大检查 loss 计算是否除以accum_steps对比相同有效 batch 的 loss 曲线统一按累积步数缩放 loss适当降低学习率开启梯度检查点后训练极慢checkpoint 重计算开销大对比开启前后的单步耗时接受速度下降如果无法接受改用 LoRA 或 ZeRO多卡训练显存不降反升ZeRO 配置错误或通信缓冲占用高检查ds_config是否正确加载查看各卡显存分布确认 stage 设置检查 allgather bucket 大小确保使用 DeepSpeed 启动器单卡低显存跑大模型失败单卡跑全量微调本身就是硬性瓶颈计算全量微调理论显存需求切换 LoRA或 ZeRO Offload但要接受极慢速度LoRA 训练完后效果比全量差秩 r 太低或目标任务与下游差异过大对比不同 r 的结果检查 target_modules 是否覆盖关键层适当增大 r尝试更多 target_modules或用 QLoRA 等进阶变体加载 LoRA 权重后没有生效基础模型与 LoRA 权重不匹配检查模型名称和路径重新加载相同基础模型再调用 peft 的加载接口这里单独强调一下做任何显存优化时都应该先记录基线。不记录 baseline你就无法判断当前优化方案到底是好是坏。建议每次实验都把 batch size、显存占用、单步耗时、loss 曲线记录下来回头调整时才有依据。9. 最佳实践与工程建议9.1 先算账再上卡启动训练前花两分钟估算理论显存需求。7B fp16 AdamW 全参微调约为 84GB 起7B fp16 LoRA 微调通常可以压到 12GB 到 16GB 左右。这类估算虽然粗糙但能让你在写代码前就对选型有清晰判断避免反复试错。9.2 固定有效 batch size使用梯度累积时有效 batch size per_device_train_batch_size * gradient_accumulation_steps * 显卡数量。在做对比实验时务必让这个值保持一致否则你无法判断实验效果差异是来自模型本身还是来自 batch 不同。9.3 优先尝试 LoRA再考虑全量个人开发者和中小团队面对大模型微调第一选择建议直接是 LoRA。它的显存占用低、训练速度快、产物便于管理。只有当任务确实需要全量微调的效果上限且显存资源足够时才启动全量方案。9.4 分布式训练注意通信与断电风险ZeRO Stage 3 会频繁进行全量参数聚合如果机器间网络带宽不足效率可能比单卡还低。生产环境务必优先考虑单机多卡或高速互联环境。同时任何分布式训练都应该开启 checkpoint 保存避免训练中断后前功尽弃。9.5 数据精度与精度损失fp16 混合精度在省显存的同时也可能带来数值稳定性问题。遇到 loss 为 NaN 或模型不收敛时检查 loss scale 是否自动调整必要时切换到 bf16如果你的 GPU 支持或关闭混合精度。9.6 不要盲目堆 LoRA 的 r 值r越大可训练参数量越大效果理论上更好但显存和速度也会线性上升。当增大r无法带来明显收益时说明任务瓶颈不在模型容量而在数据质量或训练策略。不少人误以为r64就比r8好实际结果往往是训练时间和显存翻倍效果却只是略有提升甚至持平。10. 给普通开发者的省显存路线图如果你现在手里只有一张 16G 显存的消费级显卡想微调一个 7B 模型我给你一条清晰的行动路径用 Hugging Face 加载模型先尝试 LoRA。把batch_size调到 1 或 2训练能跑动再逐步上调。如果batch_size1都 OOM开启gradient_checkpointing。如果仍然 OOM确认真实显存占用后考虑 ZeRO-Offload但一定要做好训练速度大幅下降的心理准备。如果效果不理想再调节 LoRA 的r值和target_modules。这套路线图的前提是不做全量微调。全量微调一个小型消费卡几乎是不可能的与其硬撑不如采用参数高效微调把算力集中在解决业务问题上。如果你所在团队拥有多卡 GPU则可以分两步走先用 ZeRO Stage 2 跑通全量微调如果显存仍然吃紧再叠加梯度检查点。多卡环境下仍然不建议直接开启 LoRA因为显存充足时全量微调的收益通常更大。11. 总结与后续学习方向显存优化的本质是管理训练周期的四类开销模型参数、梯度、优化器状态和激活值。缩 batch 只能解决激活值峰值问题梯度检查点进一步压缩激活值存储ZeRO 从多卡视角切分参数、梯度与优化器状态LoRA 则从方法层面绕开了全量微调的显存硬约束。这四级策略不是互斥的而是可以在一个训练流程中按需叠加的。读完这篇文章你应该能回答这几个问题训练时显存为什么不够当前 OOM 出在哪个环节到底应该先调 batch还是开梯度检查点还是上 ZeRO还是直接切 LoRA下一步的实践方向很明确。打开你手头的微调项目先记录当前显存占用和训练速度然后按“梯度累积 - 梯度检查点 - ZeRO - LoRA”的顺序逐步优化。如果你之前从未接触过 LoRA建议找一个小模型比如 1B 级别的模型跑一遍 PEFT 的官方示例把微调、保存、加载、推理整个流程走通再迁移到更大模型上。最后提醒一句不要为了炫技把四种策略全部开启也不要因为迷信“更先进的方案”而放弃最简单的 batch 调优。显存优化的目的是让模型在有限资源下跑起来、跑得好而不是把训练脚本变得复杂。哪一级该用取决于你的显存缺口有多大以及你愿意为省显存付出多少训练时间。
返回列表