【pytorch】|tensor grad

【pytorch】|tensor grad,第1张

【pytorch】|tensor grad 计算图与动态图机制

计算图是用来描述运算的有向无环图。计算图有两个主要元素:结点(Node)和边(Edge)。结点表示数据,如向量,矩阵,张量;边表示运算,如加减乘除卷积等。
下面用计算图表示:y = ( x + w ) ∗ ( w + 1 )


采用计算图描述运算的好处:不仅使得运算更加简洁,而且使得梯度求导更加方便。下面用代码展示上述计算图梯度求导过程:

import torch

# 需要计算梯度-requires_grad=True
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)

# 前向传播
a = torch.add(w, x)     # retain_grad()
b = torch.add(w, 1)
y = torch.mul(a, b)

# 反向传播-自动求导
y.backward()
print(w.grad)
# 5

# 查看叶子结点
print("is_leaf:n", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf)

# 查看梯度
print("gradient:n", w.grad, x.grad, a.grad, b.grad, y.grad)

is_leaf:
 True True False False False
gradient:
 tensor([5.]) tensor([2.]) None None None

如果我们想要保存非叶子节点的梯度,那么应该怎么做呢?

import torch

# 需要计算梯度-requires_grad=True
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)

# 前向传播
a = torch.add(w, x)     
# 保存非叶子节点a的梯度
a.retain_grad()#############
b = torch.add(w, 1)
y = torch.mul(a, b)

# 反向传播-自动求导
y.backward()
print(w.grad)

# 查看叶子结点
print("is_leaf:n", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf)

# 查看梯度
print("gradient:n", w.grad, x.grad, a.grad, b.grad, y.grad)

tensor([5.])
is_leaf:
 True True False False False
gradient:
 tensor([5.]) tensor([2.]) tensor([2.]) None None

grad_fn:grad_fn:记录创建该张量时所用的方法(函数),是自动求导的关键

# 查看创建张良所使用的函数
print("grad_fn:n", w.grad_fn, x.grad_fn, a.grad_fn, b.grad_fn, y.grad_fn)

根据计算图搭建方式,可将计算图分为静态图与动态图。静态图是先搭建图后运算。静态图的特点:高效不灵活。TensorFlow是静态图。动态图是运算与搭建同时进行。动态图的特点:灵活易调节。PyTorch是动态图。

tensor 数据类型

系统默认是torch.FloatTensor类型。

类型转换 数据类型的转换

在Tensor后加long(), int(), double(),float(),byte()等函数就能将Tensor进行类型转换

Torch.LongTensor—>Torch.FloatTensor, 直接使用data.float()

CPU-GPU

在cpu上tensor的数据类型:torch.FloatTensor
在GPU上tensor的数据类型:torch.cuda.FloatTensor
CPU张量和GPU张量之间的转换
CPU -> GPU: data.cuda()
GPU -> CPU: data.cpu()

tensor-numpy

Tensor与Numpy Array之间的转换

Tensor---->Numpy 可以使用 data.numpy(),data为Tensor变量

Numpy ----> Tensor 可以使用torch.from_numpy(data),data为numpy变量

tensor *** 作 初始化

一个张量tensor可以从Python的list或序列构建:

a=torch.FloatTensor([[1, 2, 3], [4, 5, 6]])
a.shape
>>torch.Size([2, 3])


torch.FloatTensor([1, 2, 3]).shape

>>torch.Size([3])

一个空张量tensor可以通过规定其大小来构建:

torch.IntTensor(2, 4).zero_()
torch.FloatTensor(2, 4).zero_()

a = torch.ones(2, 2, requires_grad=True)
b = torch.rand_like(a, requires_grad=True)


in-place *** 作 会改变 *** 作数tensor的函数 *** 作,用一个下划线后缀来标示。
不带下划线将会在一个新的tensor中计算结果。

clone() detach()

tensor复制可以使用clone()函数和detach()函数

clone()

clone()函数可以返回一个完全相同的tensor,新的tensor开辟新的内存,但是仍然留在计算图中。

将计算图中参与运算tensor变为clone()后的tensor。此时梯度仍然只流向了原始的tensor。

x= torch.tensor([1., 2., 3.], requires_grad=True)
clone_x = x.clone()
detach_x = x.detach()
clone_detach_x = x.detach().clone()

