Pytorch início rápido e combate real - quatro, treinamento e teste de rede

Diretório da coluna: pytorch (segmentação de imagem UNet) introdução rápida e combate real - zero, prefácio
pytorch introdução rápida e combate real - 1, preparação de conhecimento (introdução aos elementos)
pytorch introdução rápida e combate real - 2, aprendizado profundo clássico desenvolvimento de rede
pytorch rápido introdução E combate real - três, Unet realiza
introdução rápida de pytorch e combate real - quatro, treinamento e teste de rede

Continuação do acima: início rápido do pytorch e combate real - três, implementação do Unet
Após a implementação da rede, leia os dados e retorne os parâmetros.



Treinamento do Processo de Implementação da Segmentação Semântica :

De acordo com o tamanho do lote, as amostras de treinamento e os rótulos no conjunto de dados são lidos na rede neural convolucional. De acordo com as necessidades reais, as imagens e rótulos de treinamento devem ser pré-processados ​​primeiro, como corte, aprimoramento de dados etc. Isso favorece o treinamento de redes profundas, acelera o processo de convergência, evita problemas de sobreajuste e aumenta a capacidade de generalização do modelo.

verificar:

Depois de treinar por um período, leia as amostras de verificação e os rótulos no conjunto de dados na rede neural convolucional e carregue os pesos de treinamento. Verifique de acordo com o índice de segmentação semântica escrito, obtenha a pontuação do índice no processo de treinamento atual e salve o peso correspondente. O método de treinar uma vez e verificar é frequentemente usado para supervisionar melhor o desempenho do modelo.

teste:

Depois que todo o treinamento terminar, leia as amostras de teste e os rótulos no conjunto de dados na rede neural convolucional e carregue os melhores valores de peso salvos no modelo para teste. Os resultados do teste são divididos em dois tipos, um é medir o desempenho da rede com base em pontuações de índice comuns e o outro é salvar os resultados de previsão da rede na forma de imagens para sentir intuitivamente a precisão da segmentação.

1. Leia os dados

Se você não está fazendo segmentação, mas sim problemas de classificação, veja aqui ou veja outros artigos:

ImageFolder
tem um método de leitura de dados pronto no PyTorch, que é o archvision.datasets.ImageFolder. Essa API é escrita depois do keras, principalmente para problemas de classificação, e coloca cada tipo de dado na mesma pasta. Por exemplo, existem 10 categorias , crie 10 subpastas em uma pasta grande e cada subpasta contém o mesmo tipo de dados.

Segmentação de imagem veja abaixo:

1.1 Herdar a classe Dataset

O PyTorch lê as imagens, principalmente por meio da classe arch.utils.data.Dataset, e o método do guia do pacote é:

from torch.utils.data import Dataset

Em seguida, herde esta classe para implementar nossa própria classe de leitura de dados: muito concisa,
principalmente dois métodos: método de implementação de classe __init__ e método de busca de elemento __getitem__

