带你从入门到精通——深度学习(三. PyTorch中张量的形状重塑、拼接和自动微分)

建议先阅读我之前的深度学习博客,掌握一定的深度学习前置知识后再阅读本文,链接如下:

带你从入门到精通——深度学习(一. 深度学习简介和PyTorch入门)-优快云博客

带你从入门到精通——深度学习(二. PyTorch中的类型转换、运算和索引)-优快云博客

目录

三. PyTorch中张量的形状重塑、拼接和自动微分

3.1 张量的形状重塑

3.1.1 transpose函数和permute函数

3.1.2 reshape函数和view函数

3.1.3 squeeze函数和unsqueeze函数

3.2 张量的合并

3.2.1 张量的拼接

3.2.2 张量的堆叠

3.3 自动微分模块

3.3.1 计算图

3.3.2 静态计算图和动态计算图

3.3.3 自动微分模块示例


三. PyTorch中张量的形状重塑、拼接和自动微分

3.1 张量的形状重塑

3.1.1 transpose函数和permute函数

        transpose函数permute函数都可以交换张量的维度,但transpose函数一次只能交换两个维度,而permute函数可以一次性交换多个维度(必须传入所有维度,并封装到一个元组中),具体使用方法如下:

import torch
torch.random.manual_seed(0)
t1 = torch.randint(1, 9, (2, 3, 4))
print(t1.shape) # torch.Size([2, 3, 4])
t2 = t1.transpose(0, 1)
print(t2.shape) # torch.Size([3, 2, 4])
t3 = t1.permute((2, 0, 1))
print(t3.shape) # torch.Size([4, 2, 3])

3.1.2 reshape函数和view函数

        reshape函数view函数可以在保证张量数据不变的前提下,将其转换成指定的形状(数据排列遵循从左到右、从上到下的原则),具体使用方法如下:

import torch
torch.random.manual_seed(0)
t1 = torch.randint(1, 9, (2, 3, 4))
t2 = t1.reshape(2, -1)
print(t2.shape) # torch.Size([2, 12])
t3 = t1.view(6, 1, 4)
print(t3.shape) # torch.Size([6, 1, 4])

        注意:上述函数中可以使用-1表示自动计算该维度的长度(前提是转换前后维度的长度乘积相同)。

        注意:使用view函数只能修改存储在整块的内存中的张量的形状。在PyTorch中,有些张量是由不同的数据块组成的,它们并没有存储在整块的内存中,view函数无法对这样的张量的形状进行转换,例如:一个张量经过了transpose函数或者permute函数的处理之后,就无法使用view函数进行转换操作,具体示例如下:

import torch
torch.random.manual_seed(0)
t1 = torch.randint(1, 9, (2, 3, 4))
# is_contiguous函数用于判断张量是否存储在整块的内存中
print(t1.is_contiguous()) # True
t2 = t1.transpose(0, 1)
print(t2.is_contiguous()) # False
# contiguous函数用于将张量存储在整块的内存中
t3 = t2.contiguous()
print(t3.is_contiguous()) # True
print(t3.view(-1).size()) # torch.Size([24])

3.1.3 squeeze函数和unsqueeze函数

        squeeze函数可以删除张量中长度为1的维度(进行降维),unsqueeze函数可以为张量添加一个长度为1的维度(进行升维),具体使用方法如下:

import torch
torch.random.manual_seed(0)
t1 = torch.randint(1, 9, (2, 1, 4, 3))
t2 = t1.squeeze(1)
print(t2.shape) # torch.Size([2, 4, 3])
t3 = t1.unsqueeze(2)
print(t3.shape) # torch.Size([2, 1, 1, 4, 3])
t4 = t1.unsqueeze(-2)
print(t4.shape) # torch.Size([2, 1, 4, 1, 3])

        注意:使用squeeze函数时可以传入需要删除的维度,如果传入的维度的长度不为1,则不会进行降维操作,如果不传入参数,默认删除所有长度为1的维度。

        注意:使用unsqueeze函数时必须传入添加维度的位置索引,可以使用正索引和负索引,使用正索引时,在指定位置添加一个长度为1的维度之后,将其他维度向后移动一位;使用负索引时,在指定位置添加一个长度为1的维度之后,将其他维度向前移动一位

3.2 张量的合并

3.2.1 张量的拼接

        可以使用cat函数完成张量的拼接,具体使用方法如下:

