Omaroid Blog Personnel

Facebook Prophet dans toute sa splendeur 🔼

Une sĂ©rie temporelle, ou sĂ©rie chronologique, est une suite de valeurs numĂ©riques reprĂ©sentant l’évolution d’une quantitĂ© spĂ©cifique au cours du temps. De telles suites de variables alĂ©atoires peuvent ĂȘtre exprimĂ©es mathĂ©matiquement afin d’en analyser le comportement, gĂ©nĂ©ralement pour comprendre son Ă©volution passĂ©e et pour en prĂ©voir le comportement futur. Une telle transposition mathĂ©matique utilise le plus souvent des concepts de probabilitĂ©s et de statistique.

Présentation

Il y a 3 ans, l’équipe de Facebook Core Data Science team Ă  sorti en open source un puissant outil de prĂ©vision pour les sĂ©ries temporelles appelĂ© Prophet.

Prophet se base sur un modĂšle additif qui s’adapte par rapport Ă  la saisonnalitĂ© des donnĂ©es mais pas que, il est assez robuste pour des jeux de donnĂ©es qui reprĂ©sentent des donnĂ©es manquantes ou comportant des valeurs aberrantes (ou outliers).

Pour les non statisticiens ou data scientistes, il est assez dĂ©licat de partir sur une connaissance des principes d’analyse et de prĂ©vision temporelles mais Prophet est venu rĂ©pondre Ă  ce besoin de la plus simple des façon.

L’objectif de l’article est de faire une introduction de Prophet sous Python 🐍. Les donnĂ©es et le code seront disponibles sur le Github de Ingeniance ainsi que sur Google Colaboratory en fin du blog.

Pourquoi Prophet?

A travers son article scientifique, Prophet vient rĂ©pondre Ă  une problĂ©matique de scalabilitĂ© sous 3 volets: Le grand nombre de personnes qui travaillent sur les prĂ©visions temporelles et qui ont une faible connaissance mathĂ©matiques du domaine Le grand nombre de problĂšmes de prĂ©visions qui peuvent parfois venir d’un caractĂšre idiomatique Le grand nombre de sujets sur lesquels il est possible d’appliquer des prĂ©visions temporelles qui demandent des configurations diffĂ©rentes Prophet vient rĂ©pondre Ă  ce problĂšme avec:

Une intégration sous R et Python

Prophet propose sa bibliothĂšque sous CRAN pour les utilisateurs de R ainsi que sur PyPi pour les adeptes de Python.

Une saisonnalité additive et multiplicative

La saisonnalité est une caractéristique commune des séries chronologiques. Il peut apparaßtre sous deux formes:

  • Additive
  • Multiplicative

Dans le premier cas, l’amplitude de la variation saisonniĂšre est indĂ©pendante du niveau, tandis que dans le second, elle est liĂ©e. Comme le montre la figure suivante:

additive_vs_multiplicative_seasonality

Par défaut, Prophet adapte son modÚle à travers un modÚle additif, mais supporte également le modÚle multiplicatif.(Documentation)

# ModĂšle additif
additiveModel = Prophet()
additiveModel = Prophet(seasonality_mode='additive')

# ModĂšle multiplicatif
MultiplicativeModel = Prophet(seasonality_mode='multiplicative')

Un support des données mensuelles et sous quotidiennes

Prophet supporte les sĂ©ries non quotidiennes de type mensuel (si on a un enregistrement par mois) ou sous quotidien (si on a un enregistrement toutes les 6 ou 12 heures par exemple). Prophet s’adapte automatiquement Ă  ce type de donnĂ©es sans lui indiquer ceci en analysant l’écart entre les lignes du jeu de donnĂ©es d’entrĂ©e.(Documentation)

Un support des événements spéciaux, jours fériés et vacances

Un point trĂšs fort de Prophet est la possibilitĂ© de personnaliser une saisonnalitĂ© dĂ©pendamment de plusieurs Ă©vĂ©nements spĂ©ciaux, fĂȘtes et vacances. Il est possible d’utiliser ses fonctions pour crĂ©er de façon automatique une liste d’évĂšnements par pays, rĂ©gion, dĂ©partement et annĂ©e et les passer au modĂšle pour apprentissage. Si tout de mĂȘme vous n’ĂȘtes pas satisfaits par le nombre de pays assez restreint, il est possible d’utiliser la bibliothĂšque holidays ou mĂȘme en important son propre jeu de donnĂ©es de ces Ă©vĂ©nements. Prophet s’adaptera automatiquement pour en tenir compte.

Sous Python, ces Ă©vĂ©nements sont disponibles pour n’importe quelle date de n’importe quelle annĂ©e.

Sous R, ces Ă©vĂ©nements sont disponibles entre 1995 et jusqu’en 2044 et pouvant ĂȘtre Ă©tendues en lançant un script qui vient avec Prophet pour en gĂ©nĂ©rer d’avantage.

