pytorch学习记录

1.
在使用clone()时,比如x = y[1, :].clone()
此时的x不是y的视图,只是一个克隆复制品。此时修改x,并不会影响y;同样的修改y也不会影响到x。
但是如果没有使用clone(),则会互相影响。
使用clone还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源Tensor

2.
pytorch和numpy一样,内部提供了大量的线性代数方法函数。避免重复造轮子。

3.
pytorch中,索引操作不会开辟新内存地址(即使用clone函数的原因),但是普通运算会。
所以,
y = x + y
操作时等号左边的y的内存地址与等号右边的y的内存地址不一样。

y[:] = x + y
则不会开辟新的内存空间。等号两边的y的内存地址一致。
TIP:
如果不想使用索引操作,也想不开辟新空间:
y += x   #自加操作
or
y.add_(x)。 #方法带_后缀一般是replace方法,效果和自加(add_)操作一样。

4.
对张量使用view(shape)函数时,
如y = x.view(x.shape)
此时,y共享x的数据,在这个角度看,y是x的视图,修改x(y)会影响到y(x)的值。
但是y与x的内存地址并不相同,即y依然是一个全新的tensor,只是数据x与y共享。原因在于:
一个tensor除了拥有数据data外,还拥有其他属性。

5.
tensor与numpy很像,所以两者都提供了将自身转换为对方的函数:
numpy():将tensor转换为numpy。
from_numpy():将numpy转换为tensor。

Note:
如果x原先是Numpy类型,将其转换为y的tensor类型后,x与y共享内存。。。

6.
继续上面的话题:
将Numpy转换为tensor也可以使用:
torch.tensor(numpy类型)。
只是使用这种方法得到的tensor与原先的numpy并不共享内存,所以开销大些。。。

7.
tensor的自动求梯度。
如果将其属性.requires_grad设置为True,它将开始追踪(track)在其上的所有操作(这样就可以利用链式法则进行梯度传播了)。
完成计算后,可以调用.backward()来完成所有梯度计算。
此Tensor的梯度将累积到.grad属性中。

Note:
在y.backward()时,如果y是标量,则不需要为backward()传入任何参数;
否则,需要传入一个与y同形的Tensor
!!!

8.
tricks:

如果不想要被继续追踪,可以调用.detach()将其从追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了
②!!!
可以用with torch.no_grad()将不想被追踪的操作代码块包裹起来。
在评估模型的时候很常用,因为在评估模型时,我们并不需要计算可训练参数(requires_grad=True)的梯度。

9.
Function是torch中出tensor类外另外一个很重要的类。
每个Tensor都有一个.grad_fn属性。
该属性即创建该Tensor的Function, 就是说该Tensor是不是通过某些运算得到的,
若是,则grad_fn返回一个与这些运算相关的对象,否则是None。

Tip:
上面的话怎么理解呢。
可以结合神经网络中的输入层、隐藏层以及输出层来理解。
每一层都可以看成一个tensor,
其中,
①输入层的tensor在监督学习的情况下,是由外界输入的,它的值的来由没有任何理由 ---- 不是通过任何计算得到。
此时对输入层的tensor取其grad_fn属性,必定为None。
②隐藏层中的各个tensor的值都是由上一层的tensor的值通过一些激活函数组合得到的 ---- 即通过一些计算得到。
所以对这些隐藏层的tensor取其grad_fn属性,必定可以得到一个计算情况。
而且这个运算情况指的是:最后一次运算时的运算号。
如:
y = x*1
此时y.grad_fn返回:Mul
y = x+1 
此时返回:Add
y = (x*1+2) - 3
此时返回:Sub
y = (x*x).sum()
此时返回:sum

可以看出,这个grad_fn属性使得查错变得非常简便。。。

10.
继续上面的。
grad_fn返回None的tensor又称为叶子节点(因为torch的这种自动求导、链式法则就是通过构建节点数来实现的)。


11.
中断梯度。
比如:
x = torch.tensor(1.0, requires_grad=True)
y1 = x ** 2 
with torch.no_grad():
    y2 = x ** 3
y3 = y1 + y2

print(x.requires_grad)
print(y1, y1.requires_grad) # True
print(y2, y2.requires_grad) # False
print(y3, y3.requires_grad) # True

此时y1,y3很明显,没有问题。
y2的结果为false,因为它是在torch.no_grad中,相当于在节点树中,将其与x(其运算元素)叶子节点的联系人为切断了。
所以不会有y2的链式导数结果。
这样会有两个后果:
①就是上面的结果,grad为false。
②其有关梯度不会回传。
如,对y3求导时,
由于此时y3 = y1 + y2 = x^2 + x^3。
正常情况下,y3的对x的导数(此时x=1)为5,但是此时输出结果为2。就是因为y2的对x的求导结果不会回传。

12.
如果我们想要修改tensor的数值,但是又不希望被autograd记录(即不会影响反向传播),那么我么可以对tensor.data进行操作
如:
x = torch.ones(1,requires_grad=True)

print(x.data) # 还是一个tensor   tensor([100.])
print(x.data.requires_grad) # 但是已经是独立于计算图之外   False

y = 2 * x
x.data *= 100 # 只改变了值,不会记录在计算图,所以不会影响梯度传播

y.backward()  #y对x求导。因为y是个标量,所以不需要传入参数。
print(x) # 更改data的值也会影响tensor的值   100
print(x.grad)  #  tensor([2.])。可见,此时的grad并未受到影响


13.!!!
如果y是个标量,则在调用backward()时,不需要传入参数。原因:https://zhuanlan.zhihu.com/p/29923090
如果y是个张量,则需要传入与y同形的张量v来求导。这时:
y.backward(v)相当于:
先计算 l = torch.sum(w*v)。
此时的l是一个标量,然后对l求x的导数。
可以看出,传入的v张量就是一个①加权②化张量为标量 这两个作用。


14.!!!
grad在反向传播过程中是累加的(accumulated)。
这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零!!!

如:如果上一次求导,x.grad()为:[[4.5, 4.5], [4.5, 4.5]]。且x是全1的2x2张量。
# 再来反向传播一次,注意grad是累加的
out2 = x.sum()
out2.backward()
print(x.grad)  # [[5.5, 5.5], [5.5, 5.5]]   可以说明,如果不清零,则每次x上的梯度叠加。

out3 = x.sum()
x.grad.data.zero_()   #梯度清零
out3.backward()
print(x.grad)  # [[1, 1], [1, 1]]   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值