Article Sylvain Guilbaud · Août 24, 2023 9m read

À titre d'exemple d'application en Java fonctionnant avec le dialecte Hibernate pour IRIS, je souhaitais utiliser l'application RealWorld et j'ai trouvé une réalisation pour Quarkus. L'application RealWorld est un exemple d'application proche d'une application réelle, avec des tests déjà préparés pour le backend. La plupart des exemples de réalisations sont à retrouver ici

RealWorld Example App

L'exemple d'application RealWorld est souvent appelé « Wikipédia pour la création d'applications full-stack ». Il sert de prototype standardisé que les développeurs peuvent utiliser pour créer des applications à l'aide de divers langages et frameworks de programmation. L'application fournit un cas d'utilisation réel en imitant une plate-forme de blogs, avec des fonctionnalités telles que l'authentification des utilisateurs, la gestion des profils, la publication d'articles et les commentaires. Avec un ensemble complet de spécifications, y compris une documentation d'API backend prête à l'emploi et des conceptions frontend, il permet aux développeurs de voir comment les mêmes exigences fonctionnelles sont mises en œuvre dans différentes piles technologiques. L'exemple RealWorld est largement utilisé comme outil d'apprentissage et comme référence pour comparer diverses technologies.

Quarkus

Quarkus est un framework Java open source natif de Kubernetes, conçu pour GraalVM et HotSpot. Créé dans le but d'améliorer l'environnement cloud natif moderne, il réduit considérablement l'empreinte et le temps de démarrage des applications Java. Quarkus est connu pour sa philosophie « privilégiant le conteneur », permettant aux développeurs de créer des applications légères et performantes en mettant l'accent sur l'architecture des microservices. Cette flexibilité en a fait un choix populaire pour les organisations cherchant à passer à des plates-formes sans serveur ou basées sur le cloud, combinant des modèles de programmation impératifs et réactifs. Qu'il s'agisse d'une application Web traditionnelle ou d'un système complexe de microservices, Quarkus fournit une plate-forme robuste pour créer des logiciels évolutifs et maintenables.

0
0 70
Question Sylvain Guilbaud · Août 23, 2023

Est-il prévu que LOAD DATA prenne en compte plusieurs formats de DATE/DATETIME avec, par exemple un paramètre de indiquant le format utilisé dans les données sources ?

exemple :

LOAD DATA .../...
USING
{
  "from": {
    "file": {
       "dateformat": "DD/MM/YYYY"
    }
  }
}
3
0 88
InterSystems officiel Sylvain Guilbaud · Août 16, 2023

Lorsque IRIS 2023.2 atteindra la disponibilité générale, nous apporterons quelques améliorations à la façon dont nous étiquetons et distribuons les conteneurs IRIS et IRIS for Health.

Les conteneurs IRIS ont été étiquetés en utilisant le format du numéro de build complet, par exemple 2023.1.0.235.1. Les clients ont demandé des balises plus stables, afin qu'ils n'aient pas besoin de modifier leurs fichiers dockerfiles/Kubernetes à chaque nouvelle version. Dans cet esprit, nous apportons les modifications suivantes à la façon dont nous étiquetons les images de conteneurs.

0
0 56
Article Sylvain Guilbaud · Août 10, 2023 1m read

Rubrique FAQ InterSystems

Vous pouvez définir des pages d'erreur individuelles en fonction des différents types de messages d'erreur ou de réponses système de la passerelle Web :

  • erreur de serveur 
  • serveur occupé
  • serveur indisponible
  • délai d'attente du serveur
  • connexion fermée

Les paramètres sont définis sur l'écran Gestion de passerelle Web ([Portail d'Administration] > [Administration système] > [Configuration] > [Gestion de passerelle Web] > [Configuration] > [Default Parameters]).

Dans la section "Error Pages" du menu "Default Parameters", indiquez le nom de fichier de la page html à afficher ou l'URL vers laquelle rediriger la réponse lorsqu'une erreur se produit.

  

1
0 75
InterSystems officiel Sylvain Guilbaud · Août 2, 2023

InterSystems a corrigé deux défauts.

Le premier défaut peut entraîner le blocage d'un serveur d'applications ECP. Ce défaut ne concerne que les processeurs ARM et IBM Power ; il est présent dans les versions 2022.1.2 et 2022.1.3 d'InterSystems IRIS®, InterSystems IRIS for Health™ et HealthShare® Health Connect. La correction est identifiée comme DP-423661. Le défaut est corrigé dans toutes les versions futures.

0
0 68
Article Sylvain Guilbaud · Juil 17, 2023 11m read

Le besoin de créer des services REST permettant d'accéder aux informations présentes dans IRIS / HealthConnect est l'un des besoins les plus courants de nos clients. L'avantage de ces services REST est la possibilité de développer des interfaces utilisateurs personnalisées avec les technologies les plus récentes en profitant de la fiabilité et de la performance d'IRIS dans le back-end.

Dans l'article d'aujourd'hui, nous allons créer pas à pas un service web qui nous permettra à la fois de stocker des données dans notre base de données et de les consulter ultérieurement. De plus, nous allons le faire en configurant une production qui nous permettra de contrôler le flux de messages que nous recevons à tout moment et de contrôler ainsi son bon fonctionnement.

Avant de commencer, sachez simplement que vous avez tout le code disponible sur Open Exchange pour que vous puissiez le répliquer autant de fois que nécessaire, et que le projet est configuré dans Docker, en utilisant la communauté IRIS afin que vous n'ayez rien d'autre à faire que de le déployer.

Bien! Commençons donc!

Préparation de l'environnement

Avant de commencer la configuration du service REST, nous devons préparer notre environnement de développement, savoir quelles informations nous allons recevoir et ce que nous allons en faire. Pour cet exemple, nous avons décidé de recevoir des données de la personne dans le format JSON suivant :

{
    "PersonId": 1,
    "Name": "Irene",
    "LastName": "Dukas",
    "Sex": "Female",
    "Dob": "01/04/1975"
}

Comme l'un des objectifs est de stocker les informations que nous recevons, nous allons créer une classe Objectscript qui nous permettra d'enregistrer les informations dans notre IRIS. Comme vous pouvez le constater, les données sont assez simples, donc la classe n'aura pas beaucoup de complications :

Class WSTEST.Object.Person Extends%Persistent
{

/// ID of the personProperty PersonId As%Integer;/// Name of the personProperty Name As%String;/// Lastname of the personProperty LastName As%String;/// Sex of the personProperty Sex As%String;/// DOB of the personProperty Dob As%String;
Index PersonIDX On PersonId [ PrimaryKey ];
}

Parfait, nous avons déjà défini notre classe et nous pouvons commencer à nous en servir.

Création de notre point de terminaison

Maintenant que nous avons défini la classe de données avec laquelle nous allons travailler, il est temps de créer notre classe Objectscript qui fonctionnera comme un point de terminaison qui sera appelé depuis notre front-end. Voyons étape par étape la classe d'exemple que nous avons dans notre projet :

Class WSTEST.Endpoint Extends%CSP.REST
{

Parameter HandleCorsRequest = 0; XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ] { <Routes> <Route Url="/testGet/:pid" Method="GET" Call="TestGet" /> <Route Url="/testPost" Method="POST" Call="TestPost" /> </Routes> }

ClassMethod OnHandleCorsRequest(url As%String) As%Status { set url = %request.GetCgiEnv("HTTP_REFERER") set origin = $p(url,"/",1,3) // origin = "http(s)://origin.com:port"// here you can check specific origins// otherway, it will allow all origins (useful while developing only)do%response.SetHeader("Access-Control-Allow-Credentials","true") do%response.SetHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS") do%response.SetHeader("Access-Control-Allow-Origin",origin) do%response.SetHeader("Access-Control-Allow-Headers","Access-Control-Allow-Origin, Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control") quit$$$OK } // Class method to retrieve the data of a person filtered by PersonIdClassMethod TestGet(pid As%Integer) As%Status { Try { Do##class(%REST.Impl).%SetContentType("application/json") If '##class(%REST.Impl).%CheckAccepts("application/json") Do##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit// Creation of BS instanceset status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSearchBS", .instance)

    <span class="hljs-comment">// Invocation of BS with pid parameter</span>
    <span class="hljs-keyword">set</span> status = instance.OnProcessInput(pid, .response)
   	<span class="hljs-keyword">if</span> <span class="hljs-built_in">$ISOBJECT</span>(response) {
        <span class="hljs-comment">// Sending person data to client in JSON format</span>
    	<span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>(response.<span class="hljs-built_in">%JSONExport</span>())
	}
    
} <span class="hljs-keyword">Catch</span> (ex) {
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%SetStatusCode</span>(<span class="hljs-string">"400"</span>)
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>(ex.DisplayString())
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"errormessage"</span>: <span class="hljs-string">"Client error"</span>}
}
<span class="hljs-keyword">Quit</span> <span class="hljs-built_in">$$$OK</span>

} // Class method to receive person data to persist in our databaseClassMethod TestPost() As%Status { Try { Do##class(%REST.Impl).%SetContentType("application/json") If '##class(%REST.Impl).%CheckAccepts("application/json") Do##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit// Reading the body of the http call with the person dataset bodyJson = %request.Content.Read()

    <span class="hljs-comment">// Creation of BS instance</span>
    <span class="hljs-keyword">set</span> status = <span class="hljs-keyword">##class</span>(Ens.Director).CreateBusinessService(<span class="hljs-string">"WSTEST.BS.PersonSaveBS"</span>, .instance)
   	<span class="hljs-keyword">#dim</span> response <span class="hljs-keyword">as</span> <span class="hljs-built_in">%DynamicObject</span>
    <span class="hljs-comment">// Invocation of BS with person data</span>
    <span class="hljs-keyword">set</span> status = instance.OnProcessInput(bodyJson, .response)
    
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">$ISOBJECT</span>(response) {
        <span class="hljs-comment">// Returning to the client the person object in JSON format after save it</span>
        <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>(response.<span class="hljs-built_in">%JSONExport</span>())
    }
    
} <span class="hljs-keyword">Catch</span> (ex) {
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%SetStatusCode</span>(<span class="hljs-string">"400"</span>)
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>(ex.DisplayString())
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"errormessage"</span>: <span class="hljs-string">"Client error"</span>}
}
<span class="hljs-keyword">Quit</span> <span class="hljs-built_in">$$$OK</span>

}

}

Ne vous inquiétez pas si cela vous semble impossible à comprendre, voyons les parties les plus pertinentes de notre cours :

Déclaration de classe :

Class WSTEST.Endpoint Extends%CSP.REST

Comme vous pouvez le voir, notre classe WSTEST.Endpoint élargit %CSP.REST, ce qui est nécessaire pour pouvoir utiliser la classe en tant que point de terminaison.

Définition des chemins :

XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ]
{
<Routes>
	<Route Url="/testGet/:pid" Method="GET" Call="TestGet" />
	<Route Url="/testPost" Method="POST" Call="TestPost" />
</Routes>
}

Dans cet extrait de code, nous déclarons les chemins qui peuvent être appelés à partir de notre front-end.

Comme vous voyez, nous avons deux chemins déclarés, le premier sera un appel GET dans lequel nous recevrons le paramètre pid que nous utiliserons pour rechercher des personnes par leur identifiant et qui sera géré par la méthode de classe TestGet. Le second appel sera de type POST, dans lequel nous recevrons les informations sur la personne que nous devons enregistrer dans notre base de données et qui seront traitées par la méthode de classe TestPost.

Examinons les deux méthodes :

Récupération des données de la personne :

