首页 > 其他分享 >PyTorch笔记

PyTorch笔记

时间:2023-05-17 12:45:09浏览次数:60  
标签:torchvision nn self torch 笔记 PyTorch import data

前言

简介: PyTorch是一个基于Python的科学计算库,它主要提供了两个高级功能:一是支持张量计算,类似于NumPy,但是可以在GPU上运行;二是支持构建和训练深度神经网络。

在PyTorch中,张量(Tensor)是最基本的数据结构,类似于NumPy中的多维数组,但是可以在GPU上进行高效的计算。PyTorch支持多种张量操作,包括张量加法、减法、乘法、除法、矩阵乘法等,同时还支持各种元素级函数和聚合函数。

PyTorch还提供了一种灵活的、动态的计算图机制,称为Autograd。在PyTorch中,每个张量都可以被看作是计算图中的一个节点,每个节点都有一个对应的梯度节点,用于计算该节点对应的变量的梯度。当我们对计算图中的某个节点进行操作时,PyTorch会自动构建该节点的梯度计算图,并通过反向传播算法计算出该节点对应的变量的梯度。

PyTorch还提供了一种方便的、高层次的API,用于构建和训练深度神经网络。这个API称为torch.nn模块,它提供了各种层和损失函数的实现,可以方便地构建各种类型的深度神经网络,例如卷积神经网络、循环神经网络、自编码器等。同时,PyTorch还提供了torch.optim模块,用于实现各种优化算法,例如SGD、Adam等,以便训练深度神经网络。

总之,PyTorch是一个功能强大、易于使用的深度学习框架,适用于各种类型的深度学习任务,包括图像处理、自然语言处理、语音识别等。同时,PyTorch还具有优秀的可扩展性和灵活性,可以方便地与其他Python库和工具集成使用。

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

1 Dataset类

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.label_dir = label_dir
        # 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)


train_root_dir = "data/images/dataset/train"
ant_label_dir = "ants_image"
bee_label_dir = "bees_image"
ant_dataset = MyData(train_root_dir, ant_label_dir)
bee_dataset = MyData(train_root_dir, bee_label_dir)

# __getitem__() 调用方法
ant_img, ant_label = ant_dataset[0]
print(ant_dataset[0])

train_dataset = ant_dataset + bee_dataset  # 按加的顺序组合
train_img, train_label = train_dataset[124]
print(len(ant_dataset), len(bee_dataset), sep="\n")  # 124 121
train_img.show()

2 TensorBoard

  • TensorBoard是一个用于可视化TensorFlow模型和训练结果的工具。它可以帮助你更好地理解和调试你的模型,比如可视化模型的结构、训练和评估指标、数据分布、嵌入向量、图像和视频等。通过使用TensorBoard,你可以更直观地了解模型的表现,找到模型中的问题并进行优化。同时,它也可以用来比较不同模型的性能,跟踪训练过程中的进展,并与他人分享你的研究成果。TensorBoard是一个非常有用的工具,可以帮助你更高效地进行深度学习研究和开发。
from torch.utils.tensorboard import SummaryWriter


writer = SummaryWriter("logs")

# writer.add_image()

for i in range(100):
    # tag:标题;scalar_value:y轴;global_step:x轴
    writer.add_scalar(tag="y = x", scalar_value=i * i, global_step=i)

writer.close()

运行上面代码之后,在下方终端输入:

tensorboard --logdir=logs

之后打开网页:

 http://localhost:6006/ 

端口也可以自己改(6007可以随便改):

tensorboard --logdir=logs --port=6007

3 Transforms

  • 在PyTorch中,Transforms是一种数据预处理的方式。它可以用来对输入数据进行各种变换,例如调整大小、裁剪、旋转、翻转、标准化等等。这些变换可以帮助我们增加训练数据的多样性,减少过拟合,提高模型的泛化能力。
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms


writer = SummaryWriter("logs")
img = Image.open("data/images/SSSS.Dynazenon.jpg")

'''
toTensor:
Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor
PIL Image 和 numpy.ndarray 分别对应PIL和opencv的图片读取
'''
trans_toTensor = transforms.ToTensor()
img_tensor = trans_toTensor(img)
writer.add_image("ToTensor", img_tensor)

