pytorch 入门Tensor(一)

时间:2025-04-04 17:42:50

Tensor

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

# Let's begin
from __future__ import print_function
import torch  as t
t.__version__
  • 1
  • 2
  • 3
  • 4
'1.0.1'
  • 1

基础操作

tensor的接口成与Numpy类似

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

  • ,如等。
  • ,如等。

((a, b)) ((b))功能等价。

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

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

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

创建Tensor

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

常见新建tensor的方法

函数 功能
Tensor(*sizes) 基础构造函数
tensor(data,) 类似的构造函数
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).

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

# 指定tensor的形状
a = t.Tensor(2, 3)
a # 数值取决于内存空间的状态,print时候可能overflow
  • 1
  • 2
  • 3
tensor([[5.3285e+01, 4.5908e-41, 1.4013e-45],
        [0.0000e+00, 1.4013e-45, 0.0000e+00]])
  • 1
  • 2
# 用list的数据创建tensor
b = t.Tensor([[1,2,3],[4,5,6]])
b
  • 1
  • 2
  • 3
tensor([[1., 2., 3.],
        [4., 5., 6.]])
  • 1
  • 2
b.tolist() # 把tensor转为list
  • 1
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
  • 1

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

b_size = b.size()
b_size
  • 1
  • 2
([2, 3])
  • 1
b.numel() # b中元素总个数,2*3,等价于()
  • 1
6
  • 1
# 创建一个和b形状一样的tensor
c = t.Tensor(b_size)
# 创建一个元素为2和3的tensor
d = t.Tensor((2, 3))
c, d
  • 1
  • 2
  • 3
  • 4
  • 5
(tensor([[5.3285e+01, 4.5908e-41, 0.0000e+00],
         [0.0000e+00, 0.0000e+00, 0.0000e+00]]), tensor([2., 3.]))
  • 1
  • 2

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

c.shape
  • 1
([2, 3])
  • 1

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

t.ones(2, 3)
  • 1
tensor([[1., 1., 1.],
        [1., 1., 1.]])
  • 1
  • 2
t.zeros(2, 3)
  • 1
tensor([[0., 0., 0.],
        [0., 0., 0.]])
  • 1
  • 2
t.arange(1, 6, 2)
  • 1
tensor([1, 3, 5])
  • 1
# 不能以1为步长
t.linspace(1, 10, 3)
  • 1
  • 2
tensor([ 1.0000,  5.5000, 10.0000])
  • 1
t.randn(2, 3)
  • 1
tensor([[-1.4943, -0.7678, -1.3722],
        [-0.9745, -1.3788, -0.0625]])
  • 1
  • 2
t.randperm(5) # 长度为5的随机排列
  • 1
tensor([3, 2, 0, 1, 4])
  • 1
t.eye(2, 3, dtype=t.int) # 对角线为1, 不要求行列数一致
  • 1
tensor([[1, 0, 0],
        [0, 1, 0]], dtype=torch.int32)
  • 1
  • 2
常用Tensor操作

通过方法可以调整tensor的形状,必须保证调整前后元素总数一致。应用中可能经常需要添加或减少某一维度,这时候squeezeunsqueeze两个函数就派上用场了。

# 就是numpy中的reshape
a = t.Tensor([[1,2,3],[4,5,6]])
a.view(2,3)

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

        [[4., 5., 6.]]])
  • 1
  • 2
  • 3
c = b.view(1, 1, 1, 2, 3)
c.squeeze(0) # 压缩第0维的“1”
  • 1
  • 2
tensor([[[[1., 2., 3.],
          [4., 5., 6.]]]])
  • 1
  • 2
c.squeeze() # 把所有维度为“1”的压缩
  • 1
tensor([[1., 2., 3.],
        [4., 5., 6.]])
  • 1
  • 2
a[1] = 100
b # a修改,b作为view之后的,也会跟着修改
  • 1
  • 2
tensor([[  1.,   2.,   3.],
        [100., 100., 100.]])
  • 1
  • 2

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

b.resize_(1, 3)
b
  • 1
  • 2
tensor([[1., 2., 3.]])
  • 1
b.resize_(3, 3) # 旧的数据依旧保存着,多出的大小会分配新空间
b
  • 1
  • 2
