menu
Paolo Avogadro

Pytorch 9 Autoencoder

Posted on 15/10/2021, in Italiano. Reading time: 18 mins

Indice Globale degli argomenti tra i vari post:

 1.1. Indice degli argomenti.
 1.1. Introduzione e fonti.
 1.1. Lingo - Gergo utilizzato.
 1.2. Tensori in Pytorch.
 2.1. Chainrule e Autograd.
 2.2. Backpropagation.
 3.1. Loss e optimizer.
 3.2. Modelli di Pytorch.
 3.3. Dataset e Dataloader.
 3.4. Dataset Transforms.
 3.5. Softmax e Cross-Entropy Loss.
 3.6. Activation Function.
 4.1. Feed Forward Neural Network.
 5.1. Convolutional Neural Network.
 5.2. Transfer Learning.
 5.3. Tensorboard.
 6.1. I/O Saving and Loading Models.
 7.1. Recurrent Neural Networks.
 7.2. RNN, GRU e LSTM.
 8.1. Pytorch Lightning.
 8.2. LR Scheduler.
 9.1. Autoencoder.

Autoencoder

Video-lezione di Python Engineer con una spiegazione su come scrivere i codici e una introduzione alla teoria.

La teoria viene spiegata molto bene in questa video-lezione del corso MIT 6.S191, dove vengono anche introdotti ulteriori concetti come i Variational AutoEncoders (VAE).

Un autoencoder e’ una rete neurale che cerca di “riassumere i tratti principali dell’input. L’idea e’ molto sempice, Pensiamo a delle immagini. Si prende e si fanno vari strati che hanno via via meno parametri. A quel punto si fanno i passi inversi (letteralemente) in modo da riottenere un medesimo numero di valori finali. Come funzione di Loss si usa una MSE. E’ intuitivo. se ho una immagine in ingresso voglio vedere la STESSA immagine in uscita (o almeno avvicinarmi)

Risorsa da cui sono in pratica presi i codici: https://www.cs.toronto.edu/~lczhang/360/lec/w05/autoencoder.html

Qui il corso da cui sono prese i codici particolari dell’autoencoder: https://www.cs.toronto.edu/~lczhang/360/

Senza convoluzioni

import torch
import torch.nn as nn  
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt

transform = transforms.ToTensor()
mnist_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
data_loader = torch.utils.data.DataLoader(dataset =mnist_data,
                                         batch_size=64, shuffle=True)

type(mnist_data[1])
len(mnist_data[59999][0][0])
type(mnist_data[59999][0][0].numpy())
mnist_data[59999][0][0].numpy().size
784
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 
dataiter  = iter(data_loader)
images, labels = dataiter.next()
class Autoencoder (nn.Module):
    def __init__(self):
        super().__init__()
        # N= 784 (28x28)
        self.encoder = nn.Sequential(      # funzione di pytorch che fa andare uno dopo l'altro varie funzioni
            nn.Linear(28*28,128),           # numero di punti in ingresso (28*28) e neuroni in uscita(128)
            nn.ReLU(()), 
            nn.Linear(128,64),              
            nn.ReLU(()),             
            nn.Linear(64,12),               
            nn.ReLU(()),             
            nn.Linear(12,6),                # 
        )
        
        self.decoder = nn.Sequential(      # funzione di pytorch che fa andare uno dopo l'altro varie funzioni
            nn.Linear(6,12),           # numero di punti in ingresso (28*28) e neuroni in uscita(128)
            nn.ReLU(()), 
            nn.Linear(12,64),              
            nn.ReLU(()),             
            nn.Linear(64,128),               
            nn.ReLU(()),             
            nn.Linear(128,28*28)                # 
        )
        
    
    def forward (self,x):
        encoded = self.encoder(x)
        decoded = self.decoder(encoded)
        return decoded
model = Autoencoder ().to(device)
criterion = nn.MSELoss()
optimizer  = torch.optim.Adam(model.parameters(), lr= 1e-3 , weight_decay = 1e-5)
num_epochs = 10
outputs = []  
for epoch in range(num_epochs):
    for (img, _) in data_loader:
        img   = img.reshape(-1,28*28).to(device)
        recon = model(img).to(device)
        loss = criterion(recon, img)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        
        
    print(f'Epoch: {epoch+1}, Loss:{loss.item():.4f}')
    outputs.append((epoch, img, recon))
