什么是张量?
“Tensor”(张量)是一个数学和计算机科学术语,用于表示多维数组的数据结构。在深度学习和机器学习中,张量是一种常见的数据结构,用于存储和表示多维的数据,如标量(0维张量,即单个数值)、向量(1维张量,如一维数组)、矩阵(2维张量,如二维数组)以及更高维度的数据。张量可以包含整数、浮点数或其他数据类型,具体取决于应用的需要。
在深度学习中,张量是神经网络的核心数据结构。神经网络的层、权重、激活函数和输入都表示为张量。张量的多维性使其适用于处理各种复杂的数据,如图像、文本、音频等,并且能够在GPU和TPU等计算资源上执行并行式计算,大大提高资源利用率。
'2.1.0+cu121'
创建张量 参考:https://pytorch.org/docs/stable/torch.html#creation-ops
创建简单张量 创建简单张量 1 2 tensor1 = torch.tensor([[1 , 2 , 3 ], [2 , 3 , 6 ]]) tensor1
1 2 tensor([[1, 2, 3], [2, 3, 6]] )
创建未初始化张量 1 2 3 tensor2 = torch.empty(size=(2 , 3 )) tensor2
1 2 tensor([[-1 .0717e+37 , 1.3410e-42 , 0.0000e+00 ], [ 0.0000e+00 , 0.0000e+00 , 0.0000e+00 ]])
1 2 torch.empty_like(tensor1)
tensor([[0, 0, 0],
[0, 0, 0]])
1 2 tensor3 = torch.Tensor(2 , 3 ) tensor3
tensor([[-1.0718e+37, 1.3410e-42, 0.0000e+00],
[ 0.0000e+00, 0.0000e+00, 0.0000e+00]])
查看张量的基本属性与方法 每个张量都具有以下基本属性:
张量尺寸
张量元素类型
张量维度
张量中元素个数
张量的转置
获取张量中非零元素所在位置
打印张量所在设备
查看梯度
张量尺寸
torch.Size([2, 3])
torch.Size([2, 3])
张量元素类型
torch.int64
张量维度
2
张量元素个数
6
6
对张量进行转置
tensor([[1, 2],
[2, 3],
[3, 6]])
tensor([[1, 2],
[2, 3],
[3, 6]])
获取张量中非零元素所在位置
tensor([[0, 0],
[0, 1]])
张量所在设备
device(type='cpu')
查看梯度
None
张量与数组的相互转化 将数组转化为张量
1 2 array1 = np.random.randint(20 , size=(3 , 4 )) array1
array([[13, 13, 8, 0],
[18, 3, 7, 4],
[16, 8, 14, 18]])
tensor([[13., 13., 8., 0.],
[18., 3., 7., 4.],
[16., 8., 14., 18.]])
1 2 torch.from_numpy(array1)
1 2 3 tensor([[13, 13, 8, 0], [18, 3, 7, 4], [16, 8, 14, 18]] , dtype=torch.int32)
1 2 3 tensor([[13, 13, 8, 0], [18, 3, 7, 4], [16, 8, 14, 18]] , dtype=torch.int32)
第二个函数asarray()的功能更多,不仅可以将数组转化为张量,还可以将其他数据类型转化为张量,例如:标量,列表,元组等等。而from_numpy()函数只能将数组转化为张量,如果是其他类型的数据则会报错。
1 torch.from_numpy([1 , 2 , 3 , 4 ])
1 2 3 4 5 6 7 8 9 TypeError Traceback (most recent call last ) Cell In[25 ], line 1 TypeError: expected np.ndarray (got list)
1 torch.asarray([1 , 2 , 3 , 4 ])
tensor([1, 2, 3, 4])
将张量转化为数组
array([[1, 2, 3],
[2, 3, 6]], dtype=int64)
array([[1, 2, 3],
[2, 3, 6]], dtype=int64)
创建特殊张量 创建等差张量
1 2 3 C:\Users\ming\AppData\Local\Temp\ipykernel_4696\262098997 .py:1 : UserWarning: torch.range is deprecated and will be removed in a future release because its behavior is inconsistent with Python's range builtin. Instead, use torch.arange, which produces values in [start, end ). torch.range (1 , 10 , 2 ) # 该方法即将淘汰与torch.arange()等价 tensor([1 ., 3 ., 5 ., 7 ., 9 .])
tensor([1, 3, 5, 7, 9])
1 torch.linspace(1 , 10 , 5 )
1 tensor ([ 1 .0000 , 3 .2500 , 5 .5000 , 7 .7500 , 10 .0000 ])
创建等比张量 1 torch.logspace(1 , 10 , 6 , base=2 )
1 tensor ([ 2 .0000 , 6 .9644 , 24 .2515 , 84 .4485 , 294 .0668 , 1024 .0000 ])
创建全0张量 1 2 torch.zeros(size=(3 , 3 ))
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
1 2 torch.zeros_like(tensor1)
tensor([[0, 0, 0],
[0, 0, 0]])
创建全1张量 1 2 torch.ones(size=(3 , 3 ))
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
1 2 torch.ones_like(tensor1)
tensor([[1, 1, 1],
[1, 1, 1]])
创建相同元素的张量 1 2 torch.full(size=(3 , 3 ), fill_value=2.3 )
tensor([[2.3000, 2.3000, 2.3000],
[2.3000, 2.3000, 2.3000],
[2.3000, 2.3000, 2.3000]])
1 2 torch.full_like(input =tensor1, fill_value=2.3 )
tensor([[2, 2, 2],
[2, 2, 2]])
创建对角线元素为1的二维张量(单位矩阵)
tensor([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
随机抽样 设置随机种子数 设定随机种子数后,在Pytorch中使用“随机系列”方法生成的随机张量值就会固定下来,不会每次运行时更新,这对于复现某些模型的结果非常有用
1 <torch. _C. Generator at 0x22e5ae63750 >
1000
创建从0~n-1的随机整数序列 该方法得到的结果可以理解为将torch.arange()方法得到的结果进行打乱输出
1 2 tensor3 = torch.randperm(5 ) tensor3
tensor([2, 4, 0, 3, 1])
均匀分布 返回[0, 1)上的均匀分布随机数
1 2 torch.rand(size=(3 , 4 ))
1 2 3 tensor([[0.2724, 0.6261, 0.4410, 0.3653], [0.3535, 0.5971, 0.3572, 0.4807], [0.4217, 0.1254, 0.6818, 0.0571]] )
1 2 torch.rand_like(tensor2)
tensor([[0.6818, 0.0473, 0.4856],
[0.0260, 0.0131, 0.7607]])
tensor([[0.0722, 0.1026, 0.3915],
[0.4430, 0.6577, 0.4261]])
伯努利分布 1 2 torch.bernoulli(tensor2)
tensor([[0., 0., 0.],
[1., 1., 0.]])
多项式分布 1 2 3 weight = torch.Tensor([5 , 3 , 1 , 9 ]) torch.multinomial(weight, 40 , replacement=True )
1 2 tensor ([0 , 3 , 3 , 1 , 0 , 3 , 1 , 0 , 3 , 0 , 0 , 0 , 3 , 3 , 3 , 1 , 3 , 0 , 0 , 1 , 3 , 3 , 3 , 3 , 0 , 0 , 3 , 3 , 2 , 2 , 3 , 0 , 0 , 1 , 1 , 3 , 0 , 0 , 0 , 3 ])
泊松分布
1 2 rates = torch.rand(4 , 4 ) * 5 torch.poisson(rates)
tensor([[0., 1., 4., 4.],
[0., 5., 5., 3.],
[5., 0., 0., 2.],
[5., 2., 3., 1.]])
随机整数 1 2 torch.randint(20 , 50 , size=(5 , 5 ))
tensor([[20, 46, 22, 42, 22],
[33, 22, 22, 35, 45],
[30, 41, 24, 41, 21],
[28, 48, 23, 27, 45],
[20, 38, 39, 20, 47]])
1 2 torch.randint_like(tensor2, 20 , 50 )
tensor([[39., 47., 49.],
[32., 45., 31.]])
正态分布 标准正态分布 1 2 torch.randn(size=(5 , 5 ))
1 2 3 4 5 tensor([[-0 .9138, -0 .3769, -0 .3745, 1.7369, -1 .5996], [-0 .5251, -1 .9600, -0 .1617, 0.3721, 1.0565], [-0 .1531, -0 .7231, 1.5471, 0.6138, -1 .9954], [-0 .7167, 0.2939, -1 .1686, -0 .7682, -1 .0757], [-0 .7147, 0.4852, 1.0869, -0 .2248, -0 .8522]])
1 2 torch.randn_like(tensor2)
1 2 tensor([[-0.2276, 0.5628, 0.2006], [ 0.8952, 1.3668, 0.6112]] )
一般正态分布 1 2 3 torch.normal(mean=torch.arange(1 , 11 , 1.0 ), std=torch.arange(1 , 0 , -0.1 ))
1 2 tensor ([1 .1280 , 2 .4969 , 4 .2895 , 3 .8980 , 3 .6548 , 5 .6770 , 6 .6576 , 8 .1910 , 9 .0875 , 9 .9736 ])
1 2 3 torch.normal(mean=torch.Tensor([1.0 ,]), std=torch.arange(1 , 0 , -0.1 ))
1 2 tensor ([ 2 .1430 , -0 .1413 , 0 .3334 , 1 .0870 , 0 .0081 , 1 .1751 , 0 .6992 , 1 .5841 , 0 .8683 , 1 .1521 ])
1 2 3 torch.normal(mean=torch.arange(1 , 11 , 1.0 ), std=torch.Tensor([1.0 ,]))
1 2 tensor ([ 0 .2791 , 0 .9233 , 1 .1594 , 3 .5136 , 5 .8334 , 7 .4911 , 6 .7038 , 9 .4591 , 9 .6741 , 11 .2492 ])
1 2 3 torch.normal(mean=0 , std=1 , size=(3 , 3 ))
1 2 3 tensor([[ 0.8146, 0.1963, 2.1625], [-0.2048, -0.8331, 0.0816], [-0.5588, 0.5525, 0.3279]] )
张量的序列化与反序列化 将tensor保存到硬盘 1 torch.save(tensor2, 'tensor2.pt' )
将硬盘上的张量读取到内存 此时需要注意一个一个问题,默认的情况下张量存储到硬盘中会记录张量保存前的设备位置,在导入的时候也会自动导入到相应的设备。这个时候会存在一个问题,如果我在服务器中保存了cuda:2设备中的某个张量到硬盘,然后再将硬盘读取到我本机的电脑中,我本机电脑有没有cuda:2设备,这个时候就会报错。 我们可以在读取张量文件时,通过指定map_location参数将张量读取到某个设备内存中。
1 2 tensor_read = torch.load('tensor2.pt' , map_location='cuda:0' ) tensor_read
1 2 tensor([[0.0722, 0.1026, 0.3915], [0.4430, 0.6577, 0.4261]] , device='cuda:0' )
张量元素操作 1 2 3 tensor4 = torch.randint(10 , size=(3 , 4 )) tensor5 = torch.randint(10 , 20 , size=(3 , 4 ))
tensor([[2, 0, 2, 8],
[6, 7, 2, 0],
[3, 2, 0, 6]])
tensor([[12, 13, 19, 12],
[11, 10, 19, 10],
[17, 17, 13, 17]])
张量的索引和切片 首先,张量支持类似于Numpy中的元素索引和切片,例如:取出部分元素,按照条件对元素进行筛选等等。
tensor(2)
tensor([[2, 0],
[0, 6]])
tensor([8, 6, 7, 6])
张量尺寸修改 1 tensor4.reshape(shape=(4 , 3 ))
tensor([[2, 0, 2],
[8, 6, 7],
[2, 0, 3],
[2, 0, 6]])
tensor([[2, 0, 2],
[8, 6, 7],
[2, 0, 3],
[2, 0, 6]])
张量的拼接 不生成新维度
torch.cat()
torch.concat()
torch.concatenate()
1 2 torch.cat([tensor4, tensor5], dim=1 )
1 2 3 tensor([[ 2, 0, 2, 8, 12, 13, 19, 12], [ 6, 7, 2, 0, 11, 10, 19, 10], [ 3, 2, 0, 6, 17, 17, 13, 17]] )
1 2 torch.cat([tensor4, tensor5], dim=0 )
tensor([[ 2, 0, 2, 8],
[ 6, 7, 2, 0],
[ 3, 2, 0, 6],
[12, 13, 19, 12],
[11, 10, 19, 10],
[17, 17, 13, 17]])
生成新维度 1 torch.stack([tensor4, tensor5], dim=0 )
tensor([[[ 2, 0, 2, 8],
[ 6, 7, 2, 0],
[ 3, 2, 0, 6]],
[[12, 13, 19, 12],
[11, 10, 19, 10],
[17, 17, 13, 17]]])
1 torch.stack([tensor4, tensor5], dim=1 )
tensor([[[ 2, 0, 2, 8],
[12, 13, 19, 12]],
[[ 6, 7, 2, 0],
[11, 10, 19, 10]],
[[ 3, 2, 0, 6],
[17, 17, 13, 17]]])
张量的切分 指定切分的份数 1 2 torch.chunk(tensor4, 2 , dim=0 )
(tensor([[2, 0, 2, 8],
[6, 7, 2, 0]]),
tensor([[3, 2, 0, 6]]))
1 2 torch.chunk(tensor4, 2 , dim=1 )
(tensor([[2, 0],
[6, 7],
[3, 2]]),
tensor([[2, 8],
[2, 0],
[0, 6]]))
指定切分的大小 1 2 3 torch.split(tensor4, 2 , dim=0 )
(tensor([[2, 0, 2, 8],
[6, 7, 2, 0]]),
tensor([[3, 2, 0, 6]]))
汇聚操作(选讲)
1 2 3 4 5 6 7 8 9 10 11 12 13 tensor6 = torch.gather(input =tensor4, dim=0 , index=torch.LongTensor([[0 , 1 , 0 , 1 ], [1 , 0 , 1 , 0 ], [0 , 0 , 0 , 1 ]])) tensor6
tensor([[2, 7, 2, 0],
[6, 0, 2, 8],
[2, 0, 2, 0]])
散射操作(选讲) 散射操作和汇聚操作可以看成是一组逆运算操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 torch.zeros(tensor6.shape, dtype=torch.int64).scatter_(dim=0 , index=torch.LongTensor([[0 , 1 , 0 , 1 ], [1 , 0 , 1 , 0 ], [0 , 0 , 0 , 1 ]]), src=tensor6)
tensor([[2, 0, 2, 8],
[6, 7, 2, 0],
[0, 0, 0, 0]])
获取指定的行和列
tensor([[2, 0, 2, 8],
[6, 7, 2, 0],
[3, 2, 0, 6]])
1 2 torch.index_select(tensor4, 0 , torch.LongTensor([0 , 2 ]))
tensor([[2, 0, 2, 8],
[3, 2, 0, 6]])
1 2 torch.index_select(tensor4, 1 , torch.LongTensor([1 , 3 ]))
tensor([[0, 8],
[7, 0],
[2, 6]])
使用掩膜获取指定元素
tensor([[2, 0, 2, 8],
[6, 7, 2, 0],
[3, 2, 0, 6]])
1 2 3 mask = torch.BoolTensor((tensor4>4 ).bool ()) mask
1 2 3 tensor ( [ [ False , False , False , True ] , [ True , True , False , False ] , [ False , False , False , True ] ] )
1 2 torch.masked_select(tensor4, mask)
tensor([8, 6, 7, 6])
维度修改 维度交换
tensor([[2, 0, 2, 8],
[6, 7, 2, 0],
[3, 2, 0, 6]])
torch.Size([3, 4])
1 tensor4 = tensor4.transpose(0 , 1 )
torch.Size([4, 3])
维度重排 1 2 3 tensor4 = tensor4.permute(1 , 0 ) tensor4
tensor([[2, 0, 2, 8],
[6, 7, 2, 0],
[3, 2, 0, 6]])
1 2 3 4 5 6 [3, 4, 2] -> [4, 2, 3] transpose (0 , 1 ) transpose (1 , 2 ) transpose:[3 ,4 ,2] -> [4, 3, 2] -> [4, 2, 3] permute (1 , 2 , 0 ) permute: [3 , 4 , 2 ] -> [4 , 2 , 3 ]
增加维度
torch.Size([3, 4])
1 2 tensor41 = tensor4.unsqueeze(1 ).unsqueeze(-1 ) tensor41.shape
torch.Size([3, 1, 4, 1])
降低维度 1 tensor41.squeeze(dim=1 ).shape
torch.Size([3, 4, 1])
torch.Size([3, 1, 4, 1])
1 tensor41.squeeze().shape
torch.Size([3, 4])
删除维度 删除维度与降低维度的不同点在于,降低维度只能够将维度为1的维度删除,而删除维度会强制删除某个维度,如果维度不为1,那么会沿着该维度进行切分
1 (tensor ([2 , 0 , 2 , 8 ]), tensor([6 , 7 , 2 , 0 ]), tensor([3 , 2 , 0 , 6 ]))
张量计算操作 张量简单计算
tensor([[10, 13, 17, 4],
[ 5, 3, 17, 10],
[14, 15, 13, 11]])
1 2 tensor7 = torch.randn(size=(4 ,))
1 2 3 print ("tensor4:" , tensor4.shape)print ("tensor7:" , tensor7.shape)print ("tensor4 - tensor7:\n" , tensor4 - tensor7)
1 2 3 4 5 6 tensor4: torch.Size ([3, 4] ) tensor7: torch.Size ([4] ) tensor4 - tensor7: tensor ([[ 1.9346, 0.1155, 1.3009, 7.8812] , [ 5.9346, 7.1155, 1.3009, -0.1188] , [ 2.9346, 2.1155, -0.6991, 5.8812] ])
1 2 3 4 tensor4b, tensor7b = torch.broadcast_tensors(tensor4, tensor7) print ("tensor4b:" , tensor4b.shape)print ("tensor7b:" , tensor7b.shape)
tensor4b: torch.Size([3, 4])
tensor7b: torch.Size([3, 4])
1 2 3 tensor42 = tensor4.to('cuda:0' ) tensor42.device
device(type='cuda', index=0)
1 2 3 4 5 6 7 8 9 RuntimeError Traceback (most recent call last) Cell In [98 ], line 1 RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!
矩阵乘法 1 2 3 tensor81 = torch.randn(size=(5 , 2 )) tensor82 = torch.randn(size=(2 , 10 )) (tensor81 @ tensor82).shape
torch.Size([5, 10])
限定张量元素值范围 将输入input张量每个元素的夹紧到区间 [min,max],并返回结果到一个新张量
1 2 3 a = torch.randn(2 , 4 ) print (a)torch.clamp(a, -0.5 , 0.5 )
1 2 3 4 5 tensor([[ 1.6036, 0.6247, 0.4567, -0.4334], [ 0.4349, 0.7140, -1.7777, -1.5939]] )tensor([[ 0.5000, 0.5000, 0.4567, -0.4334], [ 0.4349, 0.5000, -0.5000, -0.5000]] )
向下取整 1 2 3 a = torch.randn(4 ) print (a)torch.floor(a)
1 2 tensor ([ 0 .3045 , -0 .6240 , -2 .2791 , -0 .5166 ])tensor ([ 0 ., -1 ., -3 ., -1 .])
向上取整
tensor([ 1., -0., -2., -0.])
计算除法并获取余数 1 torch.fmod(torch.Tensor([-3 , -2 , -1 , 1 , 2 , 3 ]), 2 )
1 tensor ([-1 ., -0 ., -1 ., 1 ., 0 ., 1 .])
获取浮点数小数部分 1 torch.frac(torch.Tensor([1. , 2.5 , -3.2 ]))
tensor([ 0.0000, 0.5000, -0.2000])
线性插值 1 2 3 4 start = torch.arange(1. , 5. ) end = torch.ones((4 ,)) * 10 print (start)print (end)
tensor([1., 2., 3., 4.])
tensor([10., 10., 10., 10.])
1 torch.lerp(start, end, 0.5 )
tensor([5.5000, 6.0000, 6.5000, 7.0000])
Pytorch自动求导机制 在Pytorch中每个张量都有一个requires_grad属性去控制该张量在进行计算的时候是否需要求出计算梯度。当我们需要求某个张量在计算过程中的梯度,只需将requires_grad设置为True即可。 在进行相关计算后,调用结果张量的backward()方法即可将在计算过程中涉及到的所有张量的梯度计算出来。 要查看某个张量对于结果张量的梯度,只需调用张量的grad属性即可。
1 2 x = torch.tensor([2.0 ], requires_grad=True ) a = torch.tensor([4.0 ], requires_grad = True )
1 2 3 print (x.grad)print (a.grad)
None
None
1 2 3 print (x.grad)print (a.grad)
tensor([4.])
tensor([2.])
1 2 3 4 5 y.backward() print (x.grad)print (a.grad)
tensor([8.])
tensor([4.])
其他 GPU相关 1 torch.cuda.current_device()
0
1 torch.cuda.is_available()
True
1 torch.cuda.device_count()
1
1 torch.cuda.get_device_name(0 )
'NVIDIA GeForce MX450'
1 torch.cuda.set_device(0 )
1 2 3 import osos.environ['CUDA_VISIBLE_DEVICES' ] = "0,1"
CPU并行化线程数设置 1 2 torch.get_num_threads()
4
1 torch.set_num_threads(8 )