#IntegratedML

0 Abonnés · 25 Publications

InterSystems IntegratedML est un module d'apprentissage automatique (ML) entièrement en SQL pour InterSystems IRIS ou IRIS for Health qui :
- Donne aux utilisateurs la possibilité de créer, d'entraîner et de déployer des modèles puissants à partir d'une syntaxe SQL simple sans nécessiter de data scientists.
- Regroupe les meilleurs frameworks « AutoML » propriétaires et open source, dont DataRobot.
- Met l'accent sur un déploiement facile dans IRIS, afin de vous permettre d'ajouter facilement l'apprentissage automatique à vos applications.

Retrouvez plus d'informations, y compris des vidéos et des infographies, dans le guide de ressources IntegratedML..

InterSystems officiel Adeline Icard · Avr 8, 2025

Résumé des alertes

Alert ID Produit et versions concernés Exigences explicites
DP-439207 Plateforme de données InterSystems IRIS® 2024.3 (AIX) Installations AIX utilisant le traitement JSON et les caractères Unicode non-Latin-1
DP-439280 InterSystems IRIS 2024.3 (conteneurs avec IntegratedML) Conteneurs IntegratedML utilisant TensorFlow

Détail des alertes

DP-439207 - Problème d'analyse JSON Unicode AIX

Un bug a été identifié dans InterSystems IRIS 2024.3.0 sur les instances AIX. Il affecte l'analyse des chaînes JSON Unicode. Ce problème survient lorsque la méthode %FromJSON() ou %FromJSONFile() analyse des chaînes contenant des caractères dont la valeur est inférieure à $CHAR(256) suivis de caractères Unicode dont la valeur est supérieure à $CHAR(255). Le processus de conversion transforme incorrectement les premiers caractères en $CHAR(0), ce qui entraîne une corruption silencieuse des données. Ce problème concerne uniquement la version AIX 2024.3 des produits suivants :

  • InterSystems IRIS
  • InterSystems IRIS for Health
  • HealthShare® Health Connect
0
0 24
Article Iryna Mykhailova · Avr 3, 2025 3m read

Introduction

Dans InterSystems IRIS 2024.3 et les versions ultérieures d'IRIS, le composant AutoML est désormais fourni sous forme de package Python distinct, installé après l'installation. Malheureusement, certaines versions récentes des packages Python sur lesquels AutoML s'appuie ont introduit des incompatibilités et peuvent entraîner des échecs lors de l'entraînement des modèles (instruction TRAIN MODEL). Si vous rencontrez une erreur mentionnant « TypeError » et l'argument de mot-clé « fit_params » ou « sklearn_tags », lisez la suite pour une solution rapide.

Cause principale

0
0 28
Article Sylvain Guilbaud · Fév 21, 2025 6m read

InterSystems est à l'avant-garde de la technologie des bases de données depuis sa création, en étant à l'origine d'innovations qui surpassent régulièrement ses concurrents comme Oracle, IBM et Microsoft. En se concentrant sur une conception de noyau efficace et en adoptant une approche sans compromis des performances des données, InterSystems s'est taillé une place de choix dans les applications critiques, garantissant fiabilité, rapidité et évolutivité.

Une histoire d'excellence technique

0
0 66
Article Sylvain Guilbaud · Jan 30, 2024 4m read

Traitement des ressources FHIR avec FHIR SQL BUILDER pour prédire la probabilité de développer une hépatite C

Avec le développement de la technologie, l'industrie médicale progresse également constamment et les humains accordent souvent plus d'attention à leur propre santé.
En apprenant et en traitant des ensembles de données par ordinateur, les maladies peuvent être prédites.

Prérequis: Capacité à utiliser FHIR et ML

2
0 58
InterSystems officiel Benjamin De Boe · Jan 15, 2024

InterSystems a le plaisir d'annoncer la disponibilité générale d'InterSystems IRIS Cloud SQL et d'InterSystems IRIS Cloud IntegratedML, deux services fondamentaux pour le développement de solutions cloud natives optimisées par les performances et la fiabilité éprouvées de classe entreprise de la technologie InterSystems IRIS.

0
0 59
Article Guillaume Rongier · Oct 27, 2023 7m read

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 !

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,10

Nous 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 :

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 :

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 :

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.

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 :

Voyons cela en détail dans le diagramme :

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.

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 🙄) :

  1. 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..
  2. 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 :

  1. 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
  1. 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}
  1. Nous formons notre modèle :
TRAIN MODEL PassengersPrediction
  1. 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.PassengersLine

Voyons le résultat généré dans le fichier /shared/prediction.csv.

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é !

0
0 76
Article Iryna Mykhailova · Oct 13, 2023 12m read

La série d'articles relatifs à l'application QuinielaML se poursuit. Dans cet article, nous verrons comment préparer les données brutes que nous avons capturées à l'aide de la fonctionnalité Embedded Python.

Bienvenue à toutes et à tous !

Happy Artist GIF by VIRTUTE - Find & Share on GIPHY

Introduction

Si vous vous souvenez de l'article précédent, nous avons capturé les données des résultats des matches de football de première et deuxième division pour les 23 dernières années en utilisant Embedded Python à partir d'un site web externe. Maintenant que nous disposons des données brutes, nous allons les transformer et les préparer pour faciliter la maintenance de l'application et l'utilisation de notre modèle de prédiction.

QUINIELA_Object.RawMatch

Voyons sous quelle forme se présentent les données que nous avons saisies dans notre base de données IRIS :

Comme vous le voyez dans la capture d'écran suivante, elles sont peu différentes des informations présentes sur le site web de BDFutbol :

 

Tableaux principaux :

Pour faciliter la maintenance ultérieure des données communes et améliorer les performances du modèle de prédiction, nous avons défini deux tableaux principaux pour stocker les équipes et les arbitres. Ces tableaux ne comprendront que la colonne contenant l'identifiant de l'enregistrement et le nom. Examinons les deux tableaux :

QUINIELA_Object.Referee:

Сorrespondance avec le tableau principal d'arbitres.

QUINIELA_Object.Team

Сorrespondance avec le tableau principal d'arbitres.

 

Préparation de données

Avec nos tableaux principaux et nos données brutes, nous pouvons maintenant entreprendre le processus de préparation de ces données en vue de la formation de notre modèle. Nous avons ici l'opération métier qui sera chargée de la préparation :

Class QUINIELA.BO.PrepareBO Extends Ens.BusinessOperation
{

Parameter INVOCATION = "Queue";
Method PrepareData(pRequest As QUINIELA.Message.PrepareRequest, pResponse As QUINIELA.Message.PrepareResponse) As%Status
{
    Set sc = $$$OKset pResponse = ##class(QUINIELA.Message.PrepareResponse).%New()
    set pResponse.Operation = pRequest.Operation
    
    set sqlTruncateTrain = "TRUNCATE TABLE QUINIELA_Object.MatchTrain"set statementTruncateTrain = ##class(%SQL.Statement).%New()
    set statusTruncateTrain = statementTruncateTrain.%Prepare(sqlTruncateTrain)
    if ($$$ISOK(statusTruncateTrain)) {
        set resultSetTruncateTrain = statementTruncateTrain.%Execute()
        if (resultSetTruncateTrain.%SQLCODE = 0) {
            set sqlMatchTrain = "INSERT INTO QUINIELA_Object.MatchTrain (Day, Division, Journey, LocalTeam, Referee, Result, VisitorTeam, IntDay) "_
                "SELECT "_
                "TO_DATE(RM.Day,'DD/MM/YYYY') AS DayTransformed, "_
                "RM.Division, "_
                "RM.Journey, "_
                "LT.ID as LocalTeam, "_
                "R.ID as Referee, "_
                "CASE WHEN CAST(RM.GoalsLocal As INTEGER) > CAST(RM.GoalsVisitor As INTEGER) THEN 1 WHEN CAST(RM.GoalsLocal As INTEGER) < CAST(RM.GoalsVisitor As INTEGER) THEN 2 ELSE 0 END as Result, "_
                "VT.ID as VisitorTeam, "_
                "CAST({fn CONCAT({fn CONCAT(SUBSTR(RM.Day,7,4),SUBSTR(RM.Day,4,2))},SUBSTR(RM.Day,1,2))} As INTEGER) as IntDay "_
                "FROM "_
                "QUINIELA_Object.RawMatch RM "_
                "LEFT JOIN QUINIELA_Object.Team LT ON UPPER(RM.LocalTeam) = UPPER(LT.Name) "_
                "LEFT JOIN QUINIELA_Object.Team VT ON UPPER(RM.VisitorTeam) = UPPER(VT.Name) "_
                "LEFT JOIN QUINIELA_Object.Referee R ON UPPER(RM.Referee) = UPPER(R.Name)"set statementMatchTrain = ##class(%SQL.Statement).%New()
            set statusMatchTrain = statementMatchTrain.%Prepare(sqlMatchTrain)
            if ($$$ISOK(statusMatchTrain)) {
                set resultSetMatchTrain = statementMatchTrain.%Execute()
                if (resultSetMatchTrain.%SQLCODE = 0) {
                    set sqlUpdateLocalStreak = "UPDATE QUINIELA_Object.MatchTrain SET QUINIELA_Object.MatchTrain.LocalStreak = "_
                        "(SELECT SUM(CASE WHEN IsVictory = 1 THEN 4-%VID ELSE 0 END) FROM "_
                        "(SELECT TOP 3 SubMatch.IntDay, "_
                        "CASE WHEN Result = 1 THEN 1 ELSE 0 END AS IsVictory "_
                        "FROM QUINIELA_Object.MatchTrain AS SubMatch "_
                        "WHERE "_
                        "UPPER(SubMatch.LocalTeam) = UPPER(QUINIELA_Object.MatchTrain.LocalTeam) "_
                        "AND SubMatch.IntDay < QUINIELA_Object.MatchTrain.IntDay "_
                        "ORDER BY SubMatch.IntDay DESC)) "set statementUpdateLocalStreak = ##class(%SQL.Statement).%New()
                    set statusUpdateLocalStreak = statementUpdateLocalStreak.%Prepare(sqlUpdateLocalStreak)
                    if ($$$ISOK(statusUpdateLocalStreak)) {
                        set resultSetUpdateLocalStreak = statementUpdateLocalStreak.%Execute()
                        if (resultSetUpdateLocalStreak.%SQLCODE = 0) {
                            set sqlUpdateVisitorStreak = "UPDATE QUINIELA_Object.MatchTrain SET QUINIELA_Object.MatchTrain.VisitorStreak = "_
                                "(SELECT SUM(CASE WHEN IsVictory = 1 THEN 4-%VID ELSE 0 END) FROM "_
                                "(SELECT TOP 3 SubMatch.IntDay, "_
                                "CASE WHEN Result = 2 THEN 1 ELSE 0 END AS IsVictory "_
                                "FROM QUINIELA_Object.MatchTrain AS SubMatch "_
                                "WHERE "_
                                "UPPER(SubMatch.VisitorTeam) = UPPER(QUINIELA_Object.MatchTrain.VisitorTeam) "_
                                "AND SubMatch.IntDay < QUINIELA_Object.MatchTrain.IntDay "_
                                "ORDER BY SubMatch.IntDay DESC)) "set statementUpdateVisitorStreak = ##class(%SQL.Statement).%New()
                            set statusUpdateVisitorStreak = statementUpdateVisitorStreak.%Prepare(sqlUpdateVisitorStreak)
                            if ($$$ISOK(statusUpdateVisitorStreak)) {
                                set resultSetUpdateVisitorStreak = statementUpdateVisitorStreak.%Execute()
                                set sc = statusUpdateVisitorStreak
                            }
                            else {
                                set sc = statusUpdateVisitorStreak
                            }
                        }
                    }
                    else {
                        set sc = statusUpdateLocalStreak
                    }
                }
            }
            else {
                set sc = statusMatchTrain
            }
        }
    }
    
    set pResponse.Status = "Finished"Return sc
}

XData MessageMap
{
"QUINIELA.Message.PrepareRequest">
    PrepareData
}

}

Examinons maintenant en détail chacune des instructions SQL que nous exécutons :

  1. Nous quittons le tableau de données d'apprentissage :

    TRUNCATETABLE QUINIELA_Object.MatchTrain
  2. Nous lançons une insertion massive dans notre tableau d'apprentissage QUINIELA_Object.MatchTrain

    INSERTINTO QUINIELA_Object.MatchTrain (Day, Division, Journey, LocalTeam, Referee, Result, VisitorTeam, IntDay)
    SELECTTO_DATE(RM.Day,'DD/MM/YYYY') AS DayTransformed,
    RM.Division,
    RM.Journey,
    LT.ID as LocalTeam,
    R.ID as Referee,
    CASEWHENCAST(RM.GoalsLocal AsINTEGER) > CAST(RM.GoalsVisitor AsINTEGER) THEN1WHENCAST(RM.GoalsLocal AsINTEGER) < CAST(RM.GoalsVisitor AsINTEGER) THEN2ELSE0ENDasResult,
    VT.ID as VisitorTeam,
    CAST({fn CONCAT({fn CONCAT(SUBSTR(RM.Day,7,4),SUBSTR(RM.Day,4,2))},SUBSTR(RM.Day,1,2))} AsINTEGER) as IntDay
    FROM
    QUINIELA_Object.RawMatch RM
    LEFTJOIN QUINIELA_Object.Team LT ONUPPER(RM.LocalTeam) = UPPER(LT.Name)
    LEFTJOIN QUINIELA_Object.Team VT ONUPPER(RM.VisitorTeam) = UPPER(VT.Name)
    LEFTJOIN QUINIELA_Object.Referee R ONUPPER(RM.Referee) = UPPER(R.Name)
    Pour y parvenir, nous remplaçons le libellé par le nom de l'arbitre et les équipes par leur référence dans les tableaux principaux. Nous obtenons également le résultat à partir des scores du match, 0 pour le match nul, 1 pour la victoire à domicile et 2 pour la victoire à l'extérieur. La colonne