f = torch.nn.Linear(3, 1)
y = f(clone_x)
y.backward()

print(x.grad)
print(clone_x.grad)
print(detach_x.requires_grad)
print(clone_detach_x.requires_grad)

tensor([-0.0043,  0.3097, -0.4752])
None
False
False

将原始tensor设为requires_grad=False,clone()后的梯度设为.requires_grad_(),clone()后的tensor参与计算图的运算,则梯度穿向clone()后的tensor。

x= torch.tensor([1., 2., 3.], requires_grad=False)
clone_x = x.clone().requires_grad_()
detach_x = x.detach()
clone_detach_x = x.detach().clone()

f = torch.nn.Linear(3, 1)
y = f(clone_x)
y.backward()

print(x.grad)
print(clone_x.grad)
print(detach_x.requires_grad)
print(clone_detach_x.requires_grad)

None
tensor([-0.0043,  0.3097, -0.4752])
False
False

detach()
import torch
 
a = torch.tensor([1,2,3.], requires_grad = True)
out = a.sigmoid()
c = out.detach()  # 通过.detach() “分离”得到的的变量也会与原张量使用同一数据,而且新分离得到的张量是不可求导的
c.zero_()         # 改变c的值,原来的out也会改变
print(c.requires_grad)    #false
print(c)          #tensor([0., 0., 0.])
print(out.requires_grad)  #true
print(out)        #tensor([0., 0., 0.], grad_fn=)
print("----------------------------------------------")
 
out.sum().backward()      # 对原来的out求导,
print(a.grad)     # 此时会报错,监测到梯度计算所需要的张量已经被“原位 *** 作inplace”所更改了

detach()函数可以返回一个完全相同的tensor,新的tensor开辟与旧的tensor共享内存,新的tensor会脱离计算图,不会牵扯梯度计算。此外,一些原地 *** 作(in-place, such as resize_ / resize_as_ / set_ / transpose_) 在两者任意一个执行都会引发错误。

detach()后的tensor由于与原始tensor共享内存,所以原始tensor在计算图中数值反向传播更新之后,detach()的tensor值也发生了改变。

x = torch.tensor([1., 2., 3.], requires_grad=True)
f = torch.nn.Linear(3, 1)
w = f.weight.detach()
print(f.weight)
print(w)

y = f(x)
y.backward()

optimizer = torch.optim.SGD(f.parameters(), 0.1)
optimizer.step()

print(f.weight)
print(w)

Parameter containing:
tensor([[-0.0043,  0.3097, -0.4752]], requires_grad=True)
tensor([[-0.0043,  0.3097, -0.4752]])
Parameter containing:
tensor([[-0.1043,  0.1097, -0.7752]], requires_grad=True)
tensor([[-0.1043,  0.1097, -0.7752]])

tensor属性

https://blog.csdn.net/weixin_46649052/article/details/118694624

Variable是PyTorch0.4.0之前的重要数据类型,在PyTorch0.4.0之后已经并入到Tensor中。但是我们还要了解Variable这一数据类型,因为了解Variable对了解Tensor是十分有帮助的。Variable是torch.autograd中的数据类型,进行自动求导。


data:被包装的Tensor
grad: data的梯度
grad_fn:创建Tensor的Function,是自动求导的关键
requires_grad:指示是否需要梯度
is_lea f:指示是否是叶子结点(张量)

PyTorch0.4.0版开始,Variable并入Tensor。在并入之后,Tensor有8个属性:

data:被包装的Tensor
dtype:张量的数据类型,如torch.FloatTensor, torch.cuda.FloatTensor(表示数据放到了GPU上)
shape:张量的形状,如(64,3,224,224)
device:张量所在设备,GPU/CPU,是加速的关键
grad: data的梯度
grad_fn:创建Tensor的Function,是自动求导的关键
requires_grad:指示是否需要梯度
is_lea f:指示是否是叶子结点(张量)

dtype shape device

