超细致小土堆的pytorch基础学习笔记

PyTorch

一、torch学习

1、两个工具

用于探索Python或pytorch中的工具包

  • dir():能让我们知道工具箱中有什么东西
  • help():告诉我们工具的使用方法

2、pycharm 和 jupyter的区别

在这里插入图片描述

3、pytorch加载数据

01.初认识
  • Dataset

    • 提供一种方式去获取数据及其label
      • 如何获取每一个数据及其label
      • 告诉我们总共有多少个数据
  • Dataloader

    • 为后面的网络提供不同的数据形式
  • 注:类中–init-- (self)初始化函数

#类首先要初始化,即根据这个类创建实例的时候自动调用的函数
#作用:为class提供全局变量,为后面的函数提供他们所需要的 量
import os.path
from  torch.utils.data import Dataset
from PIL import Image

class Mydata(Dataset):
    def __init__(self,root_dir,label_dir,):
        self.root_dir=root_dir
        self.label_dir=label_dir
        self.path=os.path.join(self.root_dir,self.label_dir)
        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 = "pytorch数据/hymenoptera_data/train"
ants_label_dir = "ants"
ants_dataset = Mydata(root_dir,ants_label_dir)
img,label = ants_dataset[0]
img.show()
  • output:

在这里插入图片描述

02.简单理解
1`.Dataloader()
  • 概念:是一个迭代器,方便我们去多线程地读取数据,并且可以实现batch以及shuffle的读取等

  • 用法:test_loader=DataLoader(dataset=test_data, batch_size=4, shuffle=True, num_workers=0, drop_last=True)

    • 如上,test_loader 就是我们生成的数据迭代器,
    • 即加载test_data数据集,每次打包四个数据,打包成imgs和targets,
    • shuffle表示每次迭代完之后,下次迭代是否打乱顺序
    • drop_list 表示是否删除非完整页的结尾数据
    • 完整参数参考官方文档。
  • 完整版:

    DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None,num_workers=0,collate_fn=None,pin_memory=False, drop_last=False, timeout=0,worker_init_fn=None,multiprocessing_context=None)
    
    • dataset:指定要加载数据的数据集,通常是一个Dataset对象。
    • batch_size:指定每个批次中样本的数量,默认为1。
    • shuffle:指定是否在每个epoch开始时对数据进行洗牌,默认为False。
    • sampler:可选参数,指定用于抽样数据的抽样器。
    • batch_sampler:可选参数,指定用于生成批次的批次抽样器。
    • num_workers:指定用于数据加载的子进程数,默认为0,表示所有的数据加载将在主进程中进行。
    • collate_fn:指定用于将样本列表组装成一个批次的函数,默认为None,表示使用默认的方式进行组装。
    • pin_memory:指定是否将加载的数据存储在CUDA固定内存中,默认为False。
    • drop_last:指定是否丢弃最后一个不完整的批次,默认为False。
    • timeout:指定数据加载器在等待数据时的超时时间,默认为0。
    • worker_init_fn:可选参数,指定每个工作进程在启动时要运行的初始化函数。
    • multiprocessing_context:指定在多进程加载数据时使用的上下文,默认为None
    import torchvision
    #准备的测试数据集
    from torch.utils.data import DataLoader
    from torch.utils.tensorboard import SummaryWriter
    
    test_data = torchvision.datasets.CIFAR10(root="./dataset",transform=torchvision.transforms.ToTensor(),train=False,download=True)
    
    test_loader  = DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0,drop_last=False)
    
    #测试数据集中第一张图片及target   target是标签
    img,target = test_data[0]
    print(img.shape)
    print(target)
    
    writer = SummaryWriter("dataloader")
    step = 0
    for data in test_loader:
        imgs,targets =data
        # print(imgs.shape)
        # print(targets)
        writer.add_images("test_data",imgs,step)
        step +=1
    
    writer.close()
    

    在这里插入图片描述

2`.小总结
  • Dataset是一个包装类,用来将数据包装为Dataset类,然后传入DataLoader中
  • 我们再使用DataLoader这个类来更加快捷的对数据进行操作
