PyTorch实战:从零掌握深度学习核心
课程的相关配套代码放到了下面GitHubPytorch学习笔记配套代码目录Pytorch 学习笔记1. Pytorch介绍2. tensorboard的使用3. transforms4. 常用的transformer类讲解5. torchvision.datasets6. dataloader7. nn.Module 神经网络基本骨架8.1 卷积操作8.2 神经网络-卷积层9. 最大池化10. 激活函数11. 线形层12 小实战Sequential13. 损失函数与反向传播14. 优化器15. 现有网络模型的使用和修改16. 模型的保存与加载17. 完整的模型训练套路18. 使用GPU训练常用方法119. 完整的模型验证套路Pytorch 学习笔记1. Pytorch介绍Pytorch基础功能张量自动求导反向传播神经网络 nn.Module前向传播与损失计算优化器 optim.AdamPyTorch 生态系统torchvisiontorchtexttorchaudio其他专业库PyTorch 核心torch.nn神经网络torch.optim优化器torch.utils工具函数torch核心张量计算autograd自动微分torch.utils.data数据加载2. tensorboard的使用作用用于在训练中对训练过程进行可视化# 使用示例 writer SummaryWriter(logs) writer.add_image(train,image_array,1,dataformatsHWC) writer.close()三行代码讲解创建一个名为logs的目录在界面中加入图像展示设置展示窗口名称等设置结束行代码使用方法运行代码后会根据路径生成logs文件夹及文件终端运行tensorboard --logdirlogs 进入终端中的端口即可设置指定端口防止多人使用服务器导致端口冲突 tensorboard --logdirlogs --port xxxx3. transformstransforms是一个数据处理/数据增强的库。面向Tensor / PIL图像两格式可互相转换组合使用Composefrom torchvision import transforms4. 常用的transformer类讲解Totensor转换为tensor格式Normalize标准化Resize改变图片大小Compose组合多个类RandomCrop随即裁剪这里只放了一些常见的还有很多类。使用时注意输入输出结合pytorch官方文档关注方法需要什么样的参数5. torchvision.datasets一个库可以直接下载常用数据集import torchvision train_set torchvision.datasets.CIFAR10(root./dataset,trainTrue,downloadTrue) test_set torchvision.datasets.CIFAR10(root./dataset,trainFalse,downloadTrue) ​ test_data torchvision.datasets.CIFAR10(./dataset,trainFalse,transformtorchvision.transforms.ToTensor())参数设置root数据集的下载/读取路径trainTrue参数为数据集中的训练集数据False参数为数据集中的测试集数据downloadTrue若没有数据集会自动下载若有数据集的压缩包会将其解压False仅从root读取数据集若不存在则报错transform对数据进行函数操作CtrlP查看函数的参数6. dataloader数据加载器从datasets中取数据加载到神经网络中test_data torchvision.datasets.CIFAR10(./dataset,trainFalse,transformtorchvision.transforms.ToTensor()) ​ test_loader DataLoader(datasettest_data,batch_size64,shuffleFalse,num_workers0,drop_lastTrue)一些常见的参数dataset数据集对象batch_size每次从数据集中拿取的数据数量shuffleTrue每轮epoch进行随机拿取数据False每轮epoch顺序拿取数据每轮同一批次拿取的数据一致num_workers指定数据加载的工作进程数默认 0主进程加载设为大于 0 的数值时会创建对应数量子进程并行加载数据加速预处理。所有数据加载相关代码必须放在if __name__ __main__:内这是Windows 多进程数据加载的强制要求Linux/macOS 无此限制drop_last如果最后一批数据不够数量舍弃最后一批数据。这部分如果没完全理解参考06代码调整运行看看变化。7. nn.Module 神经网络基本骨架Pytorch中构建自定义神经网络必须继承Module类必须实现的核心方法 :forward( )前向传播需手动实现手动定义前向传播过程代码示例import torch from torch import nn class Module_study(nn.Module): def __init__(self): super(Module_study,self).__init__() def forward(self, input): output input 1 return output mod_stu Module_study() # 调用时直接调用类名即可无需调用forward x torch.tensor(1.0) output mod_stu(x) print(output)8.1 卷积操作卷积操作卷积核一个小矩阵决定了卷积后输出特征图的通道数Stride卷积核移动步长决定了卷积后输出特征图的大小每次空间尺寸 H,W 下采样一半通道数通常增加到约 2 倍卷积计算用卷积核对输入图像局部区域进行乘积核计算对输入的矩阵和卷积核进行格式转换PyTorch 文档中定义的卷积输入和卷积核矩阵权重矩阵shape有4个参数所以需要使用torch.reshape对这两个矩阵进行形状转换。import torch input torch.tensor([[1, 2, 0, 3, 1], [0, 1, 2, 3, 1], [1, 2, 1, 0, 0], [5, 2, 3, 1, 1], [2, 1, 0, 1, 1]]) # 卷积核 kernel torch.tensor([[1, 2, 1], [0, 1, 0], [2, 1, 0]]) input torch.reshape(input, (1, 1, 5, 5)) kernel torch.reshape(kernel, (1, 1, 3, 3))卷积函数stride参数的使用# 卷积函数的使用 (stride) import torch import torch.nn.functional as F # 定义input,kernel矩阵... output F.conv2d(input,kernel,stride1) output2 F.conv2d(input,kernel,stride2)padding——对原矩阵周围的扩展例子说明原图像大小32stride1,若padding0输出大小为30。使用padding输出大小为32# padding的使用 import torch import torch.nn.functional as F # 定义input,kernel矩阵... ​ output3 F.conv2d(input,kernel,stride1,padding1)注8.0的目的是讲解卷积的计算原理AI项目使用时推荐使用8.1的方法来构建模型。8.2 神经网络-卷积层三种卷积函数nn.Conv1d 一维卷积nn.Conv2d 二维卷积图片nn.Conv3d 三维卷积参数介绍import torch from torch import nn from torch.nn import Conv2d ​ class Run(nn.Module): def __init__(self): super(Run,self).__init__() self.conv1 Conv2d(in_channels3,out_channels6,kernel_size3, stride1,padding0) def forward(self,x): x self.conv1(x) return xin_channels3输入通道数适配 CIFAR10数据集 的 3 通道彩色图片out_channels6输出通道数卷积核的数量最终输出 6 个特征图kernel_size3卷积核大小3×3只需传整数默认正方形stride1/padding0步长 / 填充和之前的一样。9. 最大池化池化层的目的对图片进行压缩减小训练时的数据量提高速度一般都会在卷积层后加入一层池化层Pytorch池化函数的主要参数kernel_size池化核大小stride步长默认值 kernel_size)padding周围区域填充ceil_mode对边缘小于池化核大小的区域是否取舍见下图ceil_mode若为True即ceil则保留不完整窗口对小于kernel_size的区域也进行最大池化floor向下取整 ceil向上取整池化层计算方法类似卷积池化核在输入图像上扫。以上图为例在输入图像一个3×3的区域内取最大的数作为该区域的池化结果10. 激活函数ReLUinput -1 Relu(input, inplaceTrue) # 结果 input 0 ​ input -1 output Relu(input, inplaceFalse) 结果 input -1 output 0 Sigmoid11. 线形层就是全连接层作用将输入特征映射到更高维或更低维的特征空间核心对输入进行y wxb 操作import torch.nn as nn # 展平操作 flatten nn.Flatten() # 线性层定义 linear_hidden nn.Linear(in_features3072, out_features1024) ​ img torch.randn(1, 3, 32, 32) img_flat flatten(img) # 形状变为 [1, 3072] output_hidden linear_hidden(img_flat) # 形状变为 [1, 1024]# 接上面的隐藏层输出 [1, 1024] linear_output nn.Linear(in_features1024, out_features10) output_final linear_output(output_hidden) # 形状变为 [1, 10]这个[1, 10]的输出向量每个元素对应一个类别的原始得分logit再经过 Softmax 函数就可以转换为每个类别的概率值。12 小实战SequentialSequential的作用Sequential函数可以直接把神经网络的每一层套进去这样实例化时只需要用一个变量实例化即可。class Run(nn.Module): def __init__(self): super(Run, self).__init__() self.model1 Sequential( Conv2d(3, 32, 5, padding2), MaxPool2d(2), Conv2d(32, 32, 5, padding2), MaxPool2d(2), Conv2d(32, 64, 5, padding2), MaxPool2d(2), Flatten(), Linear(1024, 64), Linear(64, 10) ) def forward(self, x): x self.model1(x) return x上述例子也就搭建了一个简单的神经网络13. 损失函数与反向传播损失函数计算模型推理结果与实际结果的差异类似考试的分数。损失值越高差异越大。反向传播计算梯度相当于告诉模型他需要调整的方向。因为有损失所以要像靠近推理结果的方向调整。总结损失函数是告诉模型与目标差异多大反向传播是告诉模型改进的方向。# 代码示例 from torch import nn loss nn.CrossEntropyLoss()# 损失函数 for data in dataloader: imgs, targets data outputs run(imgs) result_loss loss(outputs,targets) result_loss.backward()# 反向传播14. 优化器优化器核心作用调整模型参数权重偏置等让loss不断下降。定义以SGD为例随机梯度下降这里的lr是学习率run是实例化的模型类参考上面# 1. 定义优化器 optim torch.optim.SGD(run.parameters(),lr0.01)PyTorch 训练模型的 “固定套路”缺一不可# 第一步梯度清零必须 optim.zero_grad() # 原因PyTorch中梯度会累加如果不清零本轮的梯度会和上一轮叠加导致参数更新错误。 ​ # 第二步反向传播计算梯度 result_loss.backward() # 作用从损失函数出发反向计算每个参数的梯度即参数往哪个方向改能让损失降低。 ​ # 第三步更新参数 optim.step() # 作用优化器根据计算出的梯度按照SGD的规则更新模型参数核心操作。15. 现有网络模型的使用和修改在网络后添加层model1.add_module(add_linear,nn.Linear(10,4))修改网络层model2 copy.deepcopy(model1) model2[8] nn.Linear(64,4)深拷贝model2 copy.deepcopy(model1) # 完全复制model1的结构16. 模型的保存与加载即将训练好的模型保存为pth权重文件模型保存的两种方式import torchvision import torch vgg16 torchvision.models.vgg16(pretrainedFalse) # 保存方式1--保存模型结构及模型参数 torch.save(vgg16,vgg16_method1.pth) ​ # 保存方式2--仅保存模型参数存为字典不保存模型结构官方推荐 torch.save(vgg16.state_dict(),vgg16_method2.pth)模型加载import torch import torchvision # 保存方式1对应的加载模型结构 参数方式 model torch.load(vgg16_method1.pth, weights_onlyFalse) print(model) ​ # 保存方式2模型参数保存成字典的形式官方推荐存储空间小一点 vgg16 torchvision.models.vgg16(pretrainedFalse) vgg16.load_state_dict(torch.load(vgg16_method2.pth)) print(vgg16)17. 完整的模型训练套路准备并加载数据集# 准备数据集 train_data torchvision.datasets.CIFAR10( root./data, trainTrue, transformtorchvision.transforms.ToTensor(), downloadTrue ) ​ test_data torchvision.datasets.CIFAR10( root./data, trainFalse, transformtorchvision.transforms.ToTensor(), downloadTrue ) # length 长度 train_data_size len(train_data) test_data_size len(test_data) # 利用 DataLoader 来加载数据集 train_dataloader DataLoader(train_data, batch_size64) test_dataloader DataLoader(test_data, batch_size64)创建网络模型# 这个calss一般放在单独的model文件中 class Run(nn.Module): def __init__(self): super(Run, self).__init__() self.model1 Sequential( Conv2d(3, 32, 5, padding2), MaxPool2d(2), Conv2d(32, 32, 5, padding2), MaxPool2d(2), Conv2d(32, 64, 5, padding2), MaxPool2d(2), Flatten(), Linear(1024, 64), Linear(64, 10) ) ​ def forward(self, x): x self.model1(x) return x ​ # 实例化 # 创建网络模型 run Run()设置损失函数和优化器# 损失函数 loss_fn nn.CrossEntropyLoss() ​ # 优化器 learning_rate 1e-2 optimizer torch.optim.SGD(run.parameters(), lrlearning_rate)log训练轮数设置# 设置训练网络的一些参数 # 记录训练的次数 total_train_step 0 # 记录测试的次数 total_test_step 0 # 训练的轮数 epoch 10模型训练for i in range(epoch): print(--------第 {} 轮训练开始--------.format(i 1)) ​ # 训练步骤开始 for data in train_dataloader: imgs, targets data outputs run(imgs) loss loss_fn(outputs, targets) ​ # 优化器优化模型 optimizer.zero_grad() #梯度清零 loss.backward() #反向传播 optimizer.step() #优化 ​ total_train_step total_train_step 1 if total_train_step % 100 0: print(训练次数{}, Loss{}.format(total_train_step, loss)) writer.add_scalar(train_loss,loss.item(),total_train_step)模型测试# 也在上面训练的epoch循环里 # 测试步骤开始 total_test_loss 0 total_accuracy 0 with torch.no_grad():# 测试中不需要对梯度调整设置梯度都为0 for data in test_dataloader: imgs,targets data outputs run(imgs) loss loss_fn(outputs,targets) total_test_loss total_test_lossloss.item() accuracy (outputs.argmax(1) targets).sum() total_accuracy total_accuracy accuracy最后选择一种模型保存策略保存效果较好的模型18. 使用GPU训练常用方法1方法1定义device用to()函数常用# 定义一个device训练设备 device torch.device(cpu) device torch.device(cuda) ​ # 模型 run Run() run run.to(device) # 损失函数 loss_fn nn.CrossEntropyLoss() loss_fn loss_fn.to(device) # 数据 imgs, targets data imgs imgs.to(device) targets targets.to(device)device的几种写法device torch.device(cuda) # 使用默认显卡 device torch.device(cuda:0) # 指定了显卡序列号如果单显卡设别和上面没区别 device torch.device(cuda if torch.cuda.is_available() else cpu)# 有gpu就用gpu没有则用cpu常用方法2使用cuda( )函数不常用cuda( )函数的使用对象网络模型数据输入标注损失函数使用cuda( )函数时为了防止设备没有GPU而报错可以加一个if条件判断。# Run是模型的实例化类 run Run() run Run() if torch.cuda.is_available(): run run.cuda() # 损失函数 loss_fn nn.CrossEntropyLoss() if torch.cuda.is_available(): loss_fn loss_fn.cuda() # 数据 for data in train_dataloader: imgs, targets data if torch.cuda.is_available(): imgs imgs.cuda() targets targets.cuda()19. 完整的模型验证套路保存训练后的模型实例化保存的模型文件model输入注意output时加入2行model.eval() # 模型切换到评估模式 with torch.no_grad(): # 关闭自动求导节省性能 output model(image)如何查看训练模型的分类种类分类模型在训练中加载测试集的位置加断点调试模式查看变量可看到标签。完结撒花