ClassMethod TestGet(pid As%Integer) As%Status
{
    Try {
        Do##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit// Creation of BS instanceset status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSearchBS", .instance)

        // Invocation of BS with pid parameterset status = instance.OnProcessInput(pid, .response)
       	if$ISOBJECT(response) {
            // Sending person data to client in JSON formatDo##class(%REST.Impl).%WriteResponse(response.%JSONExport())
		}
        
    } Catch (ex) {
        Do##class(%REST.Impl).%SetStatusCode("400")
        Do##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit$$$OK
}

Dans cette méthode, vous pouvez voir comment nous avons déclaré dans notre méthode de classe la réception de l'attribut pid que nous utiliserons dans la recherche ultérieure. Bien que nous aurions pu effectuer la recherche directement à partir de cette classe, nous avons décidé de le faire à l'intérieur d'une production pour pouvoir contrôler chacune des opérations, c'est pourquoi nous créons une instance du service métier Business Service WSTEST.BS.PersonSearchBS à laquelle nous appelons plus tard sa méthode OnProcessInput avec le pid reçu. La réponse que nous recevrons sera de type WSTEST.Object.PersonSearchResponse que nous transformerons en JSON avant de l'envoyer au requérant.

Conservation des données de la personne :

ClassMethod TestPost() As%Status
{
    Try {
        Do##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit// Reading the body of the http call with the person dataset bodyJson = %request.Content.Read()
        
        // Creation of BS instanceset status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSaveBS", .instance)
       	#dim response as%DynamicObject// Invocation of BS with person dataset status = instance.OnProcessInput(bodyJson, .response)
        
        if$ISOBJECT(response) {
            // Returning to the client the person object in JSON format after save itDo##class(%REST.Impl).%WriteResponse(response.%JSONExport())
	    }
        
    } Catch (ex) {
        Do##class(%REST.Impl).%SetStatusCode("400")
        Do##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit$$$OK
}

Comme dans le cas précédent, nous aurions pu sauvegarder notre objet personne directement à partir de cette classe, mais nous avons décidé de le faire à partir d'une opération métier "Business Operation" qui sera appelée à partir du service métier "Business Service" WSTEST.BS.PersonSaveBS.

Comme vous pouvez le voir dans le code, nous récupérons les informations envoyées par le client dans l'appel POST en lisant le flux figurant dans %request.Content. La chaîne obtenue sera ce que nous transmettrons au Business Service..

Publication de notre point de terminaison

Pour ne pas allonger cet article, nous allons ignorer les explications relatives à la production, vous pouvez consulter le code directement dans le projet OpenExchange. La production que nous avons configurée est la suivante :

Nous avons deux services commerciaux déclarés, l'un pour recevoir les demandes de recherche et l'autre pour les demandes de stockage des données de la personne. Chacun d'entre eux invoquera une opération métier Business Operation appropriée.

Très bien, révisons ce que nous avons configuré dans notre projet :

  • 1 classe de point de terminaison qui recevra les requêtes des clients (WSTest.Endpoint).
  • 2 services métier "Business Services" qui seront appelés à partir de notre classe de point de terminaison (**WSTest.BS.PersonSaveBS **et WSTest.BS.PersonSearchBS).
  • 2 opérations métier "Business Operations" chargées d'effectuer la recherche et l'enregistrement des données (**WSTest.BS.PersonSaveBS **et WSTest.BS.PersonSearchBS).
  • 4 classes pour envoyer et recevoir des données au sein de la production qui s'étendent de Ens.Request et Ens.Response (WSTest.Object.PersonSearchRequestWSTest.Object.PersonSaveRequest, **WSTest.Object.PersonSearchResponse **y WSTest.Object.PersonSaveResponse).

Il ne nous reste plus qu'une dernière étape pour mettre en service notre service web, c'est sa publication. Pour ce faire, nous allons accéder à l'option du portail de gestion Administration du système --> Sécurité -> Applications -> Applications Web

Nous verrons une liste de toutes les applications Web configurées dans notre instance :

Créons notre application web :

Examinons les points à configurer :

  • Nom : Nous allons définir la route à utiliser pour faire les invocations à notre service, pour notre exemple ce sera /csp/rest/wstest
  • Espace de noms : l'espace de noms sur lequel le service web travaillera, dans ce cas nous avons créé WSTEST dans lequel nous avons configuré notre production et nos classes.
  • Activer l'application : activer le service web pour qu'il puisse recevoir des appels.
  • Activer - REST : En sélectionnant REST, nous indiquons que ce service web est configuré pour recevoir des appels REST. En sélectionnant REST, nous devons définir la classe de distribution qui sera notre classe de terminaison WSTEST.Endpoint.
  • Méthodes d'authentification autorisées : la configuration de l'authentification de l'utilisateur qui fait l'appel au service web. Dans ce cas, nous avons défini que l'authentification se fait par mot de passe, donc dans notre Postman nous configurerons le mode d'autorisation de base dans lequel nous indiquerons le nom d'utilisateur et le mot de passe. Nous pouvons définir l'utilisation de l'authentification JWT qui est très utile car elle n'expose pas les données de l'utilisateur et le mot de passe dans les appels REST, si vous êtes intéressé à plonger dans JWT, vous pouvez consulter [cet article] (https://community.intersystems.com/post/creating-rest-api-jwt-authentication-objectscript).

Une fois que nous avons fini de configurer notre application web, nous pouvons lancer quelques tests au moyen de Postman et en important le fichier WSTest.postman_collection.json présent dans le projet.

Test de notre point de terminaison

Une fois que tout est configuré dans notre projet et que la production a démarré, nous pouvons lancer quelques tests sur notre service web. Nous avons configuré superuser en tant qu'utilisateur requérant, de sorte que nous n'aurons pas de problèmes pour sauvegarder et récupérer des données. Dans le cas de l'utilisation d'un autre utilisateur, nous devons nous assurer qu'il a les rôles nécessaires assignés ou que nous les assignons dans l'onglet Rôles de l'application de la définition de notre application web.

Commençons par enregistrer une personne dans notre base de données :

Nous avons obtenu un 200, il semble donc que tout se soit bien passé, vérifions le message en production :

Tout s'est bien passé, le message avec le JSON a été correctement reçu dans BS et a été enregistré avec succès dans BO.

Essayons maintenant de récupérer les données de notre cher Alexios Kommenos :

Bingo ! Voici notre Alexios, avec toutes les informations le concernant. Vérifions le message en production :

Très bien, tout fonctionne comme il faut. Comme vous l'avez vu, il est très facile de créer un service web REST dans IRIS, vous pouvez utiliser ce projet comme base pour vos futurs développements et si vous avez des questions, n'hésitez pas à laisser un commentaire.

0
0 176
Article Sylvain Guilbaud · Juil 10, 2023 13m read

Découvrir Django

Django est un framework web conçu pour développer des serveurs et des API, et pour traiter des bases de données de manière rapide, évolutive et sécurisée. Pour ce faire, Django fournit des outils permettant non seulement de créer le squelette du code, mais aussi de le mettre à jour sans souci. Il permet aux développeurs de voir les changements presque en direct, de corriger les erreurs avec l'outil de débogage et de traiter la sécurité avec facilité.

Pour comprendre le fonctionnement de Django, examinons l'image :

0
0 164
Article Sylvain Guilbaud · Juil 5, 2023 17m read

Ci-dessous figure une liste de certains des utilitaires disponibles dans InterSystems IRIS.

Cliquez sur le nom de chaque utilitaire dans le tableau ci-dessous pour obtenir des informations détaillées sur l'utilitaire.

<td>
  Aperçu
</td>
<td>
  Exécution d'opérations de sauvegarde et de restauration
</td>
<td>
  Gestion des bases de données, y compris la création, la modification et la suppression de bases de données
</td>
<td>
  Vérification de la concordance du contenu des deux fichiers IRIS.DAT
</td>
<td>
  Détermination de la taille des bases de données
</td>
<td>
  Capture des données globales d'une base de données vers une autre base de données ou un autre espace de noms
</td>
<td>
  Vérification des globales temporaires utilisées dans la base de données IRISTEMP
</td>
<td>
  Une simple comparaison du contenu des deux globales
</td>
<td>
  Vérification de la taille des données de chaque globale dans l'espace de noms
</td>
<td>
  Vérification de l'intégrité structurelle d'un ensemble de bases de données ou d'un sous-ensemble de globales dans une base de données
</td>
<td>
  Affichage d'une liste de processus. Des informations détaillées sur chaque processus peuvent également être affichées et arrêtées.
</td>
<td>
  Configuration de la journalisation
</td>
<td>
  Affichage et suppression des informations de verrouillage en cours d'utilisation
</td>
<td>
  Enregistrement continu des informations du compteur d'accès global dans un fichier en unités de cycles d'échantillonnage
</td>
<td>
  Exécution des opérations de configuration, de gestion et d'état de la mise en miroir
</td>
<td>
  Surveillance des journaux de messages (messages.log) afin de générer des notifications ou d'envoyer des courriels
</td>
<td>
  Exécution de la configuration et de la maintenance des données essentielles au bon fonctionnement de la sécurité d'IRIS.
</td>
<td>
  Affichage d'une liste de processus (référence uniquement).
</td>
<td>
  Diffusion des informations du journal des erreurs internes d'IRIS enregistrées dans une partie de la mémoire partagée.
</td>
<td>
  IRIS recueille des informations sur l'état des processus en cours d'utilisation, la mémoire partagée, etc.
</td>
<td>
  Collecte de données détaillées sur les performances d'IRIS et de la plate-forme sur laquelle l'instance fonctionne.
</td>
<td>
  Planification et gestion des tâches.
</td>
Nom de l'utilitaire
^BACKUP
^DATABASE
^DATACHECK
^DBSIZE
^GBLOCKCOPY
^GETPPGINFO
^%GCMP
^%GSIZE
^INTEGRIT
^JOBEXAM
^JOURNAL
^LOCKTAB
^mgstat
^MIRROR
^MONMGR
^SECURITY
^%SS
^SYSLOG
^SystemCheck
^SystemPerformance
^TASKMGR

※Tous les utilitaires doivent être exécutés dans l'espace de noms %SYS, à l'exception des utilitaires avec %.
※Portail de gestion : indique les fonctions cibles qui peuvent être exécutées à partir du portail de gestion.
※★En cliquant sur le nom de l'utilitaire, la description se trouve dans la documentation du produit (certains ne sont pas répertoriés).
 

★^BACKUP

Processus de sauvegarde et de restauration.
L'utilitaire ^BACKUP comporte les éléments de menu suivants.

%SYS>do^BACKUP) Backup                                        // Exécution des sauvegardes de la base de données2) Restore ALL                                   // Restauration de toutes les bases de données3) Restore Selected or Renamed Directories       // Restauration des répertoires sélectionnés ou renommés4) Edit/Display List of Directories for Backups  // Modification/affichage de la liste des répertoires5) Abort Backup                                  // Annulation de la sauvegarde6) Display Backup volume information             // Affichage des informations sur le volume de sauvegarde7) Monitor progress of backup or restore         // Contrôle de la progression de la sauvegarde ou de la restauration

Portail de gestion :
Fonctionnement du système > Sauvegarde

enlightened 【Pour référence】
Les sauvegardes de bases de données(InterSystems Symposia 2014)
La différence entre la sauvegarde accumulative et la sauvegarde différentielle.
Comment effectuer des sauvegardes sans arrêter une instance en cours d'exécution ?
Comment sauvegarder une base de données
 

★^DATABASE

La gestion des bases de données, y compris la création, la modification et la suppression des bases de données, s'effectue à l'aide de cette fonction. L'utilitaire ^DATABASE comporte les éléments de menu suivants

%SYS>do^DATABASE1) Create a database                    // Création d'une base de données2) Edit a database                      // Modification des attributs des bases de données existantes.3) List databases                       // Affichage de la liste de bases de données4) Delete a database                    // Suppression de la base de données existante5) Mount a database                     // Montage de la base de données6) Dismount a database                  // Démontage de la base de données.7) Compact globals in a database        // Compression des données globales de la base de données. Cette fonction est limitée aux versions disponibles (※).8) Show free space for a database       // Affichage de l'espace libre disponible dans la base de données9) Show details for a database          // Affichage d'informations détaillées sur la base de données spécifiée10) Recreate a database                  // Création d'une nouvelle base de données vide basée sur les paramètres d'une base de données existante11) Manage database encryption           // Gestion du cryptage de la base de données.12) Return unused space for a database   // Libération de l'espace inutilisé dans la base de données13) Compact free space in a database     // Compression de l'espace libre dans la base de données. Cette fonction est limitée aux versions disponibles (※).14) Defragment a database                // Défragmentation de la base de données. Cette fonction est limitée aux versions disponibles(※).15) Show background database tasks       // Affichage des tâches de base de donnéesde référence

