PyTorch 2.0常用函数解析与用法
【图书推荐】《PyTorch深度学习与计算机视觉实践》-CSDN博客
基于Vision Transformer的mini_ImageNet图片分类实战_imagenet数据集-CSDN博客
《PyTorch深度学习与计算机视觉实践(人工智能技术丛书)》(王晓华)【摘要 书评 试读】- 京东图书 (jd.com)
在深入了解下一部分的深度学习内容之前,我们先探讨一些PyTorch 2.0中常用的函数解析和使用,这些将在后续的深度学习项目中发挥重要作用。
3.2.1 数据加载和预处理
在深度学习中,数据加载和预处理(Data Loading and Preprocessing)是必不可少的步骤。PyTorch提供了Dataset和DataLoader两个类来加载和预处理数据。以下是一个简单的例子:
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transforms
import numpy as np #定义MyDataset类继承自Dataset类
class MyDataset(Dataset): def __init__(self, data, target): self.data = data self.target = target def __getitem__(self, index): x = self.data[index] y = self.target[index] return x, y def __len__(self): return len(self.data) #定义数据预处理函数
def preprocess(self,data): #这里只是把numpy数组转换成torch张量,#还有许多其他预处理操作可以做,比如归一化、扩充维度等return torch.from_numpy(data)
在PyTorch中,数据加载和预处理是深度学习模型训练的关键步骤之一,它们的主要作用如下:
- 数据加载:在训练深度学习模型时,需要大量的数据。数据加载就是将这些数据从硬盘或其他来源读取并送入模型中进行训练的过程。PyTorch提供了DataLoader这个工具,可以方便地从硬盘加载数据,还可以对数据进行一些预处理操作。
- 数据预处理:数据预处理包括数据处理和数据增强。数据处理是对数据进行一些必要的处理,比如归一化、标准化、独热编码等,使得数据更适合模型的训练。数据增强则是一种通过改变数据的形态、颜色、大小等来增加数据量的技术,这可以帮助模型更好地泛化到新的数据。
总的来说,数据加载和预处理在PyTorch中的作用就是帮助我们将原始的数据转换成适合模型训练的形式,从而提高模型的训练效率和泛化能力。
3.2.2 张量的处理
在PyTorch 2.0中,张量是核心的数据结构,用于表示所有的输入数据和输出数据,以及模型参数。张量类似于NumPy中的多维数组,但可以在GPU上进行运算以加速计算。张量具有以下属性:
- 形状(shape):表示张量的维度和每个维度的大小,例如张量的形状为 [3, 4],表示它是一个二维张量,第一维的大小为3,第二维的大小为4。
- 数据类型(dtype):表示张量中元素的数据类型,例如float32、int64等。
- 设备(device):表示张量所在的设备,例如在CPU或GPU上。
张量的一些基本操作如下所示:
1)张量基本信息
tensor = torch.randn(3,4,5)print(tensor.type()) #数据类型
print(tensor.size()) #张量的形状,是个元组
print(tensor.dim()) #维度的数量
2)命名张量
给张量命名是一个非常有用的方法,这样可以方便地使用维度的名字来做索引或进行其他操作,大大提高了代码的可读性、易用性,防止出错。
NCHW = [‘N’, ‘C’, ‘H’, ‘W’]
images = torch.randn(32, 3, 56, 56, names=NCHW)
images.sum('C')
images.select('C', index=0)
#也可以这么设置
tensor = torch.rand(3,4,1,2,names=('C', 'N', 'H', 'W'))
#使用align_to可以方便地对维度排序
tensor = tensor.align_to('N', 'C', 'H', 'W')
3)数据类型转换
#设置默认类型,PyTorch中的FloatTensor远远快于DoubleTensor
torch.set_default_tensor_type(torch.FloatTensor)#类型转换
tensor = tensor.cuda()
tensor = tensor.cpu()
tensor = tensor.float()
tensor = tensor.long()
torch.Tensor与np.ndarray转换,除了CharTensor,其他所有CPU上的张量都支持转换为NumPy格式然后再转换回来。
ndarray = tensor.cpu().numpy()
tensor = torch.from_numpy(ndarray).float()
tensor = torch.from_numpy(ndarray.copy()).float() #如果ndarray的步幅为负
4)从只包含一个元素的张量中提取值
value = torch.rand(1).item()
5)张量形变
#在将卷积层输入全连接层的情况下,通常需要对张量做形变处理#相比torch.view,torch.reshape可以自动处理输入张量不连续的情况tensor = torch.rand(2,3,4)shape = (6, 4)tensor = torch.reshape(tensor, shape)
6)打乱顺序
tensor = tensor[torch.randperm(tensor.size(0))] #打乱第一个维度
7)水平翻转
#PyTorch不支持tensor[::-1]这样的负步长操作,水平翻转可以通过张量索引实现#假设张量的维度为[N, D, H, W]tensor = tensor[:,:,:,torch.arange(tensor.size(3) - 1, -1, -1).long()]
8)复制张量
#Operation | New/Shared memory | Still in computation graph |
tensor.clone() #| New | Yes |
tensor.detach() #| Shared | No |
tensor.detach.clone()() #| New | No |
9)张量拼接
'''
注意torch.cat和torch.stack的区别在于torch.cat沿着给定的维度拼接,
而torch.stack会新增一维。例如当参数是3个10×5的张量,torch.cat的结果是30×5的张量,
而torch.stack的结果是3×10×5的张量。'''
tensor = torch.cat(list_of_tensors, dim=0)
tensor = torch.stack(list_of_tensors, dim=0)
10)将整数标签转为one-hot编码
#PyTorch的标记默认从0开始tensor = torch.tensor([0, 2, 1, 3])N = tensor.size(0)num_classes = 4one_hot = torch.zeros(N, num_classes).long()one_hot.scatter_(dim=1, index=torch.unsqueeze(tensor, dim=1), src=torch.ones(N, num_classes).long())
11)得到非零元素
torch.nonzero(tensor) #index of non-zero elements非零元素的索引torch.nonzero(tensor==0) #index of zero elements零元素的索引torch.nonzero(tensor).size(0) #number of non-zero elements非零元素的数量torch.nonzero(tensor == 0).size(0) #number of zero elements零元素的数量
12)判断两个张量是否相等
torch.allclose(tensor1, tensor2) #浮点数型张量
torch.equal(tensor1, tensor2) #整型张量
13)张量扩展
#张量的形状从64×512扩张到64×512×7×7tensor = torch.rand(64,512)torch.reshape(tensor, (64, 512, 1, 1)).expand(64, 512, 7, 7)
14)矩阵乘法
#矩阵乘法: (m*n) * (n*p) * -> (m*p)
result = torch.mm(tensor1, tensor2)#批处理矩阵乘法: (b*m*n) * (b*n*p) -> (b*m*p)
result = torch.bmm(tensor1, tensor2)#元素乘积
result = tensor1 * tensor2
15)计算两组数据之间的两两欧氏距离
#利用广播机制
dist = torch.sqrt(torch.sum((X1[:,None,:] - X2) ** 2, dim=2))
3.2.3 模型的参数与初始化操作
在PyTorch中,模型的参数和初始化操作也是非常重要的一部分。模型的参数通常包括权重和偏置,它们是构成模型的基本元素。在PyTorch中,可以通过定义nn.Module的子类来自定义模型,并在__init__方法中初始化模型的参数。
在初始化模型参数时,可以使用PyTorch提供的各种初始化方法,如nn.init.normal_()、nn.init.constant
_()、nn.init.xavier_uniform_()等。这些初始化方法可以帮助我们设置参数的初始值,从而提高模型的训练效果和稳定性。
除了使用预定义的初始化方法之外,还可以自定义初始化过程。例如,可以通过在nn.Module的子类中重写reset_parameters()方法来实现自定义的初始化过程。
1)计算模型整体参数量
num_parameters = sum(torch.numel(parameter) for parameter in model.parameters())
2)查看网络中的参数
可以通过model.state_dict()或者model.named_parameters()函数,查看现在的全部可训练参数,包括通过继承得到的父类中的参数。
params = list(model.named_parameters())
(name, param) = params[28]
print(name)
print(param.grad)
print('-------------------------------------------------')
(name2, param2) = params[29]
print(name2)
print(param2.grad)
print('----------------------------------------------------')
(name1, param1) = params[30]
print(name1)
print(param1.grad)
3)模型权重初始化
注意model.modules()和model.children()的区别:model.modules()会迭代地遍历模型的所有子层,而model.children()只会遍历模型的下一层(子层)。
#初始化的常见做法
for layer in model.modules():if isinstance(layer, torch.nn.Conv2d):torch.nn.init.kaiming_normal_(layer.weight, mode='fan_out',nonlinearity='relu')if layer.bias is not None:torch.nn.init.constant_(layer.bias, val=0.0)elif isinstance(layer, torch.nn.BatchNorm2d):torch.nn.init.constant_(layer.weight, val=1.0)torch.nn.init.constant_(layer.bias, val=0.0)elif isinstance(layer, torch.nn.Linear):torch.nn.init.xavier_normal_(layer.weight)if layer.bias is not None:torch.nn.init.constant_(layer.bias, val=0.0)#使用给定张量初始化
layer.weight = torch.nn.Parameter(tensor)
4)提取模型中的某一层
modules()会返回模型中所有模块的迭代器,包括模型本身和它的所有子模块,能够访问到最内层的模块,如self.layer1.conv1。与之相对应的是children()方法和named_modules()方法。children()方法返回模型直接子模块的迭代器,而不像modules()那样能访问所有层级的模块。named_modules()与modules()类似,它返回所有模块的迭代器,但同时还会返回每个模块的名称,使得我们可以获取模块及其对应的名称。还有一个named_children()方法,它与children()相似,返回直接子模块及其名称的迭代器,但不会递归到更深层次的子模块。这些方法提供了不同层级和需求下遍历模型结构的灵活方式。
#提取模型中的前两层
new_model = nn.Sequential(*list(model.children())[:2]
#如果希望提取出模型中的所有卷积层,可以像下面这样操作
for layer in model.named_modules():if isinstance(layer[1],nn.Conv2d):conv_model.add_module(layer[0],layer[1])
5)导入另一个模型的相同部分到新的模型
一个模型在导入另一个模型的参数时,如果两个模型结构不一致,则直接导入参数会报错。用下面方法可以把另一个模型的相同部分导入新的模型中。
#model_new代表新的模型
#model_saved代表其他模型,比如用torch.load导入的已保存的模型
model_new_dict = model_new.state_dict()
model_common_dict = {k:v for k, v in model_saved.items() if k in model_new_dict.keys()}
model_new_dict.update(model_common_dict)
model_new.load_state_dict(model_new_dict)
作为最重要的深度学习框架,PyTorch 2.0提供了丰富的函数和类来帮助用户构建和训练神经网络,本小节仅列举了一些常用的函数的使用方法,还有更多的可以方便我们完成项目实战的方法,需要读者在后续的学习中了解和掌握。