【Pytorch学习笔记二】pytorch中的张量(Tensors)和计算梯度数值(Atuograd)

【Pytorch学习笔记二】pytorch中的张量(Tensors)和计算梯度数值(Atuograd)_第1张图片

文章目录

  • 一,张量 (Tensors)
    • 1. tensor的属性
    • 2.创建张量
    • 3.数据初始化
    • 4,常用方法
  • 二,PyTorch计算梯度数值
    • 1.Autograd
    • 2. 扩展Autograd

参考资料:

  • https://handbook.pytorch.wiki/chapter1/1.1-pytorch-introduction.html
  • https://www.bookstack.cn/read/PyTorch-cn/README.md

一,张量 (Tensors)

张量的意思是一个多维数组,它是标量(0维)、向量(1维)、矩阵(2维),RGB 图像(3维)的高维扩展。

1. tensor的属性

  • data: 被包装的 Tensor。
  • grad: data 的梯度。
  • grad_fn: 创建 Tensor 所使用的 Function,是自动求导的关键,因为根据所记录的函数才能计算出导数。
  • requires_grad: 指示是否需要梯度,并不是所有的张量都需要计算梯度。
  • is_leaf: 指示是否叶子节点(张量),叶子节点的概念在计算图中会用到,后面详细介绍。
  • dtype: 张量的数据类型,共有五种数据类型:- 32位浮点型:torch.FloatTensor。 (默认) - 64位整型:torch.LongTensor。 - 32位整型:torch.IntTensor。 - 16位整型:torch.ShortTensor。 - 64位浮点型:torch.DoubleTensor。具体可看官方文档:https://pytorch.org/docs/stable/tensors.html
  • shape: 张量的形状(Size()一样)。如 (64, 3, 224, 224)
  • device: 张量所在设备 (CPU/GPU),GPU 是加速计算的关键

2.创建张量

简单生成一个张量

import torch
import numpy as np
x= torch.rand(2,3)
print(x.size())
print(x.dtype)
print(x.requires_grad)
print(x.grad)
print(x.data)
print(x.grad_fn)
print(x.device)
结果:
torch.Size([2, 3])
torch.float32
False
None
tensor([[0.2630, 0.8515, 0.0415],
     [0.3337, 0.2660, 0.2949]])
None
cpu

利用torch.Tensor()生成张量

x = [2,3,2,4]
torch.tensor(x)
结果:
tensor([2, 3, 2, 4])

从NumPy数组生成:共享内存

x= np.zeros((2,3,4))
torch.from_numpy(x)
结果:
tensor([[[0., 0., 0., 0.],
      [0., 0., 0., 0.],
      [0., 0., 0., 0.]],

     [[0., 0., 0., 0.],
      [0., 0., 0., 0.],
      [0., 0., 0., 0.]]], dtype=torch.float64)

Tensor数据的CPU和GPU转换

#使用torch.cuda.is_available()来确定是否有cuda设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
#使用设备
x = torch.rand((2,3,4),device = device)
print(x.type())
x=x.cpu() #转为CPU
print(x.type())
结果:
cuda
torch.cuda.FloatTensor
torch.FloatTensor

3.数据初始化

初始化为0:torch.ones()

# torch.ones(*size, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
#size: 张量的形状
#out: 输出的张量,如果指定了 out,那么torch.zeros()返回的张量和 out 指向的是同一个地址
#layout: 内存中布局形式,有 strided,sparse_coo 等。当是稀疏矩阵时,设置为 sparse_coo 可以减少内存占用。
#device: 所在设备,cuda/cpu
#requires_grad: 是否需要梯度

torch.ones((2,2,2),device="cpu")
结果:
tensor([[[1., 1.],
      [1., 1.]],

     [[1., 1.],
      [1., 1.]]])

**初始化为单位矩阵:**torch.eye()

#初始化一个单位矩阵,即对角线为1 其他为0
#eye(n, m=None, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
eye=torch.eye(2,2)
eye
结果:
tensor([[1., 0., 0.],
     [0., 1., 0.],
     [0., 0., 1.]])

数据正态分布(高斯分布):torch.normal()

