#Artificial Intelligence (AI)

0 Abonnés · 41 Publications

L'intelligence artificielle (IA) est la simulation des processus d'intelligence humaine par des machines, notamment des systèmes informatiques. Ces processus comprennent l'apprentissage (l'acquisition d'informations et de règles pour utiliser ces informations), le raisonnement (l'utilisation de règles pour parvenir à des conclusions approximatives ou définitives) et l'autocorrection.

En savoir plus.

Article Guillaume Rongier · Oct 30, 2025 7m read

Introduction

Dans mon dernier article, j'ai présenté FHIR Data Explorer, une application de validation de concept qui connecte InterSystems IRIS, Python, et Ollama afin de permettre la recherche sémantique et la visualisation des données de soins de santé au format FHIR. Ce projet participe actuellement au concours InterSystems External Language Contest.

Dans cette suite, nous verrons comment j'ai intégré Ollama pour générer les résumés des dossiers médicaux directement à partir des données FHIR structurées stockées dans IRIS, à l'aide de modèles linguistiques locaux légers (LLM) tels que Llama 3.2:1B ou Gemma 2:2B.

L'objectif était de créer un pipeline d'IA entièrement local capable d'extraire, de formater et de présenter les dossiers médicaux des patients tout en garantissant la confidentialité et le contrôle total des données.

Toutes les données des patients utilisées dans cette démonstration proviennent de paquets FHIR, qui ont été analysés et chargés dans IRIS via le module IRIStool. Cette approche facilite la requête, la conversion et la vectorisation des données de soins de santé à l'aide d'opérations pandas familières en Python. Si vous désirez en savoir plus sur la manière dont j'ai construit cette intégration, consultez mon article précédent Création d'un référentiel vectoriel FHIR avec InterSystems IRIS et Python via le module IRIStool.

Les deux outils, IRIStool et FHIR Data Explorer sont disponibles sur InterSystems Open Exchange — et font partie de mes contributions au concours. Si vous les trouvez utiles, n'hésitez pas à voter pour eux!

1. Configuration avec Docker Compose

Pour simplifier la configuration et la rendre reproductible, tout fonctionne localement au moyen de Docker Compose.
Une configuration minimale se présente comme suit:

services:
  iris:
    container_name: iris-patient-search
    build:
      context: .
      dockerfile: Dockerfile
    image: iris-patient-search:latest  
    init: true
    restart: unless-stopped
    volumes:
      - ./storage:/durable
    ports:
      - "9092:52773"# Management Portal / REST APIs
      - "9091:1972"# SuperServer port
    environment:
      - ISC_DATA_DIRECTORY=/durable/iris
    entrypoint: ["/opt/irisapp/entrypoint.sh"]

  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    pull_policy: always
    tty: true
    restart: unless-stopped
    ports:
      - 11424:11434
    volumes:
      - ./ollama_entrypoint.sh:/entrypoint.sh
entrypoint: ["/entrypoint.sh"]

Toutes les configurations sont disponibles sur la page du projet GitHub.

2. Intégration d'Ollama dans le flux de travail

Ollama fournit une API REST locale simple pour exécuter efficacement des modèles sur le processeur, ce qui le rend idéal pour les applications de santé où la confidentialité et les performances sont importantes.

Pour connecter IRIS et Streamlit à Ollama, j'ai implémenté une classe Python légère pour transmettre les réponses de l'API Ollama:

import requests, json

classollama_request:def__init__(self, api_url: str):
        self.api_url = api_url

    defget_response(self, content, model):
        payload = {
            "model": model,
            "messages": [
                {"role": "user", "content": content}
            ]
        }
        response = requests.post(self.api_url, json=payload, stream=True)

        if response.status_code == 200:
            for line in response.iter_lines(decode_unicode=True):
                if line:
                    try:
                        json_data = json.loads(line)
                        if"message"in json_data and"content"in json_data["message"]:
                            yield json_data["message"]["content"]
                    except json.JSONDecodeError:
                        yieldf"Error decoding JSON line: {line}"else:
            yieldf"Error: {response.status_code} - {response.text}"

Cela permet de transmettre les résultats du modèle en temps réel, ce qui donne aux utilisateurs l'impression de “surveiller“ le travail de l'IA en direct dans l'interface utilisateur Streamlit.

3. Préparation des données du patient pour LLM

Avant d'envoyer quoi que ce soit à Ollama, les données doivent être compactes, structurées et pertinentes sur le plan clinique.
Pour cela, j'ai écrit une classe qui extrait et formate les données les plus pertinentes du patient (données démographiques, état de santé, observations, procédures, etc.) au format YAML, qui est à la fois compréhensible et compatible avec LLM.

Le processus simplifié est le suivant:

  1. Sélectionnez la chaîne du patient dans IRIS via pandas
  2. Extrayez les données démographiques et convertissez-les au format YAML
  3. Traitez chaque table médicale (conditions, observations, etc.)
  4. Supprimez les champs inutiles ou superflus
  5. Générez un document YAML concis utilisé comme contexte d'invite LLM.

Cette chaîne est ensuite transmise directement à l'invite LLM, formant le contexte structuré à partir duquel le modèle génère le compte rendu sommaire du patient.


4. Pourquoi limiter le nombre d'enregistrements?

Lors du développement de cette fonctionnalité, j'ai remarqué que la transmission de tous les dossiers médicaux provoquait souvent une confusion ou un biais dans les petits LLM envers les saisies plus anciennes, ce qui faisait perdre de vue les événements récents.

Pour résoudre ce problème, j'ai décidé:

  • De n'inclure qu'un nombre limité d'enregistrements par catégorie dans l' ordre chronologique inverse (plus récents en premier)
  • D'utiliser un format concis YAML au lieu du format brut JSON
  • De normaliser les types de données (horodatages, valeurs nulles, etc.) pour plus de cohérence

Cette conception aide les petits LLM à se concentrer sur les données les plus pertinentes sur le plan clinique data en évitant la “surcharge d'invites”.


💬 4. Génération du dossier médical du patient

Une fois les données au format YAML prêtes, l'application Streamlit les envoie à Ollama au moyen d'une simple invite telle que:

“Vous êtes assistant clinique. À partir des données suivantes du patient, rédigez un dossier médical concis en soulignant les pathologies pertinentes et les tendances récentes.”

Le résultat est renvoyé ligne par ligne à l'interface utilisateur, ce qui permet à l'utilisateur de surveiller la rédaction du dossier en temps réel.
Chaque modèle produit un résultat légèrement différent, même au moyen de la même invite, révélant des différences fascinantes dans le raisonnement et le style.


🧠 5. Comparaison des LLM locaux

Pour évaluer l'efficacité de cette approche, j'ai testé trois modèles ouverts et légers, disponibles via Ollama:

ModèleParamètresStyle de dossierRemarques
Llama 3.2:1B1BStructuré, factuelRésultat hautement littéral et schématique
Gemma 2:2B2BNarratif, à caractère humainLe plus cohérent et le plus sensible au contexte
Gemma 3:1B1BConcis, synthétiqueOmettant parfois certains détails, mais très compréhensible

Vous trouverez des exemples de résultats dans le  dossier GitHub suivant. Chaque dossier du patient met en évidence la manière dont la taille du modèle et le style d'entraînement influencent la structure, la cohérence et le niveau de détail du texte.

Voici une interprétation comparative de leur comportement:

  • Llama 3.2:1B reproduit généralement la structure des données mot pour mot, presque comme s'il effectuait une exportation de base de données. Ses résumés sont techniquement précis, mais ne sont pas fluides, ressemblant davantage à un rapport clinique structuré qu'à un texte naturel.
  • Gemma 3:1B offre une meilleure fluidité linguistique, mais compresse ou omet des détails mineurs. 
  • Gemma 2:2B offre le meilleur équilibre. Il organise les informations en sections significatives (conditions, facteurs de risque, recommandations de soins) tout en conservant un ton fluide.

En bref:

  • Llama 3.2:1B = précision factuelle
  • Gemma 3:1B = dossiers médicaux concis
  • Gemma 2:2B = narration cliniquement pertinente

Même sans ajustement, une curation des données et une conception des invites judicieuses permettent aux petits LLM locaux de produire des comptes rendus cliniques cohérents et pertinents dans leur contexte.


🔒 6. Pourquoi les modèles locaux sont-ils importants?

En utilisant Ollama localement, vous obtenez:

  • Un contrôle total des données — les données du patient ne disparaissent jamais de l'environnement
  • Des performances déterministes — une latence stable sur le processeur
  • Un déploiement léger — fonctionne même sans processeur graphique
  • Une conception modulaire — facile de passer d'un modèle à l'autre ou d'ajuster les invites

Cette configuration est donc idéale pour les hôpitaux, les centres de recherche ou les environnements universitaires qui souhaitent expérimenter en toute sécurité la documentation et la synthèse basées sur l'IA.


🧭 Conclusion

Cette intégration démontre que même les petits modèles locaux, lorsqu'ils sont correctement guidés par des données structurées et des invites claires, peuvent produire des dossiers médicaux utiles et proches de ceux rédigés par des humains.

Au moyen d'IRIS pour la gestion des données, Python pour convertir les données et Ollama pour la génération de texte, nous obtenons un pipeline IA entièrement local et axé sur la confidentialité pour la génération de renseignements cliniques.

1
0 18
Annonce Irène Mykhailova · Mars 14, 2025

Bonjour à tous,

Voici le premier concours de programmation de l'année, et une surprise vous attend ! Lisez la suite !

🏆 Concours de programmation InterSystems IA : Recherche vectorielle, GenAI et agents IA 🏆

Durée : du 17 mars au 6 avril 2025

Prix : 12 000 $ + une chance d'être invité au Sommet mondial 2025 !


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

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

Une histoire d'excellence technique

0
0 66
Article Guillaume Rongier · Nov 6, 2024 7m read

Nous poursuivons cette série d'articles sur les applications LLM et RAG et dans cet article nous traiterons de la partie encadrée en rouge du diagramme ci-dessous:

Lors de la création d'une application RAG, il est tout aussi important de choisir un modèle LLM qui réponde à vos besoins (formation dans le domaine correspondant, coût, rapidité, etc.) que de définir clairement le contexte que vous souhaitez lui fournir. Commençons par définir le terme afin d'être clair sur ce que nous entendons par contexte.

Qu'est-ce que le contexte?

Le contexte fait référence à des informations supplémentaires obtenues à partir d'une source externe, telle qu'une base de données ou un système de recherche, afin de compléter ou d'améliorer les réponses générées par un modèle linguistique. Le modèle linguistique utilise ces informations externes pertinentes pour générer des réponses plus précises et plus détaillées plutôt que de s'appuyer uniquement sur ce qu'il a appris au cours de sa formation. Le contexte permet d'actualiser les réponses et de les aligner sur le sujet spécifique de la requête.

Ce contexte peut provenir d'informations stockées dans une base de données avec un outil similaire à celui montré par notre cher membre de la communauté @José Pereira вйты суе article ou d'informations non structurées sous la forme de fichiers texte que nous alimenterons avec le LLM, ce qui sera le cas que nous allons discuter ici.

Comment générer le contexte de notre application RAG?

Tout d'abord, la chose la plus indispensable est évidemment de disposer de toutes les informations que nous considérons comme pertinentes pour les éventuelles requêtes qui seront formulées à l'encontre de notre demande. Une fois ces informations organisées de manière à être accessibles depuis notre application, nous devons être en mesure d'identifier, parmi tous les documents disponibles dans notre contexte, ceux qui se rapportent à la question spécifique posée par l'utilisateur. Dans notre exemple, nous disposons d'une série de documents PDF (notices de médicaments) que nous voulons utiliser comme contexte possible pour les questions posées par les utilisateurs de notre application.

Cet aspect est essentiel au succès d'une application RAG ; il est tout aussi mauvais pour la confiance d'un utilisateur de répondre par des généralisations et des affirmations vagues typiques d'un LLM que de répondre avec un contexte complètement erroné. C'est là qu'interviennent nos précieuses bases de données vectorielles.

Bases de données vectorielles

