PyTorch深度学习快速入门教程--学习笔记

news2024/11/15 14:01:40

目录

P4 PyCharm和Jupyter的对比

P5 PyTorch加载数据

P6 Dataset类代码实现

P7 Tensorboard 写日志

读取日志文件

Tensorboard 读图片

P10 Transforms使用

Transforms用途

常见的Transforms工具

P14 torchvision数据集使用

P15 Dataloader使用

 P16   nn.Module模块使用

P17 卷积原理

步幅、填充原理

P18 神经网络——卷积层 

搭建卷积层

​编辑

卷积层处理图片

Tensorboard显示

P19最大池化层

池化层处理数据

池化层处理图片

P20 神经网络——非线性激活

P21 神经网络——线性层及其他层

Linear Layers线性层

线性拉平

P22 搭建小实战和顺序使用

​编辑

搭建神经网络

Sequential神经网络

 Tensorboard显示网络(强推!)

P23 损失函数与逆向传播

损失函数反向传播

 P24 优化器

SGD随机梯度下降

神经网络优化一轮

 神经网络优化多轮

 神经网络学习率优化

P25 网络模型使用及修改

下载网络模型

​编辑

查看函数用法

网络模型添加

网络模型修改

P26 网络模型保存与读取

网络模型保存(方式一)

网络模型导入(方式一)

网络模型保存(方式二)

网络模型导入(方式二)

网络陷阱

P27 完整模型训练套路

CIFAR 10 model 网络模型

 DataLoader加载数据集

测试网络正确

网络训练数据

查看训练损失

保存每一轮后参数

argmax作用

打印正确率(最终版代码)

特殊层作用

P30 利用GPU训练

方式一

方式二

运行Terminal语句

P32 完整模型验证套路

验证狗是否识别

P33 查看开源项目

parser.add_argument


本文基于B站“我是土堆”的课程记录学习笔记,原视频讲解:P4. PyCharm及Jupyter使用及对比_哔哩哔哩_bilibili

P4 PyCharm和Jupyter的对比

测试程序:

#用来测试的错误代码,a是字符型,b是整型,所以加在一起会报错
print("Start")
a = "hello word"
b = 2019
c = a + b
print(c)

#正确的代码,把b也写成字符型
print("Start")
a = "hello word"
b = "2019"
c = a + b
print(c)

python控制台运行代码可读性比较差,可以用来做调试;Jupyter可用于小项目或调试。

P5 PyTorch加载数据

Dataset:提供一种方式去获取数据及其lable。

要实现的功能:

  • 如何获取每一数据及其lable
  • 告诉我们总共有多少个数据(知道训练的时候要迭代多少次)

在Jupyter里查看Dataset的官方文档解释

from torch.utils.data import Dataset
help(Dataset)

#第二种查看方式
Dataset??
  • 函数def __init__(self,xx,xx):

初始化类,创建特例实例时要运行的函数,为整个class提供共一个全局变量,为后面的函数如getitem,length等方法提供它们需要的一些量。这个类中定义的一些self.xx=...能够成为全局变量,方便后续的使用。xx为所需的全局变量

  • 函数def __getitem__(self,idx):

idx一般作为数据的编号,根据图片地址的列表获取每一条数据。

  • 函数def __len__(self):

获取数据集的长度,有多少条数据。

P6 Dataset类代码实现

数据集下载:https://download.pytorch.org/tutorial/hymenoptera_data.zip

from torch.utils.data import Dataset
from PIL import Image
import os

class MyData(Dataset):     
    def __init__(self,root_dir,label_dir):    # 该魔术方法当创建一个事例对象时,会自动调用该函数
        self.root_dir = root_dir # self.root_dir 相当于类中的全局变量
        self.label_dir = label_dir     
        self.path = os.path.join(self.root_dir,self.label_dir) # 字符串拼接,根据是Windows或Lixus系统情况进行拼接               
        self.img_path = os.listdir(self.path) # 获得路径下所有图片的地址
        
    def __getitem__(self,idx):
        img_name = self.img_path[idx]
        img_item_path = os.path.join(self.root_dir,self.label_dir,img_name)            
        img = Image.open(img_item_path)
        label = self.label_dir
        return img, label
    
    def __len__(self):
        return len(self.img_path)
    
root_dir = "Data/FirstTypeData/train"
ants_label_dir = "ants"
bees_label_dir = "bees"
ants_dataset = MyData(root_dir, ants_label_dir)
bees_dataset = MyData(root_dir, bees_label_dir)
print(len(ants_dataset))
print(len(bees_dataset))
train_dataset = ants_dataset + bees_dataset # train_dataset 就是两个数据集的集合了     
print(len(train_dataset))

img,label = train_dataset[200]
print("label:",label)
img.show()

Datalodader:为后面的网络提供不同的数据形式。将数据打包

P7 Tensorboard 写日志

from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs") # 创建一个logs文件夹,writer写的文件都在该文件夹下
#writer.add_image()
for i in range(100):
    writer.add_scalar("y=2x",2*i,i)
writer.close()

运行之后会产生一个log目录和一个日志文件

读取日志文件

 在 Anaconda 终端里面,激活py3.6.3环境,再输入

 tensorboard --logdir=logs #你自己的logs路径

命将网址赋值浏览器的网址栏,回车,即可查看tensorboard显示日志情况。

② 为避免多人使用端口导致冲突,也可以在后面加上后缀,使得端口独立

tensorboard --logdir=C:\Users\wangy\Desktop\03CV\logs --port=6007

③ 输入网址可得Tensorboard界面。

Tensorboard 读图片

from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np

img_path1 = "Data/FirstTypeData/train/ants/0013035.jpg" 
img_PIL1 = Image.open(img_path1)
img_array1 = np.array(img_PIL1)

img_path2 = "Data/SecondTypeData/train/bees_image/17209602_fe5a5a746f.jpg" 
img_PIL2 = Image.open(img_path2)
img_array2 = np.array(img_PIL2)

writer = SummaryWriter("logs") 
writer.add_image("test",img_array1,1,dataformats="HWC") # 1 表示该图片在第1步
writer.add_image("test",img_array2,2,dataformats="HWC") # 2 表示该图片在第2步                   
writer.close()

通过这种方式可以很直观的观察训练过程中给model提供了哪些数据,或者说想对model测试的时候可以看到它每阶段的输出结果,或者可以看到每个阶段的一些不同的显示。

P10 Transforms使用

Transforms用途

① Transforms当成工具箱的话,里面的class就是不同的工具。例如像totensor、resize这些工具。

② Transforms拿一些特定格式的图片,经过Transforms里面的工具,获得我们想要的结果。

transforms.Totensor使用

from torchvision import transforms
from PIL import Image

img_path = "Data/FirstTypeData/val/bees/10870992_eebeeb3a12.jpg"
img = Image.open(img_path)  

tensor_trans = transforms.ToTensor()  # 创建 transforms.ToTensor类 的实例化对象
tensor_img = tensor_trans(img)  # 调用 transforms.ToTensor类 的__call__的魔术方法   
print(tensor_img)

需要Tensor数据类型原因

Tensor有一些属性,比如反向传播、梯度等属性,它包装了神经网络需要的一些属性。

常见的Transforms工具

Transforms的工具主要关注他的输入、输出、作用。

