El estudio de Lenet señala la reproducción del código de demostración oficial de pytorch

Prefacio:

El contenido de referencia proviene de: 2.1 demostración oficial de pytorch (Lenet)_bilibili_bilibili

Código de subida y ppt: https://github.com/WZMIAOMIAO/deep-learning-for-image-processing

Código oficial de Pytorch: Entrenamiento de un clasificador - Tutoriales de PyTorch 1.13.1+documentación cu117

Descarga del conjunto de datos:

Enlace: https://pan.baidu.com/s/1NBHp0SxEOJ5EIyYUsDHm_g

Código de extracción: qp3k


Introducción

La clasificación de pytorch Tensor: [lote, canal, alto, ancho], cuando se procese Tensor más adelante, se procesará de acuerdo con este orden.

lote representa la cantidad de imágenes en un lote;

El canal representa la dimensión de la imagen. El CIFAR10 utilizado aquí es una imagen en color, por lo que el canal es 3.

La red LeNet consta de una capa convolucional, una reducción de resolución, una capa convolucional, una reducción de resolución y tres grupos de capas completamente conectados;

La red LeNet utiliza imágenes en escala de grises con una sola dimensión.

proceso de demostración

  • model.py: define el modelo de red LeNet

  • train.py: carga el conjunto de datos y entrena, calcula la pérdida en el conjunto de entrenamiento, calcula la precisión en el conjunto de prueba y guarda los parámetros de la red entrenada.

  • predict.py: después de obtener los parámetros de red entrenados, utilice las imágenes que encontró para las pruebas de clasificación.

1. modelo.py

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


class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 16, 5)   #分别代表输入特征矩阵的深度,卷积核的个数,卷积核的大小;
        self.pool1 = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(16, 32, 5)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))    # input(3, 32, 32) output(16, 28, 28)
        x = self.pool1(x)            # output(16, 14, 14)
        x = F.relu(self.conv2(x))    # output(32, 10, 10)
        x = self.pool2(x)            # output(32, 5, 5)
        x = x.view(-1, 32*5*5)       # output(32*5*5)
        x = F.relu(self.fc1(x))      # output(120)
        x = F.relu(self.fc2(x))      # output(84)
        x = self.fc3(x)              # output(10)
        return x

Pasos para construir un modelo en pytorch:

  • Defina una clase que herede la clase principal nn.Module;

  • Implemente dos métodos, la función __init__ y la función de avance en esto;

  • La función de inicialización __init__ implementa la estructura de capa de red necesaria para construir la red en la función de inicialización;

  • El proceso de propagación hacia adelante se define en la función hacia adelante. Después de crear una instancia de esta clase, cuando los parámetros se pasan a esta instancia, se realizará la propagación hacia adelante.

Pasos para construir LeNet:

  • Defina una clase LeNet, que debería heredar la clase principal nn.Module;

Definir la función de inicialización:

  • Implemente la función de inicialización __init__ en él y use la superfunción en ella. La superfunción puede resolver los problemas que pueden surgir al llamar a los métodos de la clase principal. Cuando se trata de herencia múltiple, generalmente se usa la superfunción;

  • Definir la capa convolucional en pytroch es definir la capa convolucional a través de la función nn.Conv2d (3, 16, 5), que representan respectivamente la profundidad de la matriz de características de entrada, el número de núcleos de convolución y el tamaño del núcleo de convolución. Después del cálculo Obtenga el resultado (16, 28, 28): la fórmula de cálculo se encuentra a continuación;

  • Defina la operación de reducción de resolución y use el método nn.MaxPool2d (2, 2) significa usar una suma de agrupación de 2, un paso de 2 y reducir la altura y el ancho a la mitad del original, lo que da como resultado la salida (16, 14, 14);

  • Definir la segunda capa convolucional, (16, 32, 5) significa que la profundidad de la matriz de características de entrada es 16, el número de núcleos de convolución es 32 y el tamaño del núcleo de convolución es 5. Después del cálculo, la salida (32, 10, 10);

  • Defina la segunda capa de reducción de resolución, use el método nn.MaxPool2d para definirla como (2, 2), reduzca la altura y el ancho a la mitad del original y obtenga la salida (32, 5, 5);

  • Defina la capa completamente conectada. Debido a que la entrada de la capa completamente conectada es un vector unidimensional, la matriz de características obtenida debe aplanarse en un vector unidimensional. El número de nodos de entrada de la primera capa completamente conectada es (32 *5* 5), establezca el número de nodos en 120 al mismo tiempo;

  • Defina la segunda capa completamente conectada, la entrada es la salida de la capa anterior es 120 y la salida es 84;

  • Defina la tercera capa completamente conectada. La entrada es la salida de la capa anterior, que es 84. La salida debe modificarse de acuerdo con el conjunto de entrenamiento. El CIFAR10 utilizado aquí es una categoría de datos, por lo que la salida es 10;

