代码之家  ›  专栏  ›  技术社区  ›  Nathan

如何将PyTorch子模块保持在评估模式?

  •  0
  • Nathan  · 技术社区  · 6 年前

    我有一个经过训练的模型,我正在和一个正在训练的模型一起使用。我希望预训练模型始终处于评估模式,但另一个模型将在评估和训练模式之间来回移动。不过,我仍然希望预训练模型是另一个模型的子模块(例如,使所有参数保持在同一个设备上)。有办法做到这一点吗?下面是一个简单的例子:

    from torch import nn
    
    class FixedModule(nn.Module):
        pass
    
    class TrainableModule(nn.Module):
        def __init__(self, fixed_module):
            super().__init__()
            self.fixed_module = fixed_module
    
    fixed = FixedModule().eval()
    assert not fixed.training
    
    trainable = TrainableModule(fixed)
    assert trainable.training and not trainable.fixed_module.training
    
    trainable.train()
    assert trainable.fixed_module.training  # I'd like this to give an error
    

    我知道我可以解决这个问题,例如,一直做

    trainable.train()
    trainable.fixed_module.eval()
    

    但这很容易出错,而且不能很好地与现有代码配合使用。

    0 回复  |  直到 6 年前
        1
  •  1
  •   adamconkey    6 年前

    一个解决方案是覆盖 train 这样地:

    from torch import nn
    
    class FixedModule(nn.Module):
        pass
    
    class TrainableModule(nn.Module):
        def __init__(self, fixed_module):
            super().__init__()
            self.fixed_module = fixed_module
    
        def train(self):
            super().train()
            self.fixed_module.eval()
    
    fixed = FixedModule().eval()
    assert not fixed.training
    
    trainable = TrainableModule(fixed)
    assert trainable.training and not trainable.fixed_module.training
    
    trainable.train()
    assert trainable.fixed_module.training  # This gives an error now
    
        2
  •  0
  •   Nathan    6 年前

    你可以覆盖 train 在里面 FixedModule 防止它改变模式。注意 eval 只是打电话 train(False) ,所以你也不需要覆盖它。但是打电话 FixedModule.eval 我现在什么都做不了,所以你得准备好 training = False 在init中。

    from torch import nn
    
    class FixedModule(nn.Module):
        def __init__(self):
            super().__init__()
            self.training = False
    
            # add any other nn.Module attributes here before calling self.children
    
            # you could override `train` in each child too if you really wanted,
            # but that seems like overkill unless there are external references
            # to any submodules of FixedModule
            for module in self.children():
                module.eval()
    
        def train(self, mode):
            return self
    
    class TrainableModule(nn.Module):
        def __init__(self, fixed_module):
            super().__init__()
            self.fixed_module = fixed_module    
    
    fixed = FixedModule().eval()
    assert not fixed.training
    
    trainable = TrainableModule(fixed)
    assert trainable.training and not trainable.fixed_module.training
    
    trainable.train()
    assert not trainable.fixed_module.training # passes