PyTorch深度学习快速入门教程【小土堆】

前记:此篇为观看b站小土堆视频的学习笔记,仅个人复习回顾使用。PyTorch环境的配置及安装以及python编译器的安装部分直接在此跳过,嘻嘻。

学习视频链接:PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】_哔哩哔哩_bilibili

Dataset

Dataset 是一个抽象类,用来表示数据集主要用于两个目的:

 ①数据加载和预处理:Dataset负责从数据源中加载数据,并对数据进行预处理和转换,以便模型可以直接使用。在PyTorch中,我们通常通过继承torch.utils.data.Dataset类来自定义自己的数据集。

 ②数据集信息获取:Dataset提供了数据集的基本信息,如数据的长度(__len__方法)和获取单个样本的方法(__getitem方法)。

# Dataset类提供一种方式去获取数据及其label
# 主要是用于如何获取每一个数据及其label,以及告诉我们有多少数据
# help(Dataset)可以获取到Dataset的官方文档
# Dataset是一个抽象类,继承的子类需要重写__getitem__,和__len__
from torch.utils.data import Dataset
from PIL import Image
import os
class MyData(Dataset):
    def __init__(self,root_dir,label_dir):
        # E:\code\learn_pytorch\dataset\train
        self.root_dir=root_dir
        # ants_image
        self.label_dir=label_dir
        # E:\code\learn_pytorch\dataset\train\ants_image
        # os.path.join()可以将两个路径相加
        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="E:\\code\\learn_pytorch\\dataset\\train"
# 蚂蚁训练集
ants_dir="ants_image"
ants_data=MyData(root_dir,ants_dir)
ant_image,ant_label=ants_data[0]
print(len(ants_data))

# 蜜蜂训练集
bees_dir="bees_image"
bees_data=MyData(root_dir,bees_dir)
bee_image,bee_label=bees_data[0]
print(len(bees_data))

# 将两个训练集相加
train_dataset=ants_data+bees_data
print(len(train_dataset))
train_image_1,train_label_1=train_dataset[0]
train_image_1.show()
train_image_2,train_label_2=train_dataset[1]
train_image_2.show()
train_image_244,train_label_244=train_dataset[243]
train_image_244.show()
train_image_245,train_label_245=train_dataset[244]
train_image_245.show()

小tips:

os是python的标准库之一,提供了与操作系统交互的各种功能。

os.listdir(),接受一个路径作为参数,并返回该路径下所有文件和文件夹的名称列表。

os.path.join(),用于将一个或多个路径组合成一个完整的路径,会自动根据当前操作系统添加适当的路径分隔符。

Image.open(),用于打开指定路径的图像文件,并返回一个表示该图像的对象,通常是一个PIL图像对象或者一个Pillow图像对象。

DataLoader:用于批量加载数据的工具,它可以将返回的单个样本组合成小批量数据。

主要有以下功能:

 ①批量加载和组合:可以按照指定批量大小从Dataset中加载数据,并将数据按照批量组织

 ②数据打乱:在每个epoch开始时,DataLoader可以随机打乱数据,有助于模型更好地学习数据的统计特性。

 ③多进程加载:可以使用多个进程加载数据,加快数据加载速度。

 ④数据预取:在训练时,DataLoader可以预先加载下一个批次的数据,从而减少训练时的等待时间

TensorBoard

TensorBoard 是 TensorFlow 提供的一个可视化工具,用于帮助机器学习工程师和研究人员可视化、理解和调试深度学习模型的训练过程。它提供了多种功能,包括可视化模型结构、显示训练指标、绘制学习曲线、展示图像数据、查看激活直方图等等。TensorBoard 的主要优势在于它能够帮助用户更直观地了解模型在训练过程中的表现和内部状态,从而更有效地调试和优化模型。

TensorBoard 主要功能包括:

  1. Scalars(标量)用于展示训练过程中的标量指标,比如损失函数值、准确率等。可以绘制曲线图,帮助用户监视指标的变化趋势。

  2. Graphs(计算图):展示 TensorFlow 模型的计算图结构,包括各个操作节点和数据流向。这有助于理解模型的结构和梯度流动。

  3. Distributions(分布)Histograms(直方图):用于可视化张量的值的分布情况,比如权重和激活值的分布。这些视图有助于分析权重的稳定性和激活值的分散程度。

  4. Images(图像)展示输入图像、生成图像或者卷积层输出的图像。这对于检查数据增强效果或者卷积网络中间输出非常有用。

  5. Embeddings(嵌入):用于可视化高维数据在低维空间中的表示,通常用于展示词嵌入或者数据嵌入的效果。

  6. Projector(投影仪):与嵌入类似,提供了一个交互式的界面,用于探索高维数据的低维表示。

 ❗❗❗使用TensorBoard的步骤:

导入

from torch.utils.tensorboard import SummaryWrite

创建SummaryWriter 对象writer,并指定日志存储的路径

writer=SummaryWriter("logs")

 将要展示的添加进writer中

#添加y=2x图像

for i in range(100):

        writer.add_scalar("y=2x",2*i,i)

#添加Image图像

writer.add_image(tag, img_tensor, global_step=None, walltime=None, dataformats='CHW')