tensor = torch.rand(3,4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")

import torch
a = torch.randn(3,5,requires_grad=True)
b = a.sum()
c = a.mean()
b.backward()
a.grad
c.backward()
a.grad # 不清零的话梯度将会累加
 a.grad.zero_()
 c.backward()
 a.grad
grad

该Tensor的梯度值,默认情况下是None,但是当第一次为当前张量自身self计算梯度调用backward()方法时,
该属性grad将变成一个Tensor张量类型. 该属性将包含计算所得的梯度,
在这之后如果再次调用backward()方法,将会对这个grad属性进行累加.

tensor.grad_fn

叶子节点通常为None,只有结果节点的grad_fn才有效,用于指示梯度函数是哪种类型。
print(tensor.grad_fn)

requires_grad

设置为True则表示该Tensor需要求导

print(tensor.requires_grad)

x.requires_grad_()

 a = torch.randn(3,5)
 a.requires_grad_()
 a

.只有对于 requires_grad=True的叶子张量,我们才会将梯度一直保存在该叶子张量的grad属性中,对于非叶子节点, 即中间节点的张量,我们在计算完梯度之后为了更高效地利用内存,我们会将梯度grad的内存释放掉.)

>>> import torch
>>> torch.manual_seed(seed=20200910)

>>> data_in = torch.randn(3,5,requires_grad=True)
>>> data_in
tensor([[ 0.2824, -0.3715,  0.9088, -1.7601, -0.1806],
        [ 2.0937,  1.0406, -1.7651,  1.1216,  0.8440],
        [ 0.1783,  0.6859, -1.5942, -0.2006, -0.4050]], requires_grad=True)
>>> data_mean = data_in.mean()
>>> data_mean
tensor(0.0585, grad_fn=)
>>> data_in.requires_grad
True
>>> data_mean.requires_grad
True
>>> data_1 = data_mean * 20200910.0
>>> data_1
tensor(1182591., grad_fn=)
>>> data_2 = data_1 * 15.0
>>> data_2
tensor(17738864., grad_fn=)
>>> data_2.retain_grad()
>>> data_3 = 2 * (data_2 + 55.0)
>>> loss = data_3 / 2.0 +89.2
>>> loss
tensor(17739010., grad_fn=)
>>>
>>> data_in.grad
>>> data_mean.grad
>>> data_1.grad
>>> data_2.grad
>>> data_3.grad
>>> loss.grad
>>> print(data_in.grad, data_mean.grad, data_1.grad, data_2.grad, data_3.grad, loss.grad)
None None None None None None
>>>
>>> loss.backward()
>>> data_in.grad
tensor([[20200910., 20200910., 20200910., 20200910., 20200910.],
        [20200910., 20200910., 20200910., 20200910., 20200910.],
        [20200910., 20200910., 20200910., 20200910., 20200910.]])
>>> data_mean.grad
>>> data_mean.grad
>>> data_1.grad
>>> data_2.grad
tensor(1.)
>>> data_3.grad
>>> loss.grad
>>>
>>>
>>> print(data_in.grad, data_mean.grad, data_1.grad, data_2.grad, data_3.grad, loss.grad)
tensor([[20200910., 20200910., 20200910., 20200910., 20200910.],
        [20200910., 20200910., 20200910., 20200910., 20200910.],
        [20200910., 20200910., 20200910., 20200910., 20200910.]]) None None tensor(1.) None None
>>>
>>>
>>> print(data_in.is_leaf, data_mean.is_leaf, data_1.is_leaf, data_2.is_leaf, data_3.is_leaf, loss.is_leaf)
True False False False False False
>>>
>>>
>>>

is_leaf

判断是否是叶子节点

对于自己定义的变量,我们称之为叶子节点(leaf nodes),而基于叶子节点得到的中间或最终变量则可称之为结果节点。

torch.autograd.backward(z) = z.backward()

深度学习模型的训练就是不断更新权值。权值的更新需要求解梯度。PyTorch提供自动求导系统解决这一问题。自动求导系统autograd只需要搭建前向传播的计算图,然后通过torch.autograd就可以得到每个张量的梯度。

默认为NONE ,当使用backward()后计算梯度,得到tensor
再次使用backward将会累计梯度(你可能需要在调用此函数之前将leaf variable的梯度置零)

torch.autograd.backward(
		tensors, 
		grad_tensors=None, 
		retain_graph=None, 
		create_graph=False, 
		grad_variables=None)