El proceso de propagación directa se define a continuación:

  • X representa los datos de entrada, que es la clasificación de Tensor: [lote, canal, alto, ancho]

  • Pase los datos a través de la capa convolucional 1 y luego pase los datos obtenidos a través de la función de activación de Relu;

  • Pase la salida a través de la primera reducción de resolución;

  • Pase la salida a través de la segunda capa convolucional y luego pase los datos obtenidos a través de la función de activación de Relu;

  • Pase la salida a través de la segunda reducción de resolución y luego empalme con la capa completamente conectada, que debe aplanarse;

  • El aplanamiento se realiza mediante la función de vista, (-1, 32*5*5), -1 representa la primera dimensión. Debido a que la primera dimensión es por lotes, se establece en -1 y la segunda dimensión se aplana. El número de los nodos se utilizan para obtener datos unidimensionales;

  • Pasar los datos a través de la capa 1 completamente conectada y la función de activación;

  • Pasar los datos a través de la capa 2 completamente conectada y la función de activación;

  • Pase los datos a través de la capa 3 completamente conectada para obtener el resultado final.

1.1 Conv2d

Definición de parámetros utilizados en la definición de funciones:

def __init__(
        self,
        in_channels: int,                   #输入特征矩阵的深度
        out_channels: int,                  #对应卷积核的个数
        kernel_size: _size_2_t,             #卷积核的大小
        stride: _size_2_t = 1,              #步距,默认为0
        padding: Union[str, _size_2_t] = 0, #在四周进行补零处理,默认为0
        dilation: _size_2_t = 1,
        groups: int = 1,
        bias: bool = True,                  #偏置,默认使用
    )

Definición oficial de pytorch:

La dilatación por defecto es 1, que se incluye en la fórmula, tal como la fórmula en la siguiente figura.

Calcule el tamaño de la matriz después de la convolución de self.conv1 = nn.Conv2d (3, 16, 5):

W es el tamaño de la imagen de entrada es 32 × 32, F es el tamaño del núcleo de convolución es 5, P es 0, S es 1

(32-5)/ 1 + 1 = 28

input (3, 32, 32) obtiene la salida (16, 28, 28) a través de la convolución, 16 es el número de núcleos de convolución, que se convierte en la profundidad de la matriz de características de entrada de la siguiente capa

1.2 MaxPool2d

Definición de parámetros utilizados en la definición de funciones:

def __init__(self, 
              kernel_size: _size_any_t,                #池化和的大小
              stride: Optional[_size_any_t] = None,    #步距
              padding: _size_any_t = 0, 
              dilation: _size_any_t = 1,
              return_indices: bool = False, 
              ceil_mode: bool = False) -> None:
        super(_MaxPoolNd, self).__init__()
        self.kernel_size = kernel_size
        self.stride = stride if (stride is not None) else kernel_size
        self.padding = padding
        self.dilation = dilation
        self.return_indices = return_indices
        self.ceil_mode = ceil_mode