import torch
torch.random.manual_seed(0)
t1 = torch.randint(1, 9, (2, 1, 4, 3))
t2 = torch.randint(1, 9, (2, 3, 4, 3))
t3 = torch.cat((t1, t2), dim=1)
print(t3.shape) # torch.Size([2, 4, 4, 3])

        注意:cat函数只有torch.cat()的形式,并且第一个参数为需要拼接的张量组成的元组,dim参数用于指定拼接的维度,指定的维度的长度可以不一样,但其它维度的长度必须一样。

3.2.2 张量的堆叠

        可以使用stack函数完成张量的堆叠,具体使用方法如下:

import torch

t1 = torch.rand(2, 3, 4)
t2 = torch.rand(2, 3, 4)
t3 = torch.stack((t1, t2), dim=2)
print(t3.shape) # torch.Size([2, 3, 2, 4])

        注意:dim参数用于指定堆叠张量的维度,并且张量的形状必须完全一样才能够进行堆叠。

3.3 自动微分模块

3.3.1 计算图

        计算图是一种用于表示数学表达式和程序中计算过程的数据结构,它通过节点(Nodes)和边(Edges)来表示计算中的操作和数据流。

        计算图中的节点代表计算图中的操作或变量,变量节点通常表示输入数据或中间结果;操作节点通常表示对输入数据进行的运算操作。

        计算图中的表示数据在节点之间的流动,从一个节点到另一个节点的边表示数据间的依赖关系,即一个节点的输出是另一个节点的输入。

        计算图中的前向传播是指从输入节点开始,按照计算图的结构逐步计算每个节点的值,直到得到最终结果的过程;而反向传播是指从输出节点开始,计算每个节点对损失函数的梯度,用于更新模型参数的过程,这是也是自动微分的基础。

3.3.2 静态计算图和动态计算图

        静态计算图动态计算图是两种不同的计算图构建和执行方式。

        静态计算图是指在运行之前便构建好的计算图,然后在运行时重复使用相同的图结构,这种计算图的结构在编译阶段就已经确定,不会在运行时改变。  

        静态计算图的优点:性能较高,可以在编译阶段对计算图进行优化,以提高计算效率,例如进行常量折叠、内存分配优化等,并且静态计算图的稳定性较高,固定的计算图结构使得代码更加稳定和可预测。

        静态计算图的缺点:灵活性低,难以处理变长序列和复杂的控制流(如条件分支和循环)并且调试过程较为复杂,需要额外的工具支持。

        TensorFlow的1.x版本使用的即为静态计算图。

        动态计算图是指在每次运行时根据输入数据动态构建的计算图,这种计算图的结构在运行时可以改变。

        动态计算图的优点:灵活性高,可以处理变长序列和复杂的控制流并且调试过程相对简单,可以直接使用标准调试工具。

        动态计算图的缺点:性能较低,由于计算图的结构不固定,可能无法进行一些高级优化并且稳定性较低,由于计算图的结构在运行时变化,可能导致代码不稳定。

        TensorFlow的2.x版本PyTorch使用的即为动态计算图。

3.3.3 自动微分模块示例

        训练神经网络时,最常用的算法就是反向传播(Back propagation,BP)算法,在该算法中,参数(模型权重和偏置)会根据损失函数关于对应参数的梯度进行调整,为了计算这些梯度,PyTorch的torch.autograd模块提供了自动微分功能,它支持动态计算图的自动梯度计算,具体示例如下:

import torch
# 输入张量的形状为2 * 5
x = torch.ones(2, 5)
# 目标值的形状为2 * 3
y = torch.zeros(2, 3)
# 设置要更新的权重和偏置的初始值
# requires_grad参数用于指定是否需要对该张量的所有操作进行跟踪,并在反向传播时计算其梯度
# 对于模型的参数(如权重和偏置),一般会设置requires_grad=True,以便在训练过程中更新这些参数
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
# 设置网络的输出值
z = torch.matmul(x, w) + b  # matmul函数为矩阵乘法, 加b时使用了张量的广播机制
# 创建损失函数对象, MSELoss表示使用均方误差损失函数
loss = torch.nn.MSELoss()
# 计算损失,此时的loss为一个张量
loss = loss(z, y)
# 调用loss的backward函数,底层使用了PyTorch中的torch.autograd模块进行自动微分
loss.backward()
# backward函数计算的梯度值会存储在张量的grad属性中
print("w的梯度:", w.grad)
print("b的梯度", b.grad)
'''
w的梯度: tensor([[-1.4915,  2.2535, -2.5010],
        [-1.4915,  2.2535, -2.5010],
        [-1.4915,  2.2535, -2.5010],
        [-1.4915,  2.2535, -2.5010],
        [-1.4915,  2.2535, -2.5010]])
b的梯度 tensor([-1.4915,  2.2535, -2.5010])
'''

        上述计算过程的计算图如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值