我正在学习一个PyTorch教程,该教程使用来自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 Successfullyb. Yes No 112M Successfullyc. No Yes 3M Successfullyd. No No 112M CUDA out of memory
能有人解释一下这是怎么回事吗?为什么我在(d)中得到CUDA out of memory
错误,而在(b)中没有?两者都有112M可学习参数。
回答:
这是一个较旧的讨论,随着时间的推移有所变化(主要是由于with torch.no_grad()
作为一种模式的目的)。在Stackoverflow上已经有一个非常好的回答,部分回答了你的问题,可以在这里找到Stackoverflow上的答案。
然而,由于原始问题大不相同,我不会标记为重复,特别是因为关于内存的第二部分问题。
no_grad
的初步解释在这里这里给出:
with torch.no_grad()
是一个上下文管理器,用于防止计算梯度[…]。
另一方面,requires_grad
用于
冻结模型的一部分并训练其余部分[…]。
再次来源于SO帖子。
本质上,使用requires_grad
你只是禁用网络的一部分,而no_grad
则不会存储任何梯度,因为你可能在进行推理而不是训练。
为了分析你的参数组合的行为,让我们来看看发生了什么:
a)
和b)
根本不存储任何梯度,这意味着无论参数数量如何,你都有更多的内存可用,因为你没有为可能的反向传播保留它们。c)
需要存储前向传播以便稍后进行反向传播,但是,只有有限数量的参数(300万)被存储,这仍然是可管理的。d)
然而,需要存储所有11200万参数的前向传播,这导致内存不足。