'''
normalize:归一化
计算方法:output[channel] = (input[channel] - mean[channel]) / std[channel]
防止数据集中有过大或者过小的值,影响训练
'''
trans_norm = transforms.Normalize([5, 0.5, 0.5], [0.5, 0.5, 0.5])  # 三个通道的均值和标准差
img_norm = trans_norm(img_tensor)
writer.add_image("norm img", img_norm)

'''
resize:
Resize the input image to the given size.
resize不会改变图片的数据类型
'''
trans_resize = transforms.Resize((512, 512))
img_resize = trans_resize(img_tensor)
writer.add_image("resize img", img_resize)

'''
compose:
compose就是一个指定一个transform操作序列,定义了一条加工流水线
'''
trans_resize_2 = transforms.Resize(512)  # 将短边缩到 512
# 先 toTensor 再 resize,前一个的输出对应后一个输入
trans_compose = transforms.Compose([trans_toTensor, trans_resize_2])
img_resize_2 = trans_compose(img)
writer.add_image("resize_2 img", img_resize_2)

'''
random crop:
随机剪一个指定尺寸的新图片
'''
trans_random = transforms.RandomCrop(500)  # 指定裁剪尺寸
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()

4 运用Datasets以及Transform

import torchvision
from torch.utils.tensorboard import SummaryWriter


# transforms操作序列
dataset_transform = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
])

# 训练集测试集初始化
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=dataset_transform, download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, transform=dataset_transform, download=True)

# 将测试集前 10 张在 TensorBoard 显示
writer = SummaryWriter("logs2")
for i in range(10):
    img, target = test_set[i]
    writer.add_image("test_set", img, i)

writer.close()

5 DataLoader

  • 在PyTorch中,DataLoader是一个用于加载和处理数据的工具。它可以帮助我们对数据进行批量读取、异步加载、并行处理等操作,从而提高训练的效率。

  • 通常情况下,我们需要将数据集转化为一个Dataset对象,并通过DataLoader将其加载到模型中进行训练。Dataset对象可以是PyTorch内置的数据集,也可以是用户自定义的数据集。每个数据集可以包含多个样本,每个样本包含一个输入和一个标签。在DataLoader中,我们可以指定批量大小、数据加载顺序、多线程等参数,从而对数据进行处理和加载。

  • 在训练时,我们可以通过迭代DataLoader对象来获取数据。每个迭代返回一个批量的数据,包含了输入和对应的标签。这样,我们就可以在模型上进行批量的训练和推理操作,从而提高训练的效率和准确率。

  • 总之,DataLoader是PyTorch中一个非常重要的工具,可以帮助我们更加高效地加载和处理数据,从而使得模型训练更加简单和快速。

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

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

'''
batch size:loader能每次装弹4枚进入枪膛,或者理解每次抓四张牌
shuffle:每次epoch是否打乱原来的顺序,就像打完一轮牌后,洗不洗牌
num_workers:进程数
drop last:最后的打他不够一个 batch 还要不要了
'''
test_loader = DataLoader(dataset=test, batch_size=4, shuffle=True, num_workers=0, drop_last=False)

# 使用 board 可视化
writer = SummaryWriter("logs_dataloader")
step = 0
for data in test_loader:
    imgs, targets = data

    # 这里如果你用add image会报错,因为这个方法只能一次写一个图片,你必须换add images方法来写入带有批处理的图片
    # writer.add_image("test set loader", imgs, step)
    writer.add_images("test set loader", imgs, step)
    step += 1

writer.close()

6 nn_module

  • nn 是 Neural Network(神经网络)的缩写
import torch
import torch.nn as nn


# 神经网络类的构造
class Module(nn.Module):
    def __init__(self):
        super().__init__()

    # nn.Module 中的 call 方法会调用 forward
    def forward(self, input):
        output = input + 1
        return output


mdl = Module()
x = torch.tensor(1.0)
output = mdl(x)
print(output)

