最近想用LoRA微调一个LLaMA-3-8B做代码补全,数据集大概几万条。我用的是QLoRA,量化到4bit,但batch size设为4就OOM了(单卡A100 80G)。我看有些教程说可以跑16甚至32的batch,是我哪里姿势不对吗?还是说必须要用gradient checkpointing?我用的transformers+peft,序列长度设了2048。另外想问一下,微调代码模型和微调对话模型在超参上有什么大的区别吗?新手入坑,求大佬指点。
用LoRA微调LLaMA-3,显存总爆,是我的batch size设太大了吗?
全部回复
共 188 条2048序列长度下batch size 4爆80G显存,大概率是gradient checkpointing没开,LLaMA-3的vocab有128k,logits那块的显存开销比你想的猛很多。另外检查下有没有把optimizer设成paged_adamw_8bit,用普通adamw的话优化器状态也会吃掉一大块。代码补全任务里packing很重要,能把短样本拼满2048,不然padding浪费的显存很冤。至于代码和对话模型的超参区别,代码任务一般lr可以低一点,因为模式更固定,对话模型反而需要稍高的lr来适配多样指令风格。
batch size 4就OOM确实有点夸张,A100 80G跑QLoRA按理说不至于这么紧张。你序列长度2048加上几万条代码数据,如果没开gradient checkpointing,激活值占的显存会非常猛,尤其是LLaMA-3的vocab有128k,logits那块就吃掉不少。建议先把这个打开试试,基本能省一半以上的激活显存。另外检查一下是不是把optimizer设成了adamw的全量版本,QLoRA一般用paged_adamw_8bit会好很多。还有个容易忽略的点,peft的target_modules如果设得太宽,比如把gate、up、down全加上,LoRA参数量和梯度也会涨,显存跟着上去。代码补全和对话微调确实有区别,代码那边通常packing更激进、序列更长,学习率可以稍低一点,epoch也不用太多,不然容易过拟合到特定仓库风格。你可以先拿几百条数据跑个step看看显存曲线,比盲调batch size靠谱多了。
2048的序列长度下batch size 4确实容易炸,尤其你还没开gradient checkpointing,激活值占的大头。QLoRA省的是权重显存,激活这块基本没帮上忙,先把gradient checkpointing打开试试,能省不少。另外可以看下peft的target_modules是不是设太全了,只挂q_proj和v_proj会轻很多。代码补全和对话微调差别还挺大的,代码那边一般学习率低一点、epoch少一点,因为重复模式多容易过拟合。
QLoRA下batch size 4就OOM有点不太对劲,A100 80G不该这么脆。你先把gradient checkpointing打开试试,这玩意儿对显存占用影响巨大,不开的话激活值能吃掉一大半显存。另外序列长度2048也不算短,几万条代码数据如果单条偏长,实际显存峰值会比你想的高不少,可以先用max_seq_len 1024跑通再往上加。代码补全和对话微调确实有区别,代码任务一般学习率可以稍低一点,epoch也别太多,不然容易过拟合到具体仓库风格上。
序列长度2048确实吃显存,试试降到512再开梯度检查点,batch能上去不少。
2048的序列长度下batch size 4还OOM,单卡80G确实有点不对劲,大概率是别的地方在吃显存。你可以先检查一下有没有把optimizer的显存开销算进去,QLoRA虽然权重压到4bit,但LoRA的adapter参数、梯度以及优化器状态在Adam下还是挺可观的。另外transformers默认可能会把整个模型load到GPU上再做量化,中间峰值会飙得很高,试试用device_map或者bnb的4bit配置里开double quant,能省不少。gradient checkpointing确实值得开,代价是慢一点但换来的显存空间很值,尤其你序列这么长。还有个坑是数据处理里的padding,如果没设max_length截断或者padding到最长样本,实际序列可能远超2048,那显存当然炸。代码补全和对话微调在超参上区别不大,主要是数据格式和loss mask的处理,代码任务一般不需要对prompt部分算loss,只算completion部分,这个细节容易被忽略但影响挺大。
batch size 4 在 2048 序列长度下 OOM 挺正常的,QLoRA 省的是模型权重显存,激活值该占还是占。gradient checkpointing 基本必开,能帮你把激活显存压下来一大截,再配合 gradient accumulation 把有效 batch 凑上去就行。代码补全和对话微调差别不小,代码那边序列更长、loss 更集中在局部 token,学习率通常可以低一点,epoch 也别太多,不然容易过拟合。你可以先把 per_device_batch 降到 1 或 2,开 checkpointing 试试,看显存曲线再慢慢加。
A100 80G跑4bit QLoRA,batch 4就爆确实有点奇怪,大概率是序列长度2048加代码数据本身token密度高,activation占了大头。gradient checkpointing基本是必开的,能省不少显存,就是慢个20%左右。另外检查下有没有把optimizer设成paged_adamw_8bit,还有LoRA的target modules别只挂q v,代码任务一般q k v o都加上效果更好。代码微调和对话微调差别挺大,代码通常学习率要低一点、epoch可以多跑几轮,因为代码模式更依赖精确的token分布。