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.
Processo de Implementação da Segmentação Semântica
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:
- 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.
- 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.