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 a = torch.Tensor([3]) # (不推荐)创建一个包含 [3] 的一维张量 a = torch.Tensor(5, 3) # (不推荐)创建一个 5x3 的张量(数值为0) a = torch.Tensor([5, 3]) # (不推荐)创建一个包含 [5, 3] 的一维张量;基于数值序列、NumPy 数组张量,只能创建浮点型张量(默认为 float32),不能指定类型 a = torch.tensor([5, 3]) # 创建一个包含 [5, 3] 的一维张量;接受的输入数据类型更广泛,会根据输入数据推断数据类型,可以通过 dtype 参数明确指定数据类型 a = torch.empty(5, 3) # 创建一个未初始化的5x3张量 a = torch.eye(3, 3) # 创建一个对角为1,其余为0的方形张量 a = torch.arange(1, 4) # 返回值[1,2,3] a = torch.arange(4) # 返回值[0,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 # 查看数据类型(torch 一般默认元素时 64 位浮点数,但是深度学习建议使用 32 位浮点数,能加快运算速度) x.type_as(b) # 数据类型转换 x.item() # 从张量中提取元素 x.clone() # 克隆一个张量 x.shape() # 查看一个张量的形状 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对角线元素形成一个一维向量

张量运算

  • 广播规则: 从右到左比较两个张量的每一个维度:
    • 如果两个维度相等,或者其中一个为 1,则这两个维度可以匹配。
    • 如果维度不同且都不为 1,则不能广播。
    • 缺失的维度会被视为 1。
