这个名词还是听陌生的,第一次听,不知道是一个怎么样的东西,脑子能关联到的就是向量了。在机器学习里,接触的最多的是向量和矩阵,而它们其实都可以称为张量。
向量,是一维的;矩阵,是二维的…张量似乎就是各种维度的矩阵的一个统称,但我们通常需要处理的数据有零维的(单纯的一个数字)、一维的(数组)、二维的(矩阵)、三维的(空间矩阵)、还有好多好多维的。Pytorch为了把这些各种维统一起来,所以就起名为Tensor——张量。
如果之前有学习过NumPy,那现在的Tensor和它不能说是很相似,只能说是差不多一模一样。但是Tensor提供GPU计算和自动求梯度等更多的功能,所以Tensor 这一数据类型更加适合深度学习。
详细的知识点:torch.Tensor · Pytorch 中文文档 (apachecn.org)
这里直接copy学习文章里的表格了,我觉得整理的挺好的嘿嘿…
函数 | 功能 |
---|---|
Tensor(*sizes) | 基础构造函数 |
tensor(data) | 类似于np.array |
ones(*sizes) | 全1 |
zeros(*sizes) | 全0 |
eye(*sizes) | 对角为1,其余为0 |
arange(s,e,step) | 从s到e,步长为step |
linspace(s,e,steps) | 从s到e,均匀分成step份 |
rand/randn(*sizes) | 均匀/标准分布 |
normal(mean,std)/uniform(from,to) | 正态分布/均匀分布 |
randperm(m) | 随机排列 |
我们先构建一个tensor
#导入库
import torch
x = torch.rand(4, 3)
print(x)
#运行结果
tensor([[0.0547, 0.0828, 0.5455],
[0.4604, 0.3901, 0.6721],
[0.5270, 0.2673, 0.1895],
[0.3011, 0.2195, 0.7536]])
size()
print(x.size())
#运行结果
torch.Size([4, 3])
shape()
print(x.shape)
#运行结果
torch.Size([4, 3])
tolist()
l = x.tolist()
print(type(x))
print(type(l))
print(x)
print(l)
#运行结果
<class 'torch.Tensor'>
<class 'list'>
tensor([[0.0547, 0.0828, 0.5455],
[0.4604, 0.3901, 0.6721],
[0.5270, 0.2673, 0.1895],
[0.3011, 0.2195, 0.7536]])
[[0.05465167760848999, 0.08277982473373413, 0.5455439686775208], [0.4604085683822632, 0.39012467861175537, 0.6720585227012634], [0.5270071625709534, 0.26727789640426636, 0.1895279884338379], [0.3011465072631836, 0.2194947600364685, 0.7536421418190002]]
tolist()
并不是直接将x从tensor转化为list,而是返回了一个list,通过上面的代码可以看到变量l
经过赋值后是list类型数据,但是依旧是tensor类型。
numel()
x.numel()
#运行结果
12
view()
print(x.view())
print(x)
#运行结果
tensor([[0.0547, 0.0828, 0.5455, 0.4604],
[0.3901, 0.6721, 0.5270, 0.2673],
[0.1895, 0.3011, 0.2195, 0.7536]])
tensor([[0.0547, 0.0828, 0.5455],
[0.4604, 0.3901, 0.6721],
[0.5270, 0.2673, 0.1895],
[0.3011, 0.2195, 0.7536]])
view()
仅仅是对它展示的形状做出改变,按照参数设定的形状进行呈现,tensor本身是不会发生改变的。
resize()
print(x.resize())
print(x)
#运行结果
tensor([[0.2210, 0.1887, 0.5769, 0.3312],
[0.0433, 0.8799, 0.3003, 0.4035],
[0.3473, 0.6184, 0.8061, 0.7837]])
tensor([[0.2210, 0.1887, 0.5769],
[0.3312, 0.0433, 0.8799],
[0.3003, 0.4035, 0.3473],
[0.6184, 0.8061, 0.7837]])
这里可以发现x
本身的形状还是没发生改变,和view()
一样,但是resize()
比view()
多了一个功能就是能对数据本身修改,只需要在后面加多'_'
就相当于平时的inplace = True
,差不多一个意思。
print(x.resize_())
print(x)
#运行结果
tensor([[0.2210, 0.1887, 0.5769, 0.3312],
[0.0433, 0.8799, 0.3003, 0.4035],
[0.3473, 0.6184, 0.8061, 0.7837]])
tensor([[0.2210, 0.1887, 0.5769, 0.3312],
[0.0433, 0.8799, 0.3003, 0.4035],
[0.3473, 0.6184, 0.8061, 0.7837]])
经过resize_()
操作后x
本身也已经被重新设置了形状。
unsqueeze()
扩展维度
作用:返回一个新的张量,对输入的既定位置插入维度1
注意:返回张量与输入张量共享内存,所以改变其中一个的内容会改变另外一个
参数:
#构建一个tensor
x = torch.Tensor([1, 2, 3, 4])
print(x)
print(x.size())
#运行结果
tensor([1., 2., 3., 4.])
torch.Size([4])
现在试图对该tensor进行维度的扩展,目前是一维,扩展成二维可以是[1,4]或者是[4,1]。
#[1,4]
print(torch.unsqueeze(x, 0))
print(torch.unsqueeze(x, 0).size())
#运行结果
tensor([[1., 2., 3., 4.]])
torch.Size([1, 4])
可以发现现在tensor已经变成二维了,所得的结果就是相对于在原数据最外围加多了一层[ ]
,此时size就是[1,4]。单从size来看的话,类似于list的插入操作,在指定位置插入1,所以如果想扩展成[4,1]只需要将索引dim改为1即可。
#[4,1]
print(torch.unsqueeze(x, 1))
print(torch.unsqueeze(x, 1).size())
#运行结果
tensor([[1.],
[2.],
[3.],
[4.]])
torch.Size([4, 1])
但以上操作都并不会对数据本身做出修改,如果要在原数据上动手脚,则需要在后面加多一个'_'
,变成unsqueeze_()
。
squeeze()
压缩维度
之所以去掉张量形状中的维度1,是因为它仅仅只能起到扩充维度的作用,而不会有其他的用途,为了加快计算,是可以去掉这些1的维度。
#构建一个tensor
m = torch.zeros(2, 1, 2, 1, 2)
print(m.size())
#运行结果
torch.Size([2, 1, 2, 1, 2])
目前tensor的形状是[2, 1, 2, 1, 2],接下来对所有1的维度进行压缩。
n = torch.squeeze(m)
print(n.size())
#运行结果
torch.Size([2, 2, 2])
也可以指定索引进行维度压缩。
n = torch.squeeze(m, 1)
print(n.size())
#运行结果
torch.Size([2, 2, 1, 2])
详细知识请参考:torch.unsqueeze() 和 torch.squeeze() - 知乎 (zhihu.com)
Tensor有不同的数据类型,如表3-3所示,每种类型分别对应有CPU和GPU版本(HalfTensor除外)。默认的tensor是FloatTensor,可通过 t.set_default_tensor_type
来修改默认tensor类型(如果默认类型为GPU tensor,则所有操作都将在GPU上进行)。Tensor的类型对分析内存占用很有帮助。例如对于一个size为(1000, 1000, 1000)的FloatTensor,它有 1000*1000*1000=10^9
个元素,每个元素占32bit/8 = 4Byte内存,所以共占大约4GB内存/显存。HalfTensor是专门为GPU版本设计的,同样的元素个数,显存占用只有FloatTensor的一半,所以可以极大缓解GPU显存不足的问题,但由于HalfTensor所能表示的数值大小和精度有限1,所以可能出现溢出等问题.
Data type | dtype | CPU tensor | GPU tensor |
---|---|---|---|
32-bit floating point | torch.float32 or torch.float |
torch.FloatTensor |
torch.cuda.FloatTensor |
64-bit floating point | torch.float64 or torch.double |
torch.DoubleTensor |
torch.cuda.DoubleTensor |
16-bit floating point | torch.float16 or torch.half |
torch.HalfTensor |
torch.cuda.HalfTensor |
8-bit integer (unsigned) | torch.uint8 |
torch.ByteTensor |
torch.cuda.ByteTensor |
8-bit integer (signed) | torch.int8 |
torch.CharTensor |
torch.cuda.CharTensor |
16-bit integer (signed) | torch.int16 or torch.short |
torch.ShortTensor |
torch.cuda.ShortTensor |
32-bit integer (signed) | torch.int32 or torch.int |
torch.IntTensor |
torch.cuda.IntTensor |
64-bit integer (signed) | torch.int64 or torch.long |
torch.LongTensor |
torch.cuda.LongTensor |
各数据类型之间可以互相转换,type(new_type
)是通用的做法,同时还有float
、long、half
等快捷方法。CPU tensor与GPU tensor之间的互相转换通过tensor.cuda
和tensor.cpu
方法实现,此外还可以使用tensor.to(device)
。Tensor还有一个new
方法,用法与t.Tensor
一样,会调用该tensor对应类型的构造函数,生成与当前tensor类型一致的tensor。torch.*_like(tensora)
可以生成和tensora
拥有同样属性(类型,形状,cpu/gpu)的新tensor。 tensor.new_*(new_shape)
新建一个不同形状的tensor。
参考链接:PyTorch学习之路(五)Tensor操作及其常用函数_一听音频技术-CSDN博客_pytorch tensor函数
torch.autograd
是 PyTorch 的自动差分引擎,可为神经网络训练提供支持。autograd包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义 ( define-by-run )的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。
requires_grad
在构建tensor时,设置参数 requires_grad = True
后,该tensor所有的操作都会被记录下来。完成计算之后通过调用backward()
便可以计算所有的梯度,并自动累加到grad
属性中。
#一般创建tensor
x = torch.tensor([1.0,2.0])
print(x)
#运行结果
tensor([1., 2.])
#设置requires_grad=True的tensor创建
x = torch.tensor([1.0,2.0],requires_grad=True)
print(x)
#运行结果
tensor([1., 2.], requires_grad=True)
从输出结果可以看到,tensor里边多了个requires_grad=True
,此时代表着该张量其计算历史会被追踪了。
接下来进行简单的计算看看。
y = (x + 2)**2
print(y)
#运行结果
tensor([ 9., 16.], grad_fn=<PowBackward0>)
可以看到此时计算结果y有grad_fn
属性,用于指导反向传播。
backward()
首先简单了解一下正向传播和反向传播
我在网上看到有人举过一个例子,就像是在3个人在玩你画我猜,告诉A一个东西,让A画出来给B看,B再把自己看到的画出来给C看,最终C来根据B展现的结果来猜测一开始所给出的东西是什么,这个就相对于是正向传播;而反向传播就相对于C得知正确答案之后,再告诉前面的人哪里还需要优化,哪里没有表达清楚需要进行修改…(我找不到那个人原来的比方了,我自己瞎说的纯属是)
反向传播就这样介绍到这里,因为多的我也不知道了。
接下来看看Autograd
是怎么个收集梯度。我们在之前的得到的结果y
上调用.backward()
,Autograd
将计算这些梯度并将其存储在各个张量(比如说我的x)的.grad
属性中。
y.backward()
print(x.grad)
#运行结果
RuntimeError: grad can be implicitly created only for scalar outputs
恭喜我这个b报错了!报错提示我们输出不是一个标量,确实,一维张量x经过刚刚的计算得到的y也还是一个一维的张量,面向百度搜了一下说:如果 Tensor
是一个标量(即它包含一个元素的数据),则不需要为 backward()
指定任何参数,但是如果它有更多的元素,则需要指定一个 gradient
参数,该参数是形状匹配的张量。
backward()
时,如果 y 是标量,则不需要为 backward()
传入任何参数;否则,需要传入一个与 y 同形的Tensor(我还不懂为什么)我传个参还不行吗,这就传
y.backward(torch.tensor([1,1]))
print(x.grad)
#运行结果
tensor([6., 8.])
1.Autograd看这一篇就够了!_逐梦er的博客-CSDN博客_autograd
2.pytorch中backward()函数详解_Camlin_Z的博客-CSDN博客_backward()
3.Pytorch中的backward函数 - 知乎 (zhihu.com)
4.torch.autograd的简要介绍 (apachecn.org)
5.grad can be implicitly created only for scalar outputs_溪c的博客-CSDN博客