torch之grad

一、grad

`grad`是指梯度的缩写。梯度在深度学习中非常重要,因为它们是使用反向传播算法进行网络训练时所需要计算的。PyTorch通过自动微分引擎Autograd提供了自动计算梯度的功能。每个`Tensor`对象都有一个`.grad`属性,这个属性会保存该`Tensor`的梯度。

 

二、与grad相关的几个概念

1、`requires_grad`: 如果一个`Tensor`的`requires_grad`属性被设置为`True`,PyTorch会追踪所有与这个张量有关的操作。那么在进行反向传播时,你就可以自动得到这个`Tensor`的梯度了。

2、`.backward()`: 当你完成了前向传递并计算出了损失之后,你可以调用损失`Tensor`上的`.backward()`方法来计算梯度。这个操作会计算损失相对于模型参数的梯度。

3、`.grad`: 在调用`.backward()`之后,所有参与运算并设置了`requires_grad=True`的`Tensor`的梯度将累积在它们的`.grad`属性中。

4、`.detach()`: 如果你希望从计算历史中移除一个`Tensor`,使其以后的操作不再追踪计算梯度,可以使用`.detach()`方法。

 

 三、关于自动计算梯度的例子

a = torch.tensor([1,2,3.], requires_grad=True) # 这里将requires_grad设置为True标记需要追踪
out = a * a # 这里定义了out关于a的函数为 out = a * a
y = out.sum() # 这里定义了y关于对out的求和
y.backward()  # 将y进行反向传播给a : 其实是对y的关于a的各个标量求偏导
print(a.grad) # 那么结果即为2a: [2,4,6]

如上是关于计算grad的简单例子,具体说明已经在注释中说明。

如果y有多步操作,那么求导则是将每一步都整合起来

a = torch.tensor([1,2,3.], requires_grad=True) # 这里将requires_grad设置为True标记需要追踪
out = a * a # 这里定义了out关于a的函数为 out = a * a
y = out.sum() # 这里定义了y关于对out的求和
y += torch.matmul(out, out) # 这里增加了一步矩阵乘法的操作 out * out
y.backward()  # 将y进行反向传播给a : 其实是对y的关于a的各个标量求偏导; 到这里y的公式为y = a * a + a^4
print(a.grad) # 那么求导结果即为 2a + 4a^3

 

四、detach

网上很多关于data和detach的说明

共同点:

data和detach()返回和 x 的相同数据 tensor,而且这个新的tensor和原来的tensor是共用数据的,一者改变,另一者也会跟着改变,而且新分离得到的tensor的require s_grad = False, 即不可求导的。

差异点:

1、data是属性,detach() 是方法;

2、通过data分离的对象,原对象是无感知的进而在data对象的改变不能被原对象 autograd 追踪求微分;

3、通过detach() 分离的对象,原对象知道,当detach()对象改变时候,原对象再追踪求微分则会抛出异常。

a = torch.tensor([1,2,3.], requires_grad=True) # 这里将requires_grad设置为True标记需要追踪
out = a.sigmoid()
c = out.detach() # 通过detach,则c值改变,out的值也会随之改变,因为他们是共用数据
c.zero_()
out.sum().backward() # out值被改变了,则这里会抛出异常,不允许再被求导
print(a.grad)

当然,如果上面不调用c.zero_()则在接下去的out.sum().backward()不会抛出异常

如果使用c = out.data也是不会报错,但是输出的是全0的结果

a = torch.tensor([1,2,3.], requires_grad=True) # 这里将requires_grad设置为True标记需要追踪
out = a.sigmoid()
c = out.data
c.zero_() # 这里更新了c的值,同时out的值也会被原地更新
out.sum().backward()
print(a.grad) # 这里返回的是 tensor([0., 0., 0.])

 

那么,如果out直接调用out.zero_()会怎样呢?

a = torch.tensor([1,2,3.], requires_grad=True) # 这里将requires_grad设置为True标记需要追踪
out = a.sigmoid()
out.zero_()
out.sum().backward() # RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation:
                     # [torch.FloatTensor [3]], which is output 0 of SigmoidBackward0, is at version 1; expected version 0 instead.
print(a.grad)

跟detach一样,也是会报错;因为out自身处于追踪状态,当自身内部的数据被原地更新是会感知的到。所以在后续进行反向传播时候报错了

具体解释:由于梯度是根据原始计算图通过链式法则进行反向传播的,

               如果你更改了计算图中的某些值(比如将`out`设置为0),

               那么原始计算图就会丢失,因此无法再进行梯度的计算。

               这也就是为什么`out.sum().backward()`会抛出错误的原因。

posted @ 2024-01-20 23:55  LCAC  阅读(197)  评论(0编辑  收藏  举报