class myImageDataset(Dataset):
    def __init__(self, inputs_root, labels_root):
        self.files = sorted(glob.glob(f"{
      
      inputs_root}\\*.png"))
        self.files_using = sorted(glob.glob(f"{
      
      labels_root}\\*.png"))

    def __getitem__(self, index):
        inputs = plt.imread(self.files[index % len(self.files)]) 
        labels = plt.imread(self.files_using[index % len(self.files_using)])
        return inputs, labels

    def __len__(self):
        return len(self.files)

Simplesmente leia:

  1. iniciar

inputs_root é o caminho do diretório raiz dos dados de entrada e labels_root é o caminho do rótulo.
glob é um método que vem com Python. glob.glob pode percorrer os arquivos no diretório atual. A instrução acima percorre todos os arquivos png no diretório e sorted é para a correspondência um-para-um entre entrada e rótulo sem desordem.
O self do init não importa, basta ignorá-lo ao passar parâmetros.

  1. obter item

index é um índice de auto-incremento, que é uma operação interna do método.
Use o índice para obter o caminho, leia a imagem e retorne a ela, e pronto.

1.2 Carregador Método DataLoader

Depois que o Dataset lê as imagens, como a inteligência artificial pode processá-las em lotes?
Pytorch também fornece um método: o método DataLoader na classe arch.utils.data.

from torch.utils.data import DataLoader
train_loader = DataLoader(
    dataset=myImageDataset(inputs_root=in_folder + r"\train\inputs",
                         labels_root=in_folder + r"\train\labels"),
    batch_size=16,  # 一批有几个,一般为2的指数
    shuffle=True,
    num_workers=8  # 使用的CPU核心数量
)

Ao preencher o caminho para o nosso conjunto de dados, pegar o conjunto de dados e deixar o carregador de dados processá-lo em lotes, o número de lotes é determinado pelo batch_size, o número de threads é determinado pelo parâmetro num_worker e outros parâmetros podem ser consultados e estudados por você mesmo.

O conjunto de teste é o mesmo:

test_loader = DataLoader(
    dataset=myImageDataset(inputs_root=in_folder + r"\test\inputs",
                         labels_root=in_folder + r"\test\labels"),
    batch_size=1,
    shuffle=True,
    num_workers=opt.n_cpu
)

2. Outra inicialização

2.1 Configuração de variável global

Defina os lotes de treinamento, quantos dados por lote e defina a taxa de aprendizado e outros parâmetros (defina você mesmo de acordo com suas necessidades, copiei do meu irmão):

parser = argparse.ArgumentParser()
parser.add_argument("--epoch", type=int, default=0, help="epoch to start training from")
parser.add_argument("--n_epochs", type=int, default=100, help="number of epochs of training")
# parser.add_argument("--dataset_name", type=str, default="img_align_celeba", help="name of the dataset")
parser.add_argument("--batch_size", type=int, default=16, help="size of the batches")
parser.add_argument("--lr", type=float, default=0.0002, help="adam: learning rate")
parser.add_argument("--b1", type=float, default=0.5, help="adam: decay of first order momentum of gradient")
parser.add_argument("--b2", type=float, default=0.999, help="adam: decay of first order momentum of gradient")
parser.add_argument("--decay_epoch", type=int, default=100, help="epoch from which to start lr decay")
parser.add_argument("--n_cpu", type=int, default=4, help="number of cpu threads to use during batch generation")
parser.add_argument("--channels", type=int, default=1, help="number of image channels")
parser.add_argument("--sample_interval", type=int, default=100, help="interval between saving image samples")
parser.add_argument("--checkpoint_interval", type=int, default=-1, help="interval between model checkpoints")
opt = parser.parse_args()

2.2 Definir rede, função de perda, otimizador otimizador, tensor de conversão de tensor

Não incomode o python em falar sobre esse otimizador antes, e adam é usado aqui.
Quanto aos tensores: os tensores são funções multilineares e as matrizes são representações de tensores sob um conjunto específico de vetores de base.
Vá procurar outros sozinho, e você também pode ler este artigo: Falando sobre o que é um tensor tensor

# Initialize net
net = AdUNet()
# Losses
loss = torch.nn.L1Loss()

# 数据传给显卡?
cuda = torch.cuda.is_available()
if cuda:
    net = net.cuda()
    gloss = loss.cuda()

if opt.epoch != 0:
    # Load pretrained models
    net.load_state_dict(torch.load("../saved_models/AdUnet_%d.pkl"))

# Optimizers
optimizer = torch.optim.Adam(net.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))

Tensor = torch.cuda.FloatTensor if cuda else torch.Tensor

2.3 Treinamento

O processo geral de treinamento: compensação de gradiente, retropropagação, atualização da taxa de aprendizado

optimizer.zero_grad()  # 梯度归零:step之前要进行梯度归零
loss.backward()  # 进行反向传播求出每个参数的梯度
optimizer.step()  # 更新学习率

Processo específico:
Insira uma observação : No código abaixo emmm, além das entradas e rótulos de entrada, também há um caminho inputs_labels no conjunto de dados carregado do train_loader, certo? Isso é porque eu quero gerar um mapa de resultados posteriormente, para dar ao mapa de resultados um parâmetro de configurações nomeadas, então antes meuO __getitem__ nesse conjunto de dados também é um pouco diferente, na isso é meu (na verdade, só pegou um caminho e passou):

    def __getitem__(self, index):
        inputs_path = self.files[index % len(self.files)]
        inputs = plt.imread(inputs_path)
        labels = plt.imread(self.files_using[index % len(self.files_using)])
        inputs_name = inputs_path.split("\\")[-1]
        return inputs, labels, inputs_name

2.3.1 Ler dados de treinamento, converter dados de treinamento

    for i, data in enumerate(train_loader):
        # 一个batch
        inputs, labels, inputs_path = data
        inputs = inputs.unsqueeze(1)
        labels = labels.unsqueeze(1)
        # 将这些数据转换成Variable类型
        inputs, labels = Variable(inputs), Variable(labels)
        device = torch.device("cuda" if cuda else "cpu")
        inputs = inputs.to(device)
        labels = labels.to(device)

Minha imagem em escala de cinza lida é bidimensional, ou seja (120.240), mais um batch_size é apenas tridimensional, meu batchsize é 16, então as entradas que li são (16.120.240), mas na verdade a rede é executada Parece quatro- dimensional , como fazer?
Na verdade, a imagem em tons de cinza é um único canal, ou seja (120.240.1), então temos que adicionar manualmente este canal 1, no meuDurante o processo de depuração, verificou-se que o canal na rede geralmente é colocado em segundo lugar, portanto, precisamos alterá-lo para (16,1,120,240), pode ser realizado diretamente por meio da descompressão uncompression e pode ser adicionado diretamente em segundo lugar inputs.unsqueeze(1). Da mesma forma, a primeira vantagem éinputs.unsqueeze(0)

Depois que as entradas e rótulos foram convertidos, eles precisam ser convertidos em tipos de variáveis ​​para realizar a retropropagação (não sei o motivo, copiei) e, em seguida, usar a placa gráfica para transferir os dados para a placa gráfica e em seguida, passe-o para a CPU sem a placa gráfica.

2.3.2 Rede de treinamento

  • Gradiente retorna a zero (é 0 no início, para o próximo ciclo)
  • treinamento de rede
  • obter o resultado netout
  • Calcular a perda da função de perda
  • retropropagação
  • Para atualizar a taxa de aprendizado,
    você não precisa entendê-la, basta copiá-la.
        optimizer.zero_grad()  # 梯度归零:step之前要进行梯度归零
        net.train()
        netout = net(inputs)

        # Total loss
        gloss = loss(netout, labels)

        gloss.backward()  # 进行反向传播求出每个参数的梯度
        optimizer.step()  # 更新学习率

2.4 Teste

Inicialize os critérios de avaliação ssim, psnr e rmse.

    total_s = 0  # ssim
    total_p = 0  # psnr
    total_r = 0  # rmse

skimage vem com estas bibliotecas:

from skimage.measure import compare_ssim as ssim
from skimage.measure import compare_psnr as psnr
from skimage.measure import compare_mse as mse
  • ler dados de teste
  • Formato de dados unificado
  • conversão variável
  • enviar para placa gráfica
  • Está marcado como teste (não participará da retropropagação, tem o mesmo efeito da primeira linha, basta adicioná-lo diretamente)
  • Enviar para a rede para obter o resultado netout
  • Até duas dimensões (imagens em tons de cinza são duas dimensões)
  • Avalie as imagens em comparação com o rótulo
  • sobre
with torch.no_grad():
        for inputs, labels, inputs_path in test_loader:
            # 一个batch
            inputs = inputs.unsqueeze(1)
            # labels = labels.unsqueeze(0)
            # 将这些数据转换成Variable类型
            inputs, labels = Variable(inputs), Variable(labels)
            device = torch.device("cuda" if cuda else "cpu")
            inputs = inputs.to(device)
            labels = labels.squeeze(0).cpu().numpy()

            # optimizer.zero_grad()
            net.eval()
            netout = net(inputs)
            img_out = netout.squeeze(1)
            img_out = img_out.squeeze(0)
            img_out = img_out.cpu().numpy()

            # Total loss
            # gloss = loss(netout, labels)
            # print(netout.shape)
            s = ssim(labels, img_out)
            p = psnr(labels, img_out)
            r = sqrt(mse(labels, img_out))

            total_s += float(s.item())
            total_p += float(p.item())
            total_r += float(r)

3 código geral:

import argparse
import os
import sys

import torch
from torch.autograd import Variable
from torch.utils.data import DataLoader

from main.AdUNet import AdUNet
from main.datasets import *

from skimage.measure import compare_ssim as ssim
from skimage.measure import compare_psnr as psnr
from skimage.measure import compare_mse as mse
from math import sqrt

from other.mkdir import mkdir

mkdir("../saved_models")
parser = argparse.ArgumentParser()
parser.add_argument("--epoch", type=int, default=0, help="epoch to start training from")
parser.add_argument("--n_epochs", type=int, default=100, help="number of epochs of training")
# parser.add_argument("--dataset_name", type=str, default="img_align_celeba", help="name of the dataset")
parser.add_argument("--batch_size", type=int, default=16, help="size of the batches")
parser.add_argument("--lr", type=float, default=0.0002, help="adam: learning rate")
parser.add_argument("--b1", type=float, default=0.5, help="adam: decay of first order momentum of gradient")
parser.add_argument("--b2", type=float, default=0.999, help="adam: decay of first order momentum of gradient")
parser.add_argument("--decay_epoch", type=int, default=100, help="epoch from which to start lr decay")
parser.add_argument("--n_cpu", type=int, default=4, help="number of cpu threads to use during batch generation")
parser.add_argument("--channels", type=int, default=1, help="number of image channels")
parser.add_argument("--sample_interval", type=int, default=100, help="interval between saving image samples")
parser.add_argument("--checkpoint_interval", type=int, default=-1, help="interval between model checkpoints")
opt = parser.parse_args()

in_folder = r"..\data\pigs"
train_folder = in_folder + "\\train"

# Initialize net
net = AdUNet()
# Losses
loss = torch.nn.L1Loss()

# 数据传给显卡?
cuda = torch.cuda.is_available()
if cuda:
    net = net.cuda()
    gloss = loss.cuda()

if opt.epoch != 0:
    # Load pretrained models
    net.load_state_dict(torch.load("../saved_models/AdUnet_%d.pkl"))

# Optimizers
optimizer = torch.optim.Adam(net.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))