7 nn_conv(卷积)

  • conv 是 Convolution(卷积)的缩写

  • 下面是一个简单的示意图,展示了一个 $3\times 3$ 的输入数据和一个 $2\times 2$ 的卷积核进行卷积操作的过程。

    输入数据:      卷积核:
    1 2 3           1 0
    4 5 6           0 1
    7 8 9      
    
    卷积操作后的输出:
    6  8
    12 14
    

    我们将卷积核从输入数据的左上角开始扫描,并逐步向右下方移动。对于每一个位置,我们将卷积核对应的元素与输入数据中对应位置的元素相乘,并将所有乘积相加,得到一个标量值作为输出数据 $y$ 中对应位置的值。

    例如,在扫描到输入数据的左上角时,卷积核与输入数据中对应位置的值进行乘法运算,得到的结果为:

    1*1 + 0*2 + 0*4 + 1*5 = 6
    

    具体的卷积操作可以用以下公式表示:
    $$
    y_{i,j}=\sum_{m=1}{M}\sum_{n=1}w_{m,n}x_{i+m-1,j+n-1}
    $$
    其中 $x$ 是输入数据,$w$ 是卷积核,$y$ 是输出数据,$M$ 和 $N$ 是卷积核的大小,$i$ 和 $j$ 是输出数据中的位置坐标。

  • 卷积运算示意图: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]])

# conv2d需要输入的tensor是四维的(batch, c,h,w),但是现在的input kernel是二维
# (要变换的矩阵, (批次大小(batch size), 通道数(channel), 矩阵高度, 矩阵宽度))
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))

# stride:步长,padding:如果步长是 1,又想保持输入输出高宽不变,就把 padding 设置 1
output1 = F.conv2d(input, kernel, stride=1)
print(output1)

# padding=1 会在输入矩阵外圈填充一圈 0,在卷积时,如果 stride=1,输入输出尺寸会不变
output2 = F.conv2d(input, kernel, stride=1, padding=1)
print(output2)

8 nn_conv2d(卷积)

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


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


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

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


mdl = Module()
print(mdl)

writer = SummaryWriter("logs_nn_conv2d")

step = 0
for data in dataloader:
    img, target = data
    output = mdl(img)
    # output 的 size 是 torch.Size([64, 6, 30, 30])
    writer.add_images("before conv2d", img, step)
    # output 是 6 通道,board不知道该怎么写入图片了,所以要 reshape
    output = torch.reshape(output, (-1, 3, 30, 30))  # batch size不知道可以填 -1
    writer.add_images("after conv2d", output, step)

    step += 1

writer.close()

9 nn_maxpool(最大池化)

  • 池化(Pooling)是神经网络中另一种常用的操作,通常紧随卷积层之后,用于减小特征图的尺寸,提高模型的计算效率和泛化能力。

  • 最大池化(Max Pooling)是一种常用的池化操作,其原理是在输入数据的每个局部区域中选取最大的值作为输出。下面我来详细解释一下最大池化运算的过程,并举一个简单的例子来说明。

    假设我们有一个 $4\times 4$ 的输入数据 $x$,如下所示:

    1 2 2 1
    2 2 1 1
    1 1 1 2
    2 1 2 1
    

    我们希望对这个输入数据进行最大池化操作,设置池化窗口的大小为 $2\times 2$,即将输入数据分割成若干个 $2\times 2$ 的局部区域。对于每个局部区域,我们选取其中的最大值作为输出。

    具体地,我们从输入数据的左上角开始,将池化窗口逐步向右下方移动。对于每个池化窗口,我们选取其中的最大值作为输出。例如,在扫描到输入数据的左上角时,池化窗口覆盖的局部区域为:

    1 2
    2 2
    

    其中最大的值为 $2$,因此将 $2$ 作为输出。接着,将池化窗口向右移动池化窗口尺寸大小,重复上述操作,得到第一行第二个位置的输出:

    2 1
    1 1
    

    其中最大的值为 $2$,因此将 $2$ 作为输出。以此类推,可以得到输入数据的所有局部区域的最大值,从而得到最终的输出数据 $y$:

    2 2
    2 2
    

    这个输出数据的尺寸是原输入数据的一半,因为我们使用了 $2\times 2$ 的池化窗口,将输入数据的尺寸减小了一半。

  • 最大池化操作可以减小特征图的尺寸,同时还可以提取输入数据的局部不变特征,增强模型对图像、语音等数据的抗干扰能力。在卷积神经网络中,通常将最大池化操作和卷积操作交替出现,构成卷积池化层组合,用于提取输入数据的高层特征。

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


