PatchTST una svolta nella previsione delle serie temporali.

'PatchTST, a breakthrough in time series forecasting.'

Dalla teoria alla pratica, comprendere l’algoritmo PatchTST e applicarlo in Python insieme a N-BEATS e N-HiTS

Foto di Ray Hennessy su Unsplash

I modelli basati su Transformer sono stati applicati con successo in molti campi come l’elaborazione del linguaggio naturale (pensa ai modelli BERT o GPT) e la visione artificiale, per citarne alcuni.

Tuttavia, per quanto riguarda le serie temporali, i risultati di stato dell’arte sono stati principalmente ottenuti da modelli MLP (perceptron multistrato) come N-BEATS e N-HiTS. Un recente articolo mostra persino che i modelli di previsione basati su trasformatori complessi vengono surclassati da semplici modelli lineari su molti dataset di benchmark (vedi Zheng et al., 2022).

Tuttavia, è stato proposto un nuovo modello basato su Transformer che raggiunge risultati di stato dell’arte per le attività di previsione a lungo termine: PatchTST.

PatchTST sta per patch time series transformer ed è stato proposto per la prima volta nel marzo 2023 da Nie, Nguyen et al. nel loro articolo: A Time Series is Worth 64 Words: Long-Term Forecasting with Transformers . Il loro metodo proposto ha raggiunto risultati di stato dell’arte rispetto ad altri modelli basati su trasformatori.

In questo articolo, esploreremo innanzitutto il funzionamento interno di PatchTST, utilizzando l’intuizione e nessuna equazione. Quindi, applicheremo il modello in un progetto di previsione e ne valuteremo le prestazioni rispetto ai modelli MLP, come N-BEATS e N-HiTS.

Naturalmente, per ulteriori dettagli su PatchTST, assicurati di fare riferimento all’articolo originale.

Scopri le ultime tecniche di analisi delle serie temporali con il mio cheat sheet gratuito per le serie temporali in Python! Ottieni l’implementazione di tecniche di statistica e di apprendimento profondo, tutto in Python e TensorFlow!

Cominciamo!

Esplorazione di PatchTST

Come accennato, PatchTST sta per patch time series transformer.

Come suggerisce il nome, fa uso di patching e dell’architettura del Transformer. Include anche l’indipendenza dei canali per trattare le serie temporali multivariate. L’architettura generale è mostrata di seguito.

L'architettura del modello PatchTST. Vediamo che il modello fa uso di indipendenza dei canali per trattare le serie temporali multivariate. Nel backbone del trasformatore, vediamo anche l'uso del patching (illustrato dai rettangoli). Inoltre, ci sono due versioni del modello: supervisionata e auto-supervisionata. Immagine di Nie Y., Nguyen N., Sinthong P., Kalagnanam J. tratta da A Time Series is Worth 64 Words: Long-Term Forecasting with Transformers.

C’è molta informazione da ricavare dalla figura sopra. Qui, gli elementi chiave sono che PatchTST utilizza l’indipendenza dei canali per prevedere le serie temporali multivariate. Quindi, nel suo backbone del trasformatore, il modello utilizza il patching, che sono illustrati dai piccoli rettangoli verticali. Inoltre, il modello è disponibile in due versioni: supervisionata e auto-supervisionata.

Esploriamo in maggior dettaglio l’architettura e il funzionamento interno di PatchTST.

Indipendenza dei canali

Qui, una serie temporale multivariata è considerata come un segnale multi-canale. Ogni serie temporale è essenzialmente un canale contenente un segnale.

Panoramica del modello PatchTST. Qui, mettiamo davvero in evidenza l'implementazione dell'indipendenza dei canali, dove ogni token di input al backbone del trasformatore contiene informazioni solo da un canale o da una serie temporale. Immagine di Nie Y., Nguyen N., Sinthong P., Kalagnanam J. tratta da A Time Series is Worth 64 Words: Long-Term Forecasting with Transformers.

Nella figura sopra, vediamo come una serie temporale multivariata viene separata in singole serie, e ognuna viene alimentata al Transformer backbone come un token di input. Quindi, vengono fatte previsioni per ogni serie e i risultati vengono concatenati per le previsioni finali.

Patching

La maggior parte del lavoro sui modelli di previsione basati su Transformer si è concentrata sulla costruzione di nuovi meccanismi per semplificare il meccanismo di attenzione originale. Tuttavia, continuavano ancora a fare affidamento sull’attenzione punto per punto, che non è ideale per le serie temporali.

Nella previsione delle serie temporali, vogliamo estrarre le relazioni tra i passaggi temporali passati e quelli futuri per effettuare previsioni. Con l’attenzione punto per punto, stiamo cercando di recuperare informazioni da un singolo passaggio temporale, senza guardare ciò che circonda quel punto. In altre parole, isoliamo un passaggio temporale e non guardiamo i punti prima o dopo.

