作者:曾芃壹
文章目录
- 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倍
自动微分
基本原理
将微分计算过程抽象为图像
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技术可以让我们从叶子节点开始追踪信息流,记录下整个过程所使用的函数直到输出节点,这个过程称为向前传播
示例:初始化叶子节点
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.]])
验证微分是否正确
即关于的微分是微分结果正确