【Bug已解决】Understanding loss in Training LLM 解决方案

发布时间:2026/9/29 2:22:37

【Bug已解决】Understanding loss in Training LLM 解决方案 【Bug已解决】Understanding loss in Training LLM 解决方案一、现象长什么样训练自己的 LLM用transformers的Trainer或自己写的训练循环时遇到一类「看不懂 loss」的问题loss 数值异常大比如 10、20怎么调学习率都下不来loss 看上去在降但模型生成全是乱码、复读验证集 loss 比训练集还低或者两边都不正常切换到不同tokenizer/ 不同 padding 策略后loss 量级突然变了但模型结构没动。最典型的复现把一批长短不一的样本 pad 到同一长度送进模型直接用input_ids当labels算交叉熵发现 loss 被 padding 位置严重拉高训练目标其实是「学会预测padding token」而不是「学会预测下一个真实 token」。这类问题不报错但训练出来的模型就是「不懂人话」——因为 loss 的含义从一开始就算错了。二、背景自回归 LLM 的训练目标是「给定前 i 个 token预测第 i1 个 token」。交叉熵 loss 对每个位置算一次再平均。关键点padding 位置不该参与 loss。transformers的model(**inputs)在传入labels时会自动对labels -100的位置跳过用ignore_index。但很多人这么写# 错误写法直接把 input_ids 当 labels outputs model(input_idsbatch, labelsinput_ids) loss outputs.loss如果 batch 里有 paddinginput_ids含pad_token_id那么 padding 位置也被当成「要预测的真实标签」模型被迫去预测pad_token_id这些位置的 loss 被算进平均。后果loss 被 padding 稀释/拉高短样本多的 batchpadding 占比大loss 大部分在「学 padding」真实语义信号被淹没。训练目标错位模型花大量精力拟合 padding生成时容易吐 padding 或复读。跨 tokenizer 不可比不同 tokenizer 的 pad 比例不同loss 量级跟着变你以为换了模型其实只是 pad 多了。正确理解 loss 的前提就是「让 padding 不参与 loss」。三、根因根因一句话训练时labels没有把 padding 位置标成ignore_index-100导致交叉熵把 padding 也当成要预测的目标loss 含义错误、训练目标错位。三点展开padding 参与计算labels input_ids让 pad 位置进入 lossignore_index没生效。平均基准错loss 平均的分母包含 padding 位置数真实 token 的梯度被稀释。3.缺校验没有在送入模型前断言labels里 padding 已被-100覆盖于是错误静默存在。不是模型不会学是「学什么」被 padding 污染了。四、最小可运行复现不依赖真实大模型用一个最小交叉熵演示 padding 如何污染 lossimport torch import torch.nn.functional as F vocab, seq 10, 6 pad_id 0 logits torch.randn(1, seq, vocab) # 模型输出未归一化 targets_raw torch.tensor([[1, 2, 3, pad_id, pad_id, pad_id]]) # 含 padding # 错误直接拿含 pad 的 target 算 loss loss_with_pad F.cross_entropy( logits.view(-1, vocab), targets_raw.view(-1) ) # 正确padding 标成 ignore_index-100 targets_masked targets_raw.clone() targets_masked[targets_raw pad_id] -100 loss_no_pad F.cross_entropy( logits.view(-1, vocab), targets_masked.view(-1), ignore_index-100 ) print(含 padding 的 loss:, round(loss_with_pad.item(), 4)) print(忽略 padding 的 loss:, round(loss_no_pad.item(), 4)) print(两者是否相同:, torch.isclose(loss_with_pad, loss_no_pad))跑出来含 padding 的 loss 把 3 个 pad 位置也学进去了数值和「只看真实 3 个 token」的 loss 明显不同pad 多时差异更大。这就是「loss 算错」的精确复现。五、解决方案第一层最小直接修复最小修复构造labels时把所有pad_token_id位置替换成-100再送进模型。import torch from transformers import AutoModelForCausalLM, AutoTokenizer tokenizer AutoTokenizer.from_pretrained(your-model) model AutoModelForCausalLM.from_pretrained(your-model) def make_labels(input_ids: torch.Tensor) - torch.Tensor: labels input_ids.clone() # 关键padding 位置标成 -100交叉熵忽略它 labels[labels tokenizer.pad_token_id] -100 return labels # 训练循环 for batch_input_ids in dataloader: labels make_labels(batch_input_ids) outputs model(input_idsbatch_input_ids, labelslabels) loss outputs.loss # 现在只统计真实 token loss.backward() optimizer.step() optimizer.zero_grad()如果做「下一 token 预测」且输入已经是「输入标签移位」的格式注意自回归模型内部会自己处理移位你只需保证labels里 padding 是-100不要把labels再做一次[:, 1:]移位那会和模型内部的 shift 重复。要点labels[labels pad_token_id] -100一行解决 padding 污染。model(..., labelslabels)内部用ignore_index-100自动跳过。loss 现在只反映「真实 token 的预测质量」量级和训练目标都正确。这一步单独就让 loss 回归正确含义。六、解决方案第二层结构性改进第一层是「在循环里加一行」。但训练脚本里多个数据路径SFT、预训练、带 mask 的指令数据都构造 labels容易漏。更稳的做法把「labels 如何正确屏蔽 padding / 特殊 token」收敛成单一策略对象。from dataclasses import dataclass, field from typing import List, Optional import torch dataclass class LlmLossAuditor: LLM 训练 loss 标签屏蔽的单一策略。 # 需要忽略的 token id 集合padding、特殊 token 等 ignore_ids: List[int] field(default_factorylist) # 是否同时忽略序列左侧prompt只学回答SFT 常用 train_on_completion_only: bool False # completion 起始标记SFT 用 response_start_id: Optional[int] None def build_labels(self, input_ids: torch.Tensor) - torch.Tensor: labels input_ids.clone() for ig in self.ignore_ids: labels[labels ig] -100 if self.train_on_completion_only and self.response_start_id is not None: # 找到每个样本里 response_start 的位置其之前全标 -100 mask (input_ids self.response_start_id) # 用 cumsumstart 之前为 0之后为 1 pos mask.cumsum(dim-1) labels[pos 0] -100 return labels def check(self, labels: torch.Tensor): # 防御整行全 -100 意味着该样本无监督信号 all_ignored (labels -100).all(dim-1) if all_ignored.any(): print(f[LlmLossAuditor] 警告: {int(all_ignored.sum())} 条样本整行被忽略) # 用法 auditor LlmLossAuditor(ignore_ids[tokenizer.pad_token_id, tokenizer.bos_token_id]) for ids in dataloader: labels auditor.build_labels(ids) auditor.check(labels) loss model(input_idsids, labelslabels).loss loss.backward(); optimizer.step(); optimizer.zero_grad()结构收益单一策略padding、特殊 token、SFT「只学回答」的屏蔽都集中在一处。-可校验check抓出「整行无监督」的废样本。可扩展加新的忽略规则只改LlmLossAuditor不动训练循环。七、解决方案第三层断言 / CI 守护写 pytest 守三条(1) padding 被标-100(2) 真实 token 不被误标(3) 计算出的 loss 与「仅真实 token」一致。import torch import torch.nn.functional as F import pytest from your_lib import LlmLossAuditor pytest.fixture def auditor(): return LlmLossAuditor(ignore_ids[0]) # 假设 pad_id0 def test_pad_masked_to_neg100(auditor): ids torch.tensor([[1, 2, 0, 0]]) labels auditor.build_labels(ids) assert labels[0, 2].item() -100 assert labels[0, 3].item() -100 def test_real_tokens_kept(auditor): ids torch.tensor([[1, 2, 0, 0]]) labels auditor.build_labels(ids) assert labels[0, 0].item() 1 assert labels[0, 1].item() 2 def test_loss_ignores_pad(): vocab, seq 10, 4 pad_id 0 logits torch.randn(1, seq, vocab) raw torch.tensor([[1, 2, pad_id, pad_id]]) masked raw.clone(); masked[raw pad_id] -100 l_pad F.cross_entropy(logits.view(-1, vocab), raw.view(-1)) l_mask F.cross_entropy(logits.view(-1, vocab), masked.view(-1), ignore_index-100) assert not torch.isclose(l_pad, l_mask), 含 pad 的 loss 应与忽略 pad 的不同 def test_completion_only_mode(): a LlmLossAuditor(ignore_ids[0], train_on_completion_onlyTrue, response_start_id5) ids torch.tensor([[1, 5, 6, 7]]) # 5 之后才是回答 labels a.build_labels(ids) assert labels[0, 0].item() -100 # prompt 部分忽略 assert labels[0, 1].item() -100 # response_start 本身可忽略 assert labels[0, 2].item() 6 # 回答部分保留CI 常驻跑这四条后任何「padding 又混进 loss」「真实 token 被误标」的回归都会立刻爆红。八、排查清单训练 LLM「loss 看不懂」时按顺序查先打印labels里pad_token_id是否还在——在就说明 padding 参与了 loss。确认labels是input_ids的克隆并做了-100替换而不是直接用input_ids。确认用的loss来自model(..., labelslabels).loss而不是自己手写的、没传ignore_index的F.cross_entropy。SFT 场景确认是否「只学回答」prompt 部分应标-100否则模型在学复述问题。确认ignore_index-100与模型内部一致transformers 默认就是 -100别改成别的。换 tokenizer 后重新核对pad_token_id不同 tokenizer 的 pad id 可能不同。批量打印几个样本的labels肉眼确认除 pad/特殊位外真实 token 都保留。九、小结「训练 LLM 但 loss 看不懂 / 模型学不会」的常见根子是labels没把 padding 标成ignore_index-100交叉熵把 padding 也当成学习目标loss 含义错位、训练目标被污染。修复三层次第一层构造 labels 时labels[labelspad_token_id] -100第二层用LlmLossAuditordataclass 把 padding/特殊 token/SFT「只学回答」的屏蔽收敛为单一策略并加整行忽略校验第三层用 pytest 守「padding 被标 -100」「真实 token 保留」「含 pad 与忽略 pad 的 loss 不同」「completion-only 正确」。工程启示自回归训练里「labels 怎么构造」决定了「模型学什么」。padding 必须-100、promptSFT 时必须-100、特殊 token 通常也要-100。任何训练脚本上线前先肉眼看一眼labels再训比训完发现模型废了再回头查省事得多。
延伸阅读

