张量:多维数组
张量是PyTorch中基本的数据结构。张量是一个数组,也就是一种数据结构,它存储了一组数字,这些数字可以用一个索引单独访问,也可以用多个索引访问。
构造第1个张量
import torch # 导入模块
a=torch.ones(3) # 创建一个大小为3的一维张量,用1.0填充
print(a)
print(a[1])
print(float(a[1]))
a[2]=2.0
print(a)
# 输出结果
tensor([1., 1., 1.])
tensor(1.)
1.0
tensor([1., 1., 2.])
可以使用从0开始的索引来访问一个元素,或者给它指定一个新值。
张量的本质
假设有一个坐标列表,我们想用它来表示一个几何对象:如一个二维三角形,顶点坐标为(4,1)、(5,3)和(2,1)。
'''
我们可以使用一维张量,将x轴坐标存储在偶数索引中,将y轴坐标存储在奇数索引中
'''
p=torch.zeros(6)# 使用zeros()函数只是获得适当大小的数组的一种方法
p[0]=4.0
p[1]=1.0
p[2]=5.0
p[3]=3.0
p[4]=2.0
p[5]=1.0
'''
还可以向构造函数传递一个Python列表达到同样的效果
'''
pp=torch.tensor([4.0,1.0,5.0,3.0,2.0,1.0])
print((float(p[0]),float(p[1]))) #输出第一个点的坐标
'''
尽管将第1个索引指向单独的二维点而不是点坐标是可行的,但对于这种情况,我们可以用一个二维张量
'''
ppp=torch.tensor([[4.0,1.0],[5.0,3.0],[2.0,1.0]])
print(ppp)
print(ppp.shape) # 查看张量形状
print(ppp[0,1])# 可以用2个索引来访问张量中的单个元素
print(ppp[0]) # 也可以访问张量中的第1个元素,得到第1个点的二维坐标
索引张量
如果需要得到一个张量除第1个点以外的所有点呢?使用范围索引表示法很容易实现,改方法也适用于标准的Python列表。
some_list=list(range(6))
some_list[:] #列表中所有元素
some_list[1:4]# 包含第1个元素到第3个元素,不包含第4个元素
some_list[1:] # 包含第1个元素到列表末尾元素
some_list[:4]# 从列表开始到第3个元素,不包含第4个元素
some_list[:-1]# 从列表开始到最后一个元素之前的所有元素
some_list[1:4:2]# 从第1个元素(包含)到第4个元素(不包含),移动步长为2
p[1:]# 第1行之后得所有行,隐含所有列
p[1:,:]# 第1行之后得所有行,所有列
p[1:,0]#第1行之后得所有行,第1列
p[None]# 增加大小为1的维度,就像unsqueeze()方法一样
张量的元素类型
使用dtype指定数字类型
张量构造函数通过dtype参数指定包含在张量中的数字数据类型,如tensor(),zeros()和ones()函数数据类型用于指定张量可以保存的可能值(整数与浮点数)以及每个值的字节数。张量的默认数据类型是32位浮点数
适合任何场合的dtype
在神经网络中发生的计算通常是用32位浮点精度执行的。采用更高的精度,如64位,并不会提高模型精度,反而需要更多的内存和计算时间。16位半精度浮点数的数据类型在标准cpu中并不存在,而是由现代GPU提供的。如果需要的话,可以切换到半精度来减少神经网络占用的空间,这样做对精度的影响也很小。
管理张量的dtype属性
'''
为了给张量分配一个正确的数字类型,我们可以指定适当的dtype作为构造函数的参数
'''
double_points=torch.ones(10,2,dtype=torch.double)
short_points=torch.tensor([[1,2],[3,4]],dtype=torch.short)
short_points.dtype# 通过访问相应的属性来了解一个张量的dtype值
double_points=torch.zeros(10,2).double()# 可以使用相应的转换方法将张量创建函数的输出转换为正确的类型
short_points=torch.ones(10,2).short()
double_points=torch.zeros(10,2).to(torch.double) #或者用更方便的方法
在操作中输入多种类型时,输入会自动向较大类型转换
张量的存储视图
张量中的值被分配到由trorch.Storage实例所管理的连续内存块中。存储区是由数字数据组成的一维数组,即包含给定类型的数字的连续内存块。
索引存储区
可以使用storage()访问给定张量的存储区:
points=torch.tensor([[4.0,1.0],[5.0,3.0],[2.0,1.0]])
print(points.storage())
输出:
4.0
1.0
5.0
3.0
2.0
1.0
[torch.FloatStorage of size 6]
尽管张量显示自己有3行2列,但底层的存储区是一个大小为6的连续数组。
可以手动索引存储区:
points_storage=points.storage()
points_storage[0]
points.storage()[1]
不能用2个索引来索引二维张量的存储区。不管和存储区关联的其他张量的维度是多少,它的布局始终是唯一的。
修改存储值:就地操作
有少量操作仅作为Tensor对象的方法存在。这些操作可以从名称结尾的下划线识别出来,如zero_(),这表明改方法通过修改输入张量,而不是创建一个新的输出张量,然后返回新创建的输出张量。
张量元数据:大小、偏移量和步长
为了在存储区建立索引,张量依赖于一些明确定义它们的信息:大小(size)、偏移量(offset)和步长(stride)。
大小是一个元组,表示张量在每个维度上有多少个元素。
偏移量是指存储区中某元素相对张量中的第1个元素的索引。
步长是指存储区中为了获得下一个元素需要跳过的元素数量。
second_point=points[1]
second_point.storage_offset() #得到的张量在存储区中的偏移量为2,这是因为我们需要跳过第1个点,该点有两个元素
second_point.size()
second_point.shape
second_point[0]=10.0
print(points)# 更改子张量会对原始张量产生影响
second_point=points[1].clone()# 可以把这子张量克隆成一个新的张量
second_point[0]=10.0
'''
转置不会分配新的内存,只是创建一个新Tensor实例,该实例具有与原始张量不同的步长顺序
'''
points_t=points.t() #t()是用于二维张量转置的transpose()方法的简写
id(points.storage())==id(points_t.storage())# 可以验证2个张量共享同一个存储区
points.stride()
points_t.stride() #它们两在形状和步长上不一置
将张量存储到GPU
除了dtype,PyTorch张量还有设备(device)的概念,即张量数据在计算机上的位置。
points_gpu=torch.tensor([[4.0,1.0],[5.0,3.0],[2.0,1.0]],device='cuda')# 在gpu上创建了一个张量
points_gpu=points.to(device='cuda')# 用to()方法将在cpu上创建的张量复制到gpu上
points_gpu=points.to(device='cuda:0')#确定多个gpu下存储张量的gpu
NumPy互操作性
'''
从张量到数组
'''
points=torch.ones(3,4)
points_np=points.numpy()# 返回一个大小、形状和数字类型都与代码对应的NumPy多维数组
# 如果张量是在gpu上存储的,PyTorch将把张量的内容复制到cpu上分配的numpy数组中
'''
从数组到张量
'''
points=torch.from_numpy(points_np)