Modélisation des caractéristiques saisonnières variables avec la transformation de Fourier
Modélisation des variations saisonnières des caractéristiques à l'aide de la transformation de Fourier
Améliorez les performances de prévision des séries chronologiques avec une technique provenant du traitement du signal
La modélisation des données de séries chronologiques et leur prévision sont des sujets complexes. Il existe de nombreuses techniques qui pourraient être utilisées pour améliorer les performances du modèle pour une tâche de prévision. Nous discuterons ici d’une technique qui pourrait améliorer la façon dont les modèles de prévision absorbent et utilisent les caractéristiques temporelles, et en généralisent à partir d’elles. L’accent principal sera mis sur la création des caractéristiques saisonnières qui alimentent le modèle de prévision des séries chronologiques lors de l’entraînement – il y a des gains faciles à réaliser ici si vous incluez la transformation de Fourier dans le processus de création des caractéristiques.
Cet article suppose que vous êtes familiarisé avec les aspects fondamentaux de la prévision des séries chronologiques – nous ne discuterons pas de ce sujet en général, mais seulement d’un aspect de son raffinement. Pour une introduction à la prévision des séries chronologiques, consultez le cours Kaggle sur ce sujet – la technique discutée ici s’appuie sur leur leçon sur la saisonnalité.
Ensemble de données
Considérez l’ensemble de données Production trimestrielle de Portland Cement en Australie, montrant la production trimestrielle totale de ciment Portland en Australie (en millions de tonnes) de 1956:Q1 à 2014:Q1.
df = pd.read_csv('Quarterly_Australian_Portland_Cement_production_776_10.csv', usecols=['time', 'value'])# convert time from year float to a proper datetime formatdf['time'] = df['time'].apply(lambda x: str(int(x)) + '-' + str(int(1 + 12 * (x % 1))).rjust(2, '0'))df['time'] = pd.to_datetime(df['time'])df = df.set_index('time').to_period()df.rename(columns={'value': 'production'}, inplace=True)
productiontime 1956Q1 0.4651956Q2 0.5321956Q3 0.5611956Q4 0.5701957Q1 0.529... ...2013Q1 2.0492013Q2 2.5282013Q3 2.6372013Q4 2.5652014Q1 2.229[233 rows x 1 columns]
- Des chercheurs de Stanford proposent des DDBMs une extension simple et évolutive des modèles de diffusion adaptée aux problèmes de traduction de distribution.
- Leadership basée sur les données Construire une organisation axée sur les données avec Srikanth Velamakanni
- Cette recherche en intelligence artificielle propose Kosmos-G un modèle d’intelligence artificielle qui réalise une génération d’images de haute fidélité sans apprentissage à partir d’une entrée de vision-langage généralisée en exploitant la propriété
Il s’agit de données de séries chronologiques avec une tendance, des composantes saisonnières et d’autres attributs. Les observations sont effectuées trimestriellement, couvrant plusieurs décennies. Jetons d’abord un coup d’œil aux composantes saisonnières, en utilisant le graphique du périodogramme (le code est inclus dans le notebook compagnon, lié à la fin).
Le périodogramme montre la densité de puissance des composantes spectrales (composantes saisonnières). La composante la plus forte est celle avec une période égale à 4 trimestres, soit 1 an. Cela confirme l’impression visuelle selon laquelle les variations les plus fortes dans le graphique se produisent environ 10 fois par décennie. Il y a également une composante avec une période de 2 trimestres – c’est le même phénomène saisonnier, et cela signifie simplement que la périodicité de 4 trimestres n’est pas une simple onde sinusoïdale, mais a une forme plus complexe. Nous ignorerons les composantes avec des périodes supérieures à 4, pour plus de simplicité.
Si vous essayez d’ajuster un modèle à ces données, peut-être afin de prévoir la production de ciment pour des périodes au-delà de la fin de l’ensemble de données, il serait judicieux de capturer cette saisonnalité annuelle dans une caractéristique ou un ensemble de caractéristiques, et de les fournir au modèle lors de l’entraînement. Voyons un exemple.
Caractéristiques saisonnières
Les composantes saisonnières peuvent être modélisées de différentes manières. Souvent, elles sont représentées sous forme de variables indicatrices temporelles, ou sous forme de paires sinusoïdales-cosinusoidales. Ce sont des caractéristiques synthétiques avec une période égale à la saisonnalité que vous essayez de modéliser, ou à un sous-multiple de celle-ci.
Variables indicatrices temporelles annuelles :
seasonal_year = ProcessusDéterministe(indice=df.indice, constant=False, saisonnier=True).dans_échantillon()print(seasonal_year)
s(1,4) s(2,4) s(3,4) s(4,4)temps 1956T1 1.0 0.0 0.0 0.01956T2 0.0 1.0 0.0 0.01956T3 0.0 0.0 1.0 0.01956T4 0.0 0.0 0.0 1.01957T1 1.0 0.0 0.0 0.0... ... ... ... ...2013T1 1.0 0.0 0.0 0.02013T2 0.0 1.0 0.0 0.02013T3 0.0 0.0 1.0 0.02013T4 0.0 0.0 0.0 1.02014T1 1.0 0.0 0.0 0.0[233 rows x 4 colonnes]
Paires sinus-cosinus annuelles :
cfr = CalendrierFourier(fréquence='A', ordre=2)seasonal_year_trig = ProcessusDéterministe(indice=df.indice, saisonnier=False, termes_supplémentaires=[cfr]).dans_échantillon()with pd.option_context('display.max_columns', None, 'display.expand_frame_repr', False): print(seasonal_year_trig)
sin(1,freq=A-DEC) cos(1,freq=A-DEC) sin(2,freq=A-DEC) cos(2,freq=A-DEC)temps 1956T1 0.000000 1.000000 0.000000 1.0000001956T2 0.999963 0.008583 0.017166 -0.9998531956T3 0.017166 -0.999853 -0.034328 0.9994111956T4 -0.999963 -0.008583 0.017166 -0.9998531957T1 0.000000 1.000000 0.000000 1.000000... ... ... ... ...2013T1 0.000000 1.000000 0.000000 1.0000002013T2 0.999769 0.021516 0.043022 -0.9990742013T3 0.025818 -0.999667 -0.051620 0.9986672013T4 -0.999917 -0.012910 0.025818 -0.9996672014T1 0.000000 1.000000 0.000000 1.000000[233 rows x 4 colonnes]
Les variables bidimensionnelles permettent de capturer des phénomènes saisonniers complexes. Cependant, si la période est de N, vous aurez besoin de N variables bidimensionnelles. Donc, si la période est très longue, vous aurez besoin de beaucoup de colonnes pour ces variables, ce qui peut ne pas être souhaitable. Pour les modèles non pénalisés, souvent N-1 variables bidimensionnelles sont utilisées, avec une variable laissée de côté pour éviter les problèmes de colinéarité (nous ne prendrons pas cela en compte ici).
Les paires sinus/cosinus permettent de modéliser des périodes de temps arbitrairement longues. Chaque paire modélisera une onde sinusoïdale pure avec une phase initiale. À mesure que l’onde du phénomène saisonnier devient plus complexe, vous devrez ajouter plus de paires (augmenter l’ordre de la sortie de CalendrierFourier).
(Note : nous utilisons une paire sinus/cosinus pour chaque période que nous voulons modéliser. Ce que nous voulons vraiment, c’est juste une colonne de A*sin(2*pi*t/T + phi)
où A
est le poids attribué par le modèle à la colonne, t
est l’indice de temps de la série et T
est la période composante. Mais le modèle ne peut pas ajuster la phase initiale phi
lors de l’ajustement des données – les valeurs sinus sont précalculées. Heureusement, la formule ci-dessus est équivalente à : A1*sin(2*pi*t/T) + A2*cos(2*pi*t/T)
et le modèle n’a besoin que de trouver les poids A1 et A2.)
TLDR: Ce que ces deux techniques ont en commun, c’est qu’elles représentent la saisonnalité via un ensemble de caractéristiques répétitives, avec des valeurs qui se cyclent aussi souvent qu’une fois par période de temps modélisée (time dummies et la paire sine/cosine de base), ou plusieurs fois par période (sine/cosine d’ordre supérieur). Et chacune de ces caractéristiques a des valeurs variant dans un intervalle fixe : de 0 à 1 ou de -1 à 1. Ce sont toutes les caractéristiques que nous devons modéliser ici.
Voyons ce qui se passe lorsque nous ajustons un modèle linéaire sur de telles caractéristiques saisonnières. Mais d’abord, nous devons ajouter des caractéristiques de tendance à la collection de caractéristiques utilisée pour former le modèle.
Ajuster un modèle linéaire
Créons des caractéristiques de tendance puis joignons-les aux time dummies seasonnelles générées ci-dessus :
trend_order = 2trend_year = DeterministicProcess(index=df.index, constant=True, order=trend_order).in_sample()X = trend_year.copy()X = X.join(seasonal_year)
const trend trend_squared s(1,4) s(2,4) s(3,4) s(4,4)time 1956Q1 1.0 1.0 1.0 1.0 0.0 0.0 0.01956Q2 1.0 2.0 4.0 0.0 1.0 0.0 0.01956Q3 1.0 3.0 9.0 0.0 0.0 1.0 0.01956Q4 1.0 4.0 16.0 0.0 0.0 0.0 1.01957Q1 1.0 5.0 25.0 1.0 0.0 0.0 0.0... ... ... ... ... ... ... ...2013Q1 1.0 229.0 52441.0 1.0 0.0 0.0 0.02013Q2 1.0 230.0 52900.0 0.0 1.0 0.0 0.02013Q3 1.0 231.0 53361.0 0.0 0.0 1.0 0.02013Q4 1.0 232.0 53824.0 0.0 0.0 0.0 1.02014Q1 1.0 233.0 54289.0 1.0 0.0 0.0 0.0[233 rows x 7 columns]
C’est le dataframe X (caractéristiques) qui sera utilisé pour former/valider le modèle. Nous modélisons les données avec des caractéristiques de tendance quadratique, plus les 4 time dummies nécessaires pour la saisonnalité annuelle. Le dataframe y (cible) contiendra simplement les chiffres de production de ciment.
Découpons un ensemble de validation à partir des données, contenant les observations de l’année 2010. Nous ajusterons un modèle linéaire sur les caractéristiques X mentionnées ci-dessus et la cible y représentée par la production de ciment (la partie de test), puis nous évaluerons la performance du modèle en validation. Nous ferons également tout cela avec un modèle basé uniquement sur la tendance qui ajustera uniquement les caractéristiques de tendance mais ignorera la saisonnalité.
def do_forecast(X, index_train, index_test, trend_order): X_train = X.loc[index_train] X_test = X.loc[index_test] y_train = df['production'].loc[index_train] y_test = df['production'].loc[index_test] model = LinearRegression(fit_intercept=False) _ = model.fit(X_train, y_train) y_fore = pd.Series(model.predict(X_test), index=index_test) y_past = pd.Series(model.predict(X_train), index=index_train) trend_columns = X_train.columns.to_list()[0 : trend_order + 1] model_trend = LinearRegression(fit_intercept=False) _ = model_trend.fit(X_train[trend_columns], y_train) y_trend_fore = pd.Series(model_trend.predict(X_test[trend_columns]), index=index_test) y_trend_past = pd.Series(model_trend.predict(X_train[trend_columns]), index=index_train) RMSLE = mean_squared_log_error(y_test, y_fore, squared=False) print(f'RMSLE : {RMSLE}') ax = df.plot(**plot_params, title='AUS Cement Production - Forecast') ax = y_past.plot(color='C0', label='Backcast') ax = y_fore.plot(color='C3', label='Forecast') ax = y_trend_past.plot(ax=ax, color='C0', linewidth=3, alpha=0.333, label='Trend Past') ax = y_trend_fore.plot(ax=ax, color='C3', linewidth=3, alpha=0.333, label='Trend Future') _ = ax.legend()do_forecast(X, index_train, index_test, trend_order)
RMSLE : 0.03846449744356434
Le bleu correspond à l’entraînement, le rouge à la validation. Le modèle complet est représenté par la ligne fine et nette. Le modèle basé uniquement sur la tendance est symbolisé par la ligne large et floue.
Ce n’est pas mal, mais il y a un problème évident : le modèle a appris une saisonnalité annuelle constante en amplitude. Bien que la variation annuelle augmente réellement avec le temps, le modèle ne peut adhérer qu’à des variations à amplitude fixe. Évidemment, c’est parce que nous avons donné au modèle des caractéristiques saisonnières à amplitude fixe, et il n’y a rien d’autre dans les caractéristiques (décalages de la cible, etc.) pour l’aider à surmonter ce problème.
Allons plus en profondeur dans le signal (les données) pour voir s’il y a quelque chose qui pourrait aider à résoudre le problème de l’amplitude fixe.
Le spectrogramme de Fourier
Le périodogramme mettra en évidence toutes les composantes spectrales du signal (toutes les composantes saisonnières des données) et donnera un aperçu de leur force globale, mais c’est une somme sur l’ensemble de l’intervalle de temps. Cela ne dit rien sur la manière dont l’amplitude de chaque composante saisonnière peut varier dans le temps sur l’ensemble des données.
Pour capturer cette variation, il faut utiliser le spectrogramme de Fourier. C’est comme le périodogramme, mais effectué de manière répétée sur de nombreuses fenêtres temporelles sur l’ensemble des données. Le périodogramme et le spectrogramme sont tous deux disponibles en tant que méthodes dans la bibliothèque scipy.
Traçons le spectrogramme pour les composantes saisonnières avec des périodes de 2 et 4 trimestres mentionnées ci-dessus. Comme toujours, le code complet se trouve dans le cahier d’accompagnement lié à la fin.
spectrum = compute_spectrum(df['production'], 4, 0.1)plot_spectrogram(spectrum, figsize_x=10)
Ce diagramme montre l’amplitude, la “force” des composantes de 2 trimestres et 4 trimestres au fil du temps. Elles sont assez faibles initialement, mais deviennent très fortes autour de 2010, ce qui correspond aux variations que l’on peut observer dans le graphique des données en haut de cet article.
Et si, au lieu de fournir au modèle des caractéristiques saisonnières à amplitude fixe, nous ajustions l’amplitude de ces caractéristiques dans le temps, en nous basant sur ce que nous dit le spectrogramme ? Est-ce que le modèle final performera mieux lors de la validation ?
Ajuster les composantes saisonnières
Prenons la composante saisonnière de 4 trimestres. Nous pourrions ajuster un modèle linéaire (appelé modèle enveloppe) sur la tendance d’ordre 2 de cette composante, sur les données d’entraînement (model.fit()), et étendre cette tendance à la validation / aux tests (model.predict()) :
envelope_features = DeterministicProcess(index=X.index, constant=True, order=2).in_sample()spec4_train = compute_spectrum(df['production'].loc[index_train], max_period=4)spec4_model = LinearRegression()spec4_model.fit(envelope_features.loc[spec4_train.index], spec4_train['4.0'])spec4_regress = pd.Series(spec4_model.predict(envelope_features), index=X.index)ax = spec4_train['4.0'].plot(label='enveloppe de la composante', color='gray')spec4_regress.loc[spec4_train.index].plot(ax=ax, color='C0', label='régression de l'enveloppe : passé')spec4_regress.loc[index_test].plot(ax=ax, color='C3', label="régression de l'enveloppe : futur")_ = ax.legend()
La ligne bleue représente la force de la variation de la composante de 4 trimestres dans les données d’entraînement, ajustée en tant que tendance quadratique (ordre=2). La ligne rouge est la même chose, étendue (prédite) sur les données de validation.
Nous avons modélisé la variation temporelle de la composante saisonnière de 4 trimestres. Nous pouvons prendre la sortie du modèle d’enveloppe et la multiplier par les indicateurs temporels correspondant à la composante saisonnière de 4 trimestres :
spec4_regress = spec4_regress / spec4_regress.mean()
season_columns = ['s(1,4)', 's(2,4)', 's(3,4)', 's(4,4)']
for c in season_columns:
X[c] = X[c] * spec4_regress
print(X)
const tendance tendance_au_carré s(1,4) s(2,4) s(3,4) s(4,4)temps 1956T1 1.0 1.0 1.0 0.179989 0.000000 0.000000 0.0000001956T2 1.0 2.0 4.0 0.000000 0.181109 0.000000 0.0000001956T3 1.0 3.0 9.0 0.000000 0.000000 0.182306 0.0000001956T4 1.0 4.0 16.0 0.000000 0.000000 0.000000 0.1835811957T1 1.0 5.0 25.0 0.184932 0.000000 0.000000 0.000000... ... ... ... ... ... ... ...2013T1 1.0 229.0 52441.0 2.434701 0.000000 0.000000 0.0000002013T2 1.0 230.0 52900.0 0.000000 2.453436 0.000000 0.0000002013T3 1.0 231.0 53361.0 0.000000 0.000000 2.472249 0.0000002013T4 1.0 232.0 53824.0 0.000000 0.000000 0.000000 2.4911392014T1 1.0 233.0 54289.0 2.510106 0.000000 0.000000 0.000000[233 rows x 7 columns]
Les 4 dummies temporelles ne sont plus égales à 0 ou 1. Elles ont été multipliées par l’enveloppe du composant et maintenant leur amplitude varie dans le temps, tout comme l’enveloppe.
Retraner le modèle
Retraînons le modèle principal en utilisant les dummies temporelles modifiées.
do_forecast(X, index_train, index_test, trend_order)
RMSLE: 0.02546321729737165
Nous ne cherchons pas ici une correspondance parfaite, car il s’agit juste d’un modèle fictif, mais il est évident que le modèle fonctionne mieux, il suit de plus près les variations trimestrielles de la cible. La métrique de performance s’est également améliorée de 51%, ce qui n’est pas mal du tout.
Commentaires finaux
L’amélioration des performances d’un modèle est un vaste sujet. Le comportement de tout modèle ne dépend pas d’une seule caractéristique ou d’une seule technique. Cependant, si vous souhaitez tirer le meilleur parti d’un modèle donné, vous devriez probablement lui fournir des caractéristiques significatives. Les dummies temporelles, ou les paires sinus/cosinus de Fourier, sont plus significatives lorsqu’elles reflètent la variation temporelle de la saisonnalité qu’elles modélisent.
L’ajustement de l’enveloppe du composant saisonnier via la transformation de Fourier est particulièrement efficace pour les modèles linéaires. Les arbres boostés n’en bénéficient pas autant, mais vous pouvez quand même observer des améliorations quelle que soit le modèle utilisé. Si vous utilisez un modèle hybride, où un modèle linéaire gère les caractéristiques déterministes (calendrier, etc.), tandis qu’un modèle d’arbres boostés gère les caractéristiques plus complexes, il est important que le modèle linéaire “fasse bien son travail”, laissant ainsi moins de travail à faire pour l’autre modèle.
Il est également important que les modèles d’enveloppe que vous utilisez pour ajuster les caractéristiques saisonnières ne soient entraînés qu’avec les données d’entraînement et ne voient aucune donnée de test pendant l’entraînement, tout comme tout autre modèle. Une fois que vous avez ajusté l’enveloppe, les variables temps contiennent des informations provenant de la cible – elles ne sont plus uniquement des caractéristiques déterministes pouvant être calculées à l’avance sur n’importe quelle plage de prévision arbitraire. À ce stade, des informations pourraient fuiter de la validation/test vers les données d’entraînement si vous n’êtes pas prudent.
Liens
L’ensemble de données utilisé dans cet article est disponible ici sous la licence Domaine Public (CC0) :
KEY2STATS
© 2023 KEY2STATS – RStudio® est une marque déposée de RStudio, Inc. AP® est une marque déposée du College…
www.key2stats.com
Le code utilisé dans cet article peut être trouvé ici :
misc/seasonal_features_fourier_article/cement.ipynb sur master · FlorinAndrei/misc
trucs aléatoires. Contribuez au développement de FlorinAndrei/misc en créant un compte sur GitHub.
github.com
Un notebook soumis à la compétition Store Sales — Time Series Forecasting sur Kaggle, utilisant des idées décrites dans cet article :
Spectrogramme de Fourier, modèles ensemblistes
Explorez et exécutez du code d’apprentissage automatique avec Kaggle Notebooks | Utilisation de données de Store Sales – Time Series Forecasting
www.kaggle.com
Un référentiel GitHub contenant la version de développement du notebook soumis à Kaggle se trouve ici :
GitHub – FlorinAndrei/timeseries-forecasting-fourier-time-dummies: Prévision de séries chronologiques avec …
Prévision de séries chronologiques avec des dummies temporelles ajustées par Fourier – GitHub …
github.com
Toutes les images et le code utilisés dans cet article ont été créés par l’auteur.
We will continue to update IPGirl; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles
- Cette recherche en IA dévoile ‘Kandinsky1’ une nouvelle approche dans la génération de texte vers image par diffusion latente, avec des scores FID exceptionnels sur COCO-30K.
- GPT-4V(ision) d’OpenAI une percée dans la frontière multimodale de l’IA
- LangChain 101 Partie 2d. Ajuster finement les modèles de langage avec les commentaires humains
- Un guide complet sur ZenML pour les débutants simplifiant MLOps.
- Équité dans l’apprentissage automatique (Partie 1)
- Se concentrez sur les éviers et où les cacher un parcours visuel pour la mise en œuvre du streaming LLM
- Principaux articles importants en vision par ordinateur de la semaine du 2/10 au 8/10