PyTorch torch.no_grad与require_grad = False

问题描述

我正在跟踪一个PyTorch tutorial,它使用了Huggingface Transformers库中的BERT NLP模型(特征提取器)。我不了解渐变更新的两个相互关联的代码

(1)torch.no_grad()

本教程有一个类,其中forward()函数在对BERT特征提取器的调用周围创建一个torch.no_grad()块,如下所示:

bert = BertModel.from_pretrained('bert-base-uncased')

class BERTGRUSentiment(nn.Module):
    
    def __init__(self,bert):
        super().__init__()
        self.bert = bert
        
    def forward(self,text):
        with torch.no_grad():
            embedded = self.bert(text)[0]

(2)param.requires_grad = False

同一教程中还有另外一部分冻结了BERT参数。

for name,param in model.named_parameters():                
    if name.startswith('bert'):
        param.requires_grad = False

我什么时候需要(1)和/或(2)?

  • 如果我想使用冻结的BERT进行训练,是否需要同时启用两者?
  • 如果我想培训以更新BERT,我是否需要同时禁用这两者?

此外,我运行了所有四个组合并发现:

   with torch.no_grad   requires_grad = False  Parameters  Ran
   ------------------   ---------------------  ----------  ---
a. Yes                  Yes                      3M        Successfully
b. Yes                  No                     112M        Successfully
c. No                   Yes                      3M        Successfully
d. No                   No                     112M        CUDA out of memory

有人可以解释发生了什么事吗?为什么我为(d)而不是(b)得到CUDA out of memory?两者都有112M可学习的参数。

解决方法

这是一个较老的讨论,多年来已经发生了一些变化(主要是由于with torch.no_grad()是一种模式的目的。一个很好的答案,也可以找到您的问题的答案{{3} }。
但是,由于原始问题有很大的不同,所以我将避免标记为重复,尤其是由于第二部分涉及内存。

on Stackoverflow already给出了no_grad的初始说明:

with torch.no_grad()是上下文管理器,用于防止计算梯度[...]。

另一方面,使用了

requires_grad

冻结模型的一部分并训练其余的模型。

再次来源here

本质上,使用requires_grad只是禁用了网络的一部分,而no_grad根本不会存储任何任何渐变,因为您可能会将其用于推理而不是训练。
要分析参数组合的行为,让我们调查正在发生的事情:

  • a)b)根本不存储任何渐变,这意味着无论参数有多少,您都拥有更多的可用内存,因为您没有将它们保留给用户使用。潜在的向后通过。
  • c)必须存储正向通过,以用于以后的反向传播,但是,仅存储了有限数量的参数(300万个),这仍然是可管理的。
  • 然而,
  • d)需要存储所有1.12亿个参数的正向传递 ,这将导致内存不足。