最近在跑一个法律文书摘要的微调实验,基座是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能降到0.8但生成乱码,我第一反应是推理时的max_new_tokens没设对,导致生成了大量特殊符号填充,你试试把生成参数里的repetition_penalty调高点,或者限制下生成长度看看。另外你提到base模型正常,那问题大概率出在LoRA适配层和tokenizer的embedding没对齐上,可以检查下微调时有没有冻结embedding,或者试试在tokenizer里加上pad_token和bos_token之类的设置。我之前跑中文任务也遇到过类似情况,后来发现是数据里混了特殊字符,清洗干净后就好了,你也不妨检查下训练集里有没有异常的Unicode符号。
我之前跑中文摘要也遇到过一模一样的情况,loss看着正常但输出全是符号。后来发现是数据加载时label没跟着input一起做mask,导致模型在生成时把padding位置也当成了要预测的内容,你可以检查下label里是不是有-100之外的padding token。另外LoRA只调了attention层的权重,如果base模型正常但微调后崩,也可能是你数据里混了特殊字符没清洗干净,试试把训练集里非中英文的符号全部过滤掉。还有个偏方,把生成时的repetition_penalty调到1.2左右,能压住那种重复乱码。
之前跑摘要也遇到过一模一样的,loss看着正常但生成全是符号。后来发现是数据里混了特殊字符,清洗后就好了,你可以先看看训练集里有没有非utf-8的内容。另外LoRA只调attention层的话,embedding和lm_head没训到,输出分布会偏,建议把target_modules换成q_proj, v_proj试试。分词器对齐倒不太像,因为base模型生成正常,问题大概率出在训练数据或adapter配置上。
我之前跑摘要也遇到过一模一样的,loss看着挺正常但生成全是符号。后来发现是数据预处理时把中文按字符拆了,tokenizer的tokenize方法没设置add_special_tokens=False,导致每个样本开头都塞了bos,推理时模型就懵了。你可以先检查下微调时的输入ids和base模型生成的ids对比一下,看看是不是special token位置不对。另外LoRA只调attention层的话,llama的embedding和lm_head是冻结的,有时候输出分布会偏,试试把target_modules改成全部线性层,我之前这么调好的。
这种乱码八成不是学习率的事,loss能降到0.8说明模型学进去了,但生成时解码不对。你base模型正常,那问题肯定出在微调数据上,重点查一下label的构造,是不是把输入和输出拼一起了,或者label里包含了padding部分。我之前犯过这错,模型把padding也当成了要生成的内容,结果全是重复符号。你试试把label里非答案部分全设成-100,padding也屏蔽掉,应该能解决。还有,检查下tokenizer的clean_up_tokenization_spaces,有时候这个会影响解码。
我之前跑中文摘要也遇到过一模一样的情况,loss看着挺正常但生成全是乱码。后来发现是数据加载时label没跟着input一起做padding,导致模型在计算loss时把padding token也学进去了,推理时就会疯狂输出那些特殊符号。你试试在tokenizer里把padding设为eos_token,或者训练时把label的padding部分设成-100,应该能解决。另外attention那块儿也可以查一下,但我觉得大概率不是它的问题。
这问题我熟,之前调chatglm也翻过车。你确认下是不是只对input做了truncation,但tokenizer返回的attention_mask没同步处理?LoRA本身不会改分词器,但训练时如果mask不对,模型会把无效位置也当成上下文学进去,生成时就容易崩。建议你打印一条训练样本看看input_ids和labels是不是对齐的,有时候就是这种小细节。
乱码还带^和?,这明显是生成时采样到了特殊token吧。你推理时有没有设temperature和top_p?我之前把temperature调太高也会出这种问题。不过你说base模型正常,那还是得回头查训练数据的格式,法律文书可能里有大量换行符或特殊字符,tokenizer没清洗干净的话,微调时会把噪声学进去。你可以先拿一小批干净数据试训一下,排除数据问题。
我猜是生成参数里repetition_penalty
我之前跑摘要任务也撞到过一模一样的现象,loss看着挺正常,生成出来全是符号和英文碎块。当时排查了半天,最后发现是labels的问题,微调时labels没把padding位置设为-100,导致模型在padding token上也在算loss,学了一堆奇怪的输出模式,你检查下数据构造那一步。另外你提到base模型正常但微调后崩,这其实是个关键线索,说明分词器本身没毛病,更可能是训练目标跟推理时的采样方式不匹配。LoRA加上去之后,模型输出的logits分布会变得特别尖锐,采样时温度稍微高点就容易陷入重复符号的循环,你可以试试把repetition_penalty调大,或者用top_p小一点。还有个坑是LLaMA的tokenizer对中文不太友好,你在padding到128的时候,很多位置其实是空的,如果attention mask没处理好,训练时模型会去关注那些空位。至于attention,我建议你直接打印一下微调前后模型对同一句话的attention输出对比,看看是不是权重分布乱掉了,有时候是LoRA只改了q和v,没有动k,导致信息提取错位。
我之前跑类似任务的时候也踩过这个坑,loss看着正常但生成全崩,后来发现是label的问题——你检查一下微调时有没有把label里的padding token设成ignore_index,LoRA训练时如果模型没学会忽略padding,推理时就会把那些特殊位置当成有效内容疯狂输出符号。另外你提到分词器是官方tokenizer,但LLaMA的中文tokenization其实很碎,法律文书里大量专业术语会被切成很长的token序列,128的截断长度可能把关键上下文截没了,生成时模型只能靠残缺信息瞎猜。还有个点,你用base模型直接生成正常,但微调后崩,很可能是训练时数据格式和推理时不一致,比如训练时加了prompt模板但推理时没加,或者attention mask没对齐。建议你先把生成时的repetition penalty调高到1.3以上试试,同时检查一下推理时的max_new_tokens和temperature设置,有时候乱码是采样参数太激进导致的。如果还不行,可以试着只微调attention层,冻结其他层,我上次这么干之后输出稳定很多。
我之前跑中文摘要也遇到过一模一样的现象,loss看着正常但输出全是符号。你这情况我赌不是学习率的事,重点查一下你保存和加载模型时有没有把tokenizer的pad_token_id设成eos_token_id,LoRA训练时这个特别容易漏。另外你试过用中文base模型比如Chinese-LLaMA那个词表吗,官方tokenizer对中文切分很碎,微调后embedding变化大就容易崩。还有个笨办法,你拿训练集里一条数据直接做eval,看是不是也乱码,能快速定位是数据还是推理的问题。
我之前跑中文摘要也遇到过一模一样的情况,loss看着正常但输出全是符号乱码。后来发现是数据预处理时把label也做了padding,而且padding token在训练时被参与计算了loss,导致模型学会了生成一堆填充符。你可以试试在训练时把label里的padding部分设成-100,或者检查一下attention mask是不是正确传给了模型。另外如果用的是transformers的Trainer,确认一下data collator是不是默认处理了这些,我之前就是手动写的collator漏了这个。
我之前也遇到过类似情况,loss看着正常但生成完全崩掉,后来发现是数据标签里混进了特殊字符,比如换行符和制表符,LoRA微调时把这些噪声也学进去了,生成时就会疯狂复现。你可以先检查一下训练数据的清洗流程,尤其是中文标点和不可见字符。另外,你说base模型正常但微调后崩,大概率不是分词器对齐问题,更像是微调时模型embedding被带偏了,试试在LoRA配置里把target_modules只设成q和v,别动k和o,有时候全量更新反而会让输出失控。
这情况我遇到过,大概率是生成时temperature或者top_p没调,微调后分布变了,乱码就出来了。
loss降到0.8但推理乱码,大概率是生成参数的问题,试试temperature调低加repetition_penalty。
我猜大概率是label和input没对齐,LoRA训练时如果label里混着padding token或者特殊符号,模型会把这些当正常文本学进去,生成时自然就疯狂吐乱码。你试试推理时把repetition_penalty调高到1.5以上,或者检查下tokenizer在decode时skip_special_tokens设没设。另外attention那块也可以看看,但我觉得先确认下训练数据里有没有没清洗干净的换行符或控制字符,这玩意儿经常导致生成崩坏。
我之前跑摘要任务也碰到过一模一样的现象,loss看着挺正常,一生成就全是符号乱飞。你提到base模型正常,只有LoRA微调后才崩,这基本能排除分词器本身的问题,更可能是微调过程中模型学到了某种错误的映射。我当时的排查方向是检查attention mask和label的构造,尤其是padding部分有没有被计算进loss,如果padding token的logits被强行优化,模型就会倾向于生成那些特殊符号。另外建议你直接打印一次推理时的input_ids和生成的token ids,看看乱码对应的到底是哪些token,是不是集中在某个unk或者pad id附近。学习率降到1e-5还乱的话,我怀疑不是单纯lr的问题,可能跟数据清洗有关,法律文书里如果混入了大量特殊字符,即使tokenizer能编进去,模型也会学会复读这些噪声。还有个小坑,peft的LoRA默认只改attention的q和v,你试过把target_modules改成全部线性层吗?有时候只调q/v会导致输出分布偏移。最后实在不行,可以试试先不加载LoRA权重,单独加载base模型然后merge一下看看,万一是保存加载时权重错位了呢。
我最近也踩过类似的坑,不过是在别的任务上。loss能降下来但生成乱码,大概率不是学习率的问题,你想想,base模型正常说明tokenizer本身没毛病,那问题就出在微调过程对模型分布的改变上。我怀疑是LoRA只适配了attention的q和v,但你和模型的输出层交互出了问题,特别是当训练数据里中文占比很高时,模型可能把某些特殊token的embedding学歪了。你可以试试在推理时把temperature调低到0.1,或者加上repetition_penalty,先排除采样策略的干扰。另外,你检查过数据里有没有混入奇怪的字符吗?法律文书经常有全角符号或者特殊编号,如果tokenizer把“^”和“?”当成了普通字符,而LoRA又强化了这些token的权重,生成时就会反复输出。还有一个更实操的办法:微调后单独保存adapter,加载时用base模型的原生tokenizer,别用训练时的pipeline,有时候是padding侧的bug。最后,建议你直接打印几组生成的token id,看看是不是特定id在循环,如果真是这样,那大概率是embedding矩阵里某些行被更新得太剧烈,可以试试冻结embedding层只调transformer层。
我之前跑代码生成任务也遇到过类似情况,loss降得挺漂亮但输出全是重复的括号和分号。后来发现是数据预处理时把换行符删了,导致模型学会了输出换行符对应的token但上下文里没有对应位置,于是就开始乱码。你那个法律文书摘要,有没有可能停用词或者标点被过度编码了?比如中文逗号句号在tokenizer里是单独token,如果训练数据里这些标点出现频率特别高,LoRA会放大它们的输出概率。建议你做个对比实验:用原始LLaMA的tokenizer跑一遍你的推理代码,确保输入格式完全一致,再加载你的LoRA权重,如果还是乱码,那基本可以锁定是微调时某些参数没对齐。另外,你试试把max_new_tokens设成50,看看前几个token是否正常,如果前面正常后面崩,那就是生成长度累积误差导致的,可以考虑加beam search或者top_p采样。乱码问题真的玄学,但多从token级别分析,比调超参数有效得多。
我之前跑摘要任务也栽过这个坑,loss能降但生成乱码,最后查出来是label侧的问题——微调时只用input_ids算loss,但label里没把padding部分mask掉,模型在学输出“^”和“?”这些填充符。你检查下loss是不是把padding token也算进去了,peft默认不会帮你处理这个,得手动设ignore_index=-100。另一个可能性是LoRA只作用在attention的q和v上,但你的数据里长文本多,position embedding那边没被微调,导致模型对序列位置关系产生混乱,生成时就开始复读特殊符号。你试过把LoRA的target_modules扩展到gate_proj和up_proj吗?我之前加了之后乱码频率明显下降。分词器对齐这块,base模型正常说明tokenizer本身没问题,大概率是训练时数据格式和推理时不一致,比如训练用了chat模板但推理直接调model.generate,special token的attention mask没对应上。建议你打印一条生成的token id序列,看是不是集中在某些特定id上,如果是的话基本就是loss计算或数据管道的锅。
试试把生成参数里的repetition_penalty调高,我之前也这样,调完立马正常了。
我之前踩过一个类似的坑,最后发现是label那边出了问题。你loss能降到0.8说明模型确实在学,但生成乱码很可能是因为训练时把padding token的loss也算了进去,导致模型被带偏,生成时疯狂输出特殊符号。可以试试在训练时把ignore_index设为-100,把padding部分屏蔽掉,这样loss会干净很多。另外你说base模型生成正常,那分词器本身应该没问题,重点还是得查一下你微调时数据预处理和推理时用的tokenizer配置是否完全一致,比如add_special_tokens的设置不同,或者推理时没带attention_mask,也可能导致这种抽风式输出。我之前还遇到过LoRA只作用于attention层,但没动FFN,结果某些任务上效果很差,但你这个乱码更像是解码时的概率分布崩了,建议先看看生成时的temperature和top_p是不是被设得太极端了。还有个歪招,你试试把生成时repetition_penalty调大一点,有时候能压住这种重复乱码。如果还不行,建议你直接打印一下模型生成的token id,看看是不是大量命中了special token,这样能直接定位问题。
我之前也踩过类似的坑,loss看着没问题但生成崩了,最后发现是数据集里混着没清洗干净的换行符和特殊字符,tokenizer把那些字符编码成了奇怪id,LoRA学进去了但推理时露馅。你可以先试试把数据里所有非中英文和标点符号的字符全部过滤掉再训一版,如果正常了就是数据问题。另外你提到的attention,我觉得可以先不用管,倒是建议你检查下保存和加载模型时有没有把tokenizer一起存下来,有时候加载的是默认配置就对齐不上了。
我之前也踩过类似的坑,loss看着正常但生成崩了,后来发现是数据预处理时把标签也做了截断,导致decoder端输入输出错位。你可以检查下labels是不是和input_ids对齐了,特别是padding时有没有把label的padding token设成-100。另外llama的tokenizer对中文不太友好,你可以试试把special token加到tokenizer里再resize embedding,或者换用中英混合分词器重新编码数据,说不定能解决。
如果上面没问题,建议看看attention mask和position id,LoRA微调时这些没对齐也会出乱码。我之前是直接用transformers的Trainer,它自动处理了这些,但手动写训练循环就容易漏。你用的peft的话,确认下是不是只train了adapter参数,base model的embedding有没有被意外冻结或改动。