Salta al contenuto principale

Reti neurali ricorrenti (RNN) e LSTM in Pytorch per il testo

Profile picture for user luca77king

Negli ultimi anni, il Natural Language Processing (NLP) ha subito una trasformazione radicale grazie all’uso di reti neurali profonde. Uno dei concetti fondamentali nel trattamento del linguaggio è la sequenzialità: le parole non sono elementi isolati, ma dipendono fortemente dal contesto. Per affrontare questo tipo di dati, si usano modelli progettati per gestire sequenze, come le Reti Neurali Ricorrenti (RNN) e le loro evoluzioni, in particolare le Long Short-Term Memory (LSTM). Con PyTorch, l’implementazione di queste reti diventa diretta e potente, permettendo di costruire modelli in grado di analizzare e generare testo, classificare documenti, riconoscere il sentiment e molto altro.

Cosa sono le RNN

Una RNN è una rete neurale che, a differenza delle reti standard feedforward, mantiene una memoria interna aggiornata ad ogni input. Questa memoria è uno stato nascosto che evolve nel tempo, permettendo alla rete di “ricordare” elementi precedenti della sequenza. Questo rende le RNN ideali per dati sequenziali come frasi, testi, audio, dove l'ordine conta. L’architettura è composta da un layer ricorrente che riceve in input lo stato precedente e il nuovo token, aggiornando lo stato e generando un output.

Formalmente, a ogni passo temporale t, la rete aggiorna lo stato così:

ht=f(Whht−1+Wxxt+b)

dove h_t è lo stato attuale, x_t è l’input corrente, W_h e W_x sono i pesi della rete, b è il bias e f è una funzione di attivazione non lineare. Ma le RNN standard incontrano un ostacolo: quando le sequenze diventano lunghe, i gradienticalcolati durante il training tendono ad annullarsi (vanishing gradient), impedendo l’apprendimento delle dipendenze a lungo termine.

Perché servono le LSTM

Le LSTM risolvono il problema del vanishing gradient grazie a un'architettura più sofisticata. Introducono tre porte: la porta di input, la porta di forget e la porta di output. Queste porte sono funzioni sigmoidi che regolano quanto dell'informazione corrente, passata o futura deve essere conservata o dimenticata. In questo modo, le LSTM riescono a mantenere la memoria anche su sequenze lunghe.

L’idea chiave è lo stato della cella, una linea diretta che attraversa tutta la sequenza e può conservare informazioni per molti passi temporali, controllata dalle porte. Questo meccanismo rende le LSTM estremamente efficaci in compiti come traduzione automatica, generazione di testo, classificazione del sentiment e question answering.

Implementare un modello LSTM in PyTorch

PyTorch fornisce un modulo nativo nn.LSTM che gestisce internamente tutta la logica delle porte e degli stati. Vediamo un esempio di classificazione testuale binaria (es. sentiment positivo/negativo):

import torch
import torch.nn as nn

class LSTMClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, output_dim):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim)

    def forward(self, x):
        embedded = self.embedding(x)                        # [batch_size, seq_len, embed_dim]
        _, (hidden, _) = self.lstm(embedded)                # hidden: [1, batch_size, hidden_dim]
        return self.fc(hidden[-1])                          # output: [batch_size, output_dim]

model = LSTMClassifier(vocab_size=10000, embed_dim=128, hidden_dim=256, output_dim=2)

inputs = torch.randint(0, 10000, (32, 50))                 # batch da 32 frasi, ciascuna lunga 50 token
outputs = model(inputs)                                    # [32, 2]

In questo modello:

  • Ogni parola viene trasformata in un vettore tramite un layer Embedding.

  • Gli embedding vengono processati da una LSTM.

  • Si usa l’ultimo stato nascosto della LSTM per la predizione finale.

  • Il layer fully-connected finale proietta lo stato su due classi.

Addestramento del modello

Per addestrare questo modello su un dataset di testo etichettato, bastano pochi passaggi:

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

for epoch in range(10):
    optimizer.zero_grad()
    output = model(inputs)
    loss = criterion(output, torch.randint(0, 2, (32,)))   # etichette fittizie
    loss.backward()
    optimizer.step()

Applicazioni delle LSTM nel NLP

Le LSTM possono essere usate in quasi tutti i task NLP dove è presente una sequenza:

  • Analisi del sentiment: determinare l’opinione in un testo.

  • Traduzione automatica: sequenza in input → sequenza in output.

  • Riconoscimento del parlato: segnale audio → testo.

  • Generazione di testo: predire il token successivo dato un contesto.

  • Tagging sequenziale: POS tagging, NER, chunking.

Conclusione

Le LSTM sono una soluzione potente al problema del vanishing gradient delle RNN. La loro architettura a porte permette di imparare a conservare informazioni rilevanti per molteplici passi temporali. PyTorch, con le sue API chiare e flessibili, rende l’implementazione e l’addestramento di modelli LSTM immediato, aprendo la strada a soluzioni concrete in tutti gli ambiti del NLP moderno.

Assistente virtuale (BETA)