Amusons-nous avec les séries chronologiques d'IntegratedML
Récemment, @Anastasia Dyubaylo a publié un article (celui-ci) sur une nouvelle fonctionnalité d'IntegratedML pour les prédictions de séries chronologiques présentée par @tomdlors du Global Summit 2023, organisons donc un petit atelier pour la tester !
.png)
Introduction
Nous avons choisi comme sujet de cet atelier la prédiction des utilisateurs du métro de Valence, mois par mois, ligne par ligne. Pour ce faire, nous disposons de données mensuelles ventilées par ligne depuis 2022 ainsi que de données annuelles ventilées par ligne depuis 2017 que nous extrapolerons mensuellement.
Une fois compilé le nombre mensuel de passagers, nous pourrons créer un fichier CSV qui ressemblera à celui-ci :
Year,Month,Passengers,Line
2017,1,549007,1
2017,1,515947,2
2017,1,770564,3
2017,1,621256,5
2017,1,429640,7
2017,1,520692,9
2017,1,322392,4
2017,1,95473,6
2017,1,18188,8
2017,1,0,10Nous avons inclus dans notre fichier csv une ligne finale ",,,," qui nous permettra de savoir quand le fichier est terminé.
Mappage des fichiers CSV
Bien que le mappage soit déjà effectué dans le projet inclus dans cet article, nous allons le détailler pour les utilisateurs qui ne sont pas familiers avec Record Mapper.
Comme vous pouvez le voir dans l'image précédente, la première ligne sera l'en-tête du fichier et nous avons les données pour l'année, le mois, le flux de passagers et la ligne. Pour enregistrer le CSV dans notre base de données IRIS, nous utiliserons la fonctionnalité Record Mapper, à laquelle nous accéderons via le portail de gestion à partir des options de menu Interopérabilité -> Construire -> Assistant d'enregistrement CSV Record Wizard. Une fois l'accès obtenu, nous remplirons les champs de la manière indiquée :
.png)
N'oubliez pas que notre fichier CSV comporte des en-têtes, c'est pourquoi nous avons coché l'option L'échantillon comporte une ligne d'en-tête. À partir de ces informations, IRIS génère automatiquement le mappage CSV, en affichant l'écran suivant :
.png)
Nous n'aurons pas besoin de changer quoi que ce soit et en cliquant sur Générer (Generate), le système créera automatiquement toutes les classes nécessaires pour le mappage CSV à partir de nos productions.
Configuration de la production
Jetons un coup d'œil à la production configurée qui fonctionne dans notre projet :
.png)
Comme nous le voyons, notre production est composée de 3 Composants Métier. Voyons un peu plus en détail le rôle de chacun d'entre eux :
CSVIn
Ce service d'affaires est de la classe EnsLib.RecordMap.Service.FileService, il nous permettra de capturer les fichiers CSV qui se trouvent dans un chemin indiqué dans sa configuration. Pour chaque ligne du fichier CSV capturée, il créera un objet du type MLTEST.PassengersMap.Record défini par le RecordMap configuré, dans notre cas PassengersMap, et l'enverra au composant métier indiqué dans TargetConfigNames.
.png)
PredictionModelGeneration
Ce Processus Métier est défini par la BPL MLTEST.BP.PopulateTable à partir de laquelle nous allons transformer les données brutes reçues dans le format le plus intéressant pour l'entraînement de notre modèle. Jetons un coup d'œil à la BPL :
.png)
Voyons cela en détail dans le diagramme :
.png)
Tout d'abord, nous vérifierons si l'objet reçu correspond à une donnée réelle ou s'il s'agit de l'indicateur de fin de fichier défini précédemment par nous.
L'objet N'EST PAS la fin du fichier :
Si ce n'est pas la fin du fichier, nous allons transformer les données brutes reçues en un élément plus utilisable par notre modèle et pour cela nous avons défini une petite DTL : MLTEST.DT.ParseDate.
.png)
Cette DTL va seulement générer un objet de type MLTEST.Data.PassengersInfo avec une date de type DateTime formée avec les données du CSV. Lorsque la transformation sera terminée, la BPL aura juste à sauvegarder l'objet (dans la variable contextuelle PassengersInfo) dans notre base de données.
set sc = context.PassengersInfo.%Save()
if ($$$ISERR(sc))
{
set context.Result = "Error"set context.ErrorMessage = sc
}
else
{
set context.Result = "Success"
}L'objet se trouve à la fin du fichier :
Lorsque nous avons fini de lire le fichier, nous devons procéder au formatage des données pour que le modèle de Time Series (séries chronologiques) les comprenne. ATTENTION! Il faut prendre en considération les deux points suivants (et que je n'ai pas pris en compte 🙄) :
- Il est nécessaire d'avoir un champ de type DateTime qui sera utilisé pour continuer la série chronologique, un type Date ne sera pas suffisant..
- Les enregistrements doivent être ordonnés de l'enregistrement le plus ancien au plus récent pour que l'entraînement fonctionne correctement.
Si le modèle a déjà été généré, nous le supprimerons, ainsi que les données du tableau d'apprentissage. Le processus se poursuit comme suit :
Voyons chaque étape en détail :
- Nous effectuons une insertion massive avec les données transformées.
INSERTINTO MLTEST_Data.PassengersLine (DateOfData, Line1, Line2, Line3, Line4, Line5, Line6, Line7, Line8, Line9, Line10)
SELECT DateOfData,
(SELECTMAX(s1.Passengers) FROM MLTEST_Data.PassengersInfo s1 WHERE s1.Line = 1AND s1.DateOfData = s.DateOfData) as Line1,
(SELECTMAX(s2.Passengers) FROM MLTEST_Data.PassengersInfo s2 WHERE s2.Line = 2AND s2.DateOfData = s.DateOfData) as Line2,
(SELECTMAX(s3.Passengers) FROM MLTEST_Data.PassengersInfo s3 WHERE s3.Line = 3AND s3.DateOfData = s.DateOfData) as Line3,
(SELECTMAX(s4.Passengers) FROM MLTEST_Data.PassengersInfo s4 WHERE s4.Line = 4AND s4.DateOfData = s.DateOfData) as Line4,
(SELECTMAX(s5.Passengers) FROM MLTEST_Data.PassengersInfo s5 WHERE s5.Line = 5AND s5.DateOfData = s.DateOfData) as Line5,
(SELECTMAX(s6.Passengers) FROM MLTEST_Data.PassengersInfo s6 WHERE s6.Line = 6AND s6.DateOfData = s.DateOfData) as Line6,
(SELECTMAX(s7.Passengers) FROM MLTEST_Data.PassengersInfo s7 WHERE s7.Line = 7AND s7.DateOfData = s.DateOfData) as Line7,
(SELECTMAX(s8.Passengers) FROM MLTEST_Data.PassengersInfo s8 WHERE s8.Line = 8AND s8.DateOfData = s.DateOfData) as Line8,
(SELECTMAX(s9.Passengers) FROM MLTEST_Data.PassengersInfo s9 WHERE s9.Line = 9AND s9.DateOfData = s.DateOfData) as Line9,
(SELECTMAX(s10.Passengers) FROM MLTEST_Data.PassengersInfo s10 WHERE s10.Line = 10AND s10.DateOfData = s.DateOfData) as Line10
FROM MLTEST_Data.PassengersInfo s GROUPBY DateOfData- Nous créons notre modèle de type TIME SERIES (séries chronologiques), en définissant les valeurs à prédire et la colonne contenant les séries chronologiques de type DateTime (DateOfData) :
CREATETIME SERIES MODEL PassengersPrediction
PREDICTING (Line1,Line2,Line3,Line4,Line5,Line6,Line7,Line8,Line9,Line10)
BY (DateOfData)
FROM MLTEST_Data.PassengersLine USING {"Forward":3}
- Nous formons notre modèle :
TRAIN MODEL PassengersPrediction- Enfin, nous appelons l'opération métier PredictionToCSV qui générera la prédiction et l'enregistrera dans un fichier CSV.
PredictionToCSV
Cette opération métier est extrêmement simple et tout cela grâce au projet @Evgeny Shvarovcsvgen qui nous permet d'exporter directement les résultats d'une requête SQL vers un fichier CSV de manière directe. Le code de cette BO se trouve ici :
Class MLTEST.BO.PredictionToCSV Extends Ens.BusinessOperation
{
Parameter INVOCATION = "Queue";
Method ExportPrediction(pRequest As Ens.Request, pResponse As Ens.Response) As%Status
{
set query="SELECT WITH PREDICTIONS (PassengersPrediction) * FROM MLTEST_Data.PassengersLine"w##class(community.csvgen).SQLToCSV(",",1,"/shared/prediction.csv",query)
Quit$$$OK
}
XData MessageMap
{
<MapItems>
<MapItem MessageType="Ens.Request">
<Method>ExportPrediction</Method>
</MapItem>
</MapItems>
}
}Comme vous pouvez le constater, la requête permettant d'obtenir la prédiction est très simple.
SELECTWITH PREDICTIONS (PassengersPrediction) * FROM MLTEST_Data.PassengersLineVoyons le résultat généré dans le fichier /shared/prediction.csv.
.png)
Conclusions
Comme vous pouvez le constater, la création de modèles de prédiction de séries temporelles est relativement simple, vous devez juste vous assurer que la colonne de type DateTime est correctement ordonnée afin que l'entraînement fonctionne sans problème.
Si vous avez des remarques ou des suggestions, n'hésitez pas à les laisser dans la section des commentaires. Merci beaucoup pour le temps accordé !