Notas de lectura del tutorial oficial de Pytorch (1): clasificador de imágenes

Enlace tutorial oficial

1. Conjunto de datos CIFAR10

1.1 Descripción del conjunto de datos

El conjunto de datos cifar10 contiene 60000 hojas de tamaño 32 × 32 32 \ times323 2×3 2 imágenes RGB, las imágenes del conjunto de datos se dividen en 10 categorías (consulte la figura siguiente), cada categoría tiene 6000 imágenes y las 60000 imágenes del conjunto de datos cifar10 se dividen en 50000 imágenes del conjunto de entrenamiento y 10000 imágenes Imagen del equipo de prueba.

conjunto de datos

1.2 Obtener el conjunto de datos

En pytorch, el paquete torchvision se puede utilizar para cargar y normalizar el conjunto de entrenamiento y el conjunto de prueba de cifar10. El código fuente oficial (encapsulado y modificado) es el siguiente:

import torchvision
import torchvision.transforms as transforms


def loadDataset(batch_size=4):
    """
    功能:下载并返回训练集(50000张图片)和测试集(10000张图片)
    batch_size:批大小,默认为官方教程中的4
    trainloader,testloader:可迭代对象
    """
    transform = transforms.Compose(
        [transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                            download=True, transform=transform)
    #PyTorch已有的数据读取接口的输入按照batch size封装成Tensor
    trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size,
                                            shuffle=True, num_workers=2)

    testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                        download=True, transform=transform)
    testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,
                                            shuffle=False, num_workers=2)
    """
   	print(len(trainloader) * 4,len(testloader) * 4)
   	#50000 10000
    testd = iter(testloader)
    imgs,labs = testd.next()
    print(type(imgs),type(labs))
    #<class 'torch.Tensor'> <class 'torch.Tensor'>
    print(imgs.shape,labs.shape)
    #image的通道属性为NCHW,即batch,channel,height,width
    #torch.Size([4, 3, 32, 32]) torch.Size([4])
    """
    return trainloader,testloader

Cabe señalar que si ejecuta el código anterior directamente para descargar el conjunto de datos, la velocidad de descarga puede ser lenta. La solución: cómo importar un conjunto de datos local (tome cifar10 como ejemplo) - tutorial detallado, pytorch, CIFAR10 (el conjunto de datos se puede descargar directamente a través del enlace , Y luego siga los siguientes pasos en la solución), después de que el código anterior se ejecute correctamente, podrá ver la carpeta cifar-10-batches-py localmente.

2. Modelo de CNN

2.1 Modelo LeNet-5

El modelo LeNet-5 es un modelo de clasificación utilizado en el tutorial oficial de Pytorch. El diagrama del modelo es el siguiente:

lenet-5

El código fuente correspondiente se interpreta de la siguiente manera:

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

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        #输入图片通道数 3 输出通道 6 卷积核 5*5
        self.conv1 = nn.Conv2d(3, 6, 5)
        #池化核 2*2 步长 2
        self.pool = nn.MaxPool2d(2, 2)
        #输入通道 6 输出通道 16 卷积核 5*5
        self.conv2 = nn.Conv2d(6, 16, 5)
      	#全连接层
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        #conv1:输入 32*32*3 输出 28*28*6
        #maxpool1:输入 28*28*6 输出 14*14*6
        x = self.pool(F.relu(self.conv1(x)))
        #conv2:输入 14*14*6 输出 10*10*16
        #maxpool2:输入 10*10*16 输出 5*5*16
        x = self.pool(F.relu(self.conv2(x)))
        #reshape (1,16*5*5)
        x = x.view(-1, 16 * 5 * 5)
        #full connected layer1 16*5*5->120
        x = F.relu(self.fc1(x))
        #full connected layer2 120->84
        x = F.relu(self.fc2(x))
        #full connected layer3 84->10
        x = self.fc3(x)
        return x


def loadLeNet5(gpu=True):
    """
    功能:加载网络模型
    gpu:设定是否开启gpu加速
    """
    LeNet5 = LeNet5()
    if gpu:
        LeNet5.cuda(device)
    return LeNet5

if __name__ == "__main__":
    LeNet5 = LeNet5()

2.2 modelo Alexnet

También implementé otro modelo clásico de CNN: Alexnet, referencia teórica: estructura de red neuronal AlexNet , pero eliminé la capa LRN bastante controvertida. Además, debido a que el tamaño de la imagen del conjunto de datos cafar10 es pequeño, se producirán errores durante los cálculos, por lo que al probar el modelo, la imagen cambia de tamaño a 96 × 96 96 \ times969 6×9 6 , por lo que debe modificar el código de la siguiente manera:

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

El código de implementación del modelo Alexnet es el siguiente:

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

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

class Alexnet(nn.Module):
    def __init__(self):
        super(Alexnet,self).__init__()
        #输入通道 3 输出通道 96 卷积核 11*11 步长 4
        self.conv1 = nn.Conv2d(3,96,11,stride=4)
        #池化核 3*3 步长 2
        self.pool1 = nn.MaxPool2d(3,2)
        self.relu1 = nn.ReLU()
        
        #输入通道 3 输出通道 256 卷积核 5*5 步长 1 same padding
        self.conv2 = nn.Conv2d(96,256,5,stride=1,padding=2)
        self.relu2 = nn.ReLU()
        #池化核 3*3 步长 2
        self.pool2 = nn.MaxPool2d(3,2)

        #输入通道 256 输出通道 384 卷积核 3*3 步长 1 same padding
        self.conv3 = nn.Conv2d(256,384,3,stride=1,padding=1)
        self.relu3 = nn.ReLU()

        #输入通道384 输出通道 384 卷积核 3*3 步长 1 same padding
        self.conv4 = nn.Conv2d(384,384,3,stride=1,padding=1)
        self.relu4 = nn.ReLU()
		
        #输入通道 384 输出通道 256 卷积核 3*3 步长 1 same padding
        self.conv5 = nn.Conv2d(384,256,3,stride=1,padding=1)
        self.relu5 = nn.ReLU()
        #池化核 3*3 步长 2
        self.pool5 = nn.MaxPool2d(3,2)

        self.fc6 = nn.Linear(256*1*1,4096)
        self.relu6 = nn.ReLU()
        self.dropout6 = nn.Dropout(0.5)

        self.fc7 = nn.Linear(4096,4096)
        self.relu7 = nn.ReLU()
        self.dropout7 = nn.Dropout(0.5)

        self.fc8 = nn.Linear(4096,10)
        

    
    def forward(self,x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.pool1(x)
       
        x = self.conv2(x)
        x = self.relu2(x)
        x = self.pool2(x)
       
        x = self.conv3(x)
        x = self.relu3(x)
        
        x = self.conv4(x)
        x = self.relu4(x)
        
        x = self.conv5(x)
        x = self.relu5(x)
        x = self.pool5(x)
        #print(x.shape)
        
        x = x.view(-1,256*1*1)
        x = self.fc6(x)
        x = self.relu6(x)
        x = self.dropout6(x)

        x = self.fc7(x)
        x = self.relu7(x)
        x = self.dropout7(x)

        x = self.fc8(x)

        return x

def loadAlexnet(gpu=True):
    """
    功能:加载网络模型
    gpu:设定是否开启gpu加速
    """
    alexnet = Alexnet()
    if gpu:
        alexnet.cuda(device)
    return alexnet

if __name__ == "__main__":
    x = torch.ones(1,3,96,96,dtype=torch.float32)
    net = Alexnet()
    net.forward(x)

3. Capacitación y pruebas

En el entrenamiento y prueba del modelo, activé la aceleración de GPU desde el principio. Además, no seguí el tutorial oficial para configurar los superparámetros. La siguiente tabla muestra algunos parámetros importantes del tutorial oficial y los parámetros que realmente uso:

parámetro Tutorial oficial Uso actual
Función de pérdida Entropía cruzada Entropía cruzada
Optimizador SGD Adán
tamaño del lote 4 256
iteraciones 2 50
tasa de aprendizaje 0,001 0,001, 0,0001
#定义损失函数(交叉熵)
criterion = nn.CrossEntropyLoss().cuda()

#定义优化器
#optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
optimizer = optim.Adam(net.parameters(),lr=lr)

En el proceso de entrenamiento, el número de iteraciones del conjunto de datos se modifica a 50, y batch_size se modifica a 256, es decir, el parámetro bach_size de la función loadDataset se modifica a 256, y el código fuente correspondiente es el siguiente (encapsulado en una función):

def train(trainloader,net,lr=0.001,iterations=50):
    """
    功能:训练模型
    trainloader:训练数据集(Iterable)
    net:带训练的模型
    """
    #定义损失函数(交叉熵)
    criterion = nn.CrossEntropyLoss().cuda()
    #定义优化器
    #optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
    optimizer = optim.Adam(net.parameters(),lr=lr)
    #开始训练
    for epoch in range(iterations):  #迭代数据集iterations次
        running_loss = 0.0
        for i, data in enumerate(trainloader, 0):
            #获取输入数据
            inputs, labels = data
            #张量转换为GPU张量
            inputs, labels = inputs.cuda(device), labels.cuda(device)
            #梯度清零
            optimizer.zero_grad()
            #前向传播
            outputs = net(inputs)
            #计算损失
            loss = criterion(outputs, labels)
            #后向传播
            loss.backward()
            #更新网络参数
            optimizer.step()
            #对50次的损失求平均值
            running_loss += loss.item()
            if (i+1) % 50 == 0:
                print('[%d, %5d] loss: %.3f' %
                    (epoch + 1, i + 1, running_loss / 50))
                running_loss = 0.0

    print('Finished Training')
    return net

El código de prueba es el siguiente (encapsulado en una función):

def test(testloader,net):
    """
    功能:验证模型
    testloader:测试数据集
    net:训练后的模型
    """
    correct = 0
    total = 0
    with torch.no_grad():
        for data in testloader:
            images, labels = data
            #tensor转到GPU上
            images, labels = images.cuda(device), labels.cuda(device)
            outputs = net(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

Después del entrenamiento y las pruebas, los resultados de las pruebas de los dos últimos modelos se comparan de la siguiente manera (debido a razones de tiempo, no se realizó ninguna operación de ajuste de parámetros):

Exactitud del equipo de prueba Modelo LeNet-5 Modelo Alexnet
tasa de aprendizaje = 0,001 63,4% 68,01%
tasa de aprendizaje = 0,0001 56,09% 76,09%

Supongo que te gusta

Origin blog.csdn.net/qq_42103091/article/details/109238607
Recomendado
Clasificación