Vous avez probablement entendu parler des "bases de données vectorielles" comme s'il s'agissait d'un nouveau type de base de données, telles que les bases de données relationnelles ou documentaires, mais rien n'est plus faux. Les bases de données vectorielles sont des bases de données qui supportent les types de données vectorielles et les opérations qui leur sont liées. Voyons dans le projet associé à l'article comment ce type de données sera représenté:

Voyons maintenant à quoi ressemble un enregistrement:

Bases de données vectorielles... Pourquoi faire?

Comme nous l'avons expliqué dans l'article précédent sur les LLM, l'utilisation des vecteurs est essentielle dans les modèles de langage, car ils permettent de représenter les concepts et les relations entre eux dans un espace multidimensionnel. Dans notre cas, cette représentation multidimensionnelle sera la clé pour identifier les documents de notre contexte qui seront pertinents pour la question posée.

Parfait, nous avons notre base de données vectorielle et les documents qui fourniront le contexte, il ne nous reste plus qu'à enregistrer le contenu de ces documents dans notre base de données, mais.... Avec quels critères?

Modèles pour la vectorisation

Eh bien... il n'est pas nécessaire d'ennuyer notre LLM pour vectoriser nos informations contextuelles, nous pouvons utiliser des modèles de langage plus petits qui conviennent mieux à nos besoins pour cette tâche, tels que les modèles entraînés pour détecter les similarités entre les phrases. Vous pouvez en trouver une myriade dans Hugging Face,  chacun étant entraîné avec un ensemble de données spécifique qui nous permettra d'améliorer la vectorisation de nos données.

Et si cela ne vous convainc pas d'utiliser l'un de ces modèles pour la vectorisation, sachez qu'en général ce type de modèles...

Voyons dans notre exemple comment nous invoquons le modèle choisi pour ces vectorisations:

ifnot os.path.isdir('/app/data/model/'):
    model = sentence_transformers.SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')            
    model.save('/app/data/model/')

Ici, nous téléchargeons le modèle choisi pour notre cas sur notre ordinateur local. Ce mini-LM est multi-langues, nous pourrons donc vectoriser en espagnol et en anglais sans aucun problème.

Chunking

Si vous avez déjà joué avec des modèles de langue, vous avez probablement déjà été confronté au défi du découpage en morceaux - ou "chunking". Qu'est-ce que chunking ? Il s'agit tout simplement de la division d'un texte en fragments plus petits susceptibles de contenir un sens pertinent. Grâce à ce découpage de notre contexte, nous pouvons effectuer des requêtes sur notre base de données vectorielle afin d'extraire les documents de notre contexte qui peuvent être pertinents pour la question posée.

Quels sont les critères de ce découpage ? Il n'y a pas vraiment de critère magique qui nous permette de savoir quelle doit être la longueur de nos morceaux pour qu'ils soient aussi précis que possible. Dans notre exemple, nous utilisons une bibliothèque Python fournie par langchain pour effectuer ce découpage, bien que toute autre méthode ou bibliothèque puisse être utilisée:

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 700,
    chunk_overlap  = 50,
)
path = "/app/data"
loader = PyPDFDirectoryLoader(path)
docs_before_split = loader.load()
docs_after_split = text_splitter.split_documents(docs_before_split)

Comme vous pouvez le constater, la taille choisie est de 700 caractères, avec un chevauchement de 50 pour éviter de couper des mots. Ce sont ces fragments extraits de nos documents que nous allons vectoriser et insérer dans notre base de données.

Ce processus de découpage peut être optimisé à volonté au moyen de la « lemmatisation », qui permet de transformer les mots en leur lemme correspondant (sans les temps des verbes, les pluriels, le genre, etc.) et d'éliminer ainsi une partie du bruit pour la génération du vecteur, mais nous n'allons pas nous étendre sur ce point, vous pouvez voir une explication plus détaillée sur cette page .

Vectorisation des fragments

Très bien, nous avons extrait nos fragments de chacun de nos documents, il est temps de les vectoriser et de les insérer dans notre base de données, jetons un coup d'œil au code pour comprendre comment nous pourrions le faire.

for doc in docs_after_split:
    embeddings = model.encode(doc.page_content, normalize_embeddings=True)
    array = np.array(embeddings)
    formatted_array = np.vectorize('{:.12f}'.format)(array)
    parameters = []
    parameters.append(doc.metadata['source'])
    parameters.append(str(doc.page_content))
    parameters.append(str(','.join(formatted_array)))
    cursorIRIS.execute("INSERT INTO LLMRAG.DOCUMENTCHUNK (Document, Phrase, VectorizedPhrase) VALUES (?, ?, TO_VECTOR(?,DECIMAL))", parameters)
connectionIRIS.commit()

Comme vous pouvez le constater, nous allons suivre les étapes suivantes: 

  1. Nous parcourons la liste de tous les fragments obtenus à partir de tous les documents qui vont former notre contexte.
  2. Pour chaque fragment, nous vectorisons le texte (en utilisant la bibliothèque sentence_transformers). 
  3. Nous créons un tableau à l'aide de la bibliothèque numpy avec le vecteur formaté et le transformons en chaîne de caractères.
  4. Nous enregistrons les informations du document avec son vecteur associé dans notre base de données. Comme vous pouvez le voir, nous exécutons la commande TO_VECTOR qui transformera la chaîne du vecteur que nous avons passé au format approprié.

Conclusion

Dans cet article nous avons vu la nécessité d'avoir une base de données vectorielle pour la création du contexte nécessaire dans notre application RAG, nous avons aussi vu comment découper et vectoriser l'information de notre contexte pour son enregistrement dans cette base de données.

Dans le prochain article, nous verrons comment interroger notre base de données vectorielles à partir de la question que l'utilisateur envoie au modèle LLM et comment, à l'aide d'une recherche de similarité, nous assemblerons le contexte que nous transmettrons au modèle. Ne manquez pas cet article!

0
0 460
Article Sylvain Guilbaud · Oct 25, 2024 7m read

Dans le paysage actuel des données, les activités commerciales sont confrontées à différents défis. L'un d'entre eux consiste à réaliser des analyses à partir d'une couche de données unifiée et harmonisée, accessible à tous les utilisateurs. Une couche capable de fournir les mêmes réponses aux mêmes questions, indépendamment du dialecte ou de l'outil utilisé. La plate-forme de données InterSystems IRIS répond à cette question en ajoutant la solution 'Adaptive Analytics' (Analyse adaptative) qui peut fournir cette couche sémantique unifiée. Il y a beaucoup d'articles dans DevCommunity sur l'utilisation de cette couche sémantique via des outils décisionnels. Cet article couvrira la partie concernant la façon de l'utiliser avec l'IA et également la façon d'obtenir des informations en retour. Allons-y étape par étape...

Qu'est-ce que la solution 'Adaptive Analytics'?

Vous pouvez facilement trouver une définition sur le site web de la Communauté de développeurs. En quelques mots, elle peut fournir des données sous une forme structurée et harmonisée à divers outils de votre choix pour une utilisation et une analyse ultérieures. Elle fournit les mêmes structures de données à différents outils décisionnels. Mais... elle peut également fournir les mêmes structures de données à vos outils IA/ML!

Adaptive Analytics a un composant supplémentaire appelé AI-Link qui construit ce pont entre l'IA et d'informatique décisionnelle.

Qu'est-ce que AI-Link exactement?

Il s'agit d'un composant Python conçu pour permettre une interaction programmatique avec la couche sémantique dans le but de rationaliser les étapes clés du flux de travail de l'apprentissage automatique (ML) (par exemple, l'ingénierie des fonctionnalités).

