主要参考学习链接(动手学深度学习):https://tangshusen.me/Dive-into-DL-PyTorch/#/chapter02_prerequisite/2.2_tensor
1、理解 torch.layout
torch.zeros(*sizes, out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
其中,torch.dtype 是 tensor 的数据类型
torch.device 表明计算设备是 CPU 还是 GPU
torch.layout 表明内存布局(memory layout),即 tensor 在物理设备中的储存结构。
学过数据结构的都知道储存结构(物理结构)简单可以分为:顺序储存、链式储存。
torch.layout 可选torch.stried或torch.sparse_coo。分别对应顺序储存、离散储存。
一般说,稠密张量适用torch.stried,稀疏张量(0 比较多)适用torch.sparse_coo。
参考原文链接:https://blog.csdn.net/weixin_37641832/article/details/104906327
2、错误:a leaf Variable that requires grad has been used in an in-place operation(还有疑惑未解决)
这个bug的意思是 需要梯度的叶子变量被用在了原位操作里
指的是 如果是原位操作,就会覆盖原来的值。
因为并不是计算过程,是赋值,梯度是多少这个没有一个定义(或许是一个跳变过程,梯度无穷大)。
所以产生了错误,可以加上 torch.no_grad() 来规避掉梯度问题
参考链接:https://blog.csdn.net/jacke121/article/details/82733407
a = torch.ones(2, 2, requires_grad=True)
print(a, a.grad_fn)
# tensor([[1., 1.],
# [1., 1.]], requires_grad=True) None
a += 2
print(a, a.grad_fn)
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-11-24d4d2cc7ea6> in <module>
----> 1 a += 2
2 print(a, a.grad_fn)
RuntimeError: a leaf Variable that requires grad has been used in an in-place operation.
如果先进行a = a + 2则再进行a += 2没有错误,为什么?
a = torch.ones(2, 2, requires_grad=True)
print(a, a.grad_fn)
a = a + 2
print(a, a.grad_fn)
a += 2
print(a, a.grad_fn)
# tensor([[5., 5.],
# [5., 5.]], grad_fn=<AddBackward0>) <AddBackward0 object at 0x000001FD049BAE48>
3、torch.range和torch.arange区别
尽量少用torch.range,将被移除,同时range是包括右端点,arange不包括
e = torch.arange(1, 5).view(2, 2)
print(e, e.grad_fn)
# tensor([[1, 2],
# [3, 4]]) None
>>> torch.range(1, 4)
tensor([ 1., 2., 3., 4.])
4、错误:only Tensors of floating point dtype can require gradients
变量e类型改为torch.float
e = torch.arange(1, 5).view(2, 2)
print(e, e.grad_fn, e.requires_grad)
e = (e * 3)
print(e, e.grad_fn, e.requires_grad)
e.requires_grad_(True)
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-6-965c5da3d33e> in <module>
----> 1 e.requires_grad_(True)
RuntimeError: only Tensors of floating point dtype can require gradients
5、注意在y.backward()时,如果y是标量,则不需要为backward()传入任何参数;否则,需要传入一个与y同形的Tensor。
Tensor是这个autograd包的核心类,如果将其属性 .requires_grad设置为True,它将开始追踪(track)在其上的所有操作(这样就可以利用链式法则进行梯度传播了)。完成计算后,可以 调用.backward() 来完成所有梯度计算。此Tensor的梯度将累积到.grad属性中。
不允许张量对张量求导,只允许标量对张量求导,求导结果是和自变量同形的张量。
所以必要时我们要把张量通过将所有张量的元素加权求和的方式转换为标量,举个例子,假设y由自变量x计算而来,w是和y同形的张量,则y.backward(w)的含义是:先计算l = torch.sum(y * w),则l是个标量,然后求l对自变量x的导数。
g = torch.tensor([1, 2, 3, 4, 5, 6], dtype=torch.float, requires_grad=True)
print(g, type(g), g.size(0), g.grad_fn, g.requires_grad)
h =g.view(3, 2)
print(h, h.size(0), h.grad_fn, h.requires_grad)
h.backward(torch.tensor[[1., 1.], [1., 1.], [1., 1.]])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-21-8728892ac37a> in <module>
3 h =g.view(3, 2)
4 print(h, h.size(0), h.grad_fn, h.requires_grad)
----> 5 h.backward(torch.tensor[[1., 1.], [1., 1.], [1., 1.]])
TypeError: 'builtin_function_or_method' object is not subscriptable
错误:正确格式为torch.ensor([[1., 1.], [1., 1.], [1., 1.]]),不要忘记括号。
g =torch.tensor([1, 2, 3, 4, 5, 6], dtype=torch.float, requires_grad=True)
print(g, type(g), g.size(0), g.grad_fn, g.requires_grad)
h =g.view(3, 2)
h = h * 2
print(h, h.size(0), h.grad_fn, h.requires_grad)
h.backward(torch.tensor([[1., 1.], [1., 1.], [1., 1.]]))
print(g.grad)
tensor([1., 2., 3., 4., 5., 6.], requires_grad=True) <class 'torch.Tensor'> 6 None True
tensor([[1., 2.],
[3., 4.],
[5., 6.]], grad_fn=<ViewBackward>) 3 <ViewBackward object at 0x000002049EEDF240> True
tensor([2., 2., 2., 2., 2., 2.])
注意,g.grad是和g同形的张量。
6、backward问题
6.1 需要有a, a_1,a_2之类的中间变量,不能一直对a运算,同时需要对最后一个变量进行a_2.backward(),对第一个变量a求a.grad
a = torch.ones(2, 2, requires_grad=True)
print(a, a.grad_fn)
a_1 = a * 2
print(a, a.grad_fn)
a_2 = a_1.sum()
a_2.backward()
print(a.grad)
# tensor([[1., 1.],
# [1., 1.]], requires_grad=True) None
# tensor([[2., 2.],
# [2., 2.]])
7、data()和detach()的区别(有问题未解决)
7.1 data()和detach()
.data 和.detach只取出本体tensor数据,舍弃了grad,grad_fn等额外反向图计算过程需保存的额外信息
参考链接:https://blog.csdn.net/dss_dssssd/article/details/89526623?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
(1) m.data可直接使用,m.detach不可,这是为什么?
m = torch.ones(1, requires_grad=True)
m.data = m.data * 100
print(m, m.data.grad_fn, m.data.requires_grad)
n = m * 2
n.backward()
print(m.grad)
# tensor([100.], requires_grad=True) None False# #
# tensor([2.])
m = torch.ones(1, requires_grad=True)
m.detach = 100 * m.detach
print(m, m.detach.grad_fn, m.detach.requires_grad)
n = m * 2
n.backward()
print(m.grad)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-42-ef1dd9a83298> in <module>
1 m = torch.ones(1, requires_grad=True)
----> 2 m.detach = 100 * m.detach
3 print(m, m.detach.grad_fn, m.detach.requires_grad)
4 n = m * 2
5 n.backward()
TypeError: unsupported operand type(s) for *: 'int' and 'builtin_function_or_method'
需要修改为:
m = torch.ones(1, requires_grad=True)
m.detach = 100 * m.detach() # m和m.detach是两个变量,不分享内存
print(m, m.detach.grad_fn, m.detach.requires_grad)
n = m * 2
n.backward()
print(m.grad)
tensor([1.], requires_grad=True) None False
tensor([2.])
(2) m_2直接获取detach,m_2 = m_2 + 2前后内存地址不一样,不共享内存
m = torch.ones(1, requires_grad=True)
m_2 = m.detach()
m_2 = m_2 + 2
print(m, m_2, m_2.grad_fn, m_2.requires_grad)
n = m * 2
print(n, n.requires_grad)
n.backward()
print(m.grad)
tensor([1.], requires_grad=True) tensor([3.]) None False
tensor([2.], grad_fn=<MulBackward0>) True
tensor([2.])
(3) .zero_()共享内存,m_2 += 2内存地址不变,所以detach也共享内存,并没有报错
m = torch.ones(1, requires_grad=True)
m = m + 1
m_2 = m.detach()
m_2 = m_2.zero_()
print(m, m_2, m_2.grad_fn, m_2.requires_grad)
n = m * 2
print(n, n.requires_grad)
n.backward()
print(m.grad)
tensor([0.], grad_fn=<AddBackward0>) tensor([0.]) None False
tensor([0.], grad_fn=<MulBackward0>) True
None
m = torch.ones(1, requires_grad=True)
m_2 = m.detach()
m_2 += 2
print(m, m_2, m_2.grad_fn, m_2.requires_grad)
n = m * 2
print(n, n.requires_grad)
n.backward()
print(m.grad)
#####
tensor([3.], requires_grad=True) tensor([3.]) None False
tensor([6.], grad_fn=<MulBackward0>) True
tensor([2.])
所以对m_2进行inplace操作就可以改变m的值,否则m, m_2数值之间无关
.data取出本体tensor后仍与原数据共享内存(从第一个代码段中可以看出),在使用in-place操作后,会修改原数据的值,而如果在反向传播过程中使用到原数据会导致计算错误,而使用.detach后,如果在反向传播过程中发现原数据被修改过会报错。更加安全
7.2 detach还是很保险的,有些情况下是能够报错的,但并不全都是
参考链接:https://zhuanlan.zhihu.com/p/83329768
x = torch.tensor(0., requires_grad=True)
y = x.sigmoid()
y.detach().zero_()
print(y)
y.backward()
不出所料,报错如下,
RuntimeError: one of the variables needed for gradient computation has been modified by
原因:这里修改了y的data,而bp的计算依赖这个data,因此报错
那换成另外一个操作呢,
x = torch.tensor(1., requires_grad=True)
y = x ** 2
y.detach().zero_()
print(y)
y.backward()
print(x.grad)
这里成功输出如下,
tensor(0., grad_fn=<PowBackward0>)
tensor(2.)
8、 torch.FloatTensor细节
a = torch.FloatTensor(2)
a.requires_grad = True
print(a, a.size())
tensor([3.6013e-43, 0.0000e+00], requires_grad=True) torch.Size([2])
a = torch.FloatTensor([2])
a.requires_grad = True
print(a, a.size())
tensor([2.], requires_grad=True) torch.Size([1])