Article
· Août 21, 2023 13m de lecture

Prédictions avec IntegratedML et IRIS

Si vous lisez régulièrement les articles publiés dans la Communauté, vous savez qu'en mai de l'année dernière, InterSystems a organisé le JOnTheBeach2023 Hackathon qui s'est tenu à Malaga (Espagne). Le sujet proposé était l'utilisation des outils d'analyse prédictive qu'InterSystems IRIS met à la disposition de tous les développeurs avec IntegratedML. Nous devons remercier @Thomas Dyar et @Dmitry Maslennikov pour tout le travail et les efforts qu'ils ont consacrés à faire de cet événement un succès retentissant.

Voici une brève présentation d'IntegratedML

IntegratedML

IntegratedML est un outil d'analyse prédictive qui permet à tout développeur de simplifier les tâches nécessaires à la conception, à l'élaboration et au test de modèles prédictifs.

Il nous permet de se déplacer par un modèle de conception comme celle-ci :

Une solution beaucoup plus rapide et simple comme celle-ci :

Et ceci à l'aide de commandes SQL, de sorte que tout est beaucoup plus facile et confortable à utiliser. IntegratedML nous permet également de choisir le moteur que nous allons utiliser pour la création de notre modèle, et donc de choisir celui qui nous convient le mieux.

Comment le voir en action ?

Whenever I've seen IntegratedML presentations I've loved its simplicity, but I was left wondering how to transfer that simplicity of use to a real case. Thinking a bit about our regular clients, I remembered how common it is to use IRIS to integrate data from hospital departmental applications with an HIS and the large amount of information on clinical events available in all of them, so I got down to the work to assemble a complete example.

You have the source code in Open Exchange. The project starts with Docker and you only have to feed the deployed production with the attached files that we will show.