Limitation:Veuillez noter que les fonctions de compression et de défragmentation peuvent ne pas être disponibles en fonction de votre version.
    Pour plus d'informations, voir.

Portail de gestion :
Administration du système > Configuration > Configuration du système > Base de données 

enlightened 【Pour référence】
Comment compresser le fichier de la base de données
Comment créer des espaces de noms et des bases de données à l'aide du terminal ou de l'API ?
Comment monter/démonter la base de données par programmation ?
 

★^DATACHECK

Vérification de la concordance du contenu des deux fichiers IRIS.DAT.

enlightened 【Pour référence】
Comment comparer plusieurs globales ou routines dans deux bases de données ?
 

★^DBSIZE

Mesure de la taille de la base de données.
Par exemple, elle est utilisé pour calculer l'espace disque nécessaire à une sauvegarde juste avant de l'effectuer. 

enlightened 【Pour référence】
Comment estimer la taille de la sauvegarde pour les sauvegardes en ligne
 

★^GBLOCKCOPY

Les données globales d'une base de données sont copiées dans une autre base de données ou un autre espace de noms.
Dans ce cas, la taille supplémentaire (la taille libérée par Kill) n'est pas copiée, ce qui permet de réduire la taille de la base de données.

enlightened 【Pour référence】
[FAQ] Comment réduire la taille du fichier de base de données IRIS.DAT ?
 

★^GETPPGINFO

Affichage des noms de tous les globales privés du processus en cours et l'espace qui leur est alloué en blocs.

enlightened 【Pour référence】
Comment identifier les globales temporaires qui consomment de l'espace dans la base de données IRISTEMP ?
Qu'est-ce que globale privé du processus
 

★^%GCMP

Comparaison du contenu pour voir s'il y a des différences entre les deux globales.

enlightened 【Pour référence】
Comment comparer le contenu de deux globales ?
 

★^%GSIZE

Vérification de la taille des données de chaque globale dans l'espace de noms.

Portail de gestion :
Administration du système > Configuration > Base de données locale > Globale

enlightened 【Pour référence】
Comment trouver la taille des données pour chaque globale ?
Signification de chaque élément sorti via ^%GSIZE
 

★^INTEGRIT

Vérification de l'intégrité structurelle d'un ensemble de bases de données ou d'un sous-ensemble de globales dans une base de données.

Portail de gestion :
Fonctionnement du système > Base de données > Contrôle d'intégrité
 

★^JOBEXAM

Affichage d'une liste de processus. Il est également possible d'afficher les détails de chaque processus et de l'arrêter.

Portail de gestion :
Fonctionnement du système > Processus
 

★^JOURNAL

Configuration de la journalisation.
L'utilitaire ^JOURNAL comporte les éléments de menu suivants

%SYS>do^JOURNAL1) Begin Journaling (^JRNSTART)             // Démarrage de journalisation2) Stop Journaling (^JRNSTOP)               // Arrêt de journalisation3) Switch Journal File (^JRNSWTCH)          // Commutation des fichiers du journal4) Restore Globals From Journal (^JRNRESTO) // Restauration des fichiers du journal5) Display Journal File (^JRNDUMP)          // Affichage des fichiers du journal6) Purge Journal Files (PURGE^JOURNAL)      // Suppression du fichier du journal7) Edit Journal Properties (^JRNOPTS)       // Modification des options du journal8) Activate or Deactivate Journal Encryption (ENCRYPT^JOURNAL())    // Activer ou désactiver le cryptage du journal9) Display Journal status (Status^JOURNAL)  // Affichage de l'état actuel du journal10) -not available-
11) -not available-
12) Journal catch-up for mirrored databases (MirrorCatchup^JRNRESTO) // Rattrapage du journal pour les bases de données mises en miroir13) -not available-

Portail de gestion :
Administration du système > Configuration > Configuration du système > Paramètres du journal
Fonctionnement du système > Journaux  

★^LOCKTAB

Affichage et suppression des informations de verrouillage actuellement utilisées.

Portail de gestion :
Fonctionnement du système > Verrouillage 

enlightened 【Pour référence】
Comment obtenir des informations sur le verrouillage au sein d'un programme
 

★^mgstat

Enregistrement continu des informations du compteur sur l'accès global à un fichier en unités de cycles d'échantillonnage (la valeur par défaut est de 2 secondes).
^mgstat est également exécuté lors de l'exécution de l'utilitaire ^SystemPerformance et fait partie du rapport de performance HTML.

enlightened 【Pour référence】
GUI basée sur Grafana pour mgstat (outil de surveillance du système InterSystems Caché / Ensemble / HealthShare).
 

★^MIRROR

Les fonctions de configuration, de gestion et d'état de la mise en miroir.

Portail de gestion :
Administration du système > Configuration > Paramètres du miroir

enlightened 【Pour référence】
Exemple de configuration HA et DR avec mise en miroir de la base de données
Performance 101
Synchroniqation et conditions de suppression des fichiers miroir du journal
À propos de la fonction de mise en miroir

★^MONMGR

Contrôle du journal des messages (messages.log) de l'instance IRIS afin de détecter les alertes signalées par le système et de générer des notifications (alert.log) ou d'envoyer des courriers électroniques.

enlightened 【Pour référence】
Comment faire pour que le journal des messages (messages.log) envoie un courriel avec une sévérité de 2 ou supérieure ?
 

★^SECURITY

Cette fonction permet de configurer et de maintenir les données essentielles au bon fonctionnement de la sécurité d'IRIS.
L'utilitaire ^SECURITE comporte les éléments de menu suivants.

%SYS>do^SECURITY1) User setup                     // Affichage, ajout et modification de la configuration utilisateur2) Role setup                     // Affichage, ajout et modification de la configuration des rôles3) Service setup                  // Affichage ou modification de la configuration du service4) Resource setup                 // Affichage, ajout et modification de la configuration des ressources.5) Application setup              // Affichage, ajout et modification des configurations de l'application.6) Auditing setup                 // Affichage et exportation des journaux d'audit8) SSL configuration setup        // Affichage, ajout et modification des configurations SSL9) Mobile phone service provider setup  // Affichage ou modification des configurations des fournisseurs de services de téléphonie mobile10) OpenAM Identity Services setup      // Configuration du service OpenAM ID11) Encryption key setup          // Configuration et gestion des fichiers de clés de cryptage des bases de données12) System parameter setup        // Navigation et modification des paramètres de sécurité de l'ensemble du système13) X509 User setup               // Affichage, ajout et modification des informations d'identification X.509 utilisées pour la sécurité des services web14) KMIP server setup             // Gestion et configuration des serveurs KMIP (le protocole d'interopérabilité de gestion des clés)15) Exit

Portail de gestion :
Administration du système > Sécurité

enlightened 【Pour référence】
Les applications CSP/REST ne se connectent pas. Comment faut-il procéder pour résoudre ce problème ?
Conseils sur l'exportation et l'importation des paramètres de sécurité
 

★^%SS

Liste des informations sur l'état de chaque processus actif dans le système actuel ( fonction de référence uniquement).
Pour obtenir des informations détaillées sur chaque processus ou pour mettre fin à un processus, utilisez ^JOBEXAM.

Portail de gestion :
Fonctionnement du système > Processus
 

★^SYSLOG

Sortie des informations du journal des erreurs internes d'IRIS qui sont enregistrées dans une partie de la mémoire partagée.
Ce journal peut contenir des informations de diagnostic importantes en cas de problèmes avec le système.

  1. La méthode d'exécution est la suivante
USER>zn"%SYS"%SYS>do^SYSLOG
Device:   // Appuyez sur "Enter" ou saisissez le chemin du fichier de sortie
Right margin: 80 =>   // Appuyez sur "Enter"
Show detail? No => Yes   // Appuyez sur "Yes" + <"Enter">
InterSystems IRIS System Error Log printed on May 192023 at 11:53 AM
--------------------------------------------------------
Printing the last 8 entries out of 8 total occurrences.
Err   Process    Date/Time           Mod Line  Routine            Namespace
3     30848      05/19/202309:01:02AM 911304 BF0+1373^SYS.Datab %SYS
:

※L'erreur ci-dessus est Err = 3, il s'agit donc d'une erreur du système d'exploitation "Le chemin d'accès spécifié est introuvable".
Err n'est pas nécessairement une erreur du système d'exploitation. Veuillez contacter notre centre d'assistance pour plus d'informations.

C:\>net helpmsg 3
Le chemin d'accès spécifié est introuvable.
  1. Les informations SYSLOG étant stockées dans la mémoire partagée, elles sont perdues lors de l'arrêt d'IRIS.
    En définissant les paramètres de configuration IRIS suivants, les informations du journal des erreurs internes sont enregistrées dans messages.log lorsque IRIS est arrêté.

    Portail de gestion :
    Administration du système > Configuration > Paramètres supplémentaires > Compatibilité 
       ShutDownLogErrors  Faux (par défaut) -> Vrai
     

enlightened 【Pour référence】
syslog - Ce que cela représente et ce que cela signifie
 

★^SystemCheck

IRIS recueille des informations sur l'état des processus en cours d'utilisation, la mémoire partagée, etc.
En cas de problème, il faut d'abord obtenir ces informations de diagnostic (^SystemCheck).
※Dans Caché/Ensemble, le nom de l'utilitaire était ^Buttons.

La méthode d'exécution est la suivante

%SYS>do^SystemCheck
Diagnostic Report Build # 087 Evidence Logging Tool

Cet outil de reporting fournit les informations nécessaires pour InterSystems
L'assistance technique est en mesure d'analyser la plupart des problèmes. Veuillez envoyer le fichier résultant avec
chaque nouveau problème envoyé à l'assistance technique.

Cette procédure prendra environ 5 minutes. Veuillez faire preuve de patience.

Continuer (Y)? y    // Appuyer sur Enter ou "oui"(y)
Signaler les informations spécifiques à l'interopérabilité ? [Non] n    //Appuyer sur Non (n)
Collecte d'informations, veuillez ne pas interrompre ce processus.

Veuillez attendre environ 30 secondes pour%SS les instantanés.
Veuillez attendre environ 1 minute pourles instantanés "irisstat".

Collecte des informations GloStat en cours.
Veuillez attendre environ 1 minute and 40 secondes.

Envoyez les fichiers suivants par FTP à ISC Support :
c:\intersystems\iris\mgr\***202208260909.html in text mode - 579,486 bytes
%SYS>
// \mg.*La partie *** dépendra de la licence utilisée

Si vous ne pouvez pas ouvrir le terminal parce que le système est bloqué ou pour une autre raison, exécutez IRISHung.cmd (IRISHung.sh pour les systèmes Linux) à partir de l'invite de commande Windows.
Après quelques minutes, le fichier IRISHung_mmss.html sera généré sous <IRIS répértoire d'installation>\mgr.

C:\>cd /intersystems/iris/bin    // \bin 
C:\InterSystems\IRIS\bin>IRISHung.cmd

Nom complet du répertoire InterSystems IRIS : C:\InterSystems\IRIS    // Saisissez le répertoire d'installation d'IRIS.
Écrire des informations à "C:\InterSystems\IRIS\Mgr\IRISHung_mmss.html"
Veuillez patienter...

Fichier du journal enregistré à :
"C:\InterSystems\IRIS\Mgr\IRISHung_mmss.html"
La taille du fichier est de ***** octets

C:\InterSystems\IRIS\bin>

enlightened 【Pour référence】
Guide de dépannage d'InterSystems IRIS - Collecte d'informations (^Comment utiliser SystemCheck/IRISHung)
※P.7 (1). Exécution de rapports de diagnostic (^SystemCheck).

