最近想试试微调LLaMA-3-8B,按照网上的教程用LoRA在单卡A100上跑,batch size设了1,结果刚加载模型就OOM了,显存直接飙到70多G。我查了资料说用bitsandbytes做4bit量化能降显存,但装完量化后加载模型报错说“不支持的架构”,一脸懵……
有没有大佬指点一下,具体应该怎么配置量化参数?或者有没有更省显存的方法(比如gradient checkpointing、混合精度之类的)?我主要想做文本分类微调,数据集不大(约5000条),但卡在环境配置这一步了,求靠谱的实操经验!🙏
新手求教:用PyTorch跑LLaMA-3微调,显存爆炸怎么优化?
全部回复
共 156 条看到你这个帖子,我太有感触了,因为半年前我第一个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。
试试用unsloth库加载LLaMA-3,自带4bit优化和梯度检查点,我8G显存都能跑起来。
你遇到的“不支持的架构”报错大概率是bitsandbytes版本和transformers不匹配,建议换成4.38以上版本,或者直接用AutoGPTQ做4bit量化会更稳。另外梯度检查点真的很有用,模型加载完先跑一次forward看显存占用,再配合混合精度fp16能省不少。如果还是爆,试试把LoRA的r值降到4或者8,甚至可以考虑用adalora自动分配秩,5000条数据完全够用。
试试把加载时的device_map设为auto,再加个gradient checkpointing,瞬间能省下10G显存。
我也踩过这个坑,bitsandbytes对LLaMA-3的支持确实有点问题,试试在加载模型时加上trust_remote_code=True和device_map="auto",或者换用transformers最新版配合load_in_4bit=True。另外你batch size已经1了,可以再开gradient checkpointing和混合精度fp16,A100上能省不少显存。5000条数据其实梯度累积步数调大点也行,不用急着上大batch。
刚踩过这坑,4bit量化要指定bnb_4bit_compute_dtype为float16,再开gradient checkpointing和混合精度就能跑起来了。
这问题我也踩过坑,LLaMA-3-8B用4bit量化确实能降显存,但bitsandbytes版本要跟transformers匹配,不然容易报架构错误。建议试试transformers的load_in_4bit=True参数,配合BnbConfig指定quant_type="nf4",我这样能压到20G以内。另外gradient checkpointing和混合精度fp16是必开的,batch size 1跑5000条数据完全够用,别被那些花里胡哨的参数吓到。
刚踩过同样的坑,建议用transformers的AutoModelForCausalLM.from_pretrained直接传load_in_4bit=True,配合bnb_4bit_compute_dtype=torch.float16,bitsandbytes报错大概率是版本没对齐,我换成0.41.3就好了。另外梯度检查点必须开,再加个torch.compile能省不少显存,5000条数据LoRA跑起来完全够用。
试试用QLoRA加4bit量化,bitsandbytes报错可能是版本不匹配,换个0.41.0版本就行。
A100 80G都能OOM确实有点离谱,估计是加载模型时默认用了float32,或者transformers库版本没对齐导致参数没正确量化。4bit量化报“不支持的架构”大概率是因为bitsandbytes还没适配LLaMA-3的某些层,你可以试试把load_in_4bit=True改成bnb_4bit_compute_dtype=torch.float16,再检查下transformers和accelerate的版本是不是最新的。另外gradient checkpointing和混合精度是必开的,尤其是fp16训练能直接砍掉一半显存,batch size=1还撑不住的话,可以考虑用deepspeed的zero stage2或者offload optimizer到CPU,虽然慢点但至少能跑起来。文本分类微调其实用QLoRA更省,4bit量化+LoRA+r16,单卡A100跑5000条数据应该毫无压力,记得把gradient accumulation steps设到4或8来模拟大batch。还有个小技巧:加载分词器时设置trust_remote_code=True,有些模型依赖自定义代码,不设会报奇奇怪怪的错。
同款踩坑,LLaMA-3-8B用bitsandbytes一定要指定bnb_4bit_compute_dtype=torch.bfloat16,不然会报架构不兼容。另外梯度检查点必开,配合混合精度能把单卡A100的显存压到25G左右。你5000条数据的话,其实可以试试用lora+梯度累积,batch size设2再累积4步,效果差不多还更稳。
刚跑70G显存不太正常啊,检查下是不是没指定device_map="auto",这样模型会均匀分配到多个卡上,哪怕只有一张也要加这个参数。量化报错大概率是bitsandbytes版本和transformers没对齐,建议直接用transformers的4bit加载,设置bnb_4bit_compute_dtype=torch.float16试试。另外gradient checkpointing肯定要开,配合混合精度fp16能再省点,5000条数据其实单卡A100用LoRA+4bit足够了,别被初始报错吓到。
用4bit量化要设好bnb_4bit_compute_dtype和load_in_4bit,另外开gradient checkpointing能省不少显存。
刚上手就踩这个坑挺正常的,LLaMA-3-8B全精度加载确实要70多G,单卡A100也不够吃。4bit量化方向是对的,但bitsandbytes对LLaMA-3的支持有点坑,尤其是新版本架构里有些层兼容性不好,你可以试试把load_in_4bit=True换成bnb_4bit_compute_dtype=torch.float16,然后指定一下bnb_4bit_quant_type="nf4",很多时候报“不支持的架构”是因为默认的量化配置没对齐。另外gradient checkpointing一定要开,model.gradient_checkpointing_enable()这一行代码能省不少显存,混合精度用torch.autocast加torch.cuda.amp.GradScaler组合也建议加上。还有个取巧的办法,如果数据集只有5000条,可以先试试用HuggingFace的PEFT库配合QLoRA,加载时用device_map="auto"让模型自动分配到CPU和GPU,这样即便显存不够也能慢慢跑起来。我自己的经验是先把模型用bitsandbytes量化到4bit,然后LoRA的r设成8,alpha设成16,batch size设1,再开梯度累积,基本能在24GB显存的卡上跑起来,A100应该更轻松。报错的话可以把bitsandbytes版本降到0.41.3试试,那个版本对LLaMA系列支持最稳。
我也踩过这个坑,A100 80G按理说跑8B模型加LoRA应该是够的,但直接加载原模型确实会爆,因为光模型权重就占16G左右,加上优化器状态和中间激活值,70G不奇怪。4bit量化方向是对的,但bitsandbytes那个报错大概率是transformers版本不匹配,你可以试试升级到4.38以上,或者直接用AutoModelForCausalLM.from_pretrained(..., load_in_4bit=True)这种原生写法,别手动调config。另外gradient checkpointing一定要开,这玩意儿能省一半显存,具体就是model.gradient_checkpointing_enable(),配合混合精度fp16,batch size 1应该能压到40G以内。还有个骚操作是先用CPU offload把优化器状态放内存里,虽然慢点但能跑起来。5000条数据的话其实不用全参数微调,LoRA rank设8或者16就够了,再不行就把target_modules只选query和value,别全加。实在不行试试QLoRA,那套组合拳下来8B模型能压到10G以下。
bitsandbytes那个报错大概率是transformers版本没对齐,LLaMA-3的架构在最新版里才支持4bit,试试升级到4.40+看看。另外你A100 70多G爆显存有点奇怪,可能LoRA配置里target_modules没指定对,或者加载时没调device_map="auto"。梯度检查点确实能省点,但你这情况先解决量化再说,或者换QLoRA官方给的示例配置直接跑,他们文档里有个现成的8B微调脚本。
8B模型直接加载确实吃显存,4bit量化是正解,但bitsandbytes对LLaMA-3的支持可能还没完全适配,可以试试transformers的load_in_4bit参数,或者换bnb的4bit配置时加上torch_dtype=torch.float16。另外建议先开gradient checkpointing,再加个混合精度fp16,能省不少,5000条数据batch size开1的话单卡A100应该够用,模型加载前用model.half()也能降点显存。