最近pytorch使用的特别频繁, 这里总结一些pytorch中常用的张量(tensor)操作。
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之间的转换很快
从tensor中取值
A = t.ones(5) # B仍然是一个Tensor, 只包含一个元素, 也称Scalar B = A[2] # 只包含一个元素的tensor才能使用item函数 # item返回的才是数值 V = B.item()
常见的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. 使用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)
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]])
使用cuda
# 如果支持gpu,则使用gpu Device = t.device("cuda" if.cuda.is_available() else "cpu") # 将tensor转移到指定设备中 x = x.to(device) y = y.to(device)
升维和降维(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])
交换维度/改变维度顺序(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])
获取最大/小值(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])) # min函数类似 # ...
逻辑函数 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)
创建网格(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]])
重复(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]])
扩展维度的尺寸(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]])
条件查找(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]])
截断(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]])
组合(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])
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]])
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矩阵时经常用到,功能很强大。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。