tensor([[1.0000e+00, 2.0000e+00, 3.0000e+00],
        [1.0000e+02, 1.0000e+02, 1.0000e+02],
        [1.6992e-07, 0.0000e+00, 0.0000e+00]])
  • 1
  • 2
  • 3
索引操作

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

a = t.randn(3, 4)
a
  • 1
  • 2
tensor([[-0.0766, -0.4981, -0.1263, -0.2356],
        [ 0.1508,  1.0202,  0.4058, -0.9278],
        [ 0.0286, -0.6448, -0.3914,  0.8246]])
  • 1
  • 2
  • 3
a[0] # 第0行(下标从0开始)
  • 1
tensor([-0.0766, -0.4981, -0.1263, -0.2356])
  • 1
a[:, 0] # 第0列
  • 1
tensor([-0.0766,  0.1508,  0.0286])
  • 1
a[0][2] # 第0行第2个元素,等价于a[0, 2]
  • 1
tensor(-0.1263)
  • 1
a[0, -1] # 第0行最后一个元素
  • 1
tensor(-0.2356)
  • 1
a[:2] # 前两行
  • 1
tensor([[-0.0766, -0.4981, -0.1263, -0.2356],
        [ 0.1508,  1.0202,  0.4058, -0.9278]])
  • 1
  • 2
a[:2, 0:2] # 前两行,第0,1列
  • 1
tensor([[-0.0766, -0.4981],
        [ 0.1508,  1.0202]])
  • 1
  • 2
print(a[0:1, :2]) # 第0行,前两列 
print(a[0, :2]) # 注意两者的区别:形状不同
  • 1
  • 2
tensor([[-0.0766, -0.4981]])
tensor([-0.0766, -0.4981])
  • 1
  • 2
# None类似于, 为a新增了一个轴
# 等价于(1, [0], [1])
a[None].shape
  • 1
  • 2
  • 3
([1, 3, 4])
  • 1
a[None].shape # 等价于a[None,:,:]
  • 1
([1, 3, 4])
  • 1
a[:,None,:].shape
  • 1
([3, 1, 4])
  • 1
a[:,None,:,None,None].shape
  • 1
([3, 1, 4, 1, 1])
  • 1
a > 1 # 返回一个ByteTensor
  • 1
tensor([[0, 0, 0, 0],
        [0, 1, 0, 0],
        [0, 0, 0, 0]], dtype=torch.uint8)
  • 1
  • 2
  • 3
a[a>1] # 等价于a.masked_select(a>1)
# 选择结果与原tensor不共享内存空间
  • 1
  • 2
tensor([1.0202])
  • 1
a[t.LongTensor([0,1])] # 第0行和第1行
  • 1
tensor([[-0.0766, -0.4981, -0.1263, -0.2356],
        [ 0.1508,  1.0202,  0.4058, -0.9278]])
  • 1
  • 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,输出的每个元素如下:

out[i][j] = input[index[i][j]][j]  # dim=0
out[i][j] = input[i][index[i][j]]  # dim=1
  • 1
  • 2

三维tensor的gather操作同理,下面举几个例子。

a = t.arange(0, 16).view(4, 4)
a
  • 1
  • 2
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15]])
  • 1
  • 2
  • 3
  • 4
# 选取对角线的元素
index = t.LongTensor([[0,1,2,3]])
a.gather(0, index)
  • 1
  • 2
  • 3
tensor([[ 0,  5, 10, 15]])
  • 1
# 选取反对角线上的元素
index = t.LongTensor([[3,2,1,0]]).t()
a.gather(1, index)
  • 1
  • 2
  • 3
tensor([[ 3],
        [ 6],
        [ 9],
        [12]])
  • 1
  • 2
  • 3
  • 4
# 选取反对角线上的元素,注意与上面的不同
index = t.LongTensor([[3,2,1,0]])
a.gather(0, index)
  • 1
  • 2
  • 3
tensor([[12,  9,  6,  3]])
  • 1
# 选取两个对角线上的元素
index = t.LongTensor([[0,1,2,3],[3,2,1,0]]).t()
b = a.gather(1, index)
b
  • 1
  • 2
  • 3
  • 4
tensor([[ 0,  3],
        [ 5,  6],
        [10,  9],
        [15, 12]])
  • 1
  • 2
  • 3
  • 4