Avec AI-Link, vous pouvez:

  • accéder de manière programmatique aux fonctionnalités de votre modèle de données analytiques;
  • faire des requêtes, explorer les dimensions et les mesures;
  • alimenter des pipelines de ML; ... et renvoyer les résultats vers votre couche sémantique pour qu'ils soient à nouveau utilisés par d'autres (par exemple, par le biais de Tableau ou d'Excel).

Comme il s'agit d'une bibliothèque Python, elle peut être utilisée dans n'importe quel environnement Python. Y compris les Notebooks. Dans cet article, je vais donner un exemple simple pour atteindre une solution d'analyse adaptative à partir d'un Notebook Jupyter avec l'aide d'AI-Link.

Voici le référentiel git qui aura le Notebook complet à titre d'exemple : https://github.com/v23ent/aa-hands-on

**Conditions préalables **

Les étapes suivantes supposent que vous ayez rempli les conditions préalables ci-dessous:

  1. La solution 'Adaptive Analytics' est en place et fonctionne (avec IRIS Data Platform en tant qu'entrepôt de données).
  2. Jupyter Notebook est opérationnel
  3. La connexion entre 1. et 2. peut être établie

Étape 1: Configuration

Tout d'abord, installons les composants nécessaires dans notre environnement. Ainsi, nous téléchargerons quelques paquets nécessaires au bon déroulement des étapes suivantes. 'atscale' - c'est notre paquetage principal pour se connecter 'prophet' - c'est le paquet dont nous aurons besoin pour faire des prédictions.

pip install atscale prophet

Ensuite, nous devons importer des classes clés représentant certains concepts clés de notre couche sémantique. Client - c'est la classe que nous utiliserons pour établir une connexion avec Adaptive Analytics; Project - c'est la classe qui représente les projets à l'intérieur d'Adaptive Analytics; DataModel - c'est la classe qui représentera notre cube virtuel;

from atscale.client import Client
from atscale.data_model import DataModel
from atscale.project import Project
from prophet import Prophet
import pandas as pd

Étape 2: Connexion

Maintenant, nous devrions être prêts à établir une connexion avec notre source de données.

client = Client(server='http://adaptive.analytics.server', username='sample')
client.connect()

Continuez et spécifiez les détails de connexion de votre instance d'Adaptive Analytics. Lorsque l'on vous demande l'organisation, répondez dans la boîte de dialogue et entrez votre mot de passe de l'instance AtScale.

Une fois la connexion établie, vous devrez sélectionner votre projet dans la liste des projets publiés au serveur. Vous obtiendrez la liste des projets sous la forme d'une invite interactive et la réponse devrait être l'identifiant entier du projet. Le modèle de données est ensuite sélectionné automatiquement s'il est le seul.

project = client.select_project()   
data_model = project.select_data_model()

Étape 3: Explorez votre jeu de données

Il existe un certain nombre de méthodes préparées par AtScale dans la bibliothèque de composants AI-Link. Elles permettent d'explorer votre catalogue de données, d'interroger les données et même d'ingérer des données en retour. La documentation d'AtScale a une référence API complète décrivant tout ce qui est disponible. Voyons d'abord quel est notre jeu de données en appelant quelques méthodes de data_model :

data_model.get_features()
data_model.get_all_categorical_feature_names()
data_model.get_all_numeric_feature_names()

Le résultat devrait ressembler à ceci image

Après avoir examiné un peu la situation, nous pouvons interroger les données qui nous intéressent à l'aide de la méthode 'get_data'. Elle renverra un pandas DataFrame contenant les résultats de la requête.

df = data_model.get_data(feature_list = ['Country','Region','m_AmountOfSale_sum'])
df = df.sort_values(by='m_AmountOfSale_sum')
df.head()

Ce qui affichera votre trame de données: image

Préparons un ensemble de données et affichons-le rapidement sur le graphique

import matplotlib.pyplot as plt

# Nous enregistrons des ventes pour chaque date
dataframe = data_model.get_data(feature_list = ['Date','m_AmountOfSale_sum'])

#  Création d'un graphique linéaire
plt.plot(dataframe['Date'], dataframe['m_AmountOfSale_sum'])

# Ajout des étiquettes et d'un titre
plt.xlabel('Days')
plt.ylabel('Sales')
plt.title('Daily Sales Data')

# Affichage du graphique
plt.show()

Résultat: image

Étape 4: Prédiction

La prochaine étape consistera à tirer profit du pont AI-Link - faisons quelques prédictions simples!

# Chargement des données historiques pour l'entraînement du modèle
data_train = data_model.get_data(
    feature_list = ['Date','m_AmountOfSale_sum'],
    filter_less = {'Date':'2021-01-01'}
    )
data_test = data_model.get_data(
    feature_list = ['Date','m_AmountOfSale_sum'],
    filter_greater = {'Date':'2021-01-01'}
    )

Nous disposons ici de 2 jeux de données différents: pour entraîner notre modèle et pour le tester.

# Pour l'outil que nous avons choisi pour faire la prédiction 'Prophète' ,nous devrons spécifier 2 colonnes: 'ds' et 'y'
data_train['ds'] = pd.to_datetime(data_train['Date'])
data_train.rename(columns={'m_AmountOfSale_sum': 'y'}, inplace=True)
data_test['ds'] = pd.to_datetime(data_test['Date'])
data_test.rename(columns={'m_AmountOfSale_sum': 'y'}, inplace=True)

# Initialisation et ajustement du modèle Prophet
model = Prophet()
model.fit(data_train)

Et puis nous créons une autre trame de données pour accueillir notre prédiction et l'afficher sur le graphique

# Création d'une trame de données prochaine pour la prévision
future = pd.DataFrame()
future['ds'] = pd.date_range(start='2021-01-01', end='2021-12-31', freq='D')

# Prédictions
forecast = model.predict(future)
fig = model.plot(forecast)
fig.show()

Résultat: image

Étape 5: Réécriture

Une fois notre prédiction en place, nous pouvons la renvoyer à l'entrepôt de données et ajouter un agrégat à notre modèle sémantique afin de la refléter pour d'autres utilisateurs. La prédiction serait disponible via n'importe quel autre outil décisionnel pour les analystes décisionnels et les utilisateurs commerciaux. La prédiction elle-même sera placée dans notre entrepôt de données et y sera stockée.

from atscale.db.connections import Iris
db = Iris(
    username,
    host,
    namespace,
    driver,
    schema, 
    port=1972,
    password=None, 
    warehouse_id=None
    )

data_model.writeback(dbconn=db,
                    table_name= 'SalesPrediction',
                    DataFrame = forecast)

data_model.create_aggregate_feature(dataset_name='SalesPrediction',
                                    column_name='SalesForecasted',
                                    name='sum_sales_forecasted',
                                    aggregation_type='SUM')

Fin

C'est ça! Bonne chance avec vos prédictions!

0
0 86
Article Lorenzo Scalese · Août 22, 2024 5m read

Dans l'article précédent, nous avons présenté l'application d[IA]gnosis développée pour soutenir le codage des diagnostics CIM-10. Dans le présent article, nous verrons comment InterSystems IRIS for Health nous fournit les outils nécessaires à la génération de vecteurs à partir de la liste des codes CIM-10 au moyen d'un modèle de langage pré-entraîné, à leur stockage et à la recherche ultérieure de similitudes sur tous ces vecteurs générés.

Introduction

L'une des principales fonctionnalités apparues avec le développement des modèles d'IA est ce que nous appelons RAG (Retrieval-Augmented Generation), qui nous permet d'améliorer les résultats des modèles LLM en incorporant un contexte au modèle. Dans notre exemple, le contexte est donné par l'ensemble des diagnostics CIM-10, et pour les utiliser, nous devons d'abord les vectoriser.

Comment vectoriser notre liste de diagnostics?

SentenceTransformers et Embedded Python

Pour la génération de vecteurs, nous avons utilisé la bibliothèque Python SentenceTransformers qui facilite grandement la vectorisation de texte libre à partir de modèles pré-entraînés. Extrait de leur propre site web:

Le module "Sentence Transformers" (alias SBERT) SBERT) est un module Python intégré qui permet d'accéder, d'utiliser et d'entraîner des modèles incorporés de texte et d'image à la pointe de la technologie. Il peut être utilisé pour calculer des embeddings à l'aide de modèles Sentence Transformer (quickstart) ou pour calculer des scores de similarité à l'aide de modèles Cross-Encoder (quickstart). Cela ouvre la voie à un large éventail d'applications, notamment la  recherche sémantique, la  similarité textuelle sémantique, et l' extraction de paraphrases.

Parmi tous les modèles développés par la communauté SentenceTransformers, nous avons trouvé BioLORD-2023-M, un modèle pré-entraîné qui génère des vecteurs de 786 dimensions.

Ce modèle a été entraîné à l'aide de BioLORD, une nouvelle stratégie de pré-entraînement visant à produire des représentations significatives pour les expressions cliniques et les concepts biomédicaux.

Les méthodologies de pointe maximisent la similarité de la représentation des noms se référant au même concept et évitent l'effondrement grâce à l'apprentissage contrastif. Cependant, les noms biomédicaux n'étant pas toujours explicites, il en résulte parfois des représentations non sémantiques.

BioLORD résout ce problème en fondant ses représentations de concepts sur des définitions, ainsi que sur de courtes descriptions dérivées d'un graphe de connaissances multirelationnel composé d'ontologies biomédicales. Grâce à cette base, notre modèle produit des représentations de concepts plus sémantiques qui correspondent mieux à la structure hiérarchique des ontologies. BioLORD-2023 établit un nouvel état de l'art en matière de similarité textuelle pour les expressions cliniques (MedSTS) et les concepts biomédicaux (EHR-Rel-B).

Comme vous pouvez le voir dans sa propre définition, ce modèle est pré-entraîné avec des concepts médicaux qui seront utiles lors de la vectorisation de nos codes ICD-10 et du texte brut.

Pour notre projet, nous téléchargerons ce modèle afin d'accélérer la création des vecteurs:

if not os.path.isdir('/shared/model/'):
    model = sentence_transformers.SentenceTransformer('FremyCompany/BioLORD-2023-M')            
    model.save('/shared/model/')

Lorsque nous sommes à notre ordinateur, nous pouvons introduire les textes à vectoriser dans des listes afin d'accélérer le processus. Voyons comment vectoriser les codes CIM-10 que nous avons précédemment enregistrés dans notre classe ENCODER.Object.Codes.

st = iris.sql.prepare("SELECT TOP 50 CodeId, Description FROM ENCODER_Object.Codes WHERE VectorDescription is null ORDER BY ID ASC ")
resultSet = st.execute()
df = resultSet.dataframe()

if (df.size > 0): model = sentence_transformers.SentenceTransformer("/shared/model/") embeddings = model.encode(df['description'].tolist(), normalize_embeddings=True)

df[<span class="hljs-string">'vectordescription'</span>] = embeddings.tolist()

stmt = iris.sql.prepare(<span class="hljs-string">"UPDATE ENCODER_Object.Codes SET VectorDescription = TO_VECTOR(?,DECIMAL) WHERE CodeId = ?"</span>)
<span class="hljs-keyword">for</span> index, row <span class="hljs-keyword">in</span> df.iterrows():
    rs = stmt.execute(str(row[<span class="hljs-string">'vectordescription'</span>]), row[<span class="hljs-string">'codeid'</span>])

else: flagLoop = False

Comme vous pouvez le voir, nous extrayons d'abord les codes stockés dans notre table de codes CIM-10 que nous n'avons pas encore vectorisés mais que nous avons enregistrés dans une étape précédente après les avoir extraits du fichier CSV, puis nous extrayons la liste des descriptions à vectoriser et en utilisant la bibliothèque Python sentence_transformers nous allons récupérer notre modèle et générer les embeddings associés.

Enfin, nous mettons à jour le code CIM-10 avec la description vectorisée en exécutant la commande UPDATE. Comme vous pouvez le voir, le résultat retourné par le modèle est vectorisé par la commande TO_VECTOR de SQL dans IRIS.

Utilisation dans IRIS

Très bien, nous avons déjà notre code Python, il nous suffit donc de l'inclure dans une classe qui étend Ens.BusinessProcess et de l'inclure dans notre production, puis de le connecter au Business Service chargé de récupérer le fichier CSV et le tour est joué!

Voyons à quoi ressemblera ce code dans notre production:

Comme vous pouvez le voir, nous avons notre service d'entreprise avec l'adaptateur EnsLib.File.InboundAdapter qui nous permettra de collecter le fichier de code et de le rediriger vers notre processus d'entreprise dans lequel nous effectuerons toutes les opérations de vectorisation et de stockage, ce qui se traduira par un ensemble d'enregistrements comme le suivant:

Notre application est maintenant prête à rechercher des correspondances possibles avec les textes que nous lui transmettons!

Dans le prochain article...

Dans le prochain article, nous montrerons comment le front-end de l'application développée en Angular 17 est intégré à notre production dans IRIS for Health et comment IRIS reçoit les textes à analyser, les vectorise et recherche des similitudes dans la table des codes CIM-10.

À ne pas manquer!

0
1 48
Article Lorenzo Scalese · Août 20, 2024 8m read

Avec l'introduction des types de données vectorielles et de la fonctionnalité de recherche vectorielle dans IRIS, tout un univers de possibilités de développement d'applications s'ouvre et un exemple de ces applications est celui que j'ai récemment vu publié dans un appel d'offres public du Ministère régional de la santé de Valence demandant un outil d'aide au codage de la CIM-10 à l'aide de modèles d'IA.

Comment pourrions-nous mettre en œuvre une application similaire à celle demandée? Voyons ce dont nous aurions besoin:

  1. Liste des codes CIM-10, que nous utiliserons comme contexte de notre application RAG pour rechercher des diagnostics dans les textes bruts.
  2. Un modèle entraîné pour vectoriser les textes dans lesquels nous allons rechercher des équivalences dans les codes CIM-10.
  3. Les bibliothèques Python nécessaires à l'ingestion et à la vectorisation des codes CIM-10 et des textes.
  4. Un front-end convivial qui prend en charge les textes sur lesquels nous recherchons des diagnostics possibles.
  5. L'orchestration des requêtes reçues du front-end.

Que propose IRIS pour répondre à ces besoins?

  1. Importation CSV, soit en utilisant la fonctionnalité RecordMapper, soit directement en utilisant Embedded Python.
  2. Embedded Python nous permet d'implémenter le code Python nécessaire pour générer les vecteurs à l'aide du modèle sélectionné.
  3. Publication d'API REST à invoquer à partir de l'application front-end.
  4. Les productions d'interopérabilité qui permettent le suivi des informations au sein d'IRIS.

Il ne reste plus qu'à voir l'exemple développé:

d[IA]gnosis

Associé à cet article vous avez accès à l'application développée, les prochains articles présenteront en détail la mise en œuvre de chacune des fonctionnalités, de l'utilisation du modèle au stockage des vecteurs, en passant par l'utilisation des recherches vectorielles.

Passons en revue l'application:

Importation des codes CIM-10

L'écran de configuration indique le format que doit suivre le fichier CSV contenant les codes CIE-10 que nous allons importer. Le processus de chargement et de vectorisation consomme beaucoup de temps et de ressources, c'est pourquoi le déploiement du conteneur Docker configure non seulement la mémoire RAM utilisable par Docker mais aussi la mémoire disque au cas où les besoins dépasseraient la RAM allouée:

# iris  iris:    init:true    container_name:iris    build:      context:.      dockerfile:iris/Dockerfile    ports:      -52774:52773      -51774:1972    volumes:    -./shared:/shared    environment:    -ISC_DATA_DIRECTORY=/shared/durable    command:--check-capsfalse--ISCAgentfalse    mem_limit:30G    memswap_limit:32G

Le fichier contenant les codes ICD-10 est disponible dans le chemin du projet /shared/cie10/icd10.csv, une fois que 100% est atteint, l'application sera prête à être utilisée.

Dans notre application, nous avons défini deux fonctionnalités différentes pour le codage des diagnostics, l'une basée sur les messages HL7 reçus dans le système et l'autre basée sur des textes bruts.

Saisie des diagnostics via HL7

Le projet contient une série de messages HL7 prêts à être testés, il suffit de copier le fichier /shared/hl7/messagesa01_en.hl7 dans le dossier /shared/HL7In et la production associée en extraira le diagnostic pour l'afficher dans l'application web:

L'écran de demande de diagnostic permet de voir tous les diagnostics reçus via la messagerie HL7. Pour leur codage CIM-10, il suffit de cliquer sur la loupe pour afficher une liste des codes CIM-10 les plus proches du diagnostic reçu:

Une fois sélectionné, le diagnostic et le code CIM-10 associé apparaissent dans la liste. En cliquant sur le bouton avec l'icône de l'enveloppe, un message est généré en utilisant l'original et en incluant le nouveau code sélectionné dans le segment du diagnostic:

MSH|^~\&|HIS|HULP|EMPI||||ADT^A08|592956|P|2.5.1
EVN|A01|
PID|||1556655212^^^SERMAS^SN~922210^^^HULP^PI||GARCÍA PÉREZ^JUAN^^^||20150403|M|||PASEO PEDRO ÁLVAREZ 1951 CENTRO^^LEGANÉS^MADRID^28379^SPAIN||555283055^PRN^^JUAN.GARCIA@YAHOO.COM|||||||||||||||||N|
PV1||N
DG1|1||O10.91^Hypertension préexistante non spécifiée compliquant la grossesse^CIE10-ES|Hypertension gestationnelle||A||

Ce message se trouve dans le chemin /shared/HL7Out

Captures d'écran de diagnostic en texte brut

Dans l'option Analyseur de texte, l'utilisateur peut inclure un texte brut sur lequel un processus d'analyse sera effectué. L'application recherchera des tuples de 3 mots lemmatisés (en éliminant les articles, les pronoms et d'autres mots peu pertinents). Une fois analysé, le système affichera le texte pertinent souligné et les diagnostics possibles localisés:

Une fois l'analyse effectuée, elle peut être consultée à tout moment à partir de l'historique de l'analyse.

Historique des analyses

Toutes les analyses effectuées sont enregistrées et peuvent être consultées à tout moment, en visualisant tous les codes CIM-10 possibles:

Dans le prochain article...

Nous verrons comment, en utilisant Embedded Python, nous utilisons un modèle LLM spécifique pour la vectorisation des codes CIM-10 qui nous serviront de contexte et des textes bruts.

Si vous avez des questions ou des suggestions, n'hésitez pas à écrire un commentaire dans l'article.

Avec l'introduction des types de données vectorielles et de la fonctionnalité de recherche vectorielle dans IRIS, tout un univers de possibilités de développement d'applications s'ouvre et un exemple de ces applications est celui que j'ai récemment vu publié dans un appel d'offres public du Ministère régional de la santé de Valence demandant un outil d'aide au codage de la CIM-10 à l'aide de modèles d'IA.

Comment pourrions-nous mettre en œuvre une application similaire à celle demandée? Voyons ce dont nous aurions besoin:

  1. Liste des codes CIM-10, que nous utiliserons comme contexte de notre application RAG pour rechercher des diagnostics dans les textes bruts.
  2. Un modèle entraîné pour vectoriser les textes dans lesquels nous allons rechercher des équivalences dans les codes CIM-10.
  3. Les bibliothèques Python nécessaires à l'ingestion et à la vectorisation des codes CIM-10 et des textes.
  4. Un front-end convivial qui prend en charge les textes sur lesquels nous recherchons des diagnostics possibles.
  5. L'orchestration des requêtes reçues du front-end.

Que propose IRIS pour répondre à ces besoins?

  1. Importation CSV, soit en utilisant la fonctionnalité RecordMapper, soit directement en utilisant Embedded Python.
  2. Embedded Python nous permet d'implémenter le code Python nécessaire pour générer les vecteurs à l'aide du modèle sélectionné.
  3. Publication d'API REST à invoquer à partir de l'application front-end.
  4. Les productions d'interopérabilité qui permettent le suivi des informations au sein d'IRIS.

Il ne reste plus qu'à voir l'exemple développé:

d[IA]gnosis

Associé à cet article vous avez accès à l'application développée, les prochains articles présenteront en détail la mise en œuvre de chacune des fonctionnalités, de l'utilisation du modèle au stockage des vecteurs, en passant par l'utilisation des recherches vectorielles.

Passons en revue l'application:

Importation des codes CIM-10

L'écran de configuration indique le format que doit suivre le fichier CSV contenant les codes CIE-10 que nous allons importer. Le processus de chargement et de vectorisation consomme beaucoup de temps et de ressources, c'est pourquoi le déploiement du conteneur Docker configure non seulement la mémoire RAM utilisable par Docker mais aussi la mémoire disque au cas où les besoins dépasseraient la RAM allouée:

# iris  iris:    init:true    container_name:iris    build:      context:.      dockerfile:iris/Dockerfile    ports:      -52774:52773      -51774:1972    volumes:    -./shared:/shared    environment:    -ISC_DATA_DIRECTORY=/shared/durable    command:--check-capsfalse--ISCAgentfalse    mem_limit:30G    memswap_limit:32G

Le fichier contenant les codes ICD-10 est disponible dans le chemin du projet /shared/cie10/icd10.csv, une fois que 100% est atteint, l'application sera prête à être utilisée.

Dans notre application, nous avons défini deux fonctionnalités différentes pour le codage des diagnostics, l'une basée sur les messages HL7 reçus dans le système et l'autre basée sur des textes bruts.

Saisie des diagnostics via HL7

Le projet contient une série de messages HL7 prêts à être testés, il suffit de copier le fichier /shared/hl7/messagesa01_en.hl7 dans le dossier /shared/HL7In et la production associée en extraira le diagnostic pour l'afficher dans l'application web:

L'écran de demande de diagnostic permet de voir tous les diagnostics reçus via la messagerie HL7. Pour leur codage CIM-10, il suffit de cliquer sur la loupe pour afficher une liste des codes CIM-10 les plus proches du diagnostic reçu:

Une fois sélectionné, le diagnostic et le code CIM-10 associé apparaissent dans la liste. En cliquant sur le bouton avec l'icône de l'enveloppe, un message est généré en utilisant l'original et en incluant le nouveau code sélectionné dans le segment du diagnostic:

MSH|^~\&|HIS|HULP|EMPI||||ADT^A08|592956|P|2.5.1
EVN|A01|
PID|||1556655212^^^SERMAS^SN~922210^^^HULP^PI||GARCÍA PÉREZ^JUAN^^^||20150403|M|||PASEO PEDRO ÁLVAREZ 1951 CENTRO^^LEGANÉS^MADRID^28379^SPAIN||555283055^PRN^^JUAN.GARCIA@YAHOO.COM|||||||||||||||||N|
PV1||N
DG1|1||O10.91^Hypertension préexistante non spécifiée compliquant la grossesse^CIE10-ES|Hypertension gestationnelle||A||

Ce message se trouve dans le chemin /shared/HL7Out

Captures d'écran de diagnostic en texte brut

Dans l'option Analyseur de texte, l'utilisateur peut inclure un texte brut sur lequel un processus d'analyse sera effectué. L'application recherchera des tuples de 3 mots lemmatisés (en éliminant les articles, les pronoms et d'autres mots peu pertinents). Une fois analysé, le système affichera le texte pertinent souligné et les diagnostics possibles localisés:

Une fois l'analyse effectuée, elle peut être consultée à tout moment à partir de l'historique de l'analyse.

Historique des analyses

Toutes les analyses effectuées sont enregistrées et peuvent être consultées à tout moment, en visualisant tous les codes CIM-10 possibles:

Dans le prochain article...

Nous verrons comment, en utilisant Embedded Python, nous utilisons un modèle LLM spécifique pour la vectorisation des codes CIM-10 qui nous serviront de contexte et des textes bruts.

Si vous avez des questions ou des suggestions, n'hésitez pas à écrire un commentaire dans l'article.

0
1 40
Discussion Irène Mykhailova · Juil 26, 2024

Bonjour la communauté !

Comme vous le savez peut-être, notre IA pour la communauté des développeurs est sortie depuis plus d'un mois 🎉 Nous espérons que vous avez été suffisamment curieux pour l'essayer 😁 Si ce n'est pas encore le cas, n'hésitez pas à le faire ! Quoi qu'il en soit, comme elle est encore en version bêta, nous sommes très intéressés par ce que vous en pensez, et nous avons hâte de connaître vos réflexions et vos expériences.

 

Comme nous apprécions votre temps et vos efforts, nous offrirons un joli prix à un membre de la communauté choisi au hasard qui partagera ses réflexions. Pour participer à ce tirage au sort, vous devez suivre les consignes suivantes :

1
0 74
Article Guillaume Rongier · Juin 3, 2024 4m read

L'intelligence artificielle a un potentiel transformateur pour générer de la valeur et des informations à partir des données. Alors que nous nous dirigeons vers un univers où presque toutes les applications seront pilotées par l'IA, les développeurs qui créent ces applications auront besoin des outils adéquats pour créer des expériences à partir de ces applications. C'est pourquoi nous sommes heureux d'annoncer que la recherche vectorielle a été ajoutée à la plate-forme de données InterSystems IRIS.

0
0 64
Article Lorenzo Scalese · Avr 25, 2024 7m read

En tant que modèle linguistique d'IA, ChatGPT est capable d'effectuer une variété de tâches telles que traduire, écrire des chansons, répondre à des questions de recherche et même générer du code informatique. Avec ses capacités impressionnantes, ChatGPT est rapidement devenu un outil populaire pour diverses applications, des chatbots à la création de contenu.
Mais malgré ses capacités avancées, ChatGPT n'est pas en mesure d'accéder à vos données personnelles. Mais malgré ses capacités avancées, ChatGPT n'est pas en mesure d'accéder à vos données personnelles. Ainsi, dans cet article, je vais démontrer les étapes suivantes pour construire une IA ChatGPT personnalisée en utilisant le LangChain Framework:

  • Étape 1: Chargement du document 

  • Étape 2: Division du document en blocs

  • Étape 3: Utilisation de l'incorporation pour des blocs de données et leur conversion en vecteurs

  • Étape 4: Enregistrement des données dans la base de données de vecteurs

  • Étape 5: Obtention des données (question) de l'utilisateur et leur the intégration

  • Étape 6: Connexion à VectorDB et recherche sémantique

  • Étape 7: Récupération des réponses pertinentes basées sur les requêtes de l'utilisateur et leur envoi au LLM(ChatGPT)

  • Étape 8: Obtention d'une réponse de la part de LLM et renvoi de celle-ci à l'utilisateur

  REMARQUE : Veuillez lire mon article précédent LangChain – Unleashing the full potential of LLMs (LangChain - Libération du plein potentiel des LLM) pour obtenir plus de détails sur LangChain et sur la façon d'obtenir la clé API OpenAI

Alors, commençons
     

Étape 1: Chargement du document 

Tout d'abord, il faut charger le document. Nous allons donc importer PyPDFLoader pour le document PDF 

ClassMethod SavePDF(filePath) [ Language = python ]
{
#pour un fichier PDF, il faut importer PyPDFLoader à partir du framework langchainfrom langchain.document_loaders import PyPDFLoader
# pour un fichier CSV, il faut importer csv_loader# pour un fichier Doc, il faut importer UnstructuredWordDocumentLoader# Pour le document texte, il faut importer TextLoader#importation de l'os pour définir la variable d'environnementimport os
#attribution de la clé API OpenAI à une variable d'environnement 
os.environ['OPENAI_API_KEY'] = "apiKey"#Init du lanceur
loader = PyPDFLoader(filePath)   
#Chargement du document 
documents = loader.load()
return documents
}

Étape 2: Division du document en blocs

Les modèles linguistiques sont souvent limités par la quantité de texte qui peut leur être transmise. Il est donc nécessaire de les diviser en blocs moins volumineux. LangChain fournit plusieurs utilitaires pour ce faire.

L'utilisation d'un séparateur de texte (Text Splitter) peut également contribuer à améliorer les résultats des recherches dans les répertoires de vecteurs, car, par exemple, des blocs moins volumineux ont parfois plus de chances de correspondre à une requête. Tester différentes tailles de blocs (et leur chevauchement) est un exercice intéressant pour adapter les résultats à votre cas d'utilisation.

ClassMethod splitText(documents) [ Language = python ]
{
#Afin de diviser le document, il faut importer RecursiveCharacterTextSplitter du framework Langchain  from langchain.text_splitter import RecursiveCharacterTextSplitter
#Init du séparateur de texte, définition de la taille des blocs (1000) et du chevauchement. = 0
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
#Division du document en plusieurs blocs
texts = text_splitter.split_documents(documents)
return texts
}

Étape 3: Utilisation d'incorporation pour des blocs de données et leur conversion en vecteurs

Les incorporations (embeddings) de texte sont le cœur et l'âme des outils de Large Language Operations.. Techniquement, nous pouvons travailler avec des modèles linguistiques en langage naturel, mais le stockage et l'extraction du langage naturel sont très inefficaces. 

Pour améliorer l'efficacité, il faut transformer les données textuelles en formes vectorielles. Il existe des modèles ML dédiés à la création d'intégrations à partir de textes. Les textes sont convertis en vecteurs multidimensionnels. Une fois ces données incorporées, nous pouvons les regrouper, les trier, les rechercher, etc. Nous pouvons calculer la distance entre deux phrases pour connaître leur degré de parenté. Et le plus intéressant, c'est que ces opérations ne se limitent pas à des mots-clés comme dans les recherches traditionnelles dans les bases de données, mais capturent plutôt la proximité sémantique de deux phrases. Cela rend le système beaucoup plus puissant, grâce à l'apprentissage automatique.
 

Les modèles d'incorporation de texte reçoivent la saisie de texte et renvoient une liste de flottants (embeddings), qui sont la représentation numérique du texte saisi. Les embeddings permettent d'extraire des informations d'un texte. Ces informations peuvent ensuite être utilisées, par exemple, pour calculer les similitudes entre des textes (par exemple, des résumés de films).

Les modèles d'incorporation de texte reçoivent la saisie de texte et renvoient une liste de flottants

    ClassMethod getEmbeddings(query) [ Language = python ]
    {
    #Obtention d'un modèle d'embeddings à partir du framework Langchainfrom langchain.embeddings import OpenAIEmbeddings
    #Definition d'embedding
    embedding = OpenAIEmbeddings()
    return embedding
    }
    

Étape 4: Enregistrement des données dans la base de données de vecteurs

    ClassMethod saveDB(texts,embedding) [ Language = python ]
    {
    #Obtention de la base de données Chroma à partir de langchainfrom langchain.vectorstores import Chroma      
    # Incorporation et stockage des textes# La fourniture d'un répertoire persistant (persist_directory) permet de stocker les embeddings sur le disque# par exemple dans le dossier myData du chemin d'accès à l'application en cours
    persist_directory = "myData"
    vectordb = Chroma.from_documents(documents=texts, embedding=embedding, persist_directory=persist_directory)
    #sauvegarde du document au niveau local
    vectordb.persist()
    vectordb = None
    }
    

Étape 5: Obtention des données (question) de l'utilisateur et leur the intégration

    ClassMethod getVectorData(query) [ Language = python ]
    {
    #REMARQUE: Il faudrait avoir le même embedding utilisée lorsque nous avons sauvegardé des donnéesfrom langchain.embeddings import OpenAIEmbeddings
    #obtention d'embeddings
    embedding = OpenAIEmbeddings()
    #saisie des données de l'utilisateur (paramètre)
    query = query
    #La suite du code...

Étape 6: Connexion à VectorDB et recherche sémantique

#code continue....     from langchain.vectorstores import Chroma
 persist_directory = "myData"## À présent, il est possible de charger la base de données persistante à partir du disque et de l'utiliser comme d'habitude. 
 vectordb = Chroma(persist_directory=persist_directory, embedding_function=embedding)
 return vectordb
 }

Étape 7: Récupération des réponses pertinentes basées sur les requêtes de l'utilisateur et leur envoi au LLM(ChatGPT)

La mémoire de conversation est la façon dont un chatbot peut répondre à de multiples requêtes à la manière d'un chat. Elle assure une conversation cohérente et, sans elle, chaque requête serait traitée comme une entrée totalement indépendante, sans tenir compte des interactions passées.

LLM avec et sans une mémoire de conversation. Les cases bleues représentent les assistances de l'utilisateur et les cases grises représentent les réponses des LLM. Sans mémoire de conversation (à droite), les LLM ne peuvent pas répondre en utilisant la connaissance des interactions précédentes.

La mémoire permet le  Large Language Model (LLM) de se souvenir des interactions précédentes avec l'utilisateur. Par défaut, les LLMs sont  sans état — ce qui signifie que chaque requête entrante est traitée indépendamment des autres interactions. La seule chose qui existe pour un agent sans état est la saisie actuelle, rien d'autre.


Le modèle ConversationalRetrievalChain est un modèle d'IA conversationnelle conçu pour extraire des réponses pertinentes sur la base des requêtes de l'utilisateur. Il fait partie de la technologie de l'équipe de Langchain. Le modèle utilise une approche basée sur la récupération, où il recherche des réponses préexistantes dans une base de données pour trouver la réponse la plus appropriée à une requête donnée. Le modèle est entraîné sur un grand ensemble de conversations pour apprendre les schémas et le contexte afin de fournir des réponses précises et utiles.

ClassMethod retriveResponse(vectordb) [ Language = python ]
{
from langchain.llms import OpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
#La mémoire de conversation est la façon dont un chatbot peut répondre à de multiples requêtes.
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
#Le ConversationalRetrievalChain est un modèle d'IA conversationnelle conçu pour extraire des réponses pertinentes sur la base des requêtes de l'utilisateur.
qa = ConversationalRetrievalChain.from_llm(OpenAI(temperature=0), vectordb.as_retriever(), memory=memory)
return qa
}


Étape 8: Obtention d'une réponse de la part de LLM et renvoi de celle-ci à l'utilisateur 

ClassMethod getAnswer(qa) [ Language = python ]
{
#Obtention d'une réponse de la part de LLM et renvoi de celle-ci à l'utilisateur
getAnswer = qa.run(query)
return getAnswer
}

Pour plus de détails et de fonctionnalités, veuillez consulter mon application irisChatGPT 

Vidéo connexe

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/h9JoqbUKFBk" title="YouTube video player" width="560"></iframe>

Je vous remercie

1
0 313
Article Sylvain Guilbaud · Avr 15, 2024 6m read

Comme vous avez pu le constater dans les dernières publications de la communauté, InterSystems IRIS inclut depuis la version 2024.1 la possibilité d'inclure des types de données vectorielles dans sa base de données et sur la base de ce type de données, des recherches vectorielles ont été mises en œuvre. Eh bien, ces nouvelles fonctionnalités m'ont rappelé l'article que j'ai publié il y a quelque temps et qui était basé sur la reconnaissance faciale utilisant Embedded Python.

Introduction

0
0 64
Annonce Irène Mykhailova · Avr 10, 2024

Salut la Communauté,

Nous avons des nouvelles excitantes ! Le nouveau concours de programmation en ligne InterSystems dédié à l'IA générative, à la recherche de vecteurs et au Machine Learning démarre très bientôt !

🏆 Concours InterSystems Vector Search, GenAI et ML 🏆

Durée : avril 22 - mai 19, 2024

Prix : $14,000


0
1 71
Article Sylvain Guilbaud · Mars 22, 2024 5m read

L'invention et la vulgarisation des grands modèles de langage (tels que GPT-4 d'OpenAI) ont lancé une vague de solutions innovantes capables d'exploiter de grands volumes de données non structurées qui étaient peu pratiques, voire impossibles, à traiter manuellement jusqu'à récemment. Ces applications peuvent inclure la récupération de données (voir le cours ML301 de Don Woodlock pour une excellente introduction à Retrieval Augmented Generation), l'analyse des sentiments, et même des agents d'IA entièrement autonomes, pour n'en nommer que quelques-uns !

0
0 112
Article Maria Nesterenko · Mars 15, 2024 8m read

L'intelligence artificielle (IA) attire beaucoup d'attention dernièrement car elle peut changer de nombreux domaines de nos vies. Une meilleure puissance informatique et plus de données ont aidé l'IA à réaliser des choses incroyables, comme l'amélioration des tests médicaux et la création de voitures autonomes. L'IA peut également aider les entreprises à prendre de meilleures décisions et à travailler plus efficacement, c'est pourquoi elle devient de plus en plus populaire et largement utilisée.

 

0
0 76
Article Iryna Mykhailova · Mars 12, 2024 6m read

Nous savons tous qu'il est crucial de disposer d'un ensemble de données de test appropriées avant de déployer une application en production pour garantir sa fiabilité et ses performances. Il permet de simuler des scénarios du monde réel et d'identifier les problèmes ou bugs potentiels avant qu'ils n'impactent les utilisateurs finaux. De plus, les tests avec des ensembles de données représentatifs permettent d’optimiser les performances, d’identifier les goulots d’étranglement et d’affiner les algorithmes ou les processus selon les besoins. En fin de compte, disposer d’un ensemble complet de données de test permet de fournir un produit de meilleure qualité, réduisant ainsi le risque de problèmes de post-production et améliorant l’expérience utilisateur globale.

Dans cet article, voyons comment utiliser l'IA générative, par example Gemini de Google, pour générer des données significatives sur les propriétés de plusieurs objets. Pour ce faire, j'utiliserai le service RESTful pour générer des données au format JSON puis j'utiliserai les données reçues pour créer des objets.

0
0 125
Article Pierre LaFay · Fév 28, 2024 5m read

Qu'est-ce que les données non structurées ?

Les données non structurées sont des informations qui n'ont pas de modèle de données ou d'organisation prédéfinis. Contrairement aux données structurées que l'on trouve dans les bases de données avec des structures claires (par exemple, des tables et des champs), les données non structurées n'ont pas de schéma fixe. Ce type de données comprend le texte, les images, les vidéos, les fichiers audio, les messages sur les médias sociaux, les courriels, etc.

0
0 63
Article Sylvain Guilbaud · Jan 29, 2024 13m read

Nous avons un délicieux dataset avec des recettes écrites par plusieurs utilisateurs de Reddit, mais la plupart des informations sont du texte libre comme le titre ou la description d'un article. Voyons comment nous pouvons très facilement charger l'ensemble de données, extraire certaines fonctionnalités et l'analyser à l'aide des fonctionnalités du grand modèle de langage OpenAI contenu dans Embedded Python et le framework Langchain.

Chargement de l'ensemble de données

Tout d’abord, nous devons charger l’ensemble de données ou pouvons-nous simplement nous y connecter ?

Il existe différentes manières d'y parvenir : par exemple CSV Record Mapper vous pouvez utiliser dans une production d'interopérabilité ou même de belles applications OpenExchange comme csvgen.

Nous utiliserons Foreign Tables. Une fonctionnalité très utile pour projeter des données physiquement stockées ailleurs vers IRIS SQL. Nous pouvons l'utiliser pour avoir une toute première vue des fichiers de l'ensemble de données.

Nous créons un Foreign Server:

CREATE FOREIGN SERVER dataset FOREIGN DATA WRAPPER CSV HOST '/app/data/'

Et puis une table étrangère qui se connecte au fichier CSV:

CREATE FOREIGN TABLE dataset.Recipes (
  CREATEDDATE DATE,
  NUMCOMMENTS INTEGER,
  TITLE VARCHAR,
  USERNAME VARCHAR,
  COMMENT VARCHAR,
  NUMCHAR INTEGER
) SERVER dataset FILE 'Recipes.csv' USING
{
  "from": {
    "file": {
       "skip": 1
    }
  }
}

Et voilà, nous pouvons immédiatement exécuter des requêtes SQL sur dataset.Recipes: image

## De quelles données avons-nous besoin ? L’ensemble de données est intéressant et nous avons faim. Cependant, si nous voulons décider d'une recette à cuisiner, nous aurons besoin de plus d'informations que nous pourrons utiliser pour analyser.

Nous allons travailler avec deux classes persistantes (tables):

  • yummy.data.Recipe: une classe contenant le titre et la description de la recette et quelques autres propriétés que nous souhaitons extraire et analyser (par exemple Score, Difficulty, Ingredients, CuisineType, PreparationTime)
  • yummy.data.RecipeHistory: une classe simple pour enregistrer que faisons-nous avec la recette

Nous pouvons maintenant charger nos tables yummy.data* avec le contenu de l'ensemble de données:

do ##class(yummy.Utils).LoadDataset()

Cela a l'air bien, mais nous devons encore découvrir comment générer des données pour les champs Score, Difficulty, Ingredients, PreparationTime et CuisineType. ## Analyser les recettes Nous souhaitons traiter le titre et la description de chaque recette et :

  • Extraire des informations telles que Difficulté, Ingrédients, Type de Cuisine, etc.
  • Construire notre propre score en fonction de nos critères afin que nous puissions décider de ce que nous voulons cuisiner.

Nous allons utiliser ce qui suit :

  • yummy.analysis.Analysis - une structure d'analyse générique que nous pouvons réutiliser au cas où nous souhaiterions construire plus d'analyse.
  • yummy.analysis.SimpleOpenAI - une analyse qui utilise le modèle Embedded Python + Langchain Framework + OpenAI LLM.

LLM (large language models) sont vraiment un excellent outil pour traiter le langage naturel.

LangChainest prêt à fonctionner en Python, nous pouvons donc l'utiliser directement dans InterSystems IRIS en utilisant Embedded Python.

La classe complète SimpleOpenAI ressemble à ceci:

/// Analyse OpenAI simple pour les recettes
Class yummy.analysis.SimpleOpenAI Extends Analysis
{

Property CuisineType As %String;

Property PreparationTime As %Integer;

Property Difficulty As %String;

Property Ingredients As %String;

/// Run
/// Vous pouvez essayer ceci depuis un terminal :
/// set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8))
/// do a.Run()
/// zwrite a
Method Run()
{
    try {
        do ..RunPythonAnalysis()

        set reasons = ""

        // mes types de cuisine préférés
        if "spanish,french,portuguese,italian,korean,japanese"[..CuisineType {
            set ..Score = ..Score + 2
            set reasons = reasons_$lb("It seems to be a "_..CuisineType_" recipe!")
        }

        // je ne veux pas passer toute la journée à cuisiner :)
        if (+..PreparationTime < 120) {
            set ..Score = ..Score + 1
            set reasons = reasons_$lb("You don't need too much time to prepare it") 
        }
        
        // bonus pour les ingrédients préférés !
        set favIngredients = $listbuild("kimchi", "truffle", "squid")
        for i=1:1:$listlength(favIngredients) {
            set favIngred = $listget(favIngredients, i)
            if ..Ingredients[favIngred {
                set ..Score = ..Score + 1
                set reasons = reasons_$lb("Favourite ingredient found: "_favIngred)
            }
        }

        set ..Reason = $listtostring(reasons, ". ")

    } catch ex {
        throw ex
    }
}

/// Mettre à jour la recette avec les résultats de l'analyse
Method UpdateRecipe()
{
    try {
        // appeler d'abord l'implémentation de la classe parent
        do ##super()

        // ajouter des résultats d'analyse spécifiques à OpenAI
        set ..Recipe.Ingredients = ..Ingredients
        set ..Recipe.PreparationTime = ..PreparationTime
        set ..Recipe.Difficulty = ..Difficulty
        set ..Recipe.CuisineType = ..CuisineType

    } catch ex {
        throw ex
    }
}

/// Exécuter une analyse à l'aide de Embedded Python + Langchain
/// do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8)).RunPythonAnalysis(1)
Method RunPythonAnalysis(debug As %Boolean = 0) [ Language = python ]
{
    # load OpenAI APIKEY from env
    import os
    from dotenv import load_dotenv, find_dotenv
    _ = load_dotenv('/app/.env')

    # account for deprecation of LLM model
    import datetime
    current_date = datetime.datetime.now().date()
    # date after which the model should be set to "gpt-3.5-turbo"
    target_date = datetime.date(2024, 6, 12)
    # set the model depending on the current date
    if current_date > target_date:
        llm_model = "gpt-3.5-turbo"
    else:
        llm_model = "gpt-3.5-turbo-0301"

    from langchain.chat_models import ChatOpenAI
    from langchain.prompts import ChatPromptTemplate
    from langchain.chains import LLMChain

    from langchain.output_parsers import ResponseSchema
    from langchain.output_parsers import StructuredOutputParser

    # init llm model
    llm = ChatOpenAI(temperature=0.0, model=llm_model)

    # prepare the responses we need
    cuisine_type_schema = ResponseSchema(
        name="cuisine_type",
        description="What is the cuisine type for the recipe? \
                     Answer in 1 word max in lowercase"
    )
    preparation_time_schema = ResponseSchema(
        name="preparation_time",
        description="How much time in minutes do I need to prepare the recipe?\
                     Anwer with an integer number, or null if unknown",
        type="integer",
    )
    difficulty_schema = ResponseSchema(
        name="difficulty",
        description="How difficult is this recipe?\
                     Answer with one of these values: easy, normal, hard, very-hard"
    )
    ingredients_schema = ResponseSchema(
        name="ingredients",
        description="Give me a comma separated list of ingredients in lowercase or empty if unknown"
    )
    response_schemas = [cuisine_type_schema, preparation_time_schema, difficulty_schema, ingredients_schema]

    # get format instructions from responses
    output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
    format_instructions = output_parser.get_format_instructions()
    
    analysis_template = """\
    Interprete and evaluate a recipe which title is: {title}
    and the description is: {description}
    
    {format_instructions}
    """
    prompt = ChatPromptTemplate.from_template(template=analysis_template)

    messages = prompt.format_messages(title=self.Recipe.Title, description=self.Recipe.Description, format_instructions=format_instructions)
    response = llm(messages)

    if debug:
        print("======ACTUAL PROMPT")
        print(messages[0].content)
        print("======RESPONSE")
        print(response.content)

    # populate analysis with results
    output_dict = output_parser.parse(response.content)
    self.CuisineType = output_dict['cuisine_type']
    self.Difficulty = output_dict['difficulty']
    self.Ingredients = output_dict['ingredients']
    if type(output_dict['preparation_time']) == int:
        self.PreparationTime = output_dict['preparation_time']

    return 1
}

}

La méthode RunPythonAnalysis c'est ici qu'entre en jeu OpenAI :). Vous pouvez le lancer directement depuis votre terminal pour une recette donnée :

do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)

Nous obtiendrons un résultat comme celui-ci :

USER>do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)
======ACTUAL PROMPT
                    Interprete and evaluate a recipe which title is: Folded Sushi - Alaska Roll
                    and the description is: Craving for some sushi but don't have a sushi roller? Try this easy version instead. It's super easy yet equally delicious!
[Video Recipe](https://www.youtube.com/watch?v=1LJPS1lOHSM)
# Ingredients
Serving Size:  \~5 sandwiches      
* 1 cup of sushi rice
* 3/4 cups + 2 1/2 tbsp of water
* A small piece of konbu (kelp)
* 2 tbsp of rice vinegar
* 1 tbsp of sugar
* 1 tsp of salt
* 2 avocado
* 6 imitation crab sticks
* 2 tbsp of Japanese mayo
* 1/2 lb of salmon  
# Recette     
* Place 1 cup of sushi rice into a mixing bowl and wash the rice at least 2 times or until the water becomes clear. Then transfer the rice into the rice cooker and add a small piece of kelp along with 3/4 cups plus 2 1/2 tbsp of water. Cook according to your rice cookers instruction.
* Combine 2 tbsp rice vinegar, 1 tbsp sugar, and 1 tsp salt in a medium bowl. Mix until everything is well combined.
* After the rice is cooked, remove the kelp and immediately scoop all the rice into the medium bowl with the vinegar and mix it well using the rice spatula. Make sure to use the cut motion to mix the rice to avoid mashing them. After thats done, cover it with a kitchen towel and let it cool down to room temperature.
* Cut the top of 1 avocado, then slice into the center of the avocado and rotate it along your knife. Then take each half of the avocado and twist. Afterward, take the side with the pit and carefully chop into the pit and twist to remove it. Then, using your hand, remove the peel. Repeat these steps with the other avocado. Dont forget to clean up your work station to give yourself more space. Then, place each half of the avocado facing down and thinly slice them. Once theyre sliced, slowly spread them out. Once thats done, set it aside.
* Remove the wrapper from each crab stick. Then, using your hand, peel the crab sticks vertically to get strings of crab sticks. Once all the crab sticks are peeled, rotate them sideways and chop them into small pieces, then place them in a bowl along with 2 tbsp of Japanese mayo and mix until everything is well mixed.
* Place a sharp knife at an angle and thinly slice against the grain. The thickness of the cut depends on your preference. Just make sure that all the pieces are similar in thickness.
* Grab a piece of seaweed wrap. Using a kitchen scissor, start cutting at the halfway point of seaweed wrap and cut until youre a little bit past the center of the piece. Rotate the piece vertically and start building. Dip your hand in some water to help with the sushi rice. Take a handful of sushi rice and spread it around the upper left hand quadrant of the seaweed wrap. Then carefully place a couple slices of salmon on the top right quadrant. Then place a couple slices of avocado on the bottom right quadrant. And finish it off with a couple of tsp of crab salad on the bottom left quadrant. Then, fold the top right quadrant into the bottom right quadrant, then continue by folding it into the bottom left quadrant. Well finish off the folding by folding the top left quadrant onto the rest of the sandwich. Afterward, place a piece of plastic wrap on top, cut it half, add a couple pieces of ginger and wasabi, and there you have it.

                    
Le résultat doit être un extrait de code de démarque formaté selon le schéma suivant, incluant les caractères "```json" and "```" :
json
{
        "cuisine_type": string  // Quel est le type de cuisine de la recette ? Réponse en 1 mot maximum en minuscule
        "preparation_time": integer  // De combien de temps en minutes ai-je besoin pour préparer la recette ? Répondez avec un nombre entier, ou nul si inconnu
        "difficulty": string  // À quel point cette recette est-elle difficile ? Répondez avec l'une de ces valeurs : facile, normal, difficile, très difficile
        "ingredients": string  // Donnez-moi une liste d'ingrédients séparés par des virgules en minuscules ou vide si inconnu
}

                    
======RESPONSE
json
{
        "cuisine_type": "japanese",
        "preparation_time": 30,
        "difficulty": "easy",
        "ingredients": "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
}

Ça à l'air bon. Il semble que notre invite OpenAI soit capable de renvoyer des informations utiles. Exécutons toute la classe d'analyse depuis le terminal :

set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12))
do a.Run()
zwrite a
USER>zwrite a
a=37@yummy.analysis.SimpleOpenAI  ; <OREF>
+----------------- general information ---------------
|      oref value: 37
|      class name: yummy.analysis.SimpleOpenAI
| reference count: 2
+----------------- attribute values ------------------
|        CuisineType = "japanese"
|         Difficulty = "easy"
|        Ingredients = "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
|    PreparationTime = 30
|             Reason = "It seems to be a japanese recipe!. You don't need too much time to prepare it"
|              Score = 3
+----------------- swizzled references ---------------
|           i%Recipe = ""
|           r%Recipe = "30@yummy.data.Recipe"
+-----------------------------------------------------