Compose()用法
Compose()中的参数需要是一个列表,Python中,列表的表示形式为[数据1,数据2,….]
在Compose中,数据需要是 transforms类型,所以得到,Compose([transforms参数1,transforms参数2....]

Compose类就是把不同的transform结合在一起。

transforms.Compose([
    transforms.Centercrop(10),
    transforms.ToTensor(),
])

P14 torchvision数据集使用

pytorch提供的数据集API:torchvision — Torchvision master documentation

 torchvision数据集介绍

① torchvision中有很多数据集,当我们写代码时指定相应的数据集指定一些参数,它就可以自行下载。

② CIFAR-10数据集包含60000张32×32的彩色图片,一共10个类别,其中50000张训练图片,10000张测试图片。

运行代码下载数据集:

import torchvision
train_set = torchvision.datasets.CIFAR10(root="./dataset",train=True,download=True) # root为存放数据集的相对路线
test_set = torchvision.datasets.CIFAR10(root="./dataset",train=False,download=True) # train=True是训练集,train=False是测试集  

查看CIFAR10数据集内容

import torchvision
train_set = torchvision.datasets.CIFAR10(root="./dataset",train=True,download=True) # root为存放数据集的相对路线
test_set = torchvision.datasets.CIFAR10(root="./dataset",train=False,download=True) # train=True是训练集,train=False是测试集  

print(test_set[0])       # 输出的3是target 
print(test_set.classes)  # 测试数据集中有多少种

img, target = test_set[0] # 分别获得图片、target
print(img)
print(target)

print(test_set.classes[target]) # 3号target对应的种类
img.show()

与Transform结合

定义Transform的图像变换,下载数据时加入transform

dataset_transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])     
train_set = torchvision.datasets.CIFAR10(root="./dataset",train=True,transform=dataset_transform,download=True) # 将ToTensor应用到数据集中的每一张图片,每一张图片转为Tensor数据类型     
test_set = torchvision.datasets.CIFAR10(root="./dataset",train=False,transform=dataset_transform,download=True)   

P15 Dataloader使用

① Dataset只是去告诉我们程序,我们的数据集在什么位置,数据集第一个数据给它一个索引0,它对应的是哪一个数据。

② Dataloader就是把数据加载到神经网络当中,Dataloader所做的事就是每次从Dataset中取数据,至于怎么取,是由Dataloader中的参数决定的。

加载数据的参数中:dataset数据位置,batch_size每次加载的数据量,shuffle是否要打乱数据,num_works设置多进程,默认0只设置主进程,dorp_last选数据如果有不足batch_size个剩余数的话要不要丢弃,True丢弃。

import torchvision
from torch.utils.data import DataLoader

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())               
img, target = test_data[0]
print(img.shape)
print(img)

# batch_size=4 使得 img0, target0 = dataset[0]、img1, target1 = dataset[1]、img2, target2 = dataset[2]、img3, target3 = dataset[3],然后这四个数据作为Dataloader的一个返回      
test_loader = DataLoader(dataset=test_data,batch_size=4,shuffle=True,num_workers=0,drop_last=False)      
# 用for循环取出DataLoader打包好的四个数据
for data in test_loader:
    imgs, targets = data # 每个data都是由4张图片组成,imgs.size 为 [4,3,32,32],四张32×32图片三通道,targets由四个标签组成             
    print(imgs.shape)
    print(targets)

dataloder返回的是图片和标签的打包形式,比如batch_size=4,返回的是4个img叠起来的imgs和4个target叠起来的targets。下面可以看到 imgs的形状是4个(3,32,32)形状的图片,而targets包含了4个标签分别是2,3,6,8对应四张图片。

Tensorboard展示+Dataloader多轮次

shuffle如果设置为False则第一个epoch和第二个epoch的图片顺序都一样,设置为True则每次取的图片顺序都不一样。

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())               
# batch_size=4 使得 img0, target0 = dataset[0]、img1, target1 = dataset[1]、img2, target2 = dataset[2]、img3, target3 = dataset[3],然后这四个数据作为Dataloader的一个返回      
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0,drop_last=True)      
# 用for循环取出DataLoader打包好的四个数据
writer = SummaryWriter("logs")
for epoch in range(2):
    step = 0
    for data in test_loader:
        imgs, targets = data # 每个data都是由4张图片组成,imgs.size 为 [4,3,32,32],四张32×32图片三通道,targets由四个标签组成             
        writer.add_images("Epoch:{}".format(epoch),imgs,step)
        step = step + 1
    
writer.close()

 P16   nn.Module模块使用

torch.nn — PyTorch 2.4 documentation

关于神经网络的使用在torch.nn里边,其中nn是Neural network的缩写

nn.Module模块使用

① nn.Module是对所有神经网络提供一个基本的类。init初始化函数,forward前向传播函数接受输入,处理后输出。x指输入

import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        return F.relu(self.conv2(x))

② 我们的神经网络是继承nn.Module这个类,即nn.Module为父类,nn.Module为所有神经网络提供一个模板,对其中一些我们不满意的部分进行修改。

import torch
from torch import nn

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()  # 继承父类的初始化
        
    def forward(self, input):          # 将forward函数进行重写
        output = input + 1
        return output
    
tudui = Tudui()
x = torch.tensor(1.0)  # 创建一个值为 1.0 的tensor
output = tudui(x)
print(output)

super(Myclass, self).__init__()

① 简单理解就是子类把父类的__init__()放到自己的__init__()当中,这样子类就有了父类的__init__()的那些东西。

② Myclass类继承nn.Module,super(Myclass, self).__init__()就是对继承自父类nn.Module的属性进行初始化。而且是用nn.Module的初始化方法来初始化继承的属性。

③ super().__init()__()来通过初始化父类属性以初始化自身继承了父类的那部分属性;这样一来,作为nn.Module的子类就无需再初始化那一部分属性了,只需初始化新加的元素。

③ 子类继承了父类的所有属性和方法,父类属性自然会用父类方法来进行初始化。

forward函数

① 使用pytorch的时候,不需要手动调用forward函数,只要在实例化一个对象中传入对应的参数就可以自动调用 forward 函数。

② 因为 PyTorch 中的大部分方法都继承自 torch.nn.Module,而 torch.nn.Module 的__call__(self)函数中会返回 forward()函数 的结果,因此PyTroch中的 forward()函数等于是被嵌套在了__call__(self)函数中;因此forward()函数可以直接通过类名被调用,而不用实例化对象。

P17 卷积原理

torch.nn.functional.conv2d — PyTorch 2.4 documentation

① 卷积核不停的在原图上进行滑动,对应元素相乘再相加。

② 下图为每次滑动移动1格,然后再利用原图与卷积核上的数值进行计算得到缩略图矩阵的数据,如下图右所示。

stride=1时默认横向、纵向的步幅都为1。stride=2时,表示横向和纵向步幅都为2,即计算完第一个之后,卷积核移两步。先横向走再纵向走

代码实现:

import torch
import torch.nn.functional as F

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]])

print(input.shape)
print(kernel.shape)
input = torch.reshape(input, (1,1,5,5))
kernel = torch.reshape(kernel, (1,1,3,3))
print(input.shape)
print(kernel.shape)

output = F.conv2d(input, kernel, stride=1)
print(output)

代码中原始input的形状为torch.Size([5, 5]),但是从官方文档里可以看到还要有batch批次、channel通道的维度,所以还要把input的形状改一下,用到了reshape。F是卷积函数。

padding表示填充 ,默认为0即不填充,padding=1时,表示在输入图像周围添加一圈,默认值都为0,再根据步幅去做计算。

代码实现:

import torch
import torch.nn.functional as F

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]])

print(input.shape)
print(kernel.shape)
input = torch.reshape(input, (1,1,5,5))
kernel = torch.reshape(kernel, (1,1,3,3))
print(input.shape)
print(kernel.shape)

output3 = F.conv2d(input, kernel, stride=1, padding=1)  # 周围只填充一层
print(output3)

步幅、填充原理

① 步幅:卷积核经过输入特征图的采样间隔。设置步幅的目的:希望减小输入参数的数目,减少计算量。

