BERT下一句预测(NSP)代码每次运行输出随机结果问题排查
你每次运行得到不同损失、不同推理结果,是两个核心配置遗漏导致的,不存在NSP任务逻辑层面的代码错误:
未全局固定所有随机源的随机种子
你的代码从数据集构造阶段就存在大量随机逻辑:包括正样本对的起始位置选择、50%概率采样正负样本、负样本的随机选取、DataLoader的批次打乱,再加上PyTorch、CUDA本身的随机数生成器,所有这些环节默认每次运行都会生成不同的随机值,直接导致每次训练的数据集组成、批次顺序、参数更新路径都不一样,损失和最终权重自然有差异。
你需要在所有代码逻辑执行的最开头(导入完依赖之后)加入固定种子的代码:import random import numpy as np import torch seed = 42 random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False如果后续你给DataLoader设置了大于0的
num_workers开启多进程加载,还需要额外给DataLoader传入worker_init_fn参数固定每个子进程的种子,避免多进程带来的随机波动。如果你使用PyTorch 1.8以上版本,也可以额外加一行torch.use_deterministic_algorithms(True)开启全局确定性算法,进一步避免个别算子的非确定计算带来的结果差异。推理阶段未切换模型到评估模式
你训练完之后直接执行推理,此时模型还处于model.train()开启的训练模式,BERT结构中内置的Dropout层会随机失活部分神经元,哪怕输入完全相同、模型权重完全一致,前向计算的输出也会随机波动。
推理前需要先切换模型到评估模式,同时关闭梯度计算减少额外开销:# 推理前必须先执行这行切换模式 model.eval() # 推理代码放在no_grad上下文中 with torch.no_grad(): prompt = "sentence 1 text" prompt2 = "sentence 2 text" output = tokenizer.encode_plus(prompt,prompt2, return_tensors="pt").to(device) result = model(**output)[0] prob = softmax(result, dim=1) print(prob)
完成以上两个修改后,你每次运行的训练损失变化、最终模型权重、相同输入的推理概率输出都会完全一致,符合可复现要求。
内容的提问来源于stack exchange,提问作者not_yet_a_fds

