Pytorch速查

type
Post
status
Published
summary
PyTorch Geometric is a library for deep learning on irregular input data such as graphs, point clouds, and manifolds. skorch. skorch is a high-level library for ...
slug
pytorch
date
Aug 31, 2023
tags
pytorch
速查
深度学习
框架
category
机器学习
password
icon
URL
Property
Feb 28, 2024 12:33 PM
张量(Tensor)是Pytorch中的基本数据结构
 
参考文章

创建张量

import torch torch.Tensor(5, 3) # 创建一个5x3的张量(数值为0) torch.Tensor([5, 3]) # 基于列表、array等创建一个一维的张量,默认浮点型 torch.tensor([5, 3]) # 基于列表、array等创建一个一维的张量,默认整型, a = torch.empty(5, 3) # 创建一个未初始化的5x3张量 a = torch.eye(3, 3) # 创建一个对角为1,其余为0的方形张量 a = torch.arange(1, 4) # 返回值[1,2,3] a = torch.range(1, 4) # 新版本弃用,返回值[1,2,3,4] a = torch.rand(5, 3) # 创建一个5x3张量,值在[0,1)之间均匀分布(Returns a tensor filled with random numbers from a uniform distribution on the interval [0,1)) a = torch.randn(5, 3) # 创建一个5x3张量,值服从正态分布(Returns a tensor filled with random numbers from a normal distribution with mean 0 and variance 1 (also called the standard normal distribution).) b = torch.randn_like(a, dtype=torch.float) # 创建一个形状与a相同的随机数值张量,并且重新定义类型为float a = torch.normal(2, 3, size=(2, 4)) # 创建一个均值为2,标准差为3,形状为2x4的张量 a = torch.randperm(5) # 创建一个一维的张量,值为[0,5)的整数随机排列 a.zero_() # 将原本的张量a用0填充 a = torch.zeros(5, 3, dtype=torch.long) # 创建一个5x3的全0张量,类型为long b = torch.zeros_like(a) # 创建一个形状与a相同的全0张量 a = torch.ones(5, 3, dtype=torch.double) # 创建一个5x3的全1张量(单位张量),类型为double b = torch.ones_like(a) # 创建一个形状与a相同的全1张量 a = torch.full((5,3),4) # 创建一个5x3的、值全为4的张量
rand()、randn()、ones()、zeros()、eye()、full(input,x)、arange()、linspace()、randint(begin,end,n) 以上都可加_like(a),表示生成size和a一样的tensor Pytorch中结尾带下划线_的方法表示就地执行

张量操作

x = torch.randn([1,5,2]) # 索引——张量有多少个维度,切片的时候就可以有多少个参数 x[:,:3] x[:,:3,:1] x[:,:3,:1,:] # 报错 x.dtype # 查看数据类型 x.type_as(b) # 数据类型转换 x.item() # 从张量中提取元素 x.clone() # 克隆一个张量 x.reshape() # 改变一个张量的形状(官方不推荐使用) x.view() # 改变一个张量的形状 # 新张量与原张量的内存共享,如果想要互不影响,先使用clone创建张量副本 # view只能用于内存中连续存储的tensor,如果不连续先调用.contiguous()方法,使tensor的元素在内存空间中连续,然后调用.view() x.expand(3,5,2) # 维度扩展(只能沿着值为1的维度扩展(x的原始维度为1,5,2),扩展的方法就是将该维度内的内容复制一份) x.transpose(0,1) # 维度交换(参数为维度的索引值,只能交换两个维度,x的维度交换之后为5,1,2) x.permute(2,0,1) # 维度交换(参数为维度的索引值,可以交换多个维度,x的维度交换之后为2,1,5) x.t() # 转置 x.T # 转置 torch.squeeze(x, dim=None) # 🚩除去输入张量中数值为1的维度 torch.unsqueeze(x, dim=None) # 🚩在指定位置插入一维 torch.cat((x, x, x), dim=0) # 🚩同维拼接,拼接之后维度不变 torch.stack((x, x, x), dim=0) # 🚩扩维拼接,拼接之后维度提升一维 torch.chunk(x,chunks,dim=0) # 将张量按维度dim进行平均切分,chunks是拆分的份数 torch.split(,split_size_or_sections,dim=0) # 将张量按维度dim进行切分,split_size_or_sections 为 int 时,表示每一份的长度;为 list 时,按 list 元素切分。 torch.equal() # 判断两个张量是否相等 v,s = torch.sort(input,descending=True) # 对一个张量进行排序,返回两个张量;一个是排序后的张量,一个是排序后数值原来的索引 torch.diag(x) # 取x对角线元素形成一个一维向量