Comme vous pouvez le voir, le projet contient des classes ObjectScript qui seront chargées automatiquement lorsque l'image sera construite. Pour ce faire, il suffit d'ouvrir le terminal VS Code et de lancer les commandes suivantes (avec Docker en cours d'exécution).

docker-compose build
docker-compose up -d

Lors du lancement du conteneur, un espace de noms appelé MLTEST sera créé et une production sera lancée ; nous y trouverons tous les composants commerciaux nécessaires à l'ingestion de données brutes, à la création du modèle, à sa formation et à son implémentation ultérieure par le biais de la réception de la messagerie HL7.

Mais ne nous précipitons pas encore et suivons le tableau de l'analyse prédictive.

Acquisition de données

Très bien, réduisons la cible de notre prédiction. En parcourant les pages de l'administration publique espagnole, j'ai trouvé quelques fichiers CSV qui correspondent parfaitement à l'univers des intégrations avec origine et destination dans un SIS. Dans ce cas, le fichier que j'ai choisi est celui relatif aux données sur les admissions et les sorties d'hôpital pour des fractures de la hanche en Castille-et-León (région autonome espagnole) entre les années 2020 et 2022.

Comme vous pouvez le voir, nous disposons de données telles que l'âge et le sexe du patient, les dates d'admission et de sortie et le centre hospitalier. Parfait, avec ces données, nous pourrions essayer de prédire le séjour à l'hôpital pour chaque patient, c'est-à-dire le nombre de jours entre l'admission et la sortie.

Nous avons un CSV mais nous devons le stocker dans notre IRIS et rien n'est plus facile que d'utiliser le IRIS Record Mapper. Vous pouvez voir le résultat de l'utilisation de Record Mapper dans la colonne Services commerciaux (Business Services) de la production MLTEST :

 

  • CSVToEpisodeTrain ** est la station de base BS chargée de lire le CSV et d'envoyer les données à la BP **MLTEST.BP.RecordToEpisodeTrain que nous expliquerons plus tard. Les données obtenues par cette BS seront utilisées pour entraîner notre modèle.
  • CSVToEpisode est la station de base BS qui lira les données du CSV que nous utiliserons plus tard pour lancer des prédictions de test avant d'exécuter nos prédictions obtenues à partir des messages HL7.

Les deux stations de base BS vont créer un objet de la classe User.IctusMap.Record.cls pour chaque ligne du CSV qui sera envoyé à leurs BP respectifs où les transformations nécessaires seront effectuées pour obtenir finalement des enregistrements de nos tableaux MLTEST_Data.Episode et MLTEST_Data.EpisodeTrain, ce dernier sera le tableau que nous utiliserons pour générer le modèle de prédiction, tandis que le premier est l'endroit où nous stockerons nos épisodes.

Préparation de données

Pour créer notre modèle, nous devons transformer la lecture CSV en objets facilement utilisables par le moteur de prédiction et, pour ce faire, nous utiliserons la BP suivante :

  • MLTEST.BP.RecordToEpisode: ceci effectuera la transformation de l'enregistrement CSV vers notre table d'épisodes MLTEST_Data.Episode
  • MLTEST.BP.RecordToEpisodeTrain: ceci permet d'effectuer la même transformation que dans le cas précédent, mais en stockant l'épisode dans MLTEST_Data.EpisodeTrain.

Nous aurions pu utiliser une seule BP pour l'enregistrement dans les deux tableaux, mais pour rendre le processus plus clair, nous la laisserons telle quelle. Dans la transformation effectuée par la BP, nous avons remplacé tous les champs de texte par des valeurs numériques afin d'accélérer l'apprentissage du modèle.

Voilà, nos BS et BP fonctionnent, alimentons-les en copiant le fichier /shared/train-data.csv dans le projet au chemin /shared/csv/trainIn :

Ici, tous les enregistrements de notre fichier ont été consommés, transformés et enregistrés dans notre tableau d'apprentissage. Répétons l'opération avec les enregistrements que nous allons utiliser pour un premier test de prédictions. En copiant /shared/test-data.csv au chemin /shared/csv/newIn, nous avons déjà tout ce qu'il faut pour créer notre modèle.

Dans ce projet, il ne serait pas nécessaire d'exécuter les instructions de création et d'entraînement, puisqu'elles sont incluses dans le BO qui gère l'enregistrement des données reçues par la messagerie HL7, mais pour que vous puissiez le voir plus en détail, nous allons le faire avant de tester l'intégration avec les messages HL7.

AutoML

Nous avons nos données d'entraînement et nos données de test, nous créons notre modèle. Pour ce faire, nous allons accéder à l'écran SQL de notre IRIS (System Explorer --> SQL) à partir de l'espace de noms MLTEST et exécuter les commandes suivantes :

CREATE MODEL StayModel PREDICTING (Stay) FROM MLTEST_Data.EpisodeTrain

Dans cette requête, nous créons un modèle de prédiction appelé StayModel qui va prédire la valeur de la colonne de séjour "Stay" de notre tableau avec des épisodes d'entraînement. La colonne "Stay" n'est pas présente dans notre CSV mais nous l'avons calculée dans le BP chargé de transformer l'enregistrement CSV.

Ensuite, nous procédons à l'entraînement du modèle :

TRAIN MODEL StayModel

Cette instruction prendra un certain temps, mais une fois l'entraînement terminé, nous pouvons valider le modèle avec nos données de test en exécutant l'instruction suivante :

VALIDATE MODEL StayModel FROM MLTEST_Data.Episode

Cette requête permet de calculer le degré d'approximation de nos estimations. Comme vous pouvez l'imaginer avec les données dont nous disposons, il n'y a pas vraiment de quoi s'enthousiasmer. Vous pouvez visualiser le résultat de la validation à l'aide de la requête suivante :

SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS

Les statistiques obtenues montrent que le modèle utilisé par AutoML est un modèle de classification plutôt qu'un modèle de régression. Expliquons la signification des résultats obtenus (merci @Yuri Marx pour votre article !):

  • Précision : le calcul se fait en divisant le nombre de vrais positifs par le nombre de positifs prédits (somme des vrais positifs et des faux positifs).
  • Rappel : le calcul se fait en divisant le nombre de vrais positifs par le nombre de vrais positifs (somme des vrais positifs et des faux négatifs).
  • Mesure F : le calcul se fait selon l'expression suivante : F = 2 * (Précision * Rappel) / (Précision + Rappel)
  • Précision : le calcul se fait en divisant le nombre de vrais positifs et de vrais négatifs par le nombre total de lignes (somme des vrais positifs, des faux positifs, des vrais négatifs et des faux négatifs) de l'ensemble des données.

Grâce à cette explication, nous pouvons déjà comprendre la qualité du modèle généré :

Comme vous pouvez le voir, notre modèle est assez mauvais en termes de chiffres généraux, nous atteignons à peine 35 % de réussite, si nous approfondissons la question, nous constatons que pour les séjours de courte durée, la précision se situe entre 35 % et 60 %, nous aurions donc sûrement besoin de compléter les données dont nous disposons avec des informations sur les pathologies possibles que le patient peut avoir et le triage en ce qui concerne la fracture.

Comme nous ne disposons pas de ces données qui permettraient de préciser beaucoup plus notre modèle, nous allons imaginer que ce que nous avons est largement suffisant pour notre objectif, nous pouvons donc commencer à alimenter notre production avec les messages d'admission de patients ADT_A01 et nous verrons les prédictions ainsi obtenues.

Exécution d'une production

Une fois le modèle entraîné, il ne nous reste plus qu'à préparer la production pour créer un enregistrement dans notre tableau **MLTEST_Data.Episode ** pour chaque message reçu. Voyons les composants de notre production :

  • HL7ToEpisode : c'est l'application qui va capturer le fichier avec les messages HL7. Ce BS redirigera les messages vers le BP MLTEST.BP.RecordToEpisodeBPL.
  • MLTEST.BP.RecordToEpisodeBPL : ce BPL comprendra les étapes suivantes:
    • Transformation du HL7 en un objet MLTEST.Data.Episode
    • Enregistrement dans la base de données de l'objet Episode.
    • Appel à **MLTEST.BO.PredictStayEpisode **BO pour obtenir la prédiction des jours d'hospitalisation.
    • Ecriture d'une trace avec la prédiction obtenue.
  • MLTEST.BO.PredictStayEpisode : BO chargée de lancer automatiquement les requêtes nécessaires au modèle de prédiction. A défaut, il sera chargé de le créer et de l'entraîner automatiquement, de telle sorte qu'il ne sera pas nécessaire d'exécuter les commandes SQL. Examinons le code.
Class MLTEST.BO.PredictStayEpisode Extends Ens.BusinessOperation
{

Property ModelName As %String(MAXLEN = 100);
/// Description
Parameter SETTINGS = "ModelName";
Parameter INVOCATION = "Queue";
/// Description
Method PredictStay(pRequest As MLTEST.Data.PredictionRequest, pResponse As MLTEST.Data.PredictionResponse) As %Status
{
    set predictionRequest = pRequest
    set pResponse = ##class("MLTEST.Data.PredictionResponse").%New()
    set pResponse.EpisodeId = predictionRequest.EpisodeId
    set tSC = $$$OK
    // VÉRIFIER L'EXISTENCE DU MODÈLE 
    set sql = "SELECT MODEL_NAME FROM INFORMATION_SCHEMA.ML_MODELS WHERE MODEL_NAME = '"_..ModelName_"'"
    set statement = ##class(%SQL.Statement).%New()
    set status = statement.%Prepare(sql)
    if ($$$ISOK(status)) {
        set resultSet = statement.%Execute()
        if (resultSet.%SQLCODE = 0) {
            set modelExists = 0
            while (resultSet.%Next() '= 0) {
                if (resultSet.%GetData(1) '= "") {
                    set modelExists = 1
                    // OBTENIR LA PRÉDICTION D'UN SÉJOUR AVEC LE DERNIER ÉPISODE PERSISTANT
                    set sqlPredict = "SELECT PREDICT("_..ModelName_") AS PredictedStay FROM MLTEST_Data.Episode WHERE %ID = ?"
                    set statementPredict = ##class(%SQL.Statement).%New(), statement.%ObjectSelectMode = 1
                    set statusPredict = statementPredict.%Prepare(sqlPredict)
                    if ($$$ISOK(statusPredict)) {
                        set resultSetPredict = statementPredict.%Execute(predictionRequest.EpisodeId)
                        if (resultSetPredict.%SQLCODE = 0) {
                                while (resultSetPredict.%Next() '= 0) {
                                    set pResponse.PredictedStay = resultSetPredict.%GetData(1)
                                }
                        }
                    }
                    else {
                        set tSC = statusPredict
                    }
                }
            }
            if (modelExists = 0) {
                // CRÉATION DU MODÈLE DE PRÉDICTION
                set sqlCreate = "CREATE MODEL "_..ModelName_" PREDICTING (Stay) FROM MLTEST_Data.EpisodeTrain"
                set statementCreate = ##class(%SQL.Statement).%New()
                set statusCreate = statementCreate.%Prepare(sqlCreate)
                if ($$$ISOK(status)) {
                    set resultSetCreate = statementCreate.%Execute()
                    if (resultSetCreate.%SQLCODE = 0) {
                        // LE MODÈLE EST ENTRAÎNÉ AVEC LES DONNÉES CSV PRÉCHARGÉES
                        set sqlTrain = "TRAIN MODEL "_..ModelName
                        set statementTrain = ##class(%SQL.Statement).%New()
                        set statusTrain = statementTrain.%Prepare(sqlTrain)
                        if ($$$ISOK(statusTrain)) {
                            set resultSetTrain = statementTrain.%Execute()
                            if (resultSetTrain.%SQLCODE = 0) {
                                // VALIDATION DU MODÈLE AVEC LES ÉPISODES PRÉCHARGÉS
                                set sqlValidate = "VALIDATE MODEL "_..ModelName_" FROM MLTEST_Data.Episode"
                                set statementValidate = ##class(%SQL.Statement).%New()
                                set statusValidate = statementValidate.%Prepare(sqlValidate)
                                if ($$$ISOK(statusValidate)) {
                                    set resultSetValidate = statementValidate.%Execute()
                                    if (resultSetValidate.%SQLCODE = 0) {
                                        // OBTENIR LA PRÉDICTION D'UN SÉJOUR AVEC LE DERNIER ÉPISODE PERSISTANT
                                        set sqlPredict = "SELECT PREDICT("_..ModelName_") AS PredictedStay FROM MLTEST_Data.Episode WHERE %ID = ?"
                                        set statementPredict = ##class(%SQL.Statement).%New(), statement.%ObjectSelectMode = 1
                                        set statusPredict = statementPredict.%Prepare(sqlPredict)
                                        if ($$$ISOK(statusPredict)) {
                                            set resultSetPredict = statementPredict.%Execute(predictionRequest.EpisodeId)
                                            if (resultSetPredict.%SQLCODE = 0) {
                                                while (resultSetPredict.%Next() '= 0) {
                                                    set pResponse.PredictedStay = resultSetPredict.%GetData(1)
                                                }
                                            }
                                        }
                                        else {
                                            set tSC = statusPredict
                                        }
                                    }
                                }
                                else {
                                    set tSC = statusValidate
                                }
                            }
                        }
                        else {
                            set tSC = statusTrain
                        }
                    }
                }
                else {
                    set tSC = status
                }
            }
        }
    }
    else {
        set tSC = status
    }
    quit tSC
}

XData MessageMap
{

  "MLTEST.Data.PredictionRequest">
    PredictStay
  

}

}

Comme vous pouvez le voir, nous disposons d'une propriété qui nous aidera à définir le nom souhaitable pour notre modèle de prédiction et, dans un premier temps, nous lancerons une requête dans le tableau ML_MODELS pour nous assurer que le modèle existe.

Nous sommes prêts à lancer nos messages, pour ce faire nous copierons le fichier de projet /shared/messagesa01.hl7 dans le dossier /shared/hl7/in. Cette action nous enverra 50 messages de données générés à notre production. Examinons quelques prédictions.

Pour notre patiente Sonia Martínez, âgée de 2 mois, nous aurons un séjour de...

8 jours ! Remets-toi vite !

Regardons un autre patient :

Ana Torres Fernandez, 50 ans...

Son séjour durera 9 jours.

Voilà, c'est tout pour aujourd'hui. La chose la moins importante dans cet exemple est la valeur numérique de la prédiction, vous pouvez voir qu'elle est assez médiocre d'après les statistiques que nous avons obtenues, mais elle pourrait être très utile dans les cas où vous disposez d'un bon ensemble de données sur lesquelles vous pouvez appliquer cette fonctionnalité si sympa d'IntegratedML.

Si vous voulez jouer avec ce logiciel, vous pouvez télécharger la version Community ou utiliser la version configurée dans le projet OpenExchange associé à cet article.

Si vous avez des questions ou souhaitez des précisions, n'hésitez pas à nous contacter dans les commentaires.

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer