Cómo resumir documentos grandes usando LangChain y OpenAI

Todavía existen algunas limitaciones al resumir documentos muy grandes. A continuación se muestran algunas formas de mitigar estos efectos.

Traducido de Cómo resumir documentos grandes con LangChain y OpenAI , autor Usama Jamil.

Los modelos de lenguaje grandes facilitan muchas tareas, como crear chatbots, traducción de idiomas, resumen de textos, etc. Solíamos escribir modelos para hacer resúmenes y siempre había problemas de rendimiento. Ahora podemos hacer esto fácilmente usando modelos de lenguaje grandes (LLM) . Por ejemplo, los LLM de última generación (SOTA) ya pueden procesar libros completos dentro de su ventana contextual. Pero todavía existen algunas limitaciones a la hora de resumir documentos muy extensos.

Limitaciones de LLM en el resumen de documentos grandes

El límite de contexto o la longitud del contexto en LLM se refiere a la cantidad de tokens que el modelo puede manejar. Cada modelo tiene su propia longitud de contexto, también conocida como marca máxima o límite de marca. Por ejemplo, la longitud del contexto del modelo GPT-4 estándar es de 128.000 tokens. Pierde información para más de esa cantidad de etiquetas. Algunos SOTA LLM tienen límites de contexto de hasta 1 millón de etiquetas. Sin embargo, a medida que aumentan las limitaciones contextuales, el LLM sufre limitaciones como la actualidad y la primacía. También podemos profundizar en formas de mitigar estos efectos.

  • El efecto de primacía en LLM significa que el modelo pone más énfasis en la información presentada al comienzo de la secuencia.
  • El efecto de actualidad se produce cuando un modelo enfatiza la información más reciente que procesa.

Ambos efectos sesgan el modelo hacia partes específicas de los datos de entrada. El modelo puede omitir información importante en medio de la secuencia.

La segunda cuestión es el costo. Podemos resolver el primer problema de las restricciones de contexto dividiendo el texto, pero no podemos pasar directamente el libro completo al modelo. Esto costará mucho. Por ejemplo, si tuviéramos un libro con 1 millón de etiquetas y lo pasáramos directamente al modelo GPT4, nuestro costo total sería de aproximadamente $90 (etiquetas de pista y finalización). Tuvimos que encontrar una manera ecléctica de resumir nuestro texto, teniendo en cuenta el precio, las limitaciones contextuales y el contexto completo del libro.

En este tutorial, aprenderá a resumir un libro completo considerando el precio y las limitaciones contextuales de un modelo. Empecemos.

Configurar el entorno

Para seguir este tutorial, necesitará lo siguiente:

  • Python instalado
  • Un IDE (VS Code funciona)

Para instalar dependencias, abra su terminal e ingrese el siguiente comando:

pip install langchain openai tiktoken fpdf2 pandas

Este comando instalará todas las dependencias necesarias.

Cargar libros

Utilizará David Copperfield de Charles Dickens, que se ha publicado para su uso en este proyecto. Carguemos este libro usando PyPDFLoader proporcionado por LangChain.

from langchain.document_loaders import PyPDFLoader

# Load the book
loader = PyPDFLoader("David-Copperfield.pdf")
pages = loader.load_and_split()

Cargará el libro completo, pero solo nos interesa la parte del contenido. Podemos saltarnos páginas como el prefacio y la introducción.

# 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]

Ahora tenemos contenido. Imprimamos los primeros 200 caracteres.

preprocesamiento

Eliminemos contenido innecesario del texto, como caracteres no imprimibles, espacios extra, 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)

Después de limpiar los datos , podemos sumergirnos en el problema del resumen.

Cargar API OpenAI

Antes de usar la API OpenAI, debemos configurarla aquí y proporcionar credenciales.

import os
os.environ["OPENAI_API_KEY"] = "your-openai-key-here"

Ingrese su clave API aquí y establecerá la variable de entorno.

Veamos cuántas etiquetas hay en este libro:

from langchain import OpenAI
llm = OpenAI()
Tokens = llm.get_num_tokens(clean_text)
print (f"We have {Tokens} tokens in the book")

Hay más de 466.000 marcadores en el libro y si los pasáramos todos directamente a LLM, nos cobraría mucho. Por lo tanto, para reducir el costo, implementaremos la agrupación de K-medias para extraer partes importantes del libro.

Nota : La decisión de utilizar la agrupación en clústeres K-medias se inspiró en un tutorial del experto en datos Greg Kamradt .

Para comprender las partes importantes del libro, primero dividamos el libro en diferentes partes.

Dividir el contenido en documentos

Usaremos la utilidad SemanticChunker de LangChain para dividir el contenido del libro en documentos.

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 recibe dos parámetros, el primero es el modelo de incrustación. Las incrustaciones generadas por este modelo se utilizan para dividir texto según la semántica. El segundo es breakpoint_threshold_type, que determina el punto en el que el texto debe dividirse en diferentes fragmentos en función de la similitud semántica.

Nota: Al procesar estos fragmentos más pequeños y semánticamente similares, pretendemos minimizar los efectos de actualidad y primacía en LLM. Esta estrategia permite que nuestro modelo maneje cada contexto pequeño de manera más eficiente, asegurando una interpretación y generación de respuestas más equilibradas.

Buscar incrustaciones para cada documento