# dtype:设置矩阵元素的数据类型
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.float)

input = torch.reshape(input, (-1, 1, 5, 5))

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


# ceil mode:池化核走出input时还要不要里边的最大值 默认不要
class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        self.max_pool1 = MaxPool2d(kernel_size=(3, 3), ceil_mode=True)

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


mdl = Module()
output = mdl(input)
print(output)

writer = SummaryWriter("logs_maxpool")

step = 0
for data in dataloader:
    img, target = data
    writer.add_images("input", img, step)
    output = mdl(img)
    writer.add_images("output", output, step)

    step += 1

writer.close()

10 nn_relu(非线性激活)

  • 非线性激活函数是神经网络中一种常用的激活函数,它的作用是将神经元的输入信号进行非线性变换,增强神经网络的非线性表示能力。常见的非线性激活函数包括sigmoid函数、tanh函数、ReLU函数、Leaky ReLU函数、ELU函数等。

    其中,sigmoid函数将输入映射到[0,1]的范围内,tanh函数将输入映射到[-1,1]的范围内。ReLU函数在输入为正时输出等于输入,输入为负时输出为0,Leaky ReLU函数在输入为负时输出一个小的负数,ELU函数在输入为负时输出一个接近于0的小值,比ReLU函数更平滑。

    在神经网络中,非线性激活函数的作用是引入非线性变换,使得神经网络可以学习非线性关系,从而提高模型的表达能力和性能。

  • ReLU(Rectified Linear Unit)函数是一种常用的非线性激活函数,它将输入信号直接输出作为输出信号,即:

    f(x) = max(0, x)

    其中,x是输入信号,f(x)是输出信号。

    ReLU函数的特点是在输入为正时直接输出,而在输入为负时输出为0,因此它具有计算简单、速度快的优点。此外,ReLU函数还具有一定的生物学合理性,因为在生物神经元中,当输入信号超过一定阈值时才会产生输出信号,而ReLU函数也具有这种类似的特点。

    在深度学习中,ReLU函数已经成为了一种标配的激活函数,并且在很多神经网络模型中都被广泛使用。它的应用除了可以提高模型的表达能力外,还可以缓解神经网络中的梯度消失问题,从而加速模型的训练和收敛。

  • Sigmoid函数是一种常用的非线性激活函数,它将输入信号压缩到[0,1]的范围内,具有平滑的S形曲线,定义如下:

    f(x) = 1 / (1 + exp(-x))

    其中,x是输入信号,f(x)是输出信号。

    Sigmoid函数的输出值在[0,1]之间,并且随着输入值的变化呈现出平滑的曲线变化,因此在一些需要将输出值限制在一定范围内的任务中,Sigmoid函数是一种比较常用的选择。此外,Sigmoid函数在二分类问题中也经常被用作输出层的激活函数,可以将输出值解释为概率。

    然而,Sigmoid函数也存在一些问题,其中一个主要问题是在输入值较大或较小时,函数的梯度会趋近于0,从而导致反向传播过程中的梯度消失问题。此外,Sigmoid函数的输出值不是以0为中心对称的,这可能导致一些神经元被“杀死”,即输出恒为0,从而导致模型的性能下降。

    在实际应用中,由于Sigmoid函数存在上述问题,它已经逐渐被ReLU函数等其他激活函数所取代。

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


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

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

dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, 64)


class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        # inplace=True:ReLU会返回一个output,input的值不变,可以保留原始数据,一般情况inplace=False
        self.relu1 = ReLU(inplace=False)  # 默认为 False
        self.sigmoid = Sigmoid()

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


mdl = Module()

writer = SummaryWriter("logs_relu")
step = 0

for data in dataloader:
    img, target = data
    writer.add_images("input", img, step)
    output = mdl(img)
    writer.add_images("output", img, step)

    step += 1

writer.close()

11 nn_linear(线性层)

把图像每行首尾相接,并成一行

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


dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, 64)


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

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


mdl = Module()

for data in dataloader:
    img, target = data
    print(img.shape)
    output = torch.reshape(img, (1, 1, 1, -1))
    print(output.shape)
    output = mdl(output)
    print(output.shape)

或者(常用):

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


dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, 64)

for data in dataloader:
    img, target = data
    print(img.shape)
    output = torch.flatten(img)
    print(output.shape)

