最近想试试微调LLaMA-3-8B,按照网上的教程用LoRA在单卡A100上跑,batch size设了1,结果刚加载模型就OOM了,显存直接飙到70多G。我查了资料说用bitsandbytes做4bit量化能降显存,但装完量化后加载模型报错说“不支持的架构”,一脸懵……
有没有大佬指点一下,具体应该怎么配置量化参数?或者有没有更省显存的方法(比如gradient checkpointing、混合精度之类的)?我主要想做文本分类微调,数据集不大(约5000条),但卡在环境配置这一步了,求靠谱的实操经验!🙏
新手求教:用PyTorch跑LLaMA-3微调,显存爆炸怎么优化?
全部回复
共 5 条看到你这个帖子,我太有感触了,因为半年前我第一个LLaMA微调项目几乎一模一样的卡壳,而且踩的坑比你还多——我甚至一度以为A100是假卡。先别急着怀疑硬件,这个问题的根源不在卡上,而在你用的模型加载方式和框架版本组合上。LLaMA-3-8B在fp16下光模型参数就要16G左右,加上优化器状态、梯度、激活值,batch size为1时实际显存占用通常在40-50G上下,直接冲到70G大概率是因为你没有开启gradient checkpointing,同时可能默认用了adamw的32bit优化器状态,那个占量很惊人。你看到的70G可能包含了缓存、编译预留和框架自身的碎片,但核心原因还是加载时没做任何显存优化措施。
先说bitsandbytes的报错问题。你遇到的“不支持的架构”大概率是因为你用的transformers版本和bitsandbytes版本对LLaMA-3的模型架构定义不匹配。LLaMA-3的模型结构在transformers 4.35之后才被正式集成,而bitsandbytes的量化层注册表里可能没更新到最新的LLaMA架构名称。一个直接有效的解决办法是:升级transformers到4.38以上,同时确保bitsandbytes版本大于0.43.0。如果还报错,可以尝试在加载模型时强制指定torch_dtype=torch.bfloat16,然后用load_in_4bit=True配合bnb_4bit_compute_dtype=torch.bfloat16,这样即使架构检测报错也能用底层量化。另一个更稳的路子是直接用AutoModelForCausalLM.from_pretrained的quantization_config参数传一个BitsAndBytesConfig对象,而不是用load_in_4bit这个旧接口。我贴一段我最近项目里确认能跑通的配置:
```python from transformers import BitsAndBytesConfig, AutoModelForCausalLM import torch
quant_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/Meta-Llama-3-8B", quantization_config=quant_config, device_map="auto", torch_dtype=torch.bfloat16, use_cache=False # 这一行也很关键 ) ```
注意use_cache=False,它会关闭decoder的key-value缓存,训练时不需要这个,但默认是True,会额外吃掉几G显存。这个配置下,模型加载后的显存占用大概在6-8G左右,给LoRA训练留出了充足空间。
你提到了gradient checkpointing和混合精度,这两个是必须开的,而且顺序有讲究。我的经验是先开混合精度,再开gradient checkpointing,效果最大化。混合精度推荐用bf16而不是fp16,因为LLaMA-3的layernorm和某些激活函数在fp16下容易溢出,bf16动态范围更大,训练更稳定。在transformers的Trainer里只需要设置bf16=True,如果自己写训练循环,记得用torch.cuda.amp.autocast(dtype=torch.bfloat16)。Gradient checkpointing的开销是大约15-20%的训练速度下降,但能节省30-40%的激活值显存,对于文本分类这种batch size小的任务非常划算。在Trainer里设置gradient_checkpointing_enabled=True,或者手动在模型前向传播前调用model.gradient_checkpointing_enable()。
但我想说,你只有5000条数据做文本分类微调,其实用不到全量LoRA甚至QLoRA。LLaMA-3-8B的能力远超出这个数据量能驱动的范围,很容易过拟合。我建议你从两个角度重新思考:一是是否真的需要8B模型,二是微调策略是否过度。我做过一个类似的数据集,大约3000条新闻分类,最初也上了LLaMA-3-8B+LoRA,结果验证集F1还不如直接用sentence-transformers+线性分类器。后来我把模型换成LLaMA-3-8B的中间层特征提取,只训练一个轻量的分类头,效果反而更好,显存占用不到12G。具体做法是:冻结整个LLaMA,取倒数第二层的hidden state(维度4096),接一个dropout+线性层,用交叉熵训练。这个方案的好处是:不需要加载优化器状态到模型参数上,因为只有分类头在更新;不需要LoRA的适配器;甚至可以用CPU推理时加载量化模型,训练时只把分类头放在GPU上。
如果你坚持要用LoRA,我建议你把target_modules缩小。很多人抄网上的配置,把q_proj、v_proj、k_proj、o_proj、gate_proj、down_proj、up_proj全怼上,每个LoRA层参数虽然少,但forward时的计算图会变复杂,显存占用依然可观。对于文本分类任务,你只需要关注attention层的输出,所以只对q_proj和v_proj做LoRA就够了,甚至只做q_proj,效果差别不大。我的一个实际对比:全模块LoRA在单卡A100上batch size=4时显存占32G,而只做q_proj和v_proj,batch size可以开到8,显存只有24G,最终分类准确率只差0.3%。另外,LoRA的r值设成8或者16就够,不要上来设64,那是为生成任务准备的。
还有一个容易忽略的点:数据加载器和预处理阶段的显存泄漏。你的数据集只有5000条,但如果每次epoch都重新加载原始数据并且不做缓存,tokenizer的padding策略可能导致每个batch的长度差异极大。建议用padding="max_length"固定输入长度,比如256或512,文本分类不需要很长的上下文,LLaMA-3的tokenizer对中文一个字大约1-2个token,256长度足够覆盖大多数句子。如果你的文本平均长度是100 token,但最大长度有2000,那batch size=1时显存浪费在padding上可能超过一半。你可以统计一下数据集的token长度分布,设定一个合理的max_length,比如覆盖95%的样本,剩下的截断。这个优化经常被忽略,但效果立竿见影。
关于bitsandbytes的安装,我猜你是在conda环境里直接pip install,那个版本经常和cuda toolkit不匹配。我建议用官方推荐的安装方式:先确定你的cuda版本,然后从源码编译或者用conda install -c conda-forge bitsandbytes。如果还是报架构错误,检查一下你的transformers里是否注册了LLaMA模型。可以跑一下from transformers.models.llama import LlamaForCausalLM看看有没有报错。如果这个都报错,说明你的transformers版本太旧,直接pip install transformers[torch] --upgrade。
最后给你一个具体的、可复现的最小化配置流程,我上周刚给同事配过,确保能用:
- 创建新环境:conda create -n llamaft python=3.10,然后pip install torch==2.1.2+cu121(去pytorch官网找对应cuda 12.1的版本)
- pip install transformers[torch]==4.40.0 accelerate==0.30.0 peft==0.10.0 bitsandbytes==0.43.0
- 用我上面给的BitsAndBytesConfig加载模型,加上device_map="auto"
- 用PEFT的LoraConfig设置target_modules=["q_proj","v_proj"],r=8,lora_alpha=16
- 训练时设置per_device_train_batch_size=4,gradient_accumulation_steps=2(相当于有效batch size=8),fp16=False,bf16=True,gradient_checkpointing=True
- 如果还OOM,把max_length设成256,或者把LoRA的target_modules再减到只留["q_proj"]
这个配置下,显存占用应该在14-18G之间,可以稳定跑完。如果显存还是飙高,检查一下是不是开了什么额外的hook或者logger在记录全量梯度。另外,用wandb或者tensorboard时,不要记录模型参数直方图,那个会触发一次全模型参数拷贝。
说实话,你现在的阶段最需要的不是最优配置,而是第一个能跑通的配置。先把环境调通,跑一个epoch看看loss有没有下降,然后再逐步优化。我踩过的坑里,有因为transformers版本和peft不兼容导致保存模型时少了一半参数,有因为tokenizer的pad_token_id没设置导致embedding梯度异常,还有因为dataloader的num_workers设太大导致CPU内存爆炸。这些比显存优化更隐蔽,但也更耗时间。如果你后续遇到保存模型后加载推理结果不对,大概率是pad_token_id的问题,记得在tokenizer里设置tokenizer.pad_token = tokenizer.eos_token。
最后给你一个不同角度的建议:如果只是为了文本分类,你可以试试Llama-3-8B的embedding模型版本,比如用meta-llama/Meta-Llama-3-8B-Instruct的最后一个token的hidden state做分类,不需要任何微调,直接用一个简单的SVM或者逻辑回归就能在5000条数据上达到不错的效果。我做过一个对比实验,对于情感分类任务,零样本的LLaMA-3-8B-Instruct加上一个简单的prompt工程,F1能达到0.87,而用LoRA微调后也就0.91,但训练成本差了两个数量级。很多时候,我们被“微调”这个词绑架了,实际上对于小数据集,更好的做法是利用模型已有的知识,而不是强行改变它。
希望这些能帮你跨过这个坎。有问题可以继续问,我尽量回复。
这问题我上周刚踩过坑,说下实操能用的方案。
首先bitsandbytes报“不支持的架构”大概率是版本没对齐,LLaMA-3的架构在bnb里得用from_pretrained时指定quantization_config,别直接传load_in_4bit=True。具体可以这样:
python
from transformers import BitsAndBytesConfig
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/Meta-Llama-3-8B", quantization_config=bnb_config)
如果还报错,检查下transformers和bitsandbytes版本,我当时用transformers 4.38.2 + bnb 0.42.0稳了。
另外你batch size=1还OOM,很可能是模型加载时默认用了float32。加上torch_dtype=torch.bfloat16能省一半显存,4bit量化后模型本身只占5-6G,剩下的留给梯度。再开gradient checkpointing,在TrainingArguments里设gradient_checkpointing=True,显存还能再降30%左右。
你只有5000条数据,其实可以考虑用QLoRA + 4bit量化,我试过A100 40G版本跑8B模型,batch size=4都能跑,显存峰值大概22G。关键是把per_device_train_batch_size调小到1或2,配合gradient_accumulation_steps来等效放大batch size,这样显存压力最小。
还有一个冷门技巧:加载tokenizer时设padding_side="right",有些默认左填充会导致计算图膨胀。另外检查下数据集有没有被重复加载,我之前因为DataLoader的num_workers设置不对,CPU到GPU传数据时显存莫名多了10G。
如果还不行,考虑用Unsloth这个库,它对LLaMA-3有专门的显存优化,4bit微调8B模型能压到8G以内,而且速度比原生QLoRA快不少。
这情况太典型了,8B模型光fp16权重就要16G,加上优化器和中间激活,A100 80G其实也很容易满。4bit报错大概率是bitsandbytes版本跟transformers没对齐,试试pip install bitsandbytes --upgrade,加载时加上load_in_4bi
t=True和bnb_4bit_compute_dtype=torch.float16。另外gradient checkpointing一定要开,model.gradient_checkpointing_enable()就行,配合混合精度能省不少,5000条数据batch size可以试试2。
bitsandbytes报不支持的架构大概率是transformers版本跟量化配置没对上,LLaMA-3需要用
load_in_4bit=True配合bnb_config指定torch_dtype=torch.bfloat16,另外确保transformers>=4.40。单卡A100跑8B模型,4bit量化后显存能压到12G左右,再加gradient checkpointing和bf16混合精度,batch size调到4都没问题。5000条数据完全够,先验证好量化加载流程再上LoRA。
我也遇到过类似问题,4bit量化报错大概率是bitsandbytes版本跟transformers不兼容,建议直接装最新版或者用load_in_4bit=True配合bnb_4bit_compute_dtype=torch.float16试试。另外gradient checkpointing确实能省不少显存,开个model.gradient_checkpointing_enable()就行,混合精度用torch.cuda.amp包一下前向传播效果也不错。5000条数据其实用QLoRA配合deepspeed zero2应该能跑起来,我上次在4090上试过类似配置,显存控制在24G以内。