Introduzione alle librerie di Deep Learning PyTorch e Lightning AI

Introduzione a PyTorch e Lightning AI per il Deep Learning

 

Il deep learning è un ramo del modello di apprendimento automatico basato su reti neurali. Nel modello di apprendimento automatico tradizionale, l’elaborazione dei dati per trovare le caratteristiche significative viene spesso effettuata manualmente o affidata all’esperienza nel campo; tuttavia, il deep learning può imitare il cervello umano per scoprire le caratteristiche essenziali, aumentando le prestazioni del modello.

Ci sono molte applicazioni per i modelli di deep learning, tra cui il riconoscimento facciale, il rilevamento delle frodi, la conversione del parlato in testo, la generazione di testo e molte altre. Il deep learning è diventato un approccio standard in molte applicazioni avanzate di apprendimento automatico e non abbiamo nulla da perdere imparando di più su di essi.

Per sviluppare questo modello di deep learning, ci sono varie librerie di framework su cui possiamo fare affidamento anziché partire da zero. In questo articolo, discuteremo due diverse librerie che possiamo utilizzare per sviluppare modelli di deep learning: PyTorch e Lighting AI. Iniziamo.

 

PyTorch

 

PyTorch è un framework open-source per addestrare reti neurali di deep learning. PyTorch è stato sviluppato dal gruppo Meta nel 2016 ed è diventato sempre più popolare. L’aumento di popolarità è stato grazie alla caratteristica di PyTorch che combina la libreria di backend GPU di Torch con il linguaggio Python. Questa combinazione rende il pacchetto facile da seguire per l’utente ma comunque potente nello sviluppo del modello di deep learning.

Ci sono alcune caratteristiche di spicco di PyTorch abilitate dalle librerie, tra cui un’interfaccia utente intuitiva, addestramento distribuito e un processo di sperimentazione rapido e flessibile. A causa del gran numero di utenti di PyTorch, lo sviluppo e l’investimento della comunità sono stati enormi. Ecco perché imparare PyTorch sarà vantaggioso nel lungo termine.

Il blocco di costruzione di PyTorch è il tensore, un array multidimensionale utilizzato per codificare tutti gli input, output e parametri del modello. Puoi immaginare un tensore come un array NumPy ma con la capacità di eseguire operazioni sulla GPU.

Proviamo la libreria PyTorch. È consigliabile eseguire il tutorial nel cloud, ad esempio su Google Colab, se non hai accesso a un sistema con GPU (anche se potrebbe funzionare anche con una CPU). Ma se preferisci iniziare in locale, dovremo installare la libreria tramite questa pagina. Seleziona il sistema e le specifiche appropriate che hai.

Ad esempio, il codice di seguito è per l’installazione tramite pip se hai un sistema con supporto CUDA.

pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

 

Dopo aver completato l’installazione, proviamo alcune funzionalità di PyTorch per sviluppare il modello di deep learning. In questo tutorial, svilupperemo un semplice modello di classificazione delle immagini con PyTorch, basato sul loro tutorial web. Analizzeremo il codice e spiegheremo cosa succede al suo interno.

Per prima cosa, scaricheremo il dataset con PyTorch. Per questo esempio, utilizzeremo il dataset MNIST, che è un dataset di classificazione di numeri scritti a mano.

from torchvision import datasets

train = datasets.MNIST(
    root="image_data",
    train=True,
    download=True
)

test = datasets.MNIST(
    root="image_data",
    train=False,
    download=True,
)

 

Scarichiamo sia il set di dati di addestramento che il set di dati di test di MNIST nella nostra cartella principale. Vediamo come appare il nostro dataset.

import matplotlib.pyplot as plt
 
for i, (img, label) in enumerate(list(train)[:10]):
    plt.subplot(2, 5, i+1)
    plt.imshow(img, cmap="gray")
    plt.title(f'Etichetta: {label}')
    plt.axis('off')
 
plt.show()

 

 

Ogni immagine è un numero compreso tra zero e nove, il che significa che abbiamo dieci etichette. Ora sviluppiamo un classificatore di immagini basato su questo dataset.

Dobbiamo trasformare il dataset di immagini in un tensore per sviluppare un modello di deep learning con PyTorch. Poiché la nostra immagine è un oggetto PIL, possiamo utilizzare la funzione ToTensor di PyTorch per eseguire la trasformazione. Inoltre, possiamo trasformare automaticamente l’immagine con la funzione dei dataset.

from torchvision.transforms import ToTensor
train = datasets.MNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
)

test = datasets.MNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

 

Passando la funzione di trasformazione al parametro di trasformazione, possiamo controllare come saranno i dati. Successivamente, incapsuleremo i dati nell’oggetto DataLoader in modo che il modello PyTorch possa accedere ai nostri dati sull’immagine.