Ahora, comencemos a incrustar cada documento generado. Utilizará el método predeterminado de OpenAI para obtener la incrustación.

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_embeddingsEl método puede proporcionarnos incrustaciones para todos los documentos.

OpenAI lanzó específicamente el método text-embedding-3-small, que se considera más barato y rápido.

Reordenamiento de datos

A continuación, convertimos la lista de contenido del documento y sus incrustaciones en un DataFrame de pandas para facilitar el procesamiento y análisis de datos .

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

Agrupación eficiente usando Faiss

Ahora convertimos los vectores de documentos a un formato compatible con Faiss , los agrupamos en 50 grupos usando K-medias y luego creamos un índice de Faiss para búsquedas eficientes de similitud entre documentos.

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) 

Esta K significa agrupar documentos en 50 grupos.

Nota : La razón para elegir la agrupación de K-medias es que cada grupo tendrá contenido similar o contexto similar, ya que todos los documentos del grupo tienen incrustaciones relacionadas, y seleccionaremos el documento más cercano al núcleo.

Seleccionar documento de importación

Ahora seleccionaremos sólo los documentos más importantes de cada grupo. Para ello seleccionaremos sólo el primer vector más cercano al centroide.

D, I = index.search(centroids, 1)

Este código utiliza un método de búsqueda en el índice para encontrar el documento más cercano a cada centroide en una lista de centroides. Devuelve dos matrices:

  • D, que contiene la distancia del documento más cercano a su respectivo centroide, y
  • I, que contiene el índice de estos documentos recientes. El segundo parámetro 1 en el método de búsqueda especifica que solo se encuentra el documento más cercano para cada centroide.

Ahora necesitamos ordenar el índice del documento seleccionado ya que los documentos están en orden de libro.

sorted_array = np.sort(I, axis=0)
sorted_array=sorted_array.flatten()
extracted_docs = [docs[i] for i in sorted_array]

Obtenga un resumen de cada documento

El siguiente paso es utilizar el modelo GPT-4 para obtener un resumen de cada documento para ahorrar dinero. Para utilizar GPT-4, definimos el modelo.

model = ChatOpenAI(temperature=0,model="gpt-4")

Defina sugerencias y use LangChain para crear plantillas de sugerencias para pasarlas al modelo.

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

Esta plantilla de aviso ayudará al modelo a resumir el documento de manera más efectiva y eficiente.

El siguiente paso es definir la cadena LangChain utilizando LangChain Expression Language (LCEL).

chain= (
    prompt
   | model
   |StrOutputParser() )

La cadena de resumen utiliza StrOutputParser para analizar la salida. Hay otros analizadores de salida disponibles para explorar.

Finalmente puedes aplicar una cadena definida en cada documento para obtener el resumen.

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

El código anterior aplica la cadena a cada documento uno por uno y conecta cada resumen al final_summary.

Guardar resumen como PDF

El siguiente paso es formatear el resumen y guardarlo como 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)

Aquí tenéis el resumen completo del libro en formato PDF.

en conclusión

En este tutorial, exploramos las complejidades del uso de LLM para resumir textos extensos, como libros completos, al mismo tiempo que abordamos desafíos relacionados con las limitaciones del contexto y el costo. Aprendimos los pasos del preprocesamiento de texto e implementamos una estrategia que combinaba fragmentación semántica y agrupación de K-medias para gestionar eficazmente las restricciones contextuales del modelo.

Al utilizar una agrupación eficiente, extraemos párrafos clave de manera efectiva, lo que reduce la sobrecarga del procesamiento directo de cantidades masivas de texto. Este enfoque no solo reduce significativamente los costos al minimizar la cantidad de tokens procesados, sino que también mitiga los efectos de actualidad y primacía inherentes al LLM, lo que garantiza una consideración equilibrada de todos los pasajes del texto.

El desarrollo de aplicaciones de IA a través de la API de LLM ha atraído mucha atención, en el que las bases de datos vectoriales desempeñan un papel importante al proporcionar almacenamiento y recuperación eficientes de la incrustación de contexto.

MyScaleDB es una base de datos vectorial diseñada específicamente para aplicaciones de inteligencia artificial, que tiene en cuenta todos los factores, incluidos el costo, la precisión y la velocidad. Su interfaz compatible con SQL permite a los desarrolladores comenzar a desarrollar sus aplicaciones de IA sin tener que aprender nuevos conocimientos.

Este artículo se publicó por primera vez en Yunyunzhongsheng ( https://yylives.cc/ ), todos son bienvenidos a visitarlo.

Decidí renunciar al código abierto Hongmeng Wang Chenglu, el padre del código abierto Hongmeng: El código abierto Hongmeng es el único evento de software industrial de innovación arquitectónica en el campo del software básico en China: se lanza OGG 1.0, Huawei contribuye con todo el código fuente. Google Reader es asesinado por la "montaña de mierda de código" Ubuntu 24.04 LTS se lanza oficialmente Antes del lanzamiento oficial de Fedora Linux 40, desarrolladores de Microsoft: el rendimiento de Windows 11 es "ridículamente malo", Ma Huateng y Zhou Hongyi se dan la mano, "eliminando rencores" Conocidas empresas de juegos han emitido nuevas regulaciones: los obsequios de boda de los empleados no deben exceder los 100.000 yuanes Pinduoduo fue sentenciado por competencia desleal Indemnización de 5 millones de yuanes
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/6919515/blog/11054174
Recomendado
Clasificación