You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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随机性,关键是只在实验初始化阶段固定一次种子,而非每次推理都重置。

解决方案

  1. 调整种子初始化时机
    将随机种子设置代码移到脚本的最开头、模型初始化/数据加载之前,仅执行一次。这样整个实验的初始化过程(模型权重初始化、数据 shuffle 等)是可复现的,但单次实验内的多次推理会保留随机源的状态变化,从而产生不同的dropout掩码。

  2. 改用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
    
  3. 推理时的正确操作
    执行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:57:53