张量运算

x = torch.randn([1,5,2]) # 类型修改 x.short() # 16位整型 x.int() # 32位整型 x.long() # 64位整型 x.float() # 32位浮点型 x.double() # 64位浮点型 # 最大、最小、绝对值 torch.max(x,dim) # 获取指定维度上的最大值 x.max(dim) torch.min(x,dim) # 获取指定维度上的最小值 x.min(dim) torch.abs(a) # 绝对值;将张量中的负数用绝对值替换 x.add_(1) # 如果只有一个数值,则会进行广播 torch.add(x,y) # 对应位置相加,要求形状相同,x和y既可以是Tensor也可以是标量 torch.sub(x,y) # 对应位置相减,要求形状相同,x和y既可以是Tensor也可以是标量 torch.div(x,y) # 对应位置相除,要求形状相同,x和y既可以是Tensor也可以是标量 torch.mul(x,y) # 对应位置相乘,要求形状相同,x和y既可以是Tensor也可以是标量 torch.mm(x,y.T) # 矩阵叉乘,要求x和y必须是矩阵 torch.mv(x,y) # 矩阵与向量相乘 # 对数、指数、幂函数 torch.log(input, out=None) torch.log10(input, out=None) torch.log2(input, out=None) torch.exp(input, out=None) torch.pow(x,n) # 幂次方,每个元素进行幂次方 torch.clamp(x,m,n) # 对x进行裁剪,如果x中的值大于m,则将该值替换为m;如果x中的值小于n,则将该值替换为n; torch.norm(x, p=2, dim=1) # 计算张量x在维度1上的p范数 # 所有结尾带下划线_符号的函数都会对原数据进行修改

运行设备切换

print(torch.__version__) # pytorch版本 print(torch.version.cuda) # cuda版本 print(torch.cuda.is_available()) # 查看cuda是否可用 dev = torch.device("cuda" if torch.cuda.is_available() else "cpu") # #使用GPU or CPU x = torch.tensor([1,2], device=dev) # 创建指定环境dev中的的tensor x.device # 判断某个对象是在什么环境中运行的 x = x.to(device='cuda') # 将对象环境设置为GPU环境 x = x.to(device='cpu') # 将对象环境设置为CPU环境 x = x.cpu() # 将对象环境设置为CPU环境 x.cpu().numpy() # cuda环境下tensor不能直接转化为numpy类型,必须要先转化到cpu环境中 x+y.to(device=dev) # 若一个没有环境的对象与另外一个有环境x对象进行交流,则环境全变成环境x

数据加载

