最近在跑一个法律文书摘要的微调实验,基座是LLaMA-2-7B,用的peft库里的LoRA。训练loss能降到0.8左右,但推理时生成的内容完全不是正常中文,夹杂着大量重复的“^”和“?”符号,偶尔蹦出几个英文单词。我检查过数据预处理,分词器用的是llama官方tokenizer,padding和truncation都设了128。调过学习率(从2e-4降到1e-5)和LoRA的rank(8到64),情况稍微好转但依然乱码。看网上说可能是tokenizer的special token没加,但我用base模型直接生成是正常的,只有微调后才崩。有没有大佬遇到过类似情况?是应该检查attention mask还是需要重新对齐label?求指点一下排查方向,谢谢!
用LoRA微调LLaMA模型后推理结果全是乱码,是学习率问题还是分词器没对齐?
全部回复
共 51 条我之前也踩过类似的坑,loss能降但生成乱码,大概率不是学习率的问题,而是你推理时的generation config和训练时的input格式不一致。比如padding side没设成left,或者attention mask没处理好,导致位置编码错位。你试试把tokenizer的padding_side改成left,然后生成时加一个和训练时相同的前缀模板,看会不会好点。另外LoRA只改attention层的话,如果没冻结embedding和lm_head,也可能导致输出分布漂移,建议检查下target_modules是不是只包含了q,k,v。
我遇到过几乎一模一样的情况,最后排查下来是label掩码的问题。你loss能降到0.8说明模型确实在学,但推理乱码大概率是训练时把padding位置的token也参与计算了,导致模型学会了输出那些特殊符号。建议你检查一下data collator里有没有设置ignore_index=-100,尤其是用peft的时候,默认的tokenizer.pad_token_id可能没被正确处理。
另外你提到base模型正常,微调后才崩,这其实很典型——LoRA只改了一小部分参数,如果训练时输入输出对齐稍有偏差,模型就会在解码时陷入重复生成某个token的死循环。你可以试试在生成时把repetition_penalty调高到1.5以上,同时把temperature降到0.3,先确认是不是解码策略的问题,再回头查训练数据里有没有混入异常字符。
还有一个坑是中文分词器对全角符号的处理,LLaMA的tokenizer对中文是按字节切分的,你训练语料里如果混有全角空格或者特殊标点,模型很容易学到输出那些“^”和“?”。建议你打印几条训练样本的token ids,专门看看这些乱码token是不是在数据里频繁出现。
我上次调法律文本也卡了三天,最后发现是label里没把prompt部分mask掉,模型把指令也当成了要生成的内容。你试试把labels里对应prompt的位置全部设成-100,只让模型学习摘要部分,应该会有明显改善。如果还不行,可以看看attention mask是不是在packing时弄错了,这个用peft的官方example很容易踩坑。
之前跑摘要任务也踩过这坑,loss降得挺好看但生成全是符号,后来发现是label里padding部分没mask掉,LoRA训练时把padding token也学进去了。你试试推理时把repetition_penalty调高点,或者检查下attention mask是不是只在左边,右边没遮住。另外确认下tokenizer的bos和eos在训练时有没有真正加进去,有时候peft默认配置会忽略这些。
我之前跑摘要任务也踩过类似的坑,loss看着正常但生成全崩。你试试把生成的temperature调低到0.1,然后检查一下pad_token_id有没有设成eos_token_id,LoRA没收敛到正确分布时解码很容易飘。另外你attention那块没打完,是怀疑注意力mask的问题吗?可以看看训练时有没有把label里的pad部分正确ignore掉,这个影响挺大的。
我之前跑过类似的微调,loss降到0.8其实不算低,你可以看看验证集loss是不是也同步降了,如果只是训练集降但生成崩,大概率是过拟合到训练数据里的噪声了。另外你提到base模型正常,那分词器基本没问题,问题可能出在LoRA只适配了部分层,而你没开gradient_checkpointing导致某些层更新异常。建议先试试用官方脚本跑一遍同样的数据处理,排除掉自己预处理时不小心改了label或mask的坑,然后关注一下生成时的temperature和top_p,有时候解码策略也会放大这种乱码现象。
我上周刚踩过一模一样的坑,最后发现是label没跟着一起mask掉,loss看着低但模型其实在学预测padding位的乱码。你试试把labels设成-100对应非答案部分,或者检查下data collator是不是把label也padding了。另外生成的时候把repetition_penalty调高到1.5以上,能压掉那些重复符号。
我之前跑中文摘要也遇到过一模一样的症状,loss降得挺好看但生成全乱码。后来发现是数据预处理时把输入标签也做了padding,导致模型在生成时把padding位当成了有效内容,建议检查下labels有没有设成ignore_index=-100。另外你说的attention问题也有可能,LoRA只改了q和v投影的话,可能对中文这种依赖全局依赖的语言不太够,可以试试把lora_target改成全部线性层,或者直接看看生成时temperature和top_p是不是设太激进了。
这情况我遇到过,多半是生成时temperature或top_p设太高,跟LoRA关系不大,调低点试试。
我之前跑摘要也碰到过一模一样的情况,loss看着正常但生成全是符号。后来排查发现是数据里混了太多特殊字符,比如换行符和制表符,分词器把它们拆成了奇怪的token,LoRA学了一堆噪声。你可以试试在预处理时把文本清洗一下,尤其是去掉那些控制字符,或者干脆用add_special_tokens=False再对照看看。另外,你提到base模型正常,那大概率不是分词器对齐问题,更像是训练时label没mask住,模型把padding位置也学着生成了。检查下attention mask和label的填充值是不是设成-100了。
遇到过类似的,但不是乱码而是全输出同一个token,后来发现是label没mask掉pad位置,loss看着低其实模型在学复制。你可以先看看生成的token id是不是集中在某个区间,如果是的话大概率是tokenizer的问题,试试在tokenizer里加个pad_token或者把eos也设成pad。另外你训练时loss是算在原始输入上还是只算在输出部分?LoRA微调时base模型能生成正常不代表微调后没问题,也可能你数据里混了特殊字符没清洗干净。
我也遇到过类似的,loss看着正常但生成全是乱码,最后排查下来是label没对齐,微调时label也得用tokenizer处理并设置ignore_index,只处理input的话模型学不到正确的映射。
另外你试试生成时把repetition_penalty调高一点,我这边从1.0调到1.3效果立竿见影。
还有个思路是检查一下attention mask,LoRA训练时如果mask没传对,模型会看到一堆padding位置,输出就容易飘。
base正常但微调崩,大概率不是分词器的锅,重点看训练数据里有没有特殊token被截断,或者label序列长度和input不一致。
我之前也踩过类似的坑,loss看着正常但生成全是乱码,八成不是学习率的问题。你试试把生成时的do_sample关掉或者调低temperature,有时候是采样策略在作怪,尤其是重复惩罚没设好的时候。另外,检查下LoRA是不是只作用在attention层上,如果全量微调了某些embedding或lm_head,可能把词向量空间搞歪了。我之前是加了target_modules限制才好的,你可以对比下微调前后的tokenizer输出。还有,法律文本里特殊符号多,数据清洗时没过滤干净的话,模型容易学到这些噪声,建议看看训练集里有没有乱码样本。
这问题八成出在生成参数上,试试把repetition_penalty调高到1.5,顺便检查下pad_token是不是没设成eos_token。
我之前跑摘要任务也遇到过类似情况,loss看着正常但生成全是符号,后来发现是数据里混了特殊字符,清洗之后就好了,你可以检查下原始文本里有没有奇怪的不可见字符。另外LoRA只冻住原模型但没改embedding的话,分词器一般不会出问题,倒是attention的mask可能因为padding没设对,导致训练时看了太多无效位置。你试试把generation的do_sample关掉,或者温度调低点,有时候纯贪心解码反而能暴露问题。
这个现象我太熟了,之前跑中文摘要也踩过一模一样的坑。loss能降下来但生成乱码,大概率不是学习率的问题,因为你已经试过调低,效果只是“好转”而不是“恢复”,说明方向不对。更可疑的是base模型正常而微调后崩,这往往指向LoRA适配层和tokenizer的交互出了问题——你提到attention,其实可以先检查下attention_mask是不是在padding时被错误地传给了模型,特别是当序列长度超过128时,位置编码和mask错位会导致生成时注意力发散。另外有个细节,LLaMA的tokenizer对中文是按字节切分的,如果你在数据预处理时用了别的分词器做对齐,或者无意中修改了special token的id,微调时embedding矩阵的梯度更新就可能污染到未训练的token。建议你直接打印微调后模型在几个固定输入上的logits,和base模型对比,看是不是特定token id(比如中文字符的id)输出概率分布崩塌了。还有一招,把LoRA的target_modules从q_proj和v_proj扩展到k_proj和o_proj,有时候只用Q和V会让模型学不到足够的位置信息,生成时容易陷入局部循环。如果还不行,就试着在推理时强制设置tokenizer的add_special_tokens=False,排除是特殊符号干扰生成循环。
看描述感觉不像是学习率的问题,loss能降到0.8说明模型确实在学,但生成乱码更像是在解码阶段出了问题。你试试推理时把repetition_penalty调高一点,或者检查一下generation config里的do_sample设置,有时候beam search和采样模式切换会导致这种符号刷屏。另外你提到base模型正常但微调后崩,我怀疑是LoRA只训了attention层而没动feed-forward,导致参数分布偏移太大,可以试试把target_modules换成全部线性层。我之前跑中文摘要也遇到过类似情况,最后发现是数据里混了没清洗干净的HTML实体,你排查下训练集里有没有特殊字符。
之前跑摘要也遇到过,检查下label的padding token是不是被loss算进去了,改成ignore_index=-100试试。
我之前跑摘要也遇到过一模一样的,loss看着正常但生成全是符号,后来发现是标签没mask掉padding部分,LoRA训练时把padding位置也学进去了,推理时就开始乱来。你可以试试在loss计算时ignore_index设为-100,或者检查下attention mask是不是正确传给了模型。分词器对齐一般没问题,毕竟base是好的。另外你数据里如果混了特殊字符,比如“^”这种,也可能被tokenizer拆成奇怪的id,建议清洗下语料再试。
之前跑摘要任务也遇到过类似情况,loss降得挺好看但生成全是乱码,后来发现是数据里混了特殊字符,清洗之后就好了,你可以看看训练集里有没有“^”这种符号被当成token塞进去了。另外你提到base模型正常,那分词器大概率没问题,重点查下attention mask或者labels有没有对齐,LoRA训练时偶尔会把padding位置也算进loss,导致模型学歪了。还有个笨办法就是拿训练集里一条样本,用微调前后的模型分别生成,对比一下token id差异,能更快定位是哪里崩了。
这情况我也踩过坑,检查下tokenizer的add_special_tokens和label对齐,八成是label没mask掉padding。