Tensor = torch.cuda.FloatTensor if cuda else torch.Tensor

train_loader = DataLoader(
    dataset=ImageDataset(inputs_root=in_folder + r"\train\inputs",
                         labels_root=in_folder + r"\train\labels"),
    batch_size=opt.batch_size,
    shuffle=True,
    num_workers=opt.n_cpu
)
test_loader = DataLoader(
    dataset=ImageDataset(inputs_root=in_folder + r"\test\inputs",
                         labels_root=in_folder + r"\test\labels"),
    batch_size=1,
    shuffle=True,
    num_workers=opt.n_cpu
)


# ----------
#  Training
# ----------
def train(epoch):
    for i, data in enumerate(train_loader):
        # 一个batch
        inputs, labels, inputs_path = data
        inputs = inputs.unsqueeze(1)
        labels = labels.unsqueeze(1)
        # 将这些数据转换成Variable类型
        inputs, labels = Variable(inputs), Variable(labels)
        device = torch.device("cuda" if cuda else "cpu")
        inputs = inputs.to(device)
        labels = labels.to(device)
        # ------------------
        #  Train net
        # ------------------

        optimizer.zero_grad()  # 梯度归零:step之前要进行梯度归零
        net.train()
        netout = net(inputs)

        # Total loss
        gloss = loss(netout, labels)

        gloss.backward()  # 进行反向传播求出每个参数的梯度
        optimizer.step()  # 更新学习率