Une gestion des valeurs aberrantes

Prophet peut gérer les individus aberrants de façon automatique en ne tenant pas compte de leurs présence lors du calcul des prévisions. Or, des fois il trouvera des difficultés pour le faire et si un grand nombre de valeurs aberrantes est présent à un certain moment, ceci peut biaiser les prédictions futures. Pour ceci, Facebook Prophet demande à les supprimer de la série du moment que Prophet supporte les valeurs manquantes qui seront remplacés par les prévisions de son modÚle.(Documentation)

Une gestion des changements de tendance

Prophet propose une gestion des changements sur la courbe, en positionnant sur les 80% de la courbe et de façon uniforme des points de changement (25 point pour ĂȘtre prĂ©cis) et rĂ©duira ce nombre de points au fur et Ă  mesure qu’il paramĂštre son modĂšle pour ne garder que le minimum. Il est mĂȘme possible d’indiquer de façon manuelle les points de changement sur la courbe. La flexibilitĂ© de la courbe de prĂ©diction est automatiquement initialisĂ©e, mais on peut Ă©galement la modifier en spĂ©cifiant le paramĂštre de flexibilitĂ© Ă  la crĂ©ation du modĂšle.(Documentation)

Et bien plus


Outre les forces dĂ©jĂ  citĂ©es, l’article scientifique de Facebook Data Science team et la documentation officielle peuvent vous convaincre d’avantage d’utiliser Prophet et commencer Ă  faire vos prĂ©visions temporelles.

POC

L’exemple sur lequel on va travailler est un exemple dans le domaine du retail, qui est l’exemple Retail Sales. Ce jeu de donnĂ©es concerne le nombre mensuel des ventes rĂ©alisĂ©s entre Janvier 1992 et Mai 2016 ce qui Ă©quivaut Ă  293 lignes de donnĂ©es reprĂ©sentĂ©es sur 2 colonnes:

  • Une colonne de type datetime dont le label est ds
  • Une colonne de type entier dont le label est y

Si les colonnes du jeu de donnĂ©es ne sont pas [‘ds’, ‘y’], il est nĂ©cessaire d’enlever les colonnes non pertinentes et de renommer les entĂȘtes de colonnes.

On commence par installer et importer les bibliothÚques nécessaires.

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as dates
from datetime import datetime

# Facebook Prophet
!pip install fbprophet
from fbprophet import Prophet
from fbprophet.diagnostics import cross_validation
from fbprophet.diagnostics import performance_metrics
from fbprophet.plot import plot_cross_validation_metric
from fbprophet import hdays

!pip install holidays
import holidays

On importe par la suite le jeu de donnĂ©es, on reformate le type des colonnes et on mets l’index sur la colonnes ds.

RetailSalesDataframe = pd.read_csv("./RetailSales.csv", delimiter=",")
RetailSalesDataframe["ds"] = pd.to_datetime(RetailSalesDataframe["ds"], infer_datetime_format=True)
RetailSalesDataframe["y"] = pd.to_numeric(RetailSalesDataframe["y"])
indexedDataframe = RetailSalesDataframe.set_index(["ds"])

Pour visualiser et comprendre les donnĂ©es, on calcule la moyenne mobile et l’écart type et on les affiche.

Roolmean = indexedDataframe.rolling(window = 12).mean()
Roolstd = indexedDataframe.rolling(window = 12).std()
orig = plt.plot(indexedDataframe, color = "blue", label = "Original")
mean = plt.plot(Roolmean, color = "red", label = "Moyenne mobile")
std = plt.plot(Roolstd, color = "black", label = "Ecart Type")
plt.legend(loc = "best")
plt.title("Tendance des ventes par date avec la moyenne mobile et la 
variance")
plt.rcParams["figure.figsize"] = [15,9]
plt.show(block = False)

On remarque que la courbe prĂ©sente une tendance Ă  la hausse durant toute la pĂ©riode Ă©tudiĂ©e (sauf en 2008 pĂ©riode de la crise financiĂšre) mais Ă©galement un effet saisonnier avec une hausse pour certaines pĂ©riodes de l’annĂ©e et une forte baisse pour d’autres. Pour mieux voir ceci, on affiche l’allure de la courbe pendant les annĂ©es de 1992 jusqu’à 1995.

ye1 = plt.plot(indexedDataframe[0:11], color = "blue", label = "1992")
ye2 = plt.plot(indexedDataframe[12:23], color = "orange", label = "1993")
ye3 = plt.plot(indexedDataframe[24:35], color = "green", label = "1994")
ye4 = plt.plot(indexedDataframe[36:47], color = "red", label = "1995")
plt.legend(loc = "best")
plt.title("Tendance des ventes par date sur 4 années")
plt.show(block = False)

image

Courbe dont l’allure est non stationnaire et suit une tendance saisonniĂšre avec peu de ventes sur les 1 ers mois de l’annĂ©e. Les ventes augmentent de façon significative pendant l’étĂ© et retombent aprĂšs le mois d’AoĂ»t et reprennent leurs hausses pendant la pĂ©riode de noĂ«l. Pour comprendre l’évolution des ventes en 2008, on analysera les quelques annĂ©es qui ont suivi 2008.

crisis08 = plt.plot(indexedDataframe.loc['2008-01-01':'2008-12-30'], color = "blue", label = "2008")
crisis09 = plt.plot(indexedDataframe.loc['2009-01-01':'2009-12-30'], color = "red", label = "2009")
crisis09 = plt.plot(indexedDataframe.loc['2010-01-01':'2010-12-30'], color = "green", label = "2010")
plt.legend(loc = "best")
plt.title("Tendance des ventes par date sur la période aprÚs crise de 2008")
plt.show(block = False)

image

Le graphe prĂ©cĂ©dent relate l’impact de la crise financiĂšre de 2008 sur le nombre de ventes. En effet, les ventes ont baissĂ© durant l’annĂ©e 2009 pour reprendre la tendance positive progressive Ă  partir de 2010. Passons aux prĂ©dictions, on commence par crĂ©er un objet Prophet en lui passant en paramĂštre le type de saisonnalitĂ© car on est face Ă  une saisonnalitĂ© multiplicative.

prophetModel = Prophet(seasonality_mode="multiplicative")

On entraßne le modÚle sur notre jeu de données.

prophetModel = prophetModel.fit(RetailSalesDataframe)

Une fois que c’est fait, on lance les prĂ©visions sur la frĂ©quence ainsi que le nombre de pĂ©riode voulus, pour mon cas, j’ai choisi une prĂ©vision sur 60 mois (5ans) et donc jusqu’en Avril 2021:

future = prophetModel.make_future_dataframe(periods=60, freq='M')

L’argument freq peut ĂȘtre n’importe quel argument de type pd.date_range, comme D pour Daily ou M pour Monthly.

Pour voir les ventes prévus dans 5ans (à partir de 2016):

forecast = prophetModel.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()

Image

Le yhat ou y prevu est le résultat prévu avec un degré de certitude de 95% que notre résultat sera forcement entre le yhat_lower et le yhat_upper. Enfin on affiche le graphe qui résume ces résultats:

fig1 = prophetModel.plot(forecast)

Image

fig2 = prophetModel.plot_components(forecast)

Image

L’évaluation du modĂšle

Pour diagnostiquer nos rĂ©sultats, il faudra bien comparer sur les donnĂ©es en entrĂ©es Ă  celles que Prophet Ă  prĂ©vu. Pour ceci, Prophet propose d’effectuer une validation croisĂ©e sur notre modĂšle sur un intervalle de temps. La pĂ©riode d’entrainement est 3 fois l’horizon et les coupures ont lieux chaque mi-horizon mais c’est possible de personnaliser ces paramĂštres lors de l’appel. Pour mon cas, j’ai donnĂ© 21 ans comme pĂ©riode d’entrainement initial, une pĂ©riode d’un mois et 6 mois d’horizon.

df_cv = cross_validation(prophetModel, initial = '7665 days', period = 
'30 days', horizon = '180 days')
df_p = performance_metrics(df_cv)
df_p

Image

L’erreur absolue moyenne en pourcentage (Mean Absolute Percentage Error, alias MAPE) est la moyenne des Ă©carts en valeur absolue par rapport aux valeurs observĂ©es. C’est donc un pourcentage et par consĂ©quent un indicateur pratique de comparaison.

fig = plot_cross_validation_metric(df_cv, metric='mape')

Image

Pour notre cas, le MAPE ne dépasse pas les 2.5% et donc on peut juger que nos prédictions sont excellentes pour avoir une vision des ventes sur la période des 5 années à venir.

Pour récompenser vos efforts pour arriver à la fin, un bonus relatif à expérimentation des jours fériés vous attends au niveau du code sur le repository Github et le notebook Google Collaboratory.

Conclusion

L’analyse des sĂ©ries temporelles a depuis toujours Ă©tĂ© une branche trĂšs Ă©tudiĂ©e en analyse de donnĂ©es et en machine learning et la crĂ©ation de telles librairies par l’une des plus grandes entreprises du monde en est la preuve. Ces librairies viennent cacher la complexitĂ© d’une connaissance poussĂ©e des statistiques en simplifiant la rĂ©alisation des prĂ©dictions, leur visualisation et leur exportation de la plus simple des façons.

EverythingIsWellPredicted

Liens utiles

comments powered by Disqus