Questo è come cercare di capire il significato di una parola senza guardare le parole che la circondano in una frase.

Pertanto, PatchTST utilizza il patching per estrarre informazioni semantiche locali nelle serie temporali.

Come funziona il patching

Ogni serie di input viene divisa in patch, che sono semplicemente serie più corte che provengono da quella originale.

The Transformer backbone of PatchTST. Here, we see that the input time series (at the bottom of the figure) goes through patching, resulting in multiple patches (the vertical rectangles) which are then sent to the Transformer encoder. Image by Nie Y., Nguyen N., Sinthong P., Kalagnanam J. from A Time Series is Worth 64 Words: Long-Term Forecasting with Transformers .

Qui, il patch può essere sovrapposto o non sovrapposto. Il numero di patch dipende dalla lunghezza del patch P e dalla stride S. Qui, la stride è come nella convoluzione, è semplicemente il numero di passaggi temporali che separano l’inizio dei patch consecutivi.

Visualizing patching. Here, we have a sequence of 15 timesteps, with a patch length of 5 and a stride of 5 as well, resulting in three patches. Image by the author.

Nella figura sopra, possiamo visualizzare il risultato del patching. Qui, abbiamo una lunghezza di sequenza (L) di 15 passaggi temporali, con una lunghezza di patch (P) di 5 e una stride (S) di 5. Il risultato è che la serie viene separata in 3 patch.

Vantaggi del patching

Con il patching, il modello può estrarre il significato semantico locale guardando gruppi di passaggi temporali, invece di guardare un singolo passaggio temporale.

Ha anche il vantaggio aggiunto di ridurre notevolmente il numero di token alimentati all’encoder del transformer. Qui, ogni patch diventa un token di input da inserire nel Transformer. In questo modo, possiamo ridurre il numero di token da L a circa L/S.

In questo modo, riduciamo notevolmente la complessità spaziale e temporale del modello. Ciò significa a sua volta che possiamo alimentare il modello con una sequenza di input più lunga per estrarre relazioni temporali significative.

Pertanto, con il patching, il modello è più veloce, leggero e può trattare una sequenza di input più lunga, il che significa che potrebbe imparare di più sulla serie e fare previsioni migliori.

Encoder Transformer

Una volta che la serie è stata patchata, viene quindi alimentata all’encoder del transformer. Questa è l’architettura del transformer classico. Non è stato modificato nulla.

Quindi, l’output viene alimentato al livello lineare e vengono fatte previsioni.

Migliorare PatchTST con l’apprendimento di rappresentazioni

Gli autori del paper hanno suggerito un altro miglioramento al modello utilizzando l’apprendimento di rappresentazioni.

Visualizzazione dell'apprendimento di rappresentazioni auto-supervisionato in PatchTST. Qui, il modello maschera casualmente le patch e impara a ricostruirle. Immagine di Nie Y., Nguyen N., Sinthong P., Kalagnanam J. tratta da A Time Series is Worth 64 Words: Long-Term Forecasting with Transformers .

Dalla figura sopra, possiamo vedere che PatchTST può utilizzare l’apprendimento di rappresentazioni auto-supervisionato per catturare rappresentazioni astratte dei dati. Ciò può portare a potenziali miglioramenti delle prestazioni di previsione.

Qui, il processo è abbastanza semplice, poiché le patch casuali saranno mascherate, il che significa che saranno impostate a 0. Ciò è mostrato, nella figura sopra, dai rettangoli verticali vuoti. Quindi, il modello viene addestrato a ricostruire le patch originali, che è ciò che viene prodotto nella parte superiore della figura, come i rettangoli verticali grigi.

Ora che abbiamo una buona comprensione di come funziona PatchTST, testiamolo rispetto ad altri modelli e vediamo come si comporta.

Previsione con PatchTST

Nel paper, PatchTST viene confrontato con altri modelli basati su Transformer. Tuttavia, sono stati pubblicati recentemente modelli basati su MLP, come N-BEATS e N-HiTS, che hanno anche dimostrato prestazioni all’avanguardia nelle attività di previsione a lungo termine.

Il codice sorgente completo di questa sezione è disponibile su GitHub .

Qui, applichiamo PatchTST, insieme a N-BEATS e N-HiTS e valutiamo le sue prestazioni rispetto a questi due modelli basati su MLP.

Per questo esercizio, utilizziamo il dataset Exchange, che è un dataset di benchmark comune per la previsione a lungo termine nella ricerca. Il dataset contiene i tassi di cambio giornalieri di otto paesi rispetto al dollaro USA, dal 1990 al 2016. Il dataset è reso disponibile attraverso la licenza MIT.

