2.Tensor

PyTorch基础:Tensor和Autograd

3.1 Tensor

Tensor,又名张量,读者可能对这个名词似曾相识,因它不仅在PyTorch中出现过,它也是Theano、TensorFlow、Torch和MxNet中重要的数据结构。关于张量的本质不乏深度的剖析,但从工程角度来讲,可简单地认为它就是一个数组,且支持高效的科学计算。它可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)和更高维的数组(高阶数据)。Tensor和Numpy的ndarrays类似,但PyTorch的tensor支持GPU加速。

本节将系统讲解tensor的使用,力求面面俱到,但不会涉及每个函数。对于更多函数及其用法,读者可通过在IPython/Notebook中使用函数名加?查看帮助文档,或查阅PyTorch官方文档

Let’s begin

from __future__ import print_function
import torch  as t
t.__version__

3.1.1 基础操作

学习过Numpy的读者会对本节内容感到非常熟悉,因tensor的接口有意设计成与Numpy类似,以方便用户使用。但不熟悉Numpy也没关系,本节内容并不要求先掌握Numpy。

从接口的角度来讲,对tensor的操作可分为两类:

  1. torch.function,如torch.save等。
  2. 另一类是tensor.function,如tensor.view等。
    为方便使用,对tensor的大部分操作同时支持这两类接口,在本书中不做具体区分,如torch.sum (torch.sum(a, b))tensor.sum (a.sum(b))功能等价。

而从存储的角度来讲,对tensor的操作又可分为两类:

  1. 不会修改自身的数据,如 a.add(b), 加法的结果会返回一个新的tensor。
  2. 会修改自身的数据,如 a.add_(b), 加法的结果仍存储在a中,a被修改了。

函数名以_结尾的都是inplace方式, 即会修改调用者自己的数据,在实际应用中需加以区分。

创建Tensor

在PyTorch中新建tensor的方法有很多,具体如表3-1所示。

表3-1: 常见新建tensor的方法

函数 功能
Tensor(*sizes) 基础构造函数
tensor(data,) 类似np.array的构造函数
ones(*sizes) 全1Tensor
zeros(*sizes) 全0Tensor
eye(*sizes) 对角线为1,其他为0
arange(s,e,step) 从s到e,步长为step
linspace(s,e,steps) 从s到e,均匀切分成steps份
rand/randn(*sizes) 均匀(0-1)/标准分布(均值为0,方差为1)
normal(mean,std)/uniform(from,to) 正态分布/均匀分布
randperm(m) 随机排列

这些创建方法都可以在创建的时候指定数据类型dtype和存放device(cpu/gpu).

其中使用Tensor函数新建tensor是最复杂多变的方式,它既可以接收一个list,并根据list的数据新建tensor,也能根据指定的形状新建tensor,还能传入其他的tensor,下面举几个例子:

指定tensor的形状

a = t.Tensor(2, 3)
a # 数值取决于内存空间的状态,print时候可能overflow

用list的数据创建tensor

b = t.Tensor([[1,2,3],[4,5,6]])
b

b.tolist() # 把tensor转为list

tensor.size()返回torch.Size对象,它是tuple的子类,但其使用方式与tuple略有区别

b_size = b.size()
b_size

b.numel() # b中元素总个数,2*3,等价于b.nelement()

创建一个和b形状一样的tensor

c = t.Tensor(b_size)

创建一个元素为2和3的tensor

d = t.Tensor((2, 3))
c, d

除了tensor.size(),还可以利用tensor.shape直接查看tensor的形状,tensor.shape等价于tensor.size()

c.shape

需要注意的是,t.Tensor(*sizes)创建tensor时,系统不会马上分配空间,只是会计算剩余的内存是否足够使用,使用到tensor时才会分配,而其它操作都是在创建完tensor之后马上进行空间分配。其它常用的创建tensor的方法举例如下。

t.ones(2, 3)

t.zeros(2, 3)

t.arange(1, 6, 2)

t.linspace(1, 10, 3)

t.randn(2, 3, device=t.device('cpu'))

t.randperm(5) # 长度为5的随机排列       p.s.(1,2,3,4,5)的随机排列

t.eye(2, 3, dtype=t.int) # 对角线为1, 不要求行列数一致

torch.tensor是在0.4版本新增加的一个新版本的创建tensor方法,使用的方法,和参数几乎和np.array完全一致

scalar = t.tensor(3.14159) 
print('scalar: %s, shape of sclar: %s' %(scalar, scalar.shape))

vector = t.tensor([1, 2])
print('vector: %s, shape of vector: %s' %(vector, vector.shape))

tensor = t.Tensor(1,2) # 注意和t.tensor([1, 2])的区别     p.s. tensor(2,3)是错误的写法
tensor.shape

matrix = t.tensor([[0.1, 1.2], [2.2, 3.1], [4.9, 5.2]])
matrix,matrix.shape

t.tensor([[0.11111, 0.222222, 0.3333333]],
                     dtype=t.float64,
                     device=t.device('cpu'))

empty_tensor = t.tensor([])  #空
empty_tensor.shape
常用Tensor操作

通过tensor.view方法可以调整tensor的形状,但必须保证调整前后元素总数一致(tensor.resize()可以前后元素整数不一致)。view不会修改自身的数据,返回的新tensor与源tensor共享内存,也即更改其中的一个,另外一个也会跟着改变。在实际应用中可能经常需要添加或减少某一维度,这时候squeezeunsqueeze两个函数就派上用场了。

a = t.arange(0, 6)
a.view(2, 3)

b = a.view(-1, 3) # 当某一维为-1的时候,会自动计算它的大小
b.shape

b.unsqueeze(1) # 注意形状,在第1维(下标从0开始)上增加“1” 
#等价于 b[:,None]
b[:, None].shape

b.unsqueeze(-2) # -2表示倒数第二个维度

c = b.view(1, 1, 1, 2, 3)
c.squeeze(0) # 压缩第0维的“1”

c.squeeze() # 把所有维度为“1”的压缩
a[1] = 100 #如果输入值只有一个的话,就把张量拉平,再按照索引查找
b # a修改,b作为view之后的,也会跟着修改

resize是另一种可用来调整size的方法,但与view不同,它可以修改tensor的大小。如果新大小超过了原大小,会自动分配新的内存空间,而如果新大小小于原大小,则之前的数据依旧会被保存,看一个例子。

b.resize_(1, 3)
b

b.resize_(3, 3) # 旧的数据依旧保存着,多出的大小会分配新空间
b
索引操作

Tensor支持与numpy.ndarray类似的索引操作,语法上也类似,下面通过一些例子,讲解常用的索引操作。如无特殊说明,索引出来的结果与原tensor共享内存,也即修改一个,另一个会跟着修改。

a = t.randn(3, 4)
a

a[0] # 第0行(下标从0开始)

a[:, 0] # 第0列

a[0][2] # 第0行第2个元素,等价于a[0, 2]

a[0, -1] # 第0行最后一个元素

a[:2] # 前两行

a[:2, 0:2] # 前两行,第0,1列

print(a[0:1, :2]) # 第0行,前两列 
print(a[0, :2]) # 注意两者的区别:形状不同

None类似于np.newaxis, 为a新增了一个轴
等价于a.view(1, a.shape[0], a.shape[1])

a[None].shape

a[None].shape # 等价于a[None,:,:]

a[:,None,:].shape

a[:,None,:,None,None].shape

a > 1 # 返回一个ByteTensor

a[a>1] # 等价于a.masked_select(a>1)

选择结果与原tensor不共享内存空间

a[t.LongTensor([0,1])] # 第0行和第1行

其它常用的选择函数如表3-2所示。

函数 功能
index_select(input, dim, index) 在指定维度dim上选取,比如选取某些行、某些列
masked_select(input, mask) 例子如上,a[a>0],使用ByteTensor进行选取
non_zero(input) 非0元素的下标
gather(input, dim, index) 根据index,在dim维度上选取数据,输出的size与index一样

gather是一个比较复杂的操作,对一个2维tensor,输出的每个元素如下:

# ```python
# out[i][j] = input[index[i][j]][j]  # dim=
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值