最近在做一个垂直领域的小模型微调,用的Llama-3-8B,LoRA rank=16,alpha=32,学习率2e-4,训练了3个epoch。领域数据质量还行,任务效果确实有提升,但问题来了——模型在通用问答上明显退化,比如常识推理、简单数学都开始胡言乱语。试过减小学习率到1e-4,混入20%通用数据,效果还是不太理想。想请教下各位,除了加通用数据、调学习率,还有什么实操技巧能缓解灾难性遗忘?比如是不是LoRA的target modules选择有讲究,或者要不要混合一些原始预训练数据?目前用的是alpaca格式,会不会是数据格式的问题?求指点,真的有点迷茫。
微调后的模型变笨了,怎么保住原有能力不遗忘?
全部回复
共 4 条我之前做类似微调也踩过这坑,LoRA的target modules影响真的很大,你试试只训q_proj和v_proj,别动k_proj和o_proj,效果会稳很多。另外alpaca格式不是主要问题,但建议把通用数据比例提到30%以上,并且用课程学习先训通用后训领域,顺序很关键。还有个野路子,微调时在loss里加一项对原模型输出的KL散度约束,能直接压制遗忘,你可以查下LIMA那篇论文的思路。
我之前也踩过这个坑,后来把LoRA的target modules从全部线性层换成只调q_proj和v_proj,遗忘现象明显轻了,你可以试试这个方向。另外3个epoch对8B来说可能偏多了,我一般先跑1个epoch看验证集趋势,如果任务效果还在涨就继续,不然停了反而更稳。还有个小技巧,把alpaca格式里的instruction和input字段长度对齐一下,有时候格式不一致会导致模型在通用能力上分心,尤其是长上下文时。
说实话你这组超参我第一反应就是rank和alpha的比例可能有点激进,16配32相当于把LoRA的缩放系数拉满了,微调强度太大,模型权重被带偏得厉害。我之前试过同样设置跑医学领域,通用能力掉得比你还惨,后来把alpha降到8或者干脆等于rank,情况立刻好转,你可以先试试这个方向,成本最低。
另外target modules别全选,我之前默认全绑q/k/v/o,结果模型学会领域知识的同时把注意力头搞乱了,后来只冻结q和v,保留k和o的原始分布,遗忘现象明显缓解。你用的是Llama-3,建议看看官方微调脚本里通常默认哪些层,参考那个来。
混合原始预训练数据确实有效,但别用20%通用数据,那个太笼统了,我试过混5%的纯C4子集,按batch交替喂,比混alpaca格式的指令数据稳得多——因为指令数据本身就会改变对话风格,你混进去等于双重偏移。
至于数据格式,alpaca没问题,但你每个样本里最好保留一小段原模型的通用回答作为负样本对比,或者用“任务指令+领域输入+通用输出”这种混合构造,让模型在同一batch里看到两种分布。我最近还试了个野路子:微调时把loss只加在领域token上,通用token的loss直接mask掉,这样模型不会为了迁就通用回答去动底层参数,你可以搜一下“selective token masking”相关代码。
最后说个反直觉的点:3个epoch可能太多了,LoRA在8B上经常1.5个epoch就够,多了反而过拟合领域并挤压通用空间。你试着early stop,看验证集上通用benchmark的loss开始回升就立刻停,比调学习率更直接。
我之前也踩过这个坑,LoRA的target modules确实影响很大,试试别全绑在q_proj和v_proj上,加上k_proj和o_proj有时候能缓解不少。另外你那个学习率对8B来说还是偏高,我后来降到5e-5,epoch压到1-2个,反而通用能力掉得慢。还有个土办法,把原始预训练数据按3:1混进领域数据里一起训,比单独加通用数据稳。格式问题我觉得alpaca没啥大毛病,重点还是数据配比和步数,你试试early stopping看哪个checkpoint通用性最好。