Pytorch中Tensor Variable Parameter register_buffer以及求导运算

2022-10-10 12:58:45

在较新的版本中,Variable被弃用,将功能合并给Tensor,所以不用考虑

求导

重要理论

  1. 从头开始创建的Tensor(例如x = torch.tensor(1.))称为leaf Tensor(叶张量),依赖其他Tensor计算而来的(例如y = 2 * x)称为non-leaf Tensor(非叶张量)
  2. 若一Tensor的requires_grad=True,则依赖它的所有Tensor的requires_grad=True;若一Tensor的requires_grad=False,则依赖它的所有Tensor的requires_grad=False。
  3. loss的requires_grad=False则其不能调用backward;某non-leaf Tensor的requires_grad=False则其之前的参数由于梯度反向传播时被截断,所以不会得到更新;leaf Tensor的requires_grad=False,表示其不需要梯度,所以其也不能被更新。
  4. backward()只更新leaf的梯度,因为一般只优化leaf的值(在逻辑上模型可优化的参数是从头创建的leaf Tensor)。若想得到non-leaf的梯度,可以使用autogard()函数。
  5. 使用with torch.no_gard()可以使得其中Tensor的requires_grad=False,这表示他不需要传播梯度,从而节省显存,但注意如果在no_gard()中新建Tensor,其requires_grad的优先级比no_gard()高(详细见示例)。一般将测试过程放入with torch.no_grad()

autogard

# autogard.grad返回leaf tensor和non-leaf tensor的梯度,但tensor的grad属性值不更新import torchfrom torchimport autograd

x= torch.tensor(1.)
a= torch.tensor(1., requires_grad=True)
b= torch.tensor(2., requires_grad=True)
c= torch.tensor(3., requires_grad=True)# requires_grad=True
y= a**2* x+ b* x+ c# requires_grad=True
z= y**2+2*yprint('before:', a.grad, b.grad, c.grad, y.grad)
grads= autograd.grad(z,[a, b, c, y])print('after :', grads[0], grads[1], grads[2], grads[3])print('after :', a.grad, b.grad, c.grad, y.grad)# before: None None None None# after : tensor(28.) tensor(14.) tensor(14.) tensor(14.)# after : None None None None

backward

# backward更新leaf tensor的grad属性值(在参数优化时常用backward而不是autogard.grad因为通常只需更新leaf tensor的值)import torch

x= torch.tensor(1.)
a= torch.tensor(1., requires_grad=True)
b= torch.tensor(2., requires_grad=True)
c= torch.tensor(3., requires_grad=True)# requires_grad=True
y= a**2* x+ b* x+ c# requires_grad=True
z= y**2+2*yprint('before:', a.grad, b.grad, c.grad, y.grad)
z.backward()print('after :', a.grad, b.grad, c.grad, y.grad)# before: None None None None# after : tensor(28.) tensor(14.) tensor(14.) None

no_gard

常用的场景为测试过程,因为只有训练的过程需要梯度来更新参数,测试过程的不需要更新参数,所以将整个测试过程放到no_grad()下,以节省显存

with torch.no_grad():for datain test_dataloader:
		imgs, targets= data
		outputs= my_model(imgs)
		loss= loss_fn(outputs, targets)
		total_test_loss= total_test_loss+ loss.item()
		accuracy=(outputs.argmax(1)== targets).sum()
		total_accuracy= total_accuracy+ accuracy
# 使用with torch.no_gard()可以使得在其中的Tensor的requires_grad=False,# 这表示他不需要传播梯度,从而节省显存import torchfrom torch.nnimport Parameter

x= torch.tensor(1.)
a= torch.tensor(1., requires_grad=True)
b= torch.tensor(2., requires_grad=True)
c= torch.tensor(3., requires_grad=True)

y= a**2* x+ b* x+ c

z= y**2+2*y# True True Trueprint(c.requires_grad, y.requires_grad, z.requires_grad)# y不需要梯度with torch.no_grad():
    y= a**2* x+ b* x+ c# z依赖于y,也不需要梯度
z= y**2+2*y# True False Falseprint(c.requires_grad, y.requires_grad, z.requires_grad)# 此处调用z.backward()报错,z的requires_grad=False# (注意)新建tensor且指定requires_grad=True,其优先级高于no_grad()即requires_grad=True# 但其派生而来的y和z的requires_grad仍为Falsewith torch.no_grad():
    a= torch.tensor(1., requires_grad=True)
    b= torch.tensor(2., requires_grad=True)
    c= torch.tensor(3., requires_grad=True)

    y= a**2* x+ b* x+ c

    z= y**2+2*yprint(c.requires_grad, y.requires_grad, z.requires_grad)# True False False# (注意)新建Parameter,其优先级高于no_grad()即requires_grad=True# 但其派生而来的y和z的requires_grad仍为Falsewith torch.no_grad():
    a= Parameter(torch.tensor(1.))
    b= Parameter(torch.tensor(2.))
    c= Parameter(torch.tensor(3.))

    y= a**2* x+ b* x+ c

    z= y**2+2*yprint(c.requires_grad, y.requires_grad, z.requires_grad)# True False False

optimizer更新参数

# non-leaf不能被更新import torchfrom torchimport optim

x= torch.tensor(1.)
a= torch.tensor(1., requires_grad=True)
b= torch.tensor(2., requires_grad=True)
c= torch.tensor(3., requires_grad=True)

y= a**2* x+ b* x+ c

z= y**2+2*y# 报错:can't optimize a non-leaf Tensor# optimizer = optim.SGD([a, b, c, y], lr=0.01)

