在pytorch中查找模型的准确率时会记录梯度吗?
原文标题 :Will the gradients be recorded when finding the model’s accuracy in pytorch?
我开始学习 PyTorch,但我对某些事情感到困惑。据我了解,如果我们将.requires_grad_()
设置为我们的参数,那么将记录找到这些参数梯度的必要计算。这样,我们可以执行梯度下降。然而,梯度值将被添加到之前的梯度值之上,因此在我们执行梯度下降步骤之后,我们应该使用param.grad.zero_()
重置我们的梯度,其中param
是权重或偏差项。我有一个模型,它只有输入层和一个输出神经元,非常简单(因为我只有一个输出神经元,你可以看出我只有 2 个可能的类)。我有我的参数称为weights
和bias
,我在这两个变量上设置requires_grad_()
。此外,我将训练数据放入DataLoader
称为train_dl
,将验证数据放入valid_dl
。我使用了 MNIST 数据集的一个子集,但这对于这个问题真的不重要。这些是我使用的功能:
def forward_propagation(xb):
z = xb @ weights + bias
a = z.sigmoid()
return a
def mse_loss(predictions, targets):
loss = ((predictions - targets) ** 2).mean()
return loss
def backward_propagation(loss):
loss.backward()
weights.data -= lr * weights.grad.data
weights.grad.zero_()
bias.data -= lr * bias.grad.data
bias.grad.zero_()
def train_epoch():
for xb, yb in train_dl:
a = forward_propagation(xb)
loss = mse_loss(a, yb)
backward_propagation(loss)
正如你所看到的,我使用函数train_epoch()
来执行:前向传播(其中一些梯度计算将被记录,因为这是我们第一次使用参数的地方),计算损失(这一步也将用于计算梯度),然后反向传播,我更新我的参数,然后将梯度重置为 0,这样它们就不会累积。我使用这段代码来训练我的模型,它运行良好,我对我得到的准确性感到满意。所以我认为它至少在某种程度上是有效的。
但我也使用此代码来查找我的模型的验证数据准确性:
def valid_accuracy():
accuracies = []
for xb, yb in valid_dl:
a = forward_propagation(xb)
correct = (a > 0.5) == yb
accuracies.append(correct.float().mean())
return round(torch.stack(accuracies).mean().item(), 4)
如您所见,在找到模型的准确性时,我执行前向传播(上述函数,我将权重乘以数据并添加偏差)。我的问题是:梯度也会被记录在这里吗?那么下次我使用.backward()
onloss
时,梯度是否会受到寻找精度所采取的步骤的影响?我认为就像现在一样,每次我找到模型的准确性时都会添加梯度值(我不想要也没有意义),但我不确定。我应该在函数中的某个地方valid_accuracy()
另外 2 行带有weights.grad.zero_()
和bias.grad.zero_()
,这样就不会发生这种情况吗?还是这种情况不会自动发生,所以默认情况下我得到了所需的行为,而我只是误解了一些东西?
回复
我来回复-
flawr 评论
有两件事需要考虑:一是梯度本身,二是在每个前向传递中构建的计算图。
为了计算前向传递后的梯度,我们需要记录对哪些张量以什么顺序进行了哪些操作,即计算图。因此,每当从具有
requires_grad==True
的其他张量计算新张量时,新张量具有指向先前操作和所涉及张量的属性.grad_fn
。这基本上是如何backward()
“知道”去哪里的。如果你调用backward()
,它会考虑 this.grad_fn
并递归地进行反向传递。所以目前你这样做的方式实际上会构建这个计算图,即使在计算准确性时也是如此。但是如果这个图从未被访问过,垃圾收集器最终会销毁它。
需要注意的关键是每个单独的评估都会产生一个新的计算图(取决于你的模型可能共享一些部分),但是向后传递只会从你调用的“节点”开始
.backward
,所以在你的片段中你会赢永远不会从精度计算中得到梯度,因为你从不调用a.backward()
,你只调用loss.backward()
。计算图的重新编码确实需要一些开销,但是可以使用
torch.no_grad()
上下文管理器禁用它,这是考虑到这个确切的用例。不幸的是,名称(以及文档)提到了梯度,但它实际上只是记录(前向)计算图。但很明显,如果你禁用它,你也将无法计算向后传递。2年前 -
jodag 评论
当您调用
.backward()
作为这些张量的后代的张量时,存储在叶张量(本例中为weights
和bias
)中的梯度会更新。由于您没有调用backward
duringvalid_accuracy
,因此渐变不会受到影响。也就是说,PyTorch 会将中间信息存储在临时计算图中(当程序从valid_accuracy
返回并且所有引用计算图的张量超出范围时将被丢弃),这需要时间和内存。如果您确定不需要对模型的输出执行反向传播,则可以并且应该使用
torch.no_grad()
上下文。这将禁用中间结果的记录。例如def valid_accuracy(): with torch.no_grad(): accuracies = [] for xb, yb in valid_dl: a = forward_propagation(xb) correct = (a > 0.5) == yb accuracies.append(correct.float().mean()) return round(torch.stack(accuracies).mean().item(), 4)
2年前