menu
Paolo Avogadro

Pytorch 8 Lightning

Posted on 15/10/2021, in Italiano. Reading time: 10 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.

PyTorch Lightning

Video-lezione di Python Engineer.

PyTorch Lightning e’ un wrapper per velocizzare la scrittura di reti neurali, con Pytorch.

Sito di Pytorch Lightning https://www.pytorchlightning.ai/

Per istallarlo: conda install pytorch-lightning -c conda-forge

Non e’ piu’ necessario:

  • model.train() (ovvero settare il modello in training mode)
  • model.eval() (ovvero settare il modello in evaluation mode)
  • definire una device e fare model.to(device) si puo’ “sconnettere” la GPU facilmente
  • optimizer.zero_grad()
  • loss.backwards()
  • optimizer.step()
  • with torch.nograd()
  • x= x.detach

Bonus

  • stampa consigli e aiuti!
  • supporto Tensorboard: viene costruito un folder chiamato lightning_logs. Per usare tensorboard tensorboard --logdir lightning_logs (e’ il nome della dir creata in automatico, nel video fa un errore e scrive log_dir)

A questo punto per fare inspecting del training, creiamo un altro dict.

Usiamo il codice del tutorial 13 e lo modifichiamo per PyTorch Lightning.

Suggerimenti: -suggerisce di usare il metodo validation_epoch_end() per accumulare statistiche. Nota che nel video Loeber copia i metodi dal sito e li modifica per l’occasione. Questo metodo viene poi piazzato all’interno del modello

Domande: mi viene GPU present = True, used False, devo accendere l’uso della GPU. Si va nel metodo Trainer e si mette: trainer=Trainer(gpus=1, max_epochs=num_epochs, fast_dev_run =True). Ok ho controllato e ora la GPU e’ presente e usata… ma il tempo di esecuzione praticamente non cambia!

  • Si puo’ usare anche una TPU e anche un distributed backend una DDP
  • Si puo’ passare a precisione 16 bit
  • in Trainer si puo’ mettere anche il karg: auto_lr_find =True per il learning rate
  • in Trainer si puo’ mettere anche il karg: deterministic =True per riprodurre esattamente i risultati
  • in Trainer si puo’ mettere anche il karg: gradient_clip_val =0.3 (un numero tra 0 e 1 per fare clipping dei gradienti)

Problemi:

  • nel video si vede la loss che scende in basso a dx mentre per me e’ un NAN. Ok il problema era che non facevo “ritornare” nulla dal metodo training_step che invece deve restituire un dict della forma {‘loss’:loss}, che viene preso direttamente dal PL e mostrato nella barra sotto
  • non vedo apparire la fase di validazione con una barra che si riempie (nel video c’e’)
  • mi da il seguente warning: UserWarning: The {log:dict keyword} was deprecated in 0.9.1 and will be removed in 1.0.0 Please use self.log(…) inside the lightningModule instead. #log on a step or aggregate epoch metric to the logger and/or progress bar (inside LightningModule) self.log(‘train_loss’, loss, on_step=True, on_epoch=True, prog_bar=True) warnings.warn(*args, **kwargs)
  • In tensor borad non riesco a trovare train_loss (e’ uno scalare ma non lo trovo)
import numpy as np
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import time

import torch.nn.functional as F 

import pytorch_lightning as pl # PL
from pytorch_lightning import Trainer 

%matplotlib inline

# device config
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')


# Hyper parametri 
input_size = 28*28      #  =784 sono le dimensioni delle immagini
hidden_size  = 500      #  scelto da me
num_classes = 10        #  devo classificare immagini di numeri
num_epochs = 2          #  quanti giri completi vengono fatti
batch_size = 100        #  questo non so come sia stato scelto
learning_rate = 0.001   #  piccolo

#train_dataset = torchvision.datasets.MNIST(root= './data/', train= True, transform =transforms.ToTensor(), download = True)
#test_dataset = torchvision.datasets.MNIST(root= './data/', train= False, transform =transforms.ToTensor(), download = False) # ho gia' scaricato tutto con il train_datast

#train_loader = torch.utils.data.DataLoader(dataset= train_dataset, batch_size = batch_size, shuffle=True)
#test_loader = torch.utils.data.DataLoader (dataset=  test_dataset, batch_size = batch_size, shuffle=False)


############  MODELLO ####################


