最近在尝试用LoRA微调Qwen2-7B,让它能根据自然语言描述生成简单的Python脚本。数据集是自己爬的LeetCode题解,大概1万条,用QLoRA跑在两张3090上。batch size设了4,lr调到5e-5,跑了10个epoch,loss从1.8降到0.2左右,但推理时输出全是重复的符号或者“\n\n\n\n”这样的乱码。
我怀疑是学习率太小导致过拟合,或者数据集格式有问题(我是直接用的纯文本,没加chat模板)。也试过把LoRA的rank从8调到16,效果一样。
请问有经验的朋友,这种情况一般是什么原因?是数据没处理好,还是LoRA参数设置不对?或者需要加个warmup?有没有推荐的检查思路?多谢!
用LoRA微调Qwen2-7B做代码生成,loss降到0.2后输出全是乱码
全部回复
共 2 条看到你这个情况,我第一反应就是数据格式的问题。纯文本直接喂进去,Qwen2这种基座模型在预训练阶段没经过chat模板对齐,它其实不太理解“输入-输出”的结构关系,很容易把代码生成任务学成“预测下一个token”的文本填充任务,最后loss虽然低但输出全是重复符号,其实就是模型在自嗨式地重复训练集里常见的换行符或者标点。
我自己之前用类似方法微调CodeLlama也翻过车,加上chat模板或者给每条数据加个简单的指令前缀(比如“请你根据以下描述生成Python代码:”),输出立刻正常了。另外你提到loss降到0.2,其实这个值对7B模型来说有点过低了,尤其是在只有1万条数据的情况下,很可能模型已经死记硬背了训练集里的格式特征,而不是真的学会了代码逻辑——可以试试把学习率提高到1e-4或者2e-4,同时减少epoch到3-5轮,再配合warmup steps比如100步,先让模型稳定下来。
还有一个细节:QLoRA的4bit量化本身会损失精度,如果你的rank从8调到16没变化,可能不是秩的问题,而是量化后的权重无法很好地拟合代码生成这种对精确语法要求很高的任务。我试过在代码任务上把qlora的target_modules从默认的q_proj和v_proj扩展到全部线性层,效果明显好转,你可以试试把o_proj、gate_proj这些也加进去。
另外,你的数据集是LeetCode题解,这类数据天然包含大量重复的代码模板(比如“class Solution:”),模型学到的可能只是模板匹配而不是真正的代码生成。建议在预处理时把无关的注释和冗余空格去掉,同时确保每条数据里自然语言描述和代码是一一对应的,别让模型看到描述就直接输出“\n”这种空行。
warmup我个人觉得不是主要矛盾,但加上也没坏处,一般设10%的总步数就行。关键还是先检查下data format,然后调低epoch数、拉高学习率试一两次,大概率能解决。
看到loss降到0.2但输出乱码,这太典型了,我上周调代码补全模型时也遇到过一模一样的情况。个人觉得大概率是数据格式的问题,LeetCode题解直接当纯文本喂进去,模型根本分不清哪段是描述、哪段是代码,尤其是Qwen2本身有对应的对话模板,不套chat模板的话它会把自然语言和代码混在一起学,最后生成时就开始随机拼接符号。你可以试试把每条数据整理成类似“用户说需求,模型回复代码”的格式,用Qwen2自带的system/user/assistant标签包一下,loss可能反而会稍微升高一点,但生成质量会正常很多。另外学习率5e-5对LoRA来说其实不算小,但10个epoch在1万条数据上可能确实有点多了,尤其是有监督微调时loss降到接近0往往意味着模型在死记硬背,你可以试试early stopping,或者把epoch砍到3-5轮,看看生成是不是恢复正常。warmup倒是可以加,但我觉得优先级不如数据格式化高,先改数据格式跑一两个epoch看看效果变化。