计算图与动态图机制

计算图是用来描述运算的有向无环图。计算图有两个主要元素:结点(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 torcha = 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=<SigmoidBackward>)
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)
<torch._C.Generator object at 0x000001CDB4A5D330>
>>> 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=<MeanBackward0>)
>>> data_in.requires_grad
True
>>> data_mean.requires_grad
True
>>> data_1 = data_mean * 20200910.0
>>> data_1
tensor(1182591., grad_fn=<MulBackward0>)
>>> data_2 = data_1 * 15.0
>>> data_2
tensor(17738864., grad_fn=<MulBackward0>)
>>> data_2.retain_grad()
>>> data_3 = 2 * (data_2 + 55.0)
>>> loss = data_3 / 2.0 +89.2
>>> loss
tensor(17739010., grad_fn=<AddBackward0>)
>>>
>>> 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 = 2loss = torch.cat([y0, y1], dim=0)       # [y0, y1]
grad_tensors = torch.tensor([1., 2.])loss.backward(gradient=grad_tensors)    ###### gradient 传入 torch.autograd.backward()中的grad_tensorsprint(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 torchx = 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=<MmBackward>)
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(原地操作)

【pytorch】|tensor grad相关推荐

  1. 【PyTorch】 tensor.squeeze压缩维度

    [PyTorch] tensor.squeeze压缩维度 1.背景 2.squeeze方法 3.示例 1.背景 pytorch的tensor维度扩展.压缩是一个常见的操作, tensor维度扩展参见如 ...

  2. 【Pytorch】Tensor基本操作

    [Pytorch]Tensor基本操作 一.Tensor概述 二.Tensor张量的定义 tensor基本定义 获取tensor大小 三.生成Tensor 定义全0的tensor 定义随机tensor ...

  3. python和pytorch是什么关系_【PyTorch】Tensor和tensor的区别

    本文列举的框架源码基于PyTorch1.0,交互语句在0.4.1上测试通过 import torch 在PyTorch中,Tensor和tensor都能用于生成新的张量: >>> a ...

  4. 【Pytorch】Tensor.contiguous()使用与理解

    官方文档地址:https://pytorch.org/docs/stable/generated/torch.Tensor.contiguous.html?highlight=torch%20cont ...

  5. 【Pytorch】tensor类型数据.squeeze()和.unsqueeze()函数的简明教程(一看就会)

    文章目录 1 squeeze 1.1 1.2 1.3 执行操作后需要写回 2 unsqueeze pytorch系列代码中常见的两个函数squeeze()和unsqueeze() 1 squeeze ...

  6. 【pytorch】tensor和Tenso区别

    1.Tensor的使用 torch.Tensor可以直接接受传入的维度,建立相应维度的tensor,并且是初始化默认值类型的tensor,这里的默认值也是可以改的. #证明,torch.Tensor可 ...

  7. 【pytorch】过拟合的应对办法 —— 丢弃法(dropout)

    文章目录 一.什么是丢弃法,为什么丢弃法可以缓解过拟合? 二.丢弃法的手动实现 三.丢弃法的pytorch实现 参考 关于过拟合.欠拟合的解释可以参考我的博文:[pytorch]过拟合和欠拟合详解,并 ...

  8. 【金融】【pytorch】使用深度学习预测期货收盘价涨跌——全连接神经网络模型构建与训练

    [金融][pytorch]使用深度学习预测期货收盘价涨跌--全连接神经网络模型构建与训练 模型构建与训练 模型构建与训练 def get_accuracy(SR,GT,threshold=0.5):S ...

  9. 【金融】【pytorch】使用深度学习预测期货收盘价涨跌——LSTM模型构建与训练

    [金融][pytorch]使用深度学习预测期货收盘价涨跌--LSTM模型构建与训练 LSTM 创建模型 模型训练 查看指标 LSTM 创建模型 指标函数参考<如何用keras/tf/pytorc ...

最新文章

  1. c7pro android7,三星c7pro和iphone7哪个值得买?三星Galaxy c7 pro和苹果iphone7区别对比详细评测...
  2. 1、初识Server API for JavaScript
  3. memcached+keepalived+magent高群集
  4. 论文写作常见错误(1)
  5. WPF:仿WIN7窗体打开关闭效果
  6. 使用SSH密钥对给你的阿里云ECS加把安全锁
  7. layui jquery ajax,url,type,async,dataType,data
  8. C++描述杭电OJ 2017.字符串统计||
  9. 休眠和UUID标识符
  10. Pandas知识点-合并操作merge
  11. 使用TypeScript正确键入Vuex
  12. linux 深入检测io详情的工具iopp
  13. QDUOJ LC的课后辅导 单调递增栈
  14. 图像的上采样与下采样
  15. 思科模拟器路由器配置
  16. 单片机段式LCD驱动教程
  17. postman调用webservice接口
  18. 数据结构PTA 案例6-1.3 哥尼斯堡的“七桥问题”
  19. 小冲哥c语言视频自学网,C语言二级教学视屏课件_51自学网_小冲哥.doc
  20. 线性表中的尾插法单链表的学习

热门文章

  1. 平面设计主要是学什么?平面设计主要有哪些内容?——黎乙丙
  2. 阿里云发送短信功能(环境搭建篇)
  3. vue+canvas如何实现b站萌系登录界面
  4. 如何让360浏览器打开网页默认为“极速模式”
  5. 唯一能够胜过对手的,只有你的学习能力
  6. 基于MATLAB的电弧仿真模型(Mayr/Cassie 电弧模型)
  7. 戒梭先生:随笔|合格交易者要达到的三个境界
  8. 激光打印机维护保养完全手册
  9. JS实现Excel表格数据的导出
  10. 解决在EasyUI中使用百度地图出现不居中和坐标图标显示异常的问题(红色代码部分)