Pytorch中的数据集
类型
加载方法
数据集说明
torchvision
torchvision.datasets.MNIST()
手写数字图像;训练数据:6w, 测试数据:1w
torchvision.datasets.EMNIST()
数字和字母的图像;是 MNIST 数据集的高级版本
torchvision.datasets.FashionMNIST()
T恤、裤子、包包等服装图像;训练数据:6w, 测试数据:1w
torchvision.datasets.CIFAR10()
卡车、青蛙、船、汽车、鹿等图像;有10个类别
torchvision.datasets.CIFAR100()
卡车、青蛙、船、汽车、鹿等图像;有100个类别
torchvision.datasets.CocoCaptions()
10万个日常对象(人、瓶子、文具、书籍等)的图像
torchvision.datasets.ImageNet()
它由分布在 10,000 个类别中的超过 120 万张图像组成,单独的 CPU 无法处理这么大的数据集
Torchtext
torchtext.datasets.IMDB()
情感分类数据集,训练数据:2.5w, 测试数据:2.5w;高度极端的电影评论
torchtext.datasets.WikiText2()
维基数据
Torchtext中其他数据集
SST、TREC、SNLI、MultiNLI、WikiText-2、WikiText103、PennTreebank、Multi30k 等。
一个简单的数据加载例子
# 构建数据集:可以理解为对数据进行摸底并记录相关信息 class ToyDataset(Dataset): def __init__(self,X,Y): self.X = X self.Y = Y def __len__(self): return len(self.X) # 1、获取数据集长度 def __getitem__(self,index): # 参数 index 是由 DataLoader 内部在迭代时提供的 return self.X[index],self.Y[index] # 3、根据索引提取数据,索引参数来自每次数据加载器产生的数据索引 X,Y = torch.randn(1000,3),torch.randint(low=0,high=2,size=(1000,)).float() ds = ToyDataset(X,Y) # 数据加载器:指定批次、 dl = DataLoader(ds,batch_size=4,drop_last = False) # 2、指定批次大小 features,labels = next(iter(dl)) # 在数据加载器的迭代过程中,每次迭代会调用数据集类的 __getitem__ 方法,传递一个索引值作为参数,以获取对应索引的样本 # 在每个批次中,DataLoader 会自动计算批次中每个样本在数据集中的索引,然后调用数据集类的 __getitem__ 方法,将这些索引作为参数传递给它。 print("features = ",features ) print("labels = ",labels )
创建数据集TensorDataset、Dataset
创建自定义数据集
from pathlib import Path from PIL import Image class Cifar2Dataset(Dataset): def __init__(self,imgs_dir,img_transform): self.files = list(Path(imgs_dir).rglob("*.jpg")) self.transform = img_transform def __len__(self,): return len(self.files) def __getitem__(self,i): file_i = str(self.files[i]) img = Image.open(file_i) tensor = self.transform(img) label = torch.tensor([1.0]) if "1_automobile" in file_i else torch.tensor([0.0]) return tensor,label train_dir = "./eat_pytorch_datasets/cifar2/train/" test_dir = "./eat_pytorch_datasets/cifar2/test/" # 定义图片增强 transform_train = transforms.Compose([ transforms.RandomHorizontalFlip(), #随机水平翻转 transforms.RandomVerticalFlip(), #随机垂直翻转 transforms.RandomRotation(45), #随机在45度角度内旋转 transforms.ToTensor() #转换成张量 ] ) transform_val = transforms.Compose([ transforms.ToTensor() ] ) ds_train = Cifar2Dataset(train_dir,transform_train) ds_val = Cifar2Dataset(test_dir,transform_val) dl_train = DataLoader(ds_train,batch_size = 50,shuffle = True) dl_val = DataLoader(ds_val,batch_size = 50,shuffle = True) for features,labels in dl_train: print(features.shape) print(labels.shape) break
根据Tensor创建数据集
import numpy as np import torch from torch.utils.data import TensorDataset,Dataset,DataLoader,random_split # 根据Tensor创建数据集 from sklearn import datasets iris = datasets.load_iris() ds_iris = TensorDataset(torch.tensor(iris.data),torch.tensor(iris.target)) # 分割成训练集和预测集 n_train = int(len(ds_iris)*0.8) n_val = len(ds_iris) - n_train ds_train,ds_val = random_split(ds_iris,[n_train,n_val]) print(type(ds_iris)) print(type(ds_train)) # 使用DataLoader加载数据集 dl_train,dl_val = DataLoader(ds_train,batch_size = 8),DataLoader(ds_val,batch_size = 8) for features,labels in dl_train: print(features,labels) break # 演示加法运算符(`+`)的合并作用 ds_data = ds_train + ds_val print('len(ds_train) = ',len(ds_train)) print('len(ds_valid) = ',len(ds_val)) print('len(ds_train+ds_valid) = ',len(ds_data)) print(type(ds_data))
根据图片目录创建图片数据集
import numpy as np import torch from torch.utils.data import DataLoader from torchvision import transforms,datasets # 根据图片目录创建数据集 def transform_label(x): return torch.tensor([x]).float() ds_train = datasets.ImageFolder("./eat_pytorch_datasets/cifar2/train/", transform = transform_train,target_transform= transform_label) ds_val = datasets.ImageFolder("./eat_pytorch_datasets/cifar2/test/", transform = transform_valid, target_transform= transform_label) print(ds_train.class_to_idx) # 使用DataLoader加载数据集 dl_train = DataLoader(ds_train,batch_size = 50,shuffle = True) dl_val = DataLoader(ds_val,batch_size = 50,shuffle = True) for features,labels in dl_train: print(features.shape) print(labels.shape) break
加载数据集DataLoader
DataLoader能够控制batch的大小,batch中元素的采样方法,以及将batch结果整理成模型所需输入形式的方法,并且能够使用多进程读取数据
dataset 可以直接给 dataframe
DataLoader( dataset, # 数据集 batch_size=1, # 批次大小 shuffle=False, # 是否乱序 sampler=None, # 样本采样函数,一般无需设置。 batch_sampler=None, # 批次采样函数,一般无需设置。 num_workers=0, # 使用多进程读取数据,设置的进程数。mac电脑设置会报错 collate_fn=None, # 整理一个批次数据的函数。 pin_memory=False, # 是否设置为锁业内存。默认为False,锁业内存不会使用虚拟内存(硬盘),从锁业内存拷贝到GPU上速度会更快。 drop_last=False, # 是否丢弃最后一个样本数量不足batch_size批次数据。 timeout=0, # 加载一个数据批次的最长等待时间,一般无需设置。 worker_init_fn=None, # 每个worker中dataset的初始化函数,常用于 IterableDataset。一般不使用。 multiprocessing_context=None, )
一般情况下,我们仅仅会配置 dataset, batch_size, shuffle, num_workers,pin_memory, drop_last这六个参数
其他方法
torch.utils.data.random_split # 将一个数据集分割成多份,常用于分割训练集,验证集和测试集。

数据预处理

图像
# 裁剪——Crop transforms.CenterCrop() # 中心裁剪 transforms.RandomCrop() # 随机裁剪 transforms.RandomResizedCrop() # 随机长宽比裁剪 transforms.FiveCrop() # 上下左右中心裁剪 transforms.TenCrop() # 上下左右中心裁剪后翻转 # 翻转和旋转——Flip and Rotation transforms.RandomHorizontalFlip(p=0.5)() # 依概率p水平翻转 transforms.RandomVerticalFlip(p=0.5)() # 依概率p垂直翻转 transforms.RandomRotation() # 随机旋转 # 图像变换 transforms.Resize() # resize transforms.Normalize() # 标准化 transforms.ToTensor() # 转为tensor,并归一化至[0-1] transforms.Pad() # 填充 transforms.ColorJitter() # 修改亮度、对比度和饱和度 transforms.Grayscale() # 转灰度图 transforms.LinearTransformation()() # 线性变换 transforms.RandomAffine() # 仿射变换 transforms.RandomGrayscale() # 依概率p转为灰度图 transforms.ToPILImage() # 将数据转换为PILImage # 对transforms操作,使数据增强更灵活 transforms.RandomChoice(transforms) # 从给定的一系列transforms中选一个进行操作 transforms.RandomApply(transforms, p=0.5) # 给一个transform加上概率,依概率进行操作 transforms.RandomOrder() # 将transforms中的操作随机打乱
实例
#演示一些常用的图片增强操作 from PIL import Image img = Image.open('./data/cat.jpeg') img # 随机数值翻转 transforms.RandomVerticalFlip()(img) #随机旋转 transforms.RandomRotation(45)(img) # 定义图片增强操作 transform_train = transforms.Compose([ transforms.RandomHorizontalFlip(), #随机水平翻转 transforms.RandomVerticalFlip(), #随机垂直翻转 transforms.RandomRotation(45), #随机在45度角度内旋转 transforms.ToTensor() #转换成张量 ] ) transform_valid = transforms.Compose([ transforms.ToTensor() ] )

torch.nn

# 参考:https://yey.world/2020/12/16/Pytorch-12/ ### 二维卷积层 nn.Conv2d( in_channels, # 输入通道数 out_channels, # 输出通道数,等价于卷积核个数 kernel_size, # 卷积核尺寸 stride=1, # 卷积核移动步长 padding=0, # 填充个数,常用于保持输入输出图像尺寸匹配 dilation=1, # 空洞卷积大小;常用于图像分割任务,目的是提高感受野,即输出图像的一个像素对应输入图像上更大的一块区域 groups=1, # 分组卷积的组数。常用于模型的轻量化。 bias=True, # 偏置。最终输出响应值时需加上偏置项。 padding_mode='zeros' ) # 参考:https://yey.world/2020/12/16/Pytorch-13/ ### 最大池化层 nn.MaxPool2d( kernel_size, # 池化核尺寸 stride=None, # 池化核移动步长 padding=0, # 填充个数 dilation=1, # 池化核间隔大小 return_indices=False, # 记录池化像素索引。通常在最大值反池化上采样时使用。 ceil_mode=False # 尺寸是否向上取整。用于计算输出特征图尺寸,默认设置为向下取整。 ) ### 平均池化层 nn.AvgPool2d( kernel_size, # 池化核尺寸 stride=None, # 池化核移动步长 padding=0, # 填充个数 ceil_mode=False, # 尺寸是否向上取整。用于计算输出特征图尺寸,默认设置为向下取整。 count_include_pad=True, # 是否将填充值用于平均值的计算。 divisor_override=None # 除法因子。计算平均值时代替像素个数作为分母。 ) ### 全连接层(线性层) nn.Linear( in_features, # 输入结点数。 out_features, # 输出结点数。 bias=True # 是否需要偏置。 ) ### 激活函数层 nn.Sigmoid() nn.tanh() nn.ReLU() nn.LeakyReLU() nn.PReLU() nn.RReLU() ### 初始化,参考:https://pytorch.org/docs/stable/nn.init.html#torch-nn-init nn.init.xavier_uniform_(self.conv1.weight) # 均匀分布初始化 nn.init.xavier_normal_(conv_layer.weight.data) # 正态分布初始化

损失函数

损失函数
名称
适用场景
torch.nn.MSELoss()
均方误差损失
回归
torch.nn.L1Loss()
平均绝对值误差损失
回归
torch.nn.CrossEntropyLoss()
交叉熵损失
多分类
torch.nn.NLLLoss()
负对数似然函数损失
多分类
torch.nn.NLLLoss2d()
图片负对数似然函数损失
图像分割
torch.nn.KLDivLoss()
KL散度损失
回归
torch.nn.BCELoss()
二分类交叉熵损失
二分类
torch.nn.MarginRankingLoss()
评价相似度的损失
torch.nn.MultiLabelMarginLoss()
多标签分类的损失
多标签分类
torch.nn.SmoothL1Loss()
平滑的L1损失
回归
torch.nn.SoftMarginLoss()
多标签二分类问题的损失
多标签二分类

优化器:torch.optim

import torch import torch.optim as optim optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9) # 定义优化器 optimizer.zero_grad() # 优化器清零 optimizer.step() # 优化器更新参数 # 增加参数 w2 = torch.randn((3, 3), requires_grad=True) optimizer.add_param_group({"params": w2, 'lr': 0.0001}) # 保存优化器状态信息 opt_state_dict = optimizer.state_dict() torch.save(opt_state_dict, os.path.join(BASE_DIR, "optimizer_state_dict.pkl")) # 读取优化器状态信息 state_dict = torch.load(os.path.join(BASE_DIR, "optimizer_state_dict.pkl")) optimizer.load_state_dict(state_dict)