Epoch: 1, Loss:0.0426
Epoch: 2, Loss:0.0405
Epoch: 3, Loss:0.0284
Epoch: 4, Loss:0.0297
Epoch: 5, Loss:0.0298
Epoch: 6, Loss:0.0283
Epoch: 7, Loss:0.0273
Epoch: 8, Loss:0.0265
Epoch: 9, Loss:0.0249
Epoch: 10, Loss:0.0265

ci sono dei problemi di visualizzazione rispetto al codice scritto da Python Engineer. Riguardando un po’ la struttura ho trovato la soluzione

#print(len(outputs[0]) )
outputs[1][2].shape
varie = outputs[0][2].detach().reshape(-1,28*28).numpy()
u=varie[0,:].reshape(28,28)
u.shape
plt.imshow(u)
#plt.imshow(outputs[0][2].detach().reshape(-1,28*28).numpy())
<matplotlib.image.AxesImage at 0x1d0474e0730>

png

for k in range(0, num_epochs,4):
    plt.figure(figsize= (9,2))
    plt.gray() 
    imgs  = outputs[k][1].detach().numpy()
    recon = outputs[k][2].detach().numpy()
    
    for i, item in enumerate(imgs):
        if i>=9:break               # prendi solo le prime 9
        plt.subplot(2,9,i+1)
        item = item.reshape(-1,28*28)
        plt.imshow(item[0].reshape(28,28))
    
    for i, item in enumerate(recon):
        if i>=9:break               # prendi solo le prime 9
        plt.subplot(2,9,9+i+1)
        item = item.reshape(-1,28*28)
        plt.imshow(item[0].reshape(28,28))

png

png

png

Con Convoluzioni

qui sotto prendo un modello migliore, che sfrutti le convoluzioni e riesca a dare dei risultati piu’ precisi nella ricostruzione.

import torch
import torch.nn as nn  
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt

transform = transforms.ToTensor()
mnist_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
data_loader = torch.utils.data.DataLoader(dataset =mnist_data,
                                         batch_size=64, shuffle=True)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 
dataiter  = iter(data_loader)
images, labels = dataiter.next()
class Autoencoder (nn.Module):
    def __init__(self):
        super().__init__()
        # N= 784 (28x28)
        self.encoder = nn.Sequential(      # funzione di pytorch che fa andare uno dopo l'altro varie funzioni
            nn.Conv2d(1, 16, 3, stride=2, padding =1),    # canali input, output, kernel, stride, padding | N 16 14 14
            nn.ReLU(()), 
            nn.Conv2d(16,32, 3, stride=2, padding =1),    # N 32 7  7         
            nn.ReLU(()),             
            nn.Conv2d(32,64, 7 )    # N 64  1  1 (64 parametri in uscita)             
        )
        
        self.decoder = nn.Sequential(      # funzione di pytorch che fa andare uno dopo l'altro varie funzioni
            nn.ConvTranspose2d(64, 32, 7),   # N 32 7 7   
            nn.ReLU(()), 
            nn.ConvTranspose2d(32, 16, 3, stride=2, padding =1, output_padding=1),   # N 16 14 14                 
            nn.ReLU(()),             
            nn.ConvTranspose2d(16,  1, 3, stride=2, padding =1, output_padding=1),   # N 1 28 28                  
            nn.Sigmoid()             
        )
        
    
    def forward (self,x):
        encoded = self.encoder(x)
        decoded = self.decoder(encoded)
        return decoded
    
# nn.MaxPool2d  e  nn.MaxUnpool2d    
model = Autoencoder ()   .to(device)
criterion = nn.MSELoss()
optimizer  = torch.optim.Adam(model.parameters(), lr= 1e-3 , weight_decay = 1e-5)
num_epochs = 10
outputs = []  
for epoch in range(num_epochs):
    for (img, _) in data_loader:
        img   = img.to(device)          #.to(device)
        #recon = model(img)             #.to(device)
        recon = model (img).to(device)
        loss = criterion(recon, img)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        
        
    print(f'Epoch: {epoch+1}, Loss:{loss.item():.4f}')
    outputs.append((epoch, img, recon))