12 nn_seq(Sequential)

torch.nn.Sequential 是 PyTorch 中的一个模型容器,它允许用户按照顺序组合多个神经网络层以构建神经网络模型。通过 Sequential,可以方便地将多个层组合成一个整体,并将其作为一个单独的层或模块来使用。

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

dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, 64)


class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        self.seq = Sequential(Conv2d(3, 32, kernel_size=(5, 5), padding=2),
                              MaxPool2d(kernel_size=2),
                              Conv2d(32, 32, kernel_size=(5, 5), padding=2),
                              MaxPool2d(kernel_size=2),
                              Conv2d(32, 64, kernel_size=(5, 5), padding=2),
                              MaxPool2d(kernel_size=2),
                              Flatten(),
                              Linear(1024, 64),
                              Linear(64, 10)
                              )

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


mdl = Module()
print(mdl)
# 用 1 填充矩阵;(批次大小(batch size), 通道数(channel), 矩阵高度, 矩阵宽度)
input = torch.ones((64, 3, 32, 32))
output = mdl(input)
print(output.shape)

13 nn_loss(损失函数)

  • L1 Loss和MSE Loss都是在深度学习中用于计算预测值和真实值之间差异的损失函数。
    • L1 Loss,也叫做Mean Absolute Error(MAE)损失,是预测值和真实值之差的绝对值之和的平均数。它的公式是: L1 Loss = |预测值 - 真实值|
    • L1 Loss相对于MSE Loss来说,更加注重outliers。当存在很多离群点(outliers)或数据分布不均匀的情况,使用L1 Loss可能会更加合适。
  • MSE Loss,也称为Mean Squared Error(MSE)的损失,计算预测值和真实值之间差的平方的平均数。它的公式是: MSE Loss = (预测值 - 真实值)²,MSE损失是最常用的损失函数之一。当预测值和真实值之间的差异很小,使用MSE Loss可以帮助模型更快地收敛。
  • CrossEntropyLoss是一种常用于机器学习和深度学习分类任务的损失函数。它通常用于神经网络中,例如图像分类、语音识别和自然语言处理等任务。CrossEntropyLoss测量预测概率分布与实际概率分布之间的差异。它会为错误的预测分配更高的损失,为正确的预测分配较低的损失。目标是最小化损失,使得预测的概率尽可能与实际概率匹配。CrossEntropyLoss的公式如下: L = -1/N * sum(ylog(p) + (1-y)log(1-p)) 其中,y是真实标签(0或1),p是预测概率,N是样本数,log是自然对数。
import torch
from torch.nn import L1Loss, MSELoss, CrossEntropyLoss

inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
outputs = torch.tensor([1, 2, 5], dtype=torch.float32)

inputs = torch.reshape(inputs, (1, 1, 1, 3))
outputs = torch.reshape(outputs, (1, 1, 1, 3))

loss = L1Loss(reduction="sum")
result = loss(inputs, outputs)

loss_mse = MSELoss()
result_mse = loss_mse(inputs, outputs)

print(result)
print(result_mse)

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

14 nn_optim(优化器)

  • SGD是常用的优化算法之一,用于训练神经网络。在SGD中,每次迭代时,我们从数据集中随机选择一个小批量的样本,计算这些样本的损失函数,并更新网络参数以尽量减小损失。更新的方向是梯度的相反方向,更新的大小由学习率控制,通常采用动态调整学习率的方法来提高性能。
  • 具体来说,SGD的更新公式为: $$ w_{t+1} = w_t - \alpha\nabla L(w_t;x_t,y_t) $$ 其中,$w$是待更新的参数向量,$\alpha$是学习率,$L$是损失函数,$\nabla L(w_t;x_t,y_t)$是损失函数对参数的梯度。在每次迭代中,我们从数据集中随机选择一个小批量的样本$(x_t,y_t)$,然后根据当前参数$w_t$计算出该样本的损失函数的梯度,并根据上述公式更新参数$w$,直到达到预定的迭代次数或者损失函数收敛为止。 需要注意的是,SGD很容易陷入局部最优解,因此需要采用一些正则化技术来减少过拟合的风险,如L1/L2正则化、dropout等。
  • CrossEntropyLoss() 是PyTorch中用于多分类任务的损失函数之一,常用于分类问题中。它是交叉熵损失函数的一种特殊形式,可以用于衡量模型预测的概率分布与真实标签的差距。 具体来说,对于一个大小为 N 的样本集合,其损失函数的计算方式为: $$ \text{loss} = -\frac{1}{N}\sum_{i=1}{N}\sum_{j=1}y_{ij}\log(p_{ij}) $$ 其中,$y_{ij}$表示第$i$个样本的第$j$个标签是否为正样本,当$y_{ij}=1$时表示正样本,否则为负样本。$p_{ij}$表示模型预测第$i$个样本的第$j$个类别的概率,它是模型的输出结果。 对于每个样本,该损失函数计算了它所有标签的损失,并对所有样本的损失求平均。当模型的预测与真实标签完全一致时,损失函数为0,否则损失函数会大于0。 在PyTorch中,可以使用 nn.CrossEntropyLoss() 来构造一个交叉熵损失函数。它会自动地对模型的输出进行softmax归一化,并计算交叉熵损失。
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter
from torch.optim.lr_scheduler import StepLR
from torch.utils.data import DataLoader

