RuntimeError: Trying to backward through the graph a second time (or directly access saved variable

这篇具有很好参考价值的文章主要介绍了RuntimeError: Trying to backward through the graph a second time (or directly access saved variable。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

用pytorch的时候发生了这个错误,写下来避免以后再次入坑。感谢这次坑让我对预训练模型的使用有了更清楚的认识。

RuntimeError: Trying to backward through the graph a second time (or directly access saved variables after they have already been freed). Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved variables after calling backward.

简单说一下问题就是因为某个带有梯度信息的变量在被执行了一次后,这些梯度信息就被计算图释放掉了,而我们的代码却尝试第二次反向传播的时候来访问这些变量(梯度信息)。

原因

每个人的原因也许不同。
我这里的原因就是将Embedding写在了训练模型的循环之外。
下面是我的错误代码,也就是最开始的代码示例。
可以看到首先对整个词典进行Embedding再去训练,就会出现上面的错误。

net = nn.Linear(32, 2)
Loss = nn.CrossEntropyLoss()
sample = torch.tensor([[1, 2, 3, 3],
                       [3, 2, 1, 5],
                       [4, 5, 9, 3]])
target = torch.ones((12,)).to(torch.long)  # 每个词的类别
print(sample)
embedding = nn.Embedding(10, 32)
embed_sample = embedding(sample)  # torch.Size([3, 4, 32])

net.train()
optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
for i in range(100):
    pred = net(embed_sample)    # torch.Size([3, 4, 2])
    pred = pred.reshape(-1, 2)  # torch.Size([12, 2])
    loss = Loss(pred, target)   # 计算损失
    optimizer.zero_grad()       # 清零梯度
    loss.backward()             # 反向传播
    optimizer.step()            # 更新梯度
    print(i+1, loss)

#输出
---------------------------------------------------------------------------
1 tensor(0.7125, grad_fn=<NllLossBackward>)
RuntimeError                              Traceback (most recent call last)
D:\Temp\ipykernel_8312\2990520637.py in <cell line: 3>()
      7     #sum_loss                   #一个epoch所有损失和
      8     optimizer.zero_grad()       #清零梯度
----> 9     loss.backward()             #反向传播
     10     optimizer.step()            #更新梯度
     11     print(i+1,loss)

E:\anaconda\lib\site-packages\torch\_tensor.py in backward(self, gradient, retain_graph, create_graph, inputs)
    253                 create_graph=create_graph,
    254                 inputs=inputs)
--> 255         torch.autograd.backward(self, gradient, retain_graph, create_graph, inputs=inputs)
    256 
    257     def register_hook(self, hook):

E:\anaconda\lib\site-packages\torch\autograd\__init__.py in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)
    145         retain_graph = create_graph
    146 
--> 147     Variable._execution_engine.run_backward(
    148         tensors, grad_tensors_, retain_graph, create_graph, inputs,
    149         allow_unreachable=True, accumulate_grad=True)  # allow_unreachable flag

RuntimeError: Trying to backward through the graph a second time (or directly access saved variables after they have already been freed). Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved variables after calling backward.

这就是因为这个词嵌入是静态的,我们第一次反向传播的时候已经将它释放掉了,所以进入第二次循环进行反向传播的时候,就报错了。


解决方法

解决方法就很容易了,我们只需要将它移到循环内部就好了。向下面代码所示。

net = nn.Linear(32, 2)
Loss = nn.CrossEntropyLoss()
sample = torch.tensor([[1, 2, 3, 3],
                       [3, 2, 1, 5],
                       [4, 5, 9, 3]])
target = torch.ones((12,)).to(torch.long)  # 每个词的类别
print(sample)
embedding = nn.Embedding(10, 32)
net.train()
optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
for i in range(100):
    #写到这里就好了
    embed_sample = embedding(sample)  #torch.Size([3, 4, 32])  
    pred = net(embed_sample)    # torch.Size([3, 4, 2])
    pred = pred.reshape(-1, 2)  # torch.Size([12, 2])
    loss = Loss(pred, target)   # 计算损失
    optimizer.zero_grad()       # 清零梯度
    loss.backward()             # 反向传播
    optimizer.step()            # 更新梯度
    print(i+1, loss)

Embedding不训练?

这样做导致的问题就是Embedding()中的参数不参与训练了(可自行验证),至于为什么,
因为optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)中并没有添加embedding的参数。
解决方法就是将embedding的参数加进去。
optimizer =torch.optim.Adam(list(embedding.parameters())+list(net.parameters()), lr=1e-3)

(水一下代码)