3`.数据组织形式
  • 如一个文件夹内存放多个同类的图片:文件夹的名称就是其label
  • 数据和label存放在不同的文件夹内

4.可视化工具Tensorboard—针对模型训练

  • 因为我们编写出来的TensorFlow或pytorch(1.0之后添加了这个模块)程序,建好一个神经网络,
  • 其实我们也不知道神经网络里头具体细节到底做了什么,要人工调试十分困难(就好比你无法想象出递归的所有步骤一样)。
  • 有了TensorBoard,可以将TensorFlow程序的执行步骤都显示出来,非常直观。并且,我们可以对训练的参数(比如loss值)进行统计,用图的方式来查看变化的趋势。
01.Summarywrite类:
  • SummaryWriter 类提供了一个高级API来创建一个事件文件,在给定的目录中添加摘要和事件。

    #先要添加一个头文件
    from torch.utils.tensorboard import SummaryWriter
    
    # 一般要先创建一个实例
    writer = SummaryWriter("logs")
    
    #一般运用的方法
    writer.add_image()
    writer.add_scalar()
    
    writer.close()#最后要关闭
    
    #命令行中显示事件文件的端口地址:tensorboard --logdir = 事件文件所在文件夹名
    #修改地址:tensorboard --logdir=logs --port=6007
    #某些情况可能会有发生事件冲突,造成图像混乱(如修改变量时没有修改标签):这时可以将事件删除重新运行
    
02.add_scalar()方法:
add_scalar(tag, scalar_value, global_step=None, walltime=None)

参数

  • tag (str):标量值的名称,用于在 TensorBoard 中标识该值的类型。
  • scalar_value (float):要记录的标量值。
  • global_step (int, optional):可选参数,用于指定记录标量值的全局步数,通常表示训练的迭代次数或批次数。如果不指定,则默认为当前时间戳。
  • walltime (float, optional):可选参数,用于指定记录标量值的时间戳。如果不指定,则默认为当前时间戳。

返回值

  • 无,直接将标量值添加到 SummaryWriter 对象的日志中。

说明

  • 此方法用于向 TensorBoard 日志中添加单个标量值,用于记录训练过程中的损失、准确率等指标。
  • tag 参数用于在 TensorBoard 中标识该值的类型,可以自定义标签名称。
  • global_step 参数通常用于指定记录标量值的全局步数,以便在 TensorBoard 中横轴上对标量值进行排序。
  • walltime 参数用于指定记录标量值的时间戳,如果不指定,则默认为当前时间戳。

示例

import torch
from torch.utils.tensorboard import SummaryWriter

# 创建 SummaryWriter 对象,指定日志保存的路径
writer = SummaryWriter(log_dir='logs')

# 模拟训练过程,每个 epoch 结束时记录损失值和准确率
for epoch in range(1, 11):  # 假设训练了10个epoch
    # 模拟训练损失值和准确率
    train_loss = 0.5 / epoch  # 以简单的函数模拟训练损失值
    train_accuracy = 0.7 + 0.1 * epoch  # 以简单的函数模拟训练准确率
    
    # 使用 add_scalar() 方法将损失值和准确率写入日志
    writer.add_scalar('Train/Loss', train_loss, epoch)
    writer.add_scalar('Train/Accuracy', train_accuracy, epoch)

# 关闭 SummaryWriter 对象
writer.close()
  • 在这个示例中,使用了 add_scalar() 方法将每个 epoch 的训练损失值和准确率写入到 TensorBoard 日志中。在实际应用中,train_losstrain_accuracy 应该是在实际训练过程中计算得到的真实值。通过将这些标量值记录到 TensorBoard 日志中,可以在 TensorBoard 中轻松地查看损失曲线和准确率曲线,以评估模型的训练情况。

打开logs文件方法

  • 在Pycharm中的终端里面输入tensorboard --logdir=文件名,再点击后面生成的链接
    • logdir:事件文件所在文件夹名
    • 一般要指定端口,在后面加--port=6007(或其他数)避免和其他人的端口冲突
      在这里插入图片描述
03.add_image()方法:
  • 在事件文件中添加图片(本次加载的图片为PIL类型,不符合类型要求,所以要转换,可用OpenCV或numpy直接转换
add_image(tag, img_tensor, global_step=None, walltime=None, dataformats='CHW')
  • 参数

    • tag (string):用于标识图像的名称。
    • img_tensor (Tensor):包含图像数据的张量。该张量的形状应为 [batch_size, channels, height, width]
      • 类型为:torch.Tensor,numpy.array,string/blobname
    • global_step (int, optional):用于标识该事件的全局步数。如果不提供该参数,则默认使用内部计数器。
    • walltime (float, optional):事件发生的时间戳。如果不提供该参数,则默认使用当前时间。
    • dataformats (string, optional):图像数据的格式。支持的格式包括 'CHW' (默认) 和 'HWC'
  • 描述: 将图像数据添加到 TensorBoard 中,以便进行可视化。

  • 返回值: 无。

  • 示例

    writer.add_image('MNIST Images', images, global_step=0)
    

在上述示例中,tag 参数是用于标识图像的名称,img_tensor 参数是包含图像数据的张量,global_step 参数是可选的,用于指定事件的全局步数,walltime 参数是可选的,用于指定事件发生的时间戳,dataformats 参数是可选的,用于指定图像数据的格式,默认为 'CHW' 格式。

  • 所以从PIL到numpy,需要在add_image()中指定shape中每一个数字/维表示的含义
1`将图片转化为numpy格式
1``利用opencv
2``利用 numpy.array()
  • 先导入numpy

  • 再通过np.array()将PIL格式转化为numpy格式

    import numpy as np
    img_array=np.array(img)
    
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np

writer = SummaryWriter("logs")
image_path = "pytorch数据/hymenoptera_data/train/ants/0013035.jpg"
img_PIL = Image.open(image_path)
img_array = np.array(img_PIL)
writer.add_image("test",img_array,1,dataformats='HWC')

writer.close()

在这里插入图片描述

5.transforms结构及用法

01.什么是transforms?
  • 常用的图像预处理方法 ,一般用于转换图片格式
    有多个图片处理方法,如:

    • ToTensor()对象可传入两种图片格式:

      • PIL:用PIL的Image工具打开

      • numpy:用OpenCV打开

02.transforms该如何使用?
  • 首先创建一个具体的工具(如ToTensor工具,相当于创建类对象)tool = transforms.ToTensor()

  • 然后给工具传入参数(传入图片):result = tool(input)

  • 最后得到tensor类型的图片

    from torchvision import transforms
    from PIL import Image
    
    img_path="pytorch数据/hymenoptera_data/train/ants/0013035.jpg"
    img = Image.open(img_path)
    
    tensor_trans = transforms.ToTensor()#创建具体工具
    tensor_img = tensor_trans(img)#使用工具转换图片格式
    
1`为什么我们需要Tensor数据类型?
  • tensor类型中的很多属性我们都需要在神经网络中用到,如反向传播、梯度
  • 所以我们必定要用到transforms将数据转换为tensor类型,然后进行训练
03.常见的transforms
  • 可直接去查看文档里查看其相关方法的及其用法

  • 注:Python类中 ——call—— 方法的用法:

    • 相当于类对象的有参构造
      def --call-- (self, 参数列表)
    class Person:
        def __call__(self,name):
            print("__call__"+"hello"+name)
    
        def hello(self,name):
            print("hello",name)
    
    person = Person()
    person("zhangsan")
    person.hello("list")
    
    """
    output:
    __call__hellozhangsan
    hello list
    """
    
1`ToTensor 方法的使用:
  • 前面讲过,先把图片打开为PIL类型或者numpy类型的对象
    然后将对象传入创建好的ToTensor工具,将图片格式转为Tensor类型
2`Normalize-归一化的使用:
  • 公式:

    transform.Normalize(mean,std)
    """
    mean:对应RGB三个通道的均值
    std:对应RGB三个通道的标准差
    """
    
    import torchvision.transforms as transforms
    
    # 定义均值和标准差
    mean = [0.5, 0.5, 0.5]  # 对应RGB三个通道的均值
    std = [0.5, 0.5, 0.5]   # 对应RGB三个通道的标准差
    
    # 创建transform
    normalize = transforms.Normalize(mean=mean, std=std)
    
    # 使用transform
    transform = transforms.Compose([
        transforms.ToTensor(),  # 将图像转换为Tensor
        normalize               # 对图像进行标准化处理
    ])
    
    # 应用transform
    normalized_image = transform(image)
    
  • 归一化的计算公式:input[channel] = (input[channel] - mean[channel])/std[channel]

  • 目的:改变图像的像素范围,

  • 用法:假设是三通道的图片,且像素范围是(0,1)
    trans_norm=transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
    先实例化一个Normalize对象,然后传入两个参数,一个均值,一个标准差(都是三通道的),结果就将图片的像素范围变成(-1,1)

  • 注:只有tensor类型的图片能使用这个方法,所以将上边ToTensor的图片直接使用即可。

from PIL import Image
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("logs")  # Corrected initialization

img = Image.open("pytorch数据/images/pytorch.jpg")

# Totensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
writer.add_image("Totensor", img_tensor)

# Normalize
print(img_tensor[0][0][0])
trans_norm = transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])
img_norm = trans_norm(img_tensor)
print(img_norm[0][0][0])

writer.close()

"""
output:
tensor(0.8471)
tensor(0.6941)
"""

在这里插入图片描述

3`Resize-图片缩放:
  • 目的:Resize the input PIL Image to the given size, and return a PIL Image
  • API:transforms.Resize(size, interpolation=2)
    • size(sequence 或 int):所需的输出大小。如果 size 是类似 (h, w) 的序列,则输出大小将与此匹配。如果大小为整数,则图像的较小边缘将与此数字匹配,保持纵横比。即,如果高度>宽度,则图像将重新缩放为(大小 \times \text{height} / \text{width},大小)。例如,表示图像的较短边将调整为 256,较长的边将按比例调整大小。
    • interpolation(int,可选):所需的插值。默认值为(双线性插值)。其他选项包括:
      • PIL.Image.NEAREST(最近邻插值)
      • PIL.Image.BILINEAR(双线性插值)
      • PIL.Image.BICUBIC(双三次插值)
      • PIL.Image.LANCZOS(Lanczos 重采样)
  • 用法:同样可以输入参数列表或一个参数,一个参数的话代表缩放为参数大小的正方形
    • trans_resize = transforms.Resize((512,700)) #传入一个参数列表,实例化一个resize对象对图片进行缩放
    • img_resize = trans_resize(img) #传入一个PIL图片
    • 最后再将输出的图片转为tensor类型用SummaryWriter进行输出