更多相关文章

2026/9/28 11:51:38

LinkSwift:九大网盘直链解析终极指南与高效下载方案

LinkSwift:九大网盘直链解析终极指南与高效下载方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云…

2026/9/27 0:22:11

3步让老旧游戏手柄重获新生:XOutput完整指南

3步让老旧游戏手柄重获新生:XOutput完整指南 【免费下载链接】XOutput DirectInput to XInput wrapper 项目地址: https://gitcode.com/gh_mirrors/xo/XOutput 你是否曾为那些功能完好却被现代游戏"抛弃"的老旧游戏手柄感到惋惜?那些陪…

2026/9/27 19:15:10

MySQL安装常见问题与配置优化全解析

1. MySQL安装过程中的典型问题全景MySQL作为最流行的开源关系型数据库,其安装过程看似简单却暗藏玄机。根据我多年DBA经验,90%的安装问题集中在配置文件、权限管理和服务初始化三个环节。新手常遇到的典型症状包括:服务无法启动且无明确错误提…

2026/9/29 2:19:10

《栈与队列:数据结构的“双生花”》

《栈与队列:数据结构的“双生花”》一.栈:后进先出1.1认识栈这一章的栈和队列比较简单;1.2后进先出1.3基于数组的栈模拟①.入栈②.出栈③.取栈顶元素二.队列:先进先出2.1认识队列注意:队列他是接口,接口,接口!2.2队列图解2.3 以数组模拟队列①入队列②.出队列③.取队…