Configurazione iniziale

Iniziamo importando le librerie necessarie. Qui, lavoreremo con neuralforecast, poiché hanno un’implementazione out-of-the-box di PatchTST. Per il dataset, utilizziamo la libreria datasetsforecast, che include tutti i dataset popolari per valutare gli algoritmi di previsione.

import torchimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom neuralforecast.core import NeuralForecastfrom neuralforecast.models import NHITS, NBEATS, PatchTSTfrom neuralforecast.losses.pytorch import MAEfrom neuralforecast.losses.numpy import mae, msefrom datasetsforecast.long_horizon import LongHorizon

Se hai CUDA installato, neuralforecast utilizzerà automaticamente la tua GPU per addestrare i modelli. Nel mio caso, non l’ho installato, motivo per cui non sto facendo un’ampia ricerca di iperparametri, o addestrando su dataset molto grandi.

Una volta fatto ciò, scarichiamo il dataset Exchange.

Y_df, X_df, S_df = LongHorizon.load(directory="./data", group="Exchange")

Qui vediamo che otteniamo tre DataFrames. Il primo contiene i tassi di cambio giornalieri per ogni paese. Il secondo contiene serie temporali esogene. Il terzo, contiene variabili esogene statiche (come giorno, mese, anno, ora o qualsiasi informazione futura che conosciamo).

Per questo esercizio, lavoriamo solo con Y_df.

Quindi, assicuriamoci che le date abbiano il tipo corretto.

Y_df['ds'] = pd.to_datetime(Y_df['ds'])Y_df.head()
Primi cinque righe del dataset Exchange. Immagine dell'autore.

Nella figura sopra, vediamo che abbiamo tre colonne. La prima colonna è un identificatore unico ed è necessario avere una colonna id quando si lavora con neuralforecast. Quindi, la colonna ds ha la data, e la colonna y ha il tasso di cambio.

Y_df['unique_id'].value_counts()
Mostra il numero di osservazioni per id univoco. Immagine dello stesso autore.

Dalla figura sopra, possiamo vedere che ogni id univoco corrisponde a un paese, e che abbiamo 7588 osservazioni per paese.

Ora, definiamo le dimensioni dei nostri set di validazione e di test. Qui, ho scelto 760 passaggi temporali per la validazione e 1517 per il set di test, come specificato dalla libreria datasets.

val_size = 760test_size = 1517print(n_time, val_size, test_size)

Poi, plottiamo una delle serie, per vedere con cosa stiamo lavorando. Qui, ho deciso di plottare la serie per il primo paese (unique_id = 0), ma sentiti libero di plottare un’altra serie.

u_id = '0'x_plot = pd.to_datetime(Y_df[Y_df.unique_id==u_id].ds)y_plot = Y_df[Y_df.unique_id==u_id].y.valuesx_plotx_val = x_plot[n_time - val_size - test_size]x_test = x_plot[n_time - test_size]fig, ax = plt.subplots(figsize=(12,8))ax.plot(x_plot, y_plot)ax.set_xlabel('Data')ax.set_ylabel('Tasso di cambio')ax.axvline(x_val, color='black', linestyle='--')ax.axvline(x_test, color='black', linestyle='--')plt.text(x_val, -2, 'Validazione', fontsize=12)plt.text(x_test,-2, 'Test', fontsize=12)plt.tight_layout()
Tasso di cambio giornaliero per il primo paese, dal 1990 al 2016. Immagine dello stesso autore.

Dalla figura sopra, vediamo che abbiamo dati abbastanza rumorosi senza una chiara stagionalità.

Modellizzazione

Dopo aver esplorato i dati, iniziamo la modellizzazione con neuralforecast.

Prima di tutto, dobbiamo impostare l’orizzonte. In questo caso, utilizzo 96 passaggi temporali, poiché questo orizzonte è anche utilizzato nella Paper PatchTST.

Quindi, per avere una valutazione equa di ogni modello, ho deciso di impostare la dimensione dell’input al doppio dell’orizzonte (quindi 192 passaggi temporali) e impostare il numero massimo di epoche a 50. Tutti gli altri iperparametri sono mantenuti ai loro valori predefiniti.

horizon = 96models = [NHITS(h=horizon,               input_size=2*horizon,               max_steps=50),         NBEATS(h=horizon,               input_size=2*horizon,               max_steps=50),         PatchTST(h=horizon,                 input_size=2*horizon,                 max_steps=50)]

Quindi, iniziamo l’oggetto NeuralForecast, specificando i modelli che vogliamo utilizzare e la frequenza delle previsioni, che in questo caso è quotidiana.

nf = NeuralForecast(models=models, freq='D')

