4.3Tensor的转换、拼接和拆分
- PyTorch提供了大量的对Tensor进行操作的函数方法,这些函数内部使用指针实现对矩阵的形状变换、拼接和拆分等操作,使得我们无须关心Tensor在内存的物理结构或者管理指针就可以方便且快速地执行这些操作。
- Tensor.nelement用来查询矩阵元素的个数
- Tensor.ndimension用来查询轴的个数
- ndimension.size用来查询维度
- 属性Tensor.shape也可以用来查看Tensor的维度
import torch import numpy as np a = torch.rand(1, 2, 3, 4, 5) # 创建了一个形状为(1, 2, 3, 4, 5)的随机张量,并将其赋值给变量a。这个张量的元素是从均匀分布中随机生成的。 print("元素个数", a.nelement()) print("轴的个数", a.ndimension()) print("矩阵维度", a.size(), a.shape) # 矩阵维度和形状
- Tensor.view和Tensor.reshape都能被用来更改Tensor维度,都接收要输出的维度作为参数,且输出的矩阵元素个数不能改变,可以在维度中输入-1,PyTorch会自动推断它的值。
- Tensor.view要求Tensor的物理储存必须是连续的,否则将报错。返回的一定是一个索引。
- Tensor.reshape返回的是引用还是复制是不确定的。
b = a.view(2 * 3, 4 * 5) # 将张量a的形状改变为(2 * 3, 4 * 5),即(6, 20)。view()函数用于改变张量的形状,不改变张量中元素的顺序。 print(b.shape) # 打印输出变量b的形状,即(6, 20)。 c = a.reshape(-1) # 将张量a展平为一维张量,形状为(-1)表示自动计算该维度的大小。reshape()函数用于改变张量的形状,可以指定任意维度的大小 print(c.shape) # 打印输出变量c的形状,即(60,) d = a.reshape(2 * 3, -1) # 将张量a重塑为形状为(2 * 3, -1)的二维张量,即(6, 10)。reshape()函数用于改变张量的形状,可以指定任意维度的大小。 print(d.shape) # 打印输出变量d的形状,即(6, 10)
- torch.squeeze和torch.unsqueeze用于为Tensor去掉和添加轴。其中torch.squeeze用于去掉维度为1的轴,torch.unsqueeze用于给Tensor的指定位置添加一个维度为1的轴
b = torch.squeeze(a) # 对张量a进行压缩操作,移除所有长度为1的维度。squeeze()函数用于压缩张量的维度,将长度为1的维度删除。 print(b.shape) # 打印输出变量b的形状,即压缩后的张量形状。 print(torch.unsqueeze(b, 0).shape) # 在张量b的第0个维度上增加一个维度,形状变为(1, *)。unsqueeze()函数用于在指定位置插入一个新的维度。然后打印输出新张量的形状。
- torch.t和torch.transpose用于转置二维矩阵。这两个函数只接收二维Tensor,torch.t,torch.transpose的简化版。
a = torch.tensor([[2]]) # 创建一个形状为(1, 1)的张量,并将其赋值给变量a b = torch.tensor([[2, 3]]) # 创建一个形状为(1, 2)的张量,并将其赋值给变量b print(torch.transpose(a, 1, 0)) # 对张量a进行转置操作,将第1个维度和第0个维度交换。transpose()函数用于交换张量的维度。然后打印输出转置后的张量 print(torch.t(b)) # 对张量b进行转置操作,将第1个维度和第0个维度交换。t()函数是transpose()函数的简写形式,用于交换张量的维度 print(torch.transpose(b, 1, 0)) print(torch.t(b))
- 对于高维度Tensor,可以使用permute方法来变换维度。
a = torch.rand((1, 224, 224, 3)) # 创建一个形状为(1, 224, 224, 3)的随机张量,并将其赋值给变量a。这个张量的元素是从均匀分布中随机生成的。 print(a.shape) b = a.permute(0, 3, 1, 2) # 对张量a进行维度交换操作,将第0个维度和第3个维度交换,第1个维度和第2个维度交换。permute()函数用于交换张量的维度。然后将其赋值给变量b print(b.shape)
- torch.cat(不会扩展出新的维度)和torch.stack(会在dim的维度上拓展出一个新的维度)用于拼接矩阵。不同之处是,torch.cat在已有的轴dim 上拼接矩阵给定轴的维度可以不同,而其他轴的维度必须相同。torch.stack在新的轴上拼接,它要求被拼接的矩阵的有维度都相同下面的例子可以很清楚地表明它们的使用方式和区别。
a = torch.randn(2, 3) # 创建一个形状为(2, 3)的随机张量,并将其赋值给变量a。这个张量的元素是从标准正态分布中随机生成的。 b = torch.randn(3, 3) # 创建一个形状为(3, 3)的随机张量,并将其赋值给变量b。这个张量的元素也是从标准正态分布中随机生成的。 # 默认维度dim=0 c = torch.cat((a, b)) # 将张量a和b在默认维度(dim=0)上进行拼接,生成一个新的张量c。torch.cat()函数用于在指定维度上拼接张量。 d = torch.cat((b, b, b), dim=1) # 将三个相同的张量b在维度1上进行拼接,生成一个新的张量d。这里通过设置dim=1来指定拼接的维度。 print(c.shape) # 打印输出变量c的形状,即拼接后的张量形状 print(d.shape) # 打印输出变量d的形状,即拼接后的张量形状 c = torch.stack((b, b), dim=1) d = torch.stack((b, b), dim=0) print(c.shape) print(d.shape)
- 除了拼接矩阵,PyTorch 还提供了torch.split和torch.chunk用于拆分矩阵。它们的不同之处在于,tofch.split传入的是拆分后每个矩阵的大小,可以传入list,也可以传人整数,torch.chunk 传入的是拆分的矩阵个数。
a = torch.randn(10, 3) for x in torch.split(a, [1, 2, 3, 4], dim=0): print(x.shape) for x in torch.split(a, 4, dim=0): print(x.shape) for x in torch.chunk(a, 4, dim=0): print(x.shape)
4.4 PyTorch的Reduction操作
- Reduction操作的特点是它往往对一个Tensor 内的元素执行归约操作,比如torch.max找极大值、torch.cumsum计算累加,它还提供了dim参数来指定沿矩阵的哪个维度执行操作。
# 默认求取全局最大值 a = torch.tensor([[1, 2], [3, 4]]) print("全局最大值:", torch.max(a)) # 指定维度dim后,返回最大值及其索引 torch.max(a, dim=0) a = torch.tensor([[1, 2], [3, 4]]) print("沿着横轴计算每一列的累加:") print(torch.cumsum(a, dim=0)) print("沿着纵轴计算每一行的累加:") print(torch.cumprod(a, dim=1)) # 计算矩阵的均值、中值、协方差 a = torch.Tensor([[1, 2], [3, 4]]) print("矩阵的均值:", torch.mean(a)) print("矩阵的中值:", torch.median(a)) print("矩阵的协方差:", torch.var(a)) # torch.unique用来找出矩阵中出现了哪些元素 a = torch.randint(0, 3, (3, 3)) print(a) print(torch.unique(a))
4.5 PyTorch的自动微分(Autgrod)
- 当将Tensor的requires_grad属性设置为True 时,PyTorch的torch.autograd会自动追踪它的计算轨迹。当需要计算微分的时候,只需要对最终计算结果的Tensor调用backward方法,所有计算节点的微分就会被保存在grad属性。
import torch import numpy as np x = torch.arange(10).view(-1, 1) print(x.requires_grad) x = torch.rand(3, 3, requires_grad=True) print(x) w = torch.ones(3, 3, requires_grad=True) y = torch.sum(torch.mm(w, x)) print(y) y.backward() print(y.grad) print(x.grad) print(w.grad)
- Tensor.detach会将Tensor从计算图剥离出去,不再计算它的微分。
x = torch.rand(3, 3, requires_grad=True) w = torch.ones(3, 3, requires_grad=True) print(x) print(w) yy = torch.mm(w, x) detached_yy = yy.detach() y = torch.mean(yy) y.backward() print(yy.grad) print(detached_yy) print(w.grad) print(x.grad)
- with torch.no_grad():包括的代码段不会计算微分。
y = torch.sum(torch.mm(w, x)) print(y.requires_grad) with torch.no_grad(): y = torch.sum(torch.mm(w, x)) print(y.requires_grad)