Epoch: 1, Loss:0.0097
Epoch: 2, Loss:0.0062
Epoch: 3, Loss:0.0047
Epoch: 4, Loss:0.0039
Epoch: 5, Loss:0.0036
Epoch: 6, Loss:0.0035
Epoch: 7, Loss:0.0028
Epoch: 8, Loss:0.0030
Epoch: 9, Loss:0.0031
Epoch: 10, Loss:0.0027
for k in range(0, num_epochs,4):
    plt.figure(figsize= (9,2))
    plt.gray() 
    imgs  = outputs[k][1].cpu().detach().numpy()
    recon = outputs[k][2].cpu().detach().numpy()
    
    for i, item in enumerate(imgs):
        if i>=9:break               # prendi solo le prime 9
        plt.subplot(2,9,i+1)
        item = item.reshape(-1,28*28)
        plt.imshow(item[0].reshape(28,28))
    
    for i, item in enumerate(recon):
        if i>=9:break               # prendi solo le prime 9
        plt.subplot(2,9,9+i+1)
        item = item.reshape(-1,28*28)
        plt.imshow(item[0].reshape(28,28))

png

png

png

My Playground

  • nota che quando fai andare prima il modello lineare e poi quello convoluzionale due volte i parametri continuano ad aggiornarsi, quindi la seconda volta ottieni dei valori piu’ precisi!
  • per esempio provo a mettere un parametro: min_par che indica il numero minimo di parametri (nell’esempio e’ 64, io provo 32, ecc
import torch
import torch.nn as nn  
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt

transform = transforms.ToTensor()
mnist_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
data_loader = torch.utils.data.DataLoader(dataset =mnist_data,
                                         batch_size=64, shuffle=True)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 
dataiter  = iter(data_loader)
images, labels = dataiter.next()
min_par=4

class Autoencoder (nn.Module):
    def __init__(self):
        super().__init__()
        # N= 784 (28x28)
        self.encoder = nn.Sequential(      # funzione di pytorch che fa andare uno dopo l'altro varie funzioni
            nn.Conv2d(1, 16, 3, stride=2, padding =1),    # canali input, output, kernel, stride, padding | N 16 14 14
            nn.ReLU(()), 
            nn.Conv2d(16,32, 3, stride=2, padding =1),    # N 32 7  7         
            nn.ReLU(()),             
            nn.Conv2d(32,min_par, 7 )    # N 64  1  1 (64 parametri in uscita)             
        )
        
        self.decoder = nn.Sequential(      # funzione di pytorch che fa andare uno dopo l'altro varie funzioni
            nn.ConvTranspose2d(min_par, 32, 7),   # N 32 7 7   
            nn.ReLU(()), 
            nn.ConvTranspose2d(32, 16, 3, stride=2, padding =1, output_padding=1),   # N 16 14 14                 
            nn.ReLU(()),             
            nn.ConvTranspose2d(16,  1, 3, stride=2, padding =1, output_padding=1),   # N 1 28 28                  
            nn.Sigmoid()             
        )
        
    
    def forward (self,x):
        encoded = self.encoder(x)
        decoded = self.decoder(encoded)
        return decoded
    
# nn.MaxPool2d  e  nn.MaxUnpool2d    
model = Autoencoder ()   .to(device)
criterion = nn.MSELoss()
optimizer  = torch.optim.Adam(model.parameters(), lr= 1e-3 , weight_decay = 1e-5)
num_epochs = 10
outputs = []  
for epoch in range(num_epochs):
    for (img, _) in data_loader:
        img   = img.to(device)          #.to(device)
        #recon = model(img)             #.to(device)
        recon = model (img).to(device)
        loss = criterion(recon, img)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        
        
    print(f'Epoch: {epoch+1}, Loss:{loss.item():.4f}')
    outputs.append((epoch, img, recon))
Epoch: 1, Loss:0.0467
Epoch: 2, Loss:0.0362
Epoch: 3, Loss:0.0390
Epoch: 4, Loss:0.0337
Epoch: 5, Loss:0.0352
Epoch: 6, Loss:0.0327
Epoch: 7, Loss:0.0348
Epoch: 8, Loss:0.0359
Epoch: 9, Loss:0.0347
Epoch: 10, Loss:0.0356
for k in range(0, num_epochs,4):
    plt.figure(figsize= (9,2))
    plt.gray() 
    imgs  = outputs[k][1].cpu().detach().numpy()
    recon = outputs[k][2].cpu().detach().numpy()
    
    for i, item in enumerate(imgs):
        if i>=9:break               # prendi solo le prime 9
        plt.subplot(2,9,i+1)
        item = item.reshape(-1,28*28)
        plt.imshow(item[0].reshape(28,28))
    
    for i, item in enumerate(recon):
        if i>=9:break               # prendi solo le prime 9
        plt.subplot(2,9,9+i+1)
        item = item.reshape(-1,28*28)
        plt.imshow(item[0].reshape(28,28))

png

png

png


Top