② 填充:在输入特征图的每一边添加一定数目的行列。设置填充的目的:希望每个输入方块都能作为卷积窗口的中心,或使得输出的特征图的长、宽 = 输入的特征图的长、宽。

③ 一个尺寸 a * a 的特征图,经过 b * b 的卷积层,步幅(stride)= c,填充(padding)= d,若d等于0,也就是不填充,输出的特征图的尺寸 =(a-b)/ c+1;若d不等于0,也就是填充,输出的特征图的尺寸 =(a+2d-b)/ c+1。

例子1:一个特征图尺寸为4 * 4的输入,使用3 * 3的卷积核,步幅=1,填充=0,输出的尺寸=(4 - 3)/1 + 1 = 2。

例子2: 一个特征图尺寸为5 * 5的输入,使用3 * 3的卷积核,步幅=1,填充=1,输出的尺寸=(5 + 2 * 1 - 3)/1 + 1 = 5。

例子3:一个特征图尺寸为5 * 5的输入, 使用3 * 3的卷积核,步幅=2,填充=0,输出的尺寸=(5-3)/2 + 1 = 2。

例子4:一个特征图尺寸为6 * 6的输入, 使用3 * 3的卷积核,步幅=2,填充=1,输出的尺寸=(6 + 2 * 1 - 3)/2 + 1 = 2.5 + 1 = 3.5 向下取整=3(降采样:边长减少1/2)。

P18 神经网络——卷积层 

Conv2d — PyTorch 2.4 documentation

 Conv1d代表一维卷积,Conv2d代表二维卷积,Conv3d代表三维卷积。

torch.nn.Conv2d(in_channelsout_channelskernel_sizestride=1padding=0dilation=1groups=1bias=Truepadding_mode='zeros'device=Nonedtype=None)

  • in_channels输入的通道数
  • out_channels输出的通道数,out_channels=1时,卷积层生成一个卷积核做计算,out_channels=2时,卷积层生成一个卷积核做计算,这两个卷积核不一定完全一样,叠加起来的两个输出即为2个通道

  • kernel_size卷积核大小,定义为3就是3*3大小,也可以定义一个不规则的元组。卷积核的内部参数不用设置,训练过程中会自动调整
  • stride卷积过程中步幅大小
  • padding卷积过程中填充的选项
  • dilation卷积核当中的卷积核的对应位,即距离。也叫空洞卷积,没有空洞时默认为1
  • groups一般为1,修改的话代表分组卷积
  • bias一般为True,是否对卷积后结果加上偏置
  • padding_mode如果加上padding要按什么方式及填充
  • dtype数据类型,int,float...

常用前五个参数,需要设置,之后可以根据实际计算出输出,N是batch_size:

搭建卷积层

import torch
from torch import nn
import torchvision
from torch.nn import Conv2d
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64)
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.conv1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0) # 彩色图像输入为3层,我们想让它的输出为6层,选3 * 3 的卷积                
    
    def forward(self,x):
        x = self.conv1(x)
        return x
    
tudui = Tudui()
print(tudui)

卷积层处理图片

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64)
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.conv1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0) # 彩色图像输入为3层,我们想让它的输出为6层,选3 * 3 的卷积                
    
    def forward(self,x):
        x = self.conv1(x)
        return x
    
tudui = Tudui()
for data in dataloader:
    imgs, targets = data
    output = tudui(imgs)
    print(imgs.shape)   # 输入为3通道32×32的64张图片
    print(output.shape) # 输出为6通道30×30的64张图片

Tensorboard显示

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64)
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.conv1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0) # 彩色图像输入为3层,我们想让它的输出为6层,选3 * 3 的卷积                
    
    def forward(self,x):
        x = self.conv1(x)
        return x
    
tudui = Tudui()
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
    imgs, targets = data
    output = tudui(imgs)
    print(imgs.shape)   
    print(output.shape)    
    writer.add_images("input", imgs, step)
    output = torch.reshape(output,(-1,3,30,30)) # 把原来6个通道拉为3个通道,为了保证所有维度总数不变,其余的分量分到第一个维度中
    writer.add_images("output", output, step)
    step = step + 1
这里有一个这样的变换:output = torch.reshape(output,(-1,3,30,30)),因为6个通道不知道怎么显示,只能显示3个通道,所以在这里换成3通道输出显示的,-1代表如果你不知道怎么写这个维度,就设置成-1,它自己会计算。如果这样设置的话,原本我们应该看到一个批次的64张图片呈8*8的形式排列,现在看到的是64*2张图片呈8*16的形式排列,因为通道里的改变作用到了批次上。

P19最大池化层

MaxPool2d — PyTorch 2.4 documentation

torch.nn.MaxPool2d(kernel_sizestride=Nonepadding=0dilation=1return_indices=Falseceil_mode=False)

kernel_size池化核大小

stride步幅默认为跟池化核大小一样,比如池化核3*3,那么stride=3

dilation空洞卷积

ceil_model当池化核对输入不能完全覆盖时(3*3的核在移动过程中只覆盖了输入图像的6个数字),是否还要保留当前步计算的结果。

另外提一下ceiling与floor的区别是遇到小数如2.31,向上取整还是向下取整。

最大池化层的计算规则是每次计算时,只留下最大的那个数。比如3*3的池化核,依次跟输入图像的九个数字相乘,最终只留下1个最大值,这也对应了MaxPool2d。

池化层处理数据

import torch
from torch import nn 
from torch.nn import MaxPool2d

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]], dtype = torch.float32)
input = torch.reshape(input,(-1,1,5,5)) 
print(input.shape)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.maxpool = MaxPool2d(kernel_size=3, ceil_mode=True)
        
    def forward(self, input):
        output = self.maxpool(input)
        return output
        
tudui = Tudui()
output = tudui(input)
print(output)


#结果

#torch.Size([1, 1, 5, 5])
#tensor([[[[2., 3.],
#          [5., 1.]]]])

池化层处理图片

import torch
import torchvision
from torch import nn 
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.maxpool = MaxPool2d(kernel_size=3, ceil_mode=True)
        
    def forward(self, input):
        output = self.maxpool(input)
        return output

tudui = Tudui()
writer = SummaryWriter("logs")
step = 0

for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, step)
    output = tudui(imgs)
    writer.add_images("output", output, step)
    step = step + 1

池化使得数据由5 * 5 变为3 * 3,甚至1 * 1的,这样导致计算的参数会大大减小。例如1080P的电影经过池化的转为720P的电影、或360P的电影后,同样的网速下,视频更为不卡。

P20 神经网络——非线性激活

ReLU — PyTorch 2.4 documentation

ReLU进行截断,input大于0时还是取input,input小于0时截断为0,输入有batch_size维度。这里的input如果是图片的话,就是针对每个像素值来计算的了。

inplace为原地替换,若为True,则变量的值被替换。若为False,则会创建一个新变量,将函数处理后的值赋值给新变量,原始变量的值没有修改。  右边这个的input保留了原始值,没被替换。


import torch
from torch import nn
from torch.nn import ReLU

input = torch.tensor([[1,-0.5],
                      [-1,3]])
input = torch.reshape(input,(-1,1,2,2))
print(input.shape)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.relu1 = ReLU()
        
    def forward(self, input):
        output = self.relu1(input)
        return output
    
tudui = Tudui()
output = tudui(input)
print(output)


#输出

#torch.Size([1, 1, 2, 2])
#tensor([[[[1., 0.],
#         [0., 3.]]]])

Tensorboard显示图片

import torch
import torchvision
from torch import nn 
from torch.nn import ReLU
from torch.nn import Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.relu1 = ReLU()
        self.sigmoid1 = Sigmoid()
        
    def forward(self, input):
        output = self.sigmoid1(input)
        return output

