Il existe encore certaines limites lors du résumé de documents très volumineux. Voici quelques moyens d’atténuer ces effets.
Traduit de Comment résumer des documents volumineux avec LangChain et OpenAI , auteur Usama Jamil.
Les grands modèles linguistiques facilitent de nombreuses tâches, telles que la création de chatbots, la traduction linguistique, la synthèse de texte, etc. Nous avions l'habitude d'écrire des modèles pour faire des résumés, mais il y avait toujours des problèmes de performances. Nous pouvons désormais le faire facilement en utilisant de grands modèles de langage (LLM) . Par exemple, les LLM de pointe (SOTA) peuvent déjà traiter des livres entiers dans leur fenêtre contextuelle. Mais il existe encore certaines limites lors du résumé de documents très volumineux.
Limites du LLM sur le résumé de documents volumineux
La limite de contexte ou la longueur du contexte dans LLM fait référence au nombre de jetons que le modèle peut gérer. Chaque modèle a sa propre longueur de contexte, également appelée marque maximale ou limite de marque. Par exemple, la longueur du contexte du modèle GPT-4 standard est de 128 000 jetons. Il perd des informations pour plus de ce nombre de balises. Certains LLM SOTA ont des limites de contexte allant jusqu'à 1 million de balises. Cependant, à mesure que les contraintes contextuelles augmentent, le LLM souffre de limitations telles que la récence et la primauté. Nous pouvons également examiner les moyens d’atténuer ces effets.
- L'effet de primauté dans LLM signifie que le modèle met davantage l'accent sur les informations présentées au début de la séquence.
- L'effet de récence se produit lorsqu'un modèle met l'accent sur les dernières informations qu'il traite.
Ces deux effets biaisent le modèle vers des parties spécifiques des données d’entrée. Le modèle peut sauter des informations importantes au milieu de la séquence.
Le deuxième problème est le coût. Nous pouvons résoudre le premier problème des contraintes de contexte en divisant le texte, mais nous ne pouvons pas transmettre directement le livre entier au modèle. Cela coûtera cher. Par exemple, si nous avions un livre avec 1 million de balises et que nous le passions directement au modèle GPT4, notre coût total serait d'environ 90 $ (balises d'indice et de complétion). Nous avons dû trouver une manière éclectique de résumer notre texte, en tenant compte du prix, des contraintes contextuelles et du contexte complet du livre.
Dans ce tutoriel, vous apprendrez à résumer un livre entier en tenant compte du prix et des contraintes contextuelles d'un modèle. commençons.
Configurer l'environnement
Pour suivre ce tutoriel, vous aurez besoin des éléments suivants :
- Python installé
- Un IDE (VS Code fonctionne)
Pour installer des dépendances, ouvrez votre terminal et entrez la commande suivante :
pip install langchain openai tiktoken fpdf2 pandas
Cette commande installera toutes les dépendances requises.
Charger des livres
Vous utiliserez David Copperfield de Charles Dickens, qui a été publié pour être utilisé dans ce projet. Chargeons ce livre en utilisant PyPDFLoader fourni par LangChain.
from langchain.document_loaders import PyPDFLoader
# Load the book
loader = PyPDFLoader("David-Copperfield.pdf")
pages = loader.load_and_split()
Cela chargera le livre entier, mais seule la partie contenu nous intéresse. Nous pouvons sauter des pages comme la préface et l'introduction.
# Cut out the open and closing parts
pages = pages[6:1308]
# Combine the pages, and replace the tabs with spaces
text = ' '.join([page.page_content.replace('\t', ' ') for page in pages]
Maintenant, nous avons du contenu. Imprimons les 200 premiers caractères.
prétraitement
Supprimons le contenu inutile du texte, tel que les caractères non imprimables, les espaces supplémentaires, etc.
import re
def clean_text(text):
# Remove the specific phrase 'Free eBooks at Planet eBook.com' and surrounding whitespace
cleaned_text = re.sub(r'\s*Free eBooks at Planet eBook\.com\s*', '', text, flags=re.DOTALL)
# Remove extra spaces
cleaned_text = re.sub(r' +', ' ', cleaned_text)
# Remove non-printable characters, optionally preceded by 'David Copperfield'
cleaned_text = re.sub(r'(David Copperfield )?[\x00-\x1F]', '', cleaned_text)
# Replace newline characters with spaces
cleaned_text = cleaned_text.replace('\n', ' ')
# Remove spaces around hyphens
cleaned_text = re.sub(r'\s*-\s*', '', cleaned_text)
return cleaned_text
clean_text=clean_text(text)
Après avoir nettoyé les données , nous pouvons nous plonger dans le problème récapitulatif.
Charger l'API OpenAI
Avant d'utiliser l'API OpenAI, nous devons la configurer ici et fournir les informations d'identification.
import os
os.environ["OPENAI_API_KEY"] = "your-openai-key-here"
Entrez votre clé API ici et cela définira la variable d'environnement.
Voyons combien de balises il y a dans ce livre :
from langchain import OpenAI
llm = OpenAI()
Tokens = llm.get_num_tokens(clean_text)
print (f"We have {Tokens} tokens in the book")
Il y a plus de 466 000 marqueurs dans le livre, et si nous les transmettions tous directement à LLM, cela nous coûterait cher. Par conséquent, pour réduire le coût, nous implémenterons le clustering K-means pour extraire des morceaux importants du livre.
Remarque : La décision d'utiliser le clustering K-means a été inspirée par un tutoriel de l'expert en données Greg Kamradt .
Afin d’obtenir les parties importantes du livre, divisons d’abord le livre en différents morceaux.
Diviser le contenu en documents
Nous utiliserons l'utilitaire SemanticChunker de LangChain pour diviser le contenu du livre en documents.
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings
text_splitter = SemanticChunker(
OpenAIEmbeddings(), breakpoint_threshold_type="interquartile"
)
docs = text_splitter.create_documents([clean_text])
SemanticChunker reçoit deux paramètres, le premier est le modèle d'intégration. Les intégrations générées par ce modèle sont utilisées pour diviser le texte en fonction de la sémantique. Le second est breakpoint_threshold_type, qui détermine le point auquel le texte doit être divisé en différents morceaux en fonction de la similarité sémantique.
Remarque : En traitant ces morceaux plus petits et sémantiquement similaires, nous visons à minimiser les effets de récence et de primauté dans LLM. Cette stratégie permet à notre modèle de gérer chaque petit contexte plus efficacement, garantissant une interprétation et une génération de réponses plus équilibrées.
Trouver des intégrations pour chaque document
Passons maintenant à l'intégration de chaque document généré. Vous utiliserez la méthode par défaut d'OpenAI pour obtenir l'intégration.
import numpy as np
import openai
def get_embeddings(text):
response = openai.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data
embeddings=get_embeddings([doc.page_content for doc in docs]
get_embeddings
La méthode peut nous fournir des intégrations pour tous les documents.
OpenAI a spécifiquement publié la méthode text-embedding-3-small, considérée comme moins chère et plus rapide.
Réarrangement des données
Ensuite, nous convertissons la liste du contenu du document et ses intégrations en un DataFrame pandas pour faciliter le traitement et l'analyse des données .
import pandas as pd
content_list = [doc.page_content for doc in docs]
df = pd.DataFrame(content_list, columns=['page_content'])
vectors = [embedding.embedding for embedding in embeddings]
array = np.array(vectors)
embeddings_series = pd.Series(list(array))
df['embeddings'] = embeddings_series
Clustering efficace avec Faiss
Nous convertissons maintenant les vecteurs de documents dans un format compatible Faiss , les regroupons en 50 groupes à l'aide de K-means, puis créons un index Faiss pour des recherches efficaces de similarité entre les documents.
import numpy as np
import faiss
# Convert to float32 if not already
array = array.astype('float32')
num_clusters = 50
# Vectors dimensionality
dimension = array.shape[1]
# Train KMeans with Faiss
kmeans = faiss.Kmeans(dimension, num_clusters, niter=20, verbose=True)
kmeans.train(array)
# Directly access the centroids
centroids = kmeans.centroids
# Create a new index for the original dataset
index = faiss.IndexFlatL2(dimension)
# Add original dataset to the index
index.add(array)
Ce regroupement K-means regroupe les documents en 50 groupes.
Remarque : La raison du choix du clustering K-means est que chaque cluster aura un contenu ou un contexte similaire, puisque tous les documents du cluster ont des intégrations liées, et nous sélectionnerons le document le plus proche du noyau.
Sélectionnez le document d'importation
Nous allons maintenant sélectionner uniquement les documents les plus importants de chaque cluster. Pour ce faire, nous sélectionnerons uniquement le premier vecteur le plus proche du centre de gravité.
D, I = index.search(centroids, 1)
Ce code utilise une méthode de recherche sur l'index pour trouver le document le plus proche de chaque centroïde dans une liste de centroïdes. Il renvoie deux tableaux :
- D, qui contient la distance du document le plus proche à son centre de gravité respectif, et
- I, qui contient l'index de ces documents récents. Le deuxième paramètre 1 de la méthode de recherche spécifie que seul le document le plus proche est trouvé pour chaque centroïde.
Nous devons maintenant trier l'index des documents sélectionnés car les documents sont dans l'ordre des livres.
sorted_array = np.sort(I, axis=0)
sorted_array=sorted_array.flatten()
extracted_docs = [docs[i] for i in sorted_array]
Obtenez un résumé de chaque document
L'étape suivante consiste à utiliser le modèle GPT-4 pour obtenir un résumé de chaque document afin d'économiser de l'argent. Pour utiliser GPT-4, nous définissons le modèle.
model = ChatOpenAI(temperature=0,model="gpt-4")
Définissez des indices et utilisez LangChain pour créer des modèles d'indices afin de les transmettre au modèle.
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("""
You will be given different passages from a book one by one. Provide a summary of the following text. Your result must be detailed and atleast 2 paragraphs. When summarizing, directly dive into the narrative or descriptions from the text without using introductory phrases like 'In this passage'. Directly address the main events, characters, and themes, encapsulating the essence and significant details from the text in a flowing narrative. The goal is to present a unified view of the content, continuing the story seamlessly as if the passage naturally progresses into the summary.
Passage:
```{text}```
SUMMARY:
"""
)
Ce modèle d'invite aidera le modèle à résumer le document de manière plus efficace et efficiente.
L'étape suivante consiste à définir la chaîne LangChain à l'aide du LangChain Expression Language (LCEL).
chain= (
prompt
| model
|StrOutputParser() )
La chaîne récapitulative utilise StrOutputParser pour analyser la sortie. Il existe d’autres analyseurs de sortie disponibles à explorer.
Vous pouvez enfin appliquer une chaîne définie sur chaque document pour obtenir le résumé.
from tqdm import tqdm
final_summary = ""
for doc in tqdm(extracted_docs, desc="Processing documents"):
# Get the new summary.
new_summary = chain2.invoke({"text": doc.page_content})
# Update the list of the last two summaries: remove the first one and add the new one at the end.
final_summary+=new_summary
Le code ci-dessus applique la chaîne à chaque document un par un et connecte chaque résumé au final_summary.
Enregistrer le résumé au format PDF
L'étape suivante consiste à formater le résumé et à l'enregistrer au format PDF.
from fpdf import FPDF
class PDF(FPDF):
def header(self):
# Select Arial bold 15
self.set_font('Arial', 'B', 15)
# Move to the right
self.cell(80)
# Framed title
self.cell(30, 10, 'Summary', 1, 0, 'C')
# Line break
self.ln(20)
def footer(self):
# Go to 1.5 cm from bottom
self.set_y(-15)
# Select Arial italic 8
self.set_font('Arial', 'I', 8)
# Page number
self.cell(0, 10, 'Page %s' % self.page_no(), 0, 0, 'C')
# Instantiate PDF object and add a page
pdf = PDF()
pdf.add_page()
pdf.set_font("Arial", size=12)
# Ensure the 'last_summary' text is treated as UTF-8
# Replace 'last_summary' with your actual text variable if different
# Make sure your text is a utf-8 encoded string
last_summary_utf8 = last_summary.encode('latin-1', 'replace').decode('latin-1')
pdf.multi_cell(0, 10, last_summary_utf8)
# Save the PDF to a file
pdf_output_path = "s_output1.pdf"
pdf.output(pdf_output_path)
Voici donc le résumé complet du livre au format PDF.
en conclusion
Dans ce didacticiel, nous explorons les complexités de l'utilisation de LLM pour résumer des textes volumineux, tels que des livres entiers, tout en abordant les défis liés aux contraintes contextuelles et aux coûts. Nous avons appris les étapes de prétraitement du texte et mis en œuvre une stratégie combinant le regroupement sémantique et le regroupement K-means pour gérer efficacement les contraintes contextuelles du modèle.
En utilisant un clustering efficace, nous extrayons efficacement les paragraphes clés, réduisant ainsi la surcharge liée au traitement direct d'énormes quantités de texte. Cette approche réduit non seulement considérablement les coûts en minimisant le nombre de jetons traités, mais atténue également les effets de récence et de primauté inhérents au LLM, garantissant ainsi une prise en compte équilibrée de tous les passages de texte.
Le développement d'applications d'IA via l'API de LLM a attiré beaucoup d'attention, dans lequel les bases de données vectorielles jouent un rôle important en fournissant un stockage et une récupération efficaces de l'intégration du contexte.
MyScaleDB est une base de données vectorielle conçue spécifiquement pour les applications d'IA, prenant en compte tous les facteurs, notamment le coût, la précision et la vitesse. Son interface conviviale pour SQL permet aux développeurs de commencer à développer leurs applications d'IA sans avoir à acquérir de nouvelles connaissances.
J'ai décidé d'abandonner l'open source Hongmeng Wang Chenglu, le père de l'open source Hongmeng : L'open source Hongmeng est le seul événement logiciel industriel d'innovation architecturale dans le domaine des logiciels de base en Chine - OGG 1.0 est publié, Huawei contribue à tout le code source. Google Reader est tué par la "montagne de merde de code" Ubuntu 24.04 LTS est officiellement publié Avant la sortie officielle de Fedora Linux 40, les développeurs Microsoft : les performances de Windows 11 sont "ridiculement mauvaises", Ma Huateng et Zhou Hongyi se serrent la main, "éliminant les rancunes" Des sociétés de jeux bien connues ont publié de nouvelles réglementations : les cadeaux de mariage des employés ne doivent pas dépasser 100 000 yuans. Pinduoduo a été condamné pour concurrence déloyale. Indemnisation de 5 millions de yuans.Cet article a été publié pour la première fois sur Yunyunzhongsheng ( https://yylives.cc/ ), tout le monde est invité à le visiter.