## Analyser toutes les recettes ! Naturellement, vous souhaitez exécuter l’analyse sur toutes les recettes que nous avons chargées.

Vous pouvez analyser une gamme d’identifiants de recettes de cette façon :

USER>do ##class(yummy.Utils).AnalyzeRange(1,10)
> Recipe 1 (1.755185s)
> Recipe 2 (2.559526s)
> Recipe 3 (1.556895s)
> Recipe 4 (1.720246s)
> Recipe 5 (1.689123s)
> Recipe 6 (2.404745s)
> Recipe 7 (1.538208s)
> Recipe 8 (1.33001s)
> Recipe 9 (1.49972s)
> Recipe 10 (1.425612s)

Après cela, regardez à nouveau votre tableau de recettes et vérifiez les résultats.

select * from yummy_data.Recipe

image

Je pense que je pourrais essayer la pizza à la courge poivrée ou le kimchi coréen au tofu et au porc :). De toute façon, je devrai vérifier à la maison :)

Notes finales

Vous pouvez trouver l'exemple complet sur https://github.com/isc-afuentes/recipe-inspector

Avec cet exemple simple, nous avons appris à utiliser les techniques LLM pour ajouter des fonctionnalités ou analyser certaines parties de vos données dans InterSystems IRIS.

Avec ce point de départ, vous pourriez penser à :

  • Utiliser InterSystems BI pour explorer et parcourir vos données à l'aide de cubes et de tableaux de bord.
  • Créer une application Web et fournir une interface utilisateur (par exemple Angular) pour cela, vous pouvez exploiter des packages tels que RESTForms2 pour générer automatiquement des API REST pour vos classes persistantes.
  • Et pourquoi garder en base l'information indiquant les recettes que vous aimez et celles que vous n'aimez pas, puis d'essayer de déterminer si une nouvelle recette vous plaira ? Vous pourriez essayer une approche IntegratedML, ou même une approche LLM fournissant des exemples de données et construisant un cas d'utilisation RAG (Retrieval Augmented Generation).