tudui = Tudui()
writer = SummaryWriter("logs")
step = 0

for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, step)
    output = tudui(imgs)
    writer.add_images("output", output, step)
    step = step + 1

非线性变换的主要目的是给网络中引入一些非线性特征,非线性越多的话才能训练出符合各种曲线或特征的模型。

P21 神经网络——线性层及其他层

Normalization Layers正则化层,对输入采用正则化,可以加快神经网络的训练速度

Dropout Layers训练过程中随机把一些输入的tensor数据类型的元素变为0,变为0的概率为p

Linear Layers线性层

 神经网络训练的就是函数系数Kk与d。对这两个系数不断调优,k指weight,d指bias。

torch.nn.Linear(in_featuresout_featuresbias=Truedevice=Nonedtype=None)把特定的输入转成想要的输出,比如4096个feature,经过线性层变成1000个feature。

线性拉平

import torch
import torchvision
from torch import nn 
from torch.nn import ReLU
from torch.nn import Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64)

for data in dataloader:
    imgs, targets = data
    print(imgs.shape)
    #图片尺寸变换torch.Size([64, 3, 32, 32])--》torch.Size([1, 1, 1, 196608])
    output = torch.reshape(imgs,(1,1,1,-1))
    #图片尺寸变换torch.Size([64, 3, 32, 32])--》torch.Size([196608])
    #output = torch.flatten(imgs)  # 方法二:拉平。展开为一维
    print(output.shape)

P22 搭建小实战和顺序使用

① 把网络结构放在Sequential里面,好处就是代码写起来比较简介、易懂。

② 可以根据神经网络每层的尺寸,根据下图的公式计算出神经网络中的参数。

搭建神经网络

import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.conv1 = Conv2d(3,32,5,padding=2)
        self.maxpool1 = MaxPool2d(2)
        self.cov2 = Conv2d(32,32,5,padding=2)
        self.maxpool2 = MaxPool2d(2)
        self.conv3 = Conv2d(32,64,5,padding=2)
        self.maxpool3 = MaxPool2d(2)
        self.flatten = Flatten()
        self.linear1 = Linear(1024,64)
        self.Linear2 = Linear(64,10)
        
    def forward(self, x):
        x = self.conv1(x)
        x = self.maxpool1(x)
        x = self.conv2(x)
        x = self.maxpool2(x)
        x = self.conv3(x)
        x = self.maxpool3(x)
        x = self.flatten(x)
        x = self.linear1(x)
        x = self.Linear2(x)
        return x

tudui = Tudui()
print(tudui)#查看网络结构

Sequential神经网络

import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024,64),
            Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x

tudui = Tudui()
input = torch.ones((64,3,32,32))
output = tudui(input)
print(output.shape)

代码变得更加简洁了!

 Tensorboard显示网络(强推!)

可以看到网络结构和每一步送到网络里的数据尺寸大小。

import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64,drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024,64),
            Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x

tudui = Tudui()
writer = SummaryWriter("logs")

tudui = Tudui()
input = torch.ones((64,3,32,32))
output = tudui(input)
print(output.shape)

writer.add_graph(tudui, input)
writer.close()

P23 损失函数与逆向传播

torch.nn — PyTorch 2.4 documentation

① Loss损失函数一方面计算实际输出和目标之间的差距。

② Loss损失函数另一方面为我们更新输出提供一定的依据(反向传播)。每一个卷积核的参数设置了一个梯度grad,采用反向传播的时候,每一个要更新的参数都会求出来一个对应的梯度,在优化过程中可以根据梯度对当中的参数进行优化,最终达到loss降低的目的。

反向传播可以计算出每个节点的梯度,可以选择合适的优化器对参数优化,以降低loss。

nn.L1Loss        网络输出与目标,依次做减法取绝对值相加,最后取平均

nn.CrossEntropyLoss      交叉熵损失

以交叉熵损失函数为例:

import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=1,drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024,64),
            Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x
    
tudui = Tudui()
for data in dataloader:
    imgs, targets = data
    outputs = tudui(imgs)
    print(outputs)
    print(targets)

结果给出了每张图片属于各个类别的概率,一般会取最大值作为模型的预测类别与实际标签对比。

损失函数反向传播

① 反向传播通过梯度来更新参数,使得loss损失最小,如下图所示。

import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64,drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024,64),
            Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x
    
loss = nn.CrossEntropyLoss() # 交叉熵    
tudui = Tudui()
for data in dataloader:
    imgs, targets = data
    outputs = tudui(imgs)
    result_loss = loss(outputs, targets) # 计算实际输出与目标输出的差距
    result_loss.backward()  # 计算出来的 loss 值有 backward 方法属性,反向传播来计算每个节点的更新的参数。这里查看网络的属性 grad 梯度属性刚开始没有,反向传播计算出来后才有,后面优化器会利用梯度优化网络参数。      
    print("ok")

 P24 优化器

torch.optim — PyTorch 2.4 documentation

① 损失函数调用backward方法,就可以调用损失函数的反向传播方法,就可以求出我们需要调节的梯度,我们就可以利用我们的优化器就可以根据梯度对参数进行调整,达到整体误差降低的目的。

② 梯度要清零,如果梯度不清零会导致梯度累加。

for input, target in dataset:
    optimizer.zero_grad()           #梯度清零,避免上一步的梯度影响,不可省
    output = model(input)           #获得输出
    loss = loss_fn(output, target)  #计算损失
    loss.backward()                 #反向传播,得到参数梯度
    optimizer.step()                #根据梯度更新参数

 不同的优化器有不同的算法,不过应用的时候主要修改params参数和lr学习率

SGD随机梯度下降

torch.optim.SGD(paramslr=0.001momentum=0dampening=0weight_decay=0nesterov=False*maximize=Falseforeach=Nonedifferentiable=Falsefused=None)

大致思路:定义优化器;梯度清零;调用损失函数;反向传播;模型参数调优

神经网络优化一轮

import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64,drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024,64),
            Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x
    
loss = nn.CrossEntropyLoss() # 交叉熵    
tudui = Tudui()
optim = torch.optim.SGD(tudui.parameters(),lr=0.01)   # 随机梯度下降优化器
for data in dataloader:
    imgs, targets = data
    outputs = tudui(imgs)
    result_loss = loss(outputs, targets) # 计算实际输出与目标输出的差距
    optim.zero_grad()  # 梯度清零
    result_loss.backward() # 反向传播,计算损失函数的梯度
    optim.step()   # 根据梯度,对网络的参数进行调优
    print(result_loss) # 对数据只看了一遍,只看了一轮,所以loss下降不大

 神经网络优化多轮

import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64,drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024,64),
            Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x
    
loss = nn.CrossEntropyLoss() # 交叉熵    
tudui = Tudui()
optim = torch.optim.SGD(tudui.parameters(),lr=0.01)   # 随机梯度下降优化器
for epoch in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs, targets = data
        outputs = tudui(imgs)
        result_loss = loss(outputs, targets) # 计算实际输出与目标输出的差距
        optim.zero_grad()  # 梯度清零
        result_loss.backward() # 反向传播,计算损失函数的梯度
        optim.step()   # 根据梯度,对网络的参数进行调优
        running_loss = running_loss + result_loss
    print(running_loss) # 对这一轮所有误差的总和

 神经网络学习率优化

import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       
dataloader = DataLoader(dataset, batch_size=64,drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024,64),
            Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x
    
loss = nn.CrossEntropyLoss() # 交叉熵    
tudui = Tudui()
optim = torch.optim.SGD(tudui.parameters(),lr=0.01)   # 随机梯度下降优化器
scheduler = torch.optim.lr_scheduler.StepLR(optim, step_size=5, gamma=0.1) # 每过 step_size 更新一次优化器,更新是学习率为原来的学习率的的 0.1 倍    
for epoch in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs, targets = data
        outputs = tudui(imgs)
        result_loss = loss(outputs, targets) # 计算实际输出与目标输出的差距
        optim.zero_grad()  # 梯度清零
        result_loss.backward() # 反向传播,计算损失函数的梯度
        optim.step()   # 根据梯度,对网络的参数进行调优
        scheduler.step() # 学习率太小了,所以20个轮次后,相当于没走多少
        running_loss = running_loss + result_loss
    print(running_loss) # 对这一轮所有误差的总和

P25 网络模型使用及修改

Models and pre-trained weights — Torchvision 0.19 documentation

VGG模型中常用的是VGG16和VGG19

pretrained如果为True相当于下载到的网络模型其中的一些参数已经在mo'x数据集中训练好了,在数据集中能取得不错的效果。为False的话说明参数都是初始化的参数,没有在任何数据集上训练。

下载网络模型


import torchvision

#trauin_data = torchvision.datasets.ImageNet("./dataset",split="train",download=True,transform=torchvision.transforms.ToTensor())   # 这个数据集没有办法再公开的访问了    
vgg16_true = torchvision.models.vgg16(pretrained=True) # 下载卷积层对应的参数是多少、池化层对应的参数时多少,这些参数时ImageNet训练好了的
vgg16_false = torchvision.models.vgg16(pretrained=False) # 没有预训练的参数
print("ok")
print(vgg16_true)

查看函数用法

import torchvision
help(torchvision.models.vgg16)

网络模型添加

import torchvision
from torch import nn

dataset = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)       
vgg16_true = torchvision.models.vgg16(pretrained=True) # 下载卷积层对应的参数是多少、池化层对应的参数时多少,这些参数时ImageNet训练好了的
vgg16_true.add_module('add_linear',nn.Linear(1000,10)) # 在VGG16后面添加一个线性层,使得输出为适应CIFAR10的输出,CIFAR10需要输出10个种类
#vgg16_true.classifier.add_module('add_linear',nn.Linear(1000,10))#如果想加在VGG里的特定部分,如classifier中,就多加一层
print(vgg16_true)

网络模型修改

import torchvision
from torch import nn

vgg16_false = torchvision.models.vgg16(pretrained=False) # 没有预训练的参数     
print(vgg16_false)
vgg16_false.classifier[6] = nn.Linear(4096,10)
print(vgg16_false)

P26 网络模型保存与读取

网络模型保存(方式一)

保存网络模型的结构和模型参数,参数为模型和保存地址

import torchvision
import torch
vgg16 = torchvision.models.vgg16(pretrained=False)
torch.save(vgg16,"./model/vgg16_method1.pth") # 保存方式一:模型结构 + 模型参数      
print(vgg16)

网络模型导入(方式一)

对应于模型保存方式一,只要有模型的地址就可以

import torch
model = torch.load("./model/vgg16_method1.pth") # 保存方式一对应的加载模型    
print(model)

网络模型保存(方式二)

把VGG16中的参数保存成python中的一个字典,并指定路径。只保存了模型参数,官方推荐,因为保存下来的更小。

import torchvision
import torch
vgg16 = torchvision.models.vgg16(pretrained=False)
torch.save(vgg16.state_dict(),"./model/vgg16_method2.pth") # 保存方式二:模型参数(官方推荐),不再保存网络模型结构  
print(vgg16)

网络模型导入(方式二)

对应于模型保存方式二,注意加载方式的变化,如果还用上面的加载方式只会加载出保存的字典,不会加载出网络模型。

import torch
import torchvision
vgg16 = torchvision.models.vgg16(pretrained=False)
print(vgg16)
vgg16.load_state_dict(torch.load("./model/vgg16_method2.pth")) # 将模型参数导入到模型结构中   
print(vgg16)

网络陷阱

创建一个自定义的网络结构,实例化一个模型。

import torch
from torch import nn

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui,self).__init__()
        self.conv1 = nn.Conv2d(3,64,kernel_size=3)
        
    def forward(self,x):
        x = self.conv1(x)
        return x

tudui = Tudui()
torch.save(tudui, "./model/tudui_method1.pth")

用保存方式一保存下来模型,并用对应的加载方式来导入模型,发现报错说:

用方式一的话要让加载程序能够访问到模型定义的方式

import torch
from torch import nn

# 确保网络模型是我们想要的网络模型,要在加载前还写明网络模型
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui,self).__init__()
        self.conv1 = nn.Conv2d(3,64,kernel_size=3)
        
    def forward(self,x):
        x = self.conv1(x)
        return x
    
#tudui = Tudui # 不需要写这一步,不需要创建网络模型    
model = torch.load("./model/tudui_method1.pth")  # 无法直接加载方式一保存的网络结构    
print(model)

上面这个是把网络结构直接复制过来了,但我们实际应用的时候会把网络结构单独放在一个文件里,直接import就行

import torch
import model_save import * # 它就相当于把 model_save.py 里的网络模型定义写到这里了
    
#tudui = Tudui # 不需要写这一步,不需要创建网络模型    

model = torch.load("tudui_method1.pth")
print(model)

P27 完整模型训练套路

CIFAR 10 model 网络模型

① 下面用 CIFAR 10 model网络来完成分类问题,网络模型如下图所示。

 DataLoader加载数据集

import torchvision
from torch import nn
from torch.utils.data import DataLoader

# 准备数据集
train_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)       
test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       

# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 如果train_data_size=10,则打印:训练数据集的长度为:10
print("训练数据集的长度:{}".format(train_data_size))
print("测试数据集的长度:{}".format(test_data_size))

# 利用 Dataloader 来加载数据集
train_dataloader = DataLoader(train_data_size, batch_size=64)        
test_dataloader = DataLoader(test_data_size, batch_size=64)


#输出
 # 训练数据集的长度:50000
 # 测试数据集的长度:10000

测试网络正确

import torch
from torch import nn

# 搭建神经网络
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = nn.Sequential(
            nn.Conv2d(3,32,5,1,2),  # 输入通道3,输出通道32,卷积核尺寸5×5,步长1,填充2    
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),  # 展平后变成 64*4*4 了
            nn.Linear(64*4*4,64),
            nn.Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x
    
if __name__ == '__main__':
    tudui = Tudui()
    input = torch.ones((64,3,32,32))
    output = tudui(input)
    print(output.shape)  # 测试输出的尺寸是不是我们想要的



#输出
#torch.Size([64, 10])

train文件和moudle文件要在一个目录底下

网络训练数据

import torchvision
from torch import nn
from torch.utils.data import DataLoader

# from model import * 相当于把 model中的所有内容写到这里,这里直接把 model 写在这里
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = nn.Sequential(
            nn.Conv2d(3,32,5,1,2),  # 输入通道3,输出通道32,卷积核尺寸5×5,步长1,填充2    
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),  # 展平后变成 64*4*4 了
            nn.Linear(64*4*4,64),
            nn.Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x

# 准备数据集
train_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)       
test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       

# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 如果train_data_size=10,则打印:训练数据集的长度为:10
print("训练数据集的长度:{}".format(train_data_size))
print("测试数据集的长度:{}".format(test_data_size))

# 利用 Dataloader 来加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)        
test_dataloader = DataLoader(test_data, batch_size=64)

# 创建网络模型
tudui = Tudui() 

# 损失函数
loss_fn = nn.CrossEntropyLoss() # 交叉熵,fn 是 fuction 的缩写

# 优化器
learning = 0.01  # 1e-2 就是 0.01 的意思
optimizer = torch.optim.SGD(tudui.parameters(),learning)   # 随机梯度下降优化器  

# 设置网络的一些参数
# 记录训练的次数
total_train_step = 0

# 训练的轮次
epoch = 10

for i in range(epoch):
    print("-----第 {} 轮训练开始-----".format(i+1))
    
    # 训练步骤开始
    for data in train_dataloader:
        imgs, targets = data
        outputs = tudui(imgs)
        loss = loss_fn(outputs, targets) # 计算实际输出与目标输出的差距
        
        # 优化器对模型调优
        optimizer.zero_grad()  # 梯度清零
        loss.backward() # 反向传播,计算损失函数的梯度
        optimizer.step()   # 根据梯度,对网络的参数进行调优
        
        total_train_step = total_train_step + 1
        #print("训练次数:{},Loss:{}".format(total_train_step,loss))  # 方式一:获得loss值
        print("训练次数:{},Loss:{}".format(total_train_step,loss.item()))  # 方式二:获得loss值

补充:xx.item()将原本的tensor类型转换成一个数字。

查看训练损失

① 在pytorch中,tensor有一个requires_grad参数,如果设置为True,则反向传播时,该tensor就会自动求导。

② tensor的requires_grad的属性默认为False,若一个节点(叶子变量:自己创建的tensor)requires_grad被设置为True,那么所有依赖它的节点requires_grad都为True(即使其他相依赖的tensor的requires_grad = False)

③ 当requires_grad设置为False时,反向传播时就不会自动求导了,因此大大节约了显存或者说内存。

④ with torch.no_grad的作用在该模块下,所有计算得出的tensor的requires_grad都自动设置为False。

⑤ 即使一个tensor(命名为x)的requires_grad = True,在with torch.no_grad计算,由x得到的新tensor(命名为w-标量)requires_grad也为False,且grad_fn也为None,即不会对w求导。

⑥ torch.no_grad():停止计算梯度,不能进行反向传播。

# 添加 tensorboard
writer = SummaryWriter("logs")

for i in range(epoch):
    print("-----第 {} 轮训练开始-----".format(i+1))
    
    # 训练步骤开始
    for data in train_dataloader:
        imgs, targets = data
        outputs = tudui(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.item()))  # 方式二:获得loss值
            writer.add_scalar("train_loss",loss.item(),total_train_step)
    
    # 测试步骤开始(每一轮训练后都查看在测试数据集上的loss情况)
    total_test_loss = 0
    with torch.no_grad():  # 没有梯度计算,节约内存
        for data in test_dataloader: # 测试数据集提取数据
            imgs, targets = data
            outputs = tudui(imgs)
            loss = loss_fn(outputs, targets) # 仅data数据在网络模型上的损失
            total_test_loss = total_test_loss + loss.item() # 所有loss
    print("整体测试集上的Loss:{}".format(total_test_loss))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    total_test_step = total_test_step + 1
        
writer.close()

输入 tensorboard --logdir=logs 命令,将网址赋值浏览器的网址栏,回车,即可查看tensorboard显示日志情况。

保存每一轮后参数


for i in range(epoch):
    print("-----第 {} 轮训练开始-----".format(i+1))
    
    # 训练步骤开始
    for data in train_dataloader:
        imgs, targets = data
        outputs = tudui(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.item()))  # 方式二:获得loss值
            writer.add_scalar("train_loss",loss.item(),total_train_step)
    
    # 测试步骤开始(每一轮训练后都查看在测试数据集上的loss情况)
    total_test_loss = 0
    with torch.no_grad():  # 没有梯度了
        for data in test_dataloader: # 测试数据集提取数据
            imgs, targets = data
            outputs = tudui(imgs)
            loss = loss_fn(outputs, targets) # 仅data数据在网络模型上的损失
            total_test_loss = total_test_loss + loss.item() # 所有loss
    print("整体测试集上的Loss:{}".format(total_test_loss))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    total_test_step = total_test_step + 1
    
    torch.save(tudui, "./model/tudui_{}.pth".format(i)) # 保存每一轮训练后的结果
    print("模型已保存")
    
writer.close()

argmax作用

按照行或列取出概率值最大的索引,作为预测标签

import torch
outputs = torch.tensor([[0.1,0.2],
                        [0.05,0.4]])
print(outputs.argmax(0))  # 竖着看,最大值的索引
print(outputs.argmax(1))  # 横着看,最大值的索引
preds = outputs.argmax(0)
targets = torch.tensor([0,1])
print((preds == targets).sum()) # 对应位置相等的个数



#输出

#tensor([0, 1])
#tensor([1, 1])
#tensor(2)

打印正确率(最终版代码)

import torchvision
import torch
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# from model import * 相当于把 model中的所有内容写到这里,这里直接把 model 写在这里
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = nn.Sequential(
            nn.Conv2d(3,32,5,1,2),  # 输入通道3,输出通道32,卷积核尺寸5×5,步长1,填充2    
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),  # 展平后变成 64*4*4 了
            nn.Linear(64*4*4,64),
            nn.Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x

# 准备数据集
train_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)       
test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       

# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 如果train_data_size=10,则打印:训练数据集的长度为:10
print("训练数据集的长度:{}".format(train_data_size))
print("测试数据集的长度:{}".format(test_data_size))

# 利用 Dataloader 来加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)        
test_dataloader = DataLoader(test_data, batch_size=64)

# 创建网络模型
tudui = Tudui() 

# 损失函数
loss_fn = nn.CrossEntropyLoss() # 交叉熵,fn 是 fuction 的缩写

# 优化器
learning = 0.01  # 1e-2 就是 0.01 的意思
optimizer = torch.optim.SGD(tudui.parameters(),learning)   # 随机梯度下降优化器  

# 设置网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0

# 训练的轮次
epoch = 10

# 添加 tensorboard
writer = SummaryWriter("logs")

for i in range(epoch):
    print("-----第 {} 轮训练开始-----".format(i+1))
    
    # 训练步骤开始
    tudui.train() # 当网络中有dropout层、batchnorm层时,这些层能起作用
    for data in train_dataloader:
        imgs, targets = data
        outputs = tudui(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.item()))  # 方式二:获得loss值
            writer.add_scalar("train_loss",loss.item(),total_train_step)
    
    # 测试步骤开始(每一轮训练后都查看在测试数据集上的loss情况)
    tudui.eval()  # 当网络中有dropout层、batchnorm层时,这些层不能起作用
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():  # 没有梯度了
        for data in test_dataloader: # 测试数据集提取数据
            imgs, targets = data
            outputs = tudui(imgs)
            loss = loss_fn(outputs, targets) # 仅data数据在网络模型上的损失
            total_test_loss = total_test_loss + loss.item() # 所有loss
            accuracy = (outputs.argmax(1) == targets).sum()
            total_accuracy = total_accuracy + accuracy
            
    print("整体测试集上的Loss:{}".format(total_test_loss))
    print("整体测试集上的正确率:{}".format(total_accuracy/test_data_size))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)  
    total_test_step = total_test_step + 1
    
    torch.save(tudui, "./model/tudui_{}.pth".format(i)) # 保存每一轮训练后的结果
    #torch.save(tudui.state_dict(),"tudui_{}.path".format(i)) # 保存方式二         
    print("模型已保存")
    
writer.close()

特殊层作用

① model.train()和model.eval()的区别主要在于Batch Normalization和Dropout两层。

② 如果模型中有BN层(Batch Normalization)和 Dropout,需要在训练时添加model.train()。model.train()是保证BN层能够用到每一批数据的均值和方差。对于Dropout,model.train()是随机取一部分网络连接来训练更新参数。