net = nn.Linear(32, 2)
Loss = nn.CrossEntropyLoss()
sample = torch.tensor([[1, 2, 3, 3],
                       [3, 2, 1, 5],
                       [4, 5, 9, 3]])
target = torch.ones((12,)).to(torch.long)  # 每个词的类别
print(sample)
embedding = nn.Embedding(10, 32)
print(list(embedding.parameters()))
net.train()
optimizer = torch.optim.Adam(list(embedding.parameters())+list(net.parameters()), lr=1e-3)
for i in range(100):
    #写到这里就好了
    embed_sample = embedding(sample)  #torch.Size([3, 4, 32])  
    pred = net(embed_sample)    # torch.Size([3, 4, 2])
    pred = pred.reshape(-1, 2)  # torch.Size([12, 2])
    loss = Loss(pred, target)   # 计算损失
    optimizer.zero_grad()       # 清零梯度
    loss.backward()             # 反向传播
    optimizer.step()            # 更新梯度
    print(i+1, loss)
print(list(embedding.parameters())) #对比一下之前的

Embedding直接加入到Net中

看完上面这些,我想大家都能想到的,就是直接将Embedding层直接加入到net

net = nn.Sequential(nn.Embedding(10, 32),
                    nn.Linear(32, 2))
Loss = nn.CrossEntropyLoss()
sample = torch.tensor([[1, 2, 3, 3],
                       [3, 2, 1, 5],
                       [4, 5, 9, 3]])
target = torch.ones((12,)).to(torch.long)  # 每个词的类别
print(sample)
print(list(net[0].parameters()))  #就是Embedding层的参数
net.train()
optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
for i in range(100):
    #写到这里就好了
    pred = net(sample)    # torch.Size([3, 4, 2])
    pred = pred.reshape(-1, 2)  # torch.Size([12, 2])
    loss = Loss(pred, target)   # 计算损失
    optimizer.zero_grad()       # 清零梯度
    loss.backward()             # 反向传播
    optimizer.step()            # 更新梯度
    print(i+1, loss)
print(list(net[0].parameters())) #对比一下之前的

启发——不使用Embedding的梯度信息

如果我现在加载一个预训练模型,而且不想它参与更新梯度
那我直接就在optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)中不加这个参数就好了。

但是这样的话,这个预训练模型的参数还是会参与到反向传播的过程中(不然为什么最开始的时候会报错呢),这样的话其实增加了电脑的开销了,我们想让这个预训练模型不参与到反向传播过程,也就是让它没有梯度信息就好了。

方法1:

1.使用 .detach 使得输入我们net的词嵌入向量没有预训练模型的梯度信息就好了。(我这里的话就是net(embed_sample.detach()))

(再水水代码)

net = nn.Linear(32, 2)
Loss = nn.CrossEntropyLoss()
sample = torch.tensor([[1, 2, 3, 3],
                       [3, 2, 1, 5],
                       [4, 5, 9, 3]])
target = torch.ones((12,)).to(torch.long)  # 每个词的类别
print(sample)
embedding = nn.Embedding(10, 32)
embed_sample = embedding(sample)  #torch.Size([3, 4, 32])
net.train()
optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
for i in range(100):
    #写到这里就好了
    pred = net(embed_sample.detach())    # torch.Size([3, 4, 2])
    pred = pred.reshape(-1, 2)  # torch.Size([12, 2])
    loss = Loss(pred, target)   # 计算损失
    optimizer.zero_grad()       # 清零梯度
    loss.backward()             # 反向传播
    optimizer.step()            # 更新梯度
    print(i+1, loss)

方法2:

2.使用 with torch.no_grad() 使得我们使用预训练模型生成词嵌入向量的时候就不保存梯度信息。文章来源地址https://www.toymoban.com/news/detail-792847.html

net = nn.Linear(32, 2)
Loss = nn.CrossEntropyLoss()
sample = torch.tensor([[1, 2, 3, 3],
                       [3, 2, 1, 5],
                       [4, 5, 9, 3]])
target = torch.ones((12,)).to(torch.long)  # 每个词的类别
print(sample)
embedding = nn.Embedding(10, 32)
with torch.no_grad():
    embed_sample = embedding(sample)  #torch.Size([3, 4, 32])
net.train()
optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
for i in range(100):
    #写到这里就好了
    pred = net(embed_sample)    # torch.Size([3, 4, 2])
    pred = pred.reshape(-1, 2)  # torch.Size([12, 2])
    loss = Loss(pred, target)   # 计算损失
    optimizer.zero_grad()       # 清零梯度
    loss.backward()             # 反向传播
    optimizer.step()            # 更新梯度
    print(i+1, loss)

到了这里,关于RuntimeError: Trying to backward through the graph a second time (or directly access saved variable的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包