pytorch 如何设置 .requires_grad False

Posted

技术标签:

【中文标题】pytorch 如何设置 .requires_grad False【英文标题】:pytorch how to set .requires_grad False 【发布时间】:2019-01-15 19:18:32 【问题描述】:

我想冻结我的一些模型。按照官方文档:

with torch.no_grad():
    linear = nn.Linear(1, 1)
    linear.eval()
    print(linear.weight.requires_grad)

但它会打印 True 而不是 False。如果我想将模型设置为eval模式,我该怎么办?

【问题讨论】:

The documentation 有一个简单的微调示例,应该会对您有所帮助。 【参考方案1】:

requires_grad=False

如果您想冻结部分模型并训练其余部分,可以将要冻结的参数的requires_grad 设置为False

例如,如果你只想保持 VGG16 的卷积部分固定:

model = torchvision.models.vgg16(pretrained=True)
for param in model.features.parameters():
    param.requires_grad = False

通过将requires_grad 标志切换为False,将不会保存任何中间缓冲区,直到计算到达某个操作的输入之一需要梯度的点。

torch.no_grad()

使用上下文管理器torch.no_grad 是实现该目标的另一种方法:在no_grad 上下文中,所有计算结果都将具有requires_grad=False,即使输入具有requires_grad=True。请注意,您将无法将渐变反向传播到no_grad 之前的图层。例如:

x = torch.randn(2, 2)
x.requires_grad = True

lin0 = nn.Linear(2, 2)
lin1 = nn.Linear(2, 2)
lin2 = nn.Linear(2, 2)
x1 = lin0(x)
with torch.no_grad():    
    x2 = lin1(x1)
x3 = lin2(x2)
x3.sum().backward()
print(lin0.weight.grad, lin1.weight.grad, lin2.weight.grad)

输出:

(None, None, tensor([[-1.4481, -1.1789],
         [-1.4481, -1.1789]]))

这里 lin1.weight.requires_grad 是 True,但没有计算梯度,因为操作是在 no_grad 上下文中完成的。

model.eval()

如果您的目标不是微调,而是将模型设置为推理模式,最方便的方法是使用torch.no_grad 上下文管理器。在这种情况下,您还必须将模型设置为 评估 模式,这是通过在 nn.Module 上调用 eval() 来实现的,例如:

model = torchvision.models.vgg16(pretrained=True)
model.eval()

此操作将层的属性self.training 设置为False,实际上这将改变DropoutBatchNorm 等操作的行为,这些操作在训练和测试时的行为必须不同。

【讨论】:

感谢您的解释。 torch.no_grad() 上下文管理器和t.requires_grad=False 之间是否存在效率差异,尤其是在内存效率方面?正如你之前提到的,t.requires_grad=False不会保存中间缓冲区,会不会更高效? 它们是等价的【参考方案2】:

这里是路;

linear = nn.Linear(1,1)

for param in linear.parameters():
    param.requires_grad = False

with torch.no_grad():
    linear.eval()
    print(linear.weight.requires_grad)

输出:错误

【讨论】:

【参考方案3】:

要完成@Salih_Karagoz 的回答,您还拥有torch.set_grad_enabled() 上下文(更多文档here),可用于在训练/评估模式之间轻松切换:

linear = nn.Linear(1,1)

is_train = False

for param in linear.parameters():
    param.requires_grad = is_train
with torch.set_grad_enabled(is_train):
    linear.eval()
    print(linear.weight.requires_grad)

【讨论】:

这个输出是假的。 感谢@Tengerye,答案已更新。【参考方案4】:

很好。诀窍是检查当你定义一个线性层时,默认情况下参数将有requires_grad=True,因为我们想学习,对吧?

l = nn.Linear(1, 1)
p = l.parameters()
for _ in p:
    print (_)

# Parameter containing:
# tensor([[-0.3258]], requires_grad=True)
# Parameter containing:
# tensor([0.6040], requires_grad=True)    

另一个构造,

with torch.no_grad():

意味着你不能在这里学习。

所以你的代码只是表明你有学习能力,即使你在torch.no_grad()禁止学习。

with torch.no_grad():
    linear = nn.Linear(1, 1)
    linear.eval()
    print(linear.weight.requires_grad) #true

如果你真的打算关闭 requires_grad 的 weight 参数,你也可以这样做:

linear.weight.requires_grad_(False)

linear.weight.requires_grad = False

所以你的代码可能会变成这样:

with torch.no_grad():
    linear = nn.Linear(1, 1)
    linear.weight.requires_grad_(False)
    linear.eval()
    print(linear.weight.requires_grad)

如果您打算为模块中的所有参数切换到 requires_grad:

l = nn.Linear(1, 1)
for _ in l.parameters():
    _.requires_grad_(False)
    print(_)

【讨论】:

【参考方案5】:

这个tutorial 可能会有所帮助。

简而言之,我认为这个问题的一个好方法可能是:

linear = nn.Linear(1,1)

for param in linear.parameters():
    param.requires_grad = False

linear.eval()
print(linear.weight.requires_grad)

【讨论】:

这与旧答案有何不同/扩展? @dedObed 我在教程中总结了一些不同的用例,以便准确回答这个问题。接受的答案非常好。我在教程中包含了与这个问题非常相关的 .detach()。

以上是关于pytorch 如何设置 .requires_grad False的主要内容,如果未能解决你的问题,请参考以下文章

如何将 AMD GPU 用于 fastai/pytorch?

如何在pytorch中进行并行处理

MobileNetV3 实战:植物幼苗分类(pytorch)

如何通过 pytorch 使 Intel GPU 可用于处理?

MobileNetV3 实战:植物幼苗分类(pytorch)

如何在pytorch多类问题的交叉熵损失中设置目标