PyTorch中固定随机种子同时保留Dropout随机性的实现方法
如何在固定随机种子保证可复现性的同时,维持Monte Carlo Dropout的随机性?
我尝试通过在训练和推理阶段均保留dropout概率(Monte Carlo dropout)来近似贝叶斯模型,以获取模型的认知不确定性。目前已设置随机种子保证可复现性,但运行代码后每次结果均一致,无法保留dropout的随机性。
# Set random seed for reproducibility seed = 123 torch.manual_seed(seed) random.seed(seed) np.random.seed(seed) # Training and Inference phase (with dropout) dropout_mask = torch.bernoulli(torch.full_like(input, 1 - self.dropout)) skip = self.skip0(input * dropout_mask / (1 - self.dropout)) for i in range(self.layers): residual = x filter = self.filter_convs[i](x) filter = torch.tanh(filter) gate = self.gate_convs[i](x) gate = torch.sigmoid(gate) x = filter * gate dropout_mask = torch.bernoulli(torch.full_like(x, 1 - self.dropout)) x = x * dropout_mask / (1 - self.dropout) s = x s = self.skip_convs[i](s) skip = s + skip if self.gcn_true: x = self.gconv1[i](x, adp) + self.gconv2[i](x, adp.transpose(1, 0)) else: x = self.residual_convs[i](x) x = x + residual[:, :, :, -x.size(3):] if idx is None: x = self.norm[i](x, self.idx) else: x = self.norm[i](x, idx) skip = self.skipE(x) + skip x = F.relu(skip) x = F.relu(self.end_conv_1(x)) x = self.end_conv_2(x) return x
问题根源
你当前的代码每次运行(或每次推理)都重置了全局随机种子,导致torch.bernoulli生成的dropout掩码完全一致。要兼顾可复现性和dropout随机性,关键是只在实验初始化阶段固定一次种子,而非每次推理都重置。
解决方案
调整种子初始化时机
将随机种子设置代码移到脚本的最开头、模型初始化/数据加载之前,仅执行一次。这样整个实验的初始化过程(模型权重初始化、数据 shuffle 等)是可复现的,但单次实验内的多次推理会保留随机源的状态变化,从而产生不同的dropout掩码。改用PyTorch内置
nn.Dropout模块
手动实现dropout容易出错,内置模块会自动处理训练/推理模式的切换,只需在推理阶段保持模型处于train()模式即可维持随机性:# 仅在脚本开头执行一次种子初始化 seed = 123 torch.manual_seed(seed) random.seed(seed) np.random.seed(seed) # 若使用GPU,需额外设置CUDA种子 torch.cuda.manual_seed(seed) torch.cuda.manual_seed_all(seed) # 模型定义中使用nn.Dropout class YourModel(nn.Module): def __init__(self, dropout_prob=0.5): super().__init__() self.dropout = nn.Dropout(dropout_prob) # 其余层定义(skip0、filter_convs等)... def forward(self, input, adp=None, idx=None): # 推理阶段保持dropout随机性,需确保模型处于train()模式 skip = self.skip0(self.dropout(input)) x = ... # 你的初始x赋值逻辑 for i in range(self.layers): residual = x filter = self.filter_convs[i](x) filter = torch.tanh(filter) gate = self.gate_convs[i](x) gate = torch.sigmoid(gate) x = filter * gate # 用内置dropout替代手动生成掩码 x = self.dropout(x) s = x s = self.skip_convs[i](s) skip = s + skip if self.gcn_true: x = self.gconv1[i](x, adp) + self.gconv2[i](x, adp.transpose(1, 0)) else: x = self.residual_convs[i](x) x = x + residual[:, :, :, -x.size(3):] x = self.norm[i](x, self.idx if idx is None else idx) skip = self.skipE(x) + skip x = F.relu(skip) x = F.relu(self.end_conv_1(x)) x = self.end_conv_2(x) return x推理时的正确操作
执行Monte Carlo dropout推理时,不要调用model.eval(),而是保持model.train()状态:model = YourModel(dropout_prob=0.5) # 加载训练好的权重... model.train() # 维持dropout随机性 # 多次推理获取不同结果 for _ in range(10): output = model(input_data) # 处理输出(计算不确定性等)
关键注意事项
- 不要在循环(推理/训练循环)内重复设置随机种子,这会强制重置随机状态,导致每次dropout掩码完全相同。
- 若需要复现整个实验的结果,重新运行整个脚本即可(此时种子会重新初始化,保证实验流程一致)。
内容的提问来源于stack exchange,提问作者Travelling Salesman
相关产品推荐
相关产品推荐