optimizer= optim.SGD([a, b, c], lr=0.01)print('before:', a, b, c)
optimizer.zero_grad()
z.backward()
optimizer.step()print('after :', a, b, c)# before: tensor(1., requires_grad=True) tensor(2., requires_grad=True) tensor(3., requires_grad=True)# after : tensor(0.7200, requires_grad=True) tensor(1.8600, requires_grad=True) tensor(2.8600, requires_grad=True)

Tensor vs Parameter vs. register_buffer

重要理论

  1. Parameter和register_buffer均为特殊的Tensor,其中Parameter设计目的为可以优化和保存的Tensor,register_buffer设计目的为不能优化但能保存的Tensor。
  2. Tensor默认requires_grad=False,不和模型绑定,net.parameters()访问不到,net.state_dict()访问不到
  3. Parameter默认requires_grad=True和模型绑定,net.parameters()可以访问,net.state_dict()可以访问
  4. register_buffer默认requires_grad=False,和模型绑定,net.parameters()访问不到,net.state_dict()可以访问
  5. 和模型绑定即表示 会随着模型移动到cuda而同时移动到cuda上;net.parameters()常作为优化器的参数,net.parameters()可以访问即表示 主观上想要进行优化的Tensor(指定requires_grad=True并显式的放到optim.SGD([…]中的 普通Tensor/register_buffer 也可以进行优化);net.state_dict()常作为torch.save的参数,net.state_dict()可以访问即表示主观上想要进行保存的Tensor(普通Tensor无法通过任何方式进行保存,即只能保存Parameter、register_buffer)。
import torchfrom torch.nn.parameterimport Parameterfrom torchimport nnfrom torch.nnimport functionalas FclassNet(nn.Module):def__init__(self):super(Net, self).__init__()

        self.testTensor= torch.tensor([1.0,1.0])

        self.testParameter= Parameter(torch.tensor([2.0,2.0]))# 若指定参数persistent=False,则该register_buffer与普通Tensor相同
        self.register_buffer("testRegister", torch.tensor([3.0,3.0]))defforward(self, x):
        x= F.relu(self.testTensor* x)
        x= F.relu(self.testParameter* x)
        x= F.relu(self.testRegister* x)return x


net= Net()# net.parameters()内包含Parameter,不含Tensor和register_bufferfor name, paramin net.named_parameters():print(f'{name}{param}')# testParameter Parameter containing:tensor([2., 2.], requires_grad=True)print('-------------------------------------------------')# net.state_dict()内包含Parameter和register_buffer,不含Tensorfor statein net.state_dict():print(f'{state}')# testParameter# testRegisterprint('-------------------------------------------------')# Parameter和register_buffer和模型绑定,即会随着模型移动到cuda而同时移动到cuda上print(f'{net.testTensor}{net.testParameter}{net.testRegister}')# tensor([1., 1.]) Parameter containing:tensor([2., 2.], requires_grad=True) tensor([3., 3.])
net= net.cuda()print(f'{net.testTensor}{net.testParameter}{net.testRegister}')# tensor([1., 1.]) Parameter containing:tensor([2., 2.], device='cuda:0', requires_grad=True) tensor([3., 3.], device='cuda:0')
import torchfrom torch.nn.parameterimport Parameterfrom torchimport nnfrom torch.nnimport functionalas Ffrom torchimport optimclassNet(nn.Module):def__init__(self):super(Net, self).__init__()

        self.testTensor= torch.tensor([1.0,1.0])

        self.testParameter= Parameter(torch.tensor([2.0,2.0]))

        self.register_buffer("testRegister", torch.tensor([3.0,3.0]))defforward(self, x):
        x= F.relu(self.testTensor* x)
        x= F.relu(self.testParameter* x)
        x= F.relu(self.testRegister* x)return x# net.parameters()常作为优化器的参数,net.parameters()可以访问即表示主观上想要进行优化的Tensor
net= Net()
inp= torch.tensor([5.0,5.0])
out= net(inp)
loss= F.mse_loss(out, torch.tensor([3.0,3.0]))

optimizer= optim.SGD(net.parameters(), lr=0.01)print(f'before:{net.testTensor}{net.testParameter}{net.testRegister}\n')
optimizer.zero_grad()
loss.backward()
optimizer.step()print(f'after:{net.testTensor}{net.testParameter}{net.testRegister}\n')# before: tensor([1., 1.]) Parameter containing:# tensor([2., 2.], requires_grad=True) tensor([3., 3.])## after: tensor([1., 1.]) Parameter containing:# tensor([-2.0500, -2.0500], requires_grad=True) tensor([3., 3.])print('-------------------------------------------------')# 指定requires_grad=True并显式的放到optim.SGD([...]中的 普通Tensor/register_buffer 也可以进行优化
net= Net()
net.testTensor.requires_grad=True
net.testRegister.requires_grad=True
inp= torch.tensor([5.0,5.0])
out= net(inp)
loss= F.mse_loss(out, torch.tensor([3.0,3.0]))

tmpParams=list(net.parameters())
tmpParams.extend([net.testTensor, net.testRegister])
optimizer= optim.SGD(tmpParams, lr=0.01)print(f'before:{net.testTensor}{net.testParameter}{net.testRegister}\n')
optimizer.zero_grad()
loss.backward()
optimizer.step()print(f'after:{net.testTensor}{net.testParameter}{net.testRegister}\n')# before: tensor([1., 1.], requires_grad=True) Parameter containing:# tensor([2., 2.], requires_grad=True) tensor([3., 3.], requires_grad=True)## after: tensor([-7.1000, -7.1000], requires_grad=True) Parameter containing:# tensor([-2.0500, -2.0500], requires_grad=True) tensor([0.3000, 0.3000], requires_grad=True)
  • 作者:MallocLu
  • 原文链接:https://blog.csdn.net/qq_42283621/article/details/124921414
    更新时间:2022-10-10 12:58:45