**Résultat** est celle qui définit notre modèle de prédiction comme un modèle de classification, c'est-à-dire que chaque correspondance est classée dans l'une des trois classes (1, X ou 2).  
  1. Nous calculons les séries pour chaque équipe selon qu'elle joue à domicile ou à l'extérieur. Nous avons ajouté ces colonnes pour améliorer, autant que possible, la performance du modèle prédictif. Nous avons supposé qu'une équipe qui a plusieurs victoires consécutives à domicile ou à l'extérieur a plus de facilité à poursuivre les victoires en étant "sur une série". Le calcul se fait de la manière suivante : on obtient les 3 derniers matchs (à domicile pour l'équipe qui joue à domicile ou à l'extérieur pour celle qui joue à l'extérieur), si elle a gagné le dernier match on lui attribue 3 points, si elle a gagné le match avant-dernier on lui attribue 2 points et si elle a gagné le match précédent avant-dernier on lui attribue 1 point, enfin on additionne les points obtenus ce qui donne une valeur numérique à la série. Série à domicile :
UPDATE QUINIELA_Object.MatchTrain SET QUINIELA_Object.MatchTrain.LocalStreak = 
    (SELECTSUM(CASEWHEN IsVictory = 1THEN4-%VID ELSE0END) FROM
        (SELECT TOP 3 SubMatch.IntDay, 
            CASEWHENResult = 1THEN1ELSE0ENDAS IsVictory 
        FROM QUINIELA_Object.MatchTrain AS SubMatch 
        WHEREUPPER(SubMatch.LocalTeam) = UPPER(QUINIELA_Object.MatchTrain.LocalTeam) 
            AND SubMatch.IntDay < QUINIELA_Object.MatchTrain.IntDay 
        ORDERBY SubMatch.IntDay DESC
        )
    )

Série à l'extérieur:

UPDATE QUINIELA_Object.MatchTrain SET QUINIELA_Object.MatchTrain.VisitorStreak = 
    (SELECTSUM(CASEWHEN IsVictory = 1THEN4-%VID ELSE0END) 
    FROM
        (SELECT TOP 3 SubMatch.IntDay, 
            CASEWHENResult = 2THEN1ELSE0ENDAS IsVictory 
        FROM QUINIELA_Object.MatchTrain AS SubMatch 
        WHEREUPPER(SubMatch.VisitorTeam) = UPPER(QUINIELA_Object.MatchTrain.VisitorTeam) 
            AND SubMatch.IntDay < QUINIELA_Object.MatchTrain.IntDay 
            ORDERBY SubMatch.IntDay DESC
        )
    )

Voyons quel est le résultat de cette série d'insertions et de mises à jour consultant le tableau QUINIELA_Object.MatchTrain :

Comme vous voyez, nous avons transformé les champs de texte en valeurs numériques... ou non ? Examinons la définition de la classe :

Class QUINIELA.Object.MatchTrain Extends (%Persistent, %JSON.Adaptor) [ DdlAllowed ]
{

/// Jour du matchProperty Day As%Date;/// Équipe localeProperty LocalTeam As%String;/// Équipe jouant à l'extérieurProperty VisitorTeam As%String/// Série localeProperty LocalStreak As%Integer;/// Série de victoires de l'équipe jouant à l'extérieurProperty VisitorStreak As%Integer/// ArbitreProperty Referee As%String;/// RésultatProperty Result &As%String/// DivisionProperty Division As%String;/// ItinéraireProperty Journey As%String;/// Nombre entier de joursProperty IntDay As%Integer;
}

Comme vous voyez, les références aux tableaux principaux sont toujours de type %String. Pourquoi ? Sur cette page vous trouverez l'explication de la documentation, mais en résumé, c'est parce que, bien qu'elles soient réellement numériques, elles ne correspondent pas à des valeurs quantifiables, mais à des identifiants.

Parfait, nous avons maintenant tout ce qu'il faut pour créer et former notre modèle prédictif.

Création et formation du modèle prédictif

Grâce à la fonctionnalité d'IntegratedML, cette étape est très simple pour nous, puisqu'il nous suffit d'exécuter deux commandes simples dans notre base de données. Examinons la transaction métier que nous avons créée à cette fin :

Class QUINIELA.BO.TrainBO Extends Ens.BusinessOperation
{

Parameter INVOCATION = "Queue";/// Description
Method CreateAndTrainModel(pRequest As QUINIELA.Message.TrainRequest, pResponse As QUINIELA.Message.TrainResponse) As%Status
{
        Set tSC = $$$OKset pResponse = ##class(QUINIELA.Message.TrainResponse).%New()
        set pResponse.Operation = pRequest.Operation
        set pResponse.Status = "In Process"set sql = "SELECT MODEL_NAME FROM INFORMATION_SCHEMA.ML_MODELS WHERE MODEL_NAME = 'QuinielaModel'"set statement = ##class(%SQL.Statement).%New()
        set status = statement.%Prepare(sql)
        $$$TRACE(status)
        if ($$$ISOK(status)) {
            set resultSet = statement.%Execute()
            $$$TRACE(resultSet.%SQLCODE)
            if (resultSet.%SQLCODE = 0) {
                while (resultSet.%Next() '= 0) {
                    if (resultSet.%GetData(1) '= "") {
                        set sqlDrop = "DROP MODEL QuinielaModel"set statementDrop = ##class(%SQL.Statement).%New()
                        set statusDrop = statementDrop.%Prepare(sqlDrop)
                        if ($$$ISOK(statusDrop)) {
                            set resultSetDrop = statementDrop.%Execute()
                            if (resultSetDrop.%SQLCODE = 0) {
                                set tSC = statusDrop                                                                
                            }
                        }
                    }
                }
            }            
        }
        $$$TRACE("Creating model")
        set sqlCreate = "CREATE MODEL QuinielaModel PREDICTING (Result) FROM QUINIELA_Object.MatchTrain"set statementCreate = ##class(%SQL.Statement).%New()
        set statusCreate = statementCreate.%Prepare(sqlCreate)
        if ($$$ISOK(statusCreate)) {
            $$$TRACE("Model created")
            set resultSetCreate = statementCreate.%Execute()
            if (resultSetCreate.%SQLCODE = 0) {
                set tSC = statusCreate                                
            }
        }
        else
        {
            set tSC = statusDrop
        }

        $$$TRACE("Training model")
        set sqlTrain = "TRAIN MODEL QuinielaModel"set statementTrain = ##class(%SQL.Statement).%New()
        set statusTrain = statementTrain.%Prepare(sqlTrain)
        if ($$$ISOK(statusTrain)) {
            set resultSetTrain = statementTrain.%Execute()
            if (resultSetTrain.%SQLCODE = 0) {
                // VALIDATION OF THE MODEL WITH THE PRE-LOADED MATCHESset sqlValidate = "VALIDATE MODEL QuinielaModel FROM QUINIELA_Object.MatchTrain"set statementValidate = ##class(%SQL.Statement).%New()
                set statusValidate = statementValidate.%Prepare(sqlValidate)
                set resultSetValidate = statementValidate.%Execute()
                set tSC = statusValidate                                    
            }
        }
        else {
            set tSC = statusTrain
        }
        
        set pResponse.Status = "Finished"Return tSC
}

XData MessageMap
{
"QUINIELA.Message.TrainRequest">
    CreateAndTrainModel
}

}

Analysons ce que fait notre BO :

  1. Création du modèle prédictif et définition de la valeur à prédire en indiquant la colonne de notre tableau d'entraînement correspondant.

    CREATEMODEL QuinielaModel PREDICTING (Result) FROM QUINIELA_Object.MatchTrain
  2. Formation de notre modèle nouvellement créé.

    TRAIN MODEL QuinielaModel
  3. Validation du modèle créé sur la base du tableau d'entraînement utilisé.

    VALIDATE MODEL QuinielaModel FROM QUINIELA_Object.MatchTrain

Avec ces trois étapes très simples, notre modèle est prêt à générer des prédictions. Examinons la qualité de notre modèle. Pour ce faire, exécutons la requête suivante :

SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS

Avec cette requête, nous obtiendrons les mesures suivantes :

Eh bien, pas si mal, pour la victoire au niveau local, le taux de réussite est de 52%, pour la victoire au niveau des visiteurs de 41% et pour les matchs nuls de 37%, nous dépassons le taux de réussite de 33% dû au pur hasard !

<img alt="Boxing Memes on X: "Le compatriote de Golovkin réagit à sa victoire... #GolovkinGeale http://t.co/MDW6F5eJlz" / X" src="https://pbs.twimg.com/media/BthSZmaIcAAsIXU.jpg">

0
0 91
Article Iryna Mykhailova · Oct 9, 2023 5m read

En profitant de l'application Quiniela ML et comme nous l'avons dit dans l'article précédent, nous allons expliquer comment nous pouvons réaliser une authentification JWT entre notre frontend développé en Angular et notre backend développé en InterSystems IRIS.

Je vous rappelle l'architecture de notre projet QuinielaML :

Pour les applications web, le développement de l'administration et de la gestion de l'accès des utilisateurs est généralement un processus compliqué, mais dans notre cas, InterSystems IRIS le simplifie en nous fournissant toute l'infrastructure dont nous avons besoin.

Authentification par jeton Web JSON

IRIS permet aux applications web connectées à l'instance déployée de se connecter via JWT de manière simple et directe.

Rappelons le cycle de vie d'une demande d'authentification JWT :

Flux d'authentification du client-serveur JWT

En ce qui nous concerne, le client sera notre application frontend développée en Angular et le serveur sera notre instance IRIS.

Ainsi, le processus commence par l'envoi de la demande de connexion du client au serveur. Le serveur valide les données de l'utilisateur en générant un jeton au format JSON qu'il renvoie à l'application cliente. Le client peut à son tour valider le jeton reçu et doit ensuite l'inclure dans l'en-tête de ses appels au serveur afin d'obtenir les ressources demandées.

Comme nous l'avons dit, IRIS facilite la gestion de l'authentification via JWT et pour cela il nous fournit les endpoints suivants à utiliser par notre application client :

  • /login — Un appel à ce point de terminaison avec l'authentification HTTP de base ou avec des informations d'identification valides dans le corps de la demande renvoie un jeton d'accès et un jeton d'actualisation qui peuvent être utilisés dans des demandes ultérieures.
  • /logout — Un appel à ce point de terminaison, s'il n'utilise pas Group-By-ID, invalide le jeton d'accès fourni et le jeton d'actualisation associé. S'il utilise Group-By-ID, toutes les sessions du groupe By-ID actuel sont invalidées.
  • /refresh — Un appel à ce point de terminaison émet une nouvelle paire de jetons d'accès et d'actualisation lorsque l'appel est effectué avec un jeton d'actualisation valide. Cela invalide la paire de jetons d'accès et d'actualisation précédente.
  • /revoke — Si le Group-By-ID n'est pas utilisé, ceci est fonctionnellement le même que /logout. Si l'on utilise Group-By-ID, cela révoque uniquement la paire de jetons d'accès et d'actualisation en cours.
  • Configuration frontale de la connexion

    Comme nous l'avons déjà précisé, nous avons développé le frontend de notre application en utilisant Angular. Depuis notre page HTML login.component.html, nous avons configuré un formulaire qui accepte le nom d'utilisateur et le mot de passe et nous procédons, depuis la classe TypeScript, au lancement de l'invocation depuis le composant de services que nous avons défini.

    Ici, nous voyons comment nous invoquons l'appel au service à partir de login.component.ts :

    onSubmit(): void {
        const { username, password } = this.form;
    
        this.authService.login(username, password).subscribe({
          next: data => {
            this.storageService.save(data.access_token)
            this.isLoggedIn = true;
            this.router.navigate(['home']);
          },
          error: err => {
          }
        });
    }

    Voyons maintenant l'appel que nous lançons depuis le service auth.service.ts vers notre backend :

    login(username: string, password: string): Observable<any> {
        returnthis.http.post<Response>(
          AUTH_API + 'login',
          {
            "user": username,
            "password": password,
          },
          httpOptions
        )
    }

    Comme vous le voyez, nous avons défini notre appel au point de terminaison login à partir duquel nous récupérerons la réponse et stockerons l'access_token pour authentifier ultérieurement nos appels.

    Le stockage du jeton reçu en réponse sera effectué par le service storage.service.ts :

    save(userToken: string): void {
        window.sessionStorage.setItem(USER_KEY, userToken);
    }

    Avec notre jeton stocké, il nous suffit d'intercepter chaque requête envoyée à IRIS et d'insérer le jeton dans l'en-tête. Pour ce faire, nous allons définir un HttpInterceptor dans la classe auth.interceptor.ts.

    intercept(req: HttpRequest<any>, next: HttpHandler) {
        // Get the auth token from the service.const authToken = this.storageService.getToken();
    
        // Clone the request and replace the original headers with// cloned headers, updated with the authorization.if (authToken !== ''){
            req = req.clone({
                headers: req.headers.set('Authorization', 'Bearer ' + authToken)
              });
        }
        // send cloned request with header to the next handler.return next.handle(req);
    }

    Configuration de l'authentification JWT dans IRIS

    Pour que notre IRIS puisse fournir ces services, nous devons configurer correctement notre application web et pour cela nous y accéderons à partir de l'option de menu Administration -> Security -> Applications -> Web Applications (Administration -> Sécurité -> Applications -> Applications web).

    Lorsque nous sommes dans la configuration de l'application, nous devons marquer l'utilisation de l'authentification JWT et nous pouvons définir le délai d'attente pour le jeton d'accès et le jeton d'actualisation :

    Avec cette configuration, nous n'aurons besoin de rien d'autre pour commencer à utiliser ce mode d'authentification dans nos applications web.

    Un commentaire sur la configuration ou l'utilisation de l'authentification JWT est toujours bienvenu si vous avez des questions à ce sujet.

    Le prochain article présentera la configuration de notre projet QuinielaML pour effectuer du web scraping en utilisant Embedded Python.

    0
    0 181
    Article Iryna Mykhailova · Oct 6, 2023 5m read

    Bienvenue chers membres de la Communauté à la présentation et au premier article d'un petit projet qui présentera les capacités d'InterSystems IRIS à fournir une fonctionnalité de sauvegarde complète pour une application web développée en Angular. Dans cet article, nous nous contenterons de présenter le concept ainsi que les fonctionnalités d'InterSystems IRIS utilisées de manière générale, en allant plus en détail dans les articles suivants.

    Bienvenue à QuinielaML !

    Introduction

    Peut-être avez-vous déjà entendu parler des fonctionnalités d'InterSystems IRIS comme Embedded Python et IntegratedML, mais certainement n'avez-vous pas eu l'occasion de les utiliser ou vous ne savez tout simplement pas comment intégrer ces fonctionnalités dans votre quotidien. Profitant du début de la saison de football en Espagne, j'ai pensé que ce serait une excellente occasion d'expliquer toutes ces fonctionnalités d'InterSystems IRIS en développant une application qui nous aide à prédire les résultats du football et à devenir millionnaires avec le Quiniela.

    Qu'est-ce que Quiniela ?

    Lord Of The Rings Smeagol GIF - Lord Of The Rings Smeagol Gollum - DDécouvrez et partagez des

    Pour ceux d'entre vous qui ne connaissent pas ce terme, la Quiniela est un type de pari sportif très célèbre en Espagne car, pendant plusieurs années, il s'agissait du seul format de pari sportif légal.

    Grosso modo, la Quiniela est un type de pari sportif qui consiste à deviner qui sera le vainqueur de 15 matchs à chaque tour (10 matchs de première division et 5 matchs de deuxième division). La victoire de l'équipe locale est marquée par un 1, celle de l'équipe visiteuse par un 2 et l'égalité par un X.

    Voici un billet de la Quiniela.

    Quiniela Elige8, el nuevo juego asociado a la quiniela. - Loteria Cano

    Comment prédire les résultats du Quiniela ?

    Nous ne pouvons pas vraiment prédire un résultat, mais nous pouvons estimer la probabilité de chaque résultat en fonction de certaines variables. Ici, nous avons pris en compte les éléments suivants :

    1. Résultats historiques : résultats des matches entre deux équipes depuis 2000.
    2. Série de victoires de l'équipe locale : L'équipe qui reçoit a gagné les 3 derniers matchs à domicile.
    3. Série de victoires de l'équipe jouant à l'extérieur : Victoire de l'équipe jouant à l'extérieur lors des derniers matches joués à l'extérieur.
    4. Arbitre : résultat des équipes selon l'arbitre.

    Comment obtenir toutes ces données pour construire notre modèle de prédiction ?

    Le web scraping à la rescousse ! Toutes ces données historiques sont accessibles sur le web, il nous suffira donc d'implémenter à partir de notre IRIS un moyen d'obtenir ces données depuis un site web particulier, pour notre exemple nous avons utilisé BDFutbol, qui nous fournit des données concernant tous les matchs joués historiquement à la fois en première et en deuxième division.

    Comment pouvons-nous effectuer un tel web scraping ? Très simplement, avec Embedded Python, il nous suffit d'importer les bibliothèques Python beautifulsoup4 et requests dans notre serveur IRIS et nous serons en mesure d'obtenir les informations nécessaires à partir du site web en question.

    Architecture du projet

    En tant qu'ancien développeur, j'ai choisi Angular pour développer la partie interface utilisateur et ainsi éviter de trop me compliquer la vie. Il est évident que le choix du backend se portera sur InterSystems IRIS avec la fonctionnalité IntegratedML incluse dans sa version communautaire.

    Voici un petit schéma de l'architecture choisie :

    Comment le déployer et le former :

    L'ensemble du projet est Dockerisé, donc il vous suffit de télécharger le code source depuis GitHub sur votre ordinateur local, d'installer Docker et Docker Compose et d'exécuter les instructions suivantes depuis le terminal, dans le chemin d'accès où vous avez téléchargé le projet :

    docker-compose build
    docker-compose up -d

    Une image IRIS d'InterSystems sera automatiquement déployée sur le port 52774 (en utilisant l'accès habituel au portail de gestion) et l'application Angular directement à cette URL.

    Accès à QuinielaML

    Une fois la page de connexion ouverte, il faut saisir le nom d'utilisateur et le mot de passe :

    superuser / SYS

    Celle-ci sera redirigée directement vers l'écran des prédictions, mais il est nécessaire d'afficher le menu latéral en cliquant sur l'icône de l'application en haut à gauche :

    Et choisir **Gestion des données ** dans le menu :

    Nous accédons ainsi à l'écran à partir duquel nous pouvons importer les données du site web BDFutbol, les préparer pour calculer les stries et modifier les valeurs textuelles en format numérique, et enfin créer le modèle de données et le former à l'aide des données préparées.

    Vous devez commencer par la première option à gauche et vous déplacer dans la direction des flèches, en attendant que l'étape précédente soit terminée :

    Génération de prédictions :

    Une fois le modèle formé, il ne nous reste plus qu'à entrer les matchs de la journée que nous voulons prédire. Pour ce faire, nous cliquerons sur l'icône de l'application en haut à gauche et choisirons Prédiction de résultats

    Sur cet écran, il suffit de remplir les champs en haut pour générer une prédiction adéquate, il suffit d'ajouter les données des équipes pour générer la prédiction, mais plus il y a de données, mieux c'est.

    La partie frontend n'a pas été entièrement revue, donc vous pouvez trouver un bug, n'hésitez pas à me le faire savoir pour que je puisse l'améliorer.

    Pour chaque match que vous enregistrez, vous verrez automatiquement la prédiction du résultat fournie par le modèle. Une fois la ronde terminée, vous pouvez entrer le résultat pour avoir une idée de la prédiction approximative en cliquant sur le match en question.

    C'est bien ! Vous avez déjà tout pour devenir millionnaire avec la Quiniela et InterSystems IRIS. Dans le prochain article, nous expliquerons comment ouvrir une session depuis Angular avec InterSystems IRIS via JWT.

    À la prochaine fois !

    2
    0 184
    Annonce Sylvain Guilbaud · Sept 13, 2023

    L'équipe InterSystems se rend ce week-end au plus grand hackathon du MIT, où nous présenterons un défi technologique pour les hackers.
    Nous proposons aux hackers d'utiliser IntegratedML ou InterSystems Supply Chain Orchestrator dans leurs projets afin de concourir pour des prix vraiment sympas !

    Si vous êtes à Boston et que vous êtes intéressé pour devenir un mentor InterSystems lors de l'événement, envoyez-moi un message.

     

    2
    0 77
    Article Guillaume Rongier · Août 21, 2023 13m read

    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 @tomdet @Dmitry.Maslennikovpour 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 :

    CREATEMODEL 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);/// DescriptionParameter 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 = 0while (resultSet.%Next() '= 0) { if (resultSet.%GetData(1) '= "") { set modelExists = 1// OBTENIR LA PRÉDICTION D'UN SÉJOUR AVEC LE DERNIER ÉPISODE PERSISTANTset sqlPredict = "SELECT PREDICT("..ModelName") AS PredictedStay FROM MLTEST_Data.Episode WHERE %ID = ?"set statementPredict = ##class(%SQL.Statement).%New(), statement.%ObjectSelectMode = 1set 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ÉDICTIONset 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ÉESset sqlTrain = "TRAIN MODEL "..ModelNameset 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ÉSset 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 PERSISTANTset sqlPredict = "SELECT PREDICT("..ModelName_") AS PredictedStay FROM MLTEST_Data.Episode WHERE %ID = ?"set statementPredict = ##class(%SQL.Statement).%New(), statement.%ObjectSelectMode = 1set 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 { <MapItems> <MapItem MessageType="MLTEST.Data.PredictionRequest"> <Method>PredictStay</Method> </MapItem> </MapItems> }

    } 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.

    0
    0 73
    InterSystems officiel Benjamin De Boe · Juil 4, 2023

    InterSystems IRIS Cloud SQL est un service cloud entièrement géré qui apporte la puissance des capacités de base de données relationnelles d'InterSystems IRIS utilisées par des milliers d'entreprises clientes à un large public de développeurs d'applications et de professionnels des données. InterSystems IRIS Cloud IntegratedML est une option de cette base de données en tant que service qui offre un accès facile à de puissantes capacités d'apprentissage automatique automatisé sous une forme SQL native, via un ensemble de commandes SQL simples qui peuvent facilement être intégrées dans le code d'application pour augmenter avec des modèles ML qui s'exécutent près des données.

    Aujourd'hui, nous annonçons le programme d'accès pour les développeurs pour ces deux offres. Les développeurs d'applications peuvent désormais s'inscrire eux-mêmes au service, créer des déploiements et commencer à créer des applications composables et des services de données intelligents, l'approvisionnement, la configuration et l'administration étant entièrement pris en charge par le service.

    0
    0 40
    Article Irène Mykhailova · Mai 4, 2023 6m read

    Exemple d'utilisation de la base de données FHIR d'InterSystems IRIS for Health pour effectuer de modèles ML via InterSystems IRIS IntegratedML

    Description

    IntegratedML est une fonctionnalité intéressante pour former/tester et déployer des modèles ML. FHIR est un standard puissant pour l'interopérabilité des informations de santé. Ce projet vise à montrer comment utiliser les outils IRIS/IRIS for Health, par exemple les transformations DTL pour préparer les données FHIR à l'application de modèles ML dans IntegratedML. Voici quelques applications potentielles des idées présentées dans ce projet :

    0
    0 81
    Article Irène Mykhailova · Mai 2, 2023 2m read

    En discutant avec un de mes amis, spécialiste du Machine Learning @Renato Banzai , il a évoqué l'un des plus grands défis pour les entreprises aujourd'hui : le déploiement du ML/IA dans des environnements réels.

    InterSystems IRIS propose la solution "IntegratedML". IntegratedML est une fonctionnalité très utile pour entraîner, tester et déployer des modèles de ML/IA.

    La partie la plus difficile dans la création de ML/IA est de faire le traitement des données, de les nettoyer et de les rendre fiables.

    C'est là que nous pouvons tirer parti de la puissante norme FHIR !

    L'idée du projet montre comment nous pouvons créer/entraîner/valider des modèles MI/IA avec FHIR et les utiliser avec des données provenant de différentes sources.

    0
    0 57
    Article Sylvain Guilbaud · Avr 26, 2023 3m read

    Mise à jour : ajout du support pour le modèle de régression Bonjour à toutes et à tous !

    Dans ce bref article, je vous montrerai comment écrire un adaptateur pour IRIS Interoperability afin d'utiliser des modèles ML gérés par IRIS IntegratedML.

    L'adaptateur

    L'adaptateur utilise simplement les fonctions SQL d'IntegratedML comme prédiction PREDICT et probabilité PROBABILITY, pour obtenir la classe prédite par le modèle et sa probabilité. Il s'agit d'un simple SQL :
    ![code1][code1] [code1]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-it-works-1.png
    Notez que le nom du modèle est référencé par la propriété Model. Cette propriété doit être définie dans la classe hôte qui utilise l'adaptateur, sinon une exception sera produite. Par exemple:


    La liste des modèles dans les paramètres de l'adaptateur est établie en deux étapes :
    1. Création d'une méthode dans une classe qui étend %ZEN.Portal.ContextSearch pour charger tous les modèles de classification et les renvoyer (dc.Ens.Adapter.ClassficationMLContextSearch)
      ![code2][code2] [code2]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-it-works-2.png
    2. Configuration d'une telle classe et d'une telle méthode comme chargeur pour la propriété Model dans le paramètre SETTINGS de la classe adaptateur (dc.Ens.Adapter.ClassificationMLAdapter)
      ![code3][code3] [code3]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-it-works-3.png
      Pour les modèles de régression, il existe la classe dc.Ens.Adapter.RegressionMLContextSearch, qui charge tous les modèles de régression.

    Dans ce cas, le filtre MODE_TYPE est réglé sur "régression" au lieu de "classification" :

    Utilisation de l'adaptateur

    Pour la démonstration, j'ai simulé un système simple de paiement par carte de crédit avec des fonctions de détection des fraudes, à l'aide d'un modèle de classification ML. Lorsqu'une transaction suspecte est détectée, une alerte est émise.

    Pour utiliser l'adaptateur, créez une classe hôte (une classe de processus métier "Business Process" ou d'opération métier "Business Operation") qui utilise comme adaptateur la classe dc.ENS.Adapter.ClassificationMLAdapter.
    ![code4][code4] [code4]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-to-use-it-1.png
    Vous pouvez maintenant utiliser la méthode Classify() de l'adaptateur et fournir un échantillon des caractéristiques attendues par le modèle :
    Pour l'utiliser, créez une classe hôte (une classe de processus métier ou d'opération métier) qui utilise comme adaptateur la classe dc.ENS.Adapter.ClassificationMLAdapter
    ![code5][code5] [code5]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-to-use-it-2.png
    Vous pouvez les utiliser selon vos besoins. Dans l'exemple, seul le résultat de la prédiction de la fraude était nécessaire, la classe d'opération métier "Business Operation" utilise donc simplement la valeur renvoyée dans la propriété "Predicted" :
    ![code6][code6] [code6]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-to-use-it-4.png
    Pour les modèles de régression, les résultats sont modélisés par la classe dc.Ens.Adapter.RegressionResult. Cette classe possède une propriété appelée Estimated.

    Pour les modèles de régression, les résultats sont modélisés par la classe dc.Ens.Adapter.RegressionResult. Cette classe possède une propriété appelée Estimated. Pour obtenir une valeur estimée à partir d'un échantillon, la classe d'adaptateur pour le modèle de régression dispose de la méthode "Estimate".

    Le résultat final est affiché ci-dessous :
    ![code7][code7] [code7]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/KMWbgqw1C9.gif
    Le code complet est disponible dans OpenExchange. J'espère que cela pourra vous être utile. José

    0
    0 51
    Article Guillaume Rongier · Avr 24, 2023 10m read

    Avez-vous essayé le laboratoire de la plate-forme d'apprentissage d'InterSystems pour IRIS IntegratedML ? Dans ce laboratoire, vous pouvez former et tester un modèle sur un ensemble de données de réadmission et être en mesure de prédire quand un patient sera réadmis ou non, ou de calculer sa probabilité d'être réadmis.

    Vous pouvez l'essayer sans aucune installation sur votre système, tout ce que vous avez à faire est de démarrer un environnement de laboratoire virtuel (Zeppelin) et de jouer autour !

    Dans cet article, nous utiliserons ce laboratoire pour vous présenter brièvement IntegratedML, le problème à traiter, la manière d'utiliser IntegratedML pour créer un modèle prévisionnel de réadmission, ainsi que quelques idées sur la manière d'analyser ses mesures de performance.

    Qu'est-ce qu'IntegratedML ?

    ![Infographie d'IntegratedML](https://user-images.githubusercontent.com/8899513/85149599-7848f900-b21f-11ea-9b65-b5d703752de3.PNG)
    Source: https://github.com/intersystems-community/integratedml-demo-template

    Avant de commencer ce tutoriel, parlons brièvement d'IRIS IntegratedML. Cet outil vous permet d'effectuer des tâches d'apprentissage automatique (ML) directement dans des instructions SQL, en simplifiant l'implémentation de processus complexes tels que le choix des colonnes et des algorithmes ML les plus appropriés pour effectuer une classification ou une régression sur une colonne cible, par exemple.

    Une autre excellente fonctionnalité d'IntegratedML est son déploiement facile. Une fois votre modèle formé et performant, il vous suffit d'exécuter des instructions SQL pour le mettre en production.

    IntegratedML vous permet de choisir le fournisseur ML à utiliser. Le fournisseur par défaut est AutoML, une implémentation Python d'InterSystems utilisant la bibliothèque ML bien connue scikit-learn. Mais vous pouvez également choisir entre trois autres fournisseurs : PMML, H2O et DataRobot.

    Dans cet article, nous allons utiliser le fournisseur AutoML.

    Problème et solution

    Présentons maintenant le problème et la manière dont nous pouvons nous y prendre pour proposer un moyen d'en minimiser les impacts. Nous allons essayer de réduire les problèmes liés aux réadmissions.

    Selon Wikipedia, une réadmission à l'hôpital est un épisode au cours duquel un patient est autorisé à quitter l'hôpital (déchargé), mais cette personne revient (réadmet) à nouveau à l'hôpital dans un intervalle de temps court et inattendu.

    ![Infographie sur les réadmissions à l'hôpital][https://news.yale.edu/sites/default/files/styles/horizontal_image/public/d6_files/Hospital_v03_YNews.jpg]
    Source: https://news.yale.edu/2015/01/15/when-used-effectively-discharge-summaries-reduce-hospital-readmissions

    Les réadmissions entraînent des pertes de qualité de soins pour les patients - le temps écoulé entre la sortie et la réadmission peut être critique - ainsi qu'une optimisation des ressources hospitalières.

    Une approche pour surmonter ce problème consiste à essayer d'utiliser une base de données historique pour créer un ensemble de données dans lequel les épisodes de réadmission passés pourraient être analysés par des algorithmes d'apprentissage automatique, créant ainsi un modèle ML. Si l'ensemble de données est suffisamment riche et propre, les schémas de réadmission pourraient être correctement détectés et la probabilité de nouveaux épisodes pourrait être calculée par ce modèle.

    Ainsi, la possibilité d'éviter les décharges erronées en utilisant un modèle ML pour prédire les réadmissions sera certainement une option précieuse pour les hôpitaux afin d'augmenter la qualité et le profit de leurs services.

    Création et utilisation d'un modèle ML à l'aide de IntegratedML

    Création du modèle

    La création d'un modèle ML à l'aide d'IntegratedML est aussi simple que l'exécution d'une simple instruction SQL. Il vous suffit de définir l'ensemble de données historiques où se trouvent les données et de désigner votre modèle par un nom :

    CREATE MODEL Readmission PREDICTING (MxWillReAdmit) FROM EncountersHistory
    

    Après cette instruction, vous avez déclaré et créé un modèle appelé Readmission destiné à prédire les valeurs d'une colonne appelée MxWillReAdmit, sur la base d'un ensemble de données appelé EncountersHistory.

    Vous pouvez trouver plus d'informations sur cette instruction ici. Par exemple, dans la phase de conception, il est pratique de forcer les mêmes résultats à la formation, donc vous pouvez utiliser l'argument USING avec un nombre constant arbitraire, comme suit :

    CREATE MODEL Readmission PREDICTING (MxWillReAdmit) FROM EncountersHistory USING {"seed": 3}
    

    Formation de modèle

    Votre modèle est maintenant prêt à être formé, en utilisant l'instruction TRAIN MODEL (former le modèle) :

    TRAIN MODEL Readmission
    

    Ici, IntegratedML fait une bonne partie du travail pour vous (en utilisant le fournisseur de ML " AutoML") :

    • L'ingénierie des fonctionnalités: quelles colonnes utiliser ?
    • Codage de données: comment les données dans les colonnes choisies doivent être présentées aux algorithmes de ML
    • Sélection de l'algorithme ML: quel algorithme permet d'obtenir les meilleurs résultats ?
    • Sélection du modèle: en fonction du type de données de la colonne cible, un modèle de classification ou un modèle de régression doit être sélectionné. Dans cet exemple, un modèle de classification sera sélectionné lorsque la colonne cible contient des valeurs booléennes.

    Cette déclaration peut prendre un certain temps, en fonction de la taille de l'ensemble de données et de la complexité de vos données.

    Validation du modèle

    A présent, nous devrions être impatients de connaître la qualité réelle de notre modèle. La méthode ML intégrée (Integrated ML) propose une déclaration pour le calcul des mesures de performance, sur la base d'un ensemble de données différent de celui utilisé pour la formation - sinon, vous ne seriez pas juste avec vous-même, n'est-ce pas ? :)

    Ce nouvel ensemble de données est appelé ensemble de données de test ou de validation, selon la stratégie utilisée pour la validation. Cet ensemble de données est généralement extrait du même ensemble de données que celui utilisé pour la création de l'ensemble de données de formation. Une approche courante consiste à sélectionner au hasard 70 ou 80 % de l'ensemble de données pour la formation et à laisser le reste pour le test/la validation.

    Dans le laboratoire, un nouvel ensemble de données prêt à l'emploi a été préparé pour cette tâche : l'ensemble de données EncountersNew.

    Nous pouvons maintenant déterminer la qualité (ou la médiocrité) de notre modèle :

    VALIDATE MODEL Readmission FROM EncountersNew
    

    Pour obtenir les résultats, vous devez interroger le tableau INFORMATION_SCHEMA.ML_VALIDATION_METRICS :

    SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS
    

    La performance du modèle est mesurée en quatre mesures par IntegratedML:

    • Précision : taux de prédictions correctes (des valeurs proches de 1 signifient des taux élevés de réponses correctes).
    • Précision : taux de prédictions positives correctes concernant toutes les prédictions positives faites par le modèle (des valeurs proches de 1 signifient un petit nombre de prédictions faussement positives).
    • Rappel : taux de prédictions positives correctes concernant toutes les valeurs positives réelles dans l'ensemble de données (des valeurs proches de 1 signifient un petit nombre de prédictions faussement négatives).
    • Mesure F : une autre façon de mesurer la précision, utilisée lorsque la précision n'est pas satisfaisante, généralement pour les problèmes de déséquilibre (des valeurs proches de 1 signifient un taux élevé de réponses correctes).

    Discussion sur les mesures de performance

    Ici, nous pouvons voir que le modèle a une précision de 82%. Mais cette mesure ne doit pas être analysée seule, d'autres mesures telles que la précision et le rappel doivent également être évaluées.

    Réfléchissons aux implications des prédictions erronées - faux positifs et faux négatifs. Pour notre modèle, un faux positif signifie qu'une réadmission prévue n'était pas une réadmission réelle. En revanche, un faux négatif signifie qu'un patient a été réadmis et que le modèle a prédit qu'il ne le serait pas.

    Dans les deux cas, des décisions erronées pourraient être prises. Une prédiction faussement positive pourrait conduire à la décision de garder un patient à l'hôpital plus longtemps que nécessaire, tandis qu'une prédiction faussement négative pourrait conduire à la décision de faire sortir un patient prématurément, et de le réadmettre ensuite.

    Il est à noter que, dans notre modèle, un faux négatif conduit à des cas de réadmission, ce que nous essayons d'éviter. Il faut donc choisir des modèles qui diminuent le nombre de faux négatifs, même si le nombre de faux positifs augmente.

    Ainsi, pour choisir des modèles avec de faibles taux de faux négatifs, nous devons choisir des modèles avec des taux de rappel élevés. En effet, plus le taux de rappel est élevé, moins le taux de faux négatifs est important.

    Comme notre modèle a un taux de rappel de 84 %, considérons qu'il s'agit d'une valeur raisonnable pour l'instant.

    Faire des prédictions

    Une fois que votre modèle a été entraîné et que ses performances sont acceptables, vous pouvez maintenant l'exécuter afin de prédire les résultats.

    Vous devez utiliser la fonction PREDICT afin d'utiliser un modèle pour effectuer des prédictions :

    SELECT TOP 100
        ID,
        PREDICT(Readmission) AS PredictedReadmission,
        MxWillReAdmit AS ActualReadmission
    FROM
        EncountersNew
    

    Cette fonction évalue la probabilité que la colonne cible du modèle d'une ligne soit une valeur vraie ou fausse, sur la base d'un seuil interne.

    Vous pouvez constater que notre modèle a fait des prévisions erronées, ce qui est normal.

    La fonction de probabilité PROBABILITY est une autre fonction qui peut être utilisée pour faire des prédictions.

    SELECT TOP 10
        ID,
        PROBABILITY(Readmission FOR '1') AS ReadmissionProbability,
        PREDICT(Readmission) AS PredictedReadmission,
        MxWillReAdmit AS ActualReadmission
    FROM
        EncountersNew
    

    De la même manière que PREDICTION, PROBABILITY utilise les colonnes d'une ligne comme entrée pour le modèle spécifié, mais ici nous devons définir dans quelle classe nous voulons calculer la probabilité.

    Cette fonction peut s'avérer utile si l'on souhaite personnaliser le seuil utilisé pour effectuer les prédictions.

    Requête de modèles formés

    Après avoir créé et formé vos modèles, vous pouvez les requérir à l'aide du tableau INFORMATION_SCHEMA.ML_TRAINED_MODELS, comme suit :

    SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS
    

    Conclusion

    Dans cet article, nous avons créé, formé, validé et fait des prédictions sur un ensemble de données historiques de réadmission de patients. Ces prédictions pourraient être utilisées comme un outil supplémentaire dans la recherche de meilleures décisions qui pourraient améliorer les soins aux patients et réduire les coûts des services hospitaliers. Toutes ces étapes ont été réalisées en utilisant le bon vieux langage SQL grâce à InterSystems IntegratedML, qui met la puissance de l'apprentissage automatique à la portée d'un grand nombre de développeurs.

    0
    0 53
    Article Irène Mykhailova · Avr 12, 2023 4m read

    Cet article est le deuxième d'une série expliquant comment créer un système d'apprentissage automatique de bout en bout.

    Exploration des données

    L'Intersystème IRIS dispose déjà de ce dont nous avons besoin pour explorer les données : un moteur SQL ! Pour les personnes qui ont l'habitude d'explorer des données dans des fichiers
    csv ou des fichiers texte, cela pourrait aider à accélérer cette étape. Fondamentalement, nous explorons toutes les données pour comprendre l'intersection (jointures), ce qui devrait permettre de créer un jeu de données préparé pour être utilisé par un algorithme d'apprentissage automatique.

    Tableau d'articles ( Fourni par l'équipe Intersystems )

    image

    Tableaude balises ( Fourni par l'équipe Intersystems )

    image

    Notre défi consiste à classer un article avec les bonnes balises. Et nous avons un tas d'articles et toutes les balises avec description. Il existe plusieurs techniques de classification, mais les cas les plus courants de classification utilisent des données structurées ! Un article, un long texte n'est pas exactement structuré de ce point de vue.

    L'apprentissage automatique fonctionne la plupart du temps avec des NOMBRES - Faites avec !

    Oui ! Même le modèle de données le plus normalisé contient du texte, mais lorsque nous devons utiliser des algorithmes de ML, nous devons trouver une solution pour convertir tous les textes en nombres. Mais n'oubliez pas que les *données non structurées ne prendront pas les nombres prêts à être utilisés dans un modèle de classification. Il est temps de...

    Ingénierie des caractéristiques

    Dans cette étape, nous convertissons le texte, les nombres, les éléments non structurés et le chaos dans une matrice (la plupart du temps)... oui, cet élément de vos cours d'algèbre d'autrefois. Si nous avons un tableau qui ressemble déjà à une matrice, vous aurez probablement moins de travail que nous ! Nous avons un grand texte avec des tailles, des formes et des mots différents...

    Sac de mots

    L'une des façons de transformer un texte long en une matrice est le sac de mots :

    expression
    Nicole devrait acheter une voiture
    Jack devrait vendre son bateau
     [Nicole, Jack, devrait, acheter, vendre, une, son, voiture, bateau]
     [1     ,    0,      1,   1,    0, 1,   0,   1,    0]
     [0     ,    1,      1,   0,    1, 0,   1,   0,    1]
    

    Ce n'est qu'un petit exemple. Mais vous pouvez constater que plus il y a de mots, plus la matrice est grande. Heureusement, nous disposons de nombreux composants pour vous aider à créer des sacs de mots. Dans ce cas, j'ai utilisé les composants de Sklearn pour le faire.

    from sklearn.feature_extraction.text import CountVectorizer
    from sklearn.preprocessing import MultiLabelBinarizer
    
    ml_bin = MultiLabelBinarizer(classes=all_tags)
    matrix_y = ml_bin.fit_transform(a_dataframe)
    
    count_vec = CountVectorizer(ngram_range=(1,1), tokenizer=tokenize, strip_accents='unicode', stop_words=stop_words)
    matrix_x = count_vec.fit_transform(a_dataframe)
    

    Après avoir exécuté les méthodes ci-dessus, nous disposons d'un vectoriseur que nous devrons conserver tout le temps que nous voulons prédire. Si nous changeons le vectoriseur, nous risquons de perturber les matrices et rien ne devrait fonctionner par la suite.

    Est-ce que je vous ai déjà dit que nous ne devions utiliser que des chiffres ?

    Il y a un autre problème ! Pour certains algorithmes, la taille du nombre peut perturber vos intentions. En bref, si un élément de la matrice a une valeur de 1 et qu'un autre élément a une valeur de 987, certains algorithmes pourraient interpréter cela comme l'importance de l'élément et prendre la mauvaise direction.

    Algorithmes de classification ML

    Il existe de nombreux documents à lire sur les algorithmes de classification, vous pouvez commencer par cet article : https://machinelearningmastery.com/types-of-classification-in-machine-learning/

    Article suivant : stratégie de formation (pour les données, oubliez les GIMS)

    J'espère que vous appréciez. Si vous aimez le texte et mon application, votez sur https://openexchange.intersystems.com/contest/current dans mon application iris-ml-suite

    0
    0 55
    Article Guillaume Rongier · Avr 7, 2023 9m read

    Suite à la partie précédente, il est temps de tirer parti de l'instruction de VALIDATION DU MODÈLE IntegratedML, qui fournit des informations permettant de surveiller vos modèles ML. Vous pouvez la voir en action ici

    Le code présenté ici est dérivé d'exemples fournis par le modèle InterSystems IntegratedML ou la documentation IRIS, documentation

    Remarque: Le code présenté ici n'a qu'une valeur explicative. Si vous souhaitez l'essayer, j'ai développé une application modèle - iris-integratedml-monitor-example, qui participe au concours IA d'InterSystems IRIS (InterSystems IRIS AI Contest). S'il vous plaît, après avoir lu cet article, vous pouvez le consulter et, si vous l'aimez, votez pour moi! :)

    Contenu

    Partie I:

    Partie II:

    Surveillance des performances du ML

    Pour surveiller votre modèle ML, vous aurez besoin d'au moins deux fonctions :

    1. Fournisseur de mesures de performance
    2. Service de surveillance et de notification

    Heureusement, IRIS nous fournit ces deux caractéristiques nécessaires.

    Obtenir des mesures de performance des modèles ML

    Comme nous l'avons vu dans la partie précédente, IntegratedML fournit l'instruction VALIDER LE MODÈLE pour calculer les paramètres de performance suivants :

    • Exactitude : la qualité de votre modèle (des valeurs proches de 1 signifient des taux élevés de réponses correctes).
    • Précision : dans quelle mesure votre modèle traite les faux positifs (des valeurs proches de 1 signifient un taux élevé de non faux positifs).
    • Rappel : dans quelle mesure votre modèle traite les faux négatifs (des valeurs proches de 1 signifient un taux élevé de non faux négatifs).
    • Mesure F : une autre façon de mesurer la précision, utilisée lorsque la précision n'est pas satisfaisante (des valeurs proches de 1 signifient un taux élevé de réponses correctes).

    Remarque: ces définitions ne sont pas formelles, elles sont même assez superficielles ! Je vous encourage à prendre le temps de les interpréter.

    Ce qui est intéressant, c'est qu'à chaque fois que vous appelez VALIDER LE MODÈLE (VALIDATE MODEL), IntegrateML enregistre sa mesure de performance, et nous pouvons tirer parti de cette fonctionnalité pour la surveiller.

    Moteur de surveillance

    InterSystems IRIS fournit le framework du Moniteur de Système pour traiter les tâches de surveillance. Il vous permet également de définir des règles personnalisées afin de déclencher des notifications basées sur des prédicats appliqués à ces mesures.

    Par défaut, un ensemble de mesures est fourni pour le disque, la mémoire, les processus, le réseau, etc. En outre, le Moniteur de Système vous permet également d'étendre les moniteurs pour couvrir une infinité de possibilités. De tels moniteurs personnalisés sont appelés une application de moniteur (Application Monitor) dans la terminologie de moniteur de système (System Monitor).

    Vous pouvez obtenir plus d'informations sur le Moniteur de Système ici.

    La mise en place de l'ensemble

    Jusqu'à présent, nous avons un moyen d'obtenir les valeurs des métriques de performance de chaque validation du modèle et, un outil qui pourrait déclencher des alertes basées sur des règles personnalisées appliquées à des sources de métriques personnalisées... Bien, il est temps de les combiner.

    Tout d'abord, nous devons créer une classe de moniteur d'application personnalisée, en étendant la classe %Monitor.Abstract et en implémentant les méthodes Initialize et GetSample.

    Class MyMetric.IntegratedMLModelsValidation Extends %Monitor.Adaptor
    {
    
    /// Initialiser la liste des métriques de validation des modèles.
    Method Initialize() As %Status
    {
        Return $$$OK
    }
    
    /// Obtenir un échantillon métrique de routine.
    /// Un code de retour de $$$OK indique qu'il y a une nouvelle instance d'échantillon.
    /// Tout autre code de retour indique qu'il n'y a pas d'échantillon.
    Method GetSample() As %Status
    {
        Return $$$OK
    }
    
    }
    

    Les moniteurs système émettent des appels réguliers aux classes de moniteurs afin d'obtenir un ensemble de mesures appelées échantillons. Ces échantillons peuvent être simplement surveillés ou utilisés pour vérifier si des règles d'alerte doivent être levées. Vous définissez la structure de ces échantillons en définissant des propriétés standard non internes dans la classe monitior. Il est important de souligner ici que vous devez spécifier, dans le paramètre INDEX, l'une de ces propriétés comme clé primaire de chaque échantillon - sinon une erreur de clé dupliquée sera générée.

    Class MyMetric.IntegratedMLModelsValidation1 Extends %Monitor.Adaptor
    {
    
    Parameter INDEX = "ModelTrainedName";
    
    /// Nom de la définition du modèle
    Property ModelName As %Monitor.String;
    
    /// Nom du modèle formé en cours de validation
    Property ModelTrainedName As %Monitor.String;
    
    /// Erreur de validation (le cas échéant)
    Property StatusCode As %Monitor.String;
    
    /// Précision
    Property ModelMetricPrecision As %Monitor.Numeric;
    
    /// Rappel
    Property ModelMetricRecall As %Monitor.Numeric;
    
    /// Mesure F
    Property ModelMetricFMeasure As %Monitor.Numeric;
    
    /// Exactitude
    Property ModelMetricAccuracy As %Monitor.Numeric;
    
    ...
    
    }
    

    La méthode Initialize est appelée une fois pour chaque appel de moniteur et la méthode GetSample est appelée jusqu'à ce qu'elle renvoie 0.

    Ainsi, nous pourrions mettre en place un SQL sur l'historique de validation d'IntegrateML pour fournir des informations de métriques au moniteur, en implémentant les méthodes Initialize et GetSample :

    /// Initialiser la liste des métriques de validation des modèles.
    Method Initialize() As %Status
    {
    	// Obtenir la dernière validation pour chaque modèle validé par l'instruction VALIDATION DU MODÈLE
    	Set sql =
    	"SELECT MODEL_NAME, TRAINED_MODEL_NAME, STATUS_CODE, %DLIST(pair) AS METRICS_LIST FROM ("_
    		"SELECT m.*, $LISTBUILD(m.METRIC_NAME, m.METRIC_VALUE) pair, r.STATUS_CODE "_
    		"FROM INFORMATION_SCHEMA.ML_VALIDATION_RUNS r "_
    		"JOIN INFORMATION_SCHEMA.ML_VALIDATION_METRICS m "_
    		"ON m.MODEL_NAME = r.MODEL_NAME "_
    			"AND m.TRAINED_MODEL_NAME = r.TRAINED_MODEL_NAME "_
    			"AND m.VALIDATION_RUN_NAME = r.VALIDATION_RUN_NAME "_
    		"GROUP BY m.MODEL_NAME, m.METRIC_NAME "_
    		"HAVING r.COMPLETED_TIMESTAMP = MAX(r.COMPLETED_TIMESTAMP)"_
    	") "_
    	"GROUP BY MODEL_NAME"
        Set stmt = ##class(%SQL.Statement).%New()
        $$$THROWONERROR(status, stmt.%Prepare(sql))
        Set ..Rspec = stmt.%Execute()
        Return $$$OK
    }
    
    /// Obtenir un échantillon métrique de routine.
    /// Un code de retour de $$$OK indique qu'il y a une nouvelle instance d'échantillon.
    /// Tout autre code de retour indique qu'il n'y a pas d'échantillon.
    Method GetSample() As %Status
    {
        Set stat = ..Rspec.%Next(.sc)
        $$$THROWONERROR(sc, sc)
    
        // Quitter si nous avons fait tous les jeux de données
        If 'stat {
            Quit 0
        }
    
        // remplir cette instance
        Set ..ModelName = ..Rspec.%Get("MODEL_NAME")
        Set ..ModelTrainedName = ..Rspec.%Get("TRAINED_MODEL_NAME")_" ["_$zdt($zts,3)_"]"
        Set ..StatusCode = ..Rspec.%Get("STATUS_CODE")
        Set metricsList = ..Rspec.%Get("METRICS_LIST")
        Set len = $LL(metricsList)
        For iMetric = 1:1:len {
    	    Set metric = $LG(metricsList, iMetric)
    	    Set metricName = $LG(metric, 1)
    	    Set metricValue = $LG(metric, 2)
    	    Set:(metricName = "PRECISION") ..ModelMetricPrecision = metricValue
    	    Set:(metricName = "RECALL") ..ModelMetricRecall = metricValue
    	    Set:(metricName = "F-MEASURE") ..ModelMetricFMeasure = metricValue
    	    Set:(metricName = "ACCURACY") ..ModelMetricAccuracy = metricValue
        }
    
        // quitter avec une valeur de retour indiquant que les données de l'échantillon sont prêtes
        Return $$$OK
    }
    

    Après avoir compilé la classe de moniteur, vous devez redémarrer le moniteur de système System Monitor afin que le système comprenne qu'un nouveau moniteur a été créé et qu'il est prêt à être utilisé. Pour ce faire, vous pouvez utiliser la routine ^%SYSMONMGR ou la classe %SYS.Monitor.

    Un cas d'utilisation simple

    Jusqu'à présent, nous disposons des outils nécessaires pour collecter, surveiller et émettre des alertes sur les mesures de performance du ML. Il est maintenant temps de définir une règle d'alerte personnalisée et de simuler un scénario dans lequel un modèle ML déployé commence à affecter négativement vos performances.

    Tout d'abord, nous devons configurer une alerte par courriel et sa règle de déclenchement. Cela peut être fait à l'aide de la routine ^%SYSMONMGR. Cependant, afin de rendre les choses plus faciles, j'ai créé une méthode d'installation qui définit toute la configuration de l'e-mail et de la règle d'alerte. Vous devez remplacer les valeurs entre <&gt ; par les paramètres de votre serveur de messagerie et de votre compte.

    ClassMethod NotificationSetup()
    {
    	// Régler les paramètres de l'e-mail
    	Set sender = "<your e-mail address>"
    	Set password = "<your e-mail password>"
    	Set server = "<SMTP server>"
    	Set port = "<SMTP server port>"
    	Set sslConfig = "default"
    	Set useTLS = 1
    	Set recipients = $LB("<comma-separated receivers for alerts>")
    	Do ##class(%Monitor.Manager).AppEmailSender(sender)
    	Do ##class(%Monitor.Manager).AppSmtpServer(server, port, sslConfig, useTLS)
    	Do ##class(%Monitor.Manager).AppSmtpUserName(sender)
    	Do ##class(%Monitor.Manager).AppSmtpPassword(password)
    	Do ##class(%Monitor.Manager).AppRecipients(recipients)
    
    	// La messagerie électronique comme méthode de notification par défaut
    	Do ##class(%Monitor.Manager).AppNotify(1)
    
    	// Activer les notifications par messagerie électronique
    	Do ##class(%Monitor.Manager).AppEnableEmail(1)
    
    	Set name  = "perf-model-appointments-prediction"
    	Set appname = $namespace
    	Set action = 1
    	Set nmethod = ""
    	Set nclass = ""
    	Set mclass = "MyMetric.IntegratedMLModelsValidation"
    	Set prop = "ModelMetricAccuracy"
    	Set expr = "%1 < .8"
    	Set once = 0
    	Set evalmethod = ""
    	// Créer une alerte
    	Set st = ##class(%Monitor.Alert).Create(name, appname, action, nmethod, nclass, mclass, prop, expr, once, evalmethod)
    	$$$THROWONERROR(st, st)
    
    	// Relancer le moniteur
    	Do ##class(MyMetric.Install).RestartMonitor()
    }
    

    Dans la méthode précédente, une alerte sera émise si le moniteur obtient des valeurs de précision inférieures à 90 %.

    Maintenant que notre règle d'alerte est configurée, créons, formons et validons un modèle de prédiction show/no-show avec les 500 premiers enregistrements et validons-le avec les 600 premiers enregistrements.

    Remarque : Le paramètre seed sert uniquement à garantir la reproductibilité (c.-à-d. pas de valeurs aléatoires) et doit normalement être évité en production.

    -- Création du modèle
    CREATE MODEL AppointmentsPredection PREDICTING (Show) FROM MedicalAppointments USING {\"seed\": 3}
    -- Formation à l'aide des 500 premiers enregistrements du jeu de données
    TRAIN MODEL AppointmentsPredection FROM MedicalAppointments WHERE ID <= 500 USING {\"seed\": 3}
    -- Affichage des informations sur le modèle
    SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS
    
    |   | NOM_DU_MODÈLE             | NOM_DU_MODÈLE_FORMÉ      | FOURNISSEUR | HORODATAGE_FORMÉ       | TYPE_DU_MODÈLE     | INFOS_MODÈLE                                        |
    |---|------------------------|-------------------------|----------|-------------------------|----------------|---------------------------------------------------|
    | 0 | AppointmentsPredection | AppointmentsPredection2 | AutoML   | 2020-07-12 04:46:00.615 | classification | ModelType:Logistic Regression, Package:sklearn... |
    

    Notez que IntegrateML, en utilisant AutoML comme fournisseur (colonne FOURNISSEUR), déduit du jeu de données fourni, un modèle de classification (colonne TYPE_DU_MODÈLE), avec l'algorithme de régression logistique, à partir de la bibliothèque scikit-learn (colonne INFOS_MODÈLE). Il est important de souligner ici la règle "Garbage In, Garbage Out" ("à données inexactes, résultats erronés") - c'est-à-dire que la qualité du modèle est directement liée à la qualité des données.

    Poursuivons maintenant avec la validation du modèle.

    -- CCalculer les performances du modèle en utilisant les 600 premiers enregistrements (500 de l'ensemble formé + 100 pour le test).
    VALIDATE MODEL AppointmentsPredection FROM MedicalAppointments WHERE ID < 600 USING {\"seed\": 3}
    -- Afficher les mesures de validation
    SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS WHERE MODEL_NAME = '%s'
    
    | NOM_MÉTRIQUE              | Exactitude | Mesure F | Précision | Rappel |
    |--------------------------|----------|-------------|-----------|--------|
    | AppointmentsPredection21 | 0.9      | 0.94        | 0.98      | 0.91   |
    

    Le modèle peut être utilisé pour effectuer des prédictions à l'aide de l'instruction PREDICT :

    SELECT PREDICT(AppointmentsPredection) As Predicted, Show FROM MedicalAppointments  WHERE ID <= 500
    
    |     | Prédit    | Affiché|
    |-----|-----------|-------|
    | 0   | 0         | Faux |
    | 1   | 0         | Faux |
    | 2   | 0         | Faux |
    | 3   | 0         | Faux |
    | 4   | 0         | Faux |
    | ... | ...       | ...   |
    | 495 | 1         | Vrai  |
    | 496 | 0         | Vrai  |
    | 497 | 1         | Vrai  |
    | 498 | 1         | Vrai  |
    | 499 | 1         | Vrai  |
    

    Ensuite, simulons l'ajout de 200 nouveaux enregistrements (pour un total de 800 enregistrements) au modèle de manière à ce que sa précision soit ramenée à 87 %.

    -- Calculer les performances du modèle en utilisant les 800 premiers enregistrements.
    VALIDATE MODEL AppointmentsPredection FROM MedicalAppointments WHERE ID < **800** USING {\"seed\": 3}
    -- Afficher les mesures de validation
    SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS WHERE MODEL_NAME = '%s'
    
    | NOM_MÉTRIQUE              | Exactitude | Mesure F | Précision | Rappel |
    |--------------------------|----------|-----------|-----------|--------|
    | AppointmentsPredection21 | 0.9      | 0.94      | 0.98      | 0.91   |
    | AppointmentsPredection22 | 0.87     | 0.93      | 0.98      | 0.88   |
    

    Comme nous avons mis en place une règle pour envoyer une notification par messagerie électronique si la précision est inférieure à 90%, le moniteur de système System Monitor comprend qu'il est temps de déclencher une telle alerte vers le(s) compte(s) de messagerie électronique concerné(s).

    Dans le corps du message, vous pouvez trouver des informations sur l'alerte, telles que son nom, le moniteur d'application et ses valeurs métriques qui ont déclenché l'alerte.

    Ainsi, une telle situation sera notifiée aux personnes qui pourront prendre des mesures pour y remédier. Par exemple, une action pourrait consister simplement à réentraîner le modèle, mais dans certains cas, une approche plus élaborée peut s'avérer nécessaire.

    Il est certainement possible d'approfondir les mesures de surveillance et de créer de meilleures alertes. Par exemple, imaginez que vous ayez plusieurs modèles ML en cours d'exécution et que différentes personnes soient responsables de chacun d'entre eux. Vous pourriez utiliser la métrique du nom du modèle et définir des règles d'alerte spécifiques, pour des destinataires d'e-mails spécifiques.

    Le moniteur System Monitor vous permet également de déclencher une ClassMethod au lieu d'envoyer un courriel. Ainsi, vous pouvez exécuter une logique complexe lorsqu'une alerte est déclenchée, comme entraîner automatiquement le modèle, par exemple.

    Notez que, comme System Monitor exécutera régulièrement les méthodes "Initialize" et "GetSample", ces méthodes doivent être soigneusement conçues afin de ne pas demander trop de ressources au système.

    Travaux futurs

    Comme le souligne Benjamin De BoeIRIS introduit une nouvelle façon de personnaliser votre tâche de surveillance - l' outil SAM. Ma première impression a été très positive, SAM est intégré avec les technologies de surveillance des standards du marché comme Grafana et Prometheus. Alors, pourquoi ne pas aller de l'avant et tester comment améliorer ce travail avec de telles nouvelles fonctionnalités ? Mais il s'agit là d'une matière pour un futur travail.... :)

    Voilà, c'est fait ! J'espère que cela pourra vous être utile d'une manière ou d'une autre. A bientôt !

    0
    0 60
    Article Guillaume Rongier · Avr 5, 2023 6m read

    Il y a quelques mois, j'ai lu cet article intéressant de la MIT Technology Review, qui explique comment la pandémie de COVID-19 pose des défis aux équipes informatiques du monde entier en ce qui concerne leurs systèmes d'apprentissage automatique (ML).

    Cet article m'a incité à réfléchir à la manière de traiter les problèmes de performance après le déploiement d'un modèle de ML.

    J'ai simulé un simple scénario de problème de performance dans une application modèle de la technologie Open Exchange - iris-integratedml-monitor-example, qui participe au concours IA d'InterSystems IRIS (InterSystems IRIS AI Contest). S'il vous plaît, après avoir lu cet article, vous pouvez le consulter et, si vous l'aimez, votez pour moi! :)

    Contenu

    Partie I:

    Partie II:

    Les systèmes IRIS IntegratedML et ML

    Avant de parler de COVID-19 et de son impact sur les systèmes ML dans le monde entier, parlons rapidement d'InterSystems IRIS IntegratedML.

    En automatisant des tâches telles que la sélection des caractéristiques et en s'intégrant au langage de manipulation de données SQL standard, IntegratedML pourrait nous aider à développer et à déployer une solution de ML.

    Par exemple, après avoir manipulé et analysé correctement des données provenant de rendez-vous médicaux, vous pouvez configurer un modèle de ML pour prédire la présence ou l'absence des patients à l'aide de ces instructions SQL :

    CREATE MODEL AppointmentsPredection PREDICTING (Show) FROM MedicalAppointments
    TRAIN MODEL AppointmentsPredection FROM MedicalAppointments
    VALIDATE MODEL AppointmentsPredection FROM MedicalAppointments
    

    Le fournisseur AutoML choisira l'ensemble de caractéristiques et l'algorithme ML le plus performant. Dans ce cas, le fournisseur AutoML a sélectionné le modèle de régression logistique utilisant la bibliothèque scikit-learn, obtenant ainsi une exactitude de 90 %.

    |   | NOM_DU_MODÈLE             | NOM_DU_MODÈLE_FORMÉ      | FOURNISSEUR | HORODATAGE_FORMÉ       | TYPE_DU_MODÈLE     | INFOS_MODÈLE                                        |
    |---|------------------------|-------------------------|----------|-------------------------|----------------|---------------------------------------------------|
    | 0 | AppointmentsPredection | AppointmentsPredection2 | AutoML   | 2020-07-12 04:46:00.615 | classification | ModelType:Logistic Regression, Package:sklearn... |
    
    | NOM_MÉTRIQUE              | Exactitude | Mesure F | Précision | Rappel |
    |--------------------------|----------|-----------|-----------|--------|
    | AppointmentsPredection21 | 0.9      | 0.94      | 0.98      | 0.91   |
    

    Une fois que votre modèle de ML est déjà intégré à SQL, vous pouvez l'intégrer de manière transparente à votre système de réservation existant afin d'améliorer ses performances, en utilisant des estimations sur les patients qui seront présents et ceux qui ne le seront pas :

    SELECT PREDICT(AppointmentsPredection) As Predicted FROM MedicalAppointments WHERE ID = ?
    

    Vous pouvez en savoir plus sur IntegrateML ici. Si vous souhaitez obtenir un peu plus de détails sur ce modèle de prédiction simple, vous pouvez vous référer à.

    Toutefois, comme les modèles d'IA/ML sont conçus pour s'adapter au comportement de la société, directement ou non, ils seront probablement très affectés lorsque ce comportement changera rapidement. Récemment, nous avons (malheureusement) pu expérimenter un tel scénario en raison de la pandémie de COVID-19.

    Entre les anciennes et les nouvelles normalités

    Comme l'explique l'article de MIT Technology Review, la pandémie de COVID-19 a modifié remarquablement et rapidement le comportement de la société. J'ai effectué des recherches dans Google Trends pour des termes cités dans l'article, tels que masque N95, papier toilette et désinfectant pour les mains, afin de confirmer l'augmentation de leur popularité, à mesure que la pandémie se propageait dans le monde :

    Tel que cité dans l'article :

    "Mais ils [les changements apportés par COVID-19] ont également affecté l'intelligence artificielle, causant des problèmes aux algorithmes qui fonctionnent dans les coulisses de la gestion des stocks, de la détection des fraudes, du marketing, et bien d'autres choses encore. Les modèles d'apprentissage automatique formés sur la base du comportement humain normal découvrent aujourd'hui que la normalité elle-même a changé, et certains d'entre eux ne fonctionnent plus comme ils le devraient."

    En d'autres termes, entre l'"ancienne normalité" et la "nouvelle normalité", nous vivons une "nouvelle anormalité".
    Une autre citation intéressante, également tirée de l'article :

    "Les modèles d'apprentissage automatique sont conçus pour s'adapter aux changements. Mais la plupart d'entre eux sont également fragiles ; ils donnent de mauvais résultats lorsque les données d'entrée diffèrent trop de celles sur lesquelles ils ont été formés. (...) L'IA est un moteur vivant, qui respire."

    Cet article présente ensuite des exemples de modèles d'IA/ML dont les performances commencent soudainement à être affectées négativement, ou qui doivent être modifiés de toute urgence. Quelques exemples :

    • Les entreprises de vente au détail qui se sont retrouvées en rupture de stock après avoir passé des commandes en masse pour des produits inadaptés ;
    • Des conseils biaisés de la part de services de recommandation d'investissements basés sur l'analyse du sentiment des messages médiatiques, en raison de leur contenu pessimiste ;
    • Générateurs automatiques de phrases pour les conseils qui commencent à générer un contenu inadapté, en raison d'un nouveau contexte ;
    • Amazon a modifié son système de recommandation des vendeurs pour choisir ceux qui effectuent leurs propres livraisons, afin d'éviter que la logistique de ses entrepôts ne soit trop sollicitée.

    Nous devons donc surveiller nos modèles d'IA/ML afin de garantir leur fiabilité et de continuer à aider nos clients.

    Jusqu'à présent, j'espère vous avoir montré que la création, la formation et le déploiement de votre modèle de ML ne sont pas tout - vous devez en assurer le suivi. Dans le prochain article, je vous montrerai comment utiliser le framework %Monitor.Abstract d'IRIS pour surveiller les performances de votre système de ML et définir des alertes basées sur les métriques du moniteur.

    En attendant, j'aimerais savoir si vous avez été confronté à l'un ou l'autre des problèmes soulevés par ces périodes de pandémie, et comment vous y faites face dans la section des commentaires !

    Restez à l'écoute (et en sécurité 😊)!

    0
    0 53
    Article Sylvain Guilbaud · Mars 31, 2023 22m read

    Mots-clés:  IRIS, IntegratedML, apprentissage automatique, Covid-19, Kaggle 

    Continuation de la précédente Partie I ... Dans la partie I, nous avons parcouru les approches ML traditionnelles sur ce jeu de données Covid-19 sur Kaggle.

    Dans cette partie II, nous allons exécuter les mêmes données et la même tâche, dans sa forme la plus simple possible, à travers IRIS integratedML qui est une interface SQL agréable et élégante pour les options AutoML du backend. Cette interface utilise le même environnement. 

    Approche IntegratedML ?

    Comment charger des données dans IRIS

    integredML-demo-template a défini plusieurs façons de charger des données dans IRIS. Par exemple, je peux définir une classe IRIS personnalisée spécifique à ce fichier xls au format CSV, puis le charger dans un tableau IRIS. Cela permet un meilleur contrôle pour les volumes de données importants. 

    Cependant, dans cet article, j'opte pour une méthode simplifiée et légère, en me contentant de charger le jeux des données dans un tableau IRIS via une fonction Python personnalisée que j'ai créée.  Cela nous permet de sauvegarder à tout moment les différentes étapes des dataframes brutes ou traitées dans IRIS, pour des comparaisons similaires avec l'approche ML précédente.

    def to_sql_iris(cursor, dataFrame, tableName, schemaName='SQLUser', drop_table=False ):
            """"
            Insertion dynamique d'un dataframe dans un tableau IRIS via SQL par "excutemany"
    
            Inputs:
                cursor:      Curseur Python JDBC ou PyODBC à partir d'une connexion DB valide et établie
                dataFrame:   Pandas dataframe
                tablename:   Tableau SQL IRIS à créer, à insérer ou à modifier
                schemaName:  IRIS schemaName, par défaut pour "SQLUser"
                drop_table:  Si le tableau existe déjà, le supprimer et le recréer si True ; sinon, le sauvegarder et l'appliquer
            Output:
                True en cas de succès ; False en cas d'exception.
            """
            if drop_table:   
                try:                 
                    curs.execute("DROP TABLE %s.%s" %(schemaName, tableName))
                except Exception:
                    pass
    
            try:
                dataFrame.columns = dataFrame.columns.str.replace("[() -]", "_")
                curs.execute(pd.io.sql.get_schema(dataFrame, tableName))
            except Exception:
                pass
    
            curs.fast_executemany = True
            cols = ", ".join([str(i) for i in dataFrame.columns.tolist()])
            wildc =''.join('?, ' * len(dataFrame.columns))
            wildc = '(' + wildc[:-2] + ')'
            sql = "INSERT INTO " + tableName + " ( " + cols.replace('-', '_') + " ) VALUES" + wildc
            #print(sql)
            curs.executemany(sql, list(dataFrame.itertuples(index=False, name=None)) )
            return True

    Configuration de la connexion Python JDBC

    import numpy as np
    import pandas as pd
    from sklearn.impute import SimpleImputer
    import matplotlib.pyplot as plt
    from sklearn.linear_model import LogisticRegression
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report, roc_auc_score, roc_curve
    import seaborn as sns
    sns.set(style="whitegrid")import jaydebeapi
    url = "jdbc:IRIS://irisimlsvr:51773/USER"
    driver = 'com.intersystems.jdbc.IRISDriver'
    user = "SUPERUSER"
    password = "SYS"
    jarfile = "./intersystems-jdbc-3.1.0.jar"conn = jaydebeapi.connect(driver, url, [user, password], jarfile)
    curs = conn.cursor()

     

    Définition du point de départ des données

    Pour les comparaisons à l'identique, j'ai commencé par le dataframe après les sélections de caractéristiques dans le post précédent (dans la section "Sélection de caractéristiques - Sélection finale"), où "DataS" est le dataframe exact que nous commençons ici.

    data = dataS
    data = pd.get_dummies(data)
    data.ÂGE_AU-DESSUS65 = data.ÂGE_AU-DESSUS65.astype(int)
    data.ICU = data.ICU.astype(int)
    data_new = data
    data_new
    <th>
      ÂGE_AU-DESSUS65
    </th>
    
    <th>
      GENRE
    </th>
    
    <th>
      HTN
    </th>
    
    <th>
      AUTRES
    </th>
    
    <th>
      CALCIUM_MÉDIAN
    </th>
    
    <th>
      CALCIUM_MIN
    </th>
    
    <th>
      CALCIUM_MAX
    </th>
    
    <th>
      CRÉATININE_MÉDIANE
    </th>
    
    <th>
      CRÉATININE_MOYENNE
    </th>
    
    <th>
      CRÉATININE_MIN
    </th>
    
    <th>
      ...
    </th>
    
    <th>
      DIFFÉRENCE_DU_RYTHME_CARDIAQUE_REL
    </th>
    
    <th>
      DIFFÉRENCE_DE_TAUX_RESPIRATOIRE_REL
    </th>
    
    <th>
      DIFFÉRENCE_DE_TEMPÉRATURE_REL
    </th>
    
    <th>
      DIFFÉRENCE_DE_SATURATION_D'OXYGÈNE_REL
    </th>
    
    <th>
      USI
    </th>
    
    <th>
      FENÊTRE_0-2
    </th>
    
    <th>
      FENÊTRE_2-4
    </th>
    
    <th>
      FENÊTRE_4-6
    </th>
    
    <th>
      FENÊTRE_6-12
    </th>
    
    <th>
      FENÊTRE_AU-DESSUS_12
    </th>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.183673
    </td>
    
    <td>
      0.183673
    </td>
    
    <td>
      0.183673
    </td>
    
    <td>
      -0.868365
    </td>
    
    <td>
      -0.868365
    </td>
    
    <td>
      -0.868365
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -0.817800
    </td>
    
    <td>
      -0.719147
    </td>
    
    <td>
      -0.771327
    </td>
    
    <td>
      -0.886982
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -0.817800
    </td>
    
    <td>
      -0.719147
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.326531
    </td>
    
    <td>
      0.326531
    </td>
    
    <td>
      0.326531
    </td>
    
    <td>
      -0.926398
    </td>
    
    <td>
      -0.926398
    </td>
    
    <td>
      -0.926398
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -0.230462
    </td>
    
    <td>
      0.096774
    </td>
    
    <td>
      -0.242282
    </td>
    
    <td>
      -0.814433
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.244898
    </td>
    
    <td>
      0.244898
    </td>
    
    <td>
      0.244898
    </td>
    
    <td>
      -0.934890
    </td>
    
    <td>
      -0.934890
    </td>
    
    <td>
      -0.934890
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      0.330359
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      -0.891078
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.306122
    </td>
    
    <td>
      0.306122
    </td>
    
    <td>
      0.306122
    </td>
    
    <td>
      -0.944798
    </td>
    
    <td>
      -0.944798
    </td>
    
    <td>
      -0.944798
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -0.763868
    </td>
    
    <td>
      -0.612903
    </td>
    
    <td>
      -0.551337
    </td>
    
    <td>
      -0.835052
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
     
    1
    2
    3
    4
    ...
    1920
    1921
    1922
    1923
    1924

    1925 lignes × 62 colonnes

    Ce qui précède indique que nous disposons de 58 caractéristiques sélectionnées plus 4 autres caractéristiques converties à partir de la colonne non numérique précédente ("FENÊTRE").  

    Sauvegarder les données dans le tableau IRIS

    Nous utilisons la fonction to_sql_iris ci-dessus pour sauvegarder les données dans le tableau IRIS "CovidPPP62" :

    iris_schema = 'SQLUser'
    iris_table = 'CovidPPP62'to_sql_iris(curs, data_new, iris_table, iris_schema, drop_table=True) df2 = pd.read_sql("SELECT COUNT(*) from %s.%s" %(iris_schema, iris_table),conn)
    display(df2)
    <th>
      Aggregate_1
    </th>
    
    <td>
      1925
    </td>
    
     Sauvegarder les données dans le tableau IRIS

    Définissez ensuite le nom de la vue de formation, le nom du modèle et la colonne cible de la formation, qui est ici " USI ".  

    dataTable = iris_table
    dataTableViewTrain = dataTable + 'Train1'
    dataTablePredict = dataTable + 'Predict1'
    dataColumn =  'ICU'
    dataColumnPredict = 'ICUPredicted'
    modelName = "ICUP621" #choisir un nom - doit être unique du côté serveur

    Nous pouvons ensuite diviser les données en une Vue de formation (1700 lignes) et une Vue de test (225 lignes). Nous ne sommes pas obligés de faire cela dans Integrated ML ; c'est juste à des fins de comparaison avec l'article précédent.

    curs.execute("CREATE VIEW %s AS SELECT * FROM %s WHERE ID&lt;=1700" % (dataTableViewTrain, dataTable))df62 = pd.read_sql("SELECT * from %s" % dataTableViewTrain, conn)
    display(df62)
    print(dataTableViewTrain, modelName, dataColumn)
    CovidPPP62Train1 ICUP621 ICU

     

    Formation du modèle à l'aide de l'AutoML par défaut d'IntegratedML

    curs.execute("CREATE MODEL %s PREDICTING (%s)  FROM %s" % (modelName, dataColumn, dataTableViewTrain))curs.execute("TRAIN MODEL %s FROM %s" % (modelName, dataTableViewTrain))df3 = pd.read_sql("SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS", conn)
    display(df3)
    <th>
      NOM_DU_MODÈLE
    </th>
    
    <th>
      NOM_DU_MODÈLE_FORMÉ
    </th>
    
    <th>
      FOURNISSEUR
    </th>
    
    <th>
      HORODATAGE_FORMÉ
    </th>
    
    <th>
      TYPE_DU_MODÈLE
    </th>
    
    <th>
      MODÈLE_INFO
    </th>
    
    <td>
      USIP621
    </td>
    
    <td>
      USIP6212
    </td>
    
    <td>
      AutoML
    </td>
    
    <td>
      2020-07-22 19:28:16.174000
    </td>
    
    <td>
      classification
    </td>
    
    <td>
      ModelType:Random Forest, Paquet:sklearn, Prob...
    </td>
    
     
    9

    Ainsi, nous pouvons voir que le résultat montre qu'IntegratedML a automatiquement choisi "ModelType" comme étant "Random Forest" (forêt aléatoire), et traite le problème comme une tâche de "Classification".  C'est exactement ce que nous avons obtenu après les longues comparaisons de modèles et les sélections par boîte à moustaches, ainsi que le long réglage des paramètres du modèle par quadrillage, etc. dans l'article précédent, n'est-ce pas ?

    Remarque: le SQL ci-dessus est le strict minimum selon la syntaxe d'IntegratedML. Je n'ai pas spécifié d'approche de formation ou de sélection de modèle, et je n'ai pas défini de plateforme de ML. Tout a été laissé à la décision de l'IML, qui a réussi à mettre en œuvre sa stratégie de formation interne, avant de se contenter d'un modèle raisonnable avec des résultats finaux corrects. Je dirais que cela a dépassé mes attentes.   

    Effectuons un rapide test de comparaison du modèle actuellement entraîné sur notre ensemble de test réservé.

    Prédiction des résultats sur la base de données de test

    Nous avons utilisé 1700 lignes pour la formation. Ci-dessous, nous créons une vue des données de test avec les 225 lignes restantes, et nous exécutons SELECT PREDICT sur ces enregistrements. Nous sauvegarderons le résultat prédit dans 'dataTablePredict', et le chargerons dans 'df62' en tant que data frame.

    dataTableViewTest = "SQLUSER.DTT621"
    curs.execute("CREATE VIEW %s AS SELECT * FROM %s WHERE ID > 1700" % (dataTableViewTest, dataTable))curs.execute("DROP TABLE %s" % dataTablePredict )
    curs.execute("Create Table %s (%s VARCHAR(100), %s VARCHAR(100))" % (dataTablePredict, dataColumnPredict, dataColumn))curs.execute("INSERT INTO %s  SELECT PREDICT(%s) AS %s, %s FROM %s" % (dataTablePredict, modelName, dataColumnPredict, dataColumn, dataTableViewTest)) df62 = pd.read_sql("SELECT * from %s ORDER BY ID" % dataTablePredict, conn)
    display(df62)

    Nous n'avons pas besoin de calculer manuellement sa matrice de confusion. Il s'agit simplement d'une comparaison :

    TP = df62[(df62['ICUPredicted'] == '1') & (df62['ICU']=='1')].count()['ICU']  
    TN = df62[(df62['ICUPredicted'] == '0') & (df62['ICU']=='0')].count()["ICU"]
    FN = df62[(df62['ICU'] == '1') & (df62['ICUPredicted']=='0')].count()["ICU"]
    FP = df62[(df62['ICUPredicted'] == '1') & (df62['ICU']=='0')].count()["ICU"]
    print(TP, FN, '\n', FP, TN)
    precision = (TP)/(TP+FP)
    recall = (TP)/(TP+FN)
    f1 = ((precision*recall)/(precision+recall))*2
    accuracy = (TP+TN) / (TP+TN+FP+FN)
    print("Precision: ", precision, " Recall: ", recall, " F1: ", f1, " Accuracy: ", accuracy)
    34 20
     8 163
    Précision:  0.8095238095238095  rappel:  0.6296296296296297  F1:  0.7083333333333334  Exactitude:  0.8755555555555555

    Nous pouvons également utiliser la syntaxe IntegratedML pour obtenir sa matrice de confusion intégrée :

    # valider les données de test
    curs.execute("VALIDATE MODEL %s FROM %s" % (modelName, dataTableViewTest) )  
    df5 = pd.read_sql("SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS", conn)
    df6 = df5.pivot(index='VALIDATION_RUN_NAME', columns='METRIC_NAME', values='METRIC_VALUE')
    display(df6)
    <th>
      Exactitude
    </th>
    
    <th>
      Mesure F
    </th>
    
    <th>
      Précision
    </th>
    
    <th>
      Rappel
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <td>
      0.88
    </td>
    
    <td>
      0.71
    </td>
    
    <td>
      0.81
    </td>
    
    <td>
      0.63
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    NOM_MÉTRIQUE
    NOM_DE_L'EXÉCUTION_DE_LA_VALIDATION
    USIP62121
    ...

    Si l'on compare avec le "Résultat original" de la section " Exécuter une formation de base en LR " dans la partie I, le résultat ci-dessus présente un rappel de 63 % contre 57 %, et une exactitude de 88 % contre 85 %. Il s'agit donc d'un meilleur résultat avec IntegratedML.

    Former à nouveau IntegratedML sur des données de formation rééquilibrées via SMOTE

    Le test ci-dessus a été effectué sur des données déséquilibrées, dans lesquelles le rapport entre les patients admis en USI et les patients non admis est de 1:3. Donc, comme dans l'article précédent, nous allons simplement effectuer un SMOTE pour que les données soient équilibrées, puis nous allons réexécuter le pipeline IML ci-dessus.

    'X_train_res' and 'y_train_res' sont des dataframes après SMOTE de la Partie I précédente dans sa section " Exécuter une formation de base en LR ". 

    df_x_train = pd.DataFrame(X_train_res)
    df_y_train = pd.DataFrame(y_train_res)
    df_y_train.columns=['ICU']df_smote = pd.concat([df_x_train, df_y_train], 1)
    display(df_smote)
    iris_schema = 'SQLUser'
    iris_table = 'CovidSmote'
    to_sql_iris(curs, df_smote, iris_table, iris_schema, drop_table=True) # sauvegarder ceci dans un nouveau tableau IRIS portant le nom spécifié
    df2 = pd.read_sql("SELECT COUNT(*) from %s.%s" %(iris_schema, iris_table),conn)
    display(df2)
    <th>
      Aggregate_1
    </th>
    
    <td>
      2490
    </td>
    
     

    Le jeu de données comporte désormais 2490 lignes au lieu de 1700, car SMOTE a enrichi davantage d'enregistrements avec USI = 1.

    dataTable = iris_table
    dataTableViewTrain = dataTable + 'TrainSmote'
    dataTablePredict = dataTable + 'PredictSmote'
    dataColumn =  'ICU'
    dataColumnPredict = 'ICUPredictedSmote'
    modelName = "ICUSmote1" #choisir un nom - doit être unique du côté serveurcurs.execute("CREATE VIEW %s AS SELECT * FROM %s" % (dataTableViewTrain, dataTable))df_smote = pd.read_sql("SELECT * from %s" % dataTableViewTrain, conn)
    display(df_smote)
    print(dataTableViewTrain, modelName, dataColumn)
    CovidSmoteTrainSmote ICUSmote1 ICU
    curs.execute("CREATE MODEL %s PREDICTING (%s)  FROM %s" % (modelName, dataColumn, dataTableViewTrain))curs.execute("TRAIN MODEL %s FROM %s" % (modelName, dataTableViewTrain))
    
    df3 = pd.read_sql("SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS", conn)
    display(df3)
    <th>
      NOM_DU_MODÈLE
    </th>
    
    <th>
    NOM_DU_MODÈLE_FORMÉ
    </th>
    
    <th>
      FOURNISSEUR
    </th>
    
    <th>
      HORODATAGE_FORMÉ
    </th>
    
    <th>
      TYPE_DU_MODÈLE
    </th>
    
    <th>
      MODEL_INFO
    </th>
    
    <td>
      USIP621
    </td>
    
    <td>
      USIP6212
    </td>
    
    <td>
      AutoML
    </td>
    
    <td>
      2020-07-22 19:28:16.174000
    </td>
    
    <td>
      classification
    </td>
    
    <td>
      ModelType:Random Forest, Paquet:sklearn, Prob...
    </td>
    
    <td>
      USISmote1
    </td>
    
    <td>
      USISmote12
    </td>
    
    <td>
      AutoML
    </td>
    
    <td>
      2020-07-22 20:49:13.980000
    </td>
    
    <td>
      classification
    </td>
    
    <td>
      ModelType:Random Forest, Paquet:sklearn, Prob...
    </td>
    
     
    9
    12

    Ensuite, nous préparons à nouveau un ensemble réservé de 225 lignes de données de test et nous exécutons le modèle reformé de SMOTE sur ces lignes :

    df_x_test = pd.DataFrame(X3_test)
    df_y_test = pd.DataFrame(y3_test)
    df_y_test.columns=['ICU']df_test_smote = pd.concat([df_x_test, df_y_test], 1)
    display(df_test_smote)iris_schema = 'SQLUser'
    iris_table = 'CovidTestSmote'to_sql_iris(curs, df_test_smote, iris_table, iris_schema, drop_table=True) dataTableViewTest = "SQLUSER.DTestSmote225"
    curs.execute("CREATE VIEW %s AS SELECT * FROM %s" % (dataTableViewTest, iris_table))
    curs.execute("Create Table %s (%s VARCHAR(100), %s VARCHAR(100))" % (dataTablePredict, dataColumnPredict, dataColumn))
    curs.execute("INSERT INTO %s  SELECT PREDICT(%s) AS %s, %s FROM %s" % (dataTablePredict, modelName, dataColumnPredict, dataColumn, dataTableViewTest))
    
    df62 = pd.read_sql("SELECT * from %s ORDER BY ID" % dataTablePredict, conn)
    display(df62)
    
    TP = df62[(df62['ICUPredictedSmote'] == '1') & (df62['ICU']=='1')].count()['ICU']  
    TN = df62[(df62['ICUPredictedSmote'] == '0') & (df62['ICU']=='0')].count()["ICU"]
    FN = df62[(df62['ICU'] == '1') & (df62['ICUPredictedSmote']=='0')].count()["ICU"]
    FP = df62[(df62['ICUPredictedSmote'] == '1') & (df62['ICU']=='0')].count()["ICU"]
    print(TP, FN, '\n', FP, TN)
    precision = (TP)/(TP+FP)
    recall = (TP)/(TP+FN)
    f1 = ((precision*recall)/(precision+recall))*2
    accuracy = (TP+TN) / (TP+TN+FP+FN)
    print("Precision: ", precision, " Recall: ", recall, " F1: ", f1, " Accuracy: ", accuracy)
    45 15
     9 156
    Précision:  0.8333333333333334  Rappel:  0.75  F1:  0.7894736842105262  Exactitude:  0.8933333333333333
    
    # valider les données d'essai à l'aide du modèle reformé de SMOTE
    curs.execute("VALIDATE MODEL %s FROM %s" % (modelName, dataTableViewTest) )  #Covid19aTest500, Covid19aTrain1000
    df5 = pd.read_sql("SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS", conn)
    df6 = df5.pivot(index='VALIDATION_RUN_NAME', columns='METRIC_NAME', values='METRIC_VALUE')
    display(df6)
    <th>
      Exactitude
    </th>
    
    <th>
      Mesure F
    </th>
    
    <th>
      Précision
    </th>
    
    <th>
      Rappel
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <td>
      0.88
    </td>
    
    <td>
      0.71
    </td>
    
    <td>
      0.81
    </td>
    
    <td>
      0.63
    </td>
    
    <td>
      0.89
    </td>
    
    <td>
      0.79
    </td>
    
    <td>
      0.83
    </td>
    
    <td>
      0.75
    </td>
    
    NOM_MÉTRIQUE
    NOM_DE_L'EXÉCUTION_DE_LA_VALIDATION
    USIP62121
    USISmote122

    Le résultat indique une amélioration significative du rappel de 75 % par rapport aux 63 % précédents, ainsi qu'une légère amélioration de l'exactitude et du score F1.  

    Plus notablement, ce résultat est conforme à notre "approche ML traditionnelle" dans l'article précédent, après une "sélection de modèle" intensive et un "réglage des paramètres par quadrillage", comme indiqué dans la section "Exécuter le modèle sélectionné en poursuivant "Ajustement des paramètres via la recherche par quadrillage" supplémentaire". Le résultat de l'IML n'est donc pas mauvais du tout.

    **Changement de fournisseur H2O d'IntegratedML **

    Nous pouvons modifier le fournisseur AutoML de l'IML d'une seule ligne, puis former à nouveau le modèle comme nous l'avons fait à l'étape précédente :   

    curs.execute("SET ML CONFIGURATION %H2O;  ")modelName = 'ICUSmoteH2O'
    print(dataTableViewTrain)
    curs.execute("CREATE MODEL %s PREDICTING (%s)  FROM %s" % (modelName, dataColumn, dataTableViewTrain))
    curs.execute("TRAIN MODEL %s FROM %s" % (modelName, dataTableViewTrain))df3 = pd.read_sql("SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS", conn)
    display(df3)
    <th>
      NOM_DU_MODÈLE
    </th>
    
    <th>
      NOM_DU_MODÈLE_FORMÉ
    </th>
    
    <th>
      FOURNISSEUR
    </th>
    
    <th>
      HORODATAGE_FORMÉ
    </th>
    
    <th>
      TYPE_DU_MODÈLE
    </th>
    
    <th>
      MODÈLE_INFO
    </th>
    
    <td>
      USISmote1
    </td>
    
    <td>
      USISmote12
    </td>
    
    <td>
      AutoML
    </td>
    
    <td>
      2020-07-22 20:49:13.980000
    </td>
    
    <td>
      classification
    </td>
    
    <td>
      ModelType:Random Forest, Paquet:sklearn, Prob...
    </td>
    
    <td>
      USIPPP62
    </td>
    
    <td>
      USIPPP622
    </td>
    
    <td>
      AutoML
    </td>
    
    <td>
      2020-07-22 17:48:10.964000
    </td>
    
    <td>
      classification
    </td>
    
    <td>
      ModelType:Random Forest, Paquet:sklearn, Prob...
    </td>
    
    <td>
      USISmoteH2O
    </td>
    
    <td>
      USISmoteH2O2
    </td>
    
    <td>
      H2O
    </td>
    
    <td>
      2020-07-22 21:17:06.990000
    </td>
    
    <td>
      classification
    </td>
    
    <td>
      Aucun
    </td>
    
     
    12
    13
    14
    # valider les données de test
    curs.execute("VALIDATE MODEL %s FROM %s" % (modelName, dataTableViewTest) )  #Covid19aTest500, Covid19aTrain1000
    df5 = pd.read_sql("SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS", conn)
    df6 = df5.pivot(index='VALIDATION_RUN_NAME', columns='METRIC_NAME', values='METRIC_VALUE')
    display(df6)
    <th>
      Exactitude
    </th>
    
    <th>
      Mesure F
    </th>
    
    <th>
      Précision
    </th>
    
    <th>
      Rappel
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <th>
       
    </th>
    
    <td>
      0.88
    </td>
    
    <td>
      0.71
    </td>
    
    <td>
      0.81
    </td>
    
    <td>
      0.63
    </td>
    
    <td>
      0.89
    </td>
    
    <td>
      0.79
    </td>
    
    <td>
      0.83
    </td>
    
    <td>
      0.75
    </td>
    
    <td>
      0.90
    </td>
    
    <td>
      0.79
    </td>
    
    <td>
      0.86
    </td>
    
    <td>
      0.73
    </td>
    
    NOM_MÉTRIQUE
    NOM_DE_L'EXÉCUTION_DE_LA_VALIDATION
    USIP62121
    USISmote122
    USISmoteH2O21

    Les résultats semblent montrer que H2O AutoML a une précision légèrement supérieure, le même F1, mais un rappel légèrement inférieur. Cependant, notre objectif principal dans cette tâche de Covid19 USI est de minimiser les faux négatifs si nous le pouvons. Il semble donc que le changement de fournisseur pour H2O n'ait pas encore permis d'augmenter notre performance cible.

    J'aimerais certainement tester également le fournisseur DataRobot d'IntegratedML, mais je n'ai malheureusement pas encore de clé API de DataRobot, alors je vais la mettre de côté ici.

    Récapitulatif:

    1. Performance : Pour cette tâche spécifique de l'unité de soins intensifs de Covid-19, nos comparaisons de tests indiquent que les performances de l'IntegratedML d'IRIS sont au moins équivalentes ou similaires aux résultats de l'approche ML traditionnelle. Dans ce cas précis, IntegratedML a été capable de choisir automatiquement et correctement la stratégie d'entraînement interne, et a semblé établir le bon modèle, fournissant le résultat escompté.

    2. Simplicité : IntegratedML a un processus beaucoup plus simplifié que les pipelines ML traditionnels. Comme indiqué ci-dessus, je n'ai plus besoin de me préoccuper de la sélection des modèles et l'ajustement des paramètres, etc. Je n'ai pas non plus besoin de la sélection des caractéristiques, si ce n'est à des fins de comparaison. De plus, je n'ai utilisé que la syntaxe minimale d'IntegratedML, comme indiqué dans le cahier de démonstration d'Integrated-demo-template. Bien sûr, le désavantage est que nous sacrifions les capacités de personnalisation et d'ajustement des outils courants de science des données via leurs pipelines traditionnels, mais c'est aussi plus ou moins vrai pour d'autres plateformes AutoML.

    3. Le prétraitement des données reste important : Il n'y a malheureusement pas de solution miracle ; ou plutôt, cette solution miracle prendrait du temps. Spécifiquement pour cette tâche de Covid19 USI, les tests ci-dessus montrent que les données ont encore beaucoup d'importance pour l'approche actuelle d'IntegratedML : données brutes, caractéristiques sélectionnées avec données manquantes imputées, et données rééquilibrées avec suréchantillonnage SMOTE de base, elles ont toutes abouti à des performances significativement différentes. C'est vrai pour l'AutoML par défaut d'IML et son fournisseur H2O. J'imagine que DataRobot pourrait revendiquer une performance légèrement supérieure, mais cela doit être testé plus avant avec l'enveloppe SQL d'IntegratedML. En bref, la normalisation des données est toujours importante dans IntegratedML.

    4. Déployabilité : Je n'ai pas encore comparé la déployabilité, la gestion de l'API, la surveillance et la facilité d'utilisation non fonctionnelle, etc.

    Suivant

    1. Déploiements de modèles : Jusqu'à présent, nous avons fait des démonstrations d'IA sur les radiographies pour le Covid-19 et des prédictions pour l'unité de soins intensifs pour le Covid-19 sur les signes vitaux et les observations. Pouvons-nous les déployer dans les piles de services Flask/FastAPI et IRIS, et exposer leurs capacités ML/DL de démonstration via des API REST/JSON ? Bien sûr, nous pouvons essayer de le faire dans le prochain article. Ensuite, nous pourrons ajouter d'autres capacités d'IA de démonstration au fil du temps, y compris des API NLP, etc.

    2. Interopérabilité de l'API enveloppée dans FHIR : Nous disposons également d'un modèle FHIR, ainsi que d'une API native IRIS, etc. dans cette communauté de développeurs. Pourrions-nous transformer notre service d'IA de démonstration en SMART on FHIR apps, ou en services d'IA enveloppés dans FHIR selon les normes correspondantes - pourrions-nous essayer cela ? Et n'oubliez pas que dans la gamme de produits IRIS, nous avons également API Gateway, ICM avec support Kubernetes, et SAM etc. que nous pourrions également exploiter avec nos piles de démonstrations d'IA.

    3. Démonstration d'intégration avec HealthShare Clinical Viewer et/ou Trak etc ? J'ai brièvement montré une démonstration d'intégration du PACS Viewer d'un fournisseur d'IA tiers (pour les CT Covid-19) avec HealthShare Clinical Viewer, et nous pourrions peut-être terminer cette randonnée avec nos propres services de démonstration d'IA, dans divers domaines de spécialité au fil du temps.

    0
    0 59
    Article Sylvain Guilbaud · Mars 29, 2023 31m read

    Keywords:  IRIS, IntegratedML, apprentissage automatique, Covid-19, Kaggle 

    Objectif

    J'ai récemment remarqué un jeu de données Kaggle permettant de prédire si un patient Covid-19 sera admis en soins intensifs. Il s'agit d'un tableur de 1925 enregistrements comprenant 231 colonnes de signes vitaux et d'observations, la dernière colonne " USI " valant 1 pour Oui ou 0 pour Non.

    Ce jeu de données représente un bon exemple de ce que l'on appelle une tâche "traditionnelle de ML". Les données semblent avoir une quantité suffisante et une qualité relativement bonne. Il pourrait avoir de meilleures chances d'être appliqué directement sur le kit IntegratedML demo. Quelle serait donc l'approche la plus simple pour un test rapide basé sur les pipelines ML normaux par rapport à l'approche possible avec IntegratedML ?

    Champ d'application

    Nous examinerons brièvement quelques étapes normales de ML, telles que :

    • Analyse des données (EDA)
    • Sélection des caractéristiques
    • Sélection du modèle
    • Ajustement des paramètres du modèle via le quadrillage

    Vs. 

    • Approches ML intégrées via SQL.

    Il est exécuté sur un serveur AWS Ubuntu 16.04 avec Docker-compose, etc.  

    Environnement

    Nous allons réutiliser l'environnement Docker de integredML-demo-template:

    Le fichier de bloc-notes suivant est exécuté sur "tf2jupyter", et IRIS avec IntegratedML sur "irismlsrv". Docker-compose fonctionne sur un AWS Ubuntu 16.04.

    Données et tâches

    Le jeu de données contient 1925 enregistrements collectés auprès de 385 patients, chacun comportant exactement 5 enregistrements de rendez-vous. Sur ses 231 colonnes, une seule, "USI", constitue notre cible d'apprentissage et de prédiction, et les 230 autres colonnes pourraient toutes être utilisées comme entrées de quelque manière que ce soit. L'unité de soins intensifs a une valeur binaire de 1 ou 0. À l'exception de deux colonnes qui semblent être des chaînes catégorielles (présentées comme "objet" dans le cadre de données), toutes les autres sont numériques.

    import numpy as np
    import pandas as pd
    from sklearn.impute import SimpleImputer
    import matplotlib.pyplot as plt
    from sklearn.linear_model import LogisticRegression
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report, roc_auc_score, roc_curve
    import seaborn as sns
    sns.set(style="whitegrid")
    
    import os
    for dirname, _, filenames in os.walk('./input'):
        for filename in filenames:
            print(os.path.join(dirname, filename))
    ./input/datasets_605991_1272346_Kaggle_Sirio_Libanes_ICU_Prediction.xlsx

     

    df = pd.read_excel("./input/datasets_605991_1272346_Kaggle_Sirio_Libanes_ICU_Prediction.xlsx")
    df
    <th>
      IDENTIFIANT_DE_VISITE_DU_PATIENT
    </th>
    
    <th>
      ÂGE_AU-DESSUS65
    </th>
    
    <th>
      ÂGE_POURCENTAGE
    </th>
    
    <th>
      GENRE
    </th>
    
    <th>
      GROUPE DE MALADIES 1
    </th>
    
    <th>
      GROUPE DE MALADIES 2
    </th>
    
    <th>
      GROUPE DE MALADIES 3
    </th>
    
    <th>
      GROUPE DE MALADIES 4
    </th>
    
    <th>
      GROUPE DE MALADIES 5
    </th>
    
    <th>
      GROUPE DE MALADIES 6
    </th>
    
    <th>
      ...
    </th>
    
    <th>
      DIFFÉRENCE_DE_TEMPÉRATURE
    </th>
    
    <th>
      DIFFÉRENCE_DE SATURATION_D'OXYGÈNE
    </th>
    
    <th>
      DIFFÉRENCE_DE_TENSION_DIASTOLIQUE_REL
    </th>
    
    <th>
      DIFFÉRENCE_DE_TENSION_SISTOLIQUE_REL
    </th>
    
    <th>
      DIFFÉRENCE_DU_RYTHME_CARDIAQUE_REL
    </th>
    
    <th>
      DIFFÉRENCE_DE_TAUX_RESPIRATOIRE_REL
    </th>
    
    <th>
      DIFFÉRENCE_DE_TEMPÉRATURE_REL
    </th>
    
    <th>
      DIFFÉRENCE_DE_SATURATION_D'OXYGÈNE_REL
    </th>
    
    <th>
      FENÊTRE
    </th>
    
    <th>
      ICU
    </th>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      âge de 60-69 ans
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      0-2
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      âge de 60-69 ans
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      2-4
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      âge de 60-69 ans
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      4-6
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      âge de 60-69 ans
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      NaN
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      6-12
    </td>
    
    <td>
    </td>
    
    <td>
    </td>
    
    <td>
      1
    </td>
    
    <td>
      âge de 60-69 ans
    </td>
    
    <td>
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -0.238095
    </td>
    
    <td>
      -0.818182
    </td>
    
    <td>
      -0.389967
    </td>
    
    <td>
      0.407558
    </td>
    
    <td>
      -0.230462
    </td>
    
    <td>
      0.096774
    </td>
    
    <td>
      -0.242282
    </td>
    
    <td>
      -0.814433
    </td>
    
    <td>
      AU-DESSUS_12
    </td>
    
    <td>
      1
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      âge de 50-59 ans
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      0-2
    </td>
    
    <td>
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      âge de 50-59 ans
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      2-4
    </td>
    
    <td>
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      âge de 50-59 ans
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      4-6
    </td>
    
    <td>
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      âge de 50-59 ans
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      -1.000000
    </td>
    
    <td>
      6-12
    </td>
    
    <td>
    </td>
    
    <td>
      384
    </td>
    
    <td>
    </td>
    
    <td>
      âge de 50-59 ans
    </td>
    
    <td>
      1
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      1.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      0.0
    </td>
    
    <td>
      ...
    </td>
    
    <td>
      -0.547619
    </td>
    
    <td>
      -0.838384
    </td>
    
    <td>
      -0.701863
    </td>
    
    <td>
      -0.585967
    </td>
    
    <td>
      -0.763868
    </td>
    
    <td>
      -0.612903
    </td>
    
    <td>
      -0.551337
    </td>
    
    <td>
      -0.835052
    </td>
    
    <td>
      AU-DESSUS_12
    </td>
    
    <td>
    </td>
    
     
    1
    2
    3
    4
    ...
    1920
    1921
    1922
    1923
    1924

    1925 lignes × 231 colonnes

    df.dtypes
    IDENTIFIANT_DE_VISITE_DU_PATIENT        int64
    ÂGE_AU-DESSUS65                     int64
    ÂGE_POURCENTAGE                  object
    GENRE                          int64
    GROUPE DE MALADIES 1            float64
                                   ...   
    DIFFÉRENCE_DE_TAUX_RESPIRATOIRE_REL     float64
    DIFFÉRENCE_DE_TEMPÉRATURE_REL          float64
    DIFFÉRENCE_DE SATURATION_D'OXYGÈNE_REL    float64
    FENÊTRE                         object
    USI                             int64
    Longeur: 231, dtype: object

    Il existe certainement plusieurs options pour définir ce problème et ses approches. La première option qui nous vient à l'esprit est qu'il peut s'agir d'un problème fondamental de "classification binaire". Nous pouvons traiter les 1925 enregistrements comme des enregistrements individuels "apatrides", qu'ils proviennent ou non du même patient. Bien sûr, il pourrait également s'agir d'un problème de "régression" si nous traitions les valeurs de l'unité de soins intensifs et d'autres valeurs comme étant toutes numériques.

    Il existe certainement d'autres approches possibles. Par exemple, nous pouvons considérer que l'ensemble de données comporte 385 jeux distincts de courtes "séries temporelles", chacun pour un patient. Nous pourrions dissoudre le jeu entier en 385 jeux distincts pour Train/Val/Test, et pourrions-nous essayer des modèles d'apprentissage profond tels que CNN ou LSTM pour capturer la "phase ou le modèle de développement des symptômes" caché dans chaque jeu pour chaque patient individuel ? C'est possible. Ce faisant, nous pourrions également appliquer une augmentation des données pour enrichir les données de test par divers moyens. Il s'agit là d'un sujet qui dépasse le cadre de cet article.

    Dans cet article, nous nous contenterons de tester rapidement l'approche ML dite "traditionnelle" par rapport à l'approche IntegratedML (une approche AutoML)..  

    Approche ML "traditionnelle" ?

    Il s'agit d'un jeu de données relativement normalisé par rapport à la plupart des cas réels, à l'exception de quelques valeurs manquantes, de sorte que nous pourrions sauter la partie relative à l'ingénierie des caractéristiques et utiliser directement les colonnes comme caractéristiques. Passons donc directement à la sélection des caractéristiques.

    Imputation des données manquantes

    Il faut d'abord s'assurer que toutes les valeurs manquantes sont remplies au moyen d'une imputation simple :

    df_cat = df.select_dtypes(include=['object'])
    df_numeric = df.select_dtypes(exclude=['object'])
    imp = SimpleImputer(missing_values=np.nan, strategy='mean')
    idf = pd.DataFrame(imp.fit_transform(df_numeric))
    idf.columns = df_numeric.columns
    idf.index = df_numeric.index
    idf.isnull().sum()

     

    Sélection sur les caractéristiques

    Nous pouvons certainement utiliser la fonction de corrélation normale intégrée dans la base de données pour calculer la corrélation entre les valeurs de chaque colonne et les unités de soins intensifs.

    l'ingénierie des caractéristiques - corrélation {#featuring-engineering---correlation}

    idf.drop(["PATIENT_VISIT_IDENTIFIER"],1)
    idf = pd.concat([idf,df_cat ], axis=1)
    cor = idf.corr()
    cor_target = abs(cor["ICU"])
    relevant_features = cor_target[cor_target>0.1]   # correlation above 0.1
    print(cor.shape, cor_target.shape, relevant_features.shape)
    #relevant_features.index
    #relevant_features.index.shape

    Il répertorie 88 caractéristiques présentant une corrélation >0,1 avec la valeur cible de l'unité de soins intensifs. Ces colonnes peuvent être directement utilisées comme entrée de notre modèle

    J'ai également exécuté quelques autres "méthodes de sélection de caractéristiques" qui sont normalement utilisées dans les tâches traditionnelles de ML :

    Sélection des caractéristiques - Chi carré {#feature-selection---Chi-squared}

    from sklearn.feature_selection import SelectKBest
    from sklearn.feature_selection import chi2
    from sklearn.preprocessing import MinMaxScaler
    X_norm = MinMaxScaler().fit_transform(X)
    chi_selector = SelectKBest(chi2, k=88)
    chi_selector.fit(X_norm, y)
    chi_support = chi_selector.get_support()
    chi_feature = X.loc[:,chi_support].columns.tolist()
    print(str(len(chi_feature)), 'selected features', chi_feature)
    88 caractéristiques sélectionnées ['ÂGE_AU-DESSUS65', 'GENRE', 'GROUPE DE MALADIES 1', ... ... 'P02_VENEUS_MIN', 'P02_VENEUS_MAX', ... ... RATURE_MAX', 'DIFFÉRENCE_DE_TENSION_ARTÉRIELLE_DIASTOLIQUE', ... ... 'DIFFÉRENCE_DE_TEMPÉRATURE_REL', 'DIFFÉRENCE_DE SATURATION_D'OXYGÈNE_REL']

    Sélection des caractéristiques - Corrélation de Pearson 

    def cor_selector(X, y,num_feats):
        cor_list = []
        feature_name = X.columns.tolist()
        # calculate the correlation with y for each feature
        for i in X.columns.tolist():
            cor = np.corrcoef(X[i], y)[0, 1]
            cor_list.append(cor)
        # replace NaN with 0
        cor_list = [0 if np.isnan(i) else i for i in cor_list]
        # feature name
        cor_feature = X.iloc[:,np.argsort(np.abs(cor_list))[-num_feats:]].columns.tolist()
        # Sélection des caractéristiques? 0 for not select, 1 for select
        cor_support = [Vrai if i in cor_feature else False for i in feature_name]
        return cor_support, cor_featurecor_support, cor_feature = cor_selector(X, y, 88)
    print(str(len(cor_feature)), 'selected features:  ', cor_feature)
    88 caractéristiques sélectionnées:   ['TEMPÉRATURE_MOYENNE', 'TENSION_DIASTOLIQUE_MAX', ... ... 'DIFFÉRENCE_DE_TAUX_ RESPIRATOIRE', 'AUX_ RESPIRATOIRE_MAX']

    Sélection des caractéristiques - élimination de caractéristiques récursives (RFE) {#feature-selection---Recursive-Feature-Elimination-(RFE)}

    from sklearn.feature_selection import RFE
    from sklearn.linear_model import LogisticRegression
    rfe_selector = RFE(estimator=LogisticRegression(), n_features_to_select=88, step=100, verbose=5)
    rfe_selector.fit(X_norm, y)
    rfe_support = rfe_selector.get_support()
    rfe_feature = X.loc[:,rfe_support].columns.tolist()
    print(str(len(rfe_feature)), 'selected features: ', rfe_feature)
    Estimateur d'ajustement avec 127 caractéristiques.
    88 caractéristiques sélectionnées:  ['ÂGE_AU-DESSUS65', 'GENRE', ... ... 'DIFFÉRENCE_DE_TAUX_ RESPIRATOIRE_REL', 'DIFFÉRENCE_DE_TEMPÉRATURE_REL']

    Sélection des caractéristiques - Lasso

    ffrom sklearn.feature_selection import SelectFromModel
    from sklearn.linear_model import LogisticRegression
    from sklearn.preprocessing import MinMaxScaler
    X_norm = MinMaxScaler().fit_transform(X)
    embeded_lr_selector = SelectFromModel(LogisticRegression(penalty="l2"), max_features=88)
    embeded_lr_selector.fit(X_norm, y)
    embeded_lr_support = embeded_lr_selector.get_support()
    embeded_lr_feature = X.loc[:,embeded_lr_support].columns.tolist()
    print(str(len(embeded_lr_feature)), 'selected features', embeded_lr_feature)
    65 caractéristiques sélectionnées ['ÂGE_AU-DESSUS65', 'GENRE', ... ... 'DIFFÉRENCE_DE_TAUX_ RESPIRATOIRE_REL', 'DIFFÉRENCE_DE_TEMPÉRATURE_REL']

    Sélection des caractéristiques - RF Tree-based: SelectFromModel

    from sklearn.feature_selection import SelectFromModel
    from sklearn.ensemble import RandomForestClassifier
    embeded_rf_selector = SelectFromModel(RandomForestClassifier(n_estimators=100), max_features=227)
    embeded_rf_selector.fit(X, y)
    embeded_rf_support = embeded_rf_selector.get_support()
    embeded_rf_feature = X.loc[:,embeded_rf_support].columns.tolist()
    print(str(len(embeded_rf_feature)), 'selected features', embeded_rf_feature)
    48 selected features ['ÂGE_AU-DESSUS65', 'GENRE', ... ... 'DIFFÉRENCE_DE_TEMPÉRATURE_REL', 'DIFFÉRENCE_DE SATURATION_D'OXYGÈNE_REL']

    Sélection des caractéristiques - LightGBM or XGBoost {#feature-selection---LightGBM-or-XGBoost}

    from sklearn.feature_selection import SelectFromModel
    from lightgbm import LGBMClassifierlgbc=LGBMClassifier(n_estimators=500, learning_rate=0.05, num_leaves=32, colsample_bytree=0.2,
                reg_alpha=3, reg_lambda=1, min_split_gain=0.01, min_child_weight=40)embeded_lgb_selector = SelectFromModel(lgbc, max_features=128)
    embeded_lgb_selector.fit(X, y)embeded_lgb_support = embeded_lgb_selector.get_support()
    embeded_lgb_feature = X.loc[:,embeded_lgb_support].columns.tolist()
    print(str(len(embeded_lgb_feature)), 'selected features:  ', embeded_lgb_feature)
    embeded_lgb_feature.index
    56 selected features:   ['ÂGE_AU-DESSUS65', 'GENRE', 'HTN', ... ... 'DIFFÉRENCE_DE_TEMPÉRATURE_REL', 'DIFFÉRENCE_DE SATURATION_D'OXYGÈNE_REL']

    Sélection des caractéristiques - Les regrouper tous {#feature-selection---Ensemble-them-all}

    feature_name = X.columns.tolist()
    # regrouper toute la sélection
    feature_selection_df = pd.DataFrame({'Feature':feature_name, 'Pearson':cor_support, 'Chi-2':chi_support, 'RFE':rfe_support, 'Logistics':embeded_lr_support, 'Random Forest':embeded_rf_support, 'LightGBM':embeded_lgb_support})
    # compter les temps sélectionnés pour chaque caractéristique
    feature_selection_df['Total'] = np.sum(feature_selection_df, axis=1)
    # afficher les 100 premières
    num_feats = 227
    feature_selection_df = feature_selection_df.sort_values(['Total','Feature'] , ascending=False)
    feature_selection_df.index = range(1, len(feature_selection_df)+1)
    feature_selection_df.head(num_feats)
    df_selected_columns = feature_selection_df.loc[(feature_selection_df['Total'] > 3)]
    df_selected_columns

    Nous pouvons dresser la liste des caractéristiques qui ont été sélectionnées dans le cadre d'au moins quatre méthodes :

    ... ...

    Nous pouvons certainement choisir ces 58 caractéristiques. Entre-temps, l'expérience nous a appris que la sélection des caractéristiques n'est pas nécessairement toujours un vote démocratique ; le plus souvent, elle peut être spécifique au problème du domaine, aux données spécifiques et parfois au modèle ou à l'approche ML spécifique que nous allons adopter plus tard.

    Sélection des caractéristiques - Outils tiers 

    Il existe des outils industriels et des outils AutoML largement utilisés, par exemple DataRobot qui peut fournir une bonne sélection automatique des caractéristiques :  

    Le graphe DataRobot ci-dessus montre, sans surprise, que les valeurs de fréquence respiratoire et de tension artérielle sont les caractéristiques les plus pertinentes pour l'admission en soins intensifs.    

    Sélection des caractéristiques - Sélection finale
    Dans ce cas, j'ai fait quelques expériences rapides et j'ai remarqué que la sélection des caractéristiques par LightGBM donnait un résultat un peu meilleur, c'est pourquoi nous n'utiliserons que cette méthode de sélection.   

    df_selected_columns = embeded_lgb_feature  # mieux que la sélection ensemblistedataS = pd.concat([idf[df_selected_columns],idf['ICU'], df_cat['FENÊTRE']],1)
    dataS.ICU.value_counts()
    print(dataS.shape)
    (1925, 58)

    Nous pouvons voir que 58 caractéristiques sont sélectionnées, c'est-à-dire ni trop peu, ni trop beaucoup, ce qui semble être la bonne quantité pour ce problème spécifique de classification binaire à cible unique. 

    Déséquilibre des données

    plt.figure(figsize=(10,5))
    count = sns.countplot(x = "USI",data=data)
    count.set_xticklabels(["Non admis", "Admis"])
    plt.xlabel("Admission à l'USI")
    plt.ylabel("Nombre de patients")
    plt.show()

    Cela indique que les données sont déséquilibrées, seuls 26 % des enregistrements étant admis en USI. Cela aura un impact sur les résultats et nous pouvons donc envisager des approches normales d'équilibrage des données telles que SMOTE, etc.

    Nous pouvons essayer toutes sortes d'autres AED pour analyser les différentes distributions de données en conséquence. 

    Exécuter une formation de base en LR

    Le site Kaggle propose de jolis carnets d'entraînement rapide que nous pouvons exécuter rapidement en fonction de notre propre sélection de colonnes de caractéristiques. Commençons par une exécution rapide du classificateur LR pour le pipeline de formation :

    data2 = pd.concat([idf[df_selected_columns],idf['USI'], df_cat['FENÊTRE']],1)   
    data2.AGE_ABOVE65 = data2.AGE_ABOVE65.astype(int)
    data2.ICU = data2.ICU.astype(int)
    X2 = data2.drop("USI",1)
    y2 = data2.ICU
    
    from sklearn.preprocessing import LabelEncoder
    label_encoder = LabelEncoder()
    X2.WINDOW = label_encoder.fit_transform(np.array(X2["FENÊTRE"].astype(str)).reshape((-1,)))
    
    confusion_matrix2 = pd.crosstab(y2_test, y2_hat, rownames=['Réel'], colnames=['Prédit'])
    sns.heatmap(confusion_matrix2, annot=Vrai, fmt = 'g', cmap = 'Reds') print("ORIGINAL")
    print(classification_report(y_test, y_hat))
    print("USI = ",roc_auc_score(y_test, y_hat),'\n\n')
    print("ENCODAGE D'ÉTIQUETTE")
    print(classification_report(y2_test, y2_hat))
    print("ASC = ",roc_auc_score(y2_test, y2_hat))
    y2hat_probs = LR.predict_proba(X2_test)
    y2hat_probs = y2hat_probs[:, 1] fpr2, tpr2, _ = roc_curve(y2_test, y2hat_probs) plt.figure(figsize=(10,7))
    plt.plot([0, 1], [0, 1], 'k--')
    plt.plot(fpr, tpr, label="Base")
    plt.plot(fpr2,tpr2,label="Étiquette encodée")
    plt.xlabel('Taux de faux positifs')
    plt.ylabel('Taux de vrais positifs')
    plt.title('Courbe ROC')
    plt.legend(loc="meilleur")
    plt.show()
    
    
    ORIGINAL
                  précision    rappel  score f1   support
               0       0.88      0.94      0.91       171
               1       0.76      0.57      0.65        54
        exactitude                           0.85       225
       moyenne macro       0.82      0.76      0.78       225
    moyenne pondérée       0.85      0.85      0.85       225
    ASC=  0.7577972709551657
    LABEL ENCODING
                  précision    rappel  score f1   support
               0       0.88      0.93      0.90       171
               1       0.73      0.59      0.65        54
        accuracy                           0.85       225
       moyenne macro       0.80      0.76      0.78       225
    moyenne pondérée       0.84      0.85      0.84       225
    ASC =  0.7612085769980507

            

    Il semble qu'il atteigne une AUC de 76 %, avec une précision de 85 %, mais le rappel pour les patients admis en réanimation n'est que de 59 % - il semble y avoir trop de faux négatifs. Ce n'est certainement pas l'idéal - nous ne voulons pas passer à côté des risques réels de l'USI pour le dossier d'un patient. Toutes les tâches suivantes seront donc axées sur l'objectif sur la manière d'augmenter le taux de rappel, en réduisant le FN, avec une précision globale quelque peu équilibrée, nous l'espérons.

    Dans les sections précédentes, nous avons mentionné des données déséquilibrées, de sorte que notre premier réflexe serait de stratifier l'ensemble de test et de le MODIFIER pour obtenir un ensemble de données plus équilibré.

    #stratifier les données de test, afin de s'assurer que les données de train et de test ont le même ratio de 1:0
    X3_train,X3_test,y3_train,y3_test = train_test_split(X2,y2,test_size=225/1925,random_state=42, stratify = y2, shuffle = Vrai) &lt;span> &lt;/span>
    # former et prédire
    LR.fit(X3_train,y3_train)
    y3_hat = LR.predict(X3_test)
    
    #MODIFIER les données pour faire de l'UCI 1:0 une distribution équilibrée
    from imblearn.over_sampling import SMOTE sm = SMOTE(random_state = 42)
    X_train_res, y_train_res = sm.fit_sample(X3_train,y3_train.ravel())
    LR.fit(X_train_res, y_train_res)
    y_res_hat = LR.predict(X3_test)
    
    #recréer la matrice de confusion, etc.
    confusion_matrix3 = pd.crosstab(y3_test, y_res_hat, rownames=['Actual'], colnames=['Predicted'])
    sns.heatmap(confusion_matrix3, annot=Vrai, fmt = 'g', cmap="YlOrBr")
    print("LABEL ENCODING + STRATIFY")
    print(classification_report(y3_test, y3_hat))
    print("ASC = ",roc_auc_score(y3_test, y3_hat),'\n\n')
    print("SMOTE")
    print(classification_report(y3_test, y_res_hat))
    print("ASC = ",roc_auc_score(y3_test, y_res_hat))
    y_res_hat_probs = LR.predict_proba(X3_test)
    y_res_hat_probs = y_res_hat_probs[:, 1]
    fpr_res, tpr_res, _ = roc_curve(y3_test, y_res_hat_probs) plt.figure(figsize=(10,10))
    
    #Et tracez la courbe ROC comme précédemment.

     

    LABEL ENCODING + STRATIFY (CODAGE D'ÉTIQUETTES + STRATIFICATION)
                  précision    rappel  f1 score   support
               0       0.87      0.99      0.92       165
               1       0.95      0.58      0.72        60
        exactitude                           0.88       225
       moyenne macro       0.91      0.79      0.82       225
    moyenne pondérée       0.89      0.88      0.87       225
    ASC =  0.7856060606060606
    SMOTE
                  précision    rappel  f1 score   support
               0       0.91      0.88      0.89       165
               1       0.69      0.75      0.72        60
        exactitude                           0.84       225
       moyenne macro       0.80      0.81      0.81       225
    moyenne pondérée       0.85      0.84      0.85       225
    ASC =  0.8143939393939393

                

    Les traitements des données par STRATIFY (stratification) et SMOT (optimisation) semblent donc améliorer le rappel, qui passe de 0,59 à 0,75, avec une précision globale de 0,84. 

    Maintenant que le traitement des données est largement effectué comme d'habitude pour le ML traditionnel, nous voulons savoir quel pourrait être le(s) meilleur(s) modèle(s) dans ce cas ; peuvent-ils faire mieux, et pouvons-nous alors essayer une comparaison globale relative ?

    Comparaison de l'entraînement à la course de différents modèles

    Poursuivons l'évaluation de quelques algorithmes de ML couramment utilisés, et générons un tableau de bord de résultats à comparer à l'aide de diagrammes en boîte à moustaches :

    # comparer les algorithmes
    from matplotlib import pyplot
    from sklearn.model_selection import train_test_split
    from sklearn.model_selection import cross_val_score
    from sklearn.model_selection import StratifiedKFold
    from sklearn.linear_model import LogisticRegression
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
    from sklearn.naive_bayes import GaussianNB
    from sklearn.svm import SVC
    #Importer un modèle arborescent aléatoire
    from sklearn.ensemble import RandomForestClassifier
    from xgboost import XGBClassifier# Répertorier les algorithmes ensemble
    models = []
    models.append(('LR', &lt;strong>LogisticRegression&lt;/strong>(solver='liblinear', multi_class='ovr')))
    models.append(('LDA', LinearDiscriminantAnalysis()))
    models.append(('KNN', &lt;strong>KNeighborsClassifier&lt;/strong>()))
    models.append(('CART', &lt;strong>DecisionTreeClassifier&lt;/strong>()))
    models.append(('NB', &lt;strong>GaussianNB&lt;/strong>()))
    models.append(('SVM', &lt;strong>SVC&lt;/strong>(gamma='auto')))
    models.append(('RF', &lt;strong>RandomForestClassifier&lt;/strong>(n_estimators=100)))
    models.append(('XGB', &lt;strong>XGBClassifier&lt;/strong>())) #clf = XGBClassifier()
    # évaluer chaque modèle à tour de rôle
    résultats = []
    noms = []
    pour nom, modèler dans modèles :
        kfold = StratifiedKFold(n_splits=10, random_state=1)
        cv_results = cross_val_score(model, X_train_res, y_train_res, cv=kfold, scoring='f1')  ## exactitude, précision, rappel
        results.append(cv_results)
        names.append(name)
        print('%s: %f (%f)' % (name, cv_results.mean(), cv_results.std()))# Comparer les performances de tous les modèles. Question - Souhaitez-vous voir un article intégré sur le site ?
    pyplot.figure(4, figsize=(12, 8))
    pyplot.boxplot(résultats, étiquettes=noms)
    pyplot.title('Comparaison des algorithmes')
    pyplot.show()
    LR: 0.805390 (0.021905) LDA: 0.803804 (0.027671) KNN: 0.841824 (0.032945) CART: 0.845596 (0.053828)
    NB: 0.622540 (0.060390) SVM: 0.793754 (0.023050) RF: 0.896222 (0.033732) XGB: 0.907529 (0.040693)

    Ce qui précède semble montrer que le classificateur XGB et le classificateur de la forêt aléatoire "Random Forest" obtiendraient un meilleur score F1 que les autres modèles.

    Comparons leurs résultats réels sur le même ensemble de données de test normalisées :

    Temps d'importation
    from pandas import read_csv
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report
    from sklearn.metrics import confusion_matrix
    from sklearn.metrics import accuracy_score
    from sklearn.svm import SVCpour nom, modèler dans modèles :
        print(name + ':\n\r')
        start = time.clock()
        model.fit(X_train_res, y_train_res)
        print("Temps de formation pour ", model, " ", time.clock() - start)
        predictions = model.predict(X3_test) #(X_validation)
        # Evaluate predictions
        print(accuracy_score(y3_test, predictions))  # Y_validation
        print(confusion_matrix(y3_test, predictions))
        print(classification_report(y3_test, predictions))
    LR:
    Temps de formation pour  LogisticRegression(multi_class='ovr', solver='liblinear')   0.02814499999999498
    0.8444444444444444
    [[145  20]
     [ 15  45]]
                  précision    rappel  f1 score   support
               0       0.91      0.88      0.89       165
               1       0.69      0.75      0.72        60
        exactitude                           0.84       225
       moyenne macro       0.80      0.81      0.81       225
    moyenne pondérée       0.85      0.84      0.85       225
    
    LDA:
    Temps de formation pour  LinearDiscriminantAnalysis()   0.2280070000000194
    0.8488888888888889
    [[147  18]
     [ 16  44]]
                  précision    rappel  f1 score   support
               0       0.90      0.89      0.90       165
               1       0.71      0.73      0.72        60
        exactitude                           0.85       225
       moyenne macro       0.81      0.81      0.81       225
    moyenne pondérée       0.85      0.85      0.85       225
    
    KNN:
    Temps de formation pour  KNeighborsClassifier()   0.13023699999999394
    0.8355555555555556
    [[145  20]
     [ 17  43]]
                  précision    rappel  f1 score   support
               0       0.90      0.88      0.89       165
               1       0.68      0.72      0.70        60
        exactitude                           0.84       225
       moyenne macro       0.79      0.80      0.79       225
    moyenne pondérée       0.84      0.84      0.84       225
    
    CART:
    Temps de formation pour  DecisionTreeClassifier()   0.32616000000001577
    0.8266666666666667
    [[147  18]
     [ 21  39]]
                  précision    rappel  f1 score   support
               0       0.88      0.89      0.88       165
               1       0.68      0.65      0.67        60
        exactitude                           0.83       225
       moyenne macro       0.78      0.77      0.77       225
    moyenne pondérée       0.82      0.83      0.83       225
    
    NB:
    Temps de formation pour  GaussianNB()   0.0034229999999979555
    0.8355555555555556
    [[154  11]
     [ 26  34]]
                  précision    rappel  f1 score   support
               0       0.86      0.93      0.89       165
               1       0.76      0.57      0.65        60
        exactitude                           0.84       225
       moyenne macro       0.81      0.75      0.77       225
    moyenne pondérée       0.83      0.84      0.83       225
    
    SVM:
    Temps de formation pour  SVC(gamma='auto')   0.3596520000000112
    0.8977777777777778
    [[157   8]
     [ 15  45]]
                  précision    rappel  f1 score   support
               0       0.91      0.95      0.93       165
               1       0.85      0.75      0.80        60
        exactitude                           0.90       225
       moyenne macro       0.88      0.85      0.86       225
    moyenne pondérée       0.90      0.90      0.90       225
    
    RF:
    Temps de formation pour  RandomForestClassifier()   0.50123099999999
    0.9066666666666666
    [[158   7]
     [ 14  46]]
                  précision    rappel  f1 score   support
               0       0.92      0.96      0.94       165
               1       0.87      0.77      0.81        60
        exactitude                           0.91       225
       moyenne macro       0.89      0.86      0.88       225
    moyenne pondérée       0.91      0.91      0.90       225
    
    XGB:
    Temps de formation pour  XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
                  colsample_bynode=1, colsample_bytree=1, gamma=0, gpu_id=-1,
                  importance_type='gain', interaction_constraints='',
                  learning_rate=0.300000012, max_delta_step=0, max_depth=6,
                  min_child_weight=1, missing=nan, monotone_constraints='()',
                  n_estimators=100, n_jobs=0, num_parallel_tree=1, random_state=0,
                  reg_alpha=0, reg_lambda=1, scale_pos_weight=1, subsample=1,
                  tree_method='exact', validate_parameters=1, verbosity=Aucun)   1.649520999999993
    0.8844444444444445
    [[155  10]
     [ 16  44]]
                  précision    rappel  f1 score   support
               0       0.91      0.94      0.92       165
               1       0.81      0.73      0.77        60
        exactitude                           0.88       225
       moyenne macro       0.86      0.84      0.85       225
    moyenne pondérée       0.88      0.88      0.88       225

    Le résultat semble être que RF est en fait meilleur que XGB. Cela pourrait signifier que XGB est peut-être un peu plus surajouté d'une manière ou d'une autre. Le résultat de RFC est également légèrement meilleur que celui de LR.

    Exécuter le modèle sélectionné en poursuivant "Ajustement des paramètres via la recherche par quadrillage"

    Supposons maintenant que nous ayons choisi le modèle de classificateur de la forêt aléatoire "Random Forest Classifier". Nous pouvons effectuer une nouvelle recherche sur la grille de ce modèle pour voir s'il est possible d'obtenir des résultats un peu plus performants. 

    Rappelez-vous que notre objectif est toujours d'optimiser le rappel dans ce cas, en minimisant le nombre de faux négatifs concernant les risques possibles pour l'USI lors de la rencontre avec le patient, nous utiliserons donc 'recall_score' pour réajuster le quadrillage ci-dessous. Une fois de plus, la validation croisée 10 fois sera utilisée comme d'habitude, étant donné que notre ensemble de test ci-dessus a toujours été fixé à environ 12 % de ces 2915 enregistrements.

    from sklearn.model_selection import GridSearchCV
    # Créer la grille de paramètres sur la base des résultats de la recherche aléatoire
    
    param_grid = {'bootstrap': [Vrai],
     'ccp_alpha': [0.0],
     'class_weight': [Aucun],
     'criterion': ['gini', 'entropy'],
     'max_depth': [Aucun],
     'max_features': ['auto', 'log2'],             
     'max_leaf_nodes': [Aucun],
     'max_samples': [Aucun],
     'min_impurity_decrease': [0.0],
     'min_impurity_split': [Aucun],
     'min_samples_leaf': [1, 2, 4],
     'min_samples_split': [2, 4],
     'min_weight_fraction_leaf': [0.0],
     'n_estimators': [100, 125],
     #'n_jobs': [Aucun],
     'oob_score': [False],
     'random_state': [Aucun],
     #'verbose': 0,
     'warm_start': [False]
    }#Ajuster par matrice de confusion
    from sklearn.metrics import roc_curve, précision_recall_curve, auc, make_scorer, recall_score, accuracy_score, précision_score, confusion_matrix
    scorers = {
        'recall_score': make_scorer(recall_score),
        'précision_score': make_scorer(précision_score),
        'accuracy_score': make_scorer(accuracy_score)
    }# Créer un modèle de base
    rfc = RandomForestClassifier()
    # Instancier le modèle de quadrillage
    grid_search = GridSearchCV(estimator = rfc, param_grid = param_grid,
                               scoring=scorers, refit='recall_score',
                               cv = 10, n_jobs = -1, verbose = 2)train_features = X_train_resgrid_search.fit(train_features, train_labels)
    rf_best_grid = grid_search.best_estimator_rf_best_grid.fit(train_features, train_labels)
    rf_predictions = rf_best_grid.predict(X3_test)
    print(accuracy_score(y3_test, rf_predictions))  
    print(confusion_matrix(y3_test, rf_predictions))
    print(classification_report(y3_test, rf_predictions))
    0.92
    [[ 46  14]
     [  4 161]]
                  précision    rappel f1 score   support
               0       0.92      0.77      0.84        60
               1       0.92      0.98      0.95       165
        exactitude                           0.92       225
       moyenne macro       0.92      0.87      0.89       225
    moyenne pondérée       0.92      0.92      0.92       225

    Le résultat a montré qu'un quadrillage a permis d'augmenter légèrement la précision globale, tout en maintenant le FN au même niveau. 

    Traçons également les comparaisons avec l'ASC :

    confusion_matrix4 = pd.crosstab(y3_test, rf_predictions, rownames=['Actual'], colnames=['Predicted'])
    sns.heatmap(confusion_matrix4, annot=Vrai, fmt = 'g', cmap="YlOrBr")print("LABEL ENCODING + STRATIFY")
    print(classification_report(y3_test, 1-y3_hat))
    print("ASC = ",roc_auc_score(y3_test, 1-y3_hat),'\n\n')print("SMOTE")
    print(classification_report(y3_test, 1-y_res_hat))
    print("ASC = ",roc_auc_score(y3_test, 1-y_res_hat), '\n\n')print("SMOTE + LBG Selected Weights + RF Grid Search")
    print(classification_report(y3_test, rf_predictions))
    print("ASC = ",roc_auc_score(y3_test, rf_predictions), '\n\n\n')y_res_hat_probs = LR.predict_proba(X3_test)
    y_res_hat_probs = y_res_hat_probs[:, 1]predictions_rf_probs = rf_best_grid.predict_proba(X3_test) #(X_validation)
    predictions_rf_probs = predictions_rf_probs[:, 1]fpr_res, tpr_res, _ = roc_curve(y3_test, 1-y_res_hat_probs)
    fpr_rf_res, tpr_rf_res, _ = roc_curve(y3_test, predictions_rf_probs)plt.figure(figsize=(10,10))
    plt.plot([0, 1], [0, 1], 'k--')
    plt.plot(fpr, tpr, label="Base")
    plt.plot(fpr2,tpr2,label="Label Encoded")
    plt.plot(fpr3,tpr3,label="Stratify")
    plt.plot(fpr_res,tpr_res,label="SMOTE")
    plt.plot(fpr_rf_res,tpr_rf_res,label="SMOTE + RF GRID")
    plt.xlabel('False positive rate')
    plt.ylabel('Vrai positive rate')
    plt.title('ROC curve')
    plt.legend(loc="best")
    plt.show()
    CODAGE D'ÉTIQUETTES + STRATIFICATION
                  précision    rappel  f1 score   support
               0       0.95      0.58      0.72        60
               1       0.87      0.99      0.92       165
        exactitude                           0.88       225
       moyenne macro       0.91      0.79      0.82       225
    moyenne pondérée       0.89      0.88      0.87       225
    ASC =  0.7856060606060606
    
    MODIFICATION
                  précision    rappel  f1 score   support
               0       0.69      0.75      0.72        60
               1       0.91      0.88      0.89       165
        exactitude                           0.84       225
       moyenne macro       0.80      0.81      0.81       225
    moyenne pondérée       0.85      0.84      0.85       225
    ASC =  0.8143939393939394
    
    MODIFICATION + LBG Pondérations sélectionnées + Quadrillage RF
                  précision    rappel  f1 score   support
               0       0.92      0.77      0.84        60
               1       0.92      0.98      0.95       165
        exactitude                           0.92       225
       moyenne macro       0.92      0.87      0.89       225
    moyenne pondérée       0.92      0.92      0.92       225
    ASC =  0.8712121212121211

         

    Le résultat a montré qu'après des comparaisons d'algorithmes et un quadrillage suivant, nous avons réussi à faire passer l'ASC de 78 % à 87 %, avec une précision globale de 92 % et un rappel de 77 %.

    Récapitulatif de l'approche "ML traditionnelle"

    Qu'en est-il réellement de ce résultat ? Il est correct pour un processus manuel de base avec des algorithmes ML traditionnels. Comment ce résultat apparaît-il dans les tableaux de compétition Kaggle ? Eh bien, il ne figurerait pas dans le tableau de classement. J'ai passé le jeu de données brutes par le service AutoML actuel de DataRobot, le meilleur résultat serait un ASC équivalent de ~90+% (à confirmer avec des données similaires) avec le modèle " Classificateur arborescent XGB avec fonctions d'apprentissage non supervisé " (XGB Trees Classifier with Unsupervised Learning Features), sur une comparaison des 43 meilleurs modèles. C'est peut-être le genre de modèle de base que nous devrions utiliser si nous voulons vraiment être compétitifs sur Kaggle. Je joindrai également la liste des meilleurs résultats par rapport aux modèles dans le github. Finalement, pour les cas réels spécifiques aux sites de soins, j'ai le sentiment que nous devons également intégrer un certain degré d'approches d'apprentissage profond personnalisées, comme mentionné dans la section "Données et tâches" de ce billet. Bien sûr, dans les cas réels, l'endroit où collecter des colonnes de données de qualité pourrait également être une question initiale.

    L'approche IntegratedML?

    Ce qui précède est un processus de ML dit traditionnel, qui comprend normalement l'EDA des données, l'ingénierie des caractéristiques, la sélection des caractéristiques, la sélection des modèles, et l'optimisation des performances par la quadrillage, etc. C'est l'approche la plus simple à laquelle j'ai pu penser jusqu'à présent pour cette tâche, et nous n'avons même pas encore abordé le déploiement du modèle et les cycles de vie de la gestion des services - nous le ferons dans le prochain article, en examinant comment nous pourrions tirer parti de Flask/FastAPI/IRIS et déployer ce modèle de ML de base dans une pile de services de démonstration de la radiographie de Covid-19.

    IRIS dispose désormais d'IntegratedML, qui est une enveloppe SQL élégante d'options puissantes d'AutoMLs. Dans la deuxième partie, nous verrons comment accomplir la tâche susmentionnée dans le cadre d'un processus simplifié, de sorte que nous n'aurons plus à nous préoccuper de la sélection des caractéristiques, de la sélection des modèles, de l'optimisation des performances, etc.

    Jusqu'ici, cet article pourrait être trop long pour une note de 10 minutes visant à intégrer rapidement les mêmes données, c'est pourquoi je le déplace vers l'article suivant, partie II

    0
    0 74
    Article Guillaume Rongier · Fév 17, 2023 18m read

    Keywords:  IRIS, IntegratedML, Flask, FastAPI, Tensorflow servant, HAProxy, Docker, Covid-19

    Objective:

    Nous avons abordé quelques démonstrations rapides d'apprentissage profond et d'apprentissage automatique au cours des derniers mois, notamment un simple classificateur d'images radiographiques Covid-19 et un classificateur de résultats de laboratoire Covid-19 pour les admissions possibles en soins intensifs. Nous avons également évoqué une implémentation de démonstration IntegratedML du classificateur ICU. Alors que la randonnée de la "science des données" se poursuit, le moment est peut-être venu d'essayer de déployer des services d'IA du point de vue de "l'ingénierie des données" - pourrions-nous regrouper tout ce que nous avons abordé jusqu'à présent dans un ensemble d'API de services ? Quels sont les outils, les composants et l'infrastructure communs que nous pourrions exploiter pour réaliser une telle pile de services dans son approche la plus simple possible ?

     

    Cadre

    Dans le cadre de ce qui suit:

    Pour commencer, nous pouvons simplement utiliser docker-compose pour déployer les composants dockerisés suivants dans un serveur AWS Ubuntu

    • HAProxy - équilibreur de charge
    • Gunicorn vs. Univorn ** - passerelle web **serveurs
    • Flask vs. FastAPI - serveurs d'application pour l'interface utilisateur des applications Web, les définitions des API de service, la génération des cartes thermiques, etc.
    • Tensorflow Model Serving vs. Tensorflow-GPU Model Serving - serveurs d'applications pour les classifications d'images, etc.
    • IRIS IntegratedML - AutoML consolidé App+DB avec interface SQL.
    • Python3 dans Jupyter Notebook pour émuler un client pour le benchmarking.
    • Docker et docker-compose.
    • AWS Ubuntu 16.04 avec un GPU Tesla T4  

    Remarque:   Tensorflow Serving avec GPU n'est utilisé qu'à des fins de démonstration - vous pouvez simplement désactiver l'image liée au GPU (dans un fichier docker) et la configuration (dans le fichier docker-compose.yml).

    Out of scope (Hors de portée) ou sur la prochaine liste de souhaits :

    • **Les serveurs web Nginx ou Apache etc. sont omis dans la démo pour le moment.
    • RabbitMQ et Redis - courtier de file d'attente pour une messagerie fiable qui peut être remplacé par IRIS ou Ensemble.
      IAM (Intersystems API Manger) ou Kong est sur la liste des souhaits.
    • SAM(Intersystems System Alert & Monitoring)
    • ICM (Intersystems Cloud Manager) avec l'opérateur Kubernetes - toujours l'un de mes préférés depuis sa naissance
    • FHIR (serveur FHIR R4 basé sur Intesystems IRIS et FHIR Sandbox pour les applications SMART sur FHIR)
    • Outils de développement CI/CD ou Github Actions.

    De toute façon, un "ingénieur en apprentissage automatique" ("Machine Learning Engineer") mettra inévitablement la main sur ces composants pour fournir des environnements de production tout au long des cycles de vie des services. Nous pourrons en savoir plus au fil du temps.

    Dépôt Github

    Le code source complet se trouve à l'adresse suivante : https://github.com/zhongli1990/covid-ai-demo-deployment

    Le référentiel integratedML-demo-template est également réutilisé avec le nouveau référentiel. 
     

    Modèle de déploiement

    Le schéma de déploiement logique de ce cadre de test "Démonstration de l'IA dans les Dockers" est présenté ci-dessous.

    Pour la démonstration, j'ai délibérément créé deux piles distinctes pour la classification de l'apprentissage profond et le rendu web, puis j'ai utilisé un HAProxy comme équilibreur de charge pour distribuer les requêtes API entrantes entre ces deux piles de manière indépendante.

    • Guniorn + Flask + Tensorflow Serving
    • Univcorn + FaskAPI + Tensorflow Serving GPU

    IRIS avec IntegratedML est utilisé pour les échantillons de démonstration d'apprentissage automatique, comme dans l'article précédent de prédiction de l'ICU.

    J'ai omis certains composants communs dans la démo actuelle qui seraient nécessaires ou envisagés pour les services de production :

    • Serveurs Web : Nginx ou Apache, etc. Ils seront nécessaires entre HAProxy et Gunicorn/Uvicorn, pour une gestion correcte des sessions HTTP, c'est-à-dire pour éviter les attaques DoS, etc.
    • Gestionnaire de file d'attente et bases de données : RabbitMQ et/ou Redis, etc., entre Flask/FastAPI et le serveur backend, pour un service asynchrone fiable et la persistance des données/configurations, etc.
    • Passerelle API : IAM ou Kong clusters, entre l'équilibreur de charge HAProxy et le serveur web pour la gestion des API sans créer de point singulier de défaillance.
    • Surveillance et alerte : SAM serait bien.
    • Provisionnement pour CI/CD devops : ICM avec K8s serait nécessaire pour le déploiement et la gestion neutre en nuage, et pour CI/CD avec d'autres outils devops communs.

    En fait,  IRIS lui-même peut certainement être utilisé comme gestionnaire de file d'attente de niveau entreprise ainsi que comme base de données performante pour une messagerie fiable. Dans l'analyse des modèles, il apparaît qu'IRIS peut remplacer les courtiers de file d'attente et les bases de données RabbitMQ/Redis/MongoDB, etc., et qu'il serait donc mieux consolidé avec une latence bien moindre et de meilleures performances globales. Et plus encore, IRIS Web Gateway (anciennement CSP Gateway) peut certainement être positionné à la place de Gunicorn ou Unicorn, etc, n'est-ce pas ?  

    Topologie de l'environnement

    Il existe quelques options courantes pour mettre en œuvre le modèle logique ci-dessus dans tous les composants Docker. Les plus courantes sont les suivantes :  

    • docker-compose
    • docker swarm etc
    • Kubernetes etc 
    • ICM avec K8s Operation

    Cette démonstration commence avec "docker-compose" pour un PoC fonctionnel et un certain benchmarking. Nous aimerions certainement utiliser K8s et peut-être aussi ICM au fil du temps. 

    Comme décrit dans son fichier docker-compose.yml, une implémentation physique de sa topologie d'environnement sur un serveur AWS Ubuntu ressemblerait à ceci :  

    Le diagramme ci-dessus montre comment ces ports de service de toutes les instances Docker sont mappés et exposés directement sur le serveur Ubuntu à des fins de démonstration. En production, la sécurité devrait être renforcée. Et pour les besoins de la démonstration, tous les conteneurs sont connectés au même réseau Docker, alors qu'en production, il serait séparé en routable externe et non-routable interne.

    Composants "Dockerisés" 

    Le tableau ci-dessous montre comment les volumes de stockage de la machine hôte sont montés sur chaque instance de conteneur comme spécifié dans ce fichier docker-compose.yml

    ubuntu@ip-172-31-35-104:/zhong/flask-xray$ tree ./ -L 2

    ./├── covid19                             (Les conteneurs Flask+Gunicorn et Tensorflow Serving seront montés ici)├── app.py                                (Flask main app:  Les interfaces de l'application web et du service API sont définies et mises en œuvre ici)├── covid19_models               (Les modèles Tensorflow sont publiés et versionnés ici pour la classification des images Le conteneur Tensorflow Serving avec CPU)├── Dockerfile                          (Le serveur Flask avec Gunicorn:      CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:5000", "--workers", "4", "--threads", "2"])├── models                               (Modèles au format .h5 pour l'application Flask et démonstration API de la génération de heatmaps par grad-cam sur des radiographies.)├── __pycache__├── README.md├── requirements.txt             (Paquets Python nécessaires pour les applications complètes de Flask+Gunicorn) ├── scripts├── static                                  (Fichiers statiques Web)├── templates                         (Modèles de rendu Web)├── tensorflow_serving        (Fichier de configuration pour le service de tensorflow)└── test_images├── covid-fastapi                   (Les conteneurs FastAPI+Uvicorn et Tensorflow Serving avec GPU seront définis ici)├── covid19_models            (Les modèles Tensorflow au service des GPU sont publiés et versionnés ici pour la classification des images)├── Dockerfile                       (Le serveur Uvicorn+FastAPI sera lancé ici: CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4" ])├── main.py                           (FastAPI app: les interfaces de l'application web et du service API sont définies et mises en œuvre ici)├── models                            (Modèles au format .h5 pour l'application FastAPI et démonstration API de la génération de heatmaps par grad-cam sur des radiographies)├── __pycache__├── README.md├── requirements.txt├── scripts├── static├── templates├── tensorflow_serving└── test_images├── docker-compose.yml      (Full stack Docker definition file.  La version 2.3 est utilisée pour tenir compte du GPU Docker "nvidia runtime", sinon la version 3.x peut être utilisée)├── haproxy                             (Le service docker HAProxy est défini ici.  Note : une session collante peut être définie pour le backend LB. )                             ├── Dockerfile└── haproxy.cfg└── notebooks                       (Le service conteneur Jupyter Notebook avec Tensorflow 2.2 et Tensorboard etc)├── Dockerfile├── notebooks                  (Exemples de fichiers notebook pour émuler des applications API Client externes pour les tests fonctionnels et les tests de référence API en Python sur l'équilibreur de charge, etc)└── requirements.txt

    Remarque: Le docker-compose.yml ci-dessus est destiné à la démonstration d'apprentissage profond de Convid X-Rays. Il est utilisé avec le docker-compose.yml d'un autre integratedML-demo-template pour former la pile de services complète, comme indiqué dans la topologie de l'environnement.  

    Démarrage des services 

    Un simple docker-compose up -d permettrait de démarrer tous les services de conteneurs :

    ubuntu@ip-172-31-35-104:~$ docker ps
    ID DE CONTENEUR        IMAGE                                 COMMANDE                  STATUT CRÉÉ                PORTS                                                                              NOMS
    31b682b6961d        iris-aa-server:2020.3AA               "/iris-main"             Il y a 7 semaines         Jusqu'à 2 jours (en bonne santé)   2188/tcp, 53773/tcp, 54773/tcp, 0.0.0.0:8091->51773/tcp, 0.0.0.0:8092->52773/tcp   iml-template-master_irisimlsvr_1
    6a0f22ad3ffc        haproxy:0.0.1                         "/docker-entrypoint.…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8088->8088/tcp                                                             flask-xray_lb_1
    71b5163d8960        ai-service-fastapi:0.2.0              "uvicorn main:app --…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8056->8000/tcp                                                             flask-xray_fastapi_1
    400e1d6c0f69        tensorflow/serving:latest-gpu         "/usr/bin/tf_serving…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8520->8500/tcp, 0.0.0.0:8521->8501/tcp                                     flask-xray_tf2svg2_1
    eaac88e9b1a7        ai-service-flask:0.1.0                "gunicorn app:app --…"   Шl y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8051->5000/tcp                                                             flask-xray_flask_1
    e07ccd30a32b        tensorflow/serving                    "/usr/bin/tf_serving…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8510->8500/tcp, 0.0.0.0:8511->8501/tcp                                     flask-xray_tf2svg1_1
    390dc13023f2        tf2-jupyter:0.1.0                     "/bin/sh -c '/bin/ba…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8506->6006/tcp, 0.0.0.0:8586->8888/tcp                                     flask-xray_tf2jpt_1
    88e8709404ac        tf2-jupyter-jdbc:1.0.0-iml-template   "/bin/sh -c '/bin/ba…"   Il y a 2 $ois         Jusqu'à 2 jours             0.0.0.0:6026->6006/tcp, 0.0.0.0:8896->8888/tcp                                     iml-template-master_tf2jupyter_1

    docker-compose up --scale fastapi=2 --scale flask=2 -d   par exemple, sera mis à l'échelle horizontalement jusqu'à 2 conteneurs Gunicorn+Flask et 2 conteneurs Univcorn+FastAPI :

    ubuntu@ip-172-31-35-104:/zhong/flask-xray$ docker ps
    ID DE CONTENEUR        IMAGE                                 COMMANDE                  STATUT CRÉÉ                PORTS                                                                              NOMS
    dbee3c20ea95        ai-service-fastapi:0.2.0              "uvicorn main:app --…"   Il y a 4 minutes Jusqu'à 4 minutes          0.0.0.0:8057->8000/tcp                                                             flask-xray_fastapi_2
    95bcd8535aa6        ai-service-flask:0.1.0                "gunicorn app:app --…"   Il y a 4 minutes Jusqu'à 4 minutes          0.0.0.0:8052->5000/tcp                                                             flask-xray_flask_2

    ... ...

    L'exécution d'un autre "docker-compose up -d" dans le répertoire de travail de "integrtedML-demo-template" a fait apparaître le conteneur irisimlsvr et tf2jupyter dans la liste ci-dessus.

    Tests

    1. Application web de démonstration de l'IA avec une interface utilisateur simple

    Après avoir démarré les services docker ci-dessus, nous pouvons visiter une application web de démonstration pour X-Ray Covid-19 lung detection hébergée dans une instance AWS EC2 à une adresse temporaire à http://ec2-18-134-16-118.eu-west-2.compute.amazonaws.com:8056/

    Voici ci-dessous quelques écrans capturés depuis mon mobile. L'interface utilisateur de démonstration est très simple : en gros, je clique sur "Choose File" puis sur le bouton "Submit" pour télécharger une image radiographique, puis l'application affiche un rapport de classification. S'il s'agit d'une radiographie Covid-19, une [carte thermique sera affichée] (https://community.intersystems.com/post/explainability-and-visibility-covid-19-x-ray-classifiers-deep-learning) pour reproduire la zone de lésion "détectée" par DL ; sinon, le rapport de classification ne montrera que l'image radiographique téléchargée.

            

    L'application web est une page serveur Python dont la logique est principalement codée dans le fichier main.py de FastAPI, ainsi que dans le fichier app.py de Flask.

    Quand j'aurai un peu plus de temps libre, je pourrais documenter en détail les différences de codage et de convention entre Flask et FastAPI. En fait, j'espère pouvoir faire un hébergement de démonstration Flask vs FastAPI vs IRIS pour l'IA. 

    2. Test des API de démonstration      

    FastAPI (exposé au port 8056) a intégré des documents Swagger API, comme indiqué ci-dessous. C'est très pratique. Tout ce que j'ai à faire est d'utiliser "/docs" dans son URL, par exemple : 

    J'ai intégré quelques paramètres (tels que /hello et /items) et quelques interfaces API de démonstration (telles que /healthcheck, /predict, et predict/heatmap).

    Testons rapidement ces API, en exécutant quelques lignes Python (en tant qu'émulateur d'application client API) dans l'un des [fichiers d'échantillons de Jupyter Notebook que j'ai créés] (https://github.com/zhongli1990/covid-ai-demo-deployment/tree/master/notebooks/notebooks) pour ce service de démonstration de l'IA.  

    Ci-dessous, j'exécute ce fichier à titre d'exemple : https://github.com/zhongli1990/covid-ai-demo-deployment/blob/master/notebooks/notebooks/Covid19-3class-Heatmap-Flask-FastAPI-TF-serving-all-in-one-HAProxy2.ipynb

    Tout d'abord pour tester que le backend TF-Serving (port 8511) et TF-Serving-GPU (port 8521) sont en place et fonctionnent : 

    !curl http://172.17.0.1:8511/v1/models/covid19  # servant tensorflow
    !curl http://172.17.0.1:8521/v1/models/covid19  # servant tensorflow-gpu
    {
     "model_version_status": [
      {
       "version": "2",
       "state": "AVAILABLE",
       "status": {
        "error_code": "OK",
        "error_message": ""
       }
      }
     ]
    }
    {
     "model_version_status": [
      {
       "version": "2",
       "state": "AVAILABLE",
       "status": {
        "error_code": "OK",
        "error_message": ""
       }
      }
     ]
    }

     

    Ensuite, vérifiez que les services API suivants sont en place et fonctionnent :

  • Gunicorn+Flask+TF-Serving
  • Unicorn+FastAPI+TF-Serving-GPU
  • Equilibreur de charge HAProxy en face des services gênants ci-dessus
  • r = requests.get('http://172.17.0.1:8051/covid19/api/v1/healthcheck')  # tf servant le docker avec le cpu
    print(r.status_code, r.text)
    r = requests.get('http://172.17.0.1:8056/covid19/api/v1/healthcheck')  # tf-servant le docker avec le gpu
    print(r.status_code, r.text)
    r = requests.get('http://172.17.0.1:8088/covid19/api/v1/healthcheck')  # tf-servant le docker avec le HAproxy
    print(r.status_code, r.text)

    Et les résultats attendus seraient :

    200 L'API du détecteur Covid19 est en ligne !
    200 "L'API du détecteur Covid19 est en ligne !\n\n"
    200 "L'API du détecteur Covid19 est en ligne !\n\n"

     

    Tester une interface API fonctionnelle, telle que **/predict/heatmap ** pour renvoyer le résultat de la classification et de la heatmap d'une image radiographique d'entrée. L'image entrante est codée en based64 avant d'être envoyée via HTTP POST conformément aux définitions de l'API :

    %%time# Importation de la bibliothèque des requêtes
    import argparse
    import base64import requests# définition d'un point d'entrée ap pour api
    API_ENDPOINT = "http://172.17.0.1:8051/covid19/api/v1/predict/heatmap"image_path = './Covid_M/all/test/covid/nejmoa2001191_f3-PA.jpeg'
    #image_path = './Covid_M/all/test/normal/NORMAL2-IM-1400-0001.jpeg'
    #image_path = './Covid_M/all/test/pneumonia_bac/person1940_bacteria_4859.jpeg'
    b64_image = ""
    # Encoding the JPG,PNG,etc. image to base64 format
    with open(image_path, "rb") as imageFile:
        b64_image = base64.b64encode(imageFile.read())# données à envoyer à l'api
    data = {'b64': b64_image}# envoi d'une requête post et enregistrement de la réponse en tant qu'objet réponse
    r = requests.post(url=API_ENDPOINT, data=data)print(r.status_code, r.text)# extraction de la réponse
    print("{}".format(r.text))

    Toutes ces [images de test ont également été téléchargées sur GitHub] (https://github.com/zhongli1990/Covid19-X-Rays/tree/master/all/test). Le résultat du code ci-dessus sera comme ça:

    200 {"Input_Image":"http://localhost:8051/static/source/0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png","Output_Heatmap":"http://localhost:8051/static/result/Covid19_98_0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png.png","X-Ray_Classfication_Raw_Result":[[0.805902302,0.15601939,0.038078323]],"X-Ray_Classification_Covid19_Probability":0.98,"X-Ray_Classification_Result":"Covid-19 POSITIVE","model_name":"Customised Incpetion V3"}
    
    {"Input_Image":"http://localhost:8051/static/source/0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png","Output_Heatmap":"http://localhost:8051/static/result/Covid19_98_0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png.png","X-Ray_Classfication_Raw_Result":[[0.805902302,0.15601939,0.038078323]],"X-Ray_Classification_Covid19_Probability":0.98,"X-Ray_Classification_Result":"Covid-19 POSITIVE","model_name":"Customised Incpetion V3"}
    
    CPU times: user 16 ms, sys: 0 ns, total: 16 ms
    Wall time: 946 ms

     

    3. Les applications de démonstration de services pour les tests benchmarkés

    Nous avons mis en place une instance d'équilibreur de charge HAProxy. Nous avons également démarré un service Flask avec 4 travailleurs, et un service FastAPI avec 4 travailleurs également.

    Pourquoi ne pas créer 8x processus Pyhon directement dans le fichier Notebook, pour émuler 8x clients API simultanés envoyant des requêtes dans les API du service de démonstration, pour voir ce qui se passe ? 

    #from concurrent.futures import ThreadPoolExecutor as PoolExecutor
    from concurrent.futures import ProcessPoolExecutor as PoolExecutor
    import http.client
    import socket
    import timestart = time.time()#laodbalancer:
    API_ENDPOINT_LB = "http://172.17.0.1:8088/covid19/api/v1/predict/heatmap"
    API_ENDPOINT_FLASK = "http://172.17.0.1:8052/covid19/api/v1/predict/heatmap"
    API_ENDPOINT_FastAPI = "http://172.17.0.1:8057/covid19/api/v1/predict/heatmap"
    def get_it(url):
        try:
            # boucle sur les images
            for imagePathTest in imagePathsTest:
                b64_image = ""
                with open(imagePathTest, "rb") as imageFile:
                    b64_image = base64.b64encode(imageFile.read())
    
                data = {'b64': b64_image}
                r = requests.post(url, data=data)
                #print(imagePathTest, r.status_code, r.text)
            return r
        except socket.timeout:
            # dans un scénario du monde réel, vous feriez probablement quelque chose si le
            # socket passe en timeout
            passurls = [API_ENDPOINT_LB, API_ENDPOINT_LB,
            API_ENDPOINT_LB, API_ENDPOINT_LB,
            API_ENDPOINT_LB, API_ENDPOINT_LB,
            API_ENDPOINT_LB, API_ENDPOINT_LB]with PoolExecutor(max_workers=16) as executor:
        for _ in executor.map(get_it, urls):
            pass
    
    print("--- %s seconds ---" % (time.time() - start))

    Il a donc fallu 74 secondes pour traiter 8x27 = 216 images de test. Cette pile de démonstration à charge équilibrée était capable de traiter 3 images par seconde (en renvoyant les résultats de la classification et de la heatmap aux clients) :

    --- 74.37691688537598 seconds ---

    À partir de la commande Top de la session Putty, nous pouvons voir que 8x processus de serveur (4x gunicorn + 4 unicorn/python) ont commencé à monter en puissance dès que les scripts de référence ci-dessus ont commencé à être exécutés.

    Suivant

    Cet article n'est qu'un point de départ pour mettre en place une pile de déploiement "All-in-Docker AI demo" comme cadre de test. J'espère ensuite ajouter d'autres interfaces de démonstration API, telles que l'interface de prédiction des soins intensifs Covid-19, idéalement conforme à la norme FHIR R4, etc. Cela pourrait également être un banc d'essai pour explorer une intégration plus étroite avec les capacités ML hébergées par IRIS. Au fil du temps, il peut être utilisé comme un cadre de test (et un cadre assez simple) pour intercepter de plus en plus de modèles ML ou DL spécialisés au fur et à mesure que nous avançons sur divers aspects de l'IA, notamment l'imagerie médicale, la santé de la population ou la prédiction personnalisée, le traitement automatique des langues, etc. J'ai également dressé une liste de souhaits à la toute fin de l'article précédent (dans sa section "Next" (Suivant))

    0
    0 314
    Article Guillaume Rongier · Mars 16, 2022 9m read

    1. Démonstration d'IntegratedML

    Ce dépôt est une démonstration d'IntegratedML et d'Embedded Python.

    SideBySidePythonAndCos

    2. Construction de la démo

    Pour construire la démo, il suffit d'exécuter la commande :

    docker compose up
    

    2.1. Architecture

    Deux conteneurs seront construits : un avec IRIS et un avec un serveur nginx.

    containers

    L'image IRIS utilisée contient Python embarqué. Après la construction, le conteneur exécutera un serveur wsgi avec l'API Flask.

    Nous utilisons le paquet communautaire csvgen pour importer le jeu de données titanic dans iris. Pour le jeu de données noshow, nous utilisons une autre méthode personnalisée (la classmethod Load() de la classe Util.Loader). Pour que le conteneur ait accès aux fichiers csv, nous lions le répertoire local iris/ au répertoire /opt/irisapp/ dans le conteneur.

    2.2. Construction du conteneur nginx

    Afin de construire notre conteneur nginx, docker utilise une construction en plusieurs étapes. Tout d'abord, il crée un conteneur avec node. Il installe ensuite npm et copie tous nos fichiers dans ce conteneur. Il construit le projet avec la commande ng build, et le fichier de sortie est copié dans un nouveau conteneur qui ne contient que nginx.

    Grâce à cette manœuvre, nous obtenons un conteneur très léger qui ne contient pas toutes les bibliothèques et outils nécessaires à la construction de la page Web.

    Vous pouvez vérifier les détails de ce multi-build dans le fichier angular/Dockerfile. Nous avons également configuré les paramètres de notre serveur nginx grâce au fichier angular/nginx.default.conf.

    3. Exécution de la démo

    Il suffit d'aller à l'adresse : http://localhost:8080/ et c'est tout ! Profitez-en !

    4. Backend Python

    Le back-end est réalisé avec Python Flask. Nous utilisons Python embarqué afin d'appeler les classes iris et d'exécuter des requêtes depuis Python.

    4.1. Python embarqué

    4.1.1. Configuration du conteneur

    Dans le dockerfile, nous devons d'abord expliciter deux variables d'environnement que Python embarqué utilisera :

    ENV IRISUSERNAME "SuperUser"
    ENV IRISPASSWORD $IRIS_PASSWORD
    

    Avec $IRIS_PASSWORD configuré comme ceci dans le fichier docker-compose :

    iris:
      build:
        args:
          - IRIS_PASSWORD=${IRIS_PASSWORD:-SYS}
    

    (Le mot de passe transféré est celui configuré sur votre machine locale ou -s'il n'est pas configuré- sera par défaut « SYS »)

    4.1.2. Utiliser Python embarqué

    Afin d'utiliser Python embarqué, nous utilisons irispython comme intercepteur de python, et faisons :

    import iris
    

    Tout au début du fichier.

    Nous serons alors en mesure d'exécuter des méthodes telles que :

    flaskExample

    Comme vous pouvez le voir, pour OBTENIR un passager avec un ID, nous exécutons simplement une requête et utilisons son jeu de résultats.

    Nous pouvons également utiliser directement les objets IRIS :

    flaskObjectExample

    Ici, nous utilisons une requête SQL pour obtenir tous les ID du tableau, puis nous récupérons chaque passager du tableau avec la méthode %OpenId() de la classe Titanic.Table.Passenger (notez que % étant un caractère illégal en Python, nous utilisons _ à la place).

    Grâce à Flask, nous implémentons toutes nos routes et méthodes de cette façon.

    4.1.3. Comparaison côte à côte

    Sur cette capture d'écran, vous avez une comparaison côte à côte entre une implémentation Flask et une implémentation ObjectScript.

    Comme vous pouvez le voir, il y a beaucoup de similitudes.

    SideBySidePythonAndCos

    4.2. Démarrage du serveur

    Pour lancer le serveur, nous utilisons gunicorn avec irispython.

    Dans le fichier docker-compose, nous ajoutons la ligne suivante :

    iris:
      command: -a "sh /opt/irisapp/flask_server_start.sh"
    

    Cela lancera, après le démarrage du conteneur (grâce à l'indicateur -a), le script suivant :

    #!/bin/bash
    
    cd ${FLASK_PATH}
    
    ${PYTHON_PATH} /usr/irissys/bin/gunicorn --bind "0.0.0.0:8080" wsgi:app -w 4 2>&1
    
    exit 1
    

    Avec les variables d'environnement définies dans le dockerfile comme suit :

    ENV PYTHON_PATH=/usr/irissys/bin/irispython
    ENV FLASK_PATH=/opt/irisapp/python/flask
    

    Nous aurons alors accès au back-end Flask via le port local 4040, puisque nous y avons lié le port 8080 du conteneur.

    5. IntegratedML

    5.1. Explorer les deux ensembles de données

    Pour les deux ensembles de données, vous aurez accès à un CRUD complet, vous permettant de modifier à volonté les tableaux enregistrés.

    Afin de passer d'un ensemble de données à l'autre, vous pouvez appuyer sur le bouton en haut à droite.

    5.2. Managing models

    5.2.1. Création d’un modèle

    Une fois que vous avez découvert les données, vous pouvez créer un modèle prédisant la valeur que vous souhaitez.

    En cliquant dans le menu de navigation latéral Gestionnaire de modèles, dans la liste des modèles, vous aurez accès à la page suivante (ici dans le cas de l'ensemble de données NoShow) :

    modelList

    Vous pouvez choisir la valeur que vous voulez prédire, le nom de votre modèle, et avec quelles variables vous voulez prédire.

    Dans le menu latéral, vous pouvez activer Voir les requêtes SQL ? pour voir comment les modèles sont gérés dans IRIS.

    Après avoir créé un modèle, vous devriez voir ceci :

    modelCreated

    Comme vous pouvez le voir, la création d'un modèle ne prend qu'une seule requête SQL. Les informations que vous avez sont toutes les informations que vous pouvez récupérer d'IRIS.

    Dans la colonne actions, vous pouvez supprimer un modèle ou le purger. La purge d'un modèle supprimera tous ses cycles de formation (et leurs cycles de validation), à l'exception du dernier.

    5.2.2. Entraînement d'u modèle

    Dans l'onglet suivant, vous pourrez entraîner vos modèles.

    Vous avez le choix entre 3 fournisseurs. AutoML d'InterSystems, H2O, une solution open-source, et DataRobot, dont vous pouvez avoir un essai gratuit de 14 jours si vous vous enregistrez sur leur site.

    Vous pouvez sélectionner le pourcentage de l'ensemble de données que vous souhaitez utiliser pour entraîner votre modèle. Étant donné que l'entraînement de grands ensembles de données peut prendre beaucoup de temps, il est possible, pour les besoins des démonstrations, de prendre un ensemble de données plus petit.

    Ici, nous avons entraîné un modèle en utilisant l'ensemble des données Titanic :

    modelTrained

    Le bouton dans la colonne actions vous permettra de voir le log. Pour l'AutoML, vous verrez ce que l'algorithme a réellement fait : comment il a préparé les données et comment il a choisi le modèle à utiliser.

    L'entraînement d'un modèle ne nécessite qu'une seule requête SQL, comme vous pouvez le voir dans la section des messages du menu de navigation latéral.

    Gardez à l'esprit que dans ces deux onglets, vous ne verrez que les modèles qui concernent l'ensemble de données que vous utilisez réellement.

    5.2.3. Validation d'un modèle

    Enfin, vous pouvez valider un modèle dans le dernier onglet. En cliquant sur une exécution de validation, une boîte de dialogue s'ouvre avec les métriques associées à la validation. Là encore, vous pouvez choisir un pourcentage de l'ensemble de données à utiliser pour la validation.

    modelValidated

    Une fois de plus, il suffit d'une seule requête SQL.

    5.2.4. Effectuer des prédictions

    Dans le menu Faire des prédictions, dernier onglet, vous pouvez faire des prédictions en utilisant vos modèles nouvellement formés.

    Il vous suffit de rechercher un passager / patient et de le sélectionner, de choisir l'un des modèles entraînés et d'appuyer sur prédire.

    Dans le cas d'un modèle de classification (comme dans cet exemple, pour prédire la survie), la prédiction sera associée à la probabilité d'être dans la classe prédite.

    Dans le cas de Mme Fatima Masselmani, le modèle a correctement prédit qu'elle a survécu, avec une probabilité de 73 %. Juste en dessous de cette prédiction, vous pouvez voir les données utilisées par le modèle :

    prédiction

    Une fois encore, il faut une requête pour obtenir la prédiction et une pour la probabilité.

    6. Utilisation de COS

    La démonstration fournit en fait deux API. Nous utilisons l'API Flask avec Python embarqué, mais un service REST dans COS a également été configuré lors de la construction du conteneur.

    En appuyant sur le bouton en haut à droite « Passer à l'API COS », vous pourrez utiliser ce service.

    Remarquez que rien ne change. Les deux API sont équivalentes et fonctionnent de la même manière.

    7. Plus d'explicabilité avec DataRobot

    Si vous voulez plus d'explicabilité (plus que ce que le journal peut vous offrir), nous vous suggérons d'utiliser le fournisseur DataRobot.

    Pour cela, vous devez vous rendre à l'adresse de votre instance DataRobot, et chercher les outils de développement pour obtenir votre jeton. Lors de l'entraînement de votre modèle, la page Web vous demandera votre jeton.

    Une fois l'entraînement commencé, vous pouvez accéder à votre instance DataRobot pour en savoir beaucoup plus sur votre ensemble de données et vos modèles :

    DRdata

    Ici, nous pouvons voir que les champs sexe et nom de chaque passager sont les valeurs les plus importantes pour prédire la survie. Nous pouvons également voir que le champ tarif contient des valeurs aberrantes.

    Une fois les modèles entraînés, vous pouvez avoir accès à de nombreux détails, en voici un aperçu :

    DRmodelDetails

    8. Conclusion

    Grâce à cette démonstration, nous avons pu voir à quel point il était facile de créer, d'entraîner et de valider un modèle ainsi que de prédire des valeurs à l'aide de très peu de requêtes SQL.

    Nous avons fait cela en utilisant une API RESTful avec Python Flask, en utilisant Python embarqué, et nous avons fait une comparaison avec une API COS.

    Le front-end a été réalisé avec Angular.

    9. Remerciements

    À Théophile, le stagiaire qui a construit cette belle démo pendant l'été 2021.

    0
    0 838