神经网络构建

  •  神经网络的构建主要使用torch.nn模块里提供的一个模型构造类Module,可以继承它来定义想要的模型
  • __init__ 函数,定义模型参数和网络层
  • forward 函数,定义前向计算(正向传播)

方法详解

torch.squeeze() & torch.unsqueeze()

  • squeeze
    • 除去输入张量中数值为1的维度,并返回新的张量(输出的张量与原张量共享内存),
      如果dim不指定,则删除所有值为1的维度;如果dim指定某个维度且该维度值为1,则执行删除,否则张量维度不变
  • unsqueeze
    • notion imagenotion image
      针对以上的一个三维张量, 当unsqueeze的参数是0时,torch.unsqueeze(z,0) 就是在索引为0的括号的外面再加一层括号, 当unsqueeze的参数是1时,torch.unsqueeze(z,1) 就是在索引为1 的括号前面加一层括号, 当unsqueeze的参数是2时,torch.unsqueeze(z,2) 就是在索引为2 的括号前面加一层括号, 当unsqueeze的参数是3时,torch.unsqueeze(z,3) 就是在最里面的数字上加一层括号

torch.cat() & torch.stack()

  • cat
    • notion imagenotion image
      针对以上的一个三维张量,cat的合并维度由张量的维度决定,张量维度的索引范围:[-3, 2] 当cat的参数是0时,torch.cat((x, x, x), 0) 就是将三个x张量的第一层括号内的内容进行合并, 当cat的参数是1时,torch.cat((x, x, x), 1) 就是将三个x张量的第二层括号内的内容进行合并, 当cat的参数是2时,torch.cat((x, x, x), 2) 就是将三个x张量的第三层括号内的内容进行合并,
  • stack
    • notion imagenotion image
      针对以上的一个三维张量,stack的合并维度由张量的维度决定,张量维度的索引范围:[-4, 3] 当cat的参数是0时,torch.stack((x, x, x), 0) 就是将三个x张量的第一层括号内的内容进行合并,并且在合并后的内容外增加一维, 当cat的参数是1时,torch.stack((x, x, x), 1) 就是将三个x张量的第二层括号内的内容进行合并,并且在合并后的内容外增加一维, 当cat的参数是2时,torch.stack((x, x, x), 2) 就是将三个x张量的第三层括号内的内容进行合并,并且在合并后的内容外增加一维, 当cat的参数是2时,torch.stack((x, x, x), 3) 就是将三个x张量的最内层内容进行合并,并且在合并后的内容外增加一维,
       
If you have any questions, please contact me.