Toda una rutina de modelos de prueba y entrenamiento de escritura de pytorch

Tome el modelo de red de CIFAR10 como ejemplo.

inserte la descripción de la imagen aquí

estructura de red

Primero escriba la estructura de la red de acuerdo con el diagrama de estructura:

import torch
from torch import nn

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

# 验证网络的正确性
if __name__ == '__main__':
    cifar10 = Cifar10()
    input = torch.ones([64, 3, 32, 32])
    output = cifar10(input)
    print(output.shape)

Ejecutar este archivo solo imprime en el terminal:

tensor([64,10)]

64 líneas de datos representan 64 imágenes (tamaño de lote), y cada línea tiene 10 datos que representan la distribución de probabilidad de cada imagen en 10 categorías.

archivo de entrenamiento

Luego escribe el archivo de entrenamiento:

import torchvision
from torch.utils.tensorboard import SummaryWriter

from model import *   # 导入编写网络模型的文件
from torch.utils.data import DataLoader

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


# 准备数据集
train_data = torchvision.datasets.CIFAR10(root="dataset", train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
test_data = torchvision.datasets.CIFAR10(root="dataset", train=False, transform=torchvision.transforms.ToTensor(),
                                          download=True)

# 数据集长度
train_data_size = len(train_data)
test_data_size = len(test_data)

# 加载数据集
train_dataloader = DataLoader(dataset=train_data, batch_size=64)
test_dataloader = DataLoader(dataset=test_data, batch_size=64)

# 创建网络模型
cifar10 = Cifar10()
cifar10 = cifar10.to(device)

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

# 优化器
lr = 1e-2
optimizer = torch.optim.SGD(cifar10.parameters(), lr=lr)

writer = SummaryWriter("./logs")

epochs = 100
for epoch in range(epochs):
    print("----------开始第{}轮训练".format(epoch))
    # 开始训练步骤
    # 让网络进入训练状态 对于Dropout、BatchNormal等特殊层有作用,必须调用(没有的话也可以调用)
    cifar10.train()
    for data in train_dataloader:
        imgs, targets = data
        imgs = imgs.to(device)
        targets = targets.to(device)
        output = cifar10(imgs)
        loss = loss_fn(output, targets)

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

    # 开始验证
    # 让网络进入验证状态 对于Dropout、BatchNormal等特殊层有作用,必须调用(没有的话也可以调用)
    cifar10.eval()
    total_test_loss = 0
    preds = 0
    with torch.no_grad():
        for data in test_dataloader:
            imgs, targets = data
            imgs = imgs.to(device)
            targets = targets.to(device)
            output = cifar10(imgs)
            pred = (output.argmax(1) == targets).sum()
            preds += pred
            loss = loss_fn(output, targets)
            total_test_loss += loss.item()
    print("test loss:{}".format(total_test_loss))
    print("test accuracy:{}".format(preds/test_data_size))
    writer.add_scalar("test_loss", total_test_loss, epoch)
    writer.add_scalar("test_accuracy", preds/test_data_size, epoch)

    # 保存每轮模型训练结果
    torch.save(cifar10, "models/cifar10_{}.pth".format(epoch))

writer.close()

1. Borrado de gradiente: para evitar que el gradiente calculado en la ronda anterior afecte los parámetros actualizados de esta ronda, el gradiente debe borrarse primero en cada ronda y luego los parámetros deben actualizarse de acuerdo con el gradiente.
2. El cálculo de gradiente no se realiza durante la verificación: uno es para ahorrar recursos, y el otro es que la verificación es solo para que nos refiramos al entrenamiento del modelo de varios indicadores, y el gradiente obtenido no afectará la actualización de la parámetros
3. Precisión de la predicción:
salida.argmax(1): Es la posición donde se obtiene el valor máximo de cada fila de datos de la salida de inferencia (valor predicho), es decir, la categoría predicha.

Si el parámetro es 0, se calcula la posición del valor máximo de los datos sin columna, pero aquí tenemos una distribución de probabilidad de una imagen, por lo que necesitamos calcular horizontalmente en lugar de verticalmente.

output.argmax(1) == objetivos: la categoría pronosticada se compara con la categoría real, y la categoría pronosticada y la categoría real de la misma foto son iguales a True.

(salida.argmax(1) == objetivos).sum(): Sume los valores iguales de cada predicción y luego agregue el número total de datos de prueba para obtener la precisión de la predicción en los datos de prueba.

resultado de la operación

Ingrese el comando para iniciar tensorboard en la terminal: tensorboard --logdir=logsy luego abra la URL correspondiente:
Primero, mire la tasa de precisión en el conjunto de verificación:
inserte la descripción de la imagen aquí
el aumento general es normal, pero es bastante bajo si no aumenta después de llegar a 0.65 ( pero es solo un experimento y no una aplicación práctica).
Mire train_loss nuevamente:
inserte la descripción de la imagen aquí
es normal que la pérdida disminuya.
Mire test_loss nuevamente:
inserte la descripción de la imagen aquí
la tendencia de esta curva es anormal y comienza a dispararse después de 20 rondas. Combinado con la tendencia de train_loss, puede ser un óptimo local.La red es demasiado simple y la tasa de aprendizaje no cae en el período posterior, lo que conduce a un sobreajuste, es decir, la red está aprendiendo desesperadamente en una dirección local.

Revisar

Aquí hice un experimento (solo ajuste la tasa de aprendizaje) para ver si habrá una mejora.
Coloque la declaración de uso del optimizador en un ciclo de 100 rondas y luego disminuya la tasa de aprendizaje × 0.95 para cada ronda.
inserte la descripción de la imagen aquí
Veamos primero la tasa correcta:
inserte la descripción de la imagen aquí
después de 100 rondas de entrenamiento, todavía hay una tendencia ascendente al final, y está cerca de 0.7, que es más alta que antes, lo que indica que se puede ver el efecto.
Mire train_loss nuevamente:
inserte la descripción de la imagen aquí
la curva es muy suave, lo cual es mejor que el último ligero susto de hace un momento.
Finalmente, mire el test_loss que simplemente salió mal:
inserte la descripción de la imagen aquí
es muy hermoso y también declina suavemente.
Hasta ahora podemos ver que nuestros cambios siguen siendo muy exitosos.

archivo de prueba

En primer lugar, encontré aleatoriamente una imagen de una clase en las 10 categorías del conjunto de datos cifar10 en Internet. Aquí encontré un perro como ejemplo.
inserte la descripción de la imagen aquí

import torch
from PIL import Image
import torchvision

img_path = "./dog.png"
img = Image.open(img_path)

img = img.convert("RGB")  # 保留rgb通道 png有4个通道,多一个透明度通道 这样就可以适应各种格式的图片

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

img = transform(img)
print(img.shape)

img = torch.reshape(img, (1, 3, 32, 32))

img = img.cuda()
model = torch.load("models/cifar10_99.pth")  # 因为观察曲线走势发现最后一轮的效果最好所以用最后一次训练保存的模型
model.eval()
with torch.no_grad():     # 节约内存
    output = model(img)
print(output.argmax(1))

pregunta

Hay varios problemas aquí:
1. Si hay un parámetro en el método Resize, debe estar encerrado en una tupla o lista. Debido a que un int indica que el lado mínimo se redimensiona a un valor específico, y el lado máximo se escala proporcionalmente; si es para el tamaño de dos lados, se escala a la [h, w] especificada.
2. Debido a que solo hay una entrada de imagen aquí, no un lote de imágenes en la carpeta, por lo que la adquisición por lotes de batch_size no se configura con dalaloader. Por lo tanto, si no cambia la forma en cuatro dimensiones (un lote más), informará este error:

RuntimeError: Expected 4-dimensional input for 4-dimensional weight [32, 3, 5, 5],
 but got 3-dimensional input of size [3, 32, 32] instead

Es decir, la entrada de la red debe ser de 4 dimensiones, pero en realidad es solo de 3 dimensiones.
3. Si los datos de entrada no usan cuda, informará un error como este:

RuntimeError: Input type (torch.FloatTensor) and weight type (torch.cuda.FloatTensor) 
should be the same or input should be a MKLDNN tensor and weight is a dense tensor

El tipo de entrada y el tipo de salida deben ser los mismos, porque usamos cuda para el entrenamiento con pesas, por lo que aquí también necesitamos una entrada de tipo cuda.
Si está ejecutando el modelo en un entorno diferente, como el entrenamiento en un dispositivo con una GPU y la prueba en un dispositivo con solo una CPU, debe especificar el entorno de asignación al cargar el modelo y asignarlo al entorno de la CPU:

model = torch.load("models/cifar10_99.pth", map_location=torch.device("cpu"))

producción

Resultado de salida (salida de la CPU)

tensor([5])

salida gpu

tensor([5], device='cuda:0')

Vaya al sitio web oficial de pytorch para ver la clase correspondiente al conjunto de datos cifar10:
inserte la descripción de la imagen aquí
puede ver que la clase con idx de 5 es perro y la predicción es correcta.

Resumir

train.py
test.py
model.py
输入
前向传播
输入
输出与真实值
数据集长度/batch_size次循环
epochs轮循环
模型
实例化网络模型
根据梯度更新相关卷积核参数
反向传播更新梯度
梯度清零
计算loss
使用DataLoader加载数据
生成datasets
测试集验证
保存模型
模型2
加载模型
预测结果
测试数据
编写网络结构

Supongo que te gusta

Origin blog.csdn.net/weixin_45354497/article/details/130348301
Recomendado
Clasificación