张量
张量可以理解为向量,在python中的编程中可以联想至列表和numpy的ndarray数据类型,具体包括了一维张量和高维矩阵,pytorch和numpy有很多相似之处,比如都具有广播机制和向量化操作,并且pytorch的张量和numpy的ndarray可以较为方便的进行转换。不同的是张量支持gpu运算
基本创建方式
torch.tensor()
torch.tensor()是pytorch中最为基础和常用的张量创建方式,以下为tensor()函数的参数设置
def tensor(data: Any, dtype: Optional[_dtype] = None, device: Device = None, requires_grad: _bool = False) -> Tensor:
可以看到有四个参数可以进行设置,分别是data(用于转换的值),dtype(指定数据类型),device(计算设备选择cpu/gpu),requires_grad(是否参与梯度计算),而函数的返回类型就是Tensor(张量)。这四个参数中必不可少的就是data,这里的data可以传入基本的数值类型如整形和浮点型,也可传入列表和元组(注意,这里不支持传入集合),并且也可传入ndarray类型。
data参数
若传入浮点型或整形数据
import torch
data1 = torch.tensor(10)
print(data1, data1.dtype)
data2 = torch.tensor(10.0)
print(data2, data2.dtype)
# tensor(10) torch.int64
# tensor(10.) torch.float32
由上可见,这里分别创建了两个标量张量,并且可以看到pytorch对整形和浮点数据的转换默认的数据类型分别为int64和float32
若传入容器类型数据或ndarray
import torch
import numpy as np
data1 = torch.tensor([1, 2, 3])
print(data1, data1.dtype)
data2 = torch.tensor((1, 2, 3))
print(data2, data2.dtype)
data3 = torch.tensor(np.array([1, 2, 3]))
print(data3, data3.dtype)
data4 = torch.tensor(np.array([1.0, 2.0, 3.0]))
print(data4, data4.dtype)
# tensor([1, 2, 3]) torch.int64
# tensor([1, 2, 3]) torch.int64
# tensor([1, 2, 3], dtype=torch.int32) torch.int32
# tensor([1., 2., 3.], dtype=torch.float64) torch.float64
由上述代码可以看出,若传入的是ndarrary数据类型,由于其底层默认整形数据为int32,浮点型数据为float64,所以在转换后,张量的精度也与其一样。
dtype参数
若要对创建张量的数据类型进行转换,只需指定dtype参数即可
import torch
import numpy as np
data1 = torch.tensor([1, 2, 3], dtype=torch.float32)
print(data1, data1.dtype)
data2 = torch.tensor((1, 2, 3), dtype=torch.float32)
print(data2, data2.dtype)
data3 = torch.tensor(np.array([1, 2, 3]), dtype=torch.float32)
print(data3, data3.dtype)
data4 = torch.tensor(np.array([1.0, 2.0, 3.0]), dtype=torch.float32)
print(data4, data4.dtype)
# tensor([1., 2., 3.]) torch.float32
# tensor([1., 2., 3.]) torch.float32
# tensor([1., 2., 3.]) torch.float32
# tensor([1., 2., 3.]) torch.float32
device参数
对于device参数,参数设置一般为'cpu','cuda','cuda:0','cuda:1'...分别代指将张量放入cpu进行计算或放入gpu进行计算,而'cuda:0'表示第一块gpu,以此类推':1'表示第二块,如有多块gpu可以分别调用,注意,不在同一块设备上的张量不能参与运算
import torch
data1 = torch.tensor([1.0, 2.0, 3.0], device="cuda")
print(data1, data1.device)
data2 = torch.tensor((1.0, 2.0, 3.0), device="cuda")
print(data2, data2.device)
# tensor([1., 2., 3.], device='cuda:0') cuda:0
# tensor([1., 2., 3.], device='cuda:0') cuda:0
注意这里若只有一块gpu,则device参数使用'cuda'和'cuda:0'是等效的
在张量定义之后,也可以对张量的运算设备进行转换如使用cpu(),cuda(),to()函数
import torch
data1 = torch.tensor([1.0, 2.0, 3.0], device="cuda:0")
print(data1, data1.device)
data1 = data1.cpu()
print(data1, data1.device)
data1 = data1.cuda()
print(data1, data1.device)
data1 = data1.to("cpu")
print(data1, data1.device)
data1 = data1.to("cuda")
print(data1, data1.device)
# tensor([1., 2., 3.], device='cuda:0') cuda:0
# tensor([1., 2., 3.]) cpu
# tensor([1., 2., 3.], device='cuda:0') cuda:0
# tensor([1., 2., 3.]) cpu
# tensor([1., 2., 3.], device='cuda:0') cuda:0
注意,这里在设备切换后,需要重新赋值,切换函数本身不会修改并覆盖原本数据
一般在开源项目中都可以看到这样一行代码
import torch
device = "cuda:0" if torch.cuda.is_available() else "cpu"
data1 = torch.tensor([1.0, 2.0, 3.0], device=device)
print(data1)
# tensor([1., 2., 3.], device='cuda:0')
这里为了在不同设备上运行项目,使用了三目运算符进行判定本地设备是否支持cuda,用于判断是否支持的函数就是torch.cuda.is_available(),支持返回True,否则返回False,那么这个三目运算符的作用就是在cuda支持的情况下使用'cuda:0'否则调用'cpu',关于三目运算符的详解可以参考我的另一篇博客CSDN
requires_grad参数
requires_grad参数的作用是设置创建的张量是否需要参与梯度计算,默认为False,也可以手动设置为True,注意,整形张量不支持求梯度。
import torch
import numpy as np
data1 = torch.tensor([1, 2, 3], requires_grad=True)
print(data1)
# Traceback (most recent call last):
# File "D:\Pythonproject\teach_day_01\demo01.py", line 4, in <module>
# data1 = torch.tensor([1,2,3],requires_grad=True)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# RuntimeError: Only Tensors of floating point and complex dtype can require gradients
import torch
data1 = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
print(data1, data1.requires_grad)
data2 = torch.tensor([1.0, 2.0, 3.0])
print(data2, data2.requires_grad)
# tensor([1., 2., 3.], requires_grad=True) True
# tensor([1., 2., 3.]) False
torch.Tensor()
关于torch.Tensor()函数,现在一般很少使用,或者说并不建议使用,其相对于torch.tensor()的优势在于可以指定张量形状进行创建
import torch
data1 = torch.Tensor(2, 3)
print(data1)
# tensor([[-2.1118e+14, 1.5905e-42, 0.0000e+00],
# [ 0.0000e+00, 0.0000e+00, 0.0000e+00]])
可以看到上述代码创建了一个两行三列的随机张量,这里的随机并不符合均匀分布或者正态分布,而是其内存位置上的原本任意值。
同样torch.Tensor()也支持传入数据进行张量转换,但是要注意,这里并不支持传入整形或者浮点型数据,只能传入列表、元组或者ndarray类型数据
import torch
data1 = torch.Tensor(3)
print(data1)
data2 = torch.Tensor(2.0)
print(data2)
# tensor([-1.4512e+25, 2.1398e-42, 0.0000e+00])
# Traceback (most recent call last):
# File "D:\Pythonproject\teach_day_01\demo01.py", line 5, in <module>
# data2 = torch.Tensor(2.)
# ^^^^^^^^^^^^^^^^
# TypeError: new(): data must be a sequence (got float)
由上述代码可以看出data1在创建的时候参数3被识别为了要创建一个一维随机张量,长度为3,而data2在创建的时候可以看到报错中要求data要为一个序列,所以并不支持单数值作为参数使用
import torch
import numpy as np
data1 = torch.Tensor([1, 2, 3])
print(data1, data1.dtype)
data2 = torch.Tensor((1, 2, 3))
print(data2, data2.dtype)
data3 = torch.Tensor(np.array([1, 2, 3]))
print(data3, data3.dtype)
# tensor([1., 2., 3.]) torch.float32
# tensor([1., 2., 3.]) torch.float32
# tensor([1., 2., 3.]) torch.float32
上述代码演示了由列表、元组和ndarray作为参数的时候的情况,可以注意到的一点是,torch.Tensor()不会主动识别参数的数据类型,并且没有dtype参数设置来改变数据类型,所有数据最后都会变成float32数据类型,这也是目前不推荐使用Tensor()创建张量的原因
指定数据类型创建张量
张量的基本数据类型基本包括如下几种数据类型
ShortTensor
IntTensor
LongTensor
FloatTensor
DoubleTensor
上面五种类型分别对应torch.int16,torch.int32,torch.int64,torch.float32,torch.float64。
我们可以使用这几种类型进行指定进行张量的实例化
import torch
data1 = torch.ShortTensor([1.2, 2.3, 3.4])
data2 = torch.IntTensor([1.2, 2.3, 3.4])
data3 = torch.LongTensor([1.2, 2.3, 3.4])
data4 = torch.FloatTensor([1, 2, 3])
data5 = torch.DoubleTensor([1, 2, 3])
print(data1)
print(data2)
print(data3)
print(data4)
print(data5)
# tensor([1, 2, 3], dtype=torch.int16)
# tensor([1, 2, 3], dtype=torch.int32)
# tensor([1, 2, 3])
# tensor([1., 2., 3.])
# tensor([1., 2., 3.], dtype=torch.float64)
注意,上述的五个张量创建函数,都不支持在定义的时候设置requires_grad,都需要在定义完张量后再设置
import torch
data1 = torch.FloatTensor([1, 2, 3])
data1.requires_grad = True
print(data1)
# tensor([1., 2., 3.], requires_grad=True)
张量数据类型转换
张量在创建之后也是支持元素类型的转换的,具体的方法会short(),int(),long(),float(),double(),分别对应torch.int16,torch.int32,torch.int64,torch.float32,torch.float64的转换,要注意,该方法不会修改并覆盖原数据,需要重新赋值
import torch
data1 = torch.tensor([1, 2, 3])
data1 = data1.float()
print(data1, data1.dtype)
data1 = data1.short()
print(data1, data1.dtype)
data1 = data1.int()
print(data1, data1.dtype)
data1 = data1.double()
print(data1, data1.dtype)
data1 = data1.long()
print(data1, data1.dtype)
# tensor([1., 2., 3.]) torch.float32
# tensor([1, 2, 3], dtype=torch.int16) torch.int16
# tensor([1, 2, 3], dtype=torch.int32) torch.int32
# tensor([1., 2., 3.], dtype=torch.float64) torch.float64
# tensor([1, 2, 3]) torch.int64
创建全0、全1和全指定值张量
全0张量
torch.zeros()
torch.zeros()的作用是创建指定形状的全0张量,参数为创建张量的形状以及包括tensor()中的device,dtype,requires_grad参数
import torch
data1 = torch.zeros([2, 3])
print(data1)
data2 = torch.zeros(2, 3)
print(data2)
# tensor([[0., 0., 0.],
# [0., 0., 0.]])
# tensor([[0., 0., 0.],
# [0., 0., 0.]])
注意形状参数如为高维可以使用元组或者列表的形式,也可以不加括号
import torch
data1 = torch.zeros(2, 3, device="cuda", requires_grad=True, dtype=torch.float64)
print(data1)
# tensor([[0., 0., 0.],
# [0., 0., 0.]], device='cuda:0', dtype=torch.float64,
# requires_grad=True)
torch.zeros_like()
torch.zeros_like()的作用区别于torch.zeros()在于第一个参数,zeros()是形状,zeros_like()的第一个参数是另一个张量,作用是创建与其形状相同的全0张量,其余参数设置与zeros()相同
import torch
data1 = torch.tensor([1, 2, 3])
data2 = torch.zeros_like(data1, dtype=torch.float32, device="cuda", requires_grad=True)
print(data1)
print(data2)
# tensor([1, 2, 3])
# tensor([0., 0., 0.], device='cuda:0', requires_grad=True)
全1张量
torch.ones()
详情与torch.zeros()类似,区别为创建的是全1张量
import torch
data1 = torch.ones(2, 3, requires_grad=True, dtype=torch.float32, device="cuda")
print(data1)
# tensor([[1., 1., 1.],
# [1., 1., 1.]], device='cuda:0', requires_grad=True)
torch.ones_like()
详情与torch.zeros_like()类似,区别为创建的是全1张量
import torch
data1 = torch.tensor([1, 2, 3])
data2 = torch.ones_like(data1, requires_grad=True, dtype=torch.float32, device="cuda")
print(data2)
# tensor([1., 1., 1.], device='cuda:0', requires_grad=True)
全指定值张量
torch.full()
torch.full()用于创建全指定值元素的张量,注意函数前两个参数分别为size和fill_value,所以这里的size若为高维需要使用元组或列表的形式,否则可能造成参数错位。其余参数也包括了device,dtype,requires_grad参数可以设置
import torch
data1 = torch.full((2, 3), 10, device="cuda", dtype=torch.float32, requires_grad=True)
print(data1)
# tensor([[10., 10., 10.],
# [10., 10., 10.]], device='cuda:0', requires_grad=True)
torch.full_like()
torch.full_like()用于创建与另一张量相同形状的全指定值张量,与full()的区别在于第一个参数需要设置为用于模仿形状的张量
import torch
data1 = torch.tensor([1, 2, 3])
data2 = torch.full_like(
data1, 10, device="cuda", dtype=torch.float32, requires_grad=True
)
print(data2)
# tensor([10., 10., 10.], device='cuda:0', requires_grad=True)
创建随机张量
torch.rand()
torch,rand()的作用是创建0-1区间内均匀分布的随机值,第一个参数为形状,高维可以使用列表或元组形式,也可不使用括号其余参数还包括device,dtype,requires_grad。
import torch
data1 = torch.rand(2, 3, device="cuda", dtype=torch.float64, requires_grad=True)
print(data1)
# tensor([[0.1615, 0.7415, 0.6346],
# [0.3183, 0.6323, 0.9578]], device='cuda:0', dtype=torch.float64,
# requires_grad=True)
torch.randn()
torch.randn()与torch.rand()的唯一区别为生成的是正态分布的0-1区间随机值
import torch
data1 = torch.randn(2, 3, device="cuda", dtype=torch.float64, requires_grad=True)
print(data1)
# tensor([[-1.1177, -0.6240, -1.4654],
# [-0.6016, 0.1243, -0.2280]], device='cuda:0', dtype=torch.float64,
# requires_grad=True)
torch.randint()
torch.randint()主要用于生成指定范围指定形状的随机整形张量,设计的主要参数包括low,high,size,分布对应了生成数值的下限,上限,和形状,注意,这里的生成范围为[low,high)也就是不包括上限的,并且生成的数据符合均匀分布
import torch
data1 = torch.randint(0, 10, (2, 3), device="cuda")
print(data1)
# tensor([[9, 0, 0],
# [6, 4, 2]], device='cuda:0')
torch.empty()
torch.empty()的作用是生成指定形状的未初始化的随机张量,其值取决于先前内存中存储的随机值
import torch
data1 = torch.empty(2, 3, requires_grad=True)
print(data1)
# tensor([[0., 0., 0.],
# [0., 0., 0.]], requires_grad=True)
其它常用的函数
uniform_()
uniform_()函数的作用是填充指定区间的符合均匀分布的随机数,参数未from,to分别代指生成随机数的左区间和右区间,生成的范围是[from,to),一般会跟在使用empty()创建的张量后进行数值填充
import torch
data1 = torch.tensor([1.0, 2.0, 3.0]).uniform_(0, 10)
print(data1)
data2 = torch.empty(2, 3).uniform_(0, 10)
print(data2)
# tensor([8.4342, 0.5614, 3.8917])
# tensor([[0.8433, 2.9977, 7.3362],
# [5.8126, 3.4763, 0.8273]])
注意如果在张量定义时设置了requires_grad为True,我们不能直接使用uniform_进行赋值,需要使用detach()将节点从计算图中剥离再赋值,具体如下所示,可以注意到张量在detach()后会默认将requires_grad设置为False,这是因为detach()在剥离节点后返回的张量是不在计算图上的副本,但数值是与原张量共享内存的,关于detach()的详解后续会更新博客说明。
import torch
data1 = torch.tensor([1.0, 2.0, 3.0], requires_grad=True).detach().uniform_(0, 10)
print(data1)
data2 = torch.empty(2, 3, requires_grad=True).detach().uniform_(0, 10)
print(data2)
# tensor([4.7236, 4.9742, 4.2246])
# tensor([[2.0460, 9.2987, 8.7641],
# [1.2816, 1.2288, 6.4062]])
normal_()
normal_()函数的作用是给张量填充符合特定正态分布的数值,参数有两个,mean和std,分别代表均值和标准差,用法和uniform_()类似,以下样例均以均值为1,标准差为10为例
import torch
data1 = torch.tensor([1.0, 2.0, 3.0]).normal_(1, 10)
print(data1)
data2 = torch.empty(2, 3).normal_(1, 10)
print(data2)
# tensor([ 14.6649, -2.3475, -13.1385])
# tensor([[-2.9050, 7.1260, 6.4159],
# [ 0.3098, 3.1476, -0.3499]])
import torch
data1 = torch.tensor([1.0, 2.0, 3.0], requires_grad=True).detach().normal_(1, 10)
print(data1)
data2 = torch.empty(2, 3, requires_grad=True).detach().normal_(1, 10)
print(data2)
# tensor([-0.1726, 21.7300, 12.0811])
# tensor([[ 5.9375, 14.9223, 18.2401],
# [ 5.1840, 3.2753, -1.3274]])
torch.arange()
torch.arange()的作用就是类似与range()和np.arange(),用于生成一个能指定start,end,step也就是起点,终点,步长,范围为[start,end)
import torch
data1 = torch.arange(0, 10, 2)
print(data1)
# tensor([0, 2, 4, 6, 8])
torch.linspace()
torch.linspace()的作用就是生成一个符合等差数列的张量,lin为linear的缩写,也就是线性的,同样也是包括三个参数start,end,step也就是起点,终点,步长,范围为[start,end],但是这里的步长与arange()不同,这里的步长指的是等差数列中的元素个数
import torch
data1 = torch.linspace(0, 10, 8)
print(data1)
# tensor([ 0.0000, 1.4286, 2.8571, 4.2857, 5.7143, 7.1429, 8.5714, 10.0000])
torch.logspace()
torch.logspace()的作用是生成一个符合等比数列的张量,log为logarithmic的缩写,也就是对数的,同样也是包括三个参数start,end,step,base也就是起点,终点,步长,底数,范围为[base^start,base^end],如果不设置底数的话,base默认为10
import torch
data1 = torch.logspace(0, 10, 8)
data2 = torch.logspace(0, 10, 8, base=2)
print(data1)
print(data2)
# tensor([1.0000e+00, 2.6827e+01, 7.1969e+02, 1.9307e+04, 5.1795e+05, 1.3895e+07,
# 3.7276e+08, 1.0000e+10])
# tensor([1.0000e+00, 2.6918e+00, 7.2458e+00, 1.9504e+01, 5.2501e+01, 1.4132e+02,
# 3.8041e+02, 1.0240e+03])