Quelles autres choses pourriez-vous essayer ? Laissez-moi savoir ce que vous pensez!

0
0 208
Article Pierre LaFay · Jan 20, 2024 9m read

Avec l'avènement d'Embedded Python, une myriade de cas d'utilisation sont désormais possibles depuis IRIS, directement en utilisant les librairies Python pour des opérations plus complexes. L'une de ces opérations consiste à utiliser des outils de traitement du langage naturel tels que la comparaison de similarités textuelles.

Configuration de Python intégré pour utiliser la librairie de transformateurs de phrases

0
0 170
Article Pierre LaFay · Déc 21, 2023 4m read

Je reprends ici une expérience vécue qui m'a fait changer ma manière d'aborder le développement d'une fonctionnalité nouvelle pour moi.

Dans le cadre d'un projet, je devais générer un fichier excel assez complexe (rapport avec des variables calculées sur différents intervalles de temps et sur des ensembles dépendants d'un contexte).

3
0 80
Article Guillaume Rongier · Déc 15, 2023 13m read

1. IRIS RAG Demo

IRIS RAG Demo

Ceci est une simple démo de l'IRIS avec un exemple de RAG (Retrieval Augmented Generation). Le backend est écrit en Python en utilisant IRIS et IoP, le modèle LLM est orca-mini et est servi par le serveur ollama. Le frontend est un chatbot écrit avec Streamlit.

