作者:曾芃壹


文章目录

  • Tensor
  • 基本创建方法
  • Tensor快速创建方法
  • 常用数学操作
  • 线性代数运算
  • 连接和切片
  • 变形
  • CUDA加速
  • 自动微分
  • 基本原理
  • 向前传播
  • 反向传播
  • 非标量输出


Tensor

Tensor,中文为张量,是pytorch中最基本的数据类型

#导入torch包
import torch

基本创建方法

#torch.Tensor()传入参数构造矩阵
x=torch.Tensor(2,4)
print(x)
print(x.type())
print(x.dtype)
tensor([[0., 0., 0., 0.],
        [0., 0., 0., 0.]])
torch.FloatTensor
torch.float32

注意这里初始化出的x是有初值的(非常小),只不过jupyter视作了0,Tensor函数默认类型是torch.FloatTensor,其基本数据类型是32位浮点数,Tensor一共包含8种数据类型

我们可以用DoubleTensor()创建一个2*3*4的64位浮点数的Tensor

y=torch.Tensor(2,3,4)
print(y)
tensor([[[9.3674e-39, 1.0929e-38, 1.0469e-38, 1.0561e-38],
         [8.9082e-39, 8.9082e-39, 1.0194e-38, 9.1837e-39],
         [8.4490e-39, 9.6429e-39, 8.4490e-39, 9.6429e-39]],

        [[9.2755e-39, 1.0286e-38, 9.0919e-39, 8.9082e-39],
         [9.2755e-39, 8.4490e-39, 7.3469e-39, 1.0653e-38],
         [1.0194e-38, 8.4490e-39, 1.0194e-38, 9.2755e-39]]])

由两个3*4的矩阵构成


我们可以使用python中原生List来对Tensor进行初始化

list=[[1,2,3],[4,5,6],[7,8,9]]
x=torch.Tensor(list)
print(x)
tensor([[1., 2., 3.],
        [4., 5., 6.],
        [7., 8., 9.]])

可通过python的索引的方式来获取Tensor中的元素值

也可以通过所以修改Tensor中的值

注:Tensor中索引从0开始计算

x[0][2]
x[1,1]=10#将第二行第二列的5修改为10
print(x[1][1])
tensor(10.)

Tensor快速创建方法

#创建元素全0的Tensor
torch.zeros(2,4)
tensor([[0., 0., 0., 0.],
        [0., 0., 0., 0.]])
#创建对角线元素全为1的Tensor
torch.eye(3)
tensor([[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]])
#创建元素全为1的Tensor
torch.ones(2,4)
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.]])
#创建将元素初始化为区间[0,1)的随机数Tensor
torch.rand(2,4)#randn是服从正态分布的随机数
tensor([[0.5842, 0.9712, 0.3474, 0.9844],
        [0.3346, 0.2196, 0.2342, 0.1452]])
#创建指定步长递增的一维Tensor,前两个参数指定区间范围,第三个参数指定步长
torch.arange(1,4)
torch.arange(1,4,0.5)
tensor([1.0000, 1.5000, 2.0000, 2.5000, 3.0000, 3.5000])
#numpy数组转Tensor
import numpy as np
a=np.array([
    [1,2,3],
    [4,5,6]
])
b=torch.from_numpy(a)
print(b)
tensor([[1, 2, 3],
        [4, 5, 6]], dtype=torch.int32)

常用数学操作

大多与numpy类似,add每个元素加一个标量或与另一个Tensor相加、mul、div

fmod和remainder相当于%符号

ceil每个元素向上取整,floor向下取整,clamp每个元素取上下限

round每个元素取最近的整数、frac取每个元素分数部分、reciprocal取倒数

sigmoid取sigmoid函数值,pow取幂,exp取指、sign每个元素的正负值

dist返回两个Tensor的范数

mean求平均值,norm范数值,prod所有元素之积,sum元素之和,max和min元素最大最小值

数学运算有两种操作方式如下:

a=torch.Tensor([[1,2,3],[4,5,6]])
b=torch.ones(2,3)
#法一:torch库调用函数方式
torch.add(a,b)
tensor([[2., 3., 4.],
        [5., 6., 7.]])
