最近pytorch使用的特别频繁, 这里总结一些pytorch中常用的张量(tensor)操作。

  1. tensor和array之间的转换
A = t.ones(3,  4)
# torch.tensor -> numpy.ndarray
B = A.numpy()
# numpy.ndarray -> torch.tensor
C = t.from_numpy(B)
# Note:
# A, B, C共享内存, 修改任意一个, 3个都会同时改变. 
# tensor和array之间的转换很快
  1. 从tensor中取值
A = t.ones(5)
# B仍然是一个Tensor, 只包含一个元素, 也称Scalar
B = A[2]
# 只包含一个元素的tensor才能使用item函数
# item返回的才是数值
V = B.item()
  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)

均匀/标准分布

normal(mean,std)/uniform(from,to)

正态分布/均匀分布

randperm(m)

随机排列

  1. 如何进行数据的拷贝
# 1. 使用t.tensor()
A = t.ones(5)
# 拷贝A的数据给B
B = t.tensor(A)

# 此时,A和B不共享内存
In [14]: id(A), id(B)
Out[14]: (1751028332080, 1751295611024)

# 2. 使用clone函数
In [15]: c = A.clone()

In [16]: id(A), id(c)
Out[16]: (1751028332080, 1751295519912)
  1. inplace操作
# Note:
# pytorch中,所有以下划线结尾的函数 
# 会修改Tensor本身, 比如add_, t_, sub_等
In [18]: A.cos_()
Out[18]:
tensor([[ 0.5403,  0.5403,  0.5403,  0.5403],
        [ 0.5403,  0.5403, -0.6536,  0.5403],
        [ 0.5403,  0.5403,  0.5403,  0.5403]])

In [19]: A.add_(2)
Out[19]:
tensor([[2.5403, 2.5403, 2.5403, 2.5403],
        [2.5403, 2.5403, 1.3464, 2.5403],
        [2.5403, 2.5403, 2.5403, 2.5403]])

In [20]: A
Out[20]:
tensor([[2.5403, 2.5403, 2.5403, 2.5403],
        [2.5403, 2.5403, 1.3464, 2.5403],
        [2.5403, 2.5403, 2.5403, 2.5403]])
  1. 使用cuda
# 如果支持gpu,则使用gpu
Device = t.device("cuda" if.cuda.is_available() else      "cpu")
# 将tensor转移到指定设备中
x = x.to(device)
y = y.to(device)
  1. 升维和降维(unsqueeze/squeeze)
# 1. 在指定位置增加新的维度 -> unsqueeze
In [22]: A.shape
Out[22]: torch.Size([2, 1, 4])

# 在A的第1个维度之前前插入新的维度
In [23]: B = A.unsqueeze(0)

In [24]: B.shape
Out[24]: torch.Size([1, 2, 1, 4])

# 在A的第3个维度之前插入新的维度
In [25]: B = A.unsqueeze(2)

In [26]: B.shape
Out[26]: torch.Size([2, 1, 1, 4])


# 2. 去掉指定位置的维度 -> unsqueeze
# Note: 一个维度只包含一个元素的才可以被去掉
# 去掉B的第2个维度
In [27]: C = B.squeeze(1)

In [28]: C.shape
Out[28]: torch.Size([2, 1, 4])
# 不指定参数,则会去掉B所有只有一个元素的维度
In [29]: C = B.squeeze()

In [30]: C.shape
Out[30]: torch.Size([2, 4])
  1. 交换维度/改变维度顺序(transpose/permute)
# 1. transpose一次只能改变2个维度的位置
In [33]: B.shape
Out[33]: torch.Size([2, 1, 1, 4])
#交换B的第1个维度和第2个维度,B和C共享内存
In [34]: C = B.transpose(0, 1)

In [35]: C.shape
Out[35]: torch.Size([1, 2, 1, 4])

# 2. permute可以同时改变多个维度的位置
# 重新调整B的维度顺序
In [36]: B.shape
Out[36]: torch.Size([2, 1, 1, 4])

In [37]: C = B.permute(1, 2, 0, 3)

In [38]: C.shape
Out[38]: torch.Size([1, 1, 2, 4])
  1. 获取最大/小值(max/min)