from torch.utils.data import DataLoader
size = 64

train_dl = DataLoader(train, batch_size=size)
test_dl = DataLoader(test, batch_size=size)

for X, y in test_dl:
    print(f"Forma di X [N, C, H, W]: {X.shape}")
    print(f"Forma di y: {y.shape} {y.dtype}")
    break

 

Forma di X [N, C, H, W]: torch.Size([64, 1, 28, 28])
Forma di y: torch.Size([64]) torch.int64

 

Nel codice precedente, creiamo un oggetto DataLoader per i dati di train e test. Ogni iterazione del batch di dati restituirà 64 caratteristiche e etichette nell’oggetto sopra. Inoltre, la forma della nostra immagine è 28 * 28 (altezza * larghezza).

Successivamente, svilupperemo l’oggetto modello di rete neurale.

from torch import nn

# Cambia in 'cuda' se hai accesso alla GPU
device = 'cpu'

class NNModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.lr_stack = nn.Sequential(
            nn.Linear(28*28, 128),
            nn.ReLU(),
            nn.Linear(128, 128),
            nn.ReLU(),
            nn.Linear(128, 10)
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.lr_stack(x)
        return logits

model = NNModel().to(device)
print(model)

 

NNModel(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (lr_stack): Sequential(
    (0): Linear(in_features=784, out_features=128, bias=True)
    (1): ReLU()
    (2): Linear(in_features=128, out_features=128, bias=True)
    (3): ReLU()
    (4): Linear(in_features=128, out_features=10, bias=True)
  )
)

 

Nell’oggetto sopra, creiamo un modello neurale con poche strutture di layer. Per sviluppare l’oggetto di modello neurale, utilizziamo il metodo di subclassing con la funzione nn.module e creiamo i layer della rete neurale all’interno di __init__.

Inizialmente convertiamo i dati dell’immagine 2D in valori dei pixel all’interno del layer con la funzione flatten. Quindi, utilizziamo la funzione sequenziale per incapsulare il nostro layer in una sequenza di layer. All’interno della funzione sequenziale, abbiamo il nostro layer del modello:

nn.Linear(28*28, 128),
nn.ReLU(),
nn.Linear(128, 128),
nn.ReLU(),
nn.Linear(128, 10)

 

Per sequenza, quello che accade sopra è:

  1. Prima, l’input dei dati che consiste in 28*28 caratteristiche viene trasformato utilizzando una funzione lineare nel layer lineare e si ottengono 128 caratteristiche in output.
  2. ReLU è una funzione di attivazione non lineare che è presente tra l’input e l’output del modello per introdurre non linearità.
  3. 128 caratteristiche in input al layer lineare e 128 caratteristiche in output
  4. Un’altra funzione di attivazione ReLU
  5. 128 caratteristiche in input al layer lineare e 10 caratteristiche in output (il nostro dataset di etichette ha solo 10 etichette).

Infine, la funzione forward è presente per il processo di input effettivo per il modello. Successivamente, il modello avrà bisogno di una funzione di perdita e di un’ottimizzazione.

from torch.optim import SGD

loss_fn = nn.CrossEntropyLoss()
optimizer = SGD(model.parameters(), lr=1e-3)

 

Per il codice successivo, prepariamo solo l’addestramento e la preparazione del test prima di eseguire l’attività di modellazione.

import torch
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)
        pred = model(X)
        loss = loss_fn(pred, y)

        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

        if batch % 100 == 0:
            loss, current = loss.item(), (batch + 1) * len(X)
            print(f"loss: {loss:>2f}  [{current:>5d}/{size:>5d}]")

def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= num_batches
    correct /= size
    print(f"Errore di test: \n Accuratezza: {(100*correct):>0.1f}%, Perdita media: {test_loss:>2f} \n")

 

Ora siamo pronti per eseguire l’addestramento del nostro modello. Decideremo quante epoche (iterazioni) vogliamo eseguire con il nostro modello. Per questo esempio, diciamo che vogliamo eseguirlo cinque volte.

epoch = 5
for i in range(epoch):
    print(f"Epoch {i+1}\n-------------------------------")
    train(train_dl, model, loss_fn, optimizer)
    test(test_dl, model, loss_fn)
print("Fatto!")

 

 

Il modello ha ora terminato il suo addestramento ed è pronto per essere utilizzato per qualsiasi attività di previsione delle immagini. I risultati possono variare, quindi aspettatevi risultati diversi da quelli dell’immagine sopra.