③ 不启用 Batch Normalization 和 Dropout。 如果模型中有BN层(Batch Normalization)和Dropout,在测试时添加model.eval()。model.eval()是保证BN层能够用全部训练数据的均值和方差,即测试过程中要保证BN层的均值和方差不变。对于Dropout,model.eval()是利用到了所有网络连接,即不进行随机舍弃神经元。

④ 训练完train样本后,生成的模型model要用来测试样本。在model(test)之前,需要加上model.eval(),否则的话,有输入数据,即使不训练,它也会改变权值。这是model中含有BN层和Dropout所带来的的性质。

⑤ 在做one classification的时候,训练集和测试集的样本分布是不一样的,尤其需要注意这一点。

P30 利用GPU训练

方式一

 GPU训练主要有三部分,网络模型、数据(输入、标注)、损失函数,这三部分放到GPU上。xx.cuda(),数据在训练和测试时,每批次放入gpu上。

import torchvision
import torch
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# from model import * 相当于把 model中的所有内容写到这里,这里直接把 model 写在这里
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = nn.Sequential(
            nn.Conv2d(3,32,5,1,2),  # 输入通道3,输出通道32,卷积核尺寸5×5,步长1,填充2    
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),  # 展平后变成 64*4*4 了
            nn.Linear(64*4*4,64),
            nn.Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x

# 准备数据集
train_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)       
test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)       

# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 如果train_data_size=10,则打印:训练数据集的长度为:10
print("训练数据集的长度:{}".format(train_data_size))
print("测试数据集的长度:{}".format(test_data_size))

# 利用 Dataloader 来加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)        
test_dataloader = DataLoader(test_data, batch_size=64)

# 创建网络模型
tudui = Tudui() 
if torch.cuda.is_available():
    tudui = tudui.cuda() # 网络模型转移到cuda上

# 损失函数
loss_fn = nn.CrossEntropyLoss() # 交叉熵,fn 是 fuction 的缩写
if torch.cuda.is_available():
    loss_fn = loss_fn.cuda()        # 损失函数转移到cuda上

# 优化器
learning = 0.01  # 1e-2 就是 0.01 的意思
optimizer = torch.optim.SGD(tudui.parameters(),learning)   # 随机梯度下降优化器  

# 设置网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0

# 训练的轮次
epoch = 10

# 添加 tensorboard
writer = SummaryWriter("logs")

for i in range(epoch):
    print("-----第 {} 轮训练开始-----".format(i+1))
    
    # 训练步骤开始
    tudui.train() # 当网络中有dropout层、batchnorm层时,这些层能起作用
    for data in train_dataloader:
        imgs, targets = data
        if torch.cuda.is_available():
            imgs = imgs.cuda()  # 数据放到cuda上
            targets = targets.cuda() # 数据放到cuda上
        outputs = tudui(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.item()))  # 方式二:获得loss值
            writer.add_scalar("train_loss",loss.item(),total_train_step)
    
    # 测试步骤开始(每一轮训练后都查看在测试数据集上的loss情况)
    tudui.eval()  # 当网络中有dropout层、batchnorm层时,这些层不能起作用
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():  # 没有梯度了
        for data in test_dataloader: # 测试数据集提取数据
            imgs, targets = data # 数据放到cuda上
            if torch.cuda.is_available():
                imgs = imgs.cuda() # 数据放到cuda上
                targets = targets.cuda()
            outputs = tudui(imgs)
            loss = loss_fn(outputs, targets) # 仅data数据在网络模型上的损失
            total_test_loss = total_test_loss + loss.item() # 所有loss
            accuracy = (outputs.argmax(1) == targets).sum()
            total_accuracy = total_accuracy + accuracy
            
    print("整体测试集上的Loss:{}".format(total_test_loss))
    print("整体测试集上的正确率:{}".format(total_accuracy/test_data_size))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)  
    total_test_step = total_test_step + 1
    
    torch.save(tudui, "./model/tudui_{}.pth".format(i)) # 保存每一轮训练后的结果
    #torch.save(tudui.state_dict(),"tudui_{}.path".format(i)) # 保存方式二         
    print("模型已保存")
    
writer.close()

有谷歌账号的话,谷歌的colab可以免费使用GPU,具体使用方法自寻。

方式二

电脑上有两个显卡时,可以用指定cuda:0、cuda:1。把原来的.cuda()换成.to(device)

import torchvision
import torch
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time

# 定义训练的设备
#device = torch.device("cpu")
#device = torch.device("cuda")   # 使用 GPU 方式一 
#device = torch.device("cuda:0") # 使用 GPU 方式二
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

....

# 创建网络模型
tudui = Tudui() 
tudui = tudui.to(device) # 也可以不赋值,直接 tudui.to(device) 


# 损失函数
loss_fn = nn.CrossEntropyLoss() # 交叉熵,fn 是 fuction 的缩写
loss_fn = loss_fn.to(device) # 也可以不赋值,直接loss_fn.to(device)

# 优化器
learning = 0.01  # 1e-2 就是 0.01 的意思
optimizer = torch.optim.SGD(tudui.parameters(),learning)   # 随机梯度下降优化器  

# 设置网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0

# 训练的轮次
epoch = 10

# 添加 tensorboard
writer = SummaryWriter("logs")
start_time = time.time()

for i in range(epoch):
    print("-----第 {} 轮训练开始-----".format(i+1))
    
    # 训练步骤开始
    tudui.train() # 当网络中有dropout层、batchnorm层时,这些层能起作用
    for data in train_dataloader:
        imgs, targets = data            
        imgs = imgs.to(device) # 也可以不赋值,直接 imgs.to(device)
        targets = targets.to(device) # 也可以不赋值,直接 targets.to(device)
        outputs = tudui(imgs)
        ......
    
    # 测试步骤开始(每一轮训练后都查看在测试数据集上的loss情况)
    tudui.eval()  # 当网络中有dropout层、batchnorm层时,这些层不能起作用
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():  # 没有梯度了
        for data in test_dataloader: # 测试数据集提取数据
            imgs, targets = data # 数据放到cuda上
            imgs = imgs.to(device) # 也可以不赋值,直接 imgs.to(device)
            targets = targets.to(device) # 也可以不赋值,直接 targets.to(device)
            outputs = tudui(imgs)
            .....
            
    ......
    
writer.close()

运行Terminal语句

① 运行terminal上运行的命令,可以在代码块中输入语句,在语句前加一个感叹号。

② 输入 !nvidia-smi,可以查看显卡配置。

P32 完整模型验证套路

获取图片的的相对路径,返回到上一层级,再写实际路径

image_path = "../imgs/dog.png"

验证狗是否识别

① 完整的模型验证(测试,demo)套路,利用已经训练好的模型,然后给它提供输入。

import torchvision
from PIL import Image
from torch import nn
import torch

image_path = "imgs/dog.png"
image = Image.open(image_path) # PIL类型的Image
image = image.convert("RGB")  # 4通道的RGBA转为3通道的RGB图片
print(image)

transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),   
                                            torchvision.transforms.ToTensor()])