2026/9/29 2:19:10

PPT Master:如何把一份文档变成原生可编辑的 PPT

PPT Master:如何把一份文档变成原生可编辑的 PPT 【免费下载链接】ppt-master AI turns documents or topics into real, native PowerPoint decks—with native shapes, transitions and animations, data-backed charts and tables on demand, audio narration fr…

2026/9/29 2:19:09

2023_Liu_LLaVA_总结

Visual Instruction Tuning(LLaVA)总结来源:NeurIPS 2023,Haotian Liu、Chunyuan Li 等(University of Wisconsin–Madison / Microsoft Research / Columbia University) 原文:论文原文/LLaVA.…

2026/9/29 2:14:09

投影与降维:从GIS坐标系到PCA特征压缩的通用思维

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/28 3:03:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/28 6:05:15

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/28 6:07:41

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 0:04:04

AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成

1. 为什么AI Evals值得你花时间搞明白做LLM应用的人,迟早会撞上同一堵墙:模型输出飘忽不定,今天答得好好的,明天换个问法就胡说八道。你改了一版提示词,感觉好像好了点,但到底好了多少?说不清。…

2026/9/29 0:04:04

Java采购管理系统实战:从数据库设计到事务一致性

简介:这是一套面向Java Web初学者与课程设计者的采购管理系统完整源码,采用JSP技术搭建,配合MySQL数据库,用于解决企业采购信息的管理问题,适合作为毕业设计、课程大作业或进销存类项目的参考模板。系统实现了用户登录…

2026/9/25 20:55:38

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/26 19:58:38

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/28 1:59:25

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