##
        # --------------
        #  Log Progress
        # --------------

        sys.stdout.write(
            "\n[Epoch %d/%d] [Batch %d/%d] [D loss: %f]"
            % (epoch, opt.n_epochs, i, len(train_loader), gloss.item())
        )
        with open(r"../data/pigs/loss.txt", "a") as f1:
            f1.write("\n[Epoch %d/%d] [Batch %d/%d] [D loss: %f]"
                     % (epoch, opt.n_epochs, i, len(train_loader), gloss.item()))
            f1.close()


def test():
    total_s = 0  # ssim
    total_p = 0  # psnr
    total_r = 0  # rmse

    with torch.no_grad():
        for inputs, labels, inputs_path in test_loader:
            # 一个batch
            inputs = inputs.unsqueeze(1)
            # labels = labels.unsqueeze(0)
            # 将这些数据转换成Variable类型
            inputs, labels = Variable(inputs), Variable(labels)
            device = torch.device("cuda" if cuda else "cpu")
            inputs = inputs.to(device)
            labels = labels.squeeze(0).cpu().numpy()

            # optimizer.zero_grad()
            net.eval()
            netout = net(inputs)
            img_out = netout.squeeze(1)
            img_out = img_out.squeeze(0)
            img_out = img_out.cpu().numpy()

            # Total loss
            # gloss = loss(netout, labels)
            # print(netout.shape)
            s = ssim(labels, img_out)
            p = psnr(labels, img_out)
            r = sqrt(mse(labels, img_out))

            total_s += float(s.item())
            total_p += float(p.item())
            total_r += float(r)
    print('\n|Epoch %d/%d| |Average SSIM: %f | |Average PSNR: %f| |Average RMSE: %f| '
          % ((epoch + 1), opt.n_epochs, total_s / len(test_loader), total_p / len(test_loader),
             total_r / len(test_loader)))

    with open(r"../data/pigs/test_parameters.txt", "a") as f:
        f.write("|Epoch %d/%d| |Average SSIM: %f | |Average PSNR: %f| |Average RMSE: %f| \r\n"
                % ((epoch + 1), opt.n_epochs, total_s / len(test_loader), total_p / len(test_loader),
                   total_r / len(test_loader)))
        f.close()
    return total_s / len(test_loader), total_p / len(test_loader), total_r / len(test_loader)


if __name__ == '__main__':
    for epoch in range(opt.epoch, opt.n_epochs):
        # 一个epoch
        train(epoch)
        # Save model checkpoints
        # torch.save(net.state_dict(), "saved_models/generator_%d.pkl" % epoch)
        torch.save(net, "../saved_models/AdUnet_%d.pkl" % epoch)
        epoch_s, epoch_p, epoch_r = test()

Além da classe net anterior e da classe dataset, existem três arquivos no total, basta executar este arquivo e pronto!
"Hehe, é isso"

4. Perguntas e Respostas

① mkdir de import na função de cabeçalho

Desculpe esqueci de explicar, é o mkdir.py que escrevi na outra pasta, o conteúdo é o seguinte:

import os

def mkdir(path):
    if os.path.exists(path):
        return
    else:
        os.mkdir(path)

O principal motivo é que os.mkdir não verifica o arquivo, portanto, ao usá-lo geralmente, você deve primeiro verificar se o arquivo existe com os.path.exists e criá-lo se não existir. Eu simplesmente integrei esta função
e coloque-o em Em meu kit de ferramentas auto-editado outro, se você gosta de fazer suas próprias rodas, é bom escrever seu próprio kit de ferramentas e também pode dar um nome ao seu próprio kit de ferramentas, como myUtils.

Acho que você gosta

Origin blog.csdn.net/weixin_43938876/article/details/123471901
Recomendado
Clasificación