"""
demo1:在模型训练中加入损失函数和优化器
"""
dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)
dataloader = DataLoader(dataset, batch_size=1)


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


mld = Module()
# 损失函数 优化器
loss = nn.CrossEntropyLoss()
# 调用 mld.parameters() 可以获取到 Module 中的所有可训练参数;lr是训练速率
optim = torch.optim.SGD(mld.parameters(), lr=0.01)

for i in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs, targets = data
        outputs = mld(imgs)
        result_loss = loss(outputs, targets)
        # 注意 清零--》反向传播算梯度--》更新参数
        optim.zero_grad()
        result_loss.backward()
        optim.step()
        running_loss = running_loss + result_loss
    print(running_loss)

# running_loss:
# tensor(18726.5977, grad_fn=<AddBackward0>)
# tensor(16132.8926, grad_fn=<AddBackward0>)
# tensor(15426.6357, grad_fn=<AddBackward0>)

15 model_pretrained(现有网络模型使用与修改)

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

"""
demo1:加载vgg训练好的模型,并在里边加入一个线性层
"""
# ImageNet数据集太大了,100多G,还是用CIFAR10吧
# train_data = torchvision.datasets.ImageNet("../data_image_net", split='train', download=True,
#                                         transform=torchvision.transforms.ToTensor())
train_data = torchvision.datasets.CIFAR10('../data', train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
# 加载现有的vgg模型
vgg16_not_pretrain = torchvision.models.vgg16(pretrained=False)
vgg16_pretrained = torchvision.models.vgg16(pretrained=True)

# 修改方法1:加入一个线性层,编号7
vgg16_pretrained.add_module("7", nn.Linear(1000, 10))
print(vgg16_pretrained)

# 修改方法2:修改原来的第六个线性层
vgg16_not_pretrain.classifier[6] = nn.Linear(4096, 10)
print(vgg16_not_pretrain)

16 model_save(模型的保存与加载)

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

train_data = torchvision.datasets.CIFAR10('../data', train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
# 加载现有的vgg模型
vgg16_not_pretrain = torchvision.models.vgg16(pretrained=False)
vgg16_pretrained = torchvision.models.vgg16(pretrained=True)

"""
demo2:保存/加载模型
两种保存方法 对应两种加载方法
保存模型都是用torch.save,加载模型都是用torch.load,一起保存的时候save整个模型,加载时直接torch.load加载
保存时只保存参数的,需要先向model vgg加载结构,再用model vgg.load state dict加载参数,加载参数还是要torc.load方法
保存方法1的‘陷阱’:
在使用方法1保存现有模型时,不会出错,代码更少,但是使用方法1保存自己的模型时,必须要引入这个模型的定义才可以
"""
# 保存东西需要现有东西保存
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1:模型结构+参数一起保存
torch.save(vgg16, "vgg16_pretrained_save_method1.pth")
# 多保存一个vgg16_pretrained,后面 完整模型测试讨论会用到
torch.save(vgg16_pretrained, "vgg16_pretrained_save_method1.pth")
# 加载方式1
model1 = torch.load("vgg16_save_method1.pth")


# 保存方式2:只保存模型参数(推荐)
torch.save(vgg16.state_dict(), "vgg16_save_method2.pth")
# 加载方式2
# 先加载模型结构
model_vgg = torchvision.models.vgg16(pretrained=False)
# 再加载模型参数
model_vgg.load_state_dict(torch.load("vgg16_save_method2.pth"))


# 保存方法1的‘陷阱’
"""先保存tudui模型
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()
torch.save(tudui, "tudui_save_method1.pth")
"""

"""
直接加载tudui模型会报错
tudui = torch.load("tudui_save_method1.pth")
报错:
AttributeError: Can't get attribute 'Tudui' ...>
"""

17 完整训练流程

  • model.py
import torch
from torch import nn
from torch.nn import Sequential, Conv2d, ReLU, MaxPool2d, Flatten, Linear


# 创建网络模型
class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, (5, 5), (1, 1), 2),
            ReLU(),
            MaxPool2d(2),
            Conv2d(32, 32, (5, 5), (1, 1), 2),
            ReLU(),
            MaxPool2d(2),
            Conv2d(32, 64, (5, 5), (1, 1), 2),
            ReLU(),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

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


if __name__ == '__main__':
    mdl = Module()
    inputs = torch.ones((64, 3, 32, 32))
    outputs = mdl(inputs)
    print(outputs.shape)

  • trian.py
import os
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from model import Module

# 定义训练的设备
# cuda:0 是独显,因为集显没有cuda
# cuda:0 可以替换成 cuda,对于单显卡的 pc 没有区别
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

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)