#class NeuralNet(nn.Module):             #vecchi
class LitNeuralNet(pl.LightningModule):  # nome scelto da noi, pl.LightningModule e' la versione super di nn.Module    
    def __init__(self, input_size, hidden_size, num_classes):
        super(LitNeuralNet, self).__init__()
        self.input_size = input_size
        self.l1 = nn.Linear(input_size, hidden_size)  
        self.relu = nn.ReLU()
        self.l2 = nn.Linear(hidden_size, num_classes)
       
    def forward (self, x):    # x e' l'input.
        out = self.l1(x)      # 
        out = self.relu(out)  # 
        out = self.l2(out)
        return out            # 

    
    def training_step(self, batch, batch_idx):  # PL non uso piu' i loop!!!! 
        #x,y = batch           # unpack
        images, labels  =batch # PL unpack
        images = images.reshape(-1,28*28)   # non e' piu' necessairo to(device)
        
        #y_hats = self(x)     # ????
        outputs  = self(images)  # PL questo e' il forward pass! usiamo self perche' usiamo direttamente questo modulo
        
        #loss = F.cross_entropy(y_hat, y)
        loss =F.cross_entropy(outputs, labels)
        
        tensorboard_logs = {'train_loss':loss}    # devo metterlo anche nel validatio_epoch_end
        return {'loss':loss, 'log':tensorboard_logs}   # PL i nomi delle chiavi sono FISSI: e' log non Log
        #return {'loss': loss}
    
    
    
    def configure_optimizers(self):  # PL ma il nome e' fissato da PL? penso di si'
        #optimizer = torch.optim.Adam(self.parameters(), lr = learning_rate)  # non ho capito se serve o no...   
        return torch.optim.Adam(self.parameters(), lr= 0.001)  #  PL self e' l'istanza del modello
    
    def train_dataloader(self):
        #dataset = MNIST(os.getcwd(), train=True, download=True, transform =transforms.ToTensor())
        #loader = DataLoader (dataset, batch_size= 32, num_workers=1, shuffle=True)   #lui ha messo num_workers = 4
        train_dataset = torchvision.datasets.MNIST(root= './data/', train= True, transform =transforms.ToTensor(), download = True)
        train_loader = torch.utils.data.DataLoader(dataset= train_dataset, batch_size = batch_size, num_workers=4, shuffle=True)
        return train_loader
    
    # il nome deve essere questo!
    def val_dataloader(self):
        val_dataset = torchvision.datasets.MNIST(root= './data/', train= False, transform =transforms.ToTensor(), download = False) # ho gia' scaricato tutto con il train_datast
        val_loader = torch.utils.data.DataLoader (dataset= val_dataset, batch_size = batch_size, num_workers=4, shuffle=False)
        return val_loader    
    
    
    # questo viene eseguito dopo ogni epoch di validazione
    def validation_epoch_end(self, outputs):
        avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()
     
        tensorboard_logs  = {'tavg_val_loss': avg_loss} # PL per ora non lo guardiamo
        return {'tavg_val_loss': avg_loss, 'log':tensorboard_logs}
        #return {'val_loss': avg_loss}    
    

start = time.time()
#trainer = Trainer(max_epochs = num_epochs, fast_dev_run = False)  # PL Trainer e' importato da PL
trainer = Trainer(gpus=1,max_epochs = num_epochs, fast_dev_run = False)  # PL Trainer e' importato da PL

model = LitNeuralNet(input_size, hidden_size, num_classes)        # PL istanzio il modello  
trainer.fit(model)                                                # faccio il training sul modello
print("Tempo training+test= ", time.time()-start)    
    

GPU available: True, used: True
TPU available: None, using: 0 TPU cores

  | Name | Type   | Params
--------------------------------
0 | l1   | Linear | 392 K 
1 | relu | ReLU   | 0     
2 | l2   | Linear | 5.0 K 
--------------------------------
397 K     Trainable params
0         Non-trainable params
397 K     Total params
1.590     Total estimated model params size (MB)



HBox(children=(HTML(value='Training'), FloatProgress(value=1.0, bar_style='info', layout=Layout(flex='2'), max…



Tempo training+test=  12.651966094970703

LR Scheduler

video di riferimento di Python Engineer.

Vediamo come sfruttare le funzioni di Pytorch che automaticamente modificano il learning rate per ottenere dei risultati ottimali.

Spesso (intuitivamente) si vuole diminuire il Learning Rate. La logica a mio avviso e’ la seguente, quando mi avvicino al minimo se non diminuisco il LR rischio di saltare da una parte all’altra del minimo stesso.

Per questo si usa uno scheduler. Onestamente non mi pare ultra necessario, uno puo’ costruire delle funzioni custom che facciano la cosa ogni volta. Anzi se uso una funzione che non ho scritto io c’e’ la possibilita’ che io non controlli perfertamente.

Una interessante e’ che si riduce solo se una certa metrica ha raggiunto un plateau.

Osservazioni

  • in python // e’ floor division.

Top