前言:因为最近开始读深度学习代码,主要都是用PyTorch框架,所以来补一些PyTorch基础,先从数据结构入手。
PyTorch数据结构
- PyTorch
- PyTorch数据结构
- 张量
- 属性:维度、轴、形状
- 常见的操作
- 数据集
- 构造代码
- DataLoader
- 模块
- 参考
PyTorch
PyTorch:PyTorch是一个开源深度学习框架,有很多好用的深度学习工具,提供了丰富的库,可以很方便构建和训练神经网络模型。
- GPU加持:PyTorch提供了GPU优化操作和管理,使得在GPU上运行模型更高效。
- 提供预训练模型和模型库:PyTorch提供了很多预训练模型和模型库,能很方便进行深度学习模型的开发。
- 支持分布式训练:PyTorch支持分布式训练,可以在多个GPU和多台机器上加速训练。
- 动态计算图:PyTorch使用动态计算图来计算图,在运行时动态生成而不是编译时静态生成,可以观察动态生成的数据流向。
- 自动求导:PyTorch内置了自动求导功能,避免手动去计算非常复杂的导数,极大地减少了构建模型的时间。
PyTorch数据结构
张量
Tensor(张量):Tensor是PyTorch中最基本的数据结构,类似于多维数组。它可以表示标量、向量、矩阵或任意维度的数组。
属性:维度、轴、形状
维度(Dimensions):维度又可以叫做阶(Rank),理解为数组的维度。只有标量就是0维度,一维数组就是1维度,其余以此类推。
轴(Axis):轴数和维数、阶数相同,多维张量需要索引才能引用到里面的内容,这个不同维度索引就是轴。例如形状3×4的张量,需要访问张量[0][2]位置的内容,轴指的就是“[]”里面的索引,第一个轴的长度是3,第二的轴的长度是4。
形状(Shape):张量的形状由每个轴的长度决定,轴的长度就是对应维度能索引的大小。
相关的代码:
常用的函数有Tensor.size()和Tensor.dim()。
import torch
# 创建3维张量
tensor_3d = torch.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
print("张量形状:", tensor_3d.size())# torch.Size([1,2,3]) 1个2*3的数组
print("轴数:", tensor_3d.dim())# 3
print(tensor_3d.size(1)==tensor_3d.size(-2))# True x.size(index)表示取出某个维度上的大小,正的表示从左到右,负的表示从右到左
常见的操作
重构(reshape):torch.reshape()可以重构张量的形状。过程是先把所有的内容按行排列,然后先分高纬再分低维度。
重构举例:
x = torch.tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
x.reshape(4,3) # 变成3行4列矩阵
x # tensor([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
x.reshape(2,3,2)
# 先变一维 [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# 再变二维 [[ 0, 1, 2, 3, 4, 5],[ 6, 7, 8, 9, 10, 11]]
# 再变三维 [[[ 0, 1],[ 2, 3],[ 4, 5]],[[ 6, 7],[ 8, 9],[10, 11]]]
x # tensor([[[ 0, 1],[ 2, 3],[ 4, 5]],[[ 6, 7],[ 8, 9],[10, 11]]])
张量索引:
x = torch.tensor([[[ 0, 1, 2, 3],[ 4, 5, 6, 7],[ 8, 9, 10, 11]]])
print(x[0])# tensor([[ 0, 1, 2, 3],[ 4, 5, 6, 7],[ 8, 9, 10, 11]])
print(x[0][0])# tensor([0, 1, 2, 3])
print(A[0, 0:2, :])# tensor([[0, 1, 2, 3],[4, 5, 6, 7]])
拼接(cat和stack):
- stack:在新创建的维度上进行拼接,会扩宽维度。
- cat:按张量维度进行拼接。
# [2,3]->[2,9]
x=torch.ones((2, 3))
y = torch.cat([x, x], dim=0)
print(y)# tensor([[1., 1., 1.],[1., 1., 1.],[1., 1., 1.],[1., 1., 1.]])
# [2,3]->[2, 3, 2]
y = torch.stack([x, x], dim=2)
print(y)# tensor([[[1., 1.],[1., 1.],[1., 1.]],[[1., 1.],[1., 1.],[1., 1.]]])
升维(unsqueeze):指定维度插入新的维度。
x = torch.tensor([1,2,3,4])
y = x.unsqueeze(dim=0)
print(y)# tensor([[1],[2],[3],[4]])
降维(squeeze):移除制定或维度大小为1的维度。
x = torch.tensor([[[1],[2]],[[3],[4]]])
y = x.squeeze(2)
print(y)# tensor([[1, 2],[3, 4]])
升维和降维的好处,在于深度学习通常做运算会有维度要求。
数据集
Dataset(数据集):Dataset是一个抽象类,用于表示数据集。
使用:通过继承Dataset类,可以自定义数据集,并实现数据加载、预处理和获取样本等功能。PyTorch还提供了一些内置的数据集类,如MNIST、CIFAR-10等,用于方便地加载常用的数据集。
构造代码
代码:
通过继承该类来自定义自己的数据集类,在继承时要求必须重载__len__()和__getitem__()这两个方法。
- __len__():返回的是数据集的大小。
- __getitem__():实现索引数据集中的某一个数据。
import torch
from torch.utils.data import Datasetclass BasicDataset(Dataset):# 继承Datasetdef __init__(self, data_tensor, target_tensor):self.data_tensor = data_tensorself.target_tensor = target_tensordef __getitem__(self, index):return self.data_tensor[index], self.target_tensor[index]def __len__(self):return self.data_tensor.size(0)# 生成数据
data_tensor = torch.randn(4, 3)# 生成一个每个元素服从正态分布的4行3列随机张量
target_tensor = torch.rand(10)# 从区间[0,1)的均匀分布中随机抽取一个随机数生成一个张量# 将数据封装成Dataset
tensor_dataset = BasicDataset(data_tensor, target_tensor)print(tensor_dataset[1])# 调用__getitem__print(len(tensor_dataset))# 调用__len__
DataLoader
DataLoader:DataLoader将Dataset对象或自定义数据类的对象封装成一个迭代器,通过迭代器可以输出Dataset的内容。
DataLoader参数:
- dataset:表示Dataset类,数据从哪读取以及如何读取。
- batch_size:表示批大小。
- shuffle:表示每个epoch要不要重新打乱数据,默认false。
- num_works:用多少个子进程读取数据。
- drop_last:表示当样本数不能被batch_size整除时,是否舍弃最后一批数据。
batch和epoch的区别:
- 一个epoch就是将所有训练样本训练一次的过程,神经网络的训练往往会需要很多次epoch才会loss收敛到合适的程度。
- 将整个训练样本分成若干个Batch。
使用代码:
# batch_size设置为2,shuffle=False不打乱数据顺序,num_workers=1使用1个子进程
dataloader = BasicDataset(dataset, batch_size=2, shuffle=False, num_workers=1)# 以for循环形式输出
for input, target in dataloader:print(input, target)
模块
Module(模块):Module是PyTorch中用于构建模型的基类。通过继承Module类,可以定义自己的模型,并实现前向传播和反向传播等方法。Module提供了参数管理、模型保存和加载等功能,方便模型的训练和部署。
实际去看深度学习代码的时候,会发现定义模型的类,都是继承nn.Module(模块)。
代码技巧:
- 网络中具有可学习参数的层(如全连接层、卷积层等)放在构造函数__init__()中。不具有参数的也可以放入(ReLU、dropout、BatchNormanation),如果不写在构造函数的话,可以在forward方法中用nn.functional来代替。
- forward方法是必须要重写的,是实现模型的功能,实现各个层之间的连接关系的核心。在阅读模型的时候,阅读forward是搞懂模型运作流程最好的方式。
import torch
import torch.nn.functional as Fclass MyNet(torch.nn.Module):def __init__(self):super(MyNet, self).__init__() # 第一句话,调用父类的构造函数self.conv1 = torch.nn.Conv2d(3, 32, 3, 1, 1)# self.relu1=torch.nn.ReLU()# self.max_pooling1=torch.nn.MaxPool2d(2,1)self.conv2 = torch.nn.Conv2d(3, 32, 3, 1, 1)# self.relu2=torch.nn.ReLU()# self.max_pooling2=torch.nn.MaxPool2d(2,1)self.dense1 = torch.nn.Linear(32 * 3 * 3, 128)self.dense2 = torch.nn.Linear(128, 10)def forward(self, x):x = self.conv1(x)x = F.relu(x)# self.relu1(x)x = F.max_pool2d(x)# self.max_pooling1(x)x = self.conv2(x)x = F.relu(x)# self.relu2(x)x = F.max_pool2d(x)# self.max_pooling2(x)x = self.dense1(x)x = self.dense2(x)return x
参考
nn.Module类详解
Dataset和DataLoader
PyTorch数据结构