这些参数的含义如下:

  1. tag:

    • 类型: 字符串
    • 说明: 标识要添加的图像数据。在 TensorBoard 的界面中,使用 tag 来组织和识别不同的图像数据,例如 "input_images" 或 "generated_images" 等。
  2. img_tensor:

    • 类型: Tensorndarray
    • 说明: 要添加的图像数据,通常是一个三维的张量或数组,表示图像的像素值。对于 TensorBoardX,它支持多种数据格式,例如 CHW (通道-高度-宽度) 或 HWC (高度-宽度-通道)。
  3. global_step:

    • 类型: 整数或 None
    • 说明: 可选参数,用于指定当前的训练步数或迭代次数。当你在不同的训练步骤或迭代中添加图像时,通过设定不同的 global_step 可以帮助 TensorBoardX 将这些图像数据进行时间序列化展示,从而更好地理解训练过程中图像的变化。
  4. walltime:

    • 类型: 浮点数或 None
    • 说明: 可选参数,用于指定添加图像的时间戳。通常情况下可以使用系统当前时间。
  5. dataformats:

    • 类型: 字符串
    • 说明: 可选参数,默认为 'CHW' (通道-高度-宽度)。指定输入图像数据的格式,支持 'CHW' 和 'HWC' 两种格式。具体来说:
      • 'CHW' 表示图像数据的排列顺序是 (通道数, 高度, 宽度)。
      • 'HWC' 表示图像数据的排列顺序是 (高度, 宽度, 通道数)。

记录完毕后,关闭 SummaryWriter

 writer.close()

启动TensorBoard服务

运行程序后,在Terminal中tensorboard --logdir=logs --port=6007,然后回车,可以在端口号为6007中打开logs日志(默认端口号为6006)。 

 举例展示输入图像

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

writer=SummaryWriter("logs")
image_path="dataset/train/ants_image/5650366_e22b7e1065.jpg"
img_PIL=Image.open(image_path)
#    img_tensor (torch.Tensor, numpy.array, or string/blobname): Image data
#   img_PIL的类型不对应,需要进行转换,此处转换成numpy.array类型
img_array=np.array(img_PIL)
print(type(img_array))
print(img_array.shape)
# 运行下方代码会报错
# writer.add_image("test",img_array,1)
# 可以查看img_array.shape是否是(3, H, W),此处查看过后shape为(512, 768, 3),不符合(3, H, W),#而#是(H, W, 3),因此会报错,应将代码改成如下
writer.add_image("test",img_array,2,dataformats='HWC')
writer.close()

小tips

1、img.shape 是一个用于获取图像(或者数组、张量)形状的属性。在Python中,特别是在使用像NumPy或PyTorch这样的库时,.shape 不是一个函数,而是一个属性,因此不需要加括号调用。

具体来说:

  • 对于 NumPy 数组,img.shape 返回一个描述数组维度的元组,元组中的每个元素表示对应维度的大小。
  • 对于 PyTorch 张量,img.shape 返回一个 torch.Size 对象,也是描述张量维度的。

例如,在处理图像时,可以使用 img.shape 来了解图像的尺寸和通道数,有助于进行后续的数据处理和分析。

如果shape不是默认值CHW,可以设置dataformats指定输入的格式。

2、img_tensor的类型是Tensor或ndarray如果要显示的输入图像img类型不正确,

 ① 可以用np.array(img)将图像转换成ndarray类型

 ②也可以用opencv来转换为ndarry类型(opencv读出来的是ndarry类型)

import cv2

cv_img=cv2.imread("dataset/train/ants_image/0013035.jpg")

 ③在后续学习完transforms后也可以转换为Tensor类型

tensor_trans=transforms.ToTensor()

tensor_img=tensor_trans(img)

 Transforms

__call__()

__call__() 是 Python 中的一个特殊方法(双下划线方法),用于使一个对象可以像函数一样被调用。当对象实现了 __call__() 方法时,可以直接对该对象使用括号调用,就像调用函数一样。

具体来说,__call__() 方法的作用是定义了对象实例能够被调用时所执行的操作。这种方法允许对象表现得像函数一样,可以接受参数并返回结果。

class Person:
    def __call__(self,name):
        print("__call__"+"hello"+name)
    def hello(self,name):
        print("hello"+name)
person=Person()
person("zhangzhang")
person.hello("lili")

ToTensor

transforms.ToTensor() 是 PyTorch 中的一个转换操作,用于将 PIL 图像或 numpy 数组转换为 PyTorch 的张量(tensor)格式。

具体来说,transforms.ToTensor() 的作用包括以下几个方面:

  1. 数据类型转换: 将 PIL 图像或 numpy 数组中的数据转换为张量格式。在计算机视觉任务中,神经网络通常接受张量作为输入数据。

  2. 通道调整: 将 PIL 图像从 (H x W x C) 的格式转换为 (C x H x W) 的格式,其中 H 表示图像的高度,W 表示宽度,C 表示通道数。这种格式通常是 PyTorch 中图像数据的标准格式,其中 C 表示通道(例如 RGB 图像的通道数为 3)。

  3. 数值范围调整: 将图像的像素值从 [0, 255] 范围(uint8 格式)调整到 [0.0, 1.0] 范围(float32 格式)。这个过程涉及到对数据进行归一化,以便更好地与神经网络模型配合使用,通常会有助于提高训练的稳定性和效果。

总之,transforms.ToTensor() 可以帮助将图像数据转换为适合深度学习模型输入的张量格式,并对数据进行必要的预处理,是构建 PyTorch 训练流水线中常用的一步操作。