tensor: 用于计算梯度的tensor。也就是说这两种方式是等价的:torch.autograd.backward(z) == z.backward()
grad_tensors: 在计算矩阵的梯度时会用到。多梯度权重(用于多个梯度权重的设置),他其实也是一个tensor,shape一般需要和前面的tensor保持一致。
retain_graph: 通常在调用一次backward后,pytorch会自动把计算图销毁,所以要想对某个变量重复调用backward,则需要将该参数设置为True
create_graph: 当设置为True的时候创建导数计算图,用于高阶求导
grad_variables: 这个官方说法是grad_variables' is deprecated. Use 'grad_tensors' instead.也就是说这个参数后面版本中应该会丢弃,直接使用grad_tensors就好了

注意:函数必须是求得的一个值,即标量。而求一个矩阵对另一矩阵的导数束手无策。

w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)

a = torch.add(w, x)     # retain_grad()
b = torch.add(w, 1)

y0 = torch.mul(a, b)    # y0 = (x+w) * (w+1)    dy0/dw = 5
y1 = torch.add(a, b)    # y1 = (x+w) + (w+1)    dy1/dw = 2

loss = torch.cat([y0, y1], dim=0)       # [y0, y1]
grad_tensors = torch.tensor([1., 2.])

loss.backward(gradient=grad_tensors)    ###### gradient 传入 torch.autograd.backward()中的grad_tensors

print(w.grad)

## 输出
# tensor([9.])
### grad_tensors = torch.tensor([1., 1.])  #或 grad_tensors = torch.ones_like(loss)
## 输出 tensor([7.])

举例:

x = torch.ones(2,requires_grad=True)
z = x + 2
z.backward()

>>> ...
RuntimeError: grad can be implicitly created only for scalar outputs

那么我们只要想办法把矩阵转变成一个标量不就好了?比如我们可以对z求和,然后用求和得到的标量在对x求导,这样不会对结果有影响,例如:

进一步,对z求和不就是等价于z点乘一个一样维度的全为1的矩阵吗?
而这个I也就是我们需要传入的grad_tensors参数。(点乘只是相对于一维向量而言的,对于矩阵或更高为的张量,可以看做是对每一个维度做点乘)

x = torch.ones(2,requires_grad=True)
z = x + 2
z.backward(torch.ones_like(z)) # grad_tensors需要与输入tensor大小一致
print(x.grad)

>>> tensor([1., 1.])
x = torch.ones(2,requires_grad=True)
z = x + 2
z.sum().backward()
print(x.grad)

>>> tensor([1., 1.])
import torch

x = torch.ones(5)  # input tensor
y = torch.zeros(3)  # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)

loss.backward()
print(w.grad)
print(b.grad)

If we need to do several backward calls on the same graph, we need to pass retain_graph=True to the backward call.


```c
x = torch.tensor([2., 1.], requires_grad=True)
y = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True)

z = torch.mm(x.view(1, 2), y)
print(f"z:{z}")
z.backward(torch.Tensor([[1., 0]]), retain_graph=True)
print(f"x.grad: {x.grad}")
print(f"y.grad: {y.grad}")

>>> z:tensor([[5., 8.]], grad_fn=)
x.grad: tensor([[1., 3.]])
y.grad: tensor([[2., 0.],
        [1., 0.]])

### torch.no_grad()
用来禁止梯度的计算,常用在网络推断。
被with torch.no_grad包起来的 *** 作,仍会运行或计算,但是他们的requires_grad属性会被赋为False。从而在计算图中关闭这些 *** 作的梯度计算。
![在这里插入图片描述](https://img-blog.csdnimg.cn/4b899e8266024a73a3c6dad2fabb785a.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAcnJyMg==,size_20,color_FFFFFF,t_70,g_se,x_16)
#### 梯度不自动清零,需要手动清零

```c
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)

for i in range(4):
    a = torch.add(w, x)
    b = torch.add(w, 1)
    y = torch.mul(a, b)

    y.backward()
    print(w.grad)
    # 梯度清零
    # w.grad.zero_()

依赖于叶子节点的节点,requires_grad默认为True,叶子节点不可执行in-place(原地 *** 作)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5495867.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-12
下一篇 2022-12-12

发表评论

登录后才能评论

评论列表(0条)

保存