系统性调优思路
如果你训练的 Transformer 模型严重过拟合,你会从哪些方面入手解决?¶
过拟合意味着模型在训练集上表现极好,但在验证集/测试集上表现差,本质是学到了训练集中的噪声和特定模式而缺乏泛化能力。可以从以下几个层面系统性地解决:
- 数据增强与预处理
- 增加数据量:收集更多标注或利用数据增强技术,如同义词替换、回译、随机遮蔽、句子重排、对抗样本生成等。
- 更严格的数据清洗:移除重复样本、错误标注,降低训练集中的噪声。
-
使用正则化手段如 标签平滑,避免模型对训练标签过度自信。
-
模型复杂度控制
- 减少模型参数量:降低层数、头数或隐藏维度,使用更小规模的模型。
- 增加 Dropout:在注意力权重、FFN 隐藏层、嵌入层等位置增加或调大 Dropout 概率,防止神经元共适应。
-
结构剪枝或蒸馏:用预训练大模型指导小模型学习,保留核心能力。
-
训练策略与超参数
- 早停(Early Stopping):监控验证集指标,当验证损失不再下降时停止训练,使用最佳检查点。
- 降低学习率或使用更强的学习率衰减(余弦退火、线性衰减),使模型在后期更精细地收敛。
- 增大 batch size(一定程度上可引入噪声的正则化效果,但需协调学习率)。
- 权重衰减(L2 正则化):增加 weight decay 系数,约束权重规模。
-
对注意力权重应用 Dropout,可以弱化特定 token 对的依赖。
-
训练目标与损失函数
- 引入对比损失、蒸馏损失等辅助任务,给模型更多监督信号,防止单一损失过拟合。
-
在预训练时使用更难的掩码策略(如 Span Masking、动态掩码),增加训练任务难度。
-
交叉验证与集成
- 使用 k 折交叉验证确保评估稳定性。
- 模型集成(多个种子、不同架构)平均输出,可减少单个模型的过拟合倾向。
综合来看,最直接有效的方式是 增加数据多样性 + 适当的正则化(Dropout + 权重衰减) + 早停。
模型训练中 loss 不下降,你怎么排查是数据问题、模型问题还是超参数问题?¶
当 loss 在一开始或训练中途停止下降,需系统地排查:
- 数据问题:
- 检查输入和标签的对应关系,样本是否被正确打乱、预训练/微调的数据格式是否正确。
- 查看 tokenization 是否正确,有无大量填充或无意义 token 导致信息丢失。
- 是否存在标签错误或全部为同一类标签(如分类任务的 label 全部为 0)。
-
尝试在极少数据(如 100 条)上过拟合测试:若能快速将 loss 降到接近零,则数据基本正常;否则数据本身或损失函数实现有误。
-
模型问题:
- 验证模型初始化是否正确(如查看激活值均值和方差,是否出现大量零或 NaN)。
- 检查模型结构:是否有死层(如 ReLU 后输出全部为负)、是否存在维度不匹配导致信息流失。
- 梯度检查:用
torch.autograd.gradcheck确认自定义算子或损失的反向传播正确。 -
若使用预训练权重,确认权重加载无误,层名对应,没有因为 strict=False 忽略关键层。
-
超参数问题:
- 学习率过大:可能导致 loss 震荡或发散;过小则收敛极慢。可画学习率-损失曲线寻找合适范围。
- 学习率调度器设置不当:warmup 步数太少或过多,衰减过于激进。
- batch size 过小可能造成梯度噪声太大,难以收敛;过大可能导致泛化差或停滞。
- 优化器选择:尝试从 AdamW 切换到 SGD,或调整 β1, β2, ε 等参数。
- 权重衰减过大可能压制模型学习能力;过小则无正则化。
-
损失函数实现错误(如使用 CrossEntropyLoss 时 logits 未转概率,或减少了对填充 token 的掩码)。
-
训练流程:
- 检查混合精度是否导致梯度下溢,可关闭 AMP 测试。
- 确认梯度裁剪阈值是否过小,导致有效梯度被截断。
最快速的方法是 小数据过拟合测试,通过即可排查数据加载与模型前向/反向通路问题;然后调大学习率看 loss 是否变化;最后逐步调整超参数。
如何通过调整 batch size 和学习率来改善收敛速度和最终效果?它们之间的耦合关系是什么?¶
-
耦合关系: 当 batch size 增大时,梯度估计更准确,方差变小。根据 线性缩放规则,学习率应与 batch size 成正比:
lr = lr_base * (batch_size / base_batch_size)。这样可保持参数更新步长的期望方差大致不变,保证训练动态相似。此外,还有更精确的 平方根缩放规则 或根据噪声温度理论调整。 -
改善收敛速度:
- 在硬件允许范围内,尽量使用较大的 batch size,配合对应的线性缩放学习率,可提高吞吐量,减少 epoch 数。
- 但过大的 batch size 可能导致“泛化差距”(sharp minima),需要配合 warmup 和适当正则化。
-
对于小 batch,学习率应较小以保证稳定性,但也可能更易逃离鞍点,泛化更好。
-
改善最终效果:
- 常用策略:先用较大 batch 快速收敛到较好区域,然后减小 batch size 或降低学习率进行精细搜索。
- 若在固定总训练 token 下,适中 batch size(如 256~1024)往往获得最佳验证损失,过大或过小都会略差。
-
引入学习率 warmup 和余弦衰减对于稳定训练至关重要,尤其在大 batch 场景。
-
实用建议:
- 进行 learning rate range test:线性增加 lr,观察 loss 下降最快时的 lr 范围,选择适中的值作为初始学习率。
- 使用 AdamW 等自适应优化器时,学习率对 batch size 的敏感性低于 SGD,但仍需大致按比例调整。
你在训练时发现验证损失在几个 epoch 后开始上升,但训练损失仍在下降,如何决策?是继续训练还是提前停止?¶
这是典型的过拟合信号。决策取决于目标和资源:
- 若目标是最终模型的泛化能力:应立即采取措施干预,而不是无脑继续训练。
- 早停 (Early Stopping):监控验证损失,当它在设定的 patience 个 epoch 内不创新低时停止训练,回退到最佳检查点。这是最简单有效的方法。
- 增加正则化:如增大 Dropout、权重衰减,或使用数据增强,然后从当前最佳模型继续训练或重训。
-
降低学习率:如果仍在微调阶段,可尝试将学习率调低 10 倍继续训练,可能会使验证损失重新下降。
-
若仍在实验阶段,希望观察趋势:
- 可稍微放宽 patience,但如果验证损失连续上升且训练损失还在下降,说明模型正在记忆训练集噪声,继续训练通常没有好处。
-
可使用更复杂的优化器如 SAM(Sharpness-Aware Minimization)来寻找平坦极小值,但一般建议提前停止。
-
特殊情形:
- 验证集分布与训练集不同:检查验证集是否具有代表性,或存在概念漂移。
- 学习率过大导致震荡:调低学习率可能使验证损失再次下降。
总原则:验证损失上升即停止,并使用最佳模型。继续训练仅在有强正则化或计划做模型平均等特殊策略时才考虑。
如果想加快 Transformer 训练速度,在不换硬件的前提下有哪些可操作的优化手段?¶
从模型架构、训练算法和工程实现三个层面入手:
- 工程实现:
- 使用混合精度训练(FP16/BF16 + FP32 主权重),利用 Tensor Core 加速。
- 采用优化的注意力内核:FlashAttention-2/3,xFormers 的 memory-efficient attention。
- 算子融合:将 LayerNorm + Dropout + Residual 等小操作融合为一个 kernel,减少启动开销。
- 使用 NVIDIA DALI 或 CPU 预处理流水线,将数据加载与 GPU 计算重叠。
-
启用 NCCL 通信优化(环境变量调整),使用 faster transformer 后端。
-
训练算法:
- 梯度累积:在小 batch 无法增大时,通过累积多个 micro-batch 的梯度来模拟大 batch,减少通信频率(但在单卡效果有限)。
- 使用更高效的优化器变体:如 LION(有报告显示可更快收敛)、或减少 Adam 状态内存来加速。
- 使用课程学习或动态序列长度训练:初期用短序列训练,逐渐增加长度,减少前期计算量。
-
在保证精度的情况下适当降低精度(如从 FP32 主权重换为 BF16)。
-
模型架构调整:
- 使用更高效的架构:如 SwiGLU 激活、RoPE 位置编码、GQA 减少 KV 缓存、减少层数但加宽维度等,在相同计算量下获得更好性能。
-
使用动态稀疏训练或 MoE,但仅当需要参数扩展时。
-
数据与调度:
- 数据预处理缓存:提前将文本 tokenize 并保存为数组,避免在线 tokenize 开销。
-
提高数据读取的预取深度和 num_workers,确保 GPU 不会等待数据。
-
其他技巧:
- 减少日志打印频率和评估频率。
- 使用 torch.compile 或 TensorRT 对模型进行即时编译优化。
这些小优化叠加往往能带来 30%~200% 的实际训练吞吐提升。
当你把模型从单卡扩展到多卡时,训练精度下降,可能是什么原因?如何保证精度对齐?¶
精度下降(验证指标变差)可能源于:
-
通信延迟带来的异步更新:如果使用异步分布式训练(如异步 SGD),不同 worker 之间参数不一致可能导致精度下降。现代框架多采用同步训练,但若梯度累积步骤不同步仍可能出现类似问题。
-
Batch Size 增加但学习率未正确缩放:多卡导致全局 batch size 变大,若学习率没有按比例增加(遵循线性缩放规则),训练动态改变,可能收敛到较差的点。有时即使按规则调整,大 batch 仍可能降低泛化,需微调 base lr 或使用 LARS 等优化器。
-
不同 GPU 上的数值精度差异:如 FP16 下,每个 GPU 计算的 partial softmax、Allreduce 的累积顺序可能造成微小差异,但一般不会导致显著精度下降。若使用了 BF16,则类似 FP32,影响更小。
-
数据加载分片不均或数据分布偏差:DistributedSampler 保证各卡数据不重复,但若某个卡的数据包含噪声或分布极端,全局梯度可能受影响。需确保 shuffle 种子一致,或采用全局洗牌。
-
归一化层统计问题:BatchNorm 在多卡上需同步统计量,LayerNorm 通常无需。若错误使用 SyncBN 或未正确实现,会导致统计不准。
-
随机种子未固定:导致各卡初始化和 dropout 模式不同,虽然影响不大,但完全对齐需要设置相同种子并确保确定性。
保证精度对齐的方法:
-
严格遵循学习率缩放规则,并可能微调 warmup。
-
使用同步 BatchNorm(如果模型包含),或一律用 LayerNorm/GroupNorm。
-
在多卡环境下进行小规模对齐实验:比如先用极小的 lr 在单卡跑若干步,记录 loss 和梯度;多卡跑相同全局 batch,验证 loss 和更新量一致。
-
保持 FP32 的主权重同步,定期保存检查点。
-
禁用异步梯度 allreduce,确保所有卡同步。
模型训练后的评估结果与线上表现差距大,可能是什么原因?如何缩小差距?¶
这种 gap 常见于离线评估集不能真实反映线上分布。
原因:
-
训练/评估数据与线上数据分布不同:特征分布、文本风格、任务形式发生偏移(covariate shift / concept drift)。
-
评估指标设计不合理:离线指标(困惑度、BLEU、ROUGE)可能与实际业务目标(点击率、用户满意度)弱相关。
-
曝光偏差:评估时通常在标准测试集上静态测量,而线上是流式交互,模型之前的输出会影响后续上下文(误差累积)。
-
数据泄露或过度微调:离线评估集中包含了训练数据,导致指标虚高。
-
推理策略差异:训练时使用 Teacher Forcing,推理时自回归解码,存在差异;或解码参数(温度、top-k)不同,离线可能采用贪婪解码而线上使用随机采样。
-
批处理效应:离线可能逐条评估,线上可能采用 Continuous Batching,KV 缓存的管理差异可能影响输出。
-
量化或优化的精度损失:线上模型可能经过量化、图优化,导致输出与 PyTorch 原始模型有微小偏差,累积后放大。
缩小差距的方法:
-
构建更接近线上分布的评估集:定期采样线上真实请求,人工标注或弱监督评分;使用 online evaluation(A/B test)。
-
在微调阶段加入线上数据(如用户交互日志)进行领域适应,但需注意避免数据泄露和循环反馈。
-
引入策略梯度或强化学习,直接用业务奖励优化模型,但需谨慎平衡流畅性。
-
对齐推理环境:确保线上引擎(如 TensorRT-LLM)的精度与训练框架一致,进行数值对齐测试。
-
使用更好的离线指标:如多维度模型评价(对话质量、安全性、事实性),并配合人工评估。
-
采用解码时的误差纠正(如对比解码、自我纠正)来缓解推理与训练不一致。
给定一个预训练模型,要在下游任务上微调,你如何选择合适的学习率、warmup 和训练轮次?¶
学习率:
-
对于大模型的 full fine-tuning,学习率通常比预训练小 1~2 个数量级,如预训练 lr=3e-4,微调可设为 5e-5~5e-6,防止破坏预训练表征。
-
若使用 LoRA 或 Adapter 微调,学习率可适当提高(如 1e-4~5e-4),因为只训练少量参数。
-
方法:先跑一个小 grid search,在 {1e-5, 2e-5, 5e-5, 1e-4} 中测试,选择验证集最好的。可以使用 learning rate finder(逐步增大 lr 并记录 loss,选择下降最快区间)。
Warmup:
-
微调通常需要较少的 warmup,但若训练数据较少或学习率较大,设置 5%~10% 的步数作为 warmup 可防止初期梯度破坏权重。
-
对于全量微调,warmup 比例可设 0%~5%;对于 LoRA,可不设或设少量步数。
训练轮次:
-
取决于数据量大小。小数据集(几千条)一般 3~20 个 epoch,中型数据集可 1~5 个 epoch。
-
使用 early stopping 结合 dev 指标,patience 设为 3~5 个 epoch。
-
注意:过多轮次容易过拟合,尤其当数据量少时。
-
可以预先固定一个较大的轮次,但用验证指标保存最佳模型。
实操流程:
-
固定 batch size(硬件允许最大),选择合适的优化器(AdamW)。
-
小范围搜索学习率,同时设置 5% warmup 和余弦衰减。
-
根据验证曲线调整:若 loss 快速下降后平坦,可适当增加轮次;若上升,提前停止。
训练过程中,如果某个 GPU 利用率始终很低,你会如何诊断并解决?¶
GPU 利用率低(如 SM 使用率 < 50%)意味着存在瓶颈,可能是 CPU、IO、或通信。
- 诊断步骤:
- 使用
nvidia-smi查看功率和 SM 占用;nvtop或nvitop看实时状态。 - Profiling 工具:
nsys(Nsight Systems)查看 CPU/GPU 时间线,找出 GPU 空闲段是在等待数据加载、通信还是 kernel 启动开销。 - 检查
torch.utils.data.DataLoader的num_workers是否足够,pin_memory 是否开启。如果数据预处理耗时,GPU 会经常空闲。 - 确认是否有大量小的 kernel launch,应尝试算子融合。
- 若使用分布式训练,查看 NCCL 通信日志,检查是否某些 GPU 在 allreduce 时等待其他卡(负载不均衡导致)。
- 查看 batch size 是否过小,导致每次 kernel 调用未能填满 GPU 的并行能力;可尝试梯度累积增大有效 batch。
-
检查是否误将模型部分放在 CPU 上(如 Embedding 层使用
computeon CPU),造成频繁 CPU-GPU 拷贝。 -
解决方案:
- 数据瓶颈:增加 num_workers、使用预取和缓存、将数据提前转成
.pt格式、升级磁盘(SSD/NVME)。 - 计算瓶颈:增大 batch size,启用混合精度,使用 FlashAttention,调整模型架构以降低计算强度。
- 通信瓶颈:检查网络带宽(InfiniBand/以太网),使用梯度压缩或通信计算重叠(如 Overlap)。
- 框架层面:使用
torch.compile或 TensorRT 进行图优化。 - 确保没有不必要的
.item()或print在训练循环中引起同步。
设计一个实验来比较两种注意力机制(如 MHA 和 GQA)在相同规模下的性能,需要控制哪些变量?¶
为公平比较,必须隔离注意力机制这一个变量,其他条件保持一致。
- 模型架构变量:
- 总参数量:调整 GQA 模型的 FFN 维度或层数,使总参数量与 MHA 模型相等(因为 GQA 的 KV 投影参数更少,MHA 参数略多)。
- 但更常见的做法:保持隐藏维度 d、层数 L、FFN 维度完全相同,只改变注意力头数与 KV 头数,这样模型参数量有微小差异,但推理时更体现实际差异。如果追求严格参数相等,可微调 d_ff 补齐。
-
保证训练的总 FLOPs 一致?通常固定训练 token 总数即可。
-
数据与训练策略:
- 相同的训练数据集、tokenizer、epoch 数(或 token 数)。
- 相同的 batch size(全局)和学习率调度(可能需要微调以使各自最优,但最好先对齐)。
-
相同的优化器(AdamW)、warmup 步数、权重衰减、梯度裁剪阈值、混合精度设置。
-
评测指标:
- 预训练困惑度(验证集)。
- 下游任务微调后的性能(分类、问答等)。
-
推理速度与内存(KV 缓存大小、吞吐)。
-
实验步骤:
- 使用相同随机种子初始化(确保可重复),但可跑多个种子取平均以减少方差。
- 训练过程中监控 loss 曲线和困惑度。
- 最终报告验证困惑度和若干下游任务分数,以及推理时延/KV cache 占用。
- 进行统计显著性检验。
通过严格控制上述变量,可以客观评估 MHA 与 GQA 在性能、效率和内存之间的权衡。
如果你怀疑训练数据中存在大量噪声,如何在不重新标注的情况下减轻其影响?¶
-
鲁棒损失函数:使用对噪声不敏感的损失函数,如对称交叉熵、平均绝对误差 (MAE)、广义交叉熵、或者 label smoothing 可降低对错误标签的过拟合。
-
重加权样本:训练过程中,利用模型在验证集上的损失或基于预测不确定性,为样本分配权重,降低高损失(可能是噪声)样本的权重。或使用课程学习:先学“简单”样本,再逐渐加入难样本。
-
协同训练/自训练:用当前模型预测伪标签,再用置信度高的样本重新训练,或使用多个模型预测的均值作为软标签,逐步修正噪声。
-
利用预训练鲁棒性:预训练模型往往对噪声有一定容忍度,微调时使用较小的学习率,避免破坏预训练先验。
-
数据清洗:无需人工重标,但可自动检测异常:使用 perplexity、聚类、或现有模型找出明显错误(如分类任务中置信度很低但标签确定的样本,可能是噪声),移除或降权。
-
知识蒸馏:训练一个学生模型,使用教师模型的软标签(概率分布)而不是硬标签,软标签包含类别间相似性,能缓解部分噪声。
-
集成方法:训练多个模型并对输出进行平均,可平滑个别噪声影响。
这些方法可在一定程度上提高模型对噪声的鲁棒性。
在调整模型超参数(如层数、头数)时,如何利用 scaling law 来指导决策?¶
Scaling Law(如 Kaplan 定律、Chinchilla 定律)描述了模型性能与参数量 N、训练数据量 D、计算量 C 之间的幂律关系。利用它指导超参数选择:
-
确定计算预算 C(如 1e21 FLOPs)。
-
根据定律估算最优模型大小 N_opt 和最优训练 token 数 D_opt。例如 Chinchilla 给出 N_opt ∝ C^0.5, D_opt ∝ C^0.5,即参数与数据应等比缩放。
-
在确定 N_opt 后,需将总参数分配到层数 L、头数、维度 d 等。此时需参考已有架构的分配比例(如常见模型中 d 与 L 的关系:d ≈ √(N/L) 近似固定宽深比)。可基于前人消融实验或 Neural Architecture Search 确定具体形状。
-
若受限于推理延迟,需牺牲一些性能降低模型大小,增加数据量保持性能?scaling law 可估算出给定更小参数下所需的最优数据量,以及预期损失的下限,帮助做出量化取舍。
-
微调超参数时,可固定总参数量和训练数据,在几个备选配置(如不同 L/d 比例)上小规模实验,拟合局部 scaling 曲线,选择验证损失最小者。
-
注意区分“计算最优”与“推理最优”:推理成本与参数量、层数有关,scaling law 可外推到更大模型,预测性能,结合推理成本选择性价比最高的配置。
如果让你把一个英文模型适配到中文,训练步骤和技术要点是什么?¶
-
词表扩展:英文词表通常不包含足够的中文字符和中文子词,需要扩充词表(增加常用汉字、词)。采用 BPE 或 SentencePiece 在中文语料上训练新词表,然后将原英文模型的 token embedding 和 LM head 权重部分复制、剩余部分随机初始化,并对新嵌入进行维度对齐。
-
继续预训练 (Continual Pretraining):在大量中文文本(如几百 GB)上继续训练,使用与原始预训练相同的任务(如 MLM 或 LM)。训练策略:
- 分阶段:先冻结大部分参数,只训练新嵌入和 LM head,然后解冻所有参数全模型训练。
- 数据混合:开始时可混入部分英文数据防止遗忘,后续逐步增大中文比例。
-
学习率设置远低于原始预训练(如 5e-5),配合 warmup 和余弦衰减。
-
位置编码与架构调整:一般无需改动,但若模型使用绝对位置编码且中文序列通常较长,可考虑替换为 RoPE 或引入长上下文适应。
-
中文特性处理:中文无空格分词,tokenization 质量影响很大,应选择合理词表和分词器。另外可加入全角标点、中文特殊 token。
-
微调与对齐:针对中文下游任务(问答、对话)进行监督微调(SFT)和人类反馈对齐(RLHF/DPO),构建高质量中文指令数据。
-
评估:使用中文基准(C-Eval, CMMLU, MMLU-Chinese 等)评估知识能力,并用中文对话人工评估流畅度和安全性。
-
技术要点:防止灾难性遗忘,平衡数据比例,新的嵌入训练要足够充分;可能需要多次迭代逐步适应中文。
结合你的项目经验,讲述一次你解决 Transformer 训练或部署中最棘手问题的过程。¶
(以下是一个典型经验描述,请根据实际情况替换)
在一项 LLM 部署到在线服务的项目中,我们遇到用户请求间歇性出现极高延迟(P99 从 200ms 飆升至 5s 以上),但 P50 保持稳定。通过以下步骤解决:
-
现象复现与 profiling:使用批量压测工具,模拟真实请求分布。利用
nvprof和Nsight Systems抓取时间线,发现某些迭代中AllReduce耗时突然增加到几秒,对应 GPU 利用率骤降。 -
定位瓶颈:分析通信日志,发现某几张卡在 allreduce 时一直在等待另外一张卡,那张卡的计算完成时间远晚于其他卡。进一步检查模型分布,发现模型某些层被切分在不同的 NVLink 域上,导致通信跨 NUMA 节点。
-
根因:在多节点部署中,我们使用了 Tensor Parallelism (TP) 跨 4 个 GPU,但容器编排时将其中两个 GPU 分配到了不同 NUMA 节点,NVLink 带宽受限,造成通信时间变长。某些序列长度变长时,注意力计算负载不均衡,加剧了拖后腿效应。
-
解决方案:
- 调整 GPU 亲和性,确保 TP 组内的 GPU 位于同一 NUMA 节点并启用 P2P 访问。
- 优化模型配置:改为 TP=2, PP=2,减少单组通信量。
- 开启 FlashAttention 和 PagedAttention,提高计算效率和显存利用率。
-
在推理服务中加入请求分桶,避免长短序列混合批次。
-
结果:P99 延迟回落到 300ms 以内,系统吞吐提升 40%。整个过程强化了分布式推理中网络拓扑感知的重要性。
这件事教会我要将系统 profiling 与模型计算特征结合,排查硬件拓扑带来的性能问题。
当你有一个在通用语料上预训练的模型,要把它适配到法律领域,你会采用哪些步骤?从数据、训练策略到评估。¶
数据准备
-
收集法律语料:获取大量高质量的原始法律文本,包括法律法规、司法解释、判决文书、合同范本、法学专著、法律新闻等。确保数据时效性和地域性符合目标(如中国法律、美国法律)。
-
清洗与去重:去除格式混乱、乱码、重复段落、非法律夹杂内容。使用 MinHash 或 SimHash 进行文档级去重。
-
构造预训练数据:可将长文档直接作为无监督语料进行继续预训练(continual pretraining)。同时,对于结构化数据(如判决书),可提取“法院认为”“本院查明”等关键字段,构造分类或阅读理解监督任务。
-
监督指令数据(用于微调):为适应实际法律应用,构建法律问答、摘要、要素抽取、争议焦点分析等指令数据。可基于法规条款生成问答对(将条款转换为问题),利用已有裁判文书提炼问题与答案。也可让法律专业人员少量标注高质量 seed data,再用模型自我迭代(self-instruct)扩充。
训练策略
- 继续预训练(domain-adaptive pretraining):
- 在通用预训练模型基础上,使用法律语料进行持续语言模型训练(MLM 或自回归)。学习率设置为预训练阶段的 1/10~1/5,例如 5e-5,配合 warmup 和余弦衰减。
- 数据混合:初期可混入部分通用数据(如 20%),防止灾难性遗忘;后期可全部使用法律数据。
-
若显存或时间有限,可结合 LoRA 或 Adapter 只训练少量参数,实现高效领域适配。
-
多任务微调:
- 在多种法律任务上联合微调,如判决预测、类案匹配、要素抽取、法律问答等,共享语言知识。使用统一的文本到文本格式。
-
对于生成任务,引入法律术语约束、法条引用正确性等辅助损失或强化学习奖励。
-
强化学习对齐(可选):若需生成严谨的法律文书,可使用 RLHF/DPO,让专业律师对模型输出进行质量排序,训练 reward model 并优化模型,提升法律事实准确性、逻辑严谨性和格式规范性。
评估
- 自动指标:
- 困惑度(PPL)在法律测试集上评估语言建模能力。
-
各个下游任务的专用指标:法规问答的准确率、F1;判决摘要的 ROUGE、BERTScore;要素抽取的 Micro-F1 等。
-
法律专业评测基准:如 LawBench、LexGLUE、LawGPT 相关测试集,覆盖法律推理、阅读理解、分类等。
-
人工评估:由法律专家对模型生成的法律意见、合同条款、案例分析进行准确性(法条引用是否正确)、逻辑性、无幻觉率的评判。
-
一致性与安全性:检查在不同表述下是否输出一致的法律结论,是否存在违法或违背公序良俗的内容。
迭代优化
根据评估反馈,收集 hard cases,扩充训练数据,尤其侧重模型易错的法律概念和长尾场景,再进行下一轮训练。
模型在测试集 A 上好,但在测试集 B 上差,如何分析是过拟合还是领域偏移?设计对比实验。¶
判定步骤
-
检查训练集与 A、B 的重叠:计算训练集与 A/B 的 n-gram 重叠率、文档相似度。若 A 与训练集高度重叠,而 B 无明显重叠,很可能是领域偏移(分布不一致)而非过拟合。
-
观察训练/验证曲线:若训练过程中,A 集性能持续提升而 B 集性能先升后降,且 B 集与验证集趋势一致,则偏向过拟合;若 B 从一开始就表现差,且不随训练改善,更可能是领域差异。
-
采样分析:随机抽取 A 和 B 中样本,人工观察它们与训练数据的风格、主题、词汇差异。若 B 的语言结构、实体类型完全不同,为领域偏移。
对比实验设计
-
跨评估集泛化实验:在训练集上训练模型,分别在 A、B 以及两者混合的测试集上评估。同时,使用简单基线(如 TF-IDF+LR)在 A 和 B 上测试,若基线在 A、B 差距同样大,说明数据本身可分性不同(B 更难),不完全是模型问题。
-
领域对抗实验:固定训练数据(来源领域 D_train)。测试集 A 与 D_train 同分布,B 不同分布。额外构造一个与 D_train 不同分布但无标签的新测试集 C,若模型在 B 和 C 都差,确认为领域偏移。
-
正则化与训练策略对比:
- 实验组 1:加入 Dropout、权重衰减、早停等措施,观察在 A 和 B 上的变化。若过拟合,这些方法会改善 B 的性能(或减缓 B 的下降)。
-
实验组 2:在训练集中混入少量 B 领域的无标签数据(或少量标注数据)进行领域适应,如继续预训练或微调,若 B 性能提升,说明领域偏移是主因。
-
跨领域微调实验:使用预训练模型在 A 域数据上微调得到 M_A,在少量 B 域数据上微调得到 M_B。对比 M_A 与 M_B 在 B 测试集上的性能。若 M_B 大幅优于 M_A,说明存在领域偏移,而模型有能力迁移但缺乏 B 域训练。
通过以上多维度对比,即可区分是训练过度导致泛化差,还是分布根本不同。
如果模型在训练初期收敛很快,但不久后损失平台期,且波动大,你会调整什么超参数?¶
这种现象通常意味着学习率过高或 batch 内梯度噪声过大,导致在损失表面难以稳定下降。可以从以下方面调整:
-
降低学习率:当损失进入平台期且波动大时,首先尝试将学习率减小 2~5 倍,或使用学习率衰减(如 step decay、余弦退火)。初期收敛快说明学习率足够甚至偏大,后期需要更小 lr 精细搜索。
-
增加 warmup 步数:如果初期学习率从 0 线性上升到峰值,warmup 不足可能导致前期不稳定。但若已到平台期,warmup 影响有限。
-
增大 batch size(全局):更大的 batch size 能提供更准确的梯度估计,降低梯度噪声方差,使更新更平稳。但需同步调整学习率(按线性缩放法则增大 lr)。若硬件不允许增大 batch,可增加梯度累积步数。
-
调整优化器参数:对于 Adam,可降低 β2(如从 0.999 到 0.99)减少对历史梯度平方的依赖,或适当增大 ε 防止除零精度问题。但一般较少动。
-
梯度裁剪阈值:如果波动伴随偶尔的损失尖峰,可能是梯度爆炸,需降低裁剪阈值(如从 1.0 降到 0.5)。
-
正则化考量:若 weight decay 过大,可能限制模型容量,可尝试减小;若过小,可能发生震荡。
-
检查数据:确认是否由于数据 shuffle 不够或存在大量相似样本,导致某些 batch 梯度方向冲突。加强数据打乱,或使用课程学习平滑训练。
-
调整模型结构:若上述无效,可能是模型容量不足,可考虑适当增加层数或宽度,但更可能是超参数问题。
一般优先尝试降低学习率并配合余弦衰减,往往能稳定平台期并继续收敛。
假设训练速度受限于数据加载,如何设计一个高效的流式数据管线?需要预取、异步、缓存哪些内容?¶
核心思路:使 GPU 永远不等待数据,CPU 端提前准备好下一批数据,并通过流水线隐藏 I/O 延迟。
方案设计:
-
数据集格式优化:将原始文本提前 tokenize 并保存为二进制格式(如 .npy, .bin, 或 HuggingFace arrow/parquet),避免在线分词开销。若数据过大,可采用内存映射(mmap)文件。
-
多进程并行加载:
DataLoader设置num_workers > 0,通常 4~8 个 worker。每个 worker 独立读取并预处理自己的 batch。确保pin_memory=True,加速 CPU→GPU 传输。 -
预取和重叠:设置
prefetch_factor(每个 worker 提前准备多少 batch),常用 2~4。这样在 GPU 处理当前 batch 时,CPU 已经在准备后续 batch。 -
异步传输:使用 CUDA 流(stream),将 CPU tensor 异步拷贝到 GPU,与 GPU 计算重叠。如 PyTorch 的
non_blocking=True+cuda.Stream。 -
缓存机制:
- 在内存中缓存预处理好的张量(如果数据集能放进内存),避免每次读磁盘。
-
对于大规模语料,使用基于 tfrecord/parquet 的列存,顺序读取,预读取 chunk。
-
流水线架构:可以自定义可迭代的 Dataset,内部维护缓冲队列(
queue.Queue)。主线程从队列获取 batch,worker 线程不断填充。达到高吞吐。 -
分布式共享:在多 GPU 训练中,使用
DistributedSampler保证各卡读取不同数据。各卡都有独立 DataLoader,并行读取。
避免瓶颈:
-
确保硬盘 SSD/NVME 足够快,或分布式存储缓存到本地 NVME。
-
避免复杂的在线数据增强(如大量文本替换)在 worker 中执行,尽量预处理成静态数据。
-
若使用网络存储,考虑在每台机器上本地缓存常用数据子集,或使用 Alluxio 等分布式缓存加速读取。
通过上述方法,可以实现接近计算瓶颈的高效数据供给,使训练吞吐大幅提升。
在分布式训练中,经常遇到“某个 GPU 挂起”的问题,你的排查流程是什么?¶
分布式训练中某 GPU 挂起(Hang)表现为整个训练停滞,卡在某一步不继续。排查流程:
- 确认挂起状态
- 使用
nvidia-smi看所有 GPU 利用率,挂起的 GPU 往往利用率 0% 或 100%(死循环)。 -
使用
torch.distributed.barrier()等方法检测是否所有进程到达同步点。挂起通常是某个 rank 未到达。 -
收集基础信息
- 检查各节点日志,查看最后输出。挂起通常发生在集合通信操作(
all_reduce,all_gather等)或 DataLoader 迭代。 -
确认各 rank 的 Python 进程是否存活,有无 OOM killed 或者段错误。
-
定位阻塞点
- 若可能,attach debugger(如
py-spy、gdb)到卡住的进程,查看调用栈。对于 Python,可用faulthandler或py-spy dump看当前执行到哪行代码。 - 检查 NCCL 环境变量:设置
NCCL_DEBUG=INFO重新运行,查看通信日志,常见错误如网络连接失败、拓扑不匹配等。 -
使用
torch.distributed.monitored_barrier定制超时错误,方便定位哪个 rank 未完成。 -
常见原因与解决
- NCCL 通信超时:可能是某节点网络不通或网卡故障,检查
ibstat(InfiniBand)或ifconfig,尝试用NCCL_SOCKET_IFNAME指定正确接口。 - 数据加载不均衡:某些 worker 的 DataLoader 未正确退出,导致迭代器 hang。需检查
DistributedSampler的 epoch 设置和num_workers是否合理。 - 显存不足导致进程 kill:检查系统日志
dmesg看有无 OOM killer,或nvidia-smi显存满导致 CUDA 操作挂起。 - 逻辑 bug:如 barrier 被放置在不平衡的条件分支内。确保所有 rank 执行相同次数 barrier。
-
死锁:在使用 CUDA 流或异步拷贝时,同步错误可能导致 hang,可尝试关闭异步,使用
cuda.synchronize()排查。 -
预防措施
- 启用
NCCL_TIMEOUT和TORCH_DISTRIBUTED_TIMEOUT环境变量,超时后抛出异常,防止无限挂起。 - 训练框架使用
watchdog监测 GPU 状态,异常时自动重启。
一旦通过日志和堆栈确定是通信还是数据问题,针对性地调整网络配置或修复代码。
如果要复现一篇论文中的 Transformer 模型,但缺少关键超参数,你会如何逆向推断?¶
缺少的超参数可能包括:学习率、batch size、warmup 步数、dropout、权重衰减、优化器参数(β1,β2)、层数、头数、FFN 维度、序列长度、tokenizer 细节等。
-
从论文文本和附录寻找线索:仔细阅读实验设置部分,可能部分参数被提及;查看图表(学习率曲线图);看开源代码(如果有引用仓库)。
-
根据硬件和规模推断 batch size:若论文提到使用了多少 GPU,训练了多少小时,模型大小,可结合当时典型吞吐大致估算全局 batch size。也可通过模型大小和显存限制反推可能的 batch size。
-
学习率推断:通常与 batch size 相关。常见预训练 BERT 类 lr 1e-4, GPT 类 3e-4~6e-4。根据论文模型大小和 batch size,参考同期工作设定。可以使用 linear scaling rule 由标准配置推导。
-
dropout / weight decay:常见值如 dropout 0.1, weight decay 0.01。如果论文未提,可能是默认值。可查阅作者其他论文或同期模型惯例。
-
模型结构超参数:层数、维度、头数通常会被给出。若无,可根据参数量范围大致假设:例如 6 层 768 为 BERT-base,12 层 768 为 BERT-large 等。但需结合论文上下文(年代、任务)猜测。
-
数值实验反推:搭建可调节的框架,在类似的数据集上做小规模 grid search,使得训练曲线(loss 下降速度、最终性能)与论文报告值匹配。尤其注意 loss 曲线的形状。例如,如果论文显示 loss 在 10k steps 内下降到某个值,可以调 lr 使自己在相同数据上达到类似值。
-
联系作者:如果条件允许,发邮件询问或在该论文的 GitHub issue 提问,这是最直接的方式。
-
复现的最终验证:用推断出的超参数训练,若能达到论文报告的性能指标(或在其误差范围内),则推断基本正确;否则继续微调关键超参数。
在逆向过程中,保持详细的实验记录,每次只变一个关键参数,逐步逼近原文设置。
你想尝试一种新的注意力变体,如何快速低成本地进行小规模实验验证有效性?需要控制哪些变量?¶
低成本验证路径
- 选择小规模测试平台:
- 采用参数量较小的模型,如 6 层 Transformer,隐藏维度 384,头数 6。
-
使用有限但有代表性的数据集:如 WikiText-2 或小样本下游任务(如 1% 训练数据的 GLUE 子集)。
-
设计对比基准:
- 基线:标准 MHA 注意力的相同结构模型。
-
新注意力变体:只替换注意力机制,保持其他层完全一致。
-
控制变量:
- 模型架构:层数 L,隐藏维度 d,头数 h,FFN 维度,归一化类型(Pre-LN),位置编码方式需完全相同。
- 训练配置:优化器、学习率、batch size、训练步数(或数据量)、warmup 策略、权重衰减、dropout 等完全一致。
- 随机种子:可固定单个种子先观察,之后再跑多个种子看统计显著性。
-
数据和 tokenizer:完全相同。
-
评测指标:
- 预训练困惑度(PPL)。
- 下游微调任务的性能(准确率/F1)。
-
速度和内存:每步训练时间、峰值显存、推理吞吐,以评估效率和加速比。
-
快速实验循环:
- 先在极小数据(如几百万 token)上训练少量步数,迅速观测 loss 下降趋势和速度。若新注意力 loss 不降甚至上升,可能实现有误。
- 若初步有效,再扩展训练步数到完整小规模数据,比较最终 PPL 和下游性能。
-
可结合 profiling 工具分析计算瓶颈。
-
迭代改进:根据小规模结果快速调整超参数或注意力设计,多次迭代。
关键控制变量总结:模型非注意力结构、优化器、学习率、数据、种子。只将注意力机制作为独立变量。同时报告加速比和内存开销。
训练一个多语言模型时,语言间的数据极度不平衡,你会采用采样策略还是损失加权?各自有什么风险?¶
采样策略(数据过采样/降采样)
-
温度采样:根据各语言数据量比例,引入一个温度参数 T,定义采样概率 pi∝(ni∑n)1/Tpi∝(∑nni)1/T。T=1 即原始分布,T>1 向低资源语言倾斜。
-
固定阈值截断:对高资源语言设置采样上限(如英语最多采样 10 亿 token),低资源语言重复使用直至达到最低 token 数。
-
风险:过采样低资源语言可能导致模型对它们过拟合,且重复数据降低多样性;降采样高资源语言可能浪费可用数据,损失高资源语言性能。低资源语言的生成质量可能仍因数据太少而欠佳。
损失加权(在损失函数中对不同语言赋权重)
-
给低资源语言的 loss 乘上一个大于 1 的权重 wlangwlang,高资源语言权重小于 1。通常取权重的倒数比例或平方根倒数。
-
风险:权重过大会导致低资源语言产生较大梯度,干扰模型优化,可能导致整体困惑度上升,训练不稳定。且加权不改变每个语言实际看到的 token 数,模型参数更新仍由高频语言主导,低资源语言可能无法被充分学习。
混合策略:实践中常结合使用,如温度采样控制数据分布,同时在小 batch 内对不同语言进行动态损失加权(自适应平衡)。还可配合 多任务学习,添加语言识别辅助任务,迫使模型保留语言特征。
风险评估与选择:
-
若极度不平衡(如高资源语言占比 90% 以上),推荐采样策略保证低资源语言有足够曝光,同时设置最大重复比例(如重复不超过 5 次)防止过拟合。
-
若需要维护高资源语言的 SOTA 性能,可仅用采样策略稍微提升低资源比例,并辅以轻微损失加权。
-
最终需通过验证集(每种语言独立)来监控各语言性能,决定调整方案。
当模型规模扩大 10 倍,按照 scaling law,数据量和计算量应该扩大多少?如果数据不够怎么办?¶
根据 Chinchilla scaling law(计算最优),参数量 NN 和训练 token 数 DD 应大致等比增长:Nopt∝C0.5Nopt∝C0.5,Dopt∝C0.5Dopt∝C0.5。因此,若模型参数量 N 扩大 10 倍,为保持计算最优,训练 token 数 D 也应扩大约 10 倍(理论上 10×10=1010×10=10 倍计算量 C)。换言之,模型扩大 10 倍,总计算量 C 需扩大约 100 倍(因为每个 token 的训练计算量约正比于 N)。所以数据量应扩大 10 倍,计算量扩大 100 倍。
如果数据量不够(无法获得 10 倍数据):
-
接受“过度训练”:使用较少数据但多训几个 epoch(重复数据)。缩放定律指出,对于给定数据量 D,存在一个最佳模型大小 N_opt(D),小于计算最优的 N。但若 N 已固定,过多 epoch 会导致过拟合。Chinchilla 论文也分析了数据受限下的缩放:在 data-constrained 场景,性能提升随重复而递减。可以适当增加正则化(dropout, weight decay)来减轻过拟合。
-
数据增强与合成:利用已有数据生成伪数据,如同语言翻译、回译、文本改写,但需保证质量。
-
多任务/多语言联合训练:融入其他语言或相关任务的数据,提升 token 总量。
-
降低模型部分容量:若不需极致性能,可削减层数或维度,使模型大小匹配可用数据量,遵循最优配比。
-
注重数据质量:高质量数据能有效降低对数量的需求,用更干净、更多样的数据替代低质量大量数据。
-
调整训练策略:使用早停、强正则化、或先在大规模弱相关数据上预训练,再在高质量核心数据上微调。
总之,如果数据不足,性能会偏离计算最优,但可通过质量提升和正则化尽量逼近。
在推理部署中,发现模型的性能(如准确率)比离线测试低很多,如何逐步定位原因?¶
当线上实际表现明显弱于离线测试时,需沿整个链路排查:
- 确认评估一致性
- 检查线上和离线评估的数据分布是否一致。离线测试集应尽可能模拟线上真实请求。对比线上采集的一批请求,人工评估并与离线测试集对比难度、主题、格式差异。
-
确认评估方式:离线可能使用自动指标(如 BLEU、准确率),而线上用人工或用户反馈,两者可能不等价。需统一评价标准。
-
检查模型导出与转换
- 比较原始训练框架(PyTorch)与线上推理引擎(TensorRT-LLM, ONNX Runtime, vLLM 等)的输出。用相同输入跑一遍,计算 logits 的差异(MSE 或余弦相似度)。差异应极小(<1e-3)。若差异大,可能是图优化、量化、或算子融合导致精度损失。
- 特别检查注意力实现:是否使用了 FlashAttention、不同的 masking 方式,导致数值微小差异累积。
-
检查 KV cache 实现:多轮对话中,KV cache 的存储格式、滑动窗口裁剪、或 PagedAttention 的块管理是否引入逻辑错误。
-
检查推理配置
- 解码参数:温度、top-k、top-p、重复惩罚等是否与离线测试时完全一致。不一致会导致输出分布变化。
- 最大长度、停止 token 处理是否正确。线上可能因过早截断导致答案不完整。
-
Prompt 模板:线上服务可能包裹了额外的 system prompt 或 formatting,与离线测试不同。
-
检查数据预处理
- Tokenizer 是否一致(特殊 token、BOS/EOS 处理),线上可能使用了不同的 tokenizer 版本或做了额外的清洗。
-
输入截断策略:离线测试可能允许无限长,线上可能强制截断到固定长度,丢失关键信息。
-
线上环境特殊因素
- 请求并发下的批处理效应:Continuous Batching 可能导致某些请求的序列被提前终止或被交换出 KV cache,影响输出。
- 系统资源竞争:CPU/GPU 繁忙导致超时重试,可能使用了简化的快速路径。
-
随机种子:若模型中使用了随机采样且未固定种子,不同采样结果可能影响评价,但不应导致显著准确率下降。
-
外部依赖问题
- 如果线上使用了检索增强(RAG),检查检索组件的正确性、知识库的时效性。
- 若涉及工具调用或代码执行,确保环境一致。
定位流程:抓取一组线上请求和对应的离线输出,逐一比对差异。通过消融实验(如关闭 RAG、固定采样种子、使用原始 PyTorch 模型直接推理相同请求)逐步缩小范围,最终精确定位到具体环节。