from PIL import Image
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("logs")
img = Image.open("pytorch数据/images/pytorch.jpg")
print(img)

# Totensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
writer.add_image("Totensor", img_tensor)

# Normalize
print(img_tensor[0][0][0])
trans_norm = transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])
img_norm = trans_norm(img_tensor)
print(img_norm[0][0][0])
writer.add_image("Normalize",img_norm,2)

#Resize
trans_resize = transforms.Resize((512,512))
# img PIL -> resize -> img_resize PIL
img_resize = trans_resize(img)
print(img_resize)
# img_resize PIL -> totensor -> img_resize tensor
img_resize = trans_totensor(img_resize)
writer.add_image("Resize",img_resize)

"""
output:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=474x266 at 0x21B1D576400>
tensor(0.8471)
tensor(0.6941)
<PIL.Image.Image image mode=RGB size=512x512 at 0x21B1D5E1D90>
"""

在这里插入图片描述

4`Compose-组合方法的使用
  • 目的:将几个转换组合在一起。此转换不支持torchscript。输入是一个transforms对象的列表相当于简化操作步骤(本质上相当于一个合并的功能,按照写的列表去执行流程)

  • 用法:

    • 还是先实例化对象,然后传参
    • 传入transforms对象列表进行实例化
    transform = transforms.Compose([
        transforms.Resize(256),
        transforms.RandomCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])
    
    # Compose - resize - 2
    trans_resize_2 = transforms.Resize(512)
    trans_compose = transforms.Compose([trans_resize_2,trans_totensor])
    # PIL -> PIL -> tensor
    img_resize_2 = trans_compose(img)
    writer.add_image("Resize",img_resize_2,1)
    
  • 注:组合的transforms后一个对象的输入要和前一个对象的输出一致

5`RandomCrop–随机裁剪
  • 公式:

    transforms.RandomCrop(size, padding=None, pad_if_needed=False, fill=0, padding_mode='constant')
    
    • size:指定裁剪后的图像尺寸,可以是一个整数(表示将图像裁剪为正方形),也可以是一个tuple (height, width),表示裁剪后的高度和宽度。
    • padding:可选参数,指定在裁剪之前在图像周围填充的像素数。如果提供了这个参数,图像将首先填充,然后进行随机裁剪
    • pad_if_needed:可选参数,如果设置为True,并且输入图像的尺寸小于要求的裁剪尺寸,则会进行填充。
    • fill:可选参数,用于填充的像素值。
    • padding_mode:可选参数,指定填充的方式,可以是'constant''edge''reflect''symmetric'
  • 目的:在一个随机的位置裁剪给定的图像。返回的结果也是一个PIL,可以传一个参数或两个通道的列表

  • 用法:与上边的使用类似

    #RandomCrop
    trans_random = transforms.RandomCrop(64)
    trans_compose_2 = transforms.Compose([trans_random,trans_totensor])
    for i in range(10):
        img_crop = trans_compose_2(img)
        writer.add_image("RandomCrop",img_crop,i)
    

在这里插入图片描述

6`小结
  • 多关注输入和输出类型

  • 不会的多看官方文档

  • 关注方法需要什么参数

  • 不知道返回值的时候:

    • print
    • print(type())
    • debug

6.torchvision中的数据集使用

  • 去pytorch官方文档找torchvision的datasets模块,里面有很多开元的数据集,可以在代码里直接使用和下载,方法参考官方文档。

  • 下载数据集的时候,download选项可以一直为True,因为已下载的不会重复下载。

  • 下载数据集的同时可以转换整个数据集的数据类型:在每个torchvision.datasets下的数据集都会有transforms选项,即transforms转换的实例对象。
    代码如下:

import torchvision
from torch.utils.tensorboard import SummaryWriter

dataset_transform = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
])

train_set = torchvision.datasets.CIFAR10(root="./dataset",transform=dataset_transform,train=True,download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset",transform=dataset_transform,train=False,download=True)

# print(test_set[0])
# print(test_set.classes)
#
# img,target = test_set[0]
# print(img)
# print(target)
# print(test_set.classes[target])
# img.show()
# print(test_set[0])
writer = SummaryWriter("p10")
for i in range(10):
    img,target = test_set[i]
    writer.add_image("test_set",img,i)

writer.close()

7.搭建神经网络

0.1神经网络的基本骨架–nn.Module的使用
  • 注·:Python中类的定义时,类的继承直接在括号里添加
    class 类名(继承的类)

  • nn(natural network)下的container是nn的容器,包含了基本骨架

  • 其中container中的nn.Module是所有神经网络模块最基本的一个类,为所有神经网络提供基本骨架,
    然后再其中进行填充就能形成一个神经网络

  • forward是一个前向的神经网络处理器(一般会对其进行重写)

  • 如果神经网络要重写初始方法,则必须要调用父类的初始化函数

  • 总结:

    • 一个nn.module可以视为一个块。所有的module包含两个主要函数:
init函数:在里边定义一些需要的类或参数。包括网络层

forward函数:做最终的计算和输出,其形参就是模型(块)的输入。
import torch
from torch import nn


class King(nn.Module):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

    def forward(self,input):
        output = input + 1
        return output

king = King()
x = torch.tensor(1.0)
output = king(x)
print(output)

"""
output:
tensor(2.)
"""
02.神经网络–卷积层(torch.nn.conv)
  • torch.nn其实就是对torch.nn.function的进一步封装

  • 卷积简介

    • 卷积操作是指在输入数据上应用一个滤波器(也称为卷积核或过滤器)进行局部的特征提取。这个滤波器是一个小的矩阵,通过与输入数据进行卷积运算,可以有效地捕捉输入数据中的局部模式或特征**。卷积操作的结果称为特征图或卷积特征。**
    • 卷积层通常具有多个滤波器,每个滤波器都学习到不同的特征。通过在不同位置和方向上移动滤波器进行卷积操作,可以在输入数据的不同位置捕捉到不同的特征。这样,卷积层能够提取输入数据的多个特征,并且具有参数共享的特性,可以大大减少模型的参数量,提高模型的泛化能力。
    • 卷积层通常还包括其他的组件,例如激活函数和池化操作。激活函数用于引入非线性,增加模型的表达能力;池化操作则用于减少特征图的尺寸,降低计算复杂度,并且增强模型对平移、缩放和旋转等变换的鲁棒性。
    • 卷积层的设计和参数设置对神经网络的性能和效果有着重要的影响。通过合理设计卷积核的大小、数量和步长等参数,可以使得神经网络更好地适应不同的数据和任务。因此,卷积层作为神经网络中的核心组件,在图像处理、语音识别等领域有着广泛的应用,并且在深度学习的发展中扮演着重要的角色。
  • conv2d(一般用的比较多)的用法

    import torch.nn.functional as F
    output = F.conv2d(input=x, weight=weight, bias=bias, stride=stride, padding=padding)
    
    • input: 输入的四维张量,形状为 [in_channels, batch_size, height, width]
      • in_channels: 表示输入数据的通道数,也称为特征通道数或输入通道数。对于灰度图像来说,通道数为 1,表示图像的灰度信息;对于彩色图像来说,通道数为 3(RGB),表示图像的红、绿、蓝三个通道的信息。
      • batch_size: 表示一次输入的样本数量,即一批数据中包含了多少个样本。在深度学习中,通常会使用批处理(batch)来训练模型,这样可以提高训练效率和稳定性。
      • heightwidth: 表示输入数据的高度和宽度。对于图像来说,这两个维度分别表示图像的高度和宽度像素数目。在卷积神经网络中,这两个维度通常表示图像的空间尺寸,用于表示图像的形状和结构信息。
    • weight: 卷积核的权重参数,形状为 [out_channels, in_channels, kernel_height, kernel_width]
      • out_channels 是指卷积操作后得到的输出特征图的通道数目。在二维卷积操作中,每个卷积核会生成一个输出通道,因此输出特征图的通道数目就是卷积核的数量,也就是 out_channels
    • bias: 可选的偏置参数,形状为 [out_channels],默认为 None
    • stride: 卷积操作的步长,可以是单个整数或元组 (stride_height, stride_width)
    • padding: 卷积操作的填充大小,可以是单个整数或元组 (padding_height, padding_width),默认为 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]
])

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

print(input.shape)
print(kernel.shape)

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

#设置步长为2
output2 = F.conv2d(input,kernel,stride = 2)
print(output2)

#周围填充一层为0的像素点
output3 = F.conv2d(input,kernel,stride = 1 ,padding=1)
print(output3)

"""
output:
torch.Size([1, 1, 5, 5])
torch.Size([1, 1, 3, 3])
tensor([[[[10, 12, 12],
          [18, 16, 16],
          [13,  9,  3]]]])
tensor([[[[10, 12],
          [13,  3]]]])
tensor([[[[ 1,  3,  4, 10,  8],
          [ 5, 10, 12, 12,  6],
          [ 7, 18, 16, 16,  8],
          [11, 13,  9,  3,  4],
          [14, 13,  9,  7,  4]]]])
"""
  • output1的具体示例:

在这里插入图片描述

  • 这边有部分卷积动画可以到该网址下找。
  • https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md
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",transform=torchvision.transforms.ToTensor(),train=False,download=True)

dataloader = DataLoader(dataset,batch_size=64)

class King(nn.Module):
    def __init__(self):
        super(King,self).__init__()
        self.conv1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)

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

king = King()
print(king)

step = 0
writer = SummaryWriter("../logs")
for data in dataloader:
    imgs,targets = data
    output = king(imgs)
    print(imgs.shape)
    print(output.shape)
    # torch.Size([64, 3, 32, 32])

    writer.add_image("input", imgs, step,dataformats="NCHW")
    # torch.Size([64, 6, 30, 30]) -> [xxx,3,30,30]
    output = torch.reshape(output,(-1,3,30,30))#若第一个数不清楚可以设定为-1
    writer.add_image("output", output, step,dataformats="NCHW")

    step = step+1


writer.close()

在这里插入图片描述

03.池化层–最大池化(torch.nn.maxpool)
  • 池化层(Pooling Layer)是深度学习中常用的一种层,用于减少特征图的空间尺寸,降低网络的参数数量,从而减少计算量和内存消耗,同时可以提取出特征的主要信息,有助于防止过拟合。池化层通常跟在卷积层后面,在卷积神经网络(CNN)中广泛应用。

  • 池化层的操作是对输入的特征图进行局部区域的聚合操作,通常采用最大池化(Max Pooling)或平均池化(Average Pooling)。**最大池化取输入特征图每个区域的最大值作为输出,而平均池化则取输入特征图每个区域的平均值作为输出。**池化操作通常在每个特征图的非重叠区域内进行,通过设置池化核的大小和步长来控制池化的区域大小和移动步长。

  • 池化层的主要作用包括降维、提取主要特征、保持平移不变性(translation invariance)等。通过池化层的操作,可以逐渐减小特征图的空间尺寸,从而使得网络在保留重要信息的同时降低计算成本和内存消耗。

  • 最大池化层(常用的是maxpool2d)的作用:

    • 一是对卷积层所提取的信息做更一步降维,减少计算量
    • 二是加强图像特征的不变性,使之增加图像的偏移、旋转等方面的鲁棒性
    • 类似于观看视频时不同的清晰度,实际效果就像给图片打马赛克
  • maxpool2d:注意输入的图像形状为4维,即形状不对时要先reshape

  • 具体用法

    torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
    
  • 参数:

    • kernel_size (int or tuple):池化核的大小,可以是单个整数表示正方形核大小,也可以是一个长度为2的元组 (H, W) 表示高度和宽度分别的核大小。

    • stride (int or tuple, optional):池化操作的步长,默认值为 kernel_size

    • padding (int or tuple, optional):在输入的每一边添加 0 的层数,默认值为 0。

    • dilation (int or tuple, optional):卷积核元素之间的间距,默认值为 1。

    • return_indices (bool, optional):如果为 True,则会返回最大值的索引,用于后向传播,默认值为 False。

    • ceil_mode (bool, optional):如果为 True,则使用 ceil 而不是 floor 来计算输出形状,默认值为 False。

      在这里插入图片描述

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",transform=torchvision.transforms.ToTensor(),train=False,download=True)

dataloader = DataLoader(dataset,batch_size=64)

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

# input = torch.reshape(input,(-1,1,5,5))
# print(input.shape)

class King(nn.Module):
    def __init__(self):
        super(King,self).__init__()
        self.maxpool = MaxPool2d(kernel_size=3,ceil_mode = True)

    def forward(self,input):
        output = self.maxpool(input)
        return output

king = King()
# output = king(input)
# print(output)

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

writer.close()

04.非线性激活(Non-linear Activations)
  • 非线性变换的主要目的就是给网中加入一些非线性特征

  • 非线性激活函数是神经网络中的一种特殊函数,用于在神经网络中引入非线性特性。在传统的神经网络中,每一层神经元的输出都是上一层神经元输入的加权和,这是一种线性变换。但是,线性变换无法解决复杂的非线性问题,因此需要引入非线性激活函数。

  • 非线性激活函数的作用是:

    1. 引入非线性特性:通过使用非线性激活函数,神经网络可以学习到更复杂的模式,从而解决更复杂的问题。
    2. 提升模型的表达能力:非线性激活函数可以增加模型的非线性表达能力,使得模型可以更好地拟合复杂的数据分布。
    3. 防止过拟合:非线性激活函数可以防止过拟合,因为非线性激活函数会使得模型的输出变得不稳定,从而降低模型的过拟合风险。
  • 非线性越多才能训练出符合各种特征的模型。常见的非线性激活:

    • ReLU:主要是对小于0的进行截断(将小于0的变为0),图像变换效果不明显

      • 主要参数是inplace:
        inplace为真时,将处理后的结果赋值给原来的参数;为假时,原值不会改变。
    • SIGMOID: 归一化处理

      • 效果没有ReLU好,但对于多远分类问题,必须采用sigmoid
  • relu例子:

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

input = torch.tensor([
    [1,-0.5],
    [-1,3]
])

input = torch.reshape(input,(-1,1,2,2))
print(input.shape)

class King(nn.Module):
    def __init__(self):
        super(King,self).__init__()
        self.relu1 = ReLU()

    def forward(self,input):
        output = self.relu1(input)
        return output

king = King()
output = king(input)
print(output)

"""
output:
torch.Size([1, 1, 2, 2])
tensor([[[[1., 0.],
          [0., 3.]]]])
"""
  • SIGMOID例子
import torch
import torchvision
from torch import nn
from torch.nn import  Sigmoid
from torch.nn import Module
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

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


# input = torch.tensor([
#     [1,-0.5],
#     [-1,3]
# ])

# input = torch.reshape(input,(-1,1,2,2))
# print(input.shape)

class King(nn.Module):
    def __init__(self):
        super(King,self).__init__()
        self.relu1 =Sigmoid()

    def forward(self,input):
        output = self.relu1(input)
        return output

king = King()
# output = king(input)
# print(output)
step = 0
writer = SummaryWriter("logs_relu")
for data in dataloader:
    imgs,targets = data
    writer.add_images("input",imgs,step)
    step += 1
    output = king(imgs)
    writer.add_images("output",output,step)

writer.close()

05.线性层(torch.nn.linea)
  • 线性层又叫全连接层,其中每个神经元与上一层所有神经元相连,一个简单的线性层如下图所示:

  • 线性层是深度学习中常见的一种层,用于将输入数据与权重矩阵相乘并添加偏置项,其作用是对输入数据进行线性变换。在神经网络中,线性层通常用于将输入数据映射到更高维的空间,以便后续的非线性变换可以更好地对数据进行建模。

  • 具体来说,线性层的作用可以用以下数学表达式表示:
    o u t p u t = i n p u t × w e i g h t + b i a s o u t p u t output=input×weight+biasoutput output=input×weight+biasoutput

    • 其中,inputinput 是输入数据,weightweight 是权重矩阵,biasbias 是偏置项,outputoutput 是线性层的输出。
  • 线性层的主要作用是进行特征的线性组合,通过学习适当的权重和偏置,线性层可以帮助神经网络更好地适应输入数据,从而实现更好的分类、回归或其他任务。然而,单独的线性变换是有限的,通常需要结合非线性激活函数(如ReLU、Sigmoid等)来构建更复杂的神经网络模型,以便学习非线性关系。

  • 线性函数为:

    torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)
    
  • 其中重要的3个参数in_features、out_features、bias说明如下:

    • in_features:每个输入(x)样本的特征的大小

    • out_features:每个输出(y)样本的特征的大小

    • bias:如果设置为False,则图层不会学习附加偏差默认值是True,表示增加学习偏置。在上图中,in_features=d,out_features=L。

  • 作用可以是缩小一维的数据长度

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

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

class King(nn.Module):
    def __init__(self):
        super(King,self).__init__()
        self.linear =Linear(196608,10)

    def forward(self,input):
        output = self.linear(input)
        return output

king = King()
# output = king(input)
# print(output)
step = 0
writer = SummaryWriter("logs_linear")
for data in dataloader:
    imgs,targets = data
    writer.add_images("input",imgs,step)
    step += 1
    output = torch.reshape(imgs,(1,1,1,-1))
    # 或者使用 torch。flatten()来做一个摊平操作
    #output = torch.flatten(imgs)
    print(output.shape)
    output = king(output)
    writer.add_images("output",output,step)
    print(output.shape)

writer.close()

06.其他层

在这里插入图片描述

  • 框中的层在部分特定情形使用较多
  • 其他层使用较少,再次不做赘述
07.搭建神经网络小实战与SEQUENTIAL的使用(torch.nn.Sequential)
1`搭建神经网络