In [42]: A = t.rand(7, 4)

# max函数返回指定维度的最大值以及还最大值的位置
In [43]: t.max(A, 0)
Out[43]:
torch.return_types.max(
values=tensor([0.7347, 0.9382, 0.8176, 0.9182]),
indices=tensor([4, 6, 3, 0]))

out_max, out_max_indexs = t.max(A,0)

注意这里max函数返回的是两个值,一个是指定维度的最大值,一个是最大值的索引,也就是这里的out_max,索引也就是这里的out_max_indexs。
# min函数类似
# ...
  1. 逻辑函数 t.any和t.all
# pytorch中,all和any只支持uint8和bool类型的tensor
# numpy中,所有数值类型以及bool类型的数组都支持逻辑运算
In [52]: A = t.randint(0, 5, (5, 4)).type(t.uint8)

In [53]: A
Out[53]:
tensor([[0, 4, 1, 3],
       [0, 0, 4, 1],
       [3, 0, 2, 1],
       [0, 0, 1, 1],
       [2, 0, 1, 4]], dtype=torch.uint8)

# 1. all 沿着指定维度,只要有一个False(0),即为False(0)
In [54]: A.all(0)
Out[54]: tensor([0, 0, 1, 1], dtype=torch.uint8)

# 2. any 沿着指定维度,只要有一个True(1),即为True(1)
In [55]: A.any(0)
Out[55]: tensor([1, 1, 1, 1], dtype=torch.uint8)
  1. 创建网格(meshgrid)
# t.meshgrid的第一个参数表示列, 第二个参数表示行
# 跟numpy刚好相反
>>> a, b = t.meshgrid(t.arange(5), t.arange(5))
>>> a
tensor([[0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1],
       [2, 2, 2, 2, 2],
       [3, 3, 3, 3, 3],
       [4, 4, 4, 4, 4]])
>>> b
tensor([[0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4]])
  1. 重复(repeat)
    repeat操作对数据进行了复制
# 
In [63]: A = t.rand(3, 2)

# 指定A的第1个维度重复2次,第2个维度重复2次
In [64]: A.repeat(2, 2)
Out[64]:
tensor([[0.4932, 0.4790, 0.4932, 0.4790],
       [0.2069, 0.9966, 0.2069, 0.9966],
       [0.8775, 0.1757, 0.8775, 0.1757],
       [0.4932, 0.4790, 0.4932, 0.4790],
       [0.2069, 0.9966, 0.2069, 0.9966],
       [0.8775, 0.1757, 0.8775, 0.1757]])
  1. 扩展维度的尺寸(expand)
    和repeat类似,都可以实现维度尺寸的扩展。但需要注意的是
    Note:expand并没有复制新的数据,而是在原有数据的基础上创建了视图。所以在修改源张量时,现有张量会同时被修改
In [20]: a
Out[20]: tensor([[0.2521, 0.1678, 0.4995, 0.5533]])
# 将第一个维度扩展为2,第二个维度扩展为4
In [21]: b = a.expand(2, 4)

In [22]: b
Out[22]:
tensor([[0.2521, 0.1678, 0.4995, 0.5533],
        [0.2521, 0.1678, 0.4995, 0.5533]])

# 修改b会同时修改a
In [23]: b[1, 3] = 3

In [24]: a
Out[24]: tensor([[0.2521, 0.1678, 0.4995, 3.0000]])

In [25]: b
Out[25]:
tensor([[0.2521, 0.1678, 0.4995, 3.0000],
        [0.2521, 0.1678, 0.4995, 3.0000]])
  1. 条件查找(where)
# 1. 按条件修改值
In [63]: A = t.rand(3, 2)

# where(condition, x, y)
# 满足条件,则返回x,不满足返回y
# torch.where与numpy.where 的区别是,
# torch.where中的参数x, y均要求为tensor
In [72]: B = t.where(A > 5, t.tensor([10]), t.tensor([0]))

In [73]: B
Out[73]:
tensor([[10,  0,  0,  0],
       [ 0,  0, 10,  0],
       [ 0,  0, 10, 10],
       [10, 10, 10,  0],
       [10,  0, 10, 10]])
       
