pytorch常见错误_0240826
pytorch 常见错误
RuntimeError: a leaf Variable that requires grad is being used in an in-place operation.
如下程序会抱上述错误
x=torch.randn(3,requires_grad=True)
x += 1 # 原位操作 报错:RuntimeError: a leaf Variable that requires grad is being used in an in-place operation.
报错:你在一个变量上进行操作,但是该变量是不可以修改的。这个变量是tensor。x是梯度是开启的,此时x不应该进行原位操作,但是x进行了原位操作。
原因:pytorch的自动求导机制的本质是使用链式求导法则,将求导的过程分解成一个一个的基本操作算子。其具体是通过自动构建计算图,来求解导数。
如下函数 \(y=f(x)\) , pytorch 的计算过程是这样
x = x0 # x取值x0
x.requires_grad=True # 目标函数是否关于x求梯度
y=f(x) # 前向过程
y.backward() # 反向求导
x.grad # 输出x在x0处的导数
py计算的是目标函数关于自变量在某一个确定点的导数值。此时已经构建了一张图,你将x进行了改变,此时计算图要重新构建。pytorch不允许这种操作。
假设 \(y=x*x\), 现在对x进行了操作x=x+1
, 那么再对y求x的导数的时候,y关于x的映射关系是哪个?是\(y=x*x\), 还是 \(y = (x+1) * (x+1)\)。pytorch决定不了,总要有一个选择。
解决方案:
-
使用tensor的data属性:tensor有一个基本属性data,它是tensor上存储的变量的数值,你可以修改这个数值,但是不修改其他属性。选择的是\(y=x*x的路子\)
x=torch.tensor([1.0,1.0],requires_grad=True) x.data += 1 y=torch.dot(x,x)*0.5 y.backward() x.grad #tensor([2., 2.], requires_grad=True) x.grad == x #tensor([True, True, True])
-
使用
with torch.no_grad()
此时梯度属性被禁用了x=torch.tensor([1.0,1.0],requires_grad=True) with torch.no_grad(): # 在上下文管理器中进行 x += 1 y=torch.dot(x,x) * 0.5 y.backward() x.grad #tensor([2., 2.], requires_grad=True) x.grad == x #tensor([True, True, True])
2.tensor.grad.zero_() 梯度清零的使用场景
- 构建tensor x。其梯度属性打开
- 函数
y=f(x)
对y关于\(x\) 在某一点求梯度x.grad
- 使用\(x\) 构建函数\(g=g(x)\), 如果x不进行梯度清零,那么对g关于x求导,其结果为
x.grad = g关于x的导数+y关于x的导数
在深度学习里面,神经网络的参数是在动态变化中,所以其对应的映射关系也是在动态变化中。所以一次反向求导之后,进行第二次反向求导,那么需要将导数清零。
x=torch.tensor([1.0,1.0],requires_grad=True)
y=torch.dot(x,x) * 0.5
g=2*x
y.backward()
g.sum().backward()
x.grad # tensor([3., 3.]) = tensor([1., 1.]) + 2
3.pytorch当loss是nan的时候,其是无法输出的,具体案例如下:
x=torch.tensor([0,1,1,1,1],requires_grad=True,dtype=torch.float)
y=x/x
y.sum().backward()
x.grad # 产生了除0操作,y为NAN,此时x为NAN
4.python的生成器机制yield
通过yield关键字定义生成器。使得函数能够记住上一次执行的状态,并在下一次调用的时候,从该状态继续执行。生成器有如下特性:
- 惰性求值:等到使用的时候再计算
- 节省内存:由于生成器在任何时刻只处理一个值,因此它不需要在内存中存储整个数据集,这在处理大量数据时尤其有用。
- 状态保持:生成器函数可以记住其上一次执行的状态,这意味着它可以从中断的地方继续执行。
定义生成器:
-
使用yield关键字
def f(n): for i in range(n): yield i # 使用yield定义生成器
-
生成器表达式
y = (x * x for x in range(5))
如何使用生成器?
-
使用for循环: (该方式使用比较频繁,比如pytorch的dataloader函数)
for i in f(3): x = i print(i)
-
使用next函数
next(y)
使用场景
- 大文件处理,节省内存
5.python 易错点
python 函数的特点
def f(n):
print('x:',x)
x=10
f(1) # 输出10
x=20
f(2) # 输出20
注意:函数中定义了x,会优先寻找局部变量;如果没有局部变量,寻找外部距离调用函数最近的变量;如果没有外部变量,也没有全局变量,报错
6.pytorch报错TypeError: cannot assign 'torch.FloatTensor' as parameter 'weight' (torch.nn.Parameter or None expected)
net = nn.Sequential(nn.Linear(in_features=3, out_features=1,bias=True))
net[0].weight = torch.randn(3) #[ERROR]
net[0].weight.data = torch.randn(3) # [OK]
7.pytorch报错,RuntimeError: mat2 must be a matrix, got 1-D tensor
net = nn.Sequential(nn.Linear(in_features=4, out_features=1,bias=True))
net[0].weight.data = torch.randn(4)
x=torch.randn(10,4)
net(x) # 报错,因为此时net[0].weight.data是一个向量mat2 must be a matrix, got 1-D tensor
net[0].weight.data = torch.randn(1,4) # 注意data的维度 是1*4 还是4*1
net(x) # 输出正确结果
- pytorch 错误:
RuntimeError: Trying to backward through the graph a second time
解释: 函数关于自变量x在x0处的导数,你求了两次。 会出现上述的错误。
求导是分为两个步骤:
a. 前向过程,构建计算图
b. 反向求导。通过链式法则,反向计算导数
所以问题的关键在于 重新进行构建计算图
解决方案:- 梯度清零 不行。跟梯度清空无关
- 重新进行前向过程
x = torch.randn(2,requires_grad=True) y = torch.dot(x,x) y.backward() x.grad y.backward() # 错误:RuntimeError: Trying to backward through the graph a second time y.backward() x.grad x.grad == 4 * x # 输出 tensor([True, True])