Come codificare le caratteristiche temporali periodiche

Codifica delle caratteristiche temporali periodiche

Elaborazione attenta delle date, dei giorni della settimana e degli orari per il deep learning e altri modelli di previsione.

Figura dell'autore

Molte attività di previsione richiedono informazioni temporali come input del modello. Pensate ad un modello di regressione per prevedere le vendite di limonata di una società di vendita al dettaglio (potreste ricordare l’esempio dal mio articolo sulle caratteristiche arricchite di contesto). La domanda di bevande rinfrescanti è ovviamente maggiore in estate, con una curva periodica delle vendite con picchi in luglio/agosto (considerando una località in Europa qui).

In questo caso, il tempo nell’anno è ovviamente un’informazione stagionale preziosa che dovremmo alimentare nel modello. Ma come dovremmo fare? Le date sono difficili, il numero dei giorni cambia a seconda del mese (e per febbraio anche a seconda dell’anno) e sono presenti in vari formati:

13 gen 2023

13.01.2023

2023/03/13

Prima di tutto, possiamo omettere l’anno. Per tener conto di un effetto stagionale, abbiamo solo bisogno del giorno e del mese. In un approccio molto semplice (e non molto ponderato), potremmo semplicemente inserire il mese come un numero e il giorno come un altro numero.

Perché è una cattiva idea? Il modello dovrebbe imparare come funziona il calendario cristiano gregoriano (circa 30 giorni al mese, 12 mesi all’anno, anni bisestili, ecc.). Con un numero sufficiente di dati di addestramento, un modello di deep learning sarà sicuramente in grado di “capire” il nostro calendario. “Capire” significa in questo caso: il modello può dedurre la posizione temporale relativa dell’anno dagli input del mese e della data. Ma dovremmo rendere l’apprendimento il più facile possibile per il nostro modello e prendere su di noi questo compito (almeno noi sappiamo già come funziona il calendario). Utilizziamo la libreria datetime di Python e calcoliamo il tempo relativo all’interno dell’anno con una logica molto semplice: import datetime as

from datetime import datetimeimport calendaryear = 2023month = 12day = 30passed_days = (datetime(year, month, day) - datetime(year, 1, 1)).days + 1nr_of_days_per_year= 366 if calendar.isleap(year) else 365position_within_year = passed_days / nr_of_days_per_year

La caratteristica risultante postion_within_year con un intervallo di valori da vicino a 0,0 (1 gennaio) a 1,0 (31 dicembre) è molto più facile da interpretare per il modello rispetto al (maledetto complicato) calendario gregoriano.

Ma non è ancora ideale. La caratteristica position_within_year descrive un modello a “dente di sega” con un salto netto da 1,0 a 0,0 ad ogni cambio dell’anno. Questa discontinuità brusca può essere un problema per l’apprendimento efficace. Il 31 dicembre e il 1 gennaio sono date molto simili: sono vicini diretti e hanno molto in comune (ad esempio, condizioni meteorologiche simili) e probabilmente hanno un potenziale simile per le vendite di limonata. Tuttavia, la caratteristica position_within_year non riflette questa similitudine per il 31 dicembre e il 1 gennaio; infatti, è diversa quanto possibile.

Idealemente, i punti temporali in prossimità dovrebbero avere valori temporali simili. In qualche modo dobbiamo progettare una caratteristica che rappresenti la natura ciclica dell’anno. In altre parole, il 31 dicembre dovremmo arrivare alla posizione in cui abbiamo iniziato il 1 gennaio. Quindi, ovviamente, ha senso modellare la posizione all’interno dell’anno come la posizione su un cerchio. Possiamo farlo trasformando position_within_year nelle coordinate x e y di un cerchio unitario.

Per fare ciò utilizziamo le funzioni seno e coseno:

sin(α) = x

cos(α) = y

dove α è l’angolo applicato al cerchio. Se il cerchio unitario rappresenta l’anno, α rappresenta il tempo dell’anno che è già trascorso.

α è quindi equivalente alla caratteristica di posizione_entro_l’anno, l’unica differenza è che α ha una scala diversa (α: 0.0–2π¹, position_entro_l’anno: 0.0-1.0).

Semplicemente scalando position_entro_l’anno ad α e calcolando seno e coseno, trasformiamo il pattern “a sega” in una rappresentazione circolare con transizioni morbide.

import math# scala a 2π (360 gradi)alpha = position_entro_l_anno * math.pi * 2year_circle_x = math.sin(alpha)year_circle_y = math.cos(alpha)# scala tra 0 e 1 (le posizioni originali del cerchio unitario sono tra -1 e 1)year_circle_x = (year_circle_x + 1) / 2year_circle_y = (year_circle_y + 1) / 2time_feature = (year_circle_x, year_circle_y) # così bello ;)

Il time_feature risultante è un vettore a due elementi scalato tra 0 e 1 che è facile da elaborare per il tuo modello di previsione. Con poche righe di codice, abbiamo tolto al nostro modello un sacco di sforzo di apprendimento superfluo.

Il modello del cerchio unitario può essere applicato a qualsiasi informazione temporale periodica come giorno del mese, giorno della settimana, ora del giorno, minuto dell’ora, ecc. Il concetto può anche essere esteso a caratteristiche cicliche al di fuori del dominio temporale:

  • Logistica/Trasporto pubblico: Posizione relativa di un autobus nel suo percorso circolare attraverso la città

  • Biologia: Stato di una cellula all’interno del ciclo cellulare.
  • Hai altri casi d’uso in mente? Sei invitato a lasciare un commento!

Ulteriori informazioni / Punti di collegamento

  • Un ottimo articolo pratico sullo stesso argomento di Pierre-Luis Bescond.
  • Vuoi saperne di più sull’ingegneria delle caratteristiche per modelli di deep learning? Dai un’occhiata al mio articolo su dati arricchiti di contesto.
  • Hai domande? Hai bisogno di un esperto freelance in AI, Data Science, Data Engineering o sviluppo Python? Visita il mio sito web e scrivimi un messaggio.

[1] L’angolo è dato in radianti qui. 0 in radianti corrisponde a 0°, 2π in radianti corrispondono a 360°.

Tutte le figure sono state create dall’autore.