from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
from PIL import Image
writer=SummaryWriter("logs")
img=Image.open("dataset/train/ants_image/6240338_93729615ec.jpg")
trans_totensor=transforms.ToTensor()
img_tensor=trans_totensor(img)
writer.add_image("ToTensor01",img_tensor)

 Normalize

Normalize 在深度学习中通常指的是一种数据预处理方法,用于调整输入数据的数值范围,使其更适合神经网络模型进行训练。

具体来说,Normalize 操作的主要目的是对输入数据进行归一化处理,使其具备以下特征:

  1. 零均值化: 将数据的均值调整为 0,即使得数据中心化。
  2. 单位方差化: 将数据的方差调整为 1,使得数据的幅度一致,有助于提高训练稳定性。

对于图像数据,通常会对每个通道(如RGB)进行单独的归一化处理。例如,对于每个通道的数据,计算其均值和标准差,然后使用如下公式进行归一化:

input[channel] = input[channel] - mean[channel] \ std[channel]

其中,mean[channel]std[channel] 分别是数据在该通道上的均值和标准差。

在 PyTorch 中,可以通过 transforms.Normalize(mean, std) 进行归一化处理,其中 meanstd 分别是每个通道的均值和标准差,可以是单个数值或者包含多个数值的元组。这个操作通常在数据预处理阶段进行,有助于提高模型的训练效果和稳定性。

总之,Normalize 是一种常见的数据预处理操作,用于标准化输入数据的分布,以便神经网络更好地学习和收敛。

from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
from PIL import Image
writer=SummaryWriter("logs")
img=Image.open("dataset/train/ants_image/6240338_93729615ec.jpg")
trans_totensor=transforms.ToTensor()
img_tensor=trans_totensor(img)
writer.add_image("ToTensor01",img_tensor)
#归一化
print(img_tensor[0][0][0])
trans_norm=transforms.Normalize([0.5,0.5,0.5],[0.4,0.4,0.4])
img_norm=trans_norm(img_tensor)
print(img_norm[0][0][0])
writer.add_image("Normalize01",img_norm,3)

Resize

transforms.Resize() 主要是针对 PIL.Image 类型的图像进行设计的,因为在图像预处理和数据增强过程中,PIL 库提供了广泛的图像处理功能。如果输入是 Tensor 类型的图像,PyTorch 通常会先将其转换为 PIL.Image 格式,再进行后续的操作。在 PyTorch 的 transforms.Resize() 中,可以根据具体需求传入不同的参数来调整图像的尺寸。

# Resize   input: PIL Image
# img PIL ->resize-> img_resize PIL
print(img.size)
trans_resize=transforms.Resize((512,512))
img_resize=trans_resize(img)
print(img_resize)
img_resize=trans_totensor(img_resize)
writer.add_image("Resize",img_resize,0)

trans_resize_02=transforms.Resize(512)
trans_compose = transforms.Compose([trans_resize_02,trans_totensor])
img_resize_02=trans_compose(img)
writer.add_image("Resize",img_resize_02,1)

小tips: 
transforms.Compose是一个将多个图像变换操作串联起来的类。它接受一个由图像变换操作组成的列表,并按顺序应用这些操作。
trans_compose=transforms.Compose([trans_resize_02,trans_totensor]),
trans_compose是包含两个变换操作的组合,先执行trans_resize_02操作,输出的结果再执行trans_totensor操作,将图像转换为Tensor格式

trans_resize_02=transforms.Resize(512)和trans_resize_02=transforms.Resize((512,512))的区别:
trans_resize_02=transforms.Resize(512):这种形式是将图像的短边缩放到512像素,并保持图像的宽高比
trans_resize_02=transforms.Resize((512,512)):这种形式表示将图像调整为固定的512*512像素大小,不考虑原始图像的宽高比。
如果原始图像的尺寸与512*512不匹配,将会进行拉伸或者压缩以达到目标尺寸,可能会导致图像失真。

RandomCrop

随机裁剪,在训练过程中随机裁剪图像,以增加数据的多样性和模型的鲁棒性。
需要指定一个目标的裁剪尺寸,通常为(size)或者((heighwidth)),前者:图像将会被裁剪为一个正方形,边长为size,后者:图像会被裁剪为高度为height,宽度为width的矩形。裁剪操作通常在图像中随机选择一个位置进行一般情况下,裁剪的左上角位置的选择范围是从图像的左上角到(image_width-width,image_height-height)。
每次对图像的裁剪,裁剪位置都是随机的,增加数据的多样性,帮助模型更好的泛化到不同的场景和图像之中。适合处理尺寸不一的图像,以及在需要对抗平移不变性的任务中,例如物体识别或分类任务。

trans_random=transforms.RandomCrop((100,50))
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)
writer.close()

 torchvision中的数据集

torchvision — Torchvision 0.18 documentation (pytorch.org),左上角版本号切换至0.9.0

下载和准备数据集

train_set=torchvision.datasets.CIFAR10(root="./temp_dataset",

                                 train=True,transform=dataset_transform,download=True)

test_set=torchvision.datasets.CIFAR10(root="./temp_dataset",

                                 train=False,transform=dataset_transform,download=True)