Siamo ora pronti per fare previsioni.

Previsione

Per generare previsioni, utilizziamo il metodo cross_validation per utilizzare i set di validazione e di test. Restituirà un DataFrame con le previsioni di tutti i modelli e il valore vero associato.

preds_df = nf.cross_validation(df=Y_df, val_size=val_size, test_size=test_size, n_windows=None)
Primi cinque righe del DataFrame delle previsioni. Immagine dell'autore.

Come si può vedere, per ogni id, abbiamo le previsioni di ogni modello così come il valore reale nella colonna y.

Ora, per valutare i modelli, dobbiamo ridimensionare gli array dei valori effettivi e previsti per avere la forma (numero di serie, numero di finestre, orizzonte di previsione).

y_true = preds_df['y'].valuesy_pred_nhits = preds_df['NHITS'].valuesy_pred_nbeats = preds_df['NBEATS'].valuesy_pred_patchtst = preds_df['PatchTST'].valuesn_series = len(Y_df['unique_id'].unique())y_true = y_true.reshape(n_series, -1, horizon)y_pred_nhits = y_pred_nhits.reshape(n_series, -1, horizon)y_pred_nbeats = y_pred_nbeats.reshape(n_series, -1, horizon)y_pred_patchtst = y_pred_patchtst.reshape(n_series, -1, horizon)

Fatto ciò, possiamo opzionalmente tracciare le previsioni dei nostri modelli. Qui, tracciamo le previsioni nella prima finestra della prima serie.

fig, ax = plt.subplots(figsize=(12,8))ax.plot(y_true[0, 0, :], label='Vero')ax.plot(y_pred_nhits[0, 0, :], label='N-HiTS', ls='--')ax.plot(y_pred_nbeats[0, 0, :], label='N-BEATS', ls=':')ax.plot(y_pred_patchtst[0, 0, :], label='PatchTST', ls='-.')ax.set_ylabel('Tasso di cambio')ax.set_xlabel('Orizzonte di previsione')ax.legend(loc='best')plt.tight_layout()
Previsioni del tasso di cambio giornaliero per la prima serie, nella prima finestra. Immagine dell'autore.

Questa figura è un po’ deludente, poiché N-BEATS e N-HiTS sembrano avere previsioni molto distanti dai valori effettivi. Tuttavia, PatchTST, pur essendo anch’esso distante, sembra essere il più vicino ai valori effettivi.

Certo, dobbiamo prendere ciò con le pinze, perché stiamo visualizzando solo la previsione per una serie, in una finestra di previsione.

Valutazione

Quindi, valutiamo le prestazioni di ogni modello. Per replicare la metodologia del paper, utilizziamo sia la MAE che la MSE come metriche di prestazione.

data = {'N-HiTS': [mae(y_pred_nhits, y_true), mse(y_pred_nhits, y_true)],       'N-BEATS': [mae(y_pred_nbeats, y_true), mse(y_pred_nbeats, y_true)],       'PatchTST': [mae(y_pred_patchtst, y_true), mse(y_pred_patchtst, y_true)]}metrics_df = pd.DataFrame(data=data)metrics_df.index = ['mae', 'mse']metrics_df.style.highlight_min(color='lightgreen', axis=1)
Prestazioni di tutti i modelli. Qui, PatchTST raggiunge la MAE e la MSE più basse. Immagine dell'autore.

Nella tabella sopra, vediamo che PatchTST è il modello campione poiché raggiunge la MAE e la MSE più basse.

Certo, questo non è stato l’esperimento più approfondito, poiché abbiamo utilizzato solo un dataset e un orizzonte di previsione. Tuttavia, è interessante vedere che un modello basato su Transformer può competere con modelli MLP all’avanguardia.

Conclusioni

PatchTST è un modello basato su Transformer che utilizza il patching per estrarre il significato semantico locale nei dati delle serie temporali. Ciò consente al modello di essere più veloce da addestrare e di avere una finestra di input più lunga.

Ha raggiunto le prestazioni di stato dell’arte rispetto ad altri modelli basati su Transformer. Nel nostro piccolo esercizio, abbiamo visto che ha anche ottenuto prestazioni migliori rispetto a N-BEATS e N-HiTS.

Anche se questo non significa che sia migliore di N-HiTS o N-BEATS, rimane una opzione interessante quando si fa previsione su un orizzonte temporale lungo.

Grazie per la lettura! Spero che ti sia piaciuto e che tu abbia imparato qualcosa di nuovo!

Cheers 🍻

Riferimenti

Una serie temporale vale 64 parole: previsioni a lungo termine con i Transformer di Nie Y., Nguyen N. et al.

Neuralforecast di Olivares K., Challu C., Garza F., Canseco M., Dubrawski A.