# 2. 获取满足条件的索引

In [81]: t.where(A>4)
Out[81]:
(tensor([0, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4]),
tensor([0, 2, 2, 3, 0, 1, 2, 0, 1, 2, 3]))

In [82]: A
Out[82]:
tensor([[8, 2, 3, 2],
       [1, 4, 8, 0],
       [4, 0, 9, 9],
       [8, 9, 7, 4],
       [9, 5, 8, 7]])
  1. 截断(clamp)
# 和torch.where 功能类似,但功能没有where丰富
# torch.clamp(input, min, max, out=None)
# 对于input的所有值,从min和max两个位置截断
# 小于min的值,则设为min;大于max的值,则设为max

In [82]: A
Out[82]:
tensor([[8, 2, 3, 2],
       [1, 4, 8, 0],
       [4, 0, 9, 9],
       [8, 9, 7, 4],
       [9, 5, 8, 7]])
# 将A中小于5的值设为5, 大于8的值设为8
In [83]: t.clamp(A, 5, 8)
Out[83]:
tensor([[8, 5, 5, 5],
       [5, 5, 8, 5],
       [5, 5, 8, 8],
       [8, 8, 7, 5],
       [8, 5, 8, 7]])
  1. 组合(stack/cat)
# 1. stack在新的维度上进行拼接,张量对应维度尺寸需相同

In [97]: B = t.rand(3, 4)

In [98]: C = t.rand(3, 4)

# 在第1个维度之前新增一个维度,在新的维度上拼接
In [99]: t.stack([B, C], 1).shape
Out[99]: torch.Size([3, 2, 4])

# 2. cat在现有维度上进行拼接
# 在第一个维度上进行拼接
In [107]: t.cat([B, C], 0).shape
Out[107]: torch.Size([6, 4])
  1. gather
    按维度索引取值。 在one-hot编码的处理中, 经常使用。
In [2]: a = t.rand(3, 4)

In [3]: a
Out[3]:
tensor([[0.6875, 0.7594, 0.4874, 0.0156],
        [0.6033, 0.8766, 0.3650, 0.7240],
        [0.3139, 0.4568, 0.8455, 0.9149]])

# 在第二个维度上, 取出索引为(1, 2, 0)的值, 
# Note: 除了第dim个维度, index的其它维度必须和a一致. 比如dim=1, 那除了第1个维度, 其它维度必须一致

# 输出的维度和index的维度一致

# index -> [3, 1], a -> [3, 4]
In [4]: a.gather(dim=1, index=t.tensor([[1],[2], [0]]))
Out[4]:
tensor([[0.7594],
        [0.3650],
        
# index -> [1, 4], a -> [3, 4]
In [10]: a.gather(dim=0, index=t.tensor([[1,2, 0, 0]]))
Out[10]: tensor([[0.6033, 0.4568, 0.4874, 0.0156]])
  1. scatter
    按维度索引设定值
>>> a
tensor([[1, 2, 6, 5],
        [6, 7, 9, 0],
        [7, 8, 3, 1],
        [2, 8, 0, 6]])
        
# 指定在张量的那个维度上进行操作        
>>> dim = 1

# 需要被填充的值
>>> val = 0

# index 在dim之外的维度需要保持一致
>>> index = t.tensor([0, 1, 2, 1]).unsqueeze(1)

# 可以看到,a[0, 0], a[1, 1], a[2, 2]和a[3, 1]被修改成val
>>> a.scatter(dim, index, val)
tensor([[0, 2, 6, 5],
        [6, 0, 9, 0],
        [7, 8, 0, 1],
        [2, 0, 0, 6]])
        
# 既然可以设置指定值,那么是否可以设置指定的张量呢,答案是可以的

# 比如也可以这样,val和index的维度一致
>>> val = t.randint(10, 100, (4,))
>>> val
tensor([59, 39, 76, 99])

>>> a.scatter(dim, index, val)
tensor([[59,  2,  6,  5],
        [ 6, 39,  9,  0],
        [ 7,  8, 76,  1],
        [ 2, 99,  0,  6]])

scatter在labelsmooth以及标签转onehot矩阵时经常用到,功能很强大。