torchvision.datasets.CIFAR10是一个torchvision提供的类,用于加载CIFAR-10数据集
root参数指定数据集存储的路径,如果设定了download=true,则会自动下载数据集到指定路径下的root目录
train=true表示加载训练集,train=false表示加载测试集
transform=dataset_transform 将之前定义的数据集变换(dataset_transform)应用到加载的数据集上
如果download=true,则会在指定路径下创建一个文件夹,并将数据集下载到该文件夹中,一般将download设置为true不会出错

import torchvision
from torch.utils.tensorboard import SummaryWriter

# 定义数据集的转换
# torchvision.transforms.Compose创建一个图像变换的组合,这里仅包含一个变换ToTensor()
dataset_transform=torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
])

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


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

DataLoader

Dataloader用于包装数据集,并提供批处理、数据的并行加载等功能

Search — PyTorch 2.3 documentation

test_loader=DataLoader(dataset=test_data,batch_size=64,shuffle=False,

                                                                                          num_workers=0,drop_last=False)
dataset=test_data指定要加载的数据集
batch_size=64每个批次的样本数为64
shuffle=false表示不对数据进行随机打乱
num_workers=0表示在数据加载过程中使用的子进程数量为0,即在主进程中加载数据。
drop_last=false 表示即使最后一个批次不足batch_size也不会被丢

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

# 准备的测试数据集
test_data=torchvision.datasets.CIFAR10("./temp_dataset",train=False,transform=torchvision.transforms.ToTensor())
# 创建数据加载器
test_loader=DataLoader(dataset=test_data,batch_size=64,shuffle=False,num_workers=0,drop_last=False)
# 使用SummaryWriter记录数据
writer=SummaryWriter("dataloader")
for epoch in range(2):
    step=0
    # 遍历测试数据加载器test_loader中每个批次的数据
    for data in test_loader:
        # 获取批次中的图像数据imgs和对应的标签targets
        imgs,targets =data
        writer.add_images("Epoch:{}".format(epoch),imgs,step)
        step=step+1
writer.close()

神经网络

神经网络的基本骨架nn.Module模块使用

nn.Module是对所有神经网络提供一个基本的类。

forward函数,不需手动调用,只要在实例化一个对象时传入对应的参数即可自动调用

import torch
from torch import nn
class Sky(nn.Module):
    def __init__(self):
        super().__init__()

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

sky=Sky()
x=torch.tensor(1.0)
output=sky(x)
print(output)

卷积原理

torch.nn.functional.conv2d — PyTorch 2.3 documentation

 torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1) → Tensor

各参数的含义:

input:输入的张量,即待进行卷积操作的数据,通常时一个四维张量,形状为([N,C,H,W]),其中N时批量大小,C是输入通道数,H和W是输入数据的高度和宽度。

weight:卷积核的权重张量,是一个四维张量,形状为([N,C/groups,kernel[0],kernel[1]]),其中C是输出通道数,即卷积操作产生的通道数,kernel_size是卷积核的大小。

bias(可选):可选的偏置张量,如果设置了偏置,将会为每个输出通道添加一个偏置值。

stride:卷积操作的步长,默认为1,表示卷积核每次移动的像素数,可以设置为大于1的整数,以加快卷积操作。

padding:输出的每一条边补充0的层数,用于控制输出的大小。默认为0,表示不进行补充。

dilation:卷积核元素之间的间距,默认为1。

groups:输入和输出之间连接的组数,默认为1。

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)

output=F.conv2d(input,kernel,stride=1)
print(output)
# padding 上下左右填充一行/列,为0
# stride 一次移动几格
output1=F.conv2d(input,kernel,stride=1,padding=1)
print(output1)

神经网络---卷积层

 Conv2d — PyTorch 2.3 documentation

torch.nn.Conv2d(in_channelsout_channelskernel_sizestride=1padding=0dilation=1,

                         groups=1bias=Truepadding_mode='zeros'device=Nonedtype=None)

各参数的含义:

in_channels:输入的通道数,即输入数据的深度,对于RGB彩色图像,通常为3;对于灰度图像,通常为1

out_channels:卷积产生的通道数(滤波器的数量),也即卷积核的数量,每个卷积核会产生一个输出通道。

kernel_size:卷积核的大小,可以是一个整数,表示方形卷积核的边长,也可以是一个元组,如(3,5),表示非方形的卷积核。

stride:卷积核移动的步长,默认为1,表示每次移动一个像素;可以设定为大于1的整数,以加快卷积操作。

padding:输入的每一条边补充0的层数,控制卷积操作输出的大小,使其与输入大小相同,通常设置为(kernel_size-1)/2,当stride为1时,输出大小等于输入大小。

dilation:卷积核元素之间的间距,默认为1,间距为1,可以用于控制卷积核之间的元素间距。

groups:输入核输出之间连接的组数,默认为 1,在分组卷积中使用,将输入和输出分成多个组,每个组执行卷积操作。

bias:如果设置为True(默认),则会给每个输出通道添加一个偏置值。

padding_mode:padding的模式选择,默认为‘zeros’,表示用0填充。还可以选择“reflect”(反射填充)和“replicate”(复制填充)等方式。

device和dtype:device指定张量所在的设备(例如CPU和GPU)。dtype指定张量的数据类型(例如torch.float、torch.double等)。

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)

output=F.conv2d(input,kernel,stride=1)
print(output)
# padding 上下左右填充一行/列,为0
# stride 一次移动几格
output1=F.conv2d(input,kernel,stride=1,padding=1)
print(output1)

小tips:

torch.nn.functional.conv2dtorch.nn.Conv2d 是PyTorch中进行二维卷积操作的两种方式,它们有以下区别和联系:

  1. 区别

    • 类 vs 函数

      • torch.nn.Conv2d 是一个类(类似于一个层),用于定义一个卷积层,它包含了卷积操作中的权重(卷积核)和可选的偏置参数,以及其他设置。
      • torch.nn.functional.conv2d 是一个函数,它实现了卷积的具体计算过程,但是它不包含权重参数,需要手动传入权重和偏置参数。
    • 参数传递

      • torch.nn.Conv2d 的参数中,除了需要指定输入通道数、输出通道数、卷积核大小等,还包括了设备、数据类型等参数。
      • torch.nn.functional.conv2d 的参数中,只需要传入输入张量、权重张量,可以选择性地传入偏置、步长、填充等参数。
    • 用途

      • torch.nn.Conv2d 通常用于定义和构建整个卷积神经网络的层结构,可以通过实例化来创建卷积层,并且可以方便地管理和调整参数。
      • torch.nn.functional.conv2d 则更适合于在已有模型中直接使用,或者在需要自定义卷积操作时使用,例如在自定义的前向传播函数中调用该函数进行卷积操作。
  2. 联系

    • 功能等效

      • 从功能上来讲,两者都是实现二维卷积操作。torch.nn.Conv2d 内部也会调用 torch.nn.functional.conv2d 来完成卷积运算。
    • 权重共享

      • 在训练过程中,二者都可以共享同一组卷积核(权重),因此它们的卷积操作结果是一致的,只是在使用方式和设计思路上有所不同。

总结来说,torch.nn.Conv2d 是一个类,用于定义卷积层结构和参数,并包含了权重管理的功能;而 torch.nn.functional.conv2d 则是一个函数,用于执行卷积运算,需要手动传入权重参数,适合于简单的卷积操作或者在自定义模型时的使用。

 神经网络---池化层

最大池化的使用

MaxPool2d — PyTorch 2.3 documentation

 torch.nn.MaxPool2d(kernel_sizestride=Nonepadding=0dilation=1

                                                                        return_indices=Falseceil_mode=False)

用于在二维数据上执行最大池化操作,即从输入张量中提取每个池化窗口的最大值,并生成相应的输出张量。

各参数的含义:

kernel_size:池化窗口的大小,可以是一个整数,表示正方形窗口的边长;也可以是一个元组,如(2,3),表示非正方形的窗口。

stride(可选):池化操作的步长,默认为kernel_size,即默认情况下每次移动池化窗口的大小进行池化操作。可以设置为大于1的整数,以加快池化操作。

padding:输入的每一条边补充0的层数,用于控制池化后输出的大小,默认为0,表示不进行补充。通常设置为(kernel_size-1)/2,使得池化操作后输出大小等于输入大小。

dilation:控制池化窗口中元素之间的间距。默认为1,表示元素之间的间距为1。

return_indices:如果设置为True(默认值为False),则返回输出最大值的索引张量,用于后续的torch.nn.MaxUnpool2d操作。

ceil_mode:如果设置为True(默认值为False),则使用ceil函数计算输出大小,否则使用floor函数。

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

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

learn=Learn()
output=learn(input)

 

神经网络---非线性激活

以ReLU为例ReLU — PyTorch 2.3 documentation

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 Learn(nn.Module):
    def __init__(self):
        super(Learn,self).__init__()
        self.relu1=ReLU()

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

learn=Learn()
output=learn(input)
print(output)

 

 考虑到ReLU对图像的改变不是很大,下面以Sigmoid为例对图像进行改变

Sigmoid — PyTorch 2.3 documentation

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

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

dataloader=DataLoader(dataset,batch_size=64)

class Learn(nn.Module):
    def __init__(self):
        super(Learn,self).__init__()
        self.relu1=ReLU()
        self.sigmoid1=Sigmoid()

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

learn=Learn()

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

writer.close()

 

 神经网络---线形层

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

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

dataloader=DataLoader(dataset,batch_size=64)

class Learn(nn.Module):
    def __init__(self):
        super(Learn,self).__init__()
        self.linear1=Linear(196608,10)
    def forward(self,input):
        output=self.linear1(input)
        return output

learn=Learn()

for data in dataloader:
    imgs,targets=data
    print(imgs.shape)
    output=torch.flatten(imgs)
    print(output.shape)
    output=learn(output)
    print(output.shape)

小tips:

output = torch.reshape(imgs, (1, 1, 1, -1)) # 方法一:拉平
output = torch.flatten(imgs) # 方法二:拉平。展开为一维
两者都可以实现将张量拉平

神经网络---Sequential的使用以及搭建小实战

Sequential — PyTorch 2.3 documentation

神经网络中的 Sequential 是一种容器,用于按顺序排列神经网络模块,这些模块可以是层(layers)或者其他容器(例如 Sequential 自身)。它的作用是方便地定义神经网络的前向传播过程,使得网络的结构更加清晰和易于管理。具体来说,Sequential 可以接受一系列的神经网络层或者模块作为参数,在调用时会按照传入的顺序依次执行这些层或模块。这样做的好处是可以简化神经网络的构建过程,特别是在连续堆叠多个层的情况下,可以通过简单地将这些层按顺序传入 Sequential 来定义整个模型。

 接下来以The CIFAR-10 dataset为例演示Sequential的使用

CIFAR-10(Canadian Institute for Advanced Research)是一个广泛使用的计算机视觉数据集,包含10个类别的彩色图片。这些类别包括飞机、汽车、鸟类、猫、鹿、狗、青蛙、马、船和卡车。每个类别有大约6000张大小为32x32的彩色图片。这个数据集通常用于训练和测试机器学习算法在图像识别任务中的性能。CIFAR-10是机器学习领域中常见的基准数据集之一,用来评估不同模型在小尺寸图像分类问题上的表现。

下面是CIFAR-10的一个模型

1、第一次卷积时:inputs :3@32x32   卷积核:5x5 =>outputs  :32@32x32 

self.conv1 = Conv2d(3, 32, 5, padding=2)

 注意:空洞卷积时,dilation默认为1

2、第一次最大池化:inputs :32@32x32   池化核:2x2 =>outputs :32@16x16

 self.maxpool1 = MaxPool2d(2)

3、第二次卷积: inputs :32@16x16 卷积核:5x5 =>  outputs  :32@16x16

这里肯定padding值不为默认值0,容易得出padding值为2。

self.conv2 = Conv2d(32, 32, 5, padding=2)

4、第二次最大池化: inputs :32@16x16   池化核:2x2 =>outputs :32@8x8

self.maxpool2 = MaxPool2d(2)

5、第三次卷积: inputs :32@8x8   卷积核:2x2 =>outputs :64@8x8

 self.conv3 = Conv2d(32, 64, 5, padding=2)

6、第三次最大池化: inputs :64@8x8   卷积核:2x2 =>outputs :64@4x4

self.maxpool3 = MaxPool2d(2)

7、展平数据:self.flatten = Flatten()

outputs:1024(1024=64*4*4)

8、线形层inputs:1024 => outputs:64
      self.linear1 = Linear(1024, 64)
      self.Linear2 = Linear(64, 10)

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


class Learn(nn.Module):
    def __init__(self):
        super(Learn, 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)

    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 = Learn()
print(tudui)
input = torch.ones((64, 3, 32, 32))
output = tudui(input)
print(output.shape)

输出结果 

 使用Sequential后的神经网络

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


class Learn(nn.Module):
    def __init__(self):
        super(Learn, 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


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

writer=SummaryWriter("logs_seq")
writer.add_graph(learn,input)
writer.close()

在tensorboard上的显示: 

 

损失函数与反向传播

loss:一方面计算实际输出和目标之间的差距;另一方面为我们更新输出提供一定的依据(反向传播)。loss肯定是越低越好。

损失函数---L1Loss

L1Loss — PyTorch 2.3 documentation

torch.nn.L1Loss(size_average=Nonereduce=Nonereduction='mean')

 需要重点关注的是输入Input的大小

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)

输出结果是0.6667

小tips:

可以通过设置reduction的值来选择计算损失的方式

求和:loss=L1Loss(reduction="sum")

默认为mean,求和取平均

损失函数---MSELoss 

MSELoss — PyTorch 2.3 documentation

 

import torch
from torch import nn
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_mse = nn.MSELoss()
result_mse = loss_mse(inputs,targets)

print(result_mse)

输出结果1.3333

损失函数---CrossEntropyLoss交叉熵

CrossEntropyLoss — PyTorch 2.3 documentation

适合分类问题

import torch
from torch.nn import L1Loss
from torch import nn