#normal(mean, std, *, generator=None, out=None)
#mean:平均值
#std:标准差
#generator:随机数种子
normal_t = torch.normal(mean=torch.arange(2,10,dtype=torch.float),std=2)
print(normal_t)
结果
tensor([ 2.2514,  3.6211,  4.0486,  4.8381,  6.1706,  7.6390, 10.2755,  5.9701])

标准正态分布:torch.randn()

4,常用方法

沿行取最大值:max_value, max_idx = torch.max(x, dim=int)

x=torch.randn(3,3,3)
print(x)
max_value, max_idx=torch.max(x, dim=2) # dim:要求和的维度,可以是一个列表
print(max_value)
print(max_idx)
结果:
tensor([[[ 0.5244, -0.1050,  1.0411],
      [ 0.3610,  0.8447, -1.5066],
      [-0.6421, -0.1181, -0.1401]],

     [[ 0.5994, -1.5200, -1.8082],
      [ 1.7824, -2.1836, -1.3482],
      [ 1.4337,  0.6371,  1.6609]],

     [[-0.3236, -0.4225, -1.5040],
      [ 0.9013,  0.0914,  1.3402],
      [-1.0523, -1.7614, -0.7179]]])
tensor([[ 1.0411,  0.8447, -0.1181],
     [ 0.5994,  1.7824,  1.6609],
     [-0.3236,  1.3402, -0.7179]])
tensor([[2, 1, 1],
     [0, 0, 2],
     [0, 2, 2]])

行和:torch.sum(input, *, dtype=None)

sum_x=torch.sum(x,dim=2)
print(sum_x)
结果:
tensor([[ 1.4605, -0.3009, -0.9004],
     [-2.7288, -1.7494,  3.7317],
     [-2.2501,  2.3328, -3.5316]])

二,PyTorch计算梯度数值

1.Autograd

Autograd模块实现了深度学习的算法中自动求导,得到张量的梯度,方便计算参数的权值,简化了手动计算导数的复杂过程。

创建张量时,通过设置 requires_grad 标识为Ture来记录该张量的每一步操作历史并自动计算梯度。

例如:

x= torch.rand(5,5,requires_grad=True)
y= torch.rand(5,5,requires_grad=True)
z = x.add(y) #.add()和.add_()都能把两个张量加起来,但.add_是in-place操作,比如x.add_(y),x+y的结果会存储到原来的x中。Torch里面所有带"_"的操作,都是in-place的。
k = torch.sum(x+y)
print(x)
print(y)
print(z)
print(k)
结果:
tensor([[0.7720, 0.3047, 0.7918, 0.8146, 0.4110],
     [0.6372, 0.2570, 0.1504, 0.1811, 0.5916],
     [0.3155, 0.1520, 0.1405, 0.5431, 0.3443],
     [0.0485, 0.4622, 0.8489, 0.3135, 0.3817],
     [0.5740, 0.1225, 0.5853, 0.3158, 0.4517]], requires_grad=True)
tensor([[0.2287, 0.9599, 0.8266, 0.8068, 0.8266],
     [0.9077, 0.5955, 0.0568, 0.7390, 0.7779],
     [0.1036, 0.2468, 0.2675, 0.4490, 0.8680],
     [0.0307, 0.0600, 0.1917, 0.2553, 0.4924],
     [0.3012, 0.2574, 0.8915, 0.9823, 0.2243]], requires_grad=True)
tensor([[1.0007, 1.2646, 1.6184, 1.6214, 1.2375],
     [1.5448, 0.8525, 0.2071, 0.9201, 1.3695],
     [0.4192, 0.3988, 0.4080, 0.9921, 1.2123],
     [0.0792, 0.5221, 1.0407, 0.5688, 0.8742],
     [0.8752, 0.3799, 1.4768, 1.2981, 0.6760]], grad_fn=<AddBackward0>)
tensor(22.8580, grad_fn=<SumBackward0>)  # grad_fngrad_fn被赋予了一个新的函数,这个函数引用了一个创建了这个Tensor类的Function对象。

对以上结果求梯度:

为了避免 Tensor 对 Tensor 求导,pytorch中:

假设 x 经过一番计算得到 y,torch.autograd.backward(y, w), 或者说 y.backward(w) 的含义是:先计算 l = torch.sum(y * w),然后求 l 对(能够影响到 y 的)所有变量 x 的导数。这里,y 和 w 是同型 Tensor,w 可以视为 y 的各分量的权重,也可以视为遥远的损失函数 l 对 y 的偏导数(这正是函数说明文档的含义)。也就是说,可以理解成先按照 w 对 y 的各个分量加权,加权求和之后得到真正的 loss,再计算这个 loss 对于所有相关变量的导数。 参考

torch.autograd.backward(z,x) #如果使用z.backward()的话,那么就会报如下的错:RuntimeError: grad can be implicitly created only for scalar outputs.这是因为pytorch禁止tensor对tensor求导,而是只允许标量 Scalar 对张量 Tensor 求导,求导结果是和自变量同型的 Tensor。
print(x.grad)
k.backward() #执行z.backward()方法会更新x.grad和y.grad
print(x.grad,"\n",y.grad)
tensor([[5.3160, 3.9142, 5.3753, 5.4437, 4.2329],
     [4.9115, 3.7709, 3.4511, 3.5434, 4.7747],
     [3.9466, 3.4560, 3.4216, 4.6292, 4.0329],
     [3.1454, 4.3865, 5.5468, 3.9406, 4.1452],
     [4.7220, 3.3675, 4.7559, 3.9473, 4.3552]])
tensor([[6.3160, 4.9142, 6.3753, 6.4437, 5.2329],
     [5.9115, 4.7709, 4.4511, 4.5434, 5.7747],
     [4.9466, 4.4560, 4.4216, 5.6292, 5.0329],
     [4.1454, 5.3865, 6.5468, 4.9406, 5.1452],
     [5.7220, 4.3675, 5.7559, 4.9473, 5.3552]]) 
tensor([[6.3160, 4.9142, 6.3753, 6.4437, 5.2329],
     [5.9115, 4.7709, 4.4511, 4.5434, 5.7747],
     [4.9466, 4.4560, 4.4216, 5.6292, 5.0329],
     [4.1454, 5.3865, 6.5468, 4.9406, 5.1452],
     [5.7220, 4.3675, 5.7559, 4.9473, 5.3552]])

求导的流程:

  1. 当我们执行z.backward()的时候。这个操作将调用z里面的grad_fn这个属性,执行求导的操作。
  2. 这个操作将遍历grad_fn的next_functions,然后分别取出里面的Function(AccumulateGrad),执行求导操作。这部分是一个递归的过程直到最后类型为叶子节点。
  3. 计算出结果以后,将结果保存到他们对应的variable 这个变量所引用的对象(x和y)的 grad这个属性里面。
  4. 求导结束。所有的叶节点的grad变量都得到了相应的更新

2. 扩展Autograd

自定义autograd扩展新的功能,就需要扩展Function类。因为Function使用autograd来计算结果和梯度,并对操作历史进行编码。 在Function类中最主要的方法就是forward()执行向前传播和backward()执行反向传播。

详细可参考

举个例子:

# 引入Function便于扩展
from torch.autograd.function import Function
class MulConstant(Function): #继承Function
    @staticmethod  #定义为静态方法
    def forward(ctx, tensor, constant):
        # ctx 用来保存信息这里类似self,并且ctx的属性可以在backward中调用
        ctx.constant=constant
        return tensor *constant
    @staticmethod
    def backward(ctx, grad_output):
        # 返回的参数要与输入的参数一样.
        # 第一个输入为3x3的张量,第二个为一个常数
        # 常数的梯度必须是 None.
        return grad_output, None 
    
a=torch.rand(3,3,requires_grad=True)
b=MulConstant.apply(a,5) # b为a的元素乘以5
print("a:"+str(a))
print("b:"+str(b)) 

b.backward(torch.ones_like(a))
a.grad

结果:

a:tensor([[0.5850, 0.1941, 0.9270],
        [0.7013, 0.6022, 0.4268],
        [0.0101, 0.8201, 0.4765]], requires_grad=True)
b:tensor([[2.9248, 0.9706, 4.6352],
        [3.5064, 3.0112, 2.1338],
        [0.0505, 4.1007, 2.3823]], grad_fn=)
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])

未完待续!

欢迎关注个人公众号【智能建造小硕】(分享计算机编程、人工智能、智能建造、日常学习和科研经验等,欢迎大家关注交流。)

你可能感兴趣的:(Pytorch学习笔记,pytorch,学习,python)