在这里插入图片描述

  • 一些值需要通过公式去计算,如本例中的padding值
2`SEQUENTIAL的使用

Sequential是PyTorch中一个用于构建神经网络模型的容器它允许将各种层按顺序堆叠起来以构建神经网络模型。你可以将各种类型的层(如全连接层、卷积层、池化层等)依次传递给Sequential,然后它会按照你传递的顺序自动构建网络模型。

import torch
from tensorboardX import SummaryWriter
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential


class King(nn.Module):
    def __init__(self):
        super(King, self).__init__()

        self.conv1 = Conv2d(3, 32, 5, padding=2)
        self.maxpool1 = MaxPool2d(2)
        self.conv2 = 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)

        self.model = nn.Sequential(
            self.conv1,
            self.maxpool1,
            self.conv2,
            self.maxpool2,
            self.conv3,
            self.maxpool3,
            self.flatten,
            self.linear1,
            self.linear2
        )


    def forward(self,x):
        x = self.model(x)


        return x
king = King()
print(king)

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

writer = SummaryWriter("log_sqe")
writer.add_graph(king,input)
writer.close()

08.损失函数和反向传播
  • 注意:inputs和targets的格式一定要符合要求,一般要对其进行reshape和dtype
1`损失函数(Loss):有多种计算方式
  • 计算实际输出和目标之间的差距

  • 为我们更新输出 提供一定的依据

    • 更新输出:

      • 反向传播(backward)–>
      • 计算出梯度(grade)–>
      • 根据梯度和学习率来更新参数–>
      • 减小loss
    • 损失函数举例:

      在这里插入图片描述

      • 均方方差损失(MSELoss()):

      • 在这里插入图片描述

        import torch
        from torch.nn import L1Loss, MSELoss
        
        inputs = torch.tensor([1,2,3],dtype=torch.float32)
        targets = torch.tensor([1,2,5],dtype=torch.float32)
        
        inputs = torch.reshape(inputs,(1,1,1,3))
        targets = torch.reshape(targets,(1,1,1,3))
        
        loss = L1Loss()
        result1 = loss(inputs,targets)
        
        loss_mse = MSELoss()
        result2 = loss_mse(inputs,targets)
        
        #print(result1)
        print(result2)
        
        #output:tensor(1.3333)
        
      • L1Loss(): 如图所示,计算的结果

        在这里插入图片描述
        import torch
        from torch.nn import L1Loss
        
        inputs = torch.tensor([1,2,3],dtype=torch.float32)
        targets = torch.tensor([1,2,5],dtype=torch.float32)
        
        inputs = torch.reshape(inputs,(1,1,1,3))
        targets = torch.reshape(targets,(1,1,1,3))
        
        loss = L1Loss()
        result = loss(inputs,targets)
        
        print(result)
        
        #output:tensor(0.6667)
        
      • 交叉熵损失(CrossEntropyLoss())

        • x是网络输出的数组,class是类别的下标
        • 交叉熵损失函数会自动将模型的输出应用Softmax函数,并计算交叉熵损失。因此,在使用交叉熵损失函数时,通常不需要手动在模型的输出之后应用Softmax函数。
        在这里插入图片描述
        import torch
        from torch.nn import L1Loss, MSELoss, CrossEntropyLoss
        
        inputs = torch.tensor([1,2,3],dtype=torch.float32)
        targets = torch.tensor([1,2,5],dtype=torch.float32)
        
        inputs = torch.reshape(inputs,(1,1,1,3))
        targets = torch.reshape(targets,(1,1,1,3))
        
        loss = L1Loss()
        result1 = loss(inputs,targets)
        
        loss_mse = MSELoss()
        result2 = loss_mse(inputs,targets)
        
        #print(result1)
        #print(result2)
        
        x = torch.tensor([0.1,0.2,0.3])
        y = torch.tensor([1])
        x = torch.reshape(x,(1,3))
        loss_cross = CrossEntropyLoss()
        result3 = loss_cross(x,y)
        print(result3)
        
        #output:tensor(1.1019)
        