x = torch.tensor([0.1,0.2,0.3])
y = torch.tensor([1])
x = torch.reshape(x,(1,3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x,y)
print(result_cross)

反向传播

反向传播是通过损失函数计算模型中每个参数对损失的梯度,以便更新参数来最小化损失。它利用链式法则(chain rule)从输出层向输入层逐层计算梯度。

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() 
    print("ok")
  

优化器

torch.optim — PyTorch 2.3 documentation

优化器在深度学习中是用来优化模型参数的工具,主要作用是通过调整参数来最小化或最大化损失函数。下面是优化器的基本使用方法:

  1. 选择优化器: 根据你的需求选择合适的优化器,常见的优化器包括随机梯度下降(SGD)、Adam、RMSprop等。选择优化器时可以考虑学习率的调整策略、动量等因素。

  2. 定义优化器: 在训练模型之前,需要创建一个优化器的实例,将模型的参数传递给优化器。

  3. 梯度清零: 每次计算完一个batch的损失后,调用优化器的 zero_grad() 方法清除模型参数的梯度。这是因为PyTorch默认会累积梯度,而我们需要在每个batch后清空梯度,以免梯度混乱。

  4. 反向传播: 调用损失函数的 backward() 方法进行反向传播,计算梯度。

  5. 更新参数: 调用优化器的 step() 方法来更新模型的参数,即根据计算得到的梯度更新模型权重。

  6. 学习率调整: 可以选择性地使用学习率调度器来动态调整学习率,比如按照固定的步长调整或者根据验证集表现调整。


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()
# 定义随机梯度下降(SGD)优化器optim,用于优化模型参数。学习率lr设置为0.01
optim = torch.optim.SGD(tudui.parameters(), lr=0.01)
# 创建一个学习率调整器scheduler,步进式学习率调整器
# step_size=5表示每5个epoch学习率会按照gamma=0.1的比例进行调整
# 即每5个epoch学习率乘以0.1
scheduler = torch.optim.lr_scheduler.StepLR(optim, step_size=5, gamma=0.1)

# 训练20个epoch
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()
        # 累加每个批次的损失值,最后打印出来
        running_loss = running_loss + result_loss
    print(running_loss)

现有模型的使用和修改

下面以使用vgg16模型为例:

VGG16是一种经典的卷积神经网络模型,由牛津大学视觉几何组(Visual Geometry Group,简称VGG)在2014年提出。它的名字来源于论文中的作者组合“VGG”和网络的深度参数16(包括卷积和全连接层)。VGG16模型以其简洁明了的结构和强大的表征能力而闻名,适用于图像分类和特征提取结构。

概述任务

VGG16模型的网络结构非常规整,由13个卷积层和3个全连接层组成。具体来说:

  1. 卷积层部分

    • 第1~2层:两个3x3的卷积层,每层后接一个ReLU激活函数和一个2x2的最大池化层,用于减小空间尺寸。

    • 第3~4层:两个3x3的卷积层,每层后接一个ReLU激活函数和一个2x2的最大池化层。

    • 第5~7层:三个3x3的卷积层,每层后接一个ReLU激活函数。

    • 第8~10层:三个3x3的卷积层,每层后接一个ReLU激活函数。

    • 第11~13层:三个3x3的卷积层,每层后接一个ReLU激活函数,最后是一个2x2的最大池化层。

  2. 全连接层部分

    • 第14层:包含4096个神经元的全连接层,接ReLU激活函数。

    • 第15层:包含4096个神经元的全连接层,接ReLU激活函数。

    • 第16层:输出层,包含1000个神经元,对应于ImageNet数据集的1000个类别,使用softmax激活函数输出分类概率。

主要特点

  • 深度和简洁性:VGG16相比于之前的网络模型,如AlexNet,增加了层数,提升了特征提取的能力,同时保持了简单易懂的结构。

  • 小卷积核的使用:VGG16大量使用3x3的卷积核和2x2的池化核,这种设计有助于增加网络的深度,减少参数数量,同时保持感受野的大小。

  • 预训练模型和迁移学习:由于VGG16在大规模数据集(如ImageNet)上进行了预训练,因此可以作为其他视觉任务的基础模型,进行迁移学习。

应用领域

VGG16被广泛应用于图像分类、物体检测、图像分割等视觉任务中。由于其较高的表征能力和普适性,它成为了深度学习在计算机视觉领域的重要里程碑之一。

总之,VGG16以其简单而有效的设计,成为了深度学习研究中一个重要的基准模型,对后续的网络设计和应用产生了深远的影响。

import torchvision
from torch import nn


vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)

# 打印出预训练好的VGG16模型的结构
print(vgg16_true)

train_data = torchvision.datasets.CIFAR10('data_model', train=True, transform=torchvision.transforms.ToTensor(), download=True)

# 在预训练好的VGG16模型的分类器部分(即最后的全连接层)中添加一个新的线性层,将原来输出为1000类的预测结果转换为10类(CIFAR10数据集的类别数)
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))

# 打印修改后的预训练好的VGG16模型,可以看到分类器部分多了一个新的线性层
print(vgg16_true)

# 在未预训练的VGG16模型的分类器部分中,直接将最后一个全连接层(索引为6)替换为一个新的线性层,输出维度从4096(原始输出维度)到10(CIFAR10的类别数)
vgg16_false.classifier[6] = nn.Linear(4096, 10)

# 打印修改后的未预训练的VGG16模型,可以看到分类器部分的最后一个全连接层被替换为新的线性层
print(vgg16_false)

关于pretrained属性的含义:

  • pretrained=True表示使用预训练模型。在这种情况下,torchvision.models.vgg16会加载在ImageNet数据集上预先训练好的权重。这些权重包含了从大量图像数据学习到的特征,通常在处理类似的视觉任务时能提供更好的性能和泛化能力。

  • pretrained=False表示不使用预训练模型,而是随机初始化模型的权重。在这种情况下,模型的权重会从头开始训练,需要根据具体任务的数据进行学习。

模型的保存与读取

 注意:方式1使用的 时候会有陷阱,如果是自己的网络模型进行定义的话,需要保证model_load.py能够访问到模型定义的结构。

完整的模型训练套路

train.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :train.py
# @Time      :2024/7/13 23:30
# @Author    :your name
import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from model import *


# 准备数据集
train_data=torchvision.datasets.CIFAR10(root="last_data",train=True,transform=torchvision.transforms.ToTensor(),
                                        download=True)
test_data=torchvision.datasets.CIFAR10(root="last_data",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)

# 创建神经网络
learn=Learn()

# 损失函数
loss_fn=nn.CrossEntropyLoss()

# 优化器
# 1e-2=0.01
learning_rate=1e-2
optimizer=torch.optim.SGD(learn.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("----------第{}轮训练开始------------".format(i+1))

    # 训练步骤开始
    learn.train()      
    for data in train_dataloader:
        imgs,targets=data
        outputs=learn(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()))
            writer.add_scalar("train_loss",loss.item(),total_train_step)


    # 测试步骤开始
    learn.eval()
    total_test_loss=0
    total_accuracy=0
    with torch.no_grad():
        for data in test_dataloader:
            imgs,targets=data
            outputs=learn(imgs)
            loss=loss_fn(outputs,targets)
            total_test_loss=total_test_loss+loss.item()
            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(learn,"learn_{}.pth".format(i))
    print("模型已保存")

writer.close()