image = transform(image)
print(image.shape)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = nn.Sequential(
            nn.Conv2d(3,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(64*4*4,64),
            nn.Linear(64,10)
        )
        
    def forward(self, x):
        x = self.model1(x)
        return x

model = torch.load("model/tudui_29.pth",map_location=torch.device('cpu')) # GPU上训练的东西映射到CPU上    
print(model)
image = torch.reshape(image,(1,3,32,32)) # 转为四维,符合网络输入需求
model.eval()
with torch.no_grad():  # 不进行梯度计算,减少内存计算
    output = model(image)
output = model(image)
print(output)
print(output.argmax(1)) # 概率最大类别的输出

P33 查看开源项目

parser.add_argument

① 像运行Tensorboar一样,在Terminal终端,可以命令运行.py文件。

② 如下图所示,Terminal终端运行.py文件时,--变量 后面的值是给变量进行赋值,赋值后再在.py文件中运行。例如 ./datasets/maps 是给前面的dataroot赋值,maps_cyclegan是给前面的name赋值,cycle_gan是给前面的model赋值。

③ required表示必须需要指定参数,default表示有默认的参数了。Terminal终端命令语句,如果不对该默认变量新写入,直接调用默认的参数;如果对该默认变量新写入,则默认的参数被新写入的参数覆盖。

 课代表:https://github.com/AccumulateMore/CV100-122

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2050208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

政务大数据解决方案(三)

政务大数据解决方案致力于通过整合来自不同部门和领域的海量数据,采用先进的数据分析技术与人工智能算法,建立一个全面、高效的数据管理平台,从而优化政府决策过程,提高政策制定的科学性与精准性。该方案涵盖数据采集、存储、处理…

C语言学习笔记 Day14(文件管理)

Day14 内容梳理: 目录 Chapter 10 文件操作 10.0 概述 10.1 打开、关闭文件 (1)文件指针 (2)打开文件 fopen() (3)读取文件 fgetc() (4)写入文件 fputc() Ch…

Arthas:Java应用诊断利器

1、简介 Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时&#xff0c…

【大数据算法】一文掌握大数据算法之:概述、特点、类型及难点等,值得收藏。

大数据算法概述 1、引言2、大数据算法概述2.1 什么是大数据算法2.2 大数据算法特点2.3 大数据算法类型2.4 大数据算法难点 3、总结 1、引言 小屌丝:鱼哥,大数据开篇反馈不错哦。 小鱼:嗯,是的呢, 咱这个专栏&#xff…

Fly Catcher:通过监测恶意信号来检测飞机欺骗

Fly Catcher 的开发者 Angelina Tsuboi 是一名飞行员、网络安全研究员和发明家。 她决定着手一个将这三个不同兴趣结合起来的项目,以解决航空雷达系统的一个重大问题。 ADS-B 系统最初用于基本的飞机定位和跟踪,Tsuboi 对该系统的网络安全方面进行了深…

正点原子阿尔法ARM开发板-IMX6ULL(一)——环境安装和资源介绍

文章目录 一、环境安装二、主目录的资源介绍2.1 alpzha2.2 linux2.3 c_linux 三、QT四、交叉编译五、VScode六、其他6.1 SecureCRT6.2 Putty6.3 MobaXterm6.4 NFS6.5 TFTP 这几天实习的强度下来了,就来看看嵌入式方面的知识了 也是买了正点原子的阿尔法的嵌入式开发…

一次日志记录中使用fastjson涉及到ByteBuffer的教训

背景 目前本人在公司负责的模块中,有一个模块是负责数据同步的,主要是将我们数据产线使用的 AWS Dynamodb 同步的我们的测试QA 的环境的 MongoDB 的库中,去年开始也提供了使用 EMR 批量同步的功能,但是有时候业务也需要少量的数据…

Linux内核分析(RCU机制和内存优化)

文章目录 前言一、RCU概念RCU 的基本概念1. **如何工作**2. **RCU 的工作流程** RCU 的主要优势RCU 的使用场景RCU 的挑战和局限RCU 的实现总结 二、RCU对链表的访问三、Linux中的屏障主要类型应用场景实现作用用途 前言 一、RCU概念 RCU(Read-Copy-Update&#x…

【Web】巅峰极客2024 部分题解

目录 EncirclingGame GoldenHornKing php_online admin_Test EncirclingGame 玩赢游戏就行 GoldenHornKing 利用点在传入的app 可以打python内存马 /calc?calc_reqconfig.__init__.__globals__[__builtins__][exec](app.add_api_route("/flag",lambda:__i…

【数据结构】二叉树(三)精选Oj题

本篇已经是二叉树第三篇啦,下面讲解相关面试题,写作不易,求路过的朋友给个点赞与收藏呀~ 目录 1、相同的树 2、另一颗树的子树 3、翻转二叉树 4、对称二叉树 5、平衡二叉树 6、构建二叉树 7、二叉树的最近公共祖先 孩子双亲解法 二叉…

企业为何需要渗透测试

随着数字化时代的全面到来,互联网已成为企业运营不可或缺的一部分。然而,日益复杂的网络环境和不断演变的攻击手段,使得网络安全问题日益严峻。在这一背景下,渗透测试作为一种重要的安全评估手段,对于保障企业信息安全…

day24-测试之接口测试基础

目录 一、接口的定义 二、接口的优点 三、API接口 四、接口测试流程 五、网络基础概念 六、HTTP和RURL 七、get和post请求 八、数据格式 九、状态码 十、restful风格 十一、接口工具 一、接口的定义 程序之间协作所要遵循的一套规范、标准 二、接口的优点 2.1.责任…

探索和表征大型语言模型在嵌入式系统开发和调试中的应用

这篇论文的标题是《Exploring and Characterizing Large Language Models for Embedded System Development and Debugging》,作者是来自华盛顿大学的研究团队。论文主要探讨了大型语言模型(LLMs)在嵌入式系统开发和调试方面的应用潜力。以下…

前端技巧——复杂表格在html当中的实现

应用场景 有时候我们的表格比较复杂,表头可能到处割裂,我们还需要写代码去完成这个样式,所以学会在原生html处理复杂的表格还是比较重要的。 下面我们来看这一张图: 我们可以看到有些表头项的规格不太一样,有1*1 2*…

【深入理解SpringCloud微服务】Spring-Cloud-OpenFeign源码解析(下)——LoadBalancerFeignClient详解

【深入理解SpringCloud微服务】Spring-Cloud-OpenFeign源码解析(下)——LoadBalancerFeignClient详解 RxJava简单介绍RxJava示例Observable与Subscriber相关方法介绍Observable.create(OnSubscribe)Observable#just(T value)Observable#concatMap(Func1&…

实战OpenCV之图像显示

基础入门 OpenCV提供的功能非常多,图像显示是最基础也是最直观的一部分。它让我们能够直观地看到算法处理后的效果,对于调试和验证都至关重要。在OpenCV中,图像显示主要依赖于以下四个关键的数据结构和函数。 1、Mat类。这是OpenCV中最基本的…

文心快码(Baidu Comate)快速创建数据可视化图表

给你分享一个免费的编码助手——文心快码 Baidu Comate!百度文心大模型,46%采纳率,百度30%的代码都是它写的!AI这个大腿,你确定不抱一下?快来安装使用吧,送京东卡! https://dwz.cn/3…

高校疫情防控web系统pf

TOC springboot365高校疫情防控web系统pf 第1章 绪论 1.1 课题背景 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。所以各行…

平移矩阵、点绕轴的旋转矩阵、平面直角坐标系旋转矩阵、点绕向量旋转公式(罗德里格斯旋转公式)

平移矩阵 点绕轴的旋转矩阵 平面直角坐标系旋转矩阵 点绕向量旋转公式(罗德里格斯旋转公式) 代码 #include "myPoint.h" #include <cmath> myPoint::myPoint() {m_x m_y m_z 0; }myPoint::myPoint(double x, double y, double z):m_x(x),m_y(y),m_z(z) { }…

探索tailwindcss多主题切换

现在的多主题切换基本上都是用的 css 变量的形式, 而tailwindcss也支持 css 变量定义主题的方式 至于为什么用 tailwindcss变量, 还是因为 tailwind 写类名提示比较方便, 也不需要再在css或者style中去一个个var的形式去写变量了 这里我在assets/style/theme文件夹中创建了三个…