[Comment collecter des informations lorsqu'un problème survient (IRIS / IRIS for Health / UCR eds.)

★^SystemPerformance

Collecte de données détaillées sur les performances des instances IRIS et des plates-formes sur lesquelles elles s'exécutent.
※Dans Caché/Ensemble, le nom de l'utilitaire était ^pButtons.

enlightened 【Pour référence】
InterSystems: plates-formes de données et performances - Partie 1
InterSystems: plates-formes de données et performances - Partie 2 
InterSystems: plates-formes de données et performances - Partie 3: accent sur la CPU

★^TASKMGR

Planification et gestion de tâches telles que la suppression des journaux et les sauvegardes automatiques.

Portail de gestion :
Fonctionnement du système > Tâches

enlightened 【Pour référence】
Méthode de notification par courrier électronique en cas d'erreur lors du démarrage d'une tâche

0
0 120
Article Sylvain Guilbaud · Juin 21, 2023 9m read

Aperçu général

Des performances prévisibles en matière d'E/S de stockage avec une faible latence sont essentielles pour assurer l'évolutivité et la fiabilité de vos applications. Cette série de benchmarks a pour but d'informer les utilisateurs d'IRIS qui envisagent de déployer des applications dans AWS sur les performances des volumes EBS gp3.

Résumé

  • Une bande LVM peut augmenter le nombre d'IOPS et le débit au-delà des limites de performance d'un volume EBS unique.
  • Une bande LVM réduit la latence de lecture.

**À lire en premier ** Une architecture de référence, comprenant une explication plus détaillée de la configuration de LVM dans AWS, est disponible ici : https://community.intersystems.com/post/intersystems-iris-example-reference-architectures-amazon-web-services-aws


Besoin de savoir

Résumé des performances du volume EBS type gp3.

Les fournisseurs de services en nuage imposent des limites de performance de stockage, par exemple, IOPS ou débit, généralement en augmentant la latence, ce qui aura un impact sur la performance de l'application et l'expérience de l'utilisateur final. Avec les volumes gp3, les limites de performance de stockage de base peuvent être augmentées en payant des prix plus élevés pour augmenter les limites prédéfinies.

Notez que vous devez augmenter le nombre d'IOPS et le débit pour obtenir les performances maximales d'un volume EBS. La latence est appliquée lorsque l'une ou l'autre des limites est atteinte.

Performance IOPS

  • Les volumes gp3 offrent une performance IOPS de base constante de 3 000 IOPS, qui est incluse dans le prix du stockage.
  • Vous pouvez provisionner des IOPS supplémentaires jusqu'à un maximum de 16 000 IOPS par volume.
  • Le maximum d'IOPS peut être provisionné pour les volumes de 32 GiB ou plus.
  • Les volumes gp3 n'utilisent pas les performances en rafale.

Performances de débit

  • Les volumes gp3 offrent un débit de base constant de 125 Mio/s, qui est inclus dans le prix du stockage.
  • Vous pouvez provisionner un débit supplémentaire (jusqu'à un maximum de 1.000 Mio/s) pour un coût additionnel à un ratio de 0,25 Mio/s par IOPS provisionné.
  • Le débit maximum peut être provisionné à 4.000 IOPS ou plus et 8 GiB ou plus (4.000 IOPS × 0,25 Mio/s par IOPS = 1.000 Mio/s).

Pour en savoir plus, consultez Volumes SSD à usage général - Amazon Elastic Compute Cloud.

Limites d'instance

  • Les types d'instances EC2 ont des limites maximales d'IOP et de débit. La latence est appliquée lorsque les limites d'instance ou d'EBS sont atteintes.

Pour en savoir plus, consultez Instances Amazon EBS optimisées - Amazon Elastic Compute Cloud


Gestionnaire de volume logique (Linux)

Si votre application nécessite plus de 16 000 IOPS à partir d'un seul système de fichiers, vous pouvez configurer plusieurs volumes dans une bande LVM (gestionnaire de volumes logiques).

Par exemple : si vous avez besoin de 80 000 IOPS, vous pouvez provisionner un type d'instance EC2 capable de prendre en charge 80 000 IOPS à l'aide de cinq volumes gp3 en bandes LVM. 

LVM a été utilisé pour tous les tests suivants avec un ou plusieurs volumes EBS.


Autres types de stockage par blocs

Bien qu'il existe d'autres types de volumes, tels que io2 block express, avec des IOPS et des débits plus élevés pour un seul volume, il peut être beaucoup moins cher d'utiliser des volumes EBS type gp3 dans une bande LVM pour la même quantité de stockage et des IOPS "suffisamment élevés". io2 block express n'a de valeur que pour certains types d'instances.


Exécution des tests

Deux tests sont exécutés pour générer des simulations d'E/S de stockage de l'application IRIS. Il s'agit des tests RANREAD et RANWRITE.

Les détails de ces tests et la manière de les exécuter se trouvent ici : https://community.intersystems.com/post/perftools-io-test-suite

RANREAD

Ce test génère des lectures aléatoires d'une seule base de données d'IRIS.

  • Dans IRIS, les lectures sont continues par les processus utilisateurs ; un processus utilisateur initie une entrée-sortie de disque pour lire les données. Les démons qui servent les pages web, les requêtes SQL ou les processus utilisateurs directs effectuent des lectures.
  • Les processus RANREAD lisent des blocs de données aléatoires dans la base de données de test.
  • La base de données de test est dimensionnée de manière à dépasser le cache de lecture attendu des systèmes de stockage sur site. On ne sait pas si AWS utilise la mise en cache de lecture (par exemple, Azure le fait par défaut).

RANWRITE

Génère des E/S d'écriture à l'aide du cycle du démon d'écriture de la base de données IRIS. Les trois principales activités d'écriture d'IRIS sont les suivantes :

  • Journal d'écriture d'images (WIJ). Le WIJ écrit une rafale environ toutes les 80 secondes ou lorsqu'un pourcentage du cache de la base de données est en attente de mises à jour par un seul démon d'écriture du maître de la base de données. Le WIJ protège l'intégrité des fichiers de la base de données physique contre les défaillances du système pendant un cycle d'écriture de la base de données. Les écritures sont d'environ 256 Ko chacune juste avant que les fichiers de la base de données ne soient mis à jour par des écritures aléatoires de la base de données.
  • Écritures aléatoires dans des bases de données. Les écritures de la base de données par le démon d'écriture se font en rafale environ toutes les 80 secondes ou en fonction du pourcentage de mises à jour en attente dans le cache de la base de données. Un ensemble de processus du système de base de données, connus sous le nom de démons d'écriture, effectue les écritures. Les processus utilisateur mettent à jour le cache en mémoire de la base de données et un déclencheur (basé sur un seuil de temps ou d'activité) envoie les mises à jour sur le disque à l'aide des démons d'écriture. En règle générale, quelques Mo à plusieurs Go sont écrits au cours du cycle d'écriture, en fonction des taux de transaction.
  • Écritures de journal Les écritures dans le journal sont quasi-continues, de moins de deux secondes, et sont déclenchées lorsque les tampons du journal sont pleins ou lors d'une demande de synchronisation des données (par exemple, à partir d'une transaction). Les écritures de journal sont séquentielles et leur taille varie de 4 Ko à 4 Mo. Le nombre d'écritures par seconde peut varier de quelques dizaines à plusieurs milliers pour les grands déploiements utilisant des serveurs d'application distribués.

Note : des bases de données séparées sont utilisées pour RANREAD et RANWRITE dans les tests ; cependant, le même volume EBS et le même système de fichiers (/data) sont utilisés. La taille de la base de données RANREAD est de 700 Go. IRIS sur les systèmes Linux utilise l'IO directe pour les opérations de base de données. Le cache de page du système de fichiers Linux n'est pas utilisé.


Résultats de benchmarks

A. Absence de bande LVM

Le benchmark a été exécuté en utilisant l'instance EBS optimisée suivante :

image

Le benchmark IRIS est exécuté avec trois volumes EBS ( Données, WIJ, et Journaux). Les IOPS pour le volume /data ont été provisionnés au maximum pour un seul volume. Le débit a été laissé à la valeur par défaut de 125 Mo/s.

image

A.002 - RANREAD de 30 minutes seulement avec une augmentation progressive de l'IOPS.

Cinq puis dix processus RANREAD ont été utilisés pour générer l'IO. Approche du débit maximum (16K IOPS et 125 Mo/s de débit).

Note : IRIS utilise un bloc de base de données de 8 Ko (1 500 * 8 Ko = environ 123 Mio/s).

image

Dans l'image ci-dessous : La latence de base (en vert) est cohérente entre les exécutions de RANREAD, avec une moyenne de 0,7 à 1,5 ms, bien qu'il y ait des pics à plus de 5 ms. Pour une raison inconnue, les performances se dégradent au milieu du test à 10 processus.

image

A.006 - RANREAD et RANWRITE de 20 minutes.

L'image ci-dessous montre comment le test a été rythmé pour atteindre des pics autour de 8 000 IOPS en écriture et 12 000 IOPS en lecture. L'écart en lecture est intentionnel.

image

La latence de lecture dans l'image ci-dessous est similaire au test A.002. Les bases de données en lecture et en écriture sont sur le même volume EBS. Il y a un pic dans la latence de lecture (vert) pendant le cycle du démon d'écriture des écritures aléatoires de la base de données (bleu). Le pic de latence de lecture est transitoire et n'affectera probablement pas l'expérience d'un utilisateur interactif.

Le WIJ se trouve sur un volume EBS séparé afin d'isoler les écritures séquentielles du WIJ des IOPS de lecture. Bien que dans le nuage tout le stockage EBS soit un stockage réseau, il n'y a aucune garantie que les volumes EBS individuels soient dans un stockage SSD physique séparé ou que les différents volumes attachés à une instance soient dans le même boîtier de stockage.

image


B. Augmentation des IOPS et du débit à l'aide d'une bande LVM.

La suite de tests a été exécutée en utilisant une instance EBS optimisée avec un maximum de 80 000 IOPS :

image

Le benchmark IRIS fonctionne avec cinq volumes EBS. Remarque : le fichier /data comprend cinq volumes EBS dans une bande LVM.

image


B.080 - Le RANREAD de 30 minutes n'augmente que progressivement le nombre d'IOPS.

Le test a été effectué avec 10 à 50 processus RANREAD, en augmentant par paliers de 10 processus. Le test s'approche du maximum d'IOPS (80K).

image

La latence de lecture de la base de données est inférieure (0,7 en moyenne) à celle du test sans bande.

image

B.083 - RANREAD et RANWRITE de 30 minutes.

Ce test a été cadencé à des pics d'environ 40 000 IOPS en écriture et 60 000 IOPS en lecture.

Notez que les IOPS de lecture et d'écriture combinées sont supérieures aux IOPS maximales de l'instance EC2 (80 000 IOPS).

image

La latence de lecture est similaire à celle du test B.080. Les bases de données de lecture et d'écriture sont sur le même volume EBS. Il y a un pic dans la latence de lecture (vert) pendant le cycle du démon d'écriture des écritures aléatoires de la base de données (bleu). Le pic de latence de lecture est transitoire et inférieur à 1,4 ms et n'affectera probablement pas l'expérience de l'utilisateur.

image

0
0 163
Article Sylvain Guilbaud · Juin 12, 2023 12m read

Il y a souvent des questions concernant la configuration idéale du Serveur Web HTTPD Apache pour HealthShare.  Le contenu de cet article décrit la configuration initiale recommandée du serveur Web pour tout produit HealthShare. 

APour commencer, la version 2.4.x (64 bits) d'Apache HTTPD est recommandée.  Des versions antérieures comme 2.2.x sont disponibles, mais la version 2.2 n'est pas recommandée pour les performances et l'évolutivité de HealthShare.

Configuration d'Apache

Module API Apache sans NSD {#ApacheWebServer-ApacheAPIModulewithoutNSD}

HealthShare requiert l'option d'installation Apache API Module without NSD. La version des modules liés dynamiquement dépend de la version d'Apache :

  • CSPa24.so (Apache Version 2.4.x)

La configuration de Caché Server Pages dans le fichier Apache httpd.conf doit être effectuée par l'installation de HealthShare qui est détaillée plus loin dans ce document. Cependant, la configuration peut être effectuée manuellement. Pour plus d'informations, veuillez consulter le guide de configuration d'Apache dans la documentation d'InterSystems : Recommended Option: Apache API Module without NSD (CSPa24.so)

Recommandations concernant le module multiprocesseur d'Apache (MPM) {#ApacheWebServer-ApacheMulti-ProcessingModule(MPM)Recommendations}

Apache Prefork MPM Vs. Worker MPM {#ApacheWebServer-ApachePreforkMPMVs.WorkerMPM}

Le serveur web HTTPD Apache est livré avec trois modules multiprocesseurs (MPM) : Prefork, Worker et Event.  Les MPM sont responsables de la liaison avec les ports réseau de la machine, de l'acceptation des requêtes et de l'envoi de fils pour traiter ces requêtes. Par défaut, Apache est généralement configuré avec le MPM Prefork, qui n'est pas adapté à des charges de travail importantes en termes de transactions ou d'utilisateurs simultanés.

Pour les systèmes de production HealthShare, le MPM Apache Worker doit être activé pour des raisons de performance et d'évolutivité. Worker MPM est préféré pour les raisons suivantes :

  • Prefork MPM utilise plusieurs processus enfants avec un fil d'exécution chacun et chaque processus gère une connexion à la fois. Lorsque Prefork est utilisé, les demandes simultanées souffrent car, comme chaque processus ne peut traiter qu'une seule demande à la fois, les demandes sont mises en attente jusqu'à ce qu'un processus serveur se libère. De plus, afin d'évoluer, il faut plus de processus enfants Prefork, ce qui consomme des quantités importantes de mémoire.
  • Worker MPM utilise plusieurs processus enfants avec de nombreux fils d'exécution chacun. Chaque fil d'exécution gère une connexion à la fois, ce qui favorise la concurrence et réduit les besoins en mémoire. Worker gère mieux la concurrence que Prefork, car il y aura généralement des fils d'exécution libres disponibles pour répondre aux demandes au lieu des processus Prefork à un seul fil d'exécution qui peuvent être occupés.

Paramètres MPM pour Apache Worker {#ApacheWebServer-ApacheWorkerMPMParameters}

En utilisant des fils d'exécution pour servir les demandes, Worker est capable de servir un grand nombre de demandes avec moins de ressources système que le serveur basé sur le processus Prefork. 
Les directives les plus importantes utilisées pour contrôler le MPM de Worker sont ThreadsPerChild qui contrôle le nombre de fils d'exécution déployés par chaque processus enfant et MaxRequestWorkers qui contrôle le nombre total maximum de fils d'exécution pouvant être lancés.
Les valeurs recommandées de la directive commune Worker MPM sont détaillées dans le tableau ci-dessous :

<caption>Paramètres recommandés pour le serveur web HTTPD Apache</caption>
<th>
  Valeur recommandée
</th>

<th>
  Commentaires
</th>
<td>
  Nombre maximal d'utilisateurs simultanés de HealthShare Clinical Viewer, ou des quatre autres composants de HealthShare, fixé à la somme de toutes les tailles de pool de services commerciaux entrants pour toutes les productions d'interfaces définies. * Note : Si toutes les inconnues au moment de la configuration commencent par une valeur de '1000'
</td>

<td>
    MaxRequestWorkers fixe la limite du nombre de demandes simultanées qui seront servies, c'est-à-dire qu'il restreint le nombre total de fils d'exécution qui seront disponibles pour servir les clients. Il est important que MaxRequestWorkers soit défini correctement car s'il est trop faible, les ressources seront gaspillées et s'il est trop élevé, les performances du serveur seront affectées. Notez que lorsque le nombre de connexions tentées est supérieur au nombre de travailleurs, les connexions sont placées dans une file d'attente. La file d'attente par défaut peut être ajustée avec la directive ListenBackLog.  
</td>
<td>
  250
</td>

<td>
    MaxSpareThreads traite les threads inactifs à l'échelle du serveur. S'il y a trop de threads inactifs dans le serveur, les processus enfants sont tués jusqu'à ce que le nombre de threads inactifs soit inférieur à ce nombre. L'augmentation du nombre de threads inutilisés par rapport à la valeur par défaut permet de réduire les risques de réactivation des processus.  
</td>
<td>
  75
</td>

<td>
    MinSpareThreads traite les fils d'exécution inactifs à l'échelle du serveur. S'il n'y a pas assez de fils d'exécution libres dans le serveur, des processus enfants sont créés jusqu'à ce que le nombre de fils d'exécution libres soit supérieur à ce nombre. En réduisant le nombre de fils d'exécution inutilisés par rapport à la valeur par défaut, on réduit les risques de réactivation des processus.  
</td>
<td>
  MaxRequestWorkers divisé par ThreadsPerChild
</td>

<td>
    Valeur maximale de MaxRequestWorkers pour la durée de vie du serveur. ServerLimit est une limite stricte du nombre de processus enfants actifs, et doit être supérieure ou égale à la directive MaxRequestWorkers divisée par la directive ThreadsPerChild. Avec worker, n'utilisez cette directive que si vos paramètres MaxRequestWorkers et ThreadsPerChild nécessitent plus de 16 processus serveur (valeur par défaut).  
</td>
<td>
  20
</td>

<td>
    La directive StartServers définit le nombre de processus de serveur enfant créés au démarrage. Comme le nombre de processus est contrôlé dynamiquement en fonction de la charge, il y a généralement peu de raisons d'ajuster ce paramètre, sauf pour s'assurer que le serveur est prêt à gérer un grand nombre de connexions dès son démarrage.  
</td>
<td>
  25
</td>

<td>
    Cette directive définit le nombre de threads créés par chaque processus enfant, 25 par défaut. Il est recommandé de conserver la valeur par défaut car l'augmenter pourrait conduire à une dépendance excessive vis-à-vis d'un seul processus.  
</td>
Les Directives MPM pour Apache Worker
MaxRequestWorkers 
MaxSpareThreads
MinSpareThreads
ServerLimit
StartServers
ThreadsPerChild

Pour plus d'informations, veuillez consulter la documentation relative à la version d'Apache concernée :

Exemple de configuration MPM du travailleur Apache 2.4 {#ApacheWebServer-ExampleApache2.4WorkerMPMConfiguration}

Cette section explique comment configurer Worker MPM pour un serveur Web RHEL7 Apache 2.4 nécessaire pour prendre en charge jusqu'à 500 utilisateurs simultanés de TrakCare.

  1. Vérifiez d'abord le MPM en utilisant la commande suivante :
  2. Modifiez le fichier de configuration /etc/httpd/conf.modules.d/00-mpm.conf selon les besoins, en ajoutant et en supprimant le caractère de commentaire # afin que seuls les modules Worker MPM soient chargés. Modifiez la section Worker MPM avec les valeurs suivantes dans le même ordre que ci-dessous :
  3. Redémarrer Apache
  4. Après avoir redémarré Apache avec succès, validez les processus worker en exécutant les commandes suivantes. Vous devriez voir quelque chose de similaire à ce qui suit confirmant le processus httpd.worker :

Renforcement d'Apache {#ApacheWebServer-ApacheHardening}

Modules requis pour Apache {#ApacheWebServer-ApacheRequiredModules}

L'installation du paquetage officiel d'Apache activera par défaut un ensemble spécifique de modules Apache. Cette configuration par défaut d'Apache chargera ces modules dans chaque processus httpd. Il est recommandé de désactiver tous les modules qui ne sont pas nécessaires à HealthShare pour les raisons suivantes :

  • réduire l'empreinte du processus du démon httpd.
  • réduire le risque d'un défaut de segmentation dû à un module malveillant.
  • réduire les vulnérabilités en matière de sécurité.

Le tableau ci-dessous détaille les modules Apache recommandés pour HealthShare. Tout module qui ne figure pas dans la liste ci-dessous peut être désactivé :

Nom du moduleDescription
aliasMappage des différentes parties du système de fichiers de l'hôte dans l'arborescence du document et pour la redirection des URL.
authz_hostFournit un contrôle d'accès basé sur le nom d'hôte du client, l'adresse IP.
dirPermet de rediriger les barres obliques et de servir les fichiers d'index des répertoires.
headersPour contrôler et modifier les en-têtes de demande et de réponse HTTP
log_configJournalisation des requêtes adressées au serveur.
mimeAssocie les extensions du nom de fichier demandé avec le comportement et le contenu du fichier
negotiationPermet de sélectionner le contenu du document qui correspond le mieux aux capacités du client.
setenvifPermet de définir des variables d'environnement en fonction des caractéristiques de la demande
statusAffiche l'état du serveur et les statistiques de performance

Désactivation des modules

Les modules inutiles doivent être désactivés pour renforcer la configuration qui réduira les vulnérabilités de sécurité. Le client est responsable de la politique de sécurité du serveur web. Au minimum, les modules suivants doivent être désactivés.

Nom du moduleDescription
asisEnvoie des fichiers qui contiennent leurs propres titres HTTP
autoindexGénère des indices de répertoire et affiche la liste des répertoires lorsqu'aucun fichier index.html n'est présent
envModifie la variable d'environnement transmise aux scripts CGI et aux pages SSI
cgicgi - Exécution de scripts CGI
actionsExécution de scripts CGI en fonction du type de média ou de la méthode de demande, déclenchement d'actions sur les demandes
includeDocuments HTML analysés par le serveur (Server Side includes)
filterFiltrage intelligent des demandes
versionGestion des informations de version dans les fichiers de configuration à l'aide de IfVersion
userdirMappage des requêtes vers des répertoires spécifiques à l'utilisateur. Par exemple, ~nom d'utilisateur dans l'URL sera traduit en un répertoire dans le serveur

Apache SSL/TLS {#ApacheWebServer-ApacheSSL/TLS}

Pour protéger les données en transit, assurer la confidentialité et l'authentification, InterSystems recommande que toutes les communications TCP/IP entre les serveurs et les clients de HealthShare soient cryptées avec SSL/TLS, et InterSystems recommande également d'utiliser HTTPS pour toutes les communications entre le navigateur client des utilisateurs et la couche serveur web de l'architecture proposée.   Veillez à consulter les politiques de sécurité de votre organisation pour vous assurer de la conformité à toute exigence de sécurité spécifique de votre organisation. 

Le client est responsable de la fourniture et de la gestion des certificats SSL/TLS. Si vous utilisez des certificats SSL, ajoutez le module ssl_ (mod_ssl.so).

Paramètres supplémentaires de durcissement d'Apache {#ApacheWebServer-AdditionalHardeningApacheParameters}

Pour renforcer la configuration d'Apache, apportez les modifications suivantes au fichier httpd.conf :

  • TraceEnable doit être désactivé pour éviter les problèmes potentiels de traçage intersites.

  • ServerSignature doit être désactivé afin que la version du serveur web ne soit pas affichée.

Paramètres de configuration supplémentaires d'Apache {#ApacheWebServer-SupplementalApacheConfigurationParameters}

Keep-Alive {#ApacheWebServer-Keep-Alive}

Le paramètre Apache Keep-Alive permet d'utiliser la même connexion TCP pour la communication HTTP au lieu d'ouvrir une nouvelle connexion pour chaque nouvelle demande, c'est-à-dire que Keep-Alive maintient une connexion persistante entre le client et le serveur. Lorsque l'option Keep-Alive est activée, l'amélioration des performances provient de la réduction de l'encombrement du réseau, de la réduction de la latence des requêtes ultérieures et de la diminution de l'utilisation du CPU causée par l'ouverture simultanée de connexions. L'option Keep-Alive est activée par défaut et la norme HTTP v1.1 stipule qu'elle doit être présumée activée.

Cependant, l'activation de la fonction Keep-Alive présente des inconvénients : Internet Explorer doit être IE10 ou supérieur pour éviter les problèmes de délai d'attente connus avec les anciennes versions d'IE. De même, les intermédiaires tels que les pare-feu, les équilibreurs de charge et les proxies peuvent interférer avec les "connexions TCP persistantes" et entraîner une fermeture inattendue des connexions.  

Lorsque vous activez la fonction "Keep-Alive", vous devez également définir le délai d'attente de cette fonction. Le délai d'attente par défaut pour Apache est trop faible et doit être augmenté pour la plupart des configurations, car des problèmes peuvent survenir en cas de rupture des requêtes AJAX (c'est-à-dire hyperevent). Ces problèmes peuvent être évités en s'assurant que le délai d'attente du serveur est supérieur à celui du client.  En d'autres termes, c'est le client, et non le serveur, qui devrait fermer la connexion.  Des problèmes surviennent - principalement dans IE mais dans une moindre mesure dans d'autres navigateurs - lorsque le navigateur tente d'utiliser une connexion (en particulier pour un POST) dont il s'attend à ce qu'elle soit ouverte. 

Voir ci-dessous les valeurs recommandées de KeepAlive et KeepAliveTimeout pour un serveur Web HealthShare.

Pour activer KeepAlive dans Apache, apportez les modifications suivantes au fichier httpd.conf :

Psserelle CSP {#ApacheWebServer-CSPGateway}

Pour le paramètre CSP Gateway KeepAlive, laissez la valeur par défaut No Action car le statut KeepAlive est déterminé par les titres de la réponse HTTP pour chaque requête.

0
0 465
InterSystems officiel Sylvain Guilbaud · Mai 26, 2023

InterSystems s'engage à fournir une expérience de développement de haute qualité, y compris un excellent IDE (Integrated Developer Experience). Au cours des dernières années, nous avons fait évoluer les outils ObjectScript de Visual Studio Code en parallèle avec notre IDE de longue date, InterSystems Studio. Il y a eu plus de 46 000 téléchargements du plug-in VSCode-ObjectScript, et les commentaires des développeurs indiquent qu'il s'agit d'une excellente expérience de développement, et maintenant supérieure à InterSystems Studio.

0
0 88
Article Sylvain Guilbaud · Mai 19, 2023 3m read

Apache Superset est une plate-forme moderne d'exploration et de visualisation des données. Superset peut remplacer ou augmenter les outils de business intelligence propriétaires pour de nombreuses équipes. Superset s'intègre bien à une variété de sources de données.

Désormais, il est également possible de l'utiliser avec InterSystems IRIS.

Une démo en ligne est disponible et elle utilise SQL d'IRIS Cloud comme source de données.

Apache Superset fournit un certain nombre d'exemples, qui ont été chargés avec succès dans IRIS sans aucun problème, et affichés sur des tableaux de bord d'exemple.

Le support d'IRIS est implémenté avec un paquetage Python nommé superset-iris, qui peut être installé manuellement dans Superset.

Superset utilise SQLAlchemy comme moteur de base de données, tandis que le paquet superset-iris utilise sqlalchemy-iris.

Lorsque le paquet est installé dans l'environnement Superset, il devient possible de sélectionner InterSystems IRIS dans la liste des bases de données prises en charge.

Pour se connecter à la base de données IRIS, il faut un URI SQLAlchemy sous la forme suivante: iris://{login}:{password}@{hostname}:{port}/{namespace}

La connexion de test doit vérifier la disponibilité du serveur. Cliquez ensuite sur Connecter pour terminer l'ajout de la base de données.

Dans le même formulaire d'édition de base de données, dans l'onglet avancé Advanced Tab, et dans le bloc de sécurité Security, l'option nommée Autoriser le téléchargement de fichiers vers la base de données, qui permettra de télécharger des fichiers CSV et de construire des tableaux avec des données dans IRIS en se basant sur ces derniers.

Le Labo SQL, qui permet d'effectuer des requêtes SQL

Il permet également de collecter et d'afficher des renseignements sur les schémas et les tableaux existants, de prévisualiser ces tableaux et de proposer la construction d'une simple requête SQL avec toutes les colonnes.

Pour l'essayer localement, clonez le dépôt

git clone https://github.com/caretdev/superset-iris.git superset-iris
cd superset-iris

Lancez Superset avec Docker-Compose

docker-compose pull
docker-compose up -d

Lancez Superset avec Docker-Compose Pendant le démarrage, il importe les données d'exemple dans la base de données IRIS, cela prendra un certain temps, pour attendre que ce soit fait, exécutez la commande suivante

docker-compose logs -f superset-init

Lorsque la commande ci-dessus a fini de fonctionner, allez à http://localhost:8088/dashboard/list/. Les panneaux de bord sont disponibles sans autorisation. Pour accéder au Labo SQL, utilisez admin/admin comme login et mot de passe.

Veuillez voter sur le concours

0
0 210
Article Sylvain Guilbaud · Mai 15, 2023 5m read

Aperçu général

En passant d'IRIS objectScript à Python, on s'aperçoit qu'il existe des différences syntaxiques fascinantes.

L'une d'entre elles concerne la manière dont Python renvoie des tuples à partir d'une méthode à décompression automatique.

En fait, il s'agit d'une méthode qui renvoie plusieurs valeurs. Quelle invention géniale :)

out1, out2 = some_function(in1, in2)

ObjectScript a une autre approche avec les paramètres ByRef et Output.

Do ##class(some_class).SomeMethod(.inAndOut1, in2, .out2)

Où:

  • inAndOut1 représente ByRef
  • out2 représente Output

Le point initiale (".") devant le nom de la variable passe ByRef et pour Output.

Le but de cet article est de décrire comment l'utilitaire communautaire PyHelper a été amélioré pour donner une façon pythonique de tirer parti des paramètres ByRef et Output. Il donne accès à %objlasterror et a une approche pour la gestion des types Python None.
 

Exemple ByRef

L'invocation normale pour python intégré serait :

oHL7=iris.cls("EnsLib.HL7.Message")._OpenId('er12345')

Lorsque cette méthode ne parvient pas à s'ouvrir, la variable "oHL7" est une chaîne vide.
Dans la signature de cette méthode, il y a un paramètre d'état qui est disponible pour le script de l'objet qui donne une explication du problème exact.
Par exemple :

  • L'enregistrement peut ne pas exister
  • L'enregistrement ne peut être ouvert dans le mode d'accès simultané exclusif par défaut ("1"), pendant un timeout.
ClassMethod %OpenId(id As %String = "", concurrency As %Integer = -1, ByRef sc As %Status = {$$$OK}) As %ObjectHandle

La méthode TupleOut peut aider à renvoyer la valeur de l'argument sc dans un contexte python.
 

> oHL7,tsc=iris.cls("alwo.PyHelper").TupleOut("EnsLib.HL7.Message","%OpenId",['sc'],1,'er145999', 0)
> oHL7
''
> iris.cls("%SYSTEM.Status").DisplayError(tsc)
ERROR #5809: Objet à charger introuvable, classe 'EnsLib.HL7.Message', ID 'er145999'1
```

La liste ['sc'] contient un seul élément dans ce cas. Elle peut retourner plusieurs valeurs ByRef, et dans l'ordre spécifié. Ce qui est utile pour décompresser automatiquement vers les variables python prévues.

La gestion des paramètres de sortie (Output) en cas d'exemple

Code Python :

> oHL7=iris.cls("EnsLib.HL7.Message")._OpenId('145')
> oHL7.GetValueAt('&lt;%MSH:9.1')
''

La chaîne renvoyée est vide, mais est-ce parce que l'élément est effectivement vide OU parce que quelque chose s'est mal passé ?
Dans le script objet, il existe également un paramètre de sortie d'état (pStatus) auquel il est possible d'accéder pour déterminer cette condition.

Code script objet :

> write oHL7.GetValueAt("&lt;%MSH:9.1",,.pStatus)
''
> Do $System.Status.DisplayError(pStatus)
ERROR &lt;Ens>ErrGeneral: Aucun segment n'a été trouvé lors du trajet '&lt;%MSH'

Avec TupleOut, la fonctionnalité équivalente peut être atteinte en renvoyant et en décompressant à la fois la valeur de retour de la méthode ET le paramètre de sortie d'état.

Code Python :

> hl7=iris.cls("EnsLib.HL7.Message")._OpenId(145,0)
> val, status = iris.cls("alwo.PyHelper").TupleOut(hl7,"GetValueAt",['pStatus'],1,"&lt;&$BadMSH:9.1")
> val==''
True
> iris.cls("%SYSTEM.Status").IsError(status)
1
> iris.cls("%SYSTEM.Status").DisplayError(status)
ERROR &lt;Ens>ErrGeneral: Aucun segment n'a été trouvé lors du trajet '&lt;&$BadMSH'1

Variable spéciale %objlasterror

Dans ObjectScript, il est possible d'accéder à des variables de pourcentage dans le cadre d'une méthode.
Dans certains cas, il est utile de détecter la variable spéciale %objlasterror ou d'y accéder après avoir appelé une API CORE ou une API de tiers.
La méthode TupleOut permet d'accéder à %objlasterror, comme si elle avait été définie en tant que paramètre de sortie, lors de l'invocation de méthodes à partir de Python.

> del _objlasterror

> out,_objlasterror=iris.cls("alwo.PyHelper").TupleOut("EnsLib.HL7.Message","%OpenId",['%objlasterror'],1,'er145999', 0)

> iris.cls("%SYSTEM.Status").DisplayError(_objlasterror)
ERROR #5809: Objet à charger introuvable, classe 'EnsLib.HL7.Message', ID 'er145999'1

Quand "None" n'est pas une chaîne

TupleOut traite les références python "None" comme objectscript "undefined". Cela permet aux paramètres d'être définis par défaut et aux méthodes de se comporter de manière cohérente.
C'est important, par exemple, dans le cas de %Persistent::%OnNew, où la méthode %OnNew n'est pas déclenchée lorsque "None" est fourni comme valeur initiale "initvalue", alors qu'elle serait déclenchée si une chaîne vide était fournie.

En objectscript, l'implémentation pourrait être la suivante :

do oHL7.myMethod("val1",,,"val2")

Notez l'absence de variables entre les virgules.

TupleOut facilite le même comportement avec :

Python:

iris.cls("alwo.PyHelper").TupleOut(oHL7,"myMethod",[],0,"val1",None,None,"val2")

Une autre façon d'envisager la question est d'avoir une implémentation du code d'invocation en une seule ligne, qui se comporte de manière flexible en fonction de la configuration préalable des variables :

Object Script:

set arg1="val1"
kill arg2
kill arg3
set arg4="val2"
do oHL7.myMethod(.arg1, .arg2, .arg3, .arg4)

TupleOut facilite le même comportement avec :

Python:

arg1="val1"
arg2=None
arg3=None
arg4="val2"
iris.cls("alwo.PyHelper").TupleOut(oHL7,"myMethod",[],0,arg1,arg2,arg3,arg4)

Liste et dictionnaires

Lors de la gestion des paramètres d'entrée, de ByRef et Output, TupleOut utilise la correspondance automatique de PyHelper entre: l les listes IRIS et les listes Python
les tableaux IRIS et les tableaux Python
Il prend soin de toujours utiliser des chaînes pour représenter les clés du dictionnaire lorsqu'il passe des tableaux IRIS aux types Dict de Python.

Conclusion

J'espère que cet article contribuera à inspirer de nouvelles idées et discussions sur les idées et suggestions relatives à Python intégré.

J'espère aussi qu'il encouragera à explorer la flexibilité d'IRIS, qui peut facilement s'adapter à de nouveaux défis.

0
0 80
InterSystems officiel Sylvain Guilbaud · Mai 9, 2023

InterSystems a corrigé un défaut pouvant entraîner la corruption des bases de données et des fichiers journaux sur les systèmes AIX avec IBM POWER8 ou des processeurs POWER ultérieurs. Ce défaut peut être déclenché uniquement lorsque le chiffrement de la base de données ou du journal est utilisé.

Pour déclencher ce défaut, les conditions suivantes sont requises :

0
0 55
Annonce Sylvain Guilbaud · Mai 5, 2023

Restez en contact avec InterSystems et recevez rapidement des alertes, des avis et des actualités sur les produits. Le processus est vraiment simple :

Comme vous pouvez le constater, il faut moins d'une minute pour se tenir informé de l'actualité !

0
0 34
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
InterSystems officiel Sylvain Guilbaud · Avr 13, 2023

InterSystems a corrigé un défaut pouvant entraîner une instabilité du client ECP (Enterprise Cache Protocol) dans de rares cas.

Le défaut existe dans les produits suivants et dans toutes les offres InterSystems basées sur ceux-ci.

Les versions concernées sont 2022.1.x, 2022.2 et 2022.3 :

InterSystems IRIS®

InterSystems IRIS for Health™

HealthShare® Connexion Santé

La version concernée est 2022.2 (uniquement pour les clients déployant ECP) :

InterSystems HealthShare®

0
0 74
InterSystems officiel Sylvain Guilbaud · Avr 11, 2023

InterSystems a corrigé un défaut qui pouvait amener une requête SQL à renvoyer des résultats incorrects.

Le défaut existe dans les produits suivants et dans toutes les offres InterSystems basées sur ceux-ci.

Les versions concernées sont 2021.2, 2022.1.x, 2022.2 et 2022.3 :

InterSystems IRIS®

InterSystems IRIS for Health™

HealthShare® Connexion Santé

La version impactée est 2022.2 :

InterSystems HealthShare®

0
0 59
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
InterSystems officiel Sylvain Guilbaud · Mars 16, 2023

Je suis heureux d'annoncer la version 2.6.0 de l'extension VS Code ObjectScript, contenant un certain nombre d'améliorations qui facilitent la vie d'un développeur. Certains faits saillants sont décrits ci-dessous. Comme toujours, retrouvez la liste complète des modifications dans le CHANGELOG, y compris de nombreux correctifs de bogues et de vulnérabilités.

Changez rapidement de namespace

0
0 138
Article Sylvain Guilbaud · Mars 10, 2023 4m read

Ce Template s'agit du modèle de base pour utiliser InterSystems IRIS for Health Community Edition en tant que serveur FHIR.

Il configure un SERVEUR FHIR, importe les données de test, fait la démonstration de l'utilisation de l'API REST avec une simple page web.

Conditions préalables

Assurez-vous que vous avez installé git et Docker desktop.

Installation

IPM

Ouvrez l'installation d'IRIS for Health avec le client IPM installé. Appel dans n'importe quel espace de nom :

USER>zpm "install fhir-server"

Ainsi, le serveur FHIR sera installé dans l'espace de noms FHIRSERVER.

0
0 104
InterSystems officiel Sylvain Guilbaud · Fév 28, 2023 3m read

Je voulais donner un aperçu d'une amélioration de la façon dont nous générons et appelons le code de méthode dans IRIS 2023.1.

Une classe dans IRIS est composée de deux composants d'exécution principaux :

  1. Descripteur de classe - une liste hautement optimisée de méthodes, propriétés, paramètres de classe qui composent la classe ainsi que les attributs associés à chacun d'entre eux, par ex. cadre public/privé.
  2. Code ObjectScript : ensemble de routines contenant le code ObjectScript à exécuter lorsqu'une méthode est appelée.
0
0 86
Article Sylvain Guilbaud · Fév 15, 2023 8m read

Introduction

Dans un article précédent, j'ai abordé les modèles d'exécution des tests unitaires via le gestionnaire de paquets ObjectScript. Cet article va un peu plus loin, en utilisant les actions GitHub pour piloter l'exécution des tests et la création de rapports. Le cas d'utilisation qui nous motive est l'exécution du CI pour l'un de mes projets Open Exchange, AppS.REST (voir l'article d'introduction à ce projet ici). Vous pouvez voir l'implémentation complète dont les extraits de cet article ont été tirés sur GitHub ; elle pourrait facilement servir de modèle pour l'exécution de l'IC pour d'autres projets utilisant le gestionnaire de paquets ObjectScript.

Les fonctionnalités dont la mise en œuvre a été démontrée comprennent :

  • Compilation et test d'un paquet ObjectScript
  • Rapport sur la mesure de la couverture des tests (en utilisant le paquet TestCoverage) via codecov.io
  • Téléchargement d'un rapport sur les résultats des tests en tant qu'artefact de comppilation.

L'environnement de compilation

Il existe une documentation complète sur les actions GitHub ici. Dans le cadre de cet article, nous nous contenterons d'explorer les aspects présentés dans cet exemple.

Un flux de travail dans les actions GitHub est déclenché par un ensemble configurable d'événements et consiste en un certain nombre de tâches qui peuvent être exécutées séquentiellement ou en parallèle. Chaque tâche comporte un ensemble d'étapes - nous allons entrer dans le détail des étapes de notre exemple d'action plus tard. Ces étapes consistent en références à des actions disponibles sur GitHub, ou peuvent simplement être des commandes shell. Un extrait du modèle initial de notre exemple ressemble à ceci :

# Flux de travail d'intégration continue
name: CI

# Contrôle le moment où l'action sera exécutée. Déclenche le flux de travail sur les événements push ou pull request
# événements dans toutes les branches
on: [push, pull_request]

# Un flux de travail est composé d'une ou plusieurs tâches qui peuvent être exécutées séquentiellement ou en parallèle.
jobs:
  # Ce flux de travail contient une seule tâche appelé "build".
  build :
    # Le type d'exécuteur sur lequel le travail sera exécuté
    runs-on : ubuntu-latest

    env:
    # Variables d'environnement utilisables tout au long de la tâche de "compilation", par exemple dans les commandes au niveau du système d'exploitation.
package: apps.rest
    container_image : intersystemsdc/iris-community:2019.4.0.383.0-zpm
    # D'autres éléments seront abordés plus tard...

    # Les étapes représentent une séquence de tâches qui seront exécutées dans le cadre du travail.
     steps:
          # Ceux-ci seront montrés plus tard...

Dans cet exemple, un certain nombre de variables d'environnement sont utilisées. Pour appliquer cet exemple à d'autres paquets utilisant le gestionnaire de paquets ObjectScript, la plupart d'entre elles n'auront pas besoin d'être modifiées, alors que certaines le seront.

    env:
      # ** POUR UN USAGE GÉNÉRAL, IL FAUDRA PROBABLEMENT CHANGER : **
      package: apps.rest
      container_image: intersystemsdc/iris-community:2019.4.0.383.0-zpm

      # ** POUR UN USAGE GÉNÉRAL, IL FAUDRA PEUT-ÊTRE CHANGER : **
      build_flags: -dev -verbose # Télécharger en mode -dev pour obtenir le code de test unitaire préchargé
      test_package: UnitTest

      # ** POUR UN USAGE GÉNÉRAL, IL NE FAUDRA PAS CHANGER : **
      instance: iris
      # Remarque : la valeur test_reports est dupliquée dans la variable d'environnement test_flags.
      test_reports: test-reports
      test_flags: >-
       -verbose -DUnitTest.ManagerClass=TestCoverage.Manager -DUnitTest.JUnitOutput=/test-reports/junit.xml
       -DUnitTest.FailuresAreFatal=1 -DUnitTest.Manager=TestCoverage.Manager
       -DUnitTest.UserParam.CoverageReportClass=TestCoverage.Report.Cobertura.ReportGenerator
       -DUnitTest.UserParam.CoverageReportFile=/source/coverage.xml

Si vous voulez adapter cela à votre propre paquet, il suffit de déposer votre propre nom de paquet et votre image de conteneur préférée (doit inclure zpm - voir https://hub.docker.com/r/intersystemsdc/iris-community). Vous pourriez également vouloir changer le paquet de tests unitaires pour qu'il corresponde à la convention de votre propre paquet (si vous devez charger et compiler les tests unitaires avant de les exécuter pour gérer toutes les dépendances de chargement/compilation ; j'ai eu quelques problèmes bizarres spécifiques aux tests unitaires pour ce paquet, donc cela pourrait même ne pas être pertinent dans d'autres cas).

Le nom de l'instance et le répertoire test_reports ne devraient pas être modifiés pour d'autres utilisations, et les test_flags fournissent un bon ensemble de valeurs par défaut - ils permettent de faire en sorte que les échecs des tests unitaires signalent l'échec de la compilation, et gèrent également l'exportation des résultats des tests au format jUnit et un rapport de couverture de code.

Étapes de compilation

Vérification des référentiels GitHub

Dans notre exemple de motivation, deux dépôts doivent être vérifiés - celui qui est testé, et aussi mon fork de Forgery (parce que les tests unitaires en ont besoin).

    # Vérifie ce référentiel sous $GITHUB_WORKSPACE, afin que votre tâche puisse y accéder.
    - uses: actions/checkout@v2

    # Il faut aussi vérifier le timleavitt/forgery jusqu'à la version officielle installable via ZPM
    - uses: actions/checkout@v2
      with:
        repository: timleavitt/forgery
        path: forgery

$GITHUB_WORKSPACE est une variable d'environnement très importante, représentant le répertoire racine où tout cela fonctionne. Du point de vue des permissions, vous pouvez faire à peu près tout ce que vous voulez dans ce répertoire ; ailleurs, vous pouvez rencontrer des problèmes.

Exécution du conteneur IRIS d'InterSystems

Après avoir configuré un répertoire où nous finirons par placer nos rapports de résultats de tests, nous allons exécuter le conteneur InterSystems IRIS Community Edition (+ZPM) pour notre compilation.

    - name: Run Container
      run: |
        # Créer le répertoire test_reports pour partager les résultats des tests avant l'exécution du conteneur.
        mkdir $test_reports
        chmod 777 $test_reports
        # Lancer l'instance InterSystems IRIS
        docker pull $container_image
        docker run -d -h $instance --name $instance -v $GITHUB_WORKSPACE:/source -v $GITHUB_WORKSPACE/$test_reports:/$test_reports --init $container_image
        echo halt > wait
        # Attendez que l'instance soit prête
        until docker exec --interactive $instance iris session $instance &lt; wait; do sleep 1; done

Il y a deux volumes partagés avec le conteneur - l'espace de travail GitHub (pour que le code puisse être chargé ; nous y rapporterons également des informations sur la couverture des tests), et un répertoire séparé où nous placerons les résultats des tests jUnit.

Après la fin de "docker run", cela ne signifie pas que l'instance est complètement démarrée et prête à être commandée. Pour attendre que l'instance soit prête, nous continuerons à essayer d'exécuter une commande "halt" via la session iris ; cela échouera et continuera à essayer une fois par seconde jusqu'à ce que cela réussisse (éventuellement), indiquant que l'instance est prête.

Installation des bibliothèques liées aux tests

Pour notre cas d'utilisation motivant, nous utiliserons deux autres bibliothèques pour les tests - TestCoverage et Forgery. TestCoverage peut être installé directement via le Community Package Manager ; Forgery (actuellement) doit être chargé via zpm "load" ; mais les deux approches sont valables.

    - name: Install TestCoverage
      run: |
        echo "zpm \"install testcoverage\":1:1" > install-testcoverage
        docker exec --interactive $instance iris session $instance -B &lt; install-testcoverage
        # Solution aux problèmes de permissions dans TestCoverage (création d'un répertoire pour l'exportation des sources)
        chmod 777 $GITHUB_WORKSPACE

    - name: Install Forgery
      run: |
        echo "zpm \"load /source/forgery\":1:1" > load-forgery
        docker exec --interactive $instance iris session $instance -B &lt; load-forgery

L'approche générale consiste à écrire les commandes dans un fichier, puis à les exécuter en session IRIS. Le ":1:1" supplémentaire dans les commandes ZPM indique que la commande doit quitter le processus avec un code d'erreur si une erreur se produit, et s'arrêter à la fin si aucune erreur ne se produit ; cela signifie que si une erreur se produit, elle sera signalée comme une étape de compilation ayant échoué, et nous n'avons pas besoin d'ajouter une commande "halt" à la fin de chaque fichier.

Compilation et test du paquet

Enfin, nous pouvons effectivement compiler et exécuter des tests pour notre paquet. C'est assez simple - remarquez l'utilisation des variables d'environnement $build_flags/$test_flags que nous avons définies plus tôt.

    # Exécute un ensemble de commandes en utilisant l'exécuteur runners
    - name: Build and Test
      run: |
        # Exécution de compilation
        echo "zpm \"load /source $build_flags\":1:1" > build
        # Le paquet de test est compilé en premier comme solution de contournement pour certains problèmes de dépendance.
        echo "do \$System.OBJ.CompilePackage(\"$test_package\",\"ckd\") " > test
        # Exécution des tests
        echo "zpm \"$package test -only $test_flags\":1:1" >> test
        docker exec --interactive $instance iris session $instance -B < build && docker exec --interactive $instance iris session $instance -B < test && bash <(curl -s https://codecov.io/bash)

Cela suit le même schéma que nous avons vu, écrire des commandes dans un fichier puis utiliser ce fichier comme entrée de la session iris.

La dernière partie de la dernière ligne télécharge les résultats de la couverture du code sur codecov.io. Super facile !

Téléchargement des résultats des tests unitaires

Supposons qu'un test unitaire échoue. Il serait vraiment ennuyeux de devoir revenir en arrière dans le journal de compilation pour trouver ce qui n'a pas fonctionné, bien que cela puisse toujours fournir un contexte utile. Pour nous faciliter la vie, nous pouvons télécharger nos résultats formatés par jUnit et même exécuter un programme tiers pour les transformer en un joli rapport HTML.

    # Générer et télécharger le rapport HTML xUnit
    - name: XUnit Viewer
      id: xunit-viewer
      uses: AutoModality/action-xunit-viewer@v1
      if: always()
      with:
        # Avec -DUnitTest.FailuresAreFatal=1, un test unitaire qui échoue fera échouer la compilation avant ce point.
        # Cette action pourrait autrement mal interpréter notre sortie de style xUnit et faire échouer la compilation même si
        # tous les tests sont passés.
        fail: false
    - name: Atacher le rapport
      uses: actions/upload-artifact@v1
      if: always()
      with:
        name: ${{ steps.xunit-viewer.outputs.report-name }}
        path: ${{ steps.xunit-viewer.outputs.report-dir }}

Ces informations sont principalement tirées du fichier readme à l'adresse https://github.com/AutoModality/action-xunit-viewer.

Le résultat final

Si vous voulez voir les résultats de ce flux de travail, regardez :

Les journaux pour le job CI sur intersystems/apps-rest (y compris les artefacts de compilation) : https://github.com/intersystems/apps-rest/actions?query=workflow%3ACI
Rapports de couverture de test : https://codecov.io/gh/intersystems/apps-rest

N'hésitez pas à me faire savoir si vous avez des questions !

0
0 96
Article Sylvain Guilbaud · Nov 11, 2022 7m read

Selon une étude menée par IDC, 83% des CEO souhaitent que leur organisation soit plus axée sur l’utilisation de la donnée. Paradoxalement, 92% d’entre eux n’ont pas confiance dans les données de leur entreprise. Comment convaincre les décideurs d’appuyer davantage leurs décisions sur les données (Data Driven) plutôt que sur leurs intuitions et leurs connaissances du métier ?

Supply chain

Les enjeux actuels dans la chaîne d’approvisionnement confirment que les données s’imposent comme un actif précieux et indispensable dans l’élaboration et l’exécution d’une stratégie Data Driven.

0
1 98
Article Sylvain Guilbaud · Avr 20, 2022 4m read

Lors d'une montée de version majeure il est conseillé de recompiler les classes et les routines de tous vos espaces de noms (cf. Major Version Post-Installation Tasks).

do $system.OBJ.CompileAllNamespaces("u")
do ##Class(%Routine).CompileAllNamespaces()

Pour automatiser cette tâche d'administration et conserver un journal des erreurs éventuelles, vous trouverez ci-dessous un exemple d'une classe à importer et compiler dans l'espace de noms USER que vous pourrez utiliser après chaque montée de version : admin.utils.cls

0
1 175
Article Sylvain Guilbaud · Avr 19, 2022 2m read

Kong fournit en open source un outil de gestion de ses configurations (écrit en Go), appelé decK (pour declarative Kong)

  • Vérifiez que decK reconnaît votre installation Kong Gateway via deck ping
deck ping   
Successfully connected to Kong!
Kong version:  2.3.3.2-enterprise-edition
  • Exporter la configuration de Kong Gateway dans un fichier "kong.yaml" via deck dump
deck dump
  • Après avoir modifié le kong.yaml, afficher les différences via deck diff
0
0 182
Article Sylvain Guilbaud · Mars 18, 2022 6m read

Dans cet article, je vais vous montrer comment vous pouvez facilement conteneuriser les passerelles .Net/Java.

Pour notre exemple, nous allons développer une intégration avec Apache Kafka.

Et pour interopérer avec le code Java/.Net, nous utiliserons PEX.

Architecture

Notre solution fonctionnera entièrement dans docker et ressemblera à ceci :

Passerelle Java

Tout d'abord, nous allons développer l'opération Java pour envoyer des messages dans Kafka. Le code peut être écrit dans l'IDE de votre choix et il peut ressembler à ceci.

En bref :

  • Pour développer une nouvelle opération commerciale PEX, nous devons implémenter la classe abstraite com.intersystems.enslib.pex.BusinessOperation
  • Les propriétés publiques sont les paramètres de l'hôte de l'entreprise
  • La méthode OnInit est utilisée pour initier la connexion à Kafka et obtenir un pointeur vers InterSystems IRIS
  • OnTearDown est utilisé pour se déconnecter de Kafka (à l'arrêt du processus)
  • OnMessage reçoit le message dc.KafkaRequest et l'envoie à Kafka

Maintenant, plaçons-le dans Docker !

Voici notre dockerfile :

FROM openjdk:8 AS builder

ARG APP_HOME=/tmp/app

COPY src $APP_HOME/src

COPY --from=intersystemscommunity/jgw:latest /jgw/*.jar $APP_HOME/jgw/

WORKDIR $APP_HOME/jar/
ADD https://repo1.maven.org/maven2/org/apache/kafka/kafka-clients/2.5.0/kafka-clients-2.5.0.jar .
ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar .
ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar .
ADD https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar .

WORKDIR $APP_HOME/src

RUN javac -classpath $APP_HOME/jar/*:$APP_HOME/jgw/* dc/rmq/KafkaOperation.java && \
    jar -cvf $APP_HOME/jar/KafkaOperation.jar dc/rmq/KafkaOperation.class

FROM intersystemscommunity/jgw:latest

COPY --from=builder /tmp/app/jar/*.jar $GWDIR/

Allons-y ligne par ligne et voyons ce qui se passe ici (je suppose que vous connaissez les constructions docker à plusieurs niveaux) :

FROM openjdk:8 AS builder

Notre image de départ est JDK 8.

ARG APP_HOME=/tmp/app
COPY src $APP_HOME/src

Nous copions nos sources du dossier /src dans le dossier /tmp/app.

COPY --from=intersystemscommunity/jgw:latest /jgw/*.jar $APP_HOME/jgw/

Nous copions les sources de la passerelle Java dans le dossier /tmp/app/jgw.

WORKDIR $APP_HOME/jar/
ADD https://repo1.maven.org/maven2/org/apache/kafka/kafka-clients/2.5.0/kafka-clients-2.5.0.jar .
ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar .
ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar .
ADD https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar .

WORKDIR $APP_HOME/src

RUN javac -classpath $APP_HOME/jar/*:$APP_HOME/jgw/* dc/rmq/KafkaOperation.java && \
    jar -cvf $APP_HOME/jar/KafkaOperation.jar dc/rmq/KafkaOperation.class

Maintenant toutes les dépendances sont ajoutées et javac/jar est appelé pour compiler le fichier jar. Pour un projet concret, il est préférable d'utiliser maven ou gradle.

FROM intersystemscommunity/jgw:latest

COPY --from=builder /tmp/app/jar/*.jar $GWDIR/

Et enfin, les jars sont copiés dans l'image de base jgw (l'image de base se charge également du démarrage de la passerelle et des tâches connexes).

Passerelle .Net

Vient ensuite le service .Net qui recevra les messages de Kafka. Le code peut être écrit dans l'IDE de votre choix et il peut ressembler à ceci.

En bref :

  • Pour développer un nouveau service d'entreprise PEX nous devons implémenter la classe abstraite InterSystems.EnsLib.PEX.BusinessService
  • Les propriétés publiques sont les paramètres de l'hôte de l'entreprise
  • La méthode OnInit est utilisée pour établir une connexion avec Kafka, s'abonner à des sujets et obtenir un pointeur vers InterSystems IRIS
  • OnTearDown est utilisé pour se déconnecter de Kafka (à l'arrêt du processus)
  • OnMessage consomme les messages de Kafka et envoie des messages Ens.StringContainer aux autres hôtes d'Interoperability

Maintenant, plaçons-le dans Docker !

Voici notre dockerfile :

FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build

ENV ISC_PACKAGE_INSTALLDIR /usr/irissys
ENV GWLIBDIR lib
ENV ISC_LIBDIR ${ISC_PACKAGE_INSTALLDIR}/dev/dotnet/bin/Core21

WORKDIR /source
COPY --from=store/intersystems/iris-community:2020.2.0.211.0 $ISC_LIBDIR/*.nupkg $GWLIBDIR/

# copier csproj et restaurer en tant que couches distinctes
COPY *.csproj ./
RUN dotnet restore

# copier et publier l'application et les bibliothèques
COPY . .
RUN dotnet publish -c release -o /app

# étape/image finale
FROM mcr.microsoft.com/dotnet/core/runtime:2.1
WORKDIR /app
COPY --from=build /app ./

# Configurations pour démarrer le serveur passerelle
RUN cp KafkaConsumer.runtimeconfig.json IRISGatewayCore21.runtimeconfig.json && \
    cp KafkaConsumer.deps.json IRISGatewayCore21.deps.json

ENV PORT 55556

CMD dotnet IRISGatewayCore21.dll $PORT 0.0.0.0

Allons-y ligne par ligne :

FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build

Nous utilisons le SDK complet .Net Core 2.1 pour construire notre application.

ENV ISC_PACKAGE_INSTALLDIR /usr/irissys
ENV GWLIBDIR lib
ENV ISC_LIBDIR ${ISC_PACKAGE_INSTALLDIR}/dev/dotnet/bin/Core21

WORKDIR /source
COPY --from=store/intersystems/iris-community:2020.2.0.211.0 $ISC_LIBDIR/*.nupkg $GWLIBDIR/

Copiez .Net Gateway NuGets de l'image Docker officielle d'InterSystems dans notre image de constructeur

# copier csproj et restaurer comme couches distinctes
COPY *.csproj ./
RUN dotnet restore

# copier et publier l'application et les bibliothèques
COPY . .
RUN dotnet publish -c release -o /app

Construisez notre bibliothèque.

# étape/image finale
FROM mcr.microsoft.com/dotnet/core/runtime:2.1
WORKDIR /app
COPY --from=build /app ./

Copiez les dll de la bibliothèque dans le conteneur final que nous exécuterons réellement.

# Configurats pour démarrer le serveur Gateway
RUN cp KafkaConsumer.runtimeconfig.json IRISGatewayCore21.runtimeconfig.json && \
    cp KafkaConsumer.deps.json IRISGatewayCore21.deps.json

Actuellement, la passerelle .Net doit charger toutes les dépendances au démarrage, nous lui faisons donc connaître toutes les dépendances possibles.

ENV PORT 55556

CMD dotnet IRISGatewayCore21.dll $PORT 0.0.0.0

Démarrez la passerelle sur le port 55556 en écoutant sur toutes les interfaces.

Et voilà, c'est tout !

Voici un docker-compose complet pour que tout fonctionne (y compris Kafka et Kafka UI pour voir les messages).

Pour exécuter la démo, vous dever :

  1. Installer :
  2. Exécuter :
git clone https://github.com/intersystems-community/pex-demo.git cd pex-demo docker-compose pull docker-compose up -d

 

Avis important : Les bibliothèques Java Gateway et .Net Gateway DOIVENT provenir de la même version que le client InterSystems IRIS.

0
0 229