#法二:Tensor实例中的方法
b.add(a)
print("a+b的两种方式:\n",a+b)
print(b)
#此外若方法中带有下划线则返回值将覆盖对象
b.add_(a)
print(b)
#也可以将Tensor对象直接与标量相加
print("a+2:",a+2)
a+b的两种方式:
 tensor([[ 8., 15., 22.],
        [29., 36., 43.]])
tensor([[ 7., 13., 19.],
        [25., 31., 37.]])
tensor([[ 8., 15., 22.],
        [29., 36., 43.]])
a+2: tensor([[3., 4., 5.],
        [6., 7., 8.]])

线性代数运算

#点积(内积)
a=torch.Tensor([1,2,3])
b=torch.Tensor([2,3,4])
torch.dot(a,b)
tensor(20.)
#矩阵与向量相乘
a=torch.Tensor([[1,2,3],[2,3,4],[3,4,5]])
b=torch.Tensor([1,2,3])
print(torch.mv(a,b))
tensor([14., 20., 26.])
#矩阵相乘
b=torch.Tensor([[2,3,4],[3,4,5],[4,5,6]])
print(torch.mm(a,b))
tensor([[20., 26., 32.],
        [29., 38., 47.],
        [38., 50., 62.]])

其他线代操作:addr两个向量进行张量积(外积)的结果与另一个矩阵相加,bmm两个恶batch内的矩阵进行批矩阵乘法,eig计算方阵的特征值和特征向量,ger两个向量的张量积,inverse方阵求逆,baddbmm将两个batch内批矩阵乘法再与另一个batch内矩阵相加

连接和切片

#torch.cat()将多个Tensor沿某维度进行连接
a=torch.rand(2,2)
b=torch.rand(2,2)
#传入两个参数第一个参数是由需要进行连接的所有Tensor组成的元组
#第二个是连接的维度(维度从0开始,即按第一个维度连接则要传入0)
print(torch.cat((a,b),0))#第一个维度是行
print(torch.cat((a,b),1))#第二个维度是列
tensor([[0.0315, 0.1849],
        [0.7352, 0.0664],
        [0.6340, 0.6524],
        [0.1324, 0.6634]])
tensor([[0.0315, 0.1849, 0.6340, 0.6524],
        [0.7352, 0.0664, 0.1324, 0.6634]])
#chunk函数用来切片
#第一个参数是要切片的对象,第二个参数是切分的块数,第三个参数为切分的维度
c=torch.rand(2,4)
print(c)
print(torch.chunk(c,2,1))#按第2个维度切成两块
print(torch.chunk(c,2,0))#按第1个维度切成两块,猜测:若该维度无法整除则向下取整
tensor([[0.2835, 0.0232, 0.3525, 0.3784],
        [0.4178, 0.1260, 0.1649, 0.7116]])
(tensor([[0.2835, 0.0232],
        [0.4178, 0.1260]]), tensor([[0.3525, 0.3784],
        [0.1649, 0.7116]]))
(tensor([[0.2835, 0.0232, 0.3525, 0.3784]]), tensor([[0.4178, 0.1260, 0.1649, 0.7116]]))
#矩阵转置
a=torch.rand(2,2)
print(a)
#打印转置
torch.t(a)
tensor([[0.1474, 0.6817],
        [0.2687, 0.0331]])





tensor([[0.1474, 0.2687],
        [0.6817, 0.0331]])

其他:index_select沿某维度切片,取index中指定的元素
unbind沿某维度切分,并返回各切片,split沿某维度切成相等形状的块,nonzero返回非0索引的Tensor,squeeze将Tensor形状中的1去除并返回,unsqueeze将Tensor的形状中指定维度添加维度1,stack沿某维度对输入的Tensor序列进行连接,transpose对Tensor指定的两个维度进行转置

变形

#view可以改变Tensor形状
x=torch.rand(2,3,4)
#将x转换为2*12的Tensor
y=x.view(2,12)
y.size()
torch.Size([2, 12])
#当参数为-1时代表维度数目自动计算
#例如指定第二维为1时自动计算第一维
y=x.view(-1,1)
y.size()
torch.Size([24, 1])

CUDA加速

查看计算机是否支持CUDA加速

torch.cuda.is_available()
True

返回值是true说明可以进行GPU加速