x = torch.randn([1,5,2]) y = torch.arange(24).reshape([2,3,4]).float() # 类型修改 x.short() # 16位整型 x.int() # 32位整型 x.long() # 64位整型 x.float() # 32位浮点型 x.double() # 64位浮点型 # 聚合运算(在指定轴上进行聚合运算🚩) torch.sum(x,dim) # 获取指定维度上的求和(在某个轴上求和,就是将该轴的长度降为 0,如张量形状为[2,5,4],在 axis=1 的轴上求和得到的形状为:[2,4],如果设置参数 keepdims=True,则得到的形状为:[2,1,4]) torch.mean(x,dim) x.sum(dim) x.sum(dim, keepdims=True) # 保持维度不变 x.mean(dim) x.cumsum(axis=0) # 累加求和 # 比较运算 x.max(dim) torch.max(x,dim) # 获取指定维度上的最大值 x.min(dim) torch.min(x,dim) # 获取指定维度上的最小值 x.abs() torch.abs(a) # 绝对值;将张量中的负数用绝对值替换 # 算术元算: # 1. 如果两个张量的形状完全相同,那么逐元素运算会直接进行。 # 2. 如果两个张量的形状不同,但它们可以通过广播机制使得形状匹配,那么运算也可以进行。 x.add_(1) # 如果只有一个数值,则会进行广播 z = x + y torch.add(x,y) # 对应位置相加,要求形状相同,x和y既可以是Tensor也可以是标量 z = x - y torch.sub(x,y) # 对应位置相减,要求形状相同,x和y既可以是Tensor也可以是标量 z = x * y # 哈达马乘积(矩阵对应位置相成) torch.mul(x,y) # 对应位置相乘,要求形状相同,x和y既可以是Tensor也可以是标量 z = x / y torch.div(x,y) # 对应位置相除,要求形状相同,x和y既可以是Tensor也可以是标量 z = 3 * x # 3 乘以 x 中的所有元素(广播) z = 3 + x # 3 加上 x 中的所有元素(广播) # 代数运算 torch.dot(a,b) # 一维向量之间的点积 torch.mv(x,a) # 矩阵与向量相乘,x 必须是矩阵,a 必须是向量 torch.mm(x,y.T) # 二维矩阵之间的叉乘,要求x和y必须是矩阵 torch.matmul(A, B) # 任意维度矩阵乘法,要求第一个矩阵的列数等于第二个矩阵的行数。 # 统计运算 torch.std(x, dim) # 标准差 torch.var(x, dim) # 方差 torch.median(x, dim) # 中位数 # 范数运算(范数是一个向量或矩阵的长度):torch.norm(x) 可以根据 x 的形状和 p 参数的值来计算不同类型的范数 # 1. 向量范数 x = torch.tensor([1.0, -2.0, 3.0]) l1_norm = torch.norm(x, p=1) # L1 范数是张量中所有元素绝对值的和。 l2_norm = torch.norm(x, p=2) # L2 范数是张量中所有元素平方和的平方根,也叫欧几里得范数。norm方法默认p=2 inf_norm = torch.norm(x, p=float('inf')) # 无穷范数是张量中元素绝对值的最大值。 # 2. 矩阵范数 frobenius_norm = torch.norm(x) # Frobenius 范数是矩阵中所有元素平方和的平方根,类似于 L2 范数,但用于矩阵。 spectral_norm = torch.norm(x, p=2) # 计算谱范数 nuclear_norm = torch.norm(x, p=1) # 计算核范数 # 对数、指数、幂函数 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中的数据集
数据集名称
数据集类型
数据集描述
数据集加载方法
加载参数
MNIST
图像分类
手写数字识别数据集,包含 60,000 张训练图像和 10,000 张测试图像
torchvision.datasets.MNIST
root, train=True, transform=None, target_transform=None, download=False
CIFAR-10
图像分类
10类彩色图像数据集,包含 50,000 张训练图像和 10,000 张测试图像
torchvision.datasets.CIFAR10
root, train=True, transform=None, target_transform=None, download=False
CIFAR-100
图像分类
100类彩色图像数据集,包含 50,000 张训练图像和 10,000 张测试图像
torchvision.datasets.CIFAR100
root, train=True, transform=None, target_transform=None, download=False
ImageNet
图像分类
1000类大规模图像数据集
torchvision.datasets.ImageNet
root, split='train', transform=None, target_transform=None
COCO
目标检测/分割
大规模目标检测、分割和标注数据集
torchvision.datasets.CocoDetection
root, annFile, transform=None, target_transform=None
VOC
目标检测
Pascal VOC目标检测数据集
torchvision.datasets.VOCDetection
root, year='2012', image_set='train', download=False, transform=None
FashionMNIST
图像分类
时尚物品分类数据集,结构同MNIST
torchvision.datasets.FashionMNIST
root, train=True, transform=None, target_transform=None, download=False
SVHN
图像分类
街景门牌号数字识别数据集
torchvision.datasets.SVHN
root, split='train', transform=None, target_transform=None, download=False
STL10
图像分类
适用于无监督学习的图像数据集
torchvision.datasets.STL10
root, split='train', transform=None, target_transform=None, download=False
CelebA
人脸属性
大规模人脸属性数据集
torchvision.datasets.CelebA
root, split='train', transform=None, target_transform=None, download=False
LSUN
场景理解
大规模场景理解数据集
torchvision.datasets.LSUN
root, classes='train', transform=None, target_transform=None
Cityscapes
语义分割
城市街景语义分割数据集
torchvision.datasets.Cityscapes
root, split='train', mode='fine', target_type='instance', transform=None
SBU
图像描述
图像描述数据集
torchvision.datasets.SBU
root, transform=None, target_transform=None, download=False
Flickr
图像描述
Flickr图像描述数据集
torchvision.datasets.Flickr8k/30k
root, ann_file, transform=None, target_transform=None
PhotoTour
图像匹配
照片特征匹配数据集
torchvision.datasets.PhotoTour
root, name, transform=None, download=False
Omniglot
少样本学习
torchvision.datasets.Omniglot
root, background=True, transform
Kinetics400
视频分类
torchvision.datasets.Kinetics400
root, frames_per_clip, transform
UCF101
视频分类
torchvision.datasets.UCF101
root, annotation_path, frames_per_clip
HMDB51
视频分类
torchvision.datasets.HMDB51
root, annotation_path, frames_per_clip
一个简单的数据加载例子
# 构建数据集:可以理解为对数据进行摸底并记录相关信息 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 # 将一个数据集分割成多份,常用于分割训练集,验证集和测试集。