model.py 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :model.py
# @Time      :2024/7/13 23:36
# @Author    :your name
import torch
from torch import nn


# 搭建神经网络
class Learn(nn.Module):
    def __init__(self):
        super(Learn,self).__init__()
        self.module=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.module(x)
        return x

if __name__ == '__main__':
    learn=Learn()
    input=torch.ones((64,3,32,32))
    output=learn(input)
    print(output.shape)

 

利用GPU训练

特点:比较快

方法一:

只需要改变网络模型,数据和损失函数使用.cuda()

1、网络模型
learn=Learn()
if torch.cuda.is_available():
    learn=learn.cuda()
2、损失函数
loss_fn=nn.CrossEntropyLoss()
if torch.cuda.is_available():
    loss_fn=loss_fn.cuda()
3、数据(训练集和测试集上的都需要)
for data in train_dataloader:
    imgs,targets=data
    if torch.cuda.is_available():
        imgs=imgs.cuda()
        targets=targets.cuda()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :train_GPU.py
# @Time      :2024/7/14 0:33
# @Author    :your name

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



# 准备数据集
train_data=torchvision.datasets.CIFAR10(root="last_data",train=True,transform=torchvision.transforms.ToTensor(),
                                        download=True)
test_data=torchvision.datasets.CIFAR10(root="last_data",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)

# 创建神经网络
class Learn(nn.Module):
    def __init__(self):
        super(Learn,self).__init__()
        self.module=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.module(x)
        return x
learn=Learn()
if torch.cuda.is_available():
    learn=learn.cuda()
# 损失函数
loss_fn=nn.CrossEntropyLoss()
if torch.cuda.is_available():
    loss_fn=loss_fn.cuda()
# 优化器
# 1e-2=0.01
learning_rate=1e-2
optimizer=torch.optim.SGD(learn.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("----------第{}轮训练开始------------".format(i+1))

    # 训练步骤开始
    learn.train()
    for data in train_dataloader:
        imgs,targets=data
        if torch.cuda.is_available():
            imgs=imgs.cuda()
            targets=targets.cuda()
        outputs=learn(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()))
            writer.add_scalar("train_loss",loss.item(),total_train_step)


    # 测试步骤开始
    learn.eval()
    total_test_loss=0
    total_accuracy=0
    with torch.no_grad():
        for data in test_dataloader:
            imgs,targets=data
            if torch.cuda.is_available():
                imgs = imgs.cuda()
                targets = targets.cuda()
            outputs=learn(imgs)
            loss=loss_fn(outputs,targets)
            total_test_loss=total_test_loss+loss.item()
            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(learn,"learn_{}.pth".format(i))
    print("模型已保存")

writer.close()

方法二 :

只需要设置训练的设备,然后改变网络模型,数据和损失函数使用.cuda()

定义训练的设备 
device=torch.device("cuda")

1、网络模型
learn=Learn()
learn=learn.to(device)
2、损失函数
loss_fn=nn.CrossEntropyLoss()
loss_fn=loss_fn.to(device)
3、数据
for data in test_dataloader:
    imgs,targets=data
    imgs = imgs.to(device)
    targets = targets.to(device)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :train_GPU02.py
# @Time      :2024/7/14 0:48
# @Author    :your name


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


# 定义训练的设备
device=torch.device("cuda")

# 准备数据集
train_data=torchvision.datasets.CIFAR10(root="last_data",train=True,transform=torchvision.transforms.ToTensor(),
                                        download=True)
test_data=torchvision.datasets.CIFAR10(root="last_data",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)

# 创建神经网络
class Learn(nn.Module):
    def __init__(self):
        super(Learn,self).__init__()
        self.module=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.module(x)
        return x
learn=Learn()
learn=learn.to(device)
# 损失函数
loss_fn=nn.CrossEntropyLoss()
loss_fn=loss_fn.to(device)
# 优化器
# 1e-2=0.01
learning_rate=1e-2
optimizer=torch.optim.SGD(learn.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("----------第{}轮训练开始------------".format(i+1))

    # 训练步骤开始
    learn.train()
    for data in train_dataloader:
        imgs,targets=data
        imgs=imgs.to(device)
        targets=targets.to(device)
        outputs=learn(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()))
            writer.add_scalar("train_loss",loss.item(),total_train_step)


    # 测试步骤开始
    learn.eval()
    total_test_loss=0
    total_accuracy=0
    with torch.no_grad():
        for data in test_dataloader:
            imgs,targets=data
            imgs = imgs.to(device)
            targets = targets.to(device)
            outputs=learn(imgs)
            loss=loss_fn(outputs,targets)
            total_test_loss=total_test_loss+loss.item()
            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(learn,"learn_{}.pth".format(i))
    print("模型已保存")

writer.close()

 完整的模型验证(测试、demo)套路

利用已经训练好的模型,然后给它提供输入

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :train_yanzheng.py
# @Time      :2024/7/14 1:04
# @Author    :your name
import torch
import torchvision.transforms
from PIL import Image
from torch import nn

image_path="E:\code\learn_pytorch\dog.png"
image=Image.open(image_path)
print(image)
# png格式是四个通道,除了RGB三通道外,还有一个透明通道。
image=image.convert('RGB')

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

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

class Learn(nn.Module):
    def __init__(self):
        super(Learn,self).__init__()
        self.module=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.module(x)
        return x

model=torch.load("learn_0.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))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值