Ce projet d'initiative personnelle vise à créer une application avec une interface graphique, résolvant des cas d'usages type. L'application utilise des LLMs d'Hugging Face, le framework LangChain pour créer l'application (back) et Streamlit pour l'interface utilisateur.
Le Dossier contient :
-
un fichier ChatGPT.py (le main)
-
un dossier /page/ contenant les 2 autres pages : QA.py et Summerizer.py
-
un dossier /documents/ contenant un fichier d'illustration : constitution.py
La première application est un chatbot. Son principe est simple : il s'agit d'une conversation entre l'IA et un humain. En temps normal, lorsque qu'on infère un LLM, chaque appel est indépendant. Ainsi, il faut ajouter de la mémoire au modèle. L'idée est très simple : on ajoute dans le prompt l'historique de la conversation.
Il y a 3 fonctions :
-
load_LLMqui va télécharger en 8bit le LLM demandé sur Hugging Face en local. On créer unepipelineregroupant certains paramètres importants : max_length, temperature, top p. On utiliseHuggingFacePipelinede LangChain. -
chainequi va créer la chaine LLM. On y décrit lepromptutilisé. On instancie la classeConversationBufferMemorypour créer l'objetmemory. Il y a d'autres classes qui permettent de créer un ConversationBuffer* (qui permet de changer la manière dont on choisit l'historique de conversation : les k derniers échanges, derniers tokens...). Enfin, on créer la chaine avec la classeLLMChain. -
mainqui contient le cœur de l'application : l'affichage Streamlit. Avecst.sidebar.success, on peut afficher les fichiers .py contenus dans le dossier /pages/. Il faut créer des variables de sessionsllm_chainetmessages: à chaque action, la page Streamlit se recharge (on veut éviter de recharger ces variables à chaque fois). Avec la méthodepredict, surllm_chain, on peut inférer le modèle. On veillera bien à ajouter chaque message dans la variablemessagespour les afficher.
Cette deuxième application permet d'effectuer du Question/Answering sur des documents. Pour ce faire, on utilise la recherche sémantique entre la question et notre base de données, puis avec la méthode stuffing, on insère dans le prompt les morceaux de textes qui semblent les plus pertinents. NB : il existe un notebook dans /Stage-Marijan/Notebooks/QA PDF avec LangChain. Voici les étapes clés :
-
Découper le(s) document(s) en morceaux
-
Import le modèle d'embeddings, calculer les embeddings des morceaux de document pour les stocker dans une base de données.
-
Créer une chaîne avec la classe
ConversationalRetrievalChain
Il y a 5 fonctions :
-
load_LLMqui va télécharger en 8bit le LLM demandé sur Hugging Face en local. On créer unepipelineregroupant certains paramètres importants : max_length, temperature, top p. On utiliseHuggingFacePipelinede LangChain. -
load_document, objet de la classeRecursiveCharacterTextSplitter, permet de découper un long texte initial, en plus petits morceaux (plusieurs méthodes possibles). Elle renvoie une liste de morceaux de textes. La taille de ces morceaux doit être bien pensée : on calculera les embeddings de ces morceaux. -
qaqui va créer la chaîne grâce à la classeConversationalRetrievalChain. L'objetmemoryn'est malheureusement pas utile ni utilisable : on ne peut poser qu'une seule question à la fois.retrieverdécrit la manière dont on souhaite récupérer la donnée : recherche par similarité sémantique (calcul du cosinus), en sélectionnant les 3 meilleurs résultats. Le paramètrereturn_source_documentspermet de renvoyer la source du document. -
process_llm_responsepermet de mettre en forme l'output de la chaîne de QA (qui est un dictionnaire). Elle renvoie une liste contenant les informations de la source (numéro de page et localisation du ficher) ainsi que la réponse de la chaîne -
mainqui contient le cœur de l'application : l'affichage Streamlit. Il faut créer des variables de sessionslocal_llm,messages_qa(historique),qa(chaine de QA),embedding(modèle d'embedding). Pour éviter les problèmes de variables, j'ai mis le cœur des étapes dans lemain(pratique qu'il ne faut pas faire). Dans un premier temps, on charge tous les .pdf du dossier /document/. NB : Il était plus délicat de faire cette application en ajoutant un espace de dépôt de fichier avec streamlit : en effet, nous n'avons accès qu'au texte (raw) il manque donc des informations comme la page, etc... Ce qui ne permet pas de remonter à la source. Ensuite, on créer notre liste de morceaux de texte avecload_document. On télécharge un modèle d'embedding d'HuggingFace. On créé ensuitevectordbavec la librairieChroma, qui contiendra les embeddings des morceaux de textes. Enfin, pour une question donnée, on peut inférer le modèle avec la chaineqa.
Cette dernière application permet de synthétiser un document texte. En utilisant une technique au choix :
-
map reduce : On résume dans une première phase chaque morceau de texte (appels au LLM parallélisables). Puis on résume la concaténation de ces résumés.
-
refine : On résume le premier morceau de texte. Puis on résume la concaténation de ce résumé avec le deuxième morceau. Ainsi de suite jusqu'à obtenir un résumé final (appels au LLM en série).
Il y a 4 fonctions :
-
load_LLMqui va télécharger en 8bit le LLM demandé sur Hugging Face en local. On créer unepipelineregroupant certains paramètres importants : max_length, temperature, top p. On utiliseHuggingFacePipelinede LangChain. -
load_document, objet de la classeRecursiveCharacterTextSplitter, permet de découper un long texte intial, en plus petits morceaux (plusieurs méthodes possibles). Elle renvoie une liste de morceaux de textes (qui sont desDocument). -
load_chainqui va créer la chaîne grâce à la classeConversationalRetrievalChain. On y retrouve les 2 prompts utilisés (pour le map reduce, ne pas les inclure pour le refine). Avecverboseon peut observer étape par étape le processus de map reduce. -
get_pdf_textpermet de récupérer le contenu texte d'un fichier (.pdf ou .txt) déposé par l'utilisateur. -
mainqui contient le cœur de l'application : l'affichage Streamlit. Il faut créer des variables de sessionslocal_llm,chain(chaine de summarizer). Avecfile_uploaderon récupère le fichier de l'utilisateur. Ensuite, on extrait le contenu texte, et on le découpe en morceaux. On créé la chaîne avecload_chain. Enfin, on affiche (dans la pseudo-conversation avec le LLM) d'abord le texte (attention, il peut être très long !) puis le résultat de la chaîne avec la méthoderun.