数据预处理

这些数据处理方法的选择要根据具体任务和数据特点来决定:
  • 图像任务通常需要更多的数据增强
  • NLP任务可能需要更多的文本预处理
  • 结构化数据可能需要更多的特征工程
  • 时序数据可能需要特殊的采样策略
图像
# 裁剪——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中的操作随机打乱
实例
  1. 数据预处理 :
    1. # 标准化/归一化 transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 缩放到指定范围 transforms.Lambda(lambda x: x / 255.0) # 缩放到[0,1] transforms.Lambda(lambda x: (x - 0.5) * 2) # 缩放到[-1,1] # 数据类型转换 torch.FloatTensor(data) torch.LongTensor(labels)
  1. 数据增强 :
    1. # 图像增强示例 transforms.Compose([ transforms.RandomHorizontalFlip(p=0.5), # 水平翻转 transforms.RandomRotation(10), # 随机旋转 transforms.RandomResizedCrop(224), # 随机裁剪并缩放 transforms.ColorJitter(brightness=0.4), # 亮度调整 transforms.RandomAffine(degrees=0, translate=(0.1,0.1)), # 仿射变换 transforms.RandomErasing(p=0.5) # 随机擦除 ]) # 文本增强示例 def text_augment(text): # 同义词替换 # 回译 # 随机删除 # 随机插入 pass
  1. 数据清洗 :
    1. # 异常值处理 def remove_outliers(data, threshold=3): z_scores = np.abs(stats.zscore(data)) return data[z_scores < threshold] # 缺失值处理 def handle_missing(data): # 删除缺失值 data_cleaned = data.dropna() # 填充缺失值 data_filled = data.fillna(data.mean()) return data_cleaned, data_filled
  1. 数据采样 :
    1. # 处理类别不平衡 from torch.utils.data import WeightedRandomSampler # 计算采样权重 weights = 1. / torch.tensor(class_counts) samples_weights = weights[targets] # 创建采样器 sampler = WeightedRandomSampler( weights=samples_weights, num_samples=len(samples_weights), replacement=True )
  1. 数据加载 :
    1. # 自定义数据集 class CustomDataset(Dataset): def __init__(self, data_path, transform=None): self.data = ... self.transform = transform def __len__(self): return len(self.data) def __getitem__(self, idx): sample = self.data[idx] if self.transform: sample = self.transform(sample) return sample # 数据加载器 train_loader = DataLoader( dataset, batch_size=32, shuffle=True, num_workers=4, pin_memory=True )
  1. 特征工程 :
    1. # 特征选择 from sklearn.feature_selection import SelectKBest, f_classif # 特征缩放 from sklearn.preprocessing import StandardScaler, MinMaxScaler # 特征编码 from sklearn.preprocessing import LabelEncoder, OneHotEncoder
  1. 数据集划分 :
    1. # 训练集、验证集、测试集划分 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y # 保持类别比例 )
  1. 数据流水线 :
    1. class DataPipeline: def __init__(self): self.transforms = [] def add_transform(self, transform): self.transforms.append(transform) def process(self, data): for t in self.transforms: data = t(data) return data