2`反向传播:
  • 反向传播是一个更新参数的过程。
  • (1)前向传播:将训练集数据输入到ANN的输入层,经过隐藏层,最后到达输出层并输出结果。【输入层—隐藏层–输出层】
  • (2)反向传播:由于ANN的输入结果与输出结果有误差,则计算估计值与实际值之间的误差,并将该误差从输出层向隐藏层反向传播,直至传播到输入层。【输出层–隐藏层–输入层】
  • (3)权重更新:在反向传播的过程中,根据误差调整各种参数的值;不断迭代上述过程,直至收敛。
    举一个例子来说明我理解的反向传播的思想是:
    • (1)前向传播:三个人在玩你画我猜的游戏,然后第一个人给第二个人描述,再将信息传递给第三个人,由第三个人说出画的到底是啥。
    • (2)反向传播:第三个人得知自己说的和真实答案之间的误差后,发现他们在传递时的问题差在哪里,向前面一个人说下次描述的时候怎样可以更加准确的传递信息。就这样一直向前一个人告知。
    • (3)三个人之间的的默契一直在磨合,然后描述的更加准确。
08.优化器
1`过程描述
  • 继上节的计算损失函数和反向传播,

  • 之后便是根据损失值,利用优化器进行梯度更新,然后不断降低loss的过程

  • 一般要对数据集扫描多遍,进行参数的多次更新,才能得到一个较好的效果。

    注意,每次更新后要将梯度置0,然后重新计算梯度注意,每次更新后要将梯度置0,然后重新计算梯度

2`常用优化器:
  • 优化器的种类比较多,常用的就是随机梯度下降(SGD) 等
  • 不同的优化器的参数列表一般不同,但都会有 params(模型的参数列表)和lr(学习率)参数,
  • 一般设置这两个参数,其他的可用默认值
3`举例SGD
import torch
import torch.nn as nn
import torch.optim as optim

# 定义一个简单的神经网络模型
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.fc = nn.Linear(10, 1)  # 一个线性层,输入大小为 10,输出大小为 1

    def forward(self, x):
        return self.fc(x)

# 创建模型实例
model = SimpleModel()

# 定义损失函数
criterion = nn.MSELoss()

# 定义优化器,学习率设置为 0.01
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 假设有输入数据 input_data 和对应的目标数据 target_data

# 模型训练
for epoch in range(num_epochs):
    # 前向传播
    outputs = model(input_data)
    
    # 计算损失
    loss = criterion(outputs, target_data)
    
    # 反向传播和优化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
import torch
import torchvision
from tensorboardX import SummaryWriter
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

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

dataloader = DataLoader(dataset,batch_size=64)
class King(nn.Module):
    def __init__(self):
        super(King, self).__init__()
        self.model = nn.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.model(x)
        return x

loss = nn.CrossEntropyLoss()
king = King()
optim = torch.optim.SGD(king.parameters(),lr=0.01)
# lr是学习速率
for epoh in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs, targets = data
        outputs = king(imgs)
        result_loss = loss(outputs, targets)
        optim.zero_grad()  # 将梯度清零
        result_loss.backward()  # 通过反向传播求出每一个节点的梯度
        optim.step()  # 对每一个进行调优
        # print(result_loss)
        running_loss = running_loss + result_loss
    print(running_loss)

8.模型训练

01.现有网络模型的使用及修改

1.1、VGG16模型:

vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
  • 上面两行代码的区别:

    • 第一个是模型初始化的参数,第二个是经过训练的参数
    • 第一个相当于一个单纯的 神经网络结构,第二个是经过训练的神经网络结构(需要下载参数)
  • 因为VGG16最终的输出是1000个分类,加入我们需要10个分类的话,就需要改动

    • VGG16的原始结构

      VGG(
        (features): Sequential(
          (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (1): ReLU(inplace=True)
          (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (3): ReLU(inplace=True)
          (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
          (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (6): ReLU(inplace=True)
          (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (8): ReLU(inplace=True)
          (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
          (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (11): ReLU(inplace=True)
          (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (13): ReLU(inplace=True)
          (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (15): ReLU(inplace=True)
          (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
          (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (18): ReLU(inplace=True)
          (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (20): ReLU(inplace=True)
          (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (22): ReLU(inplace=True)
          (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
          (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (25): ReLU(inplace=True)
          (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (27): ReLU(inplace=True)
          (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (29): ReLU(inplace=True)
          (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
        )
        (avgpool): AdaptiveAvgPool2d(output_size=(7, 7))
        (classifier): Sequential(
          (0): Linear(in_features=25088, out_features=4096, bias=True)
          (1): ReLU(inplace=True)
          (2): Dropout(p=0.5, inplace=False)
          (3): Linear(in_features=4096, out_features=4096, bias=True)
          (4): ReLU(inplace=True)
          (5): Dropout(p=0.5, inplace=False)
          (6): Linear(in_features=4096, out_features=1000, bias=True)
        )
      )
      
    • 修改的方法有两种:

      • 添加一层(线性层),改变最后的输出类别数

        vgg16_true.classifier.add_module("add_linear",nn.Linear(1000,10))
        print(vgg16_true)
        """
        output:
        VGG(
          (features): Sequential(
            (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (1): ReLU(inplace=True)
            (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (3): ReLU(inplace=True)
            (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
            (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (6): ReLU(inplace=True)
            (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (8): ReLU(inplace=True)
            (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
            (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (11): ReLU(inplace=True)
            (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (13): ReLU(inplace=True)
            (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (15): ReLU(inplace=True)
            (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
            (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (18): ReLU(inplace=True)
            (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (20): ReLU(inplace=True)
            (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (22): ReLU(inplace=True)
            (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
            (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (25): ReLU(inplace=True)
            (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (27): ReLU(inplace=True)
            (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
            (29): ReLU(inplace=True)
            (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
          )
          (avgpool): AdaptiveAvgPool2d(output_size=(7, 7))
          (classifier): Sequential(
            (0): Linear(in_features=25088, out_features=4096, bias=True)
            (1): ReLU(inplace=True)
            (2): Dropout(p=0.5, inplace=False)
            (3): Linear(in_features=4096, out_features=4096, bias=True)
            (4): ReLU(inplace=True)
            (5): Dropout(p=0.5, inplace=False)
            (6): Linear(in_features=4096, out_features=1000, bias=True)
            (add_linear): Linear(in_features=1000, out_features=10, bias=True)
          )
        )
        
        """
        
      • 改变原有模型的输出
        以vgg_false模型为例,要改变下标为6的那一层

        vgg16_false.classifier[6] = nn.Linear(4096,10)
        
02.网络模型的保存和读取
1`概述
  • 因为有些较大的网络模型(无论是加载初始参数还是预训练过的参数)都需要花费一定的时间,特别是预训练的模型,要花很长时间下载参数,
  • 所以我们可以将反复用到的模型保存下来,到时候直接读取使用即可
2`保存和读取方法
  • 一般训练好的模型都需要进行保存,否则每次使用都要重新训练。

  • 方式一

    • 保存:保存模型结构及其参数。torch.save(model, path)

      torch.save(vgg16,"vgg16_method1.pth")
      
    • 读取:获取一个完整的模型。torch.load(“模型名”)(结构和参数皆能获取)

      model = torch.load("vgg16_method1.pth")
      
  • 方式二

    • 保存:只保存模型的参数。(将参数以字典的方式存储)torch.save(model.state_dict(), path)(官方推荐)

    • 读取:只能加载出模型的参数,要先新建网络模型,然后再装载参数(一般用于加载预训练的参数)。

      vgg16 = torchvision.models.vgg16(pretrained=False)
      vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
      # model = torch.load("vgg16_method2.pth")
      print(vgg16)
      
  • 陷阱:自定义的网络如果保存后再加载的话,需要再重新定义一遍网络结构。

03.完整的模型训练套路
  • 准备数据集
  • 创建迭代器
  • 创建网络模型
  • 创建损失函数
  • 添加优化器
  • 设置一些训练的参数(训练次数、训练轮数等),
  • 开始模型训练
    • 优化器不断优化模型
  • 开始测试
  • 打印测试结果(准确度)
补充:

1``torch.argmax()`

  • torch.argmax()是 PyTorch 中的一个函数,用于找到张量中指定维度上最大值的位置索引。
torch.argmax(input, dim=None, keepdim=False, out=None, dtype=None)

参数说明:

  • input:要进行操作的输入张量。
  • dim:要沿着哪个维度进行操作。如果为 None,则在整个张量中查找最大值的索引,为1则是横向,为2则是纵向。
  • keepdim:如果为 True,则结果张量保持输入张量的维度。
  • out:输出张量,用于保存结果。
  • dtype:输出张量的数据类型。

返回值:

  • 返回一个新的张量,包含输入张量中最大值的索引。
import torch

outputs = torch.tensor([
    [0.1,0.2],
    [0.3,0.4]
])

print(outputs.argmax(1))

#output:
tensor([1, 1])
import torch.optim
import torchvision
from tensorboardX import SummaryWriter
from torch import nn
from torch.utils.data import DataLoader


train_data = torchvision.datasets.CIFAR10("./dataset",transform=torchvision.transforms.ToTensor(),train=True,download=True)
test_data = torchvision.datasets.CIFAR10("./dataset",transform=torchvision.transforms.ToTensor(),train=False,download=True)

# 数据集的大小
train_data_size = len(train_data)
test_data_size = len(test_data)
print(f"训练数据集的长度为:{format(train_data_size)}")
print(f"测试数据集的长度为:{format(test_data_size)}")

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

# 搭建神经网络
class King(nn.Module):
    def __init__(self):
        super(King, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(3, 32, 5, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, padding=2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(1024, 64),
            nn.Linear(64, 10)
        )
    def forward(self,x):
        x = self.model(x)
        return x

king = King()

# 损失函数    此处是交叉熵
loss_fn = nn.CrossEntropyLoss()

# 优化器
learning_rate = 0.01
optimzer = torch.optim.SGD(king.parameters(),lr=learning_rate)

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

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

for i in range(epoch):
    print(f"------第 {i+1} 轮训练开始------")

    #训练步骤开始
    for data in train_dataloader:
        imgs,targets = data
        outputs = king(imgs)
        loss = loss_fn(outputs,targets)

        # 优化器优化模型
        optimzer.zero_grad()
        loss.backward()
        optimzer.step()

        total_train_step+=1
        if total_train_step % 100 ==0:
            print(f"训练次数:{total_train_step},Loss: {loss}")
            writer.add_scalar("train_loss",loss.item(),total_train_step)

    # 测试步骤开始
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():
        for data in test_dataloader:
            imgs,targets = data
            outputs = king(imgs)
            loss = loss_fn(outputs, targets)
            total_test_loss += loss
            accuracy = (outputs.argmax(1) ==targets).sum()
            total_accuracy += accuracy

    print(f"整体测试集上的Loss:{total_test_loss}")
    print(f"整体测试集上的正确率:{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+=1

    torch.save(king,"king_{}.pth".format(i))
    print("模型已保存")

writer.close()


"""
output:
训练数据集的长度为:50000
测试数据集的长度为:10000
------第 1 轮训练开始------
训练次数:100,Loss: 2.2905163764953613
训练次数:200,Loss: 2.281721353530884
训练次数:300,Loss: 2.252750873565674
训练次数:400,Loss: 2.1662673950195312
训练次数:500,Loss: 2.0202040672302246
训练次数:600,Loss: 1.976378321647644
训练次数:700,Loss: 2.002121686935425
整体测试集上的Loss:1956.302734375
整体测试集上的正确率:0.30079999566078186
模型已保存
------第 2 轮训练开始------
训练次数:800,Loss: 1.848758339881897
训练次数:900,Loss: 1.7890944480895996
训练次数:1000,Loss: 1.8642159700393677
训练次数:1100,Loss: 1.963551640510559
训练次数:1200,Loss: 1.671011209487915
训练次数:1300,Loss: 1.59224271774292
训练次数:1400,Loss: 1.7198998928070068
训练次数:1500,Loss: 1.7643325328826904
整体测试集上的Loss:1887.615478515625
整体测试集上的正确率:0.32919999957084656
模型已保存
------第 3 轮训练开始------
训练次数:1600,Loss: 1.7233344316482544
训练次数:1700,Loss: 1.6460649967193604
训练次数:1800,Loss: 1.9495211839675903
训练次数:1900,Loss: 1.6880135536193848
训练次数:2000,Loss: 1.8684566020965576
训练次数:2100,Loss: 1.5057942867279053
训练次数:2200,Loss: 1.4742074012756348
训练次数:2300,Loss: 1.766707420349121
整体测试集上的Loss:1669.1881103515625
整体测试集上的正确率:0.39800000190734863
模型已保存
------第 4 轮训练开始------
训练次数:2400,Loss: 1.7554244995117188
训练次数:2500,Loss: 1.3697314262390137
训练次数:2600,Loss: 1.564285397529602
训练次数:2700,Loss: 1.7147036790847778
训练次数:2800,Loss: 1.4876322746276855
训练次数:2900,Loss: 1.5656487941741943
训练次数:3000,Loss: 1.337398648262024
训练次数:3100,Loss: 1.5050169229507446
整体测试集上的Loss:1603.9476318359375
整体测试集上的正确率:0.4185999929904938
模型已保存
------第 5 轮训练开始------
训练次数:3200,Loss: 1.3732656240463257
训练次数:3300,Loss: 1.4424837827682495
训练次数:3400,Loss: 1.496065616607666
训练次数:3500,Loss: 1.530358076095581
训练次数:3600,Loss: 1.5837149620056152
训练次数:3700,Loss: 1.3562780618667603
训练次数:3800,Loss: 1.2520641088485718
训练次数:3900,Loss: 1.4492415189743042
整体测试集上的Loss:1554.6851806640625
整体测试集上的正确率:0.43479999899864197
模型已保存
------第 6 轮训练开始------
训练次数:4000,Loss: 1.392557144165039
训练次数:4100,Loss: 1.4287751913070679
训练次数:4200,Loss: 1.5785491466522217
训练次数:4300,Loss: 1.1886277198791504
训练次数:4400,Loss: 1.1640338897705078
训练次数:4500,Loss: 1.3129788637161255
训练次数:4600,Loss: 1.4037131071090698
整体测试集上的Loss:1473.1033935546875
整体测试集上的正确率:0.4634999930858612
模型已保存
------第 7 轮训练开始------
训练次数:4700,Loss: 1.327232003211975
训练次数:4800,Loss: 1.5121268033981323
训练次数:4900,Loss: 1.349504828453064
训练次数:5000,Loss: 1.4058730602264404
训练次数:5100,Loss: 1.000952124595642
训练次数:5200,Loss: 1.2390520572662354
训练次数:5300,Loss: 1.185653567314148
训练次数:5400,Loss: 1.3723623752593994
整体测试集上的Loss:1397.11767578125
整体测试集上的正确率:0.4909999966621399
模型已保存
------第 8 轮训练开始------
训练次数:5500,Loss: 1.2305434942245483
训练次数:5600,Loss: 1.23661470413208
训练次数:5700,Loss: 1.2103543281555176
训练次数:5800,Loss: 1.2133100032806396
训练次数:5900,Loss: 1.3391343355178833
训练次数:6000,Loss: 1.5014292001724243
训练次数:6100,Loss: 1.0114054679870605
训练次数:6200,Loss: 1.1058776378631592
整体测试集上的Loss:1325.4554443359375
整体测试集上的正确率:0.5213000178337097
模型已保存
------第 9 轮训练开始------
训练次数:6300,Loss: 1.405073642730713
训练次数:6400,Loss: 1.1180084943771362
训练次数:6500,Loss: 1.5476839542388916
训练次数:6600,Loss: 1.0472126007080078
训练次数:6700,Loss: 1.1250394582748413
训练次数:6800,Loss: 1.1972692012786865
训练次数:6900,Loss: 1.036494255065918
训练次数:7000,Loss: 0.8717333674430847
整体测试集上的Loss:1268.876708984375
整体测试集上的正确率:0.545799970626831
模型已保存
------第 10 轮训练开始------
训练次数:7100,Loss: 1.1975603103637695
训练次数:7200,Loss: 0.9316152930259705
训练次数:7300,Loss: 1.1453012228012085
训练次数:7400,Loss: 0.8225905299186707
训练次数:7500,Loss: 1.2555389404296875
训练次数:7600,Loss: 1.247175931930542
训练次数:7700,Loss: 0.842944324016571
训练次数:7800,Loss: 1.1817939281463623
整体测试集上的Loss:1218.3699951171875
整体测试集上的正确率:0.5684000253677368
模型已保存
"""
image-20240427173641667
04.GPU训练模型
  • 将网络模型,数据(输入,标注),损失函数都转换为cuda()

    #由之前的代码
    king = king.cuda()
    loss_fn = loss_fn.cuda()
    imgs = imgs.cuda()
    targets = targets.cuda()
    
  • 更好的写法

    if torch.cuda.is_available():
    	king = king.cuda()
    
05.完整的模型验证套路
import torchvision
from PIL import Image

image_path = "pytorch数据/images/dog.png"
image = Image.open(image_path)
print(image)

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


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

import torch
from torch import nn

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

model = torch.load("king_9.pth") #导入训练后的模型
print(model)
image = torch.reshape(image,(1,3,32,32))
model.eval()
with torch.no_grad():#可以节约一些性能
    output = model(image)
print(output)
print(output.argmax(1))

"""
output:
torch.Size([3, 32, 32])
King(
  (model): Sequential(
    (0): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (4): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Flatten(start_dim=1, end_dim=-1)
    (7): Linear(in_features=1024, out_features=64, bias=True)
    (8): Linear(in_features=64, out_features=10, bias=True)
  )
)
tensor([[ 1.3082, -8.2826,  4.5064,  2.6434,  2.8140,  3.6594, -2.4351,  0.3129,
         -1.6153, -5.1815]])
tensor([2])
"""
  • 虽然上面预测错了,但毕竟模型训练的次数太少,多训练几次,正确率会提高,像我运行了30次就成功了
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值