
先说实话我见过太多人在微调大模型时被 OOM 卡死。明明手里有一张 32GB 的卡比如 RTX 3090、A5000 或者 A6000结果加载一个 7B 模型、配几个默认参数还没来得及看训练曲线就直接爆显存。更糟的是很多人直接放弃微调转头去租更大的卡白花不少钱。这篇文章我想认真聊一聊在 32GB 单卡上跑 LoRA 和 QLoRA 微调怎么把显存从“勉强能跑”优化到“稳稳跑完”。我会从显存到底被谁吃掉开始讲然后逐步拆解 LoRA/QLoRA 的原理和区别再给出一套我在实际项目中验证过的参数组合和排错清单。不管你是刚入门微调的新手还是被 OOM 折磨过几次的“老战士”这篇文章都希望能帮你把显存用得明明白白。1. 先把账算清楚32GB 显存到底被谁吃掉了1.1 显存占用的五大来源很多人一看到 OOM 就习惯性认为“模型太大了”听起来很直觉但实际上一张大模型训练过程中显存被分摊到了五个完全不同的地方。如果不搞清楚谁是大头你连优化方向都是错的。以 Llama-2-7B 为例模型权重按 FP16 存储一份就是 14GB70亿参数 × 2 字节。但如果直接用全参数微调你需要的远不止这 14GB因为训练过程还涉及另外四块开销优化器状态AdamW 优化器要为每个参数保存两份额外状态一阶动量 m 和二阶动量 vFP32 精度下每个参数占 8 字节。单这一项就是 70亿 × 8 56GB直接爆炸。梯度反向传播后每个参数都会产生梯度FP32 精度下又是 4 字节/参数即 28GB。激活值前向传播会保存每层的中间结果用于反向传播计算梯度。这部分跟 batch size 和序列长度强相关7B 模型上动辄就是几十 GB。CUDA 上下文和中间缓存PyTorch 每次启动就会占掉约 500MB 到 1GB加上 cuDNN 等库的缓存差不多 1-2GB 是保底开销。你把这五块加起来算一下14GB 权重 56GB 优化器 28GB 梯度光这三个就 98GB 了。这就是为什么全参微调 7B 模型通常需要 6-8 张 A100 80GB单张 32GB 在数学上就不够。核心结论全参数微调对你手里的单卡来说几乎是不可能的这不是调参技巧问题而是显存账面上的硬约束。LoRA 和 QLoRA 能跑起来本质上都是在“优化器状态”和“梯度”这两个大头上做文章。1.2 一个常见的认知误区显存是从“加载模型”就开始爆的还有一个让很多人困惑的点明明只是加载模型做推理不训练为什么也 OOM这跟 PyTorch 的显存分配机制有关。PyTorch 在 CUDA 上有自己的缓存分配器它不会用完就立刻释放而是先缓存起来。所以你在nvidia-smi里看到的占用往往比实际需求高很多这是正常的。训练和推理的显存估算方式也完全不同推理场景显存 ≈ 模型权重 × 1.2包含 KV Cache 和中间激活训练场景显存 ≈ 模型权重 × 12 到 20取决于优化器、梯度、激活值的综合开销所以不要拿“推理能跑”来推断“训练也能跑”两者的显存需求差了将近一个数量级。我见过有人在 24GB 卡上推理 70B 模型成功就以为微调 7B 也没问题结果刚跑到第一个 batch 就被 OOM 教做人。2. LoRA 和 QLoRA为什么它们能省下这么多显存2.1 LoRA 的核心原理冻结全部微调极少数LoRALow-Rank Adaptation的核心思路很直接冻结原来的预训练模型权重不参与梯度更新在模型的某些线性层旁边额外插入一份“低秩分解”的小矩阵只训练这部分新增的参数。具体来说假设原来某个线性层的权重矩阵是 W形状为 d × dLoRA 会为它引入两个小矩阵 Ad × r和 Br × d其中 r 远远小于 d比如 7B 模型里 d4096r 可以取 8 或 16。前向传播时输出就是 Wx BAx反传更新时只更新 A 和 BW 一直保持冻结。这样做直接改变了显存账单优化器状态从 56GB 降到大约 0.3GB假设 r8插入的参数量约为 700万 × 8 字节 56MB 量级梯度从 28GB 降到同样量级的 0.1GB 左右模型权重14GB 的 FP16 权重仍然要加载进显存这部分是省不掉的除非用 QLoRA于是 32GB 单卡跑 7B 微调就从“数学上不可能”变成了“绰绰有余”。以我实际跑过的 Llama-2-7B LoRA 为例batch size1、序列长度 512 时显存占用大约 17-19GB32GB 卡上很从容。2.2 QLoRA 省的可不是一点半点QLoRA 是 LoRA 的进一步升级。它不光冻结原模型还把原模型的权重用 4bit 量化方式加载进显存。还是拿 7B 模型来说FP16 权重需要 14GB量化为 4bit 之后只需要大约 3.5GB直接砍掉 75%。QLoRA 的三个关键技术点值得说一下NF4 量化不是简单的 4bit 截断而是基于正态分布设计的一种信息保持型量化格式。它尽量把有限 bit 的表示能力集中在权重密度高的区间比普通均匀量化精度损失小很多。双重量化对量化常数scale再做一次量化进一步压缩内存大约能再省 0.37 倍的内存。分页优化器借用 CPU 内存和 GPU 显存之间的“分页”机制当显存不够时优化器状态自动换出到 CPU 内存避免直接 OOM。我实测过同样 Llama-2-7BQLoRA batch size1 序列长度 512 时显存占用大概在 8-10GB。这意味着你在 32GB 卡上不仅跑得动而且还有很大的余量去调大 batch size、加长序列或者挂更多的并行实验。3. 实操配置与参数选择从 28GB OOM 到 14GB 稳跑3.1 环境准备与基础依赖动手之前先把环境装好。我建议直接用以下组合针对 PyTorch 2.xpython 3.10 torch2.1.0 # 注意选对应 CUDA 版本的安装包 transformers4.31 peft0.5.0 bitsandbytes0.41.0 accelerate0.24.0 datasets2.14.0安装 PyTorch 时建议直接从官方站选择对应的 CUDA 版本我当时用的是 CUDA 12.1 对应的版本。bitsandbytes 在 Linux 下安装最稳定Windows 的话稍微麻烦一点但也可以用预编译的 wheel 包装上。然后跑一段最简单的验证代码确认 CUDA 可用、bitsandbytes 能正常加载import torch import bitsandbytes as bnb print(CUDA available:, torch.cuda.is_available()) print(GPU name:, torch.cuda.get_device_name(0)) print(GPU memory:, torch.cuda.get_device_properties(0).total_memory / 1024**3, GB) print(bnb version:, bnb.__version__)3.2 一个能直接跑的 QLoRA 微调骨架下面这个配置是我经过多次调整后固定下来的一个“保底方案”在 32GB 卡上非常稳from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, BitsAndBytesConfig ) from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training from trl import SFTTrainer model_id meta-llama/Llama-2-7b-hf # 4bit 量化配置 bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_quant_typenf4, bnb_4bit_use_double_quantTrue, bnb_4bit_compute_dtypetorch.float16, ) # 加载模型和分词器 model AutoModelForCausalLM.from_pretrained( model_id, quantization_configbnb_config, device_mapauto, trust_remote_codeTrue, ) tokenizer AutoTokenizer.from_pretrained(model_id) tokenizer.pad_token tokenizer.eos_token # 为 kbit 训练做预处理 model prepare_model_for_kbit_training(model) model.config.use_cache False # 训练时关闭 KV Cache # LoRA 参数 lora_config LoraConfig( r8, lora_alpha16, target_modules[q_proj, k_proj, v_proj, o_proj], lora_dropout0.05, biasnone, task_typeCAUSAL_LM, ) model get_peft_model(model, lora_config) model.print_trainable_parameters() # 输出trainable params: 4,194,304 || all params: 3,540,271,104 || trainable%: 0.1185训练参数部分我建议这样设置training_args TrainingArguments( output_dir./lora-llama2-7b, per_device_train_batch_size1, gradient_accumulation_steps16, num_train_epochs3, learning_rate2e-4, fp16True, logging_steps10, save_strategyepoch, gradient_checkpointingTrue, optimpaged_adamw_8bit, max_grad_norm0.3, warmup_ratio0.03, lr_scheduler_typecosine, )我们来看一下这套配置在 32GB 卡上的实际显存表现。加载完 4bit 模型后大约占 4GB额外加上 LoRA 参数、梯度和优化器状态约 1-2GB再加上激活值和 CUDA 上下文整体稳定在 8-11GB。注意我特意把 batch size 降到了 1然后用 gradient_accumulation_steps16 来模拟更大的 batch。这样做的原因是激活值在显存里是大头batch size1 可以大幅削减这一块。3.3 关键参数到底该怎么选很多人喜欢照抄网上的 LoRA 参数但完全不理解每个参数的作用出了问题也不知道怎么调。这里我拆开讲讲r秩LoRA 矩阵的维度。r 越大能学习的表示能力越强但参数量和显存开销也会上升。有意思的是学术界有不少实验表明 r 不是越大越好过大的 r 反而可能引入噪声。实际做下游任务微调时r8 或 r16 通常就足够了。除非你的任务是领域风格迁移、新语言适配这类需要大容量调整的任务否则不建议超过 32。lora_alpha缩放系数这个参数是用来调节 LoRA 分支影响强度的。最终缩放比例是 alpha/r所以如果你把 r 从 8 调到 16又想保持同样的影响强度需要同步把 alpha 翻倍。常见的设定是让 alpha 是 r 的两倍左右。target_modules目标模块LoRA 要插入到哪些层。对于 LLaMA 架构的模型默认一般选注意力层的 q、k、v、o 四个投影矩阵。如果你想节省显存、加快训练只改 q_proj 和 v_proj 也能跑只是效果可能略差。我这套配置里选的是完整的四个注意模块效果和开销比较均衡。gradient_checkpointing这是显存优化的“第一神器”。开启后前向传播不再保存所有激活值而是在反向传播时重新计算一遍相当于用时间换空间。它能让你在 batch size1 时再省下 30%-50% 的显存代价是训练速度大约下降 20%-30%。不加这个32GB 卡跑 7B 模型没问题但要是换 13B 模型就悬了所以建议直接开着。4. 从 28GB 到 14GB一次实际训练中的显存调优实录4.1 初始状态什么都没调直接 OOM 了我自己的项目里第一次在 A600032GB上尝试微调一个 13B 模型时用的是全参微调的代码我天真地以为 32GB 够用结果加载模型到一半就 OOM 了连训练都没开始。后来换成 LoRA 方案模型能加载了训练也启动了但跑了大概几十步之后显存占用一路飙升到 28GB 左右然后在第 30 步左右 OOM。当时的配置大概是7B 模型FP16 加载LoRA 微调batch size 4sequence length 1024gradient_checkpointing 未开启AdamWFP32 默认显存策略这一步的教训非常明显我忽略了两件事。第一batch size4 seq_len1024 时激活值占据了巨大空间第二FP32 的 AdamW 优化器状态在 7B 模型上也相当夸张。把 lr、目标模块调来调去并不能解决问题方向从一开始就错了。4.2 优化过程一步一步把显存压下来我把优化过程按顺序列出来每一步对显存的改善我都实测过第一步启用 gradient checkpointing只加一行model.gradient_checkpointing_enable()显存直接从 28GB 降到 19GB 左右。这个操作可以说是性价比之王基本上不加白不加。第二步切到 QLoRA 4bit 加载把BitsAndBytesConfig配置好模型从 FP16 加载变成 4bit 加载。这一步让权重部分从 14GB 降到 3.5GB总显存从 19GB 降到 9GB 左右。第三步使用 paged_adamw_8bit 优化器将optimpaged_adamw_8bit优化器状态会使用 8bit 存储并且支持自动换页。这一步省的显存不是特别多但对稳定性很有帮助尤其是 batch size 稍微一大就容易触顶的场景。第四步把 batch size 从 4 降到 1并调大梯度累积步数这一步非常关键。很多人舍不得调低 batch size觉得会损害训练稳定性。但 LoRA 这种微调方式本身参数量很少只有 0.1% 左右对 batch size 的敏感度没有全参微调那么高。我实测batch_size1 grad_accum16和batch_size4 grad_accum4的最终效果几乎一样但显存峰值从 9GB 降到了约 5GB不含模型权重部分。最终的显存占用分布大概是组成部分显存占用4bit 量化模型权重约 3.5GBLoRA 参数 梯度 优化器状态约 1GB激活值batch1, seq512约 2-3GBCUDA 上下文与缓存约 1-2GB总计约 8-9GB这在 32GB 卡上已经相当轻松了。我的最终训练配置就是在 3.2 节里展示的那套跑完整个 3 epoch 都没有再出现 OOM。4.3 如果显存还不够CPU Offload 是最后的手段如果你的显存还是没有余量可以尝试 CPU Offload。PEFT 配合 accelerate 可以把部分优化器状态或模型层放到 CPU 内存上理论上是能跑起来但我个人的观点是这是最后的手段不要一开始就用。因为 CPU 和 GPU 之间的带宽非常有限offload 之后训练速度会直线下降本来 1 小时能跑完的 epoch可能会拖到 3-4 小时。我宁愿把 batch size 调小、序列长度缩短也不愿意用 CPU offload 来换那点显存。另外提一句torch.cuda.empty_cache()在训练中没什么用。它只是把 PyTorch 缓存里“空闲但未归还”的显存退给 CUDA对当前已占用且活跃的 tensor 一点办法都没有。频繁调用反而会让缓存分配器反复申请释放拖慢训练速度。很多人一看到显存报警就到处塞这个函数实际是在自我安慰。5. 常见 OOM 问题与排查技巧实录5.1 最经典的几个报错场景我在各种群里看到最多的微调 OOM 问题其实高度相似。这里整理成速查表大家可以直接对照症状可能原因解决方案加载模型时就 OOM模型权重太大 / device_map 配置不当改用 4bit/8bit加载指定device_mapauto跑几步后 OOM激活值增长 / batch size 偏大开 gradient checkpointing调小 batch size开启 QLoRA 后报 bitsandbytes 错误版本不匹配 / CPU 不支持升级 bitsandbytes确认 CUDA 驱动版本加了 gradient checkpointing 后报错可能和某些模型/量化方式冲突检查 transformers 和 PEFT 版本序列化保存时重新加载验证batch size1 仍然 OOM序列长度太长 / 模型结构复杂截断序列检查是否把 LoRA 错误地应用到了所有层出现“CUDA out of memory”但 nvidia-smi 还有显存PyTorch 缓存分配器碎片化调小 batch、重启进程不用空缓存函数硬撑5.2 QLoRA 训练中一个隐蔽的陷阱错误的可训练参数我第一次使用prepare_model_for_kbit_training时有一个非常隐蔽的问题这个方法会为量化模型做一次“反量化”的准备工作让某些层重新变成可训练状态。如果后面没有正确配置 LoRA 的 target_modules很容易出现“你以为在跑 LoRA实际上跑了一大半全参训练”的情况。怎么看自己是不是中招了用model.print_trainable_parameters()打印一下。如果 trainable params 的数量级是百万/千万级别说明 LoRA 正常工作如果是几十亿级别说明你的设置有问题赶紧停掉检查。还有一次我把 target_modules 写成了所有的nn.Linear层包括 embedding 输出层、MLP 层和 attention 层全部打上 LoRA。结果 trainable params 一下子增加了好几倍显存也直接涨了 2GB。有些人是故意这样做的为了更好效果但对大多数人来说默认的 q/k/v/o 就够用没必要为一点效果增益承担更多显存压力。5.3 实验管理一次只改一个变量这个建议听起来太简单了但真的是我踩过很多坑之后才知道的。调优显存的时候一次只改一个变量比如这次只把 batch size 从 2 改成 1下次再开 gradient checkpointing。看到网上很多人优化到一半就乱了同时改了量化精度、改了优化器、改了 batch size结果 OOM 解决了但完全不知道是哪个改动起了作用。等到换新数据集时问题又回来了重新调试又是一顿乱试。我自己一般会开一个表格记录每次实验的配置和肉眼观察到的显存峰值实验编号batch size量化优化器checkpointing显存峰值备注14FP16AdamW关28GBOOM24FP16AdamW开19GB稳定314bitpaged_8bit开9GB推荐5.4 关于训练速度和显存的取舍不是越省越好显存优化做得越极致训练速度往往越慢。QLoRA 的 4bit 加载本身就会比 8bit 慢 10%-20%再加 gradient checkpointing 又慢 20%-30%。如果你的数据量不大几千条样本、训练轮数少这些时间成本完全能接受。但如果你要跑几万条数据我建议不要一味追求最低显存而是在“稳定跑不 OOM”和“训练速度可接受”之间找一个平衡点。比如 32GB 卡跑 7B 模型如果显存占用压到 9GB 左右还有很大余量我可能会尝试把 batch size 调高到 4让训练速度提升一些。还有一个很实际的经验微调之前先跑一个 10-20 步的小实验肉眼观察显存曲线是不是稳定。如果这个阶段不 OOM后面基本也不会 OOM。不要直接甩一个完整训练任务不然跑了两小时突然 OOM白白浪费时间。6. 工具选型与命令备忘除了 PEFT还有什么能用6.1 现代工具链的取舍现在做 LoRA/QLoRA 微调主流的方案有peft transformers、Unsloth、Axolotl、LLaMA-Factory这几类。我的建议是如果是自定义数据处理和训练流程直接用 PEFT灵活度最高如果只是想快速验证一个开源模型的微调效果用 Unsloth 或 LLaMA-Factory 非常省事。Unsloth 的核心优势在显存优化和训练加速上做了很多底层优化据官方测试最高可以减少显存占用 80%训练速度提升 2-3 倍。我实测过它确实比原生 PEFT 快一些而且显存峰值更低。但它的定制性和社区生态不如 PEFT 成熟碰到奇怪的 bug 时排查成本也高。6.2 把一个可直接运行的 Unsloth 脚本贴在这里我用 Unsloth 微调的感觉是“真香”哪怕你完全不懂底层优化它都能帮你开好默认配置。下面的脚本可以一步步跟着做任务就是微调一个对话模型from unsloth import FastLanguageModel import torch from trl import SFTTrainer from transformers import TrainingArguments from datasets import load_dataset max_seq_length 2048 dtype None load_in_4bit True model, tokenizer FastLanguageModel.from_pretrained( model_nameunsloth/llama-2-7b-bnb-4bit, max_seq_lengthmax_seq_length, dtypedtype, load_in_4bitload_in_4bit, ) model FastLanguageModel.get_peft_model( model, r16, target_modules[ q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj, ], lora_alpha16, lora_dropout0, biasnone, use_gradient_checkpointingunsloth, # 这一行比普通 gradient checkpointing 更快更省 random_state3407, use_rsloraFalse, loftq_configNone, ) dataset load_dataset(json, data_filesyour_data.jsonl)[train] trainer SFTTrainer( modelmodel, tokenizertokenizer, train_datasetdataset, dataset_text_fieldtext, max_seq_lengthmax_seq_length, dataset_num_proc4, packingFalse, argsTrainingArguments( per_device_train_batch_size2, gradient_accumulation_steps4, warmup_steps5, max_steps60, learning_rate2e-4, fp16not torch.cuda.is_bf16_supported(), bf16torch.cuda.is_bf16_supported(), logging_steps1, optimadamw_8bit, weight_decay0.01, lr_scheduler_typelinear, seed3407, output_diroutputs, ), ) trainer.train()注意这段代码用的是“unsloth”的 gradient checkpointing比标准实现更快而且显存占用更低。我在 32GB 卡上跑这个配置时显存峰值大概 13GB 左右。如果想把 batch size 再拉高也问题不大。6.3 监控显存状态别等到 OOM 才打开 nvidia-smi训练前和训练中都需要关注显存状态但很多人只是在 OOM 之后看一眼nvidia-smi。我建议用如下方式连续监控watch -n 1 nvidia-smi这样每秒刷新一次能够实时看到显存波动。尤其当你调大 batch size 或者序列长度时先跑 5 分钟看看峰值是否稳定如果显存曲线一直在涨说明某个环节在持续累积可能是 KV Cache、也可能是输出 logits 太长要赶紧排查。如果你是远程开发环境可以用gpustat更友好地查看pip install gpustat gpustat --watch另外补充一点PyTorch 2.x 的torch.profiler可以看到每个操作的内存使用明细。遇到优化半天也找不到原因的 OOM可以用它来精确定位到底是哪个 op 分配的显存最多这个手段比瞎猜高效得多。7. 近期微调项目中踩过的三个坑版本、序列长度与设备分配7.1 版本匹配是最大的隐形杀手很多 OOM 其实不是显存问题而是版本不匹配导致的“假 OOM”或直接崩溃。比如 transformers 和 PEFT 版本差异过大时from_pretrained加载 4bit 模型可能就会报一些奇怪的 CUDA 错误或者量化加载后参数形状对不上一跑就崩。我的经验是如果哪天换了大版本比如 transformers 从 4.30 升到 4.40先单独跑一遍from_pretrainedprint(model)确认能加载、能推理了再开始微调。升级前最好把原来的环境用 conda 做一个备份别一上来就pip install --upgrade全给覆盖了。7.2 序列长度比 batch size 更容易被忽略显存优化的直觉是“先砍 batch size”其实序列长度同样是激活值的决定性因素。每一条样本的激活值大小和序列长度几乎线性相关。如果你用 4096 长度的序列去微调激活值会比 512 长度大近十倍。很多人开着默认参数加载模型其实默认 max_seq_length 可能是 2048 甚至 4096这会让显存压力大幅上升。解决方案有两个一是训练之前先确认你的任务真的需要那么长的上下文二是如果你的数据确实长可以考虑做样本截断或分块处理。我见过一个做长文档摘要的项目把 4096 的序列截断到 2048 之后效果几乎没有下降但显存峰值直接掉了一半。7.3 device_map 配置不当带来的隐性问题在from_pretrained中设置device_mapauto通常是最省心的选择它会自动帮你在多个 GPU 或 CPU 之间分配模型。但你如果在显存紧张的机器上手动指定了device_mapcuda:0有可能会把模型整个塞到一张卡上导致显存直接打满。而且有些量化模型如果 device_map 没有正确配置反量化过程会把临时张量放在 GPU 上造成显存波动剧烈。我习惯的做法是单卡环境直接用device_mapauto多卡环境再手动规划每一层放到哪个设备。用device_mapauto时可以用model.hf_device_map查看模型的分布情况确认所有层都落在 GPU 上而不是被分到 CPU。print(model.hf_device_map) # 比如输出{model.embed_tokens: 0, model.layers.0: 0, ...} # 如果出现 -1说明有层被放到了 CPU训练速度会非常慢8. 从 7B 到 13B扩展场景下的显存策略8.1 13B 模型的可行性分析如果你手里只有一张 32GB 卡又想微调 13B 模型那到底能不能跑答案是QLoRA 可以但余量非常有限。13B 模型 4bit 量化后权重大约 7GB加上 LoRA 参数、梯度、优化器状态和激活值实际运行大概需要 13-15GB。前提是 batch size1、sequence length 不要超过 1024、开启 gradient checkpointing、用 paged_adamw_8bit。如果不小心把 batch size 调到 2或者序列长度拉到 2048很容易再涨 5-6GB 就到顶了。我的建议是13B 模型在 32GB 卡上跑 QLoRA 是可行的但训练过程会比较娇气适合短任务、小数据集验证。如果你的目标是正式的、长时间的训练任务还是想办法上多卡或者更大的显存更稳妥。8.2 多卡并行与梯度累积的配合32GB 单卡的另一种“扩容”方案是多卡并行。现在很多人手上可能有两张甚至四张 24GB/32GB 卡但不会配置仍然只在一张上跑。其实用 DeepSpeed Stage 3 或简单的数据并行可以轻松把 7B 模型的训练扩展到多张卡上。在多卡配置下我推荐的组合是per_device_train_batch_size1gradient_accumulation_steps适当调小 DeepSpeed ZeRO Stage 3deepspeed --num_gpus2 train.py \ --deepspeed ds_config.json \ --per_device_train_batch_size 1 \ --gradient_accumulation_steps 8 \ --model_name_or_path meta-llama/Llama-2-13b-hf \ --lora_r 16 \ --lora_alpha 32注意ds_config.json里建议把zero_optimization: {stage: 3}配好同时开启 offload optimizer 到 CPU这样可以进一步缓解显存压力。不过这一节并不是每个刚入门的人都需要如果你的数据量不大单卡 QLoRA 已经够了没必要为了多卡去引入额外的复杂度。9. 实战建议与个人体会微调大模型的显存优化说到底是三个层面的事第一是模型加载层面的量化QLoRA 的 4bit第二是训练策略层面的参数冻结LoRA 的只训练少量参数第三是运行配置层面的梯度重计算、batch size 控制和优化器选择。这三个层面你都照顾到了32GB 单卡跑 7B 甚至 13B 的微调就真的没那么难。我在实际项目中已经把这套方案固定下来30B 以下模型用 QLoRA默认 r8、alpha16、target_modules 覆盖注意力四层开启 gradient checkpointing使用 paged_adamw_8bitbatch size 从 1 开始序列长度按任务需求裁剪。这套组合在大多数 32GB 卡上都能稳定运行出问题的概率非常低。如果你正准备开始第一次微调我最后想给一个具体建议不要直接拿自己的完整数据冲上去。先用 100 条样本、10 步训练跑通整个流程确认显存稳定、损失在下降、保存的 checkpoint 能正常重新加载再切换到全量数据。这个小习惯帮我省下的时间远比我在显存优化上花的时间多得多。当你把“能跑起来”变成一种肌肉记忆再去调参、优化、加速思路会清晰很多。