#演示一些常用的图片增强操作 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()
均方误差损失
回归问题,预测连续值
loss = nn.MSELoss()(pred, target)
nn.L1Loss()
平均绝对误差损失
回归问题,对异常值不敏感
loss = nn.L1Loss()(pred, target)
nn.CrossEntropyLoss()
交叉熵损失
多分类问题
loss = nn.CrossEntropyLoss()(logits, labels)
nn.BCELoss()
二元交叉熵损失
二分类问题(需要先sigmoid)
loss = nn.BCELoss()(sigmoid(pred), target)
nn.BCEWithLogitsLoss()
带Logits的二元交叉熵损失
二分类问题(内置sigmoid)
loss = nn.BCEWithLogitsLoss()(pred, target)
nn.NLLLoss()
负对数似然损失
多分类问题(需要先log_softmax)
loss = nn.NLLLoss()(log_softmax(pred), target)
nn.KLDivLoss()
KL散度损失
概率分布的相似度度量
loss = nn.KLDivLoss()(pred_dist.log(), target_dist)
nn.HuberLoss()
Huber损失
回归问题,结合MSE和L1的优点
loss = nn.HuberLoss(delta=1.0)(pred, target)
nn.SmoothL1Loss()
平滑L1损失
回归问题,对异常值较为鲁棒
loss = nn.SmoothL1Loss()(pred, target)
nn.CosineEmbeddingLoss()
余弦嵌入损失
度量两个向量的相似度
loss = nn.CosineEmbeddingLoss()(input1, input2, target)
nn.CTCLoss()
CTC损失
序列学习(如语音识别)
loss = nn.CTCLoss()(log_probs, targets, input_lengths, target_lengths)
nn.MarginRankingLoss()
边界排序损失
排序和排名问题
loss = nn.MarginRankingLoss()(input1, input2, target)
nn.MultiMarginLoss()
多类别边界损失
多分类问题的边界优化
loss = nn.MultiMarginLoss()(pred, target)
nn.TripletMarginLoss()
三元组边界损失
度量学习,如人脸识别
loss = nn.TripletMarginLoss()(anchor, positive, negative)
nn.PoissonNLLLoss()
泊松负对数似然损失
计数数据的回归
loss = nn.PoissonNLLLoss()(pred, target)

损失函数的常用参数

损失函数
重要参数
参数说明
CrossEntropyLoss
weight
各类别的权重,用于处理类别不平衡
ignore_index
忽略特定的标签值,常用于序列任务
reduction
可选'none'/'mean'/'sum',控制损失的归约方式
BCELoss
weight
各样本的权重
reduction
可选'none'/'mean'/'sum',控制损失的归约方式
MSELoss
reduction
可选'none'/'mean'/'sum',控制损失的归约方式

常见的损失函数组合使用场景

组合方式
使用场景
示例
MSE + L1
图像重建任务
loss = mse_loss + 0.1 * l1_loss
CE + KL
知识蒸馏
loss = ce_loss + temperature * kl_loss
Focal Loss
目标检测中解决正负样本不平衡
基于CE的变体

一些实用建议

  • 对于回归问题:
    • 如果数据中有异常值,优先考虑 L1Loss 或 SmoothL1Loss
    • 如果数据分布较正常,可以使用 MSELoss
  • 对于分类问题:
    • 二分类问题推荐使用 BCEWithLogitsLoss(而不是 BCELoss)
    • 多分类问题推荐使用 CrossEntropyLoss(而不是手动组合 Softmax + NLLLoss)
  • 对于特殊任务:
    • 生成模型可能需要组合多个损失函数
    • 对抗训练中的损失函数需要分别针对生成器和判别器

优化器:torch.optim

torch中常见的优化算法

优化器调用方法
名称
适用场景
示例
torch.optim.SGD
随机梯度下降
最基础的优化器,适用于大多数简单场景
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
torch.optim.Adam
Adam优化器
深度学习最常用的优化器,适用于大多数场景
optimizer = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999))
torch.optim.AdamW
AdamW优化器
解决Adam中权重衰减实现问题的改进版本
optimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)
torch.optim.RMSprop
RMSprop优化器
适用于RNN等循环神经网络
optimizer = optim.RMSprop(model.parameters(), lr=0.01, alpha=0.99)
torch.optim.Adagrad
Adagrad优化器
适用于稀疏数据场景
optimizer = optim.Adagrad(model.parameters(), lr=0.01)
torch.optim.Adadelta
Adadelta优化器
不需要设置初始学习率,适用于对超参数敏感的模型
optimizer = optim.Adadelta(model.parameters(), rho=0.9)
torch.optim.Adamax
Adamax优化器
Adam的一个变体,对学习率上限提供了更好的控制
optimizer = optim.Adamax(model.parameters(), lr=0.002)
torch.optim.ASGD
ASGD优化器
随机平均梯度下降,适用于大规模凸优化问题
optimizer = optim.ASGD(model.parameters(), lr=0.01)
torch.optim.LBFGS
L-BFGS优化器
适用于小批量数据的全批量优化
optimizer = optim.LBFGS(model.parameters(), lr=1)
torch.optim.SparseAdam
稀疏Adam优化器
适用于稀疏梯度更新的场景
optimizer = optim.SparseAdam(model.parameters(), lr=0.001)
torch.optim.RAdam
修正Adam优化器
解决Adam预热期的问题,训练更稳定
optimizer = optim.RAdam(model.parameters(), lr=0.001)

优化器的常用参数

参数
说明
适用优化器
lr
学习率
所有优化器
weight_decay
权重衰减系数
所有优化器
momentum
动量系数
SGD
betas
一阶和二阶矩估计的指数衰减率
Adam系列
eps
数值稳定性系数
大多数优化器

学习率调度器

调度器
说明
示例
StepLR
按固定步长降低学习率
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
MultiStepLR
在指定步数降低学习率
scheduler = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)
ExponentialLR
指数衰减学习率
scheduler = ExponentialLR(optimizer, gamma=0.95)
CosineAnnealingLR
余弦退火调整学习率
scheduler = CosineAnnealingLR(optimizer, T_max=100)
ReduceLROnPlateau
当指标停止改善时降低学习率
scheduler = ReduceLROnPlateau(optimizer, mode='min', patience=10)
OneCycleLR
一个周期的学习率调整
scheduler = OneCycleLR(optimizer, max_lr=0.1, epochs=10, steps_per_epoch=100)

优化器的选择建议

  • 初始选择 :
    • 一般项目首选 Adam
    • 需要更好泛化性能时考虑 AdamW
    • 计算资源受限时可以使用 SGD+Momentum
  • 特殊场景 :
    • 图像分类:SGD+Momentum 通常效果更好
    • NLP任务:Adam/AdamW 是主流选择
    • 生成模型:Adam 及其变体较为常用

常见问题解决方案

  • 训练不稳定:降低学习率或使用梯度裁剪
  • 收敛太慢:增加学习率或使用自适应优化器
  • 过拟合:增加权重衰减或使用学习率调度
  • 欠拟合:增加学习率或减少权重衰减

使用示例

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.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) # 梯度累积 for i in range(accumulation_steps): loss = criterion(model(data), target) loss = loss / accumulation_steps loss.backward() if (i + 1) % accumulation_steps == 0: optimizer.step() optimizer.zero_grad() # 不同层使用不同学习率 optimizer = torch.optim.Adam([ {'params': model.base.parameters()}, {'params': model.classifier.parameters(), 'lr': 1e-3} ], lr=1e-4)

保存和加载模型

 
PyTorch | 保存和加载模型(包含在训练器的模型保存和加载可以看这里

方法详解

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张量的最内层内容进行合并,并且在合并后的内容外增加一维,
       

      在指定轴上进行聚合运算

      最值计算

      x = torch.arange(24).reshape([2,3,4]) """ tensor([[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]], [[12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]]]) """ x[0,2,2] = 33 x.min(dim=0) # 此时轴为 0,其中有 2 个元素(此处为两个矩阵),将两个元素的对应位置进行比较(两个矩阵的对应位置的元素进行比较),返回最小值组成的矩阵及其对应的索引(因为只有两个元素,所以这个索引是在 0 和 1 之间);返回的结果会将轴 0 上的长度降为 0,所以返回的形状是:[3,4] """ torch.return_types.min( values=tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 22, 11]]), indices=tensor([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0]])) """ x[0,2,2] = -1 x.min(dim=1) # 此时轴为 1,其中有 3 个元素(此处三个向量),将三个元素的对应位置进行比较(三个向量的对应位置的元素进行比较),返回最小值组成的向量及其对应的索引(因为只有三个元素,所以这个索引是在 0、1、2 之间);返回的结果会将轴 1 上的长度降为 0,所以返回的形状是:[2,4] """ torch.return_types.min( values=tensor([[ 0, 1, -1, 3], [12, 13, 14, 15]]), indices=tensor([[0, 0, 2, 0], [0, 0, 0, 0]])) """ x[0,2,3] = -3 x.min(dim=2) # 此时轴为 2,其中有 4 个元素(此处四个标量),在四个元素(标量)中求最小值,返回最小值组成的向量及其对应的索引(因为有四个元素,所以这个索引是在 0、1、2、3 之间);返回的结果会将轴 2 上的长度降为 0,所以返回的形状是:[2,3] """ torch.return_types.min( values=tensor([[ 0, 4, -3], [12, 16, 20]]), indices=tensor([[0, 0, 3], [0, 0, 0]])) """

      求和、求均值运算

      x = torch.arange(24).reshape([2,3,4]) """ tensor([[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]], [[12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]]]) """ x.sum() # 不指定轴,求所有元素的总和 """ tensor(276.) """ x.sum(dim=0) # 此时轴为 0,其中有 2 个元素(此处为两个矩阵),将两个元素的对应位置进行相加(两个矩阵的对应位置的元素进行相加),返回求和后的矩阵;返回的结果会将轴 0 上的长度 2 降为 0,所以返回的形状是:[3,4] """ tensor([[12, 14, 16, 18], [20, 22, 24, 26], [28, 30, 55, 34]]) """ x.sum(dim=1) # 此时轴为 1,其中有 3 个元素(此处三个向量),将三个元素的对应位置进行相加(三个向量的对应位置的元素进行相加),返回求和后的向量;返回的结果会将轴 1 上的长度 3 降为 0,所以返回的形状是:[2,4] """ tensor([[12, 15, 7, 21], [48, 51, 54, 57]]) """ x.sum(dim=2) # 此时轴为 2,其中有 4 个元素(此处四个标量), 将四个元素进行相加,返回求和后的标量;返回的结果会将轴 2 上的长度 4 降为 0,所以返回的形状是:[2,3] """ tensor([[ 6, 22, 13], [54, 70, 86]]) """ x.sum(dim=[0,1]) # 求 0、1 轴上的和,最终返回值是将 0、1 轴的维度去掉,返回一个长度为 4 的向量;计算过程可以理解为先求 0 轴上的和,在此结果上再求 1 轴上的和。 """ tensor([60., 66., 61., 64.]) """ x = torch.arange(24).reshape([2,3,4]).float() # mean方法要求输入张量必须是浮点数,所以要进行转换;其返回的值的形状与sum 方法一至。 x.mean() x.mean(dim=0) x.mean(dim=1) x.mean(dim=2) x.mean(dim=[0,1]) # 累加求和 x = torch.arange(24).reshape([2,3,4]) x.cumsum(axis=0) """ tensor([[[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.]], [[12., 14., 16., 18.], [20., 22., 24., 26.], [28., 30., 32., 34.]]]) """ x.cumsum(axis=1) """ tensor([[[ 0., 1., 2., 3.], [ 4., 6., 8., 10.], [12., 15., 18., 21.]], [[12., 13., 14., 15.], [28., 30., 32., 34.], [48., 51., 54., 57.]]]) """ x.cumsum(axis=2) """ tensor([[[ 0., 1., 3., 6.], [ 4., 9., 15., 22.], [ 8., 17., 27., 38.]], [[12., 25., 39., 54.], [16., 33., 51., 70.], [20., 41., 63., 86.]]]) """
对于本文内容有任何疑问, 可与我联系.