self.pool1 = nn.MaxPool2d(2, 2) significa que la suma de la agrupación es 2 y el paso también es 2;

La salida obtenida mediante la reducción máxima de resolución es # salida (16, 14, 14), que reduce la altura y el ancho a la mitad del valor original. La operación de agrupación solo cambia la altura y el ancho de la matriz de características, pero no cambia la profundidad. ;

1.3 Prueba simple: depuración de puntos de interrupción

Puedes ver que la salida de la izquierda es la entrada configurada.

Después de la primera capa de agrupación

2. tren.py

Paquete de importación

import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import time

2.1 Preprocesamiento de datos

Preprocesar los datos de la imagen de entrada:

ToTensor函数:即由forma (H x W x C) en el rango [0, 255] → forma (C x H x W) en el rango [0.0, 1.0];

注通过PIL或者numpy导入的图片都是 (H x W x C) 格式,必须使用Tensor函数转化为(C x H x W)格式才能使用;

Normalize函数:output[channel] = (input[channel] - mean[channel]) / std[channel];

即输出=(原始数据-均值)/标准差

transform = transforms.Compose(
        [transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

数据集介绍

利用torchvision.datasets函数可以在线导入pytorch中的数据集,包含一些常见的数据集如MNIST等

导入数据集:通过CIFAR10导入训练集

# 第一次使用时要将download设置为True才会自动去下载数据集
# 导入50000张训练图片
train_set = torchvision.datasets.CIFAR10(root='./data',      # 数据集存放目录
                                         train=True,         # 表示是数据集中的训练集
                                        download=True,       # 第一次运行时为True,下载数据集,下载完成后改为False
                                        transform=transform) # 预处理过程
# 加载训练集,实际过程需要分批次(batch)训练                                        
train_loader = torch.utils.data.DataLoader(train_set,       # 导入的训练集
                                           batch_size=50, # 每批训练的样本数
                                          shuffle=False,  # 是否打乱训练集
                                          num_workers=0)  # 使用线程数,在windows下设置为0

导入测试集

# 导入10000张测试图片
test_set = torchvision.datasets.CIFAR10(root='./data', 
                                        train=False,    # 表示是数据集中的测试集
                                        download=False,transform=transform)
# 加载测试集
test_loader = torch.utils.data.DataLoader(test_set, 
                                          batch_size=10000, # 每批用于验证的样本数
                                          shuffle=False, num_workers=0)
# 获取测试集中的图像和标签,用于accuracy计算
test_data_iter = iter(test_loader)
test_image, test_label = test_data_iter.next()

数据处理:

val_data_iter = iter(val_loader)             #iter将val_loader变为可迭代的迭代器
val_image, val_label = next(val_data_iter)   #next可得到一批数据,即图像和标签
    
classes = ('plane', 'car', 'bird', 'cat',    #导入标签,元组类型(值不能改变)
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

2.2 训练过程
    net = LeNet()                                                    #实例化模型
    loss_function = nn.CrossEntropyLoss()                            #定义损失函数,这个函数已经包含softmax函数,所以网络的最后一层不再使用
    optimizer = optim.Adam(net.parameters(), lr=0.001)                    #定义优化器,Adam优化器,第一个参数是需要训练的参数,将lenet可训练的参数都进行训练,lr是学习率

    for epoch in range(5):  # loop over the dataset multiple times        #将训练集训练多少次

        running_loss = 0.0                                                #累加训练过程中的损失
        for step, data in enumerate(train_loader, start=0):               #通过循环遍历训练集样本,通过enumerate函数,不仅返回数据,还可以返回每一批数据的步数
            # get the inputs; data is a list of [inputs, labels]
            inputs, labels = data                                         #将数据分成输入和标签

            # zero the parameter gradients
            optimizer.zero_grad()                                         #将历史损失梯度清零
            # forward + backward + optimize
            outputs = net(inputs)                                         #将输入的图片进行正向传播
            loss = loss_function(outputs, labels)                         #outputs是网络的预测值,labels是输入图片对应的真实标签
            loss.backward()                                               #反向传播
            optimizer.step()                                              #参数的更新

            # print statistics
            running_loss += loss.item()                                   #将损失累加
            if step % 500 == 499:    # print every 500 mini-batches       #每隔500步打印一次数据信息
                with torch.no_grad():                                     #with是一个上下文管理器,torch.no_grad表示在接下来不要计算误差损失梯度
                    outputs = net(val_image)  # [batch, 10]               #进行正向传播

                    predict_y = torch.max(outputs, dim=1)[1]              #寻找最大的index在哪里,[batch, 10] 第0个维度是batch,第一个维度是输出,[1]是索引,得到最大值对应的标签类别

                    accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)            
# 预测标签类别和真实标签类别比较,在相同的地方返回1  true,.sum()计算在预测过程中预测对了多少样本,得到结果是tensor,通过.item()拿到数值,在除以训练样本的数目

                    print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %
                          (epoch + 1, step + 1, running_loss / 500, accuracy))
                                         # %d=epoch,%5d是某一轮的多少步, %.3f训练过程中累加的误差
                    running_loss = 0.0   #清零,进行下一次的500步

    print('Finished Training')

    save_path = './Lenet.pth'
    torch.save(net.state_dict(), save_path)                               #保存模型

打印信息如下:

[1,   500] train_loss: 1.779  test_accuracy: 0.451
[1,  1000] train_loss: 1.435  test_accuracy: 0.527
[2,   500] train_loss: 1.230  test_accuracy: 0.578
[2,  1000] train_loss: 1.177  test_accuracy: 0.613
[3,   500] train_loss: 1.053  test_accuracy: 0.632
[3,  1000] train_loss: 1.034  test_accuracy: 0.635
[4,   500] train_loss: 0.943  test_accuracy: 0.651
[4,  1000] train_loss: 0.951  test_accuracy: 0.666
[5,   500] train_loss: 0.846  test_accuracy: 0.668
[5,  1000] train_loss: 0.856  test_accuracy: 0.666
Finished Training

3. predict.py

import torch
import torchvision.transforms as transforms
from PIL import Image

from model import LeNet


def main():
    transform = transforms.Compose(
        [transforms.Resize((32, 32)),           #缩放图片 
         transforms.ToTensor(),                 #转化为tensor
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])    #标准化处理

    classes = ('plane', 'car', 'bird', 'cat',
               'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

    net = LeNet()                                                           #实例化LeNet
    net.load_state_dict(torch.load('Lenet.pth'))                            #载入权重文件

    im = Image.open('1.jpg')                                                #使用Image模块载入图片
    im = transform(im)                                         # [C, H, W],通过PIL载入的图片是高度,宽度,深度,必须变为tensor格式才能进行正向传播,进行预处理,得到[C, H, W]格式
    im = torch.unsqueeze(im, dim=0)  # [N, C, H, W],          #使用unsqueeze增加一个维度

    with torch.no_grad():
        outputs = net(im)                                                   #图像传入网络
        predict = torch.max(outputs, dim=1)[1].numpy()                      #寻找输出中最大的index
    print(classes[int(predict)])                                            #传入classes


if __name__ == '__main__':
    main()

输出即为预测的标签

预测结果也可以用 softmax 表示,输出10个概率:

with torch.no_grad():
    outputs = net(im)
    predict = torch.softmax(outputs, dim=1)
print(predict)

输出结果中最大概率值对应的索引即为预测标签的索引

tensor([[8.9619e-01, 8.9764e-04, 5.7712e-02, 4.5166e-03, 1.2172e-02, 9.6238e-04,
         7.4281e-04, 1.6205e-04, 2.4677e-02, 1.9665e-03]])

Supongo que te gusta

Origin blog.csdn.net/weixin_45897172/article/details/128550035
Recomendado
Clasificación