当前位置: 首页 > news >正文

Pytorch 反向传播 计算图被修改的报错

先看看报错的内容

RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.FloatTensor [5, 1]], which is output 0 of AsStridedBackward0, is at version 2; expected version 1 instead. Hint: enable anomaly detection to find the operation that failed to compute its gradient, with torch.autograd.set_detect_anomaly(True).

报错中说,一个需要梯度计算的变量已经被原地修改了,这引发了报错。

torch.set_grad_enabled(True)

然后我使用上述语句开启了梯度跟踪,发现问题出在我的标签计算函数:

def get_label(net, X):return net(X).reshape((-1, 1))

为什么会出错呢?在这种情况下,由于 label 是从网络输出直接计算得到的,它与网络的计算图相连接。如果在 label 上进行了原地操作(上述的修改形状操作),就可能破坏计算图,使其不可导或其他,总之是导致反向传播时无法正确计算梯度,从而引发报错。

那怎么解决这个问题?将该结果与计算图进行分离就行了,此刻如果再进行反向传播,梯度就不会传播到此处。修改后,代码如下;

def get_label(net, X):return net(X).detach().reshape((-1, 1))

detach()函数的作用是将数据和计算图分离开来,得到数据部分,与计算图再无瓜葛。

举一个更形象的例子,看下面的代码:

label = net(X)  # 计算标签
# 对 label 或 label 的某个部分进行了原地操作,比如:
# label[0, 0] = label[0, 0] * 2
# 或
# label += 1
loss = Loss(label, y)  # 计算损失

在这个例子中,label由第一条语句前向传播得到,是直接与网络的输出连在一起,后面我却对label的值进行了手动修改。

这些操作可能导致计算图的结构不完整或不可导,从而影响反向传播的计算。为了避免这样的问题,一般建议避免在计算标签或损失时对张量进行原地操作。如果需要修改张量的值,最好创建一个新的张量,而不是直接在原有张量上进行修改。

下面是我的整个程序,大家也可以调试代码来理解其中的含义:

import torch.nn as nn
import matplotlib.pyplot as plt
import torch
from torch.utils import data
def get_label(net, X):#计算标签,计算完后必须要使用detach()分离计算图,否则代码将报计算图被修改的错误return net(X).detach().reshape((-1, 1))def train(net, trainer, Loss, train_data, train_label, epochs, batch_size):#将训练数据和标签捆在一起,便于后面一起便利data_iter = data.DataLoader(list(zip(train_data, train_label)), batch_size=batch_size)#用来存储数据的变化值,前者为训练轮次,后者为每一轮训练平均损失draw_x, draw_y = [], []for epoch in range(epochs):#每次处理一个批次的数据for X, y in data_iter:trainer.zero_grad()  # 清除梯度pre_y = net(X)  # 前向传播loss = Loss(pre_y, y)  # 计算损失loss.backward()  # 反向传播,计算梯度trainer.step()  # 更新权重,进行优化#添加绘图需要的数据draw_x.append(epoch)draw_y.append(torch.mean(Loss(net(train_data),train_label)).data)#设置绘图参数plt.figure(figsize=(5, 4), dpi=150)#设置图像大小和分辨率plt.plot(draw_x, draw_y, label='train_loss')#设置要绘制的数据,被给出图例plt.xlabel('epoch')#设置X轴标题plt.ylabel('loss')#设置y轴标题plt.legend()#显示图例#显示最终图像plt.show()def test(net, Loss, test_data, test_label):loss_sum = torch.zeros_like(test_label)data_iter = data.DataLoader(list(zip(test_data, test_label)), batch_size=batch_size, shuffle=False)for X, y in data_iter:pre_y = net(X)  # 前向传播loss = Loss(pre_y, y)  # 计算损失loss_sum += loss  # 累加损失return torch.sum(loss_sum) / len(loss_sum)  # 返回平均损失def init_weight(m):if type(m) == nn.Linear:#权重使用何凯明正态初始化方法进行初始化nn.init.kaiming_normal_(m.weight)#偏置使用0偏置nn.init.zeros_(m.bias)lr = 0.01  # 学习率
epochs = 100  # 训练轮数
batch_size = 5  # 批大小
shared = nn.Linear(5, 5)  # 共享层
net = nn.Sequential(nn.Linear(10, 5), nn.ReLU(),  # 输入层到隐藏层1的线性层,ReLU激活函数shared, nn.ReLU(),  # 共享层,ReLU激活函数shared, nn.ReLU(),  # 共享层,ReLU激活函数nn.Linear(5, 1))  # 从隐藏层到输出层的线性层,无激活函数(线性回归)#显示真实参数(我们的标签就是用这个参数跑出来的),这也是我们最终需要拟合的参数
for name, param in net.named_parameters():print(name, param)#获取随机数作为样本
X = torch.randn((200, 10))
# 通过网络得到真实标签
True_label = get_label(net, X)
#一开始自动随机生成了参数已经被我当作真实参数了,此刻我需要另重新初始化参数
net.apply(init_weight)
#获取训练器
trainer = torch.optim.SGD(net.parameters(), lr=lr)
#获取损失函数
Loss = nn.MSELoss()  # 定义损失函数,使用均方误差。#开始训练模型发
train(net, trainer, Loss, X[:50], True_label[:50], epochs, batch_size=batch_size)
#打印测试损失
print(f'测试损失{test(net, Loss, X[50:], True_label[50:])}')

http://www.lryc.cn/news/278631.html

相关文章:

  • android studio设置gradle和gradle JDK版本
  • Android 15即将到来,或将推出5大新功能特性
  • sqlalchemy 事务自动控制(类java aop)
  • vue2-手写轮播图
  • Google I/O大会:Android 13
  • VUE指令(一)
  • 微信小程序开发学习笔记《7》全局配置以及小程序窗口
  • Vue、uniApp、微信小程序、Html5等实现数缓存
  • 如何将ArcGIS工程文件迁移到ArcGIS Pro内
  • Jenkins基础篇--添加用户和用户权限设置
  • C语言基础内容(七)——第08章_C语言常用函数
  • CRM系统针对销售管理有哪些功能?如何帮助销售效率增长?
  • 基于Pixhawk和ROS搭建自主无人车(一):底盘控制篇
  • 部署 Spring Boot 应用中文文档
  • 【数据库原理】(23)实际应用中的查询优化方法
  • MySQL中datetime和timestamp的区别
  • 2024年如何使用WordPress构建克隆Udemy市场
  • (leetcode)Z字形变换 -- 模拟算法
  • STM32--基于STM32F103的MAX30102心率血氧测量
  • Qt/C++音视频开发63-设置视频旋转角度/支持0-90-180-270度旋转/自定义旋转角度
  • Python电能质量扰动信号分类(五)基于CNN-Transformer的一维信号分类模型
  • 基于Vue组合式API的实用工具集
  • 065:vue中将一维对象数组转换为二维对象数组
  • mysql 字符串分割
  • 解决Windows11 “我们无法设置移动热点”
  • python tcp socket中实现SSL/TLS认证
  • SQL-修改表操作
  • 【Node.js学习 day3——http模块】
  • 初探UAF漏洞(3)
  • C++学习笔记(二十一)