1.1. Quest-ce que RAG?

RAG signifie Retrieval Augmented Generation, il permet d'utiliser un modèle LLM (GPT-3.5/4, Mistral, Orca, etc.) avec une base de connaissances.

Pourquoi est-ce important ? Parce que cela permet d'utiliser une base de connaissances pour répondre aux questions, et d'utiliser le LLM pour générer la réponse.

Par exemple, si vous demandez "Qu'est-ce que le module grongier.pex ?" directement au LLM, il ne pourra pas répondre, car il ne sait pas ce qu'est ce module (et peut-être que vous ne le savez pas non plus 🤪).

Mais si vous posez la même question à RAG, il pourra répondre, car il utilisera la base de connaissances qui sait ce qu'est le module grongier.pex pour trouver la réponse.

Maintenant que vous savez ce qu'est RAG, voyons comment cela fonctionne.

1.2. Comment ça marche?

Tout d'abord, nous devons comprendre comment fonctionne un LLM. Les LLM sont entraînés pour prédire le mot suivant, étant donné les mots précédents. Ainsi, si vous lui donnez une phrase, il essaiera de prédire le mot suivant, et ainsi de suite. Facile, non ?

Pour interagir avec un LLM, vous devez généralement lui donner une requête, et il générera le reste de la phrase. Par exemple, si vous lui donnez la requête Qu'est-ce que le module grongier.pex ?, il générera le reste de la phrase, et cela ressemblera à ceci :