Sono solo alcune delle cose che PyTorch può fare, ma si può vedere che costruire un modello con PyTorch è facile. Se siete interessati al modello pre-addestrato, PyTorch ha un hub a cui si può accedere.

 

Lighting AI

 

Lighting AI è un’azienda che offre vari prodotti per ridurre il tempo di addestramento del modello di deep learning PyTorch e semplificarlo. Uno dei loro prodotti open-source è PyTorch Lighting, che è una libreria che offre un framework per addestrare e distribuire il modello PyTorch.

Lighting offre alcune funzionalità, tra cui flessibilità del codice, nessun boilerplate, API minimali e miglior collaborazione in team. Lighting offre anche funzionalità come l’utilizzo di multi-GPU e l’addestramento rapido a bassa precisione. Questo rende Lighting una buona alternativa per sviluppare il nostro modello PyTorch.

Proviamo lo sviluppo del modello con Lighting. Per iniziare, è necessario installare il pacchetto.

pip install lightning

 

Con Lighting installato, installeremo anche un altro prodotto di Lighting AI chiamato TorchMetrics per semplificare la selezione delle metriche.

pip install torchmetrics

 

Con tutte le librerie installate, proveremo a sviluppare lo stesso modello del nostro esempio precedente utilizzando un wrapper di Lighting. Di seguito è riportato il codice completo per lo sviluppo del modello.

import torch
import torchmetrics
import pytorch_lightning as pl
from torch import nn
from torch.optim import SGD

# Cambia in 'cuda' se hai accesso alla GPU
device = 'cpu'

class NNModel(pl.LightningModule):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.lr_stack = nn.Sequential(
            nn.Linear(28 * 28, 128),
            nn.ReLU(),
            nn.Linear(128, 128),
            nn.ReLU(),
            nn.Linear(128, 10)
        )
        self.train_acc = torchmetrics.Accuracy(task="multiclass", num_classes=10)
        self.valid_acc = torchmetrics.Accuracy(task="multiclass", num_classes=10)

    def forward(self, x):
        x = self.flatten(x)
        logits = self.lr_stack(x)
        return logits

    def training_step(self, batch, batch_idx):
        x, y = batch
        x, y = x.to(device), y.to(device)
        pred = self(x)
        loss = nn.CrossEntropyLoss()(pred, y)
        self.log('train_loss', loss)
       
        # Calcola l'accuratezza dell'addestramento
        acc = self.train_acc(pred.softmax(dim=-1), y)
        self.log('train_acc', acc, on_step=True, on_epoch=True, prog_bar=True)
        return loss

    def configure_optimizers(self):
        return SGD(self.parameters(), lr=1e-3)

    def test_step(self, batch, batch_idx):
        x, y = batch
        x, y = x.to(device), y.to(device)
        pred = self(x)
        loss = nn.CrossEntropyLoss()(pred, y)
        self.log('test_loss', loss)
       
        # Calcola l'accuratezza del test
        acc = self.valid_acc(pred.softmax(dim=-1), y)
        self.log('test_acc', acc, on_step=True, on_epoch=True, prog_bar=True)
        return loss

 

Analizziamo cosa succede nel codice sopra. La differenza con il modello PyTorch sviluppato in precedenza è che ora la classe NNModel utilizza l’ereditarietà del LightingModule. Inoltre, assegniamo le metriche di accuratezza da valutare utilizzando il TorchMetrics. Quindi, abbiamo aggiunto il passaggio di addestramento e test all’interno della classe e configurato la funzione di ottimizzazione. 

Con tutti i modelli impostati, eseguiremo l’addestramento del modello utilizzando l’oggetto DataLoader trasformato per addestrare il nostro modello.

# Creare un trainer PyTorch Lightning
trainer = pl.Trainer(max_epochs=5)

# Creare il modello
model = NNModel()

# Adattare il modello
trainer.fit(model, train_dl)

# Testare il modello
trainer.test(model, test_dl)

print("Fine addestramento")

 

 

Con la libreria Lightning, possiamo facilmente modificare la struttura di cui abbiamo bisogno. Per ulteriori informazioni, è possibile consultare la loro documentazione.

 

Conclusioni

 

PyTorch è una libreria per lo sviluppo di modelli di deep learning e fornisce un framework semplice per accedere a molte API avanzate. Lighting AI supporta anche la libreria, che fornisce un framework per semplificare lo sviluppo del modello e migliorare la flessibilità di sviluppo. Questo articolo ci ha introdotto alle caratteristiche della libreria e all’implementazione di codice semplice.     Cornellius Yudha Wijaya è un assistente manager di data science e scrittore di dati. Mentre lavora a tempo pieno presso Allianz Indonesia, ama condividere consigli su Python e dati tramite i social media e i media scritti.