创建、操作Tensor(一)

Tensor,又名张量,从工程角度来讲,可简单地认为它就是一个数组,且支持高效的科学计算。它可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)和更高维的数组(高阶数据)。Tensor和Numpy的ndarrays类似,但PyTorch的tensor支持GPU加速。

创建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)均匀/标准分布
normal(mean,std)/uniform(from,to)正态分布/均匀分布
randperm(m)随机排列

创建的时候可以指定数据类型dtype和存放device(cpu/gpu),特别注意的是t.Tensor(*sizes)创建tensor时,系统不会马上分配空间,只是会计算剩余的内存是否足够使用,使用到tensor时才会分配,而其它操作都是在创建完tensor之后马上进行空间分配。

示例代码:

from __future__ import print_function
import torch as t

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

b = t.Tensor([[1,2,3],[4,5,6]])# 用list的数据创建tensor
print(b)
print(b.tolist())# 把tensor转为list
print(b.numel()) # b中元素总个数,2*3,等价于b.nelement()
print(b.nelement())
# 创建一个和b形状一样的tensor
c = t.Tensor(b.size())
# 创建一个元素为2和3的tensor
d = t.Tensor((2, 3))
#查看tensor的形状,tensor.shape等价于tensor.size()
print(c.shape)
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的随机排列
t.eye(2, 3, dtype=t.int) # 对角线为1, 不要求行列数一致
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))
matrix = t.tensor([[0.1, 1.2], [2.2, 3.1], [4.9, 5.2]])

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

empty_tensor = t.tensor([])

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

示例代码:

from __future__ import print_function
import torch as t

a = t.arange(0, 6)
print(a.view(2, 3))
b = a.view(-1, 3) # 当某一维为-1的时候,会自动计算它的大小
b.unsqueeze(1) # 注意形状,在第1维(下标从0开始)上增加“1”
#等价于 b[:,None]
print(b[:,None])
print(b.unsqueeze(1).shape)
b.unsqueeze(-2) # -2表示倒数第二个维度
c = b.view(1, 1, 1, 2, 3)
c.squeeze(0) # 压缩第0维的“1”
c.squeeze() # 把所有维度为“1”的压缩

a[1] = 100# a修改,b作为view之后的,也会跟着修改

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

示例代码:

print(b)
b.resize_(1, 3)#新大小小于原大小,
print(b)
b.resize_(3, 3) # 旧的数据依旧保存着,多出的大小会分配新空间
print(b)

结果:

tensor([[  0, 100,   2],
        [  3,   4,   5]])
tensor([[  0, 100,   2]])
tensor([[                0,               100,                 2],
        [                3,                 4,                 5],
        [32088598324117601, 31244151918821473, 34058953221341293]])

索引操作

示例代码:

from __future__ import print_function
import torch as t


a = t.randn(3, 4)
"""
tensor([[ 1.1741,  1.4335, -0.8156,  0.7622],
        [-0.6334, -1.4628, -0.7428,  0.0410],
        [-0.6551,  1.0258,  2.0572,  0.3923]])
"""
a[0] # 第0行(下标从0开始) tensor([ 1.1741,  1.4335, -0.8156,  0.7622])
a[:, 0] # 第0列 tensor([ 1.1741, -0.6334, -0.6551])
a[0][2] # 第0行第2个元素,等价于a[0, 2] tensor(-0.8156)
a[0, -1] # 第0行最后一个元素 tensor(0.7622)
a[:2] # 前两行   tensor([[ 1.1741,  1.4335, -0.8156,  0.7622],
              #          [-0.6334, -1.4628, -0.7428,  0.0410]])
a[:2, 0:2] # 前两行,第0,1列  tensor([[ 1.1741,  1.4335],
            #                      [-0.6334, -1.4628]])

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

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

a > 1 # 返回一个ByteTensor
"""
tensor([[1, 1, 0, 0],
        [0, 0, 0, 0],
        [0, 1, 1, 0]], dtype=torch.uint8)
"""

a[a>1] # 等价于a.masked_select(a>1)选择结果与原tensor不共享内存空间
# tensor([1.1741, 1.4335, 1.0258, 2.0572])

a[t.LongTensor([0,1])] # 第0行和第1行
"""
tensor([[ 1.1741,  1.4335, -0.8156,  0.7622],
        [-0.6334, -1.4628, -0.7428,  0.0410]])
"""

其它常用的选择函数下表所示:

函数功能
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,输出的每个元素如下:

from __future__ import print_function
import torch as t


a = t.arange(0, 16).view(4, 4)
print(a)
"""
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15]])
"""
# 选取对角线的元素
index = t.LongTensor([[0,1,2,3]]) # tensor([[0, 1, 2, 3]])
a.gather(0, index) #tensor([[ 0,  5, 10, 15]])

# 选取反对角线上的元素,注意与上面的不同
index = t.LongTensor([[3,2,1,0]])
a.gather(0, index) #tensor([[12,  9,  6,  3]])

# 选取两个对角线上的元素
index = t.LongTensor([[0,1,2,3],[3,2,1,0]]).t()
b = a.gather(1, index)
"""
tensor([[ 0,  3],
        [ 5,  6],
        [10,  9],
        [15, 12]])
"""

与gather相对应的逆操作是scatter_,gather把数据从input中按index取出,而scatter_是把取出的数据再放回去。注意scatter_函数是inplace操作。

# 把两个对角线元素放回去到指定位置
c = t.zeros(4,4)
c.scatter_(1, index, b.float())
"""
tensor([[ 0.,  0.,  0.,  3.],
        [ 0.,  5.,  6.,  0.],
        [ 0.,  9., 10.,  0.],
        [12.,  0.,  0., 15.]])
"""

对tensor的任何索引操作仍是一个tensor,想要获取标准的python对象数值,需要调用tensor.item(), 这个方法只对包含一个元素的tensor适用。

a[0,0] #依旧是tensor       tensor(0)
a[0,0].item() # python float   0

d = a[0:1, 0:1, None]
print(d.shape)  #torch.Size([1, 1, 1])
print(d)    #tensor([[[0]]])
d.item() # 只包含一个元素的tensor即可调用tensor.item,与形状无关   0

高级索引
高级索引可以看成是普通索引操作的扩展,但是高级索引操作的结果一般不和原始的Tensor共享内存。

x = t.arange(0,27).view(3,3,3)
"""
tensor([[[ 0,  1,  2],
         [ 3,  4,  5],
         [ 6,  7,  8]],

        [[ 9, 10, 11],
         [12, 13, 14],
         [15, 16, 17]],

        [[18, 19, 20],
         [21, 22, 23],
         [24, 25, 26]]])
"""
x[[1, 2], [1, 2], [2, 0]]# x[1,1,2]和x[2,2,0])  tensor([14, 24])
x[[2, 1, 0], [0], [1]] # x[2,0,1],x[1,0,1],x[0,0,1]   tensor([19, 10,  1])
x[[0, 2], ...] # x[0] 和 x[2]
"""
tensor([[[ 0,  1,  2],
         [ 3,  4,  5],
         [ 6,  7,  8]],

        [[18, 19, 20],
         [21, 22, 23],
         [24, 25, 26]]])
"""

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值