Je suis désolé, mais je ne connais pas le module Pex que vous avez mentionné. Pouvez-vous fournir plus d'informations ou de contexte à ce sujet ?

Ok, comme prévu, il ne sait pas ce qu'est le module grongier.pex. Mais que se passe-t-il si nous lui donnons une requête qui contient la réponse ? Par exemple, si nous lui donnons la requête Qu'est-ce que le module grongier.pex ? C'est un module qui vous permet de faire X, Y et Z., il générera le reste de la phrase, et cela ressemblera à ceci :

Le module grongier.pex est un module qui vous permet de faire X, Y et Z.

Ok, maintenant il sait ce qu'est le module grongier.pex.

Mais que se passe-t-il si nous ne savons pas ce qu'est le module grongier.pex ? Comment pouvons-nous lui donner une requête qui contient la réponse ? Eh bien, c'est là que la base de connaissances entre en jeu.

RAG

L'idée de RAG est d'utiliser la base de connaissances pour trouver le contexte, puis d'utiliser le LLM pour générer la réponse.

Pour trouver le contexte, RAG utilisera un retriever. Le retriever recherchera la base de connaissances pour les documents les plus pertinents, puis RAG utilisera le LLM pour générer la réponse.

Pour rechercher la base de connaissances, nous utiliserons la recherche vectorielle.

La recherche vectorielle est une technique qui permet de trouver les documents les plus pertinents étant donné une requête. Elle fonctionne en convertissant les documents et la requête en vecteurs, puis en calculant la similarité cosinus entre le vecteur de la requête et les vecteurs des documents. Plus la similarité cosinus est élevée, plus le document est pertinent.

Pour plus d'informations sur la recherche vectorielle, vous pouvez consulter ce lien. Merci à @Dmitry Maslennikov pour son article.

Vector Search

Maintenant que nous savons comment fonctionne RAG, voyons comment l'utiliser.

1.3. Installation de la démo

Pour installer la démo, vous devez avoir Docker et Docker Compose installés sur votre machine.

Ensuite, il suffit de cloner le repo et d'exécuter la commande docker-compose up.

git clone https://github.com/grongierisc/iris-rag-demo
cd iris-rag-demo
docker-compose up

⚠️ tout est local, rien n'est envoyé dans le cloud, donc soyez patient, cela peut prendre quelques minutes pour démarrer.

1.4. Usage

Une fois la démo démarrée, vous pouvez accéder au frontend à l'adresse http://localhost:8501.

Frontend

Vous pouvez poser des questions sur l'IRIS, par exemple :

  • Qu'est-ce que le module grongier.pex ?

Question

Comme vous pouvez le voir, la réponse n'est pas très bonne, car le LLM ne sait pas ce qu'est le module grongier.pex.

Maintenant, essayons avec RAG :

Uploader la documentation du module grongier.pex, elle se trouve dans le dossier docs, fichier grongier.pex.md.

Ensuite, posez la même question :

  • Qu'est-ce que le module grongier.pex ?

Question

Comme vous pouvez le voir, la réponse est bien meilleure, car le LLM sait maintenant ce qu'est le module grongier.pex.

Vous pouvez voir les détails dans les logs :

Aller dans le portail de gestion à l'adresse http://localhost:53795/csp/irisapp/EnsPortal.ProductionConfig.zen?$NAMESPACE=IRISAPP&$NAMESPACE=IRISAPP& et cliquer sur l'onglet Messages.

Premièrement, vous verrez le message envoyé au processus RAG :

Message

Ensuite, la requête de recherche dans la base de connaissances (base de données vectorielle) :

Message

Et enfin la nouvelle requête envoyée au LLM :

Message

1.5. Comment fonctionne la démo ?

La démo est composée de 3 parties :

  • Le frontend, écrit avec Streamlit
  • Le backend, écrit avec Python et IRIS
  • La base de connaissances Chroma et la base de données vectorielle
  • Le LLM, Orca-mini, servi par le serveur Ollama

1.5.1. Le frontend

Le frontend est écrit avec Streamlit, c'est un simple chatbot qui vous permet de poser des questions.

Rien de bien compliqué ici, juste un simple chatbot.

import os
import tempfile
import time
import streamlit as st
from streamlit_chat import message

from grongier.pex import Director

_service = Director.create_python_business_service("ChatService")

st.set_page_config(page_title="ChatIRIS")


def display_messages():
    st.subheader("Chat")
    for i, (msg, is_user) in enumerate(st.session_state["messages"]):
        message(msg, is_user=is_user, key=str(i))


def process_input():
    if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0:
        user_text = st.session_state["user_input"].strip()
        with st.spinner(f"Thinking about {user_text}"):
            rag_enabled = False
            if len(st.session_state["file_uploader"]) > 0:
                rag_enabled = True
            time.sleep(1) # help the spinner to show up
            agent_text = _service.ask(user_text, rag_enabled)

        st.session_state["messages"].append((user_text, True))
        st.session_state["messages"].append((agent_text, False))


def read_and_save_file():

    for file in st.session_state["file_uploader"]:
        with tempfile.NamedTemporaryFile(delete=False,suffix=f".{file.name.split('.')[-1]}") as tf:
            tf.write(file.getbuffer())
            file_path = tf.name

        with st.spinner(f"Ingesting {file.name}"):
            _service.ingest(file_path)
        os.remove(file_path)

    if len(st.session_state["file_uploader"]) > 0:
        st.session_state["messages"].append(
            ("File(s) successfully ingested", False)
        )

    if len(st.session_state["file_uploader"]) == 0:
        _service.clear()
        st.session_state["messages"].append(
            ("Clearing all data", False)
        )

def page():
    if len(st.session_state) == 0:
        st.session_state["messages"] = []
        _service.clear()

    st.header("ChatIRIS")

    st.subheader("Upload a document")
    st.file_uploader(
        "Upload document",
        type=["pdf", "md", "txt"],
        key="file_uploader",
        on_change=read_and_save_file,
        label_visibility="collapsed",
        accept_multiple_files=True,
    )

    display_messages()
    st.text_input("Message", key="user_input", on_change=process_input)


if __name__ == "__main__":
    page()

💡 Je n'utilise que :

_service = Director.create_python_business_service("ChatService")

Pour créer un lien entre le frontend et le backend.

ChatService est un simple service métier dans la production d'interopérabilité.

1.5.2. Le backend

Le backend est écrit avec Python et IRIS.

Il est composé de 3 parties :

  • Le service métier
    • point d'entrée du frontend
  • Le processus métier
    • effectuer la recherche dans la base de connaissances si nécessaire
  • Deux opérations métier
    • Une pour la base de connaissances
      • Ingestion des documents
      • Recherche des documents
      • Effacer les documents
    • Une pour le LLM
      • Générer la réponse

1.5.2.1. Le business service

Le service métier est un simple service métier qui permet :

  • D'uploader des documents
  • De poser des questions
  • De vider la base de données vectorielle
from grongier.pex import BusinessService

from rag.msg import ChatRequest, ChatClearRequest, FileIngestionRequest

class ChatService(BusinessService):

    def on_init(self):
        if not hasattr(self, "target_chat"):
            self.target_chat = "ChatProcess"
        if not hasattr(self, "target_vector"):
            self.target_vector = "VectorOperation"

    def ingest(self, file_path: str):
        # build message
        msg = FileIngestionRequest(file_path=file_path)
        # send message
        self.send_request_sync(self.target_vector, msg)

    def ask(self, query: str, rag: bool = False):
        # build message
        msg = ChatRequest(query=query)
        # send message
        response = self.send_request_sync(self.target_chat, msg)
        # return response
        return response.response

    def clear(self):
        # build message
        msg = ChatClearRequest()
        # send message
        self.send_request_sync(self.target_vector, msg)

Si vous regardez le code, vous verrez que le service métier est très simple, il ne fait que passer entre l'opération et le processus.

1.5.2.2. Le business process

Le processus métier est aussi un simple processus qui permet de rechercher la base de connaissances si nécessaire.

from grongier.pex import BusinessProcess

from rag.msg import ChatRequest, ChatResponse, VectorSearchRequest

class ChatProcess(BusinessProcess):
    """
    the aim of this process is to generate a prompt from a query
    if the vector similarity search returns a document, then we use the document's content as the prompt
    if the vector similarity search returns nothing, then we use the query as the prompt
    """
    def on_init(self):
        if not hasattr(self, "target_vector"):
            self.target_vector = "VectorOperation"
        if not hasattr(self, "target_chat"):
            self.target_chat = "ChatOperation"

        # prompt template
        self.prompt_template = "Given the context: \n {context} \n Answer the question: {question}"


    def ask(self, request: ChatRequest):
        query = request.query
        prompt = ""
        # build message
        msg = VectorSearchRequest(query=query)
        # send message
        response = self.send_request_sync(self.target_vector, msg)
        # if we have a response, then use the first document's content as the prompt
        if response.docs:
            # add each document's content to the context
            context = "\n".join([doc['page_content'] for doc in response.docs])
            # build the prompt
            prompt = self.prompt_template.format(context=context, question=query)
        else:
            # use the query as the prompt
            prompt = query
        # build message
        msg = ChatRequest(query=prompt)
        # send message
        response = self.send_request_sync(self.target_chat, msg)
        # return response
        return response