train_data_size = len(train_data)
test_data_size = len(test_data)

print(f"训练数据集长度:{train_data_size}")
print(f"测试数据集长度:{test_data_size}")

# 使用dataloader加载数据
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

# 创建模型
mdl = Module()
mdl = mdl.to(device)

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

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

# 设置控制训练次数的参数
# 记录训练 测试次数
total_train_step = 0
total_test_step = 0
# 训练轮数
epoch = 20
# 记录最佳准确率
best_accuracy = 0
# 写入board
writer = SummaryWriter("logs_train")

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

    # 训练步骤开始
    mdl.train()
    for data in train_dataloader:
        imgs, targets = data
        imgs = imgs.to(device)
        targets = targets.to(device)

        outputs = mdl(imgs)
        loss = loss_fn(outputs, targets)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        total_train_step += 1
        writer.add_scalar("train loss", loss.item(), total_train_step)

        if total_train_step % 100 == 0:
            print("训练次数:{}, loss:{}".format(total_train_step, loss.item()))

    # 测试步骤开始
    mdl.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 = mdl(imgs)
            loss = loss_fn(outputs, targets)
            total_test_loss += loss
            accuracy = (outputs.argmax(1) == targets).sum()
            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)

    now_accuracy = total_accuracy / test_data_size
    writer.add_scalar("test accuracy", total_accuracy / test_data_size, total_test_step)
    total_test_step += 1

    model_folder = "./model"
    if not os.path.exists(model_folder):
        os.makedirs(model_folder)

    # 保存准确率最高的模型
    if now_accuracy > best_accuracy:
        best_accuracy = now_accuracy
        torch.save(mdl.state_dict(), f"./model/best.pth")
    # 保存最新的模型
    if i == epoch - 1:
        torch.save(mdl.state_dict(), f"./model/last.pth")

writer.close()

18 完整推理流程

  • 找一张狗的图片,放在./data/images/dog.jpg
import torch
import torchvision
from PIL import Image

from model import Module

# 加载数据集
dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
                                         download=False)
# 获取分类列表
class_list = list(dataset.class_to_idx.keys())

# 加载图片
image_path = "./data/images/dog.jpg"
image = Image.open(image_path)

'''
image = image.convert('RGB')
由于图片有png jpg的不同格式,而png图片是四通道的 多一个透明度通道,jpg是三通道的 只有三个颜色通道
这一行代码可以让png jpg都只有三个颜色通道,增强了代码的适应性
'''
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
                                            torchvision.transforms.ToTensor()])
image = transform(image)
print(image.shape)

# 加载模型
model = Module()
model.load_state_dict(torch.load("./model/best.pth", map_location=torch.device('cuda')))
print(model)
image = torch.reshape(image, (1, 3, 32, 32))

