欢迎关注『youcans的深度学习』系列,持续更新中…
【youcans的深度学习 01】安装环境之 miniconda
【youcans的深度学习 02】PyTorch CPU版本安装与环境配置
【youcans的深度学习 03】PyTorch CPU版本安装与环境配置
【youcans的深度学习 04】PyTorch入门教程:基础知识
【youcans的深度学习 05】PyTorch入门教程:快速入门
【youcans的深度学习 06】PyTorch入门教程:张量的基本操作 1
【youcans的深度学习 07】PyTorch入门教程:张量的基本操作 2
【youcans的深度学习 07】PyTorch入门教程:张量的基本操作 2
- 5. 改变Tensor的形状
- 5.1 张量的阶、轴与形状
- 5.2 张量的形状处理
- 5.2.1 张量的维度交换(transpose)
- 5.2.2 张量形状的重构(reshape)
- 5.2.3 张量形状的重构(view)
- 5.2.4 维度删除(squeeze)和插入(unsqueeze)
- 5.2.5 重新排列张量的维度(permute)
- 6. 张量的索引、切片和连接
- 6.1 张量的索引与切片
- 一维张量的索引
- 二维张量的索引
- 三维张量的索引
- 索引函数 index_select
- 6.2 张量的拆分
- 拆分函数 split
- 拆分函数 chunk
- 6.3 张量的连接
- 张量连接函数 cat
- 张量连接函数 stack
- 7. PyTorch张量与 Numpy/列表/元组的转换
- 7.1 Numpy/列表/元组转换为张量
- 7.2 张量转换为Numpy/列表/元组
- 7.3 提取张量的数值
- 8. 序列化张量
5. 改变Tensor的形状
本节深入讨论张量的三个基本属性:阶(Rank)、轴(Axis)和形状(Shape),在此基础上讨论如何改变张量的形状。
5.1 张量的阶、轴与形状
- 张量的阶(Rank)是指张量中的维数。
张量的阶告诉我们访问(引用)张量数据结构中的特定数据元素需要多少个索引。
A tensor’s rank tells us how many indexes are needed to refer to a specific element within the tensor.
例如,一个二阶张量,对应于一个二维数组、二维矩阵。
- 张量的轴(axis)是张量的某个特定的维度。
An axis of a tensor is a specific dimension of a tensor.
张量中的元素被认为是存在并且在某个轴上运动,并受到该轴的长度的限制。换句话说,每个轴的长度,表示沿着这个轴有多少个索引。
对于 n维张量,沿着第一个轴 Axis0 的每个元素是一个 n-1 维数组,沿着第二个轴 Axis1 的每个元素是一个 n-2 维数组,…,沿着最后一个轴 的元素是数字。
二维张量的轴
例如,一个二阶张量有两个维度,这个张量有两个轴。
t = torch.tensor([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
print("t.ndim:", t.ndim) # 张量的维数,t.ndim: 2
print("t.shape:", t.shape) # 张量的形状,t.shape: torch.Size([3, 4])
二维张量 t 的第一个轴 Axis0 的长度为 3,因此沿着第一个轴可以索引 3个位置:
t[0], t[1], t[2]
二维张量 t 的沿着第一个轴 Axis0 的每个元素是一个一维张量(数组):
Axis0:
t[0]: tensor([1, 2, 3, 4])
t[1]: tensor([5, 6, 7, 8])
t[2]: tensor([ 9, 10, 11, 12])
t 的第二个轴 Axis1 的长度为4,因此沿着第二个轴可以索引 4个位置:
t[0][0], t[1][0], t[2][0]
t[0][1], t[1][1], t[2][1]
t[0][2], t[1][2], t[2][2]
t[0][3], t[1][3], t[2][3]
二维张量 t 的沿着第二个轴 Axis1 的每个元素是一个 0维张量(数字):
Axis1:
t[0][0]: tensor(1)
t[0][3]: tensor(4)
t[2][3]: tensor(12)
三维张量的轴
三维张量的第一个轴 Axis0 表示的是“深度”或者“高度”(z方向),第二个轴 Axis1 表示的是“长度”(x方向),第三个轴 Axis2 表示的是“宽度”(y方向)。
因此,三维张量的结构是多个二维张量从上到下叠放而成
B = torch.tensor([[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]], # B[0]
[[4, 3, 2, 1],
[8, 7, 6, 5],
[12, 11, 10, 9]]]) # B[1]
三维张量 B 的第一个轴 Axis0 的长度为 2,因此沿着第一个轴可以索引 2个位置:
B[0], B[1]
三维张量 B 的沿着第一个轴 Axis0 的每个元素是一个二维张量:
Axis0:
B[0]:
tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
B[1]:
tensor([[ 4, 3, 2, 1],
[ 8, 7, 6, 5],
[12, 11, 10, 9]])
类似地,四维张量的结构是多个三维张量从上到下叠放而成,N维张量的结构是多个(N-1)维张量从上到下叠放而成。所谓从上到下叠放是相对的,取决于第一个轴 Axis0 的观察角度。但是, PyTorch 张量在输出时是按照从上到小的竖直方向排列,所以选择从上到下 的方向作为第一个轴 Axis0 的观察角度比较直观。
- 张量的形状(shape)描述了每个轴的长度(元素数),也可以说张量的形状由每个轴的长度决定。
The shape of a tensor gives us the length of each axis of the tensor.
张量的形状不仅给出了张量的每个轴的长度,而且可以获得有关轴、秩以及索引的相关信息。在 PyTorch 中,张量的大小和形状是一样的。使用 shape
或 size()
可以查看张量的形状。
注意张量形状的维度是按第一轴 Axis0 向第(N-1)轴 Axis(N-1) 排列的,dim=0 表示第一轴,dim=-1 表示最后一轴。因此,任何一个二维以上的 PyTorch 张量,dim=-1 都表示 y 轴(最后一个维度),dim=-2 都表示 x 轴(倒数第二个维度)。例如,torch.Size([2, 3, 4] 表示张量的第一轴长度为 2、第二轴长度为 3、第三轴长度为 4。
print("B.ndim:", B.ndim) # 张量的维数,B.ndim: 3
print("B.shape:", B.shape) # 张量的形状,Bshape: torch.Size([2, 3, 4])
例程:
# (16) 张量的阶(Rank)、轴(Axis)和形状(Shape)
# 二维张量
t = torch.tensor([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
print("t.ndim:", t.ndim) # 张量的维数,t.ndim: 2
print("t.shape:", t.shape) # 张量的形状,t.shape: torch.Size([3, 4])
print("Axis0:", t[0], t[1], t[2])
print("Axis1:", t[0][0], t[0][3], t[2][3])
# 三维张量
B = torch.tensor([[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]], # B[0]
[[4, 3, 2, 1],
[8, 7, 6, 5],
[12, 11, 10, 9]]]) # B[1]
print("B.ndim:", B.ndim) # 张量的维数,B.ndim: 3
print("B.shape:", B.shape) # 张量的形状,Bshape: torch.Size([2, 3, 4])
print("Axis0:")
print(B[0])
print(B[1])
5.2 张量的形状处理
PyTorch 中的张量形状处理方法,是指改变张量的维度而不改变元素的数值。常用方法有:torch.reshape,torch.view,torch.repeat,torch.expand,torch.permute,torch.transpose。
方法 | 说明 |
---|---|
torch.reshape(input, shape) | 改变 input 的维度为 shape (如果适合),也可以使用 torch.Tensor.reshape() . |
torch.Tensor.view(shape) | 张量的维度变为 shape ,返回的张量与原始张量共享数据。 |
torch.stack(tensors, dim=0) | tensors 沿新维度 (dim )连接一系列序列,所有序列的tensors 大小必须相同。 |
torch.squeeze(input) | 移除input 维度为1 的行或者列。 |
torch.unsqueeze(input, dim) | 添加 input 维度为1 的行或者列。 |
torch.permute(input, dims) | input 的维度根据 dims 重新排列,若赋予新的张量与原始张量共享数据 |
5.2.1 张量的维度交换(transpose)
transpose 方法用于对张量进行维度交换。
对于多维张量,使用函数 torch.transpose
交换指定的维度。
torch.transpose(input, dim0, dim1) → Tensor
其中,input 表示张量,dim0、dim1 表示交换的张量维度(第几轴)。
# (17) 张量的转置 transpose (变换给定张量的维度)
X3d = torch.tensor([[[1, 2, 3, 4], # 三维张量
[5, 6, 7, 8],
[9, 10, 11, 12]], # B[0]
[[4, 3, 2, 1],
[8, 7, 6, 5],
[12, 11, 10, 9]]]) # B[1]
Xt1 = torch.transpose(X3d, 0, 1) # 交换 Axis0 与 Axis1
Xt2 = torch.transpose(X3d, 0, 2) # 交换 Axis0 与 Axis2
Xt3 = torch.transpose(X3d, 1, 2) # 交换 Axis1 与 Axis2
print("X3d.shape:", X3d.shape) # shape of X3d: torch.Size([2, 3, 4])
print("Xt1.shape:", Xt1.shape) # shape of Xt1: torch.Size([3, 2, 4])
print("Xt2.shape:", Xt2.shape) # shape of Xt2: torch.Size([4, 3, 2])
print("Xt3.shape:", Xt3.shape) # shape of Xt3: torch.Size([2, 4, 3])
对于二维张量,只有两个维度 dim0 和 dim1,既可以使用函数 torch.transpose
交换 dim0 与 dim1,也可以使用 troch.t(input)
或 input.T
实现矩阵转置。
X2d = torch.tensor([[1, 2, 3, 4], # 三维张量
[5, 6, 7, 8],
[9, 10, 11, 12]])
print("X2d.shape:", X2d.shape) # 张量的形状,X2d.shape: torch.Size([3, 4])
Xt = torch.t(X2d) # 等价于:Xt = X2d.T
print("Xt.ndim:", Xt.ndim) # 张量的维数,Xt.ndim: 2
print("Xt.shape:", Xt.shape) # 张量的形状,Xt.shape: torch.Size([4, 3])
5.2.2 张量形状的重构(reshape)
函数 torch.reshape 返回将输入张量转变为指定的形状大小,元素总数不变。可以理解为将张量压扁为一维后再按指定形状重构。
torch.reshape(input, shape) → Tensor
其中,input 表示张量,shape 表示重构张量的形状。如果 shape 中的一个参数指定为 -1,代表自动调整这个维度上的元素个数,以保证元素的总数不变。
返回的张量将尽可能是输入的视图,否则才会返回输入的拷贝。注意:这里很容易由于混淆“视图”与“拷贝”而发生错误,推荐编程时先强制使用“复制”,调试通过后再考虑优化内存。
连续输入和步幅兼容的输入可以在不复制的情况下重新整形,但不应依赖于复制与查看行为。
# (18) 张量的形状重构
X3d = torch.tensor([[[1, 2, 3, 4], # 三维张量
[5, 6, 7, 8],
[9, 10, 11, 12]], # B[0]
[[4, 3, 2, 1],
[8, 7, 6, 5],
[12, 11, 10, 9]]]) # B[1]
X1 = torch.reshape(X3d, (3, 4, 2))
print(X1)
print("X1.shape:", X1.shape) # shape of X1: torch.Size([3, 4, 2])
X2 = torch.reshape(X3d, (3, 8))
print(X2)
print("X2.shape:", X2.shape) # shape of X2: ([3, 8])
X3 = torch.reshape(X3d, (-1,))
print(X3)
print("X3.shape:", X3.shape) # shape of X3: torch.Size([24])
5.2.3 张量形状的重构(view)
函数 torch.Tensor.view
返回一个新张量,将输入张量转变为指定的形状大小,元素总数不变。可以理解为将张量压扁为一维后再按指定形状重构,相当于 reshape 操作。
torch.Tensor.view(*shape) → Tensor
其中,Tensor 表示张量,shape 表示重构张量的形状。
返回的张量与输入张量必须具有相同数量的元素,但可能具有不同的形状。返回张量的形状,必须与输入张量的形状(size)和步长(stride)兼容。如果 shape 中的一个参数指定为 -1,代表自动调整这个维度上的元素个数,以保证元素的总数不变。
注意,返回的张量与输入张量共享内存,因此更改其中一个张量的内容也会改变另一个张量的内容。如果需要不共享内存,返回一个真正新的副本,可以先使用 clone() 创造一个副本然后再使用 view() 。使用 clone() 还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源 Tensor。
s
t
r
i
d
e
[
i
]
=
s
t
r
i
d
e
[
i
+
1
]
×
s
i
z
e
[
i
+
1
]
stride[i]=stride[i+1]×size[i+1]
stride[i]=stride[i+1]×size[i+1]
5.2.4 维度删除(squeeze)和插入(unsqueeze)
函数 torch.squeeze() 用于删除长度为 1 的维度,函数 torch.unsqueeze() 用于在指定位置插入大小为 1 的维度。
torch.squeeze(input, dim=None, *, out=None) → Tensor
torch.unsqueeze(input, dim) → Tensor
其中,tensor 表示输入的张量,dim 表示指定的位置。
函数 torch.squeeze() 默认将 input 中所有长度为 1 的维度删掉;也可以通过 dim 指定位置,删掉指定位置的维数为 1的维度。例如,input 的形状为 (A, 1, B),squeeze(input) 返回结果的形状是 (A, B),squeeze(input, 1) 返回结果的形状也是 (A, B),而 squeeze(input, 0) 返回结果的形状仍是 (A, 1, B),
注意:
(1)返回的张量与输入张量共享内存,因此更改其中一个张量的内容也会改变另一个张量的内容。
(2)对于批量张量,如果批量轴(batch axis)的长度为 1(batch=1),维度删除操作也会删除批量维度(batch dimension),可能会导致错误。
# (19) 张量的维度删除和维度插入
x = torch.zeros(2, 1, 3, 4, 1)
y1 = torch.squeeze(x)
y2 = torch.squeeze(x, 0)
y3 = torch.squeeze(x, 1)
print(x.shape) # torch.Size([2, 1, 3, 4, 1])
print(y1.shape) # torch.Size([2, 3, 4])
print(y2.shape) # torch.Size([2, 1, 3, 4, 1])
print(y3.shape) # torch.Size([2, 3, 4, 1])
z1 = torch.unsqueeze(y1, 0)
z2 = torch.unsqueeze(y1, 1)
print(z1.shape) # torch.Size([1, 2, 3, 4])
print(z2.shape) # torch.Size([2, 1, 3, 4])
5.2.5 重新排列张量的维度(permute)
函数 torch.permute() 用于重新排列张量的维度。
torch.permute(input, dims) → Tensor
其中,tensor 表示输入的张量,dims 表示所需的维度及长度。
dims 是元组,表示张量的维数,元组元素依次是第0维长度、第1维长度、等等…。
# (20) 重新排列张量的维度
x = torch.randint(0, 10, size=(2, 3, 5)) # 均匀分布随机整数,[0, 10) 区间
print(x)
print(x.shape) # torch.Size([2, 3, 5])
y = torch.permute(x, (2, 0, 1))
print(y)
print(y.shape) # torch.Size([5, 2, 3])
6. 张量的索引、切片和连接
6.1 张量的索引与切片
索引是指访问张量中的唯一元素。张量是有序序列,可以根据每个元素在系统内的顺序位置,访问特定的元素,这就是索引 。
张量的索引方式与 Python 中的列表索引方式类似,可以使用整数、切片、布尔值和其他张量来进行索引。例如,对于一个二维张量,可以使用两个整数来访问其中的元素,如 tensor[0][1]
。
切片是针对某个维度方向下标访问张量, 通过切片可以一次访问多个顺序的元素。每个维度方向上都可以进行各自独立的切片访问,最终得到分布在不同维度方向上的多个张量元素。例如,对于一个二维张量,可以使用切片来访问张量中的元素,如 tensor[0:2, 0:2]
。
切片的一般形式是 [start: end: step]
,通过三个参数和冒号来定义切片的方式。其中:
- start 是闭合区间即包含 start 索引的元素,end 是开区间即不包含 end 索引的元素。
- start 如果缺省则默认为 0,end 如果缺省则默认为 len(tensor)。
- 在 PyTorch 中 step 必须是正整数,不允许负数步长。
一维张量的索引
一维张量索引是从左到右,从 0 开始的。
# (21) 张量的索引与切片
# 一维张量的索引
t1 = torch.arange(10)*2
print(t1) # tensor([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
# 访问索引位置为 0 的元素
print(t1[1]) # tensor(2),零维张量
print(t1[1].item()) # 2,取出零维张量的值
# 一维张量的切片
print(t1[0:2]) # tensor([0, 2])
print(t1[:2]) # tensor([0, 2])
print(t1[::2]) # tensor([ 0, 4, 8, 12, 16])
特别注意:
- 张量元素的索引是零维张量,而不是一个数值!要得到张量元素索引的数值,需要使用 item()方法。
- 张量索引的结果与原始的张量共享内存,修改原始张量或张量索引中一个的值,则另一个也会被修改。
- 注意
t1[:2]
是对第 0 列到 第 1 列进行切片,而t1[::2]
是对所有列以 2 为步长进行切片。
二维张量的索引
二维张量索引的逻辑与一维张量索引相同。形状为 (nx, ny) 的二维张量,可以理解为由 nx 个一维张量构成,每个一维张量由 ny 个元素构成 。
对于一个二维张量,可以使用两个整数来访问其中的元素,如 tensor[0][1]
表示索引第 0 行、第 1 列的元素。
# 二维张量的索引
t2 = torch.arange(16).reshape(4, 4)
print(t2[0, 1]) # tensor(1)
print(t2[0][1]) # tensor(1)
print(t2[0, 1].item()) # 1
# 二维张量的切片
print(t2[::2, ::2]) # tensor([[0, 2], [8, 10]])
print(t2[::2][::2]) # tensor([[0, 1, 2, 3]])
print(t2[::2]) # tensor([[0, 1, 2, 3], [8, 9, 10, 11]])
print((t2[::2])[::2]) # tensor([[ 0, 1, 2, 3]])
特别注意:
-
二维张量元素的索引
t2[0][1]
,也可以写成t2[0,1]
,结果相同,都是零维张量。 -
但是,
t2[::2][::2]
与t2[::2, ::2]
切片的结果不同。二维切片t2[::2, ::2]
使用逗号隔开时,可以理解为对二维张量的全局索引,取隔行隔列的元素。二维切片t2[::2][::2]
在两个中括号中时,可以理解为对二维张量的两次隔行切片(t2[::2])[::2]
,先取隔行切片构成一个新的张量(t2[::2])
,又对新的张量(t2[::2])
再进行隔行索引,因此切片的结果是张量 t2 的第 0 行。
三维张量的索引
形状为 (nx, ny, nz) 三维张量,可以理解为由 nx 个二维张量构成,每个二维张量由 ny 个一维张量构成,每个一维张量由 nz 个元素构成 。
# 三维张量的索引和切片
t3 = torch.arange(27).reshape(3, 3, 3)
print(t3[0, 1, 2]) # tensor(5)
print(t3[0, 1, 2].item()) # 5
print(t3[0, ::2, ::2]) # tensor([[0, 2], [6, 8]])
print(t3[0, :, ::2]) # tensor([[0, 2], [3, 5], [6, 8]])
索引函数 index_select
PyTorch 还提供了函数 torch.index_select
来对张量进行索引。
torch.index_select(input, dim, index, *, out=None) → Tensor
参数说明:
input,张量,要索引的张量
dim,整数,要索引的维度
index,整形张量,包括索引序号的一维张量
函数 index_select() 表示在张量的哪个维度进行索引,索引的位值是多少,返回依 index 索引数据拼接的张量。
# 张量的索引函数
x = torch.arange(12).reshape(3, 4)
indices = torch.tensor([0, 2])
t1 = torch.index_select(x, 0, indices)
t2 = torch.index_select(x, 1, indices)
print(x) # tensor([[0, 1, 2, 3], [4, 5, 6, 7], [ 8, 9, 10, 11]])
print(t1) # tensor([[0, 1, 2, 3], [ 8, 9, 10, 11]])
print(t2) # tensor([[0, 2], [4, 6], [8, 10]])
6.2 张量的拆分
拆分函数 split
PyTorch 提供了函数 torch.split
将张量拆分为块,每个块都是输入张量的一个视图。
torch.split(tensor, split_size_or_sections, dim=0) → List[Tensor]
参数说明:
- tensor,张量,要拆分的张量
- split_size_or_sections,整数类型,表示单个块的大小;或整型列表,表示每个块大小的列表。
- dim,整数,拆分张量的维度
- List[Tensor],返回值,张量的列表
函数说明:
- split_size_or_sections 是整数类型时,则将张量按整数拆分为大小相等的块,不能整除时最后一块将更小。
- split_size_or_sections 是整型列表时,则按列表长度将张量拆分为若干块,每块大小是对应的列表元素值。
# (22) 张量的拆分
x = torch.arange(10).reshape(5, 2)
s1 = torch.split(x, 2)
s2 = torch.split(x, [2, 3])
print(x)
# tensor([[0, 1],
# [2, 3],
# [4, 5],
# [6, 7],
# [8, 9]])
print(s1)
# (tensor([[0, 1], [2, 3]]),
# tensor([[4, 5], [6, 7]]),
# tensor([[8, 9]]))
print(s2)
# (tensor([[0, 1], [2, 3]]),
# tensor([[4, 5], [6, 7], [8, 9]]))
拆分函数 chunk
PyTorch 提供了函数 torch.chunk
将张量拆分为指定数量的块,每个块都是输入张量的一个视图。
torch.chunk(input, chunks, dim=0) → List[Tensor]
参数说明:
- input,张量,要拆分的张量
- chunk,整数,表示返回的块数。
- dim,整数,拆分张量的维度
- List[Tensor],返回值,张量的列表
函数说明:
- 如果沿着维度 dim 的张量大小可以被块数 chunk 整除,那么所有块的大小相同。
- 如果沿着维度 dim 的张量大小不能被块数 chunk 整除,那么最后一块将更小,其它所有块都的大小相同。
- 如果无法按以上规则拆分,则函数返回的块数可能少于指定的块数 chunk。
# 张量拆分函数 chunk
t1 = torch.arange(10)
print(t1.chunk(3))
# (tensor([0, 1, 2, 3]), tensor([4, 5, 6, 7]), tensor([8, 9]))
print(t1.chunk(4))
# (tensor([0, 1, 2]), tensor([3, 4, 5]), tensor([6, 7, 8]), tensor([9]))
print(t1.chunk(5))
# (tensor([0, 1]), tensor([2, 3]), tensor([4, 5]), tensor([6, 7]), tensor([8, 9]))
6.3 张量的连接
函数 torch.cat
和函数 torch.stack
都可以实现张量的连接,区别在于是否产生新的维度。函数 torch.cat
不产生新的维度,相当于张量堆叠;而函数 torch.stack
可以在新的维度上进行拼接,产生高维张量。
张量连接函数 cat
函数 torch.cat
沿着指定维度 dim 对输入的张量序列进行连接。
torch.cat(tensors, dim=0, *, out=None) → Tensor
参数说明:
- tensors,张量序列(列表或元组),要连接的张量
- dim,整数,张量连接的维度,可选项,默认为 0
函数说明:
- 张量序列中所有非空的张量,进行连接的维度 dim 的大小可以不同,其它维度的形状必须相同。
- 函数
torch.cat
是函数torch.chunk
的反向操作。
# (23) 张量的连接函数
# 张量连接函数 cat
t1 = torch.arange(6).reshape(2, 3)
t2 = torch.arange(10, 13).reshape(1, 3)
c1 = torch.cat((t1, t1), dim=0)
print(c1)
# tensor([[0, 1, 2],
# [3, 4, 5],
# [0, 1, 2],
# [3, 4, 5]])
c2 = torch.cat((t1, t1, t1), dim=1)
print(c2)
# tensor([[0, 1, 2, 0, 1, 2],
# [3, 4, 5, 3, 4, 5]])
c3 = torch.cat((t1, t2), dim=0)
print(c3)
# tensor([[ 0, 1, 2],
# [ 3, 4, 5],
# [10, 11, 12]])
# c4 = torch.cat((t1, t2), dim=1) # 报错, dim=1 维度不一致
张量连接函数 stack
函数 torch.stack
沿着一个新维度对输入的张量序列进行连接。
torch.stack(tensors, dim=0, *, out=None) → Tensor
参数说明:
- tensors,张量序列(列表或元组),要连接的张量
- dim,整数,插入的维度,0~len(out) 之间的整数
函数说明:
- 张量序列中所有张量的形状必须相同。
- 将张量序列在增加的新维度 dim 进行堆叠,例如把多个二维张量拼接为一个三维张量,多个三维张量拼接为一个四维张量。
# 张量连接函数 stack
x1 = torch.tensor([[1, 2, 3], [4, 5, 6]])
x2 = torch.zeros_like(x1)
x3 = torch.ones_like(x1)
print(x1.shape) # torch.Size([2, 3])
xStack1 = torch.stack((x1, x2), dim=0)
print(xStack1.shape) # torch.Size([2, 2, 3])
print(xStack1)
xStack2 = torch.stack((x1, x2), dim=1)
print(xStack2.shape) # torch.Size([2, 2, 3])
print(xStack2)
print(torch.stack((x1, x2, x3), dim=2).shape) # torch.Size([2, 3, 3])
# print(torch.stack((x1, x2), dim=3).shape) # IndexError: Dimension out of range
结果如下:
x1.shape: torch.Size([2, 3])
xStack1.shape: torch.Size([2, 2, 3])
tensor([[[1, 2, 3],
[4, 5, 6]],
[[0, 0, 0],
[0, 0, 0]]])
xStack2.shape: torch.Size([2, 2, 3])
tensor([[[1, 2, 3],
[0, 0, 0]],
[[4, 5, 6],
[0, 0, 0]]])
xStack3.shape: torch.Size([2, 3, 3])
7. PyTorch张量与 Numpy/列表/元组的转换
7.1 Numpy/列表/元组转换为张量
可以将 Numpy 数组、列表 list 或元组 tuple 转换为张量。
# (24) 将 Numpy 数组、列表 list 或元组 tuple转换为张量
# Numpy 数组转换为张量
xnp = np.array([[1, 2, 3], [4, 5, 6]])
x1 = torch.tensor(xnp)
print(x1)
x2 = torch.from_numpy(xnp)
print(x2)
# list 列表转换为张量
xlist = [[1, 2, 3], [4, 5, 6]]
x2 = torch.tensor(xlist)
print(x2)
# tuple 元组转换为张量
xtuple = ((1, 2, 3), (4, 5, 6))
x3 = torch.tensor(xtuple)
print(x3)
输出为:
tensor([[1, 2, 3],
[4, 5, 6]], dtype=torch.int32)tensor([[1, 2, 3],
[4, 5, 6]], dtype=torch.int32)tensor([[1, 2, 3],
[4, 5, 6]])
tensor([[1, 2, 3],
[4, 5, 6]])
torch.tensor(xnp) 与 torch.from_numpy(xnp) 都能将 Numpy 数组转换为张量,但 torch.from_numpy() 可以自动识别并保留 Numpy 数组的数据类型。
7.2 张量转换为Numpy/列表/元组
函数 torch.numpy() 用于将张量转换为 Numpy 数组,函数 torch.tolist() 用于将张量转换为 list 列表。
# (25) 张量转换为Numpy/列表/元组
X = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 张量转换为 Numpy 数组
xnp = X.numpy()
print(type(xnp)) # <class 'numpy.ndarray'>
print(xnp) # [[1 2 3], [4 5 6]]
# 张量转换为 list 列表
xlist = X.tolist()
print(type(xlist)) # <class 'list'>
print(xlist) # [[1, 2, 3], [4, 5, 6]]
# 张量转换为 list 列表
Xlist = list(X)
print(type(Xlist)) # <class 'list'>
print(Xlist) # [tensor([1, 2, 3]), tensor([4, 5, 6])]
注意:由于 Numpy 数组不支持 GPU 运算,要把 GPU 上创建的张量转换为 Numpy 数组,需要先通过 to('cpu')
或 cpu()
函数把 GPU 上的张量转移到 CPU 上,然后再分离数据结构。
7.3 提取张量的数值
函数 torch.item() 用于将 0 维张量转换为数值,此方法只能用于只有一个元素的 0 维张量,通常用于输出最终的计算结果。
# 0 维张量转换为 数值
tout = torch.tensor(6) # 仅用于只有一个元素的张量
print("tout =", tout.item()) # tout = 6
8. 序列化张量
序列化张量是指保存某个时序的张量到文件中,在后面可以装载到程序中。
PyTorch 在内部使用 pickle 来序列化张量对象,并为存储添加专用的序列化代码。
通过下述方法可以将张量 points 保存到 ourpoints.t 文件中:
# (26) 序列化张量
# Method 1
torch.save(points, './ourpoints.t')
# Method 2
with open('./ourpoints.t', 'wb') as f:
torch.save(points, f)
从 ourpoints.t 中读取:
# Method 1
points = torch.load('./ourpoints.t')
# Method 2
with open('./ourpoints.t', 'rb') as f:
points = torch.load(f)
上述保存张量的方法很简单,但是对于 .t 的文件,只能通过 PyTorch 打开。
我们也可以使用 h5py 来序列化到 HDF5,但需要安装 h5py。
conda install h5py
使用方法如下。
import h5py
f = h5py.File('ourpoints.hdf5', 'w')
dset = f.create_dataset('coords', data=points.numpy())
f.close()
HDF5 的优势是可以在磁盘上索引数据集,并且只访问我们需要的元素。
例如只想要 points 中最后两个点的坐标,例程如下:
f = h5py.File('ourpoints.hdf5', 'r')
dset = f['coords']
last_points = dset[-2:]
last_points
'''
array([[5., 3.],
[2., 1.]], dtype=float32)
'''
last_points = torch.from_numpy(dset[-2:])
f.close()
last_points
'''
tensor([[5., 3.],
[2., 1.]])
'''
【本节完】
版权声明:
欢迎关注『youcans的深度学习』系列,转发请注明原文链接:
【youcans的深度学习 06】PyTorch入门教程:张量的基本操作 2 (https://youcans.blog.csdn.net/article/details/130564877)
Copyright 2023 youcans, XUPT
Crated:2023-05-08