Comme je le disais, le processus est très simple, il ne fait que passer entre l'opération et le processus.

Si la recherche vectorielle retourne des documents, alors il utilisera le contenu des documents comme prompt, sinon il utilisera la requête comme prompt.

1.5.2.3. L'opération LLM

L'opération LLM est une simple opération qui permet de générer la réponse.


class ChatOperation(BusinessOperation):

    def __init__(self):
        self.model = None

    def on_init(self):
        self.model = Ollama(base_url="http://ollama:11434",model="orca-mini")

    def ask(self, request: ChatRequest):
        return ChatResponse(response=self.model(request.query))

Difficile de faire plus simple, non ?

1.5.2.4. L'opération vectorielle

L'opération vectorielle est une opération qui permet d'ingérer des documents, de rechercher des documents et de vider la base de données vectorielle.


class VectorOperation(BusinessOperation):

    def __init__(self):
        self.text_splitter = None
        self.vector_store = None

    def on_init(self):
        self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100)
        self.vector_store = Chroma(persist_directory="vector",embedding_function=FastEmbedEmbeddings())

    def ingest(self, request: FileIngestionRequest):
        file_path = request.file_path
        file_type = self._get_file_type(file_path)
        if file_type == "pdf":
            self._ingest_pdf(file_path)
        elif file_type == "markdown":
            self._ingest_markdown(file_path)
        elif file_type == "text":
            self._ingest_text(file_path)
        else:
            raise Exception(f"Unknown file type: {file_type}")

    def clear(self, request: ChatClearRequest):
        self.on_tear_down()

    def similar(self, request: VectorSearchRequest):
        # do a similarity search
        docs = self.vector_store.similarity_search(request.query)
        # return the response
        return VectorSearchResponse(docs=docs)

    def on_tear_down(self):
        docs = self.vector_store.get()
        for id in docs['ids']:
            self.vector_store.delete(id)
        
    def _get_file_type(self, file_path: str):
        if file_path.lower().endswith(".pdf"):
            return "pdf"
        elif file_path.lower().endswith(".md"):
            return "markdown"
        elif file_path.lower().endswith(".txt"):
            return "text"
        else:
            return "unknown"

    def _store_chunks(self, chunks):
        ids = [str(uuid.uuid5(uuid.NAMESPACE_DNS, doc.page_content)) for doc in chunks]
        unique_ids = list(set(ids))
        self.vector_store.add_documents(chunks, ids = unique_ids)
        
    def _ingest_text(self, file_path: str):
        docs = TextLoader(file_path).load()
        chunks = self.text_splitter.split_documents(docs)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)
        
    def _ingest_pdf(self, file_path: str):
        docs = PyPDFLoader(file_path=file_path).load()
        chunks = self.text_splitter.split_documents(docs)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)

    def _ingest_markdown(self, file_path: str):
        # Document loader
        docs = TextLoader(file_path).load()

        # MD splits
        headers_to_split_on = [
            ("#", "Header 1"),
            ("##", "Header 2"),
        ]

        markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
        md_header_splits = markdown_splitter.split_text(docs[0].page_content)

        # Split
        chunks = self.text_splitter.split_documents(md_header_splits)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)

Si vous regardez le code, vous verrez que l'opération vectorielle est un peu plus complexe que les autres. Les raisons sont les suivantes :

  • Nous devons ingérer des documents
  • Nous devons rechercher des documents
  • Nous devons vider la base de données vectorielle

Pour ingérer des documents, nous devons d'abord les charger, puis les diviser en morceaux, puis les stocker dans la base de données vectorielle.

Le processus de diviser est important, car cela permettra à la recherche vectorielle de trouver les documents les plus pertinents.

Par exemple, si nous avons un document qui contient 1000 mots, et que nous le divisons en 10 morceaux de 100 mots, alors la recherche vectorielle pourra trouver les documents les plus pertinents, car elle pourra comparer les vecteurs de la requête avec les vecteurs des morceaux.

Dans le cas des markdowns, nous utilisons également les en-têtes pour diviser le document en morceaux.

1.6. Remarques générales

Tout cela peut être fait avec langchains, mais je voulais vous montrer comment le faire avec le framework d'interopérabilité. Et le rendre plus accessible à tous pour comprendre comment le principe des RAG fonctionne.

2
0 567
Question Antoine.Dh · Déc 13, 2023

Bonjour,

Je cherche à faire un systeme de pagination en SQL et je suis tombé sur un article ici https://community.intersystems.com/post/scrollable-resultset-pagination-sample#comment-166186

J'ai essayé d'appliquer la solution, mais j'ai un bug assez particulier et je me demandais si quelqu'un pouvait m'éclairer. Sur mon environnement de dev local aucun soucis, par contre pour la même requete en environnement de preprod j'ai cette erreur:

<UNDEFINED>newvar+3^%qaqcasl *%classname

la requete que j'effectue:

SELECT * FROM (SELECT _DATA.ID, ROW_NUMBER() _RN, COUNT(*) _COUNT FROM XX.srsshiptype _DATA ) WHERE _RN BETWEEN 1 and 1000

La seule piste que j'ai serait peut-etre la une difference au niveau de la version d'IRIS, je suis en 2023.1 conteneur Ubuntu en local, et 2022.1 Windows sur l'environnement de preprod.

Mais comme la solution date de 2021 je me dit qu'elle devrait fonctionner en 2022.1

2
0 93
Article Iryna Mykhailova · Sept 25, 2023 6m read

FHIR a transformé le secteur des soins de santé en fournissant un modèle de données normalisé pour la création d'applications de soins de santé et en favorisant l'échange de données entre les différents systèmes de soins de santé. La norme FHIR est basée sur des approches modernes axées sur les API, ce qui la rend plus accessible aux développeurs mobiles et web. Cependant, l'interaction avec les API FHIR peut encore s'avérer difficile, en particulier lorsqu'il s'agit de requêter des données à l'aide du langage naturel.

Nous présentons l'application Chaîne OpenAPI et IA - FHIR une solution qui permet aux utilisateurs d'interagir avec les API FHIR à l'aide de requêtes en langage naturel. Conçue avec OpenAI, LangChain et Streamlit, cette application simplifie le processus d'interrogation des API FHIR et le rend plus facile à utiliser.

 

Quelles sont les spécifications OpenAPI de FHIR ?

Les spécifications OpenAPI (anciennement connues sous le nom de "Swagger" et faisant actuellement partie de l'" Initiative OpenAPI " (https://www.openapis.org/)) sont devenues un outil essentiel dans le monde du développement logiciel, permettant aux développeurs de concevoir, de documenter et d'interagir avec les API de manière plus efficace. Les spécifications OpenAPI définissent un format normalisé, lisible par une machine, pour décrire les API RESTful, offrant ainsi un moyen clair et cohérent de comprendre leurs capacités et de les utiliser de manière efficace.

Dans le domaine des soins de santé, FHIR s'est imposé comme une norme de premier plan pour l'échange de données et l'interopérabilité. Pour améliorer les capacités d'interopérabilité de FHIR, HL7 a officiellement documenté les spécifications OpenAPI de FHIR, qui permettent aux développeurs d'intégrer de manière transparente les ressources et les opérations FHIR dans leurs solutions logicielles.

 

Avantages des spécifications OpenAPI de FHIR :

  1. Description normalisée de l'API : Les spécifications OpenAPI fournissent une description complète et normalisée des ressources, des opérations et des interactions FHIR. Les développeurs peuvent facilement comprendre la structure et les capacités des API basées sur FHIR, ce qui facilite la création d'intégrations et l'interaction avec les systèmes de santé.
  2. Promotion de l'interopérabilité : La collaboration entre les développeurs est encouragée, ce qui favorise l'adoption des normes et des meilleures pratiques FHIR. Les spécifications fournissent un langage et un cadre communs pour discuter des intégrations et des implémentations basées sur FHIR, ce qui favorise la collaboration entre les développeurs.
  3. Documentation et tests améliorés : Documentation interactive et séries de tests pour une meilleure compréhension et une meilleure validation. Les développeurs peuvent créer une documentation détaillée sur les API, ce qui permet aux autres développeurs de comprendre et d'utiliser plus facilement les API basées sur FHIR. Les séries de tests basées sur les spécifications permettent d'effectuer des tests complets et de valider les intégrations d'API, garantissant ainsi la fiabilité et la précision des échanges de données de santé.
  4. Expérience améliorée pour les développeurs : Génération automatique de bibliothèques client et de SDK pour une intégration transparente. Cela simplifie le processus d'intégration et réduit le temps et les efforts nécessaires pour incorporer la fonctionnalité FHIR dans leurs applications.

 

Comment les chaînes FHIR, OpenAI et OpenAPI fonctionnent-elles ensemble ?

L'application Chaîne OpenAPI et IA - FHIR s'appuie sur LangChain pour charger et analyser les spécifications OpenAPI (Chaîne OpenAPI). Ensuite, sur la base de ces spécifications, la chaîne d'invites fournies par OpenAI vise à comprendre les requêtes en langage naturel et à les convertir en demandes d'API FHIR appropriées. Les utilisateurs peuvent poser des questions en langage naturel et l'application interagira avec l'API FHIR choisie pour récupérer les informations pertinentes.

Par exemple, un utilisateur peut demander : "Quelle est la dernière mesure de la tension artérielle du patient John Doe (ID 111) ?" L'application traduira alors cette requête en une demande d'API FHIR, récupérera les données requises et les présentera à l'utilisateur dans un format facilement compréhensible.

 

Avantages de l'application Chaîne OpenAPI et IA - FHIR

  1. Interactions faciles à utiliser : L'application permet aux utilisateurs d'interagir avec les API FHIR à l'aide de requêtes en langage naturel, ce qui facilite l'accès et l'analyse des données de santé pour les utilisateurs non techniques.
  2. Amélioration de l'efficacité : L'application rationalise le processus d'interrogation des API FHIR, réduisant ainsi le temps et les efforts nécessaires pour obtenir des informations pertinentes. Elle permet également de réduire le nombre de clics (temps passé) pour trouver une information particulière à partir de l'application.
  3. Personnalisable : Les normes FHIR simplifient la récupération de données cohérentes à partir de n'importe quel serveur FHIR, ce qui permet une personnalisation aisée. Il peut être configuré sans effort pour s'intégrer de manière transparente à n'importe quelle API FHIR, offrant ainsi une solution flexible et adaptable aux diverses exigences en matière de données de santé.

 

Premiers pas avec l'application Chaîne OpenAPI et IA - FHIR

Pour commencer à utiliser l'application Chaîne OpenAPI et IA - FHIR, suivez les étapes suivantes :

  1. Obtenez une clé API d'OpenAI auprès de la Plate-forme OpenAI.
  2. Obtenez un point de terminaison API du serveur FHIR. Vous pouvez utiliser votre propre exemple de serveur FHIR (accès non authentifié nécessaire) ou créer un exemple de serveur temporaire en suivant les instructions données dans la Plate-forme d'apprentissage de FHIR pour InterSystems IRIS.
  3. Essayez l'application en ligne ou ou configurez-la localement en suivant les instructions fournies.

Grâce à l'intégration de capacités d'IA et de traitement du langage naturel, l'application haîne OpenAPI et IA - FHIR offre un moyen plus intuitif pour interagir avec les API FHIR, rendant les données de santé plus accessibles et plus faciles à analyser pour les utilisateurs de tous niveaux techniques.

Votez pour notre application dans le cadre du concours Grand Prix si vous la trouvez pertinente !

Si vous pensez à des applications qui pourraient utiliser cette implémentation, n'hésitez pas à les partager dans le fil de discussion.

0
0 73