# 查看模型输出
model.eval()
with torch.no_grad():
    output = model(image)
# 所有类别的概率
print(output)

# 找到最大的概率值的位置 查看数字对应类别在debug datasets.CIFAR10的class to idx
print(output.argmax(1))
# 输出预测结果
print(class_list[output.argmax(1)])

标签:torchvision,nn,self,torch,笔记,PyTorch,import,data
From: https://www.cnblogs.com/xiufanivan/p/17408281.html

相关文章

  • SpringBoot学习笔记--系列文章
    随笔分类 -  SpringBootSpringBoot学习笔记(八)——JWT、(Vue3、Axios、Vue-Router、TypeScript实现授权与验证示例)SpringBoot学习笔记(七)——综合示例BookStore图书管理系统SpringBoot学习笔记(六)——分页、跨域、上传、定制banner、Lombok、HutoolSpringBoot学习......
  • 第二章笔记大全
         ......
  • mongo笔记
    #登录mongo数据库mongomongo-u用户名-p密码--authenticationDatabase数据库名#数据库简单操作showdbsuse数据库名showtablesdb.数据库名.find()db.createCollection("集合名")db.集合名.drop()#导出导入操作mongoexport-d数据库名-c集合名-o/db/Si......
  • windows笔记本一天不关机就卡如何快速解决
    背景每天要打开很多应用,包括扫码登录的,账号密码登录的,需要启动运行脚本启动的,因此每天重启会很麻烦方案一macbook:如果之前使用windows内存8G就够用,那么推荐更换macair;如果之前使用windows内存在16G和32G才够用,推荐macpro方案二在笔记本支持双通道的情况下,添加内存条续......
  • 基于大模型的优质Prompt开发课--学习笔记ing
    大规模与训练语言模型(LLMs) Large-scaleandtrainedlanguagemodel 近十年深度学习模型主要更迭 当模型能够习得的知识量级越来越大,其生成的内容亦呈现出无线可能 为什么大模型能够如此强大的表现力大模型(LLMs)涌现出的三大能力:上下文学习(In-cotnextlearning)上下......
  • 人件集 人性化的软件开发阅读笔记01
    《人件集:人性化的软件开发》第一部分团队开发第一章决策,决策讲述了中庸的风险以及轻度领导研究表明,集体的决策比从集体中的个体独立做选择更具有风险倾向。如果将这种决策模式应用于软件编程,我们可能会看到这样的结果:团队可能使用更奇特的数据结构、更古怪的算法或者更......
  • 计算机网络学习笔记之数据链路层的介质访问控制
    计算机网络学习笔记之数据链路层的介质访问控制介质访问控制信道划分介质访问控制随机访问介质访问控制ALOHA协议(不听就说)纯ALOHA协议时隙ALOHA协议比较CSMA协议(先听再说)坚持CSMA非坚持CSMAp-坚持CSMA比较CSMA/CD协议(先听再说,边听边说)......
  • 优秀课件笔记之什么是数据结构
    1、本文所以内容来自著名高校课件和学生笔记(校园里面经常见到有人高价买笔记)2、任课教师不会提供参考文献,所以只能对作者表示感谢,如果引用了您的作品,可以用回复方式补充参考文献。3、我不对文章无关问题进行解答,文章内容也比较难,我也很难解答您遇到的问题,如果发现BUG可以用回复方......
  • python学生管理系统笔记(+增删改查,但不存入数据库或文件中)
    原本的基础上+增删改查,但不存入数据库或文件中,就是数据只在一次运行的页面中进行增删改查,但是重新运行不会有之前的数据,因为没有更新到json或者数据库中。1.LoginPage.pyimporttkinterastkfromtkinterimportmessageboxfromdbimportdbfromMainPageimportMainPage......
  • 大数据测试学习笔记之监控工具Dr.Elephant
     这是2018年度业余主要学习和研究的方向的笔记:大数据测试整个学习笔记以短文为主,记录一些关键信息和思考预计每周一篇短文进行记录,可能是理论、概念、技术、工具等等学习资料以IBM开发者社区、华为开发者社区以及搜索到的相关资料为主我的公众号:开源优测大数据测试学习笔记之监控......