返回值为False的几种原因:1、计算机没有装支持CUDA的NVIDIA显卡2、显卡驱动、CUDA或cudDNN等软件没有成功安装

对比CPU和GPU计算速度的差距:

import torch
from time import perf_counter

X = torch.rand(10000, 100000)
Y = torch.rand(100000, 1000)
# cpu计算
start = perf_counter()
X.mm(Y)
finish = perf_counter()
time = finish - start
print("Cpu cal time is:%s" % time)
# gpu计算
X = X.cuda()
Y = Y.cuda()
start = perf_counter()
X.mm(Y)
finish = perf_counter()
time_cuda = finish - start
print("Gpu加速计算时间:%s" % time_cuda)
print("Cpu计算时间是Gpu计算时间的%s倍" % str(time / time_cuda))
Cpu cal time is:7.457452499991632
Gpu加速计算时间:1.1469121999980416
Cpu计算时间是Gpu计算时间的6.502199994039968倍

实验说明在1660ti下使用GPU计算两个矩阵相乘是i7 CPU的6到7倍

自动微分

基本原理

将微分计算过程抽象为图像

pytorch tensor变成str pytorch tensor tensor_pytorch tensor变成str

Tensor自动微分的3个重要属性:requires_grad、grad和grad_fn

  • requires_grad为True时表示该Tensor需要自动微分默认为False
  • grad存储Tensor的微分值
  • grad_fn用于存储Tensor的微分函数
  • 当叶子节点的requires_grad属性设置为True时,信息流经该结点时所有 中间结点的requires_grad属性都会变成True,只要输出节点调用反向传播函数backward(),pytorch就会自动求出叶子节点的微分值并更新存储在叶子结点的grad属性,只有叶子节点的grad属性才能被更新。

向前传播

Autograd技术可以让我们从叶子节点开始追踪信息流,记录下整个过程所使用的函数直到输出节点,这个过程称为向前传播


示例:初始化叶子节点pytorch tensor变成str pytorch tensor tensor_pytorch tensor变成str_02

x=torch.ones(2)
print(x.requires_grad)#默认为False
False
x.requires_grad=True
x
tensor([1., 1.], requires_grad=True)
#此时属性grad和grad_fn是空值
print(x.grad)
print(x.grad_fn)
None
None
#让向量x乘4
z=4*x
print(z)
tensor([4., 4.], grad_fn=<MulBackward0>)

这里z的grad_fn=MulBackward,是乘法的微分函数。

最后求z的二范数

y=z.norm()
y
tensor(5.6569, grad_fn=<CopyBackwards>)

反向传播

调用输出节点的backward函数对整个图进行反向传播求出微分值

y.backward()
x.grad
tensor([2.8284, 2.8284])

发现叶子节点x中的grad属性已经变成了向量x的微分值

print(z.grad)
print(y.grad)
None
None


<ipython-input-113-e0cceee784f8>:1: UserWarning: The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its .grad attribute won't be populated during autograd.backward(). If you indeed want the gradient for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor instead. See github.com/pytorch/pytorch/pull/30531 for more informations.
  print(z.grad)
<ipython-input-113-e0cceee784f8>:2: UserWarning: The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its .grad attribute won't be populated during autograd.backward(). If you indeed want the gradient for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor instead. See github.com/pytorch/pytorch/pull/30531 for more informations.
  print(y.grad)

而中间节点z和输出节点y的grad属性并没有变化

非标量输出

当输出节点为非标量时,使用backward函数需要增加一个参数gradient。gradient的形状与输出节点的形状保持一致且元素值均为1

示例:

#矩阵X与向量z相乘得到2*1的y
z=torch.ones(2,1)
X=torch.Tensor([[2,3],
                [1,2]])
X.requires_grad=True
y=X.mm(z)
print(y)
tensor([[5.],
        [3.]], grad_fn=<MmBackward>)
y.backward(torch.ones(2,1))
X.grad
tensor([[1., 1.],
        [1., 1.]])

验证微分是否正确


pytorch tensor变成str pytorch tensor tensor_深度学习_03


pytorch tensor变成str pytorch tensor tensor_pytorch_04关于pytorch tensor变成str pytorch tensor tensor_python_05的微分是pytorch tensor变成str pytorch tensor tensor_深度学习_06微分结果正确