Una guía sobre cómo extraer temas de manera eficiente de documentos grandes utilizando modelos de lenguaje grande (LLM) y el algoritmo de asignación latente de Dirichlet (LDA).
Introducción
Estaba desarrollando una aplicación web para chatear con archivos PDF, capaz de procesar documentos grandes, de más de 1000 páginas. Pero antes de iniciar una conversación con el documento, quería que la aplicación le diera al usuario un breve resumen de los temas principales, así sería más fácil iniciar la interacción.
Una forma de hacerlo es resumiendo el documento usando LangChaincomo se muestra en su documentación. El problema, sin embargo, es el elevado coste computacional y, por extensión, el coste monetario. Un documento de mil páginas contiene aproximadamente 250.000 palabras y cada palabra debe introducirse en el LLM. Es más, los resultados deben procesarse más, como ocurre con el método de reducción de mapas. Una estimación conservadora del costo de usar gpt-3.5 Turbo con contexto 4k es superior a 1 dólar por documento, solo para el resumen. Incluso cuando se utilizan recursos gratuitos, como el API no oficial de HuggingChat, la gran cantidad de llamadas API requeridas sería un abuso. Entonces necesitaba un enfoque diferente.
LDA al rescate
El algoritmo de asignación latente de Dirichlet fue una elección natural para esta tarea. Este algoritmo toma un conjunto de “documentos” (en este contexto, un “documento” se refiere a un fragmento de texto) y devuelve una lista de temas para cada “documento” junto con una lista de palabras asociadas con cada tema. Lo importante para nuestro caso es la lista de palabras asociadas a cada tema. Estas listas de palabras codifican el contenido del archivo, por lo que pueden enviarse al LLM para solicitar un resumen. recomiendo Este artículo para una explicación detallada del algoritmo.
Hay dos consideraciones clave que debemos abordar antes de que podamos obtener un resultado de alta calidad: seleccionar los hiperparámetros para el algoritmo LDA y determinar el formato de la salida. El hiperparámetro más importante a considerar es el número de temas, ya que es el más significativo en el resultado final. En cuanto al formato de salida, uno que funcionó bastante bien es la lista con viñetas anidadas. En este formato, cada tema se representa como una lista con viñetas con subentradas que describen con más detalle el tema. En cuanto a por qué funciona esto, creo que, al usar este formato, el modelo puede centrarse en extraer contenido de las listas sin la complejidad de articular párrafos con conectores y relaciones.
Implementación
Implementé el código en colaboración de google. Las bibliotecas necesarias fueron gensim para LDA, pypdf para procesamiento de PDF, nltk para procesamiento de textos y LangChain para sus plantillas de solicitud y su interfaz con la API OpenAI.
import gensim
import nltk
from gensim import corpora
from gensim.models import LdaModel
from gensim.utils import simple_preprocess
from nltk.corpus import stopwords
from pypdf import PdfReader
from langchain.chains import LLMChain
from langchain.prompts import ChatPromptTemplate
from langchain.llms import OpenAI
A continuación, definí una función de utilidad, preproceso, para ayudar en el procesamiento del texto de entrada. Elimina palabras vacías y tokens cortos.
def preprocess(text, stop_words):
"""
Tokenizes and preprocesses the input text, removing stopwords and short
tokens.Parameters:
text (str): The input text to preprocess.
stop_words (set): A set of stopwords to be removed from the text.
Returns:
list: A list of preprocessed tokens.
"""
result = []
for token in simple_preprocess(text, deacc=True):
if token not in stop_words and len(token) > 3:
result.append(token)
return result
La segunda función, get_topic_lists_from_pdfimplementa el LDA porción del código. Acepto la ruta al archivo PDF, la cantidad de temas y la cantidad de palabras por tema, y me devuelve una lista. Cada elemento de esta lista contiene una lista de palabras asociadas con cada tema. Aquí, consideramos que cada página del archivo PDF es un “documento”.
def get_topic_lists_from_pdf(file, num_topics, words_per_topic):
"""
Extracts topics and their associated words from a PDF document using the
Latent Dirichlet Allocation (LDA) algorithm.Parameters:
file (str): The path to the PDF file for topic extraction.
num_topics (int): The number of topics to discover.
words_per_topic (int): The number of words to include per topic.
Returns:
list: A list of num_topics sublists, each containing relevant words
for a topic.
"""
# Load the pdf file
loader = PdfReader(file)
# Extract the text from each page into a list. Each page is considered a document
documents= []
for page in loader.pages:
documents.append(page.extract_text())
# Preprocess the documents
nltk.download('stopwords')
stop_words = set(stopwords.words(['english','spanish']))
processed_documents = [preprocess(doc, stop_words) for doc in documents]
# Create a dictionary and a corpus
dictionary = corpora.Dictionary(processed_documents)
corpus = [dictionary.doc2bow(doc) for doc in processed_documents]
# Build the LDA model
lda_model = LdaModel(
corpus,
num_topics=num_topics,
id2word=dictionary,
passes=15
)
# Retrieve the topics and their corresponding words
topics = lda_model.print_topics(num_words=words_per_topic)
# Store each list of words from each topic into a list
topics_ls = []
for topic in topics:
words = topic[1].split("+")
topic_words = [word.split("*")[1].replace('"', '').strip() for word in words]
topics_ls.append(topic_words)
return topics_ls
La siguiente función, temas_de_pdf, invoca el modelo LLM. Como se indicó anteriormente, se solicitó al modelo que formateara la salida como una lista con viñetas anidada.
def topics_from_pdf(llm, file, num_topics, words_per_topic):
"""
Generates descriptive prompts for LLM based on topic words extracted from a
PDF document.This function takes the output of `get_topic_lists_from_pdf` function,
which consists of a list of topic-related words for each topic, and
generates an output string in table of content format.
Parameters:
llm (LLM): An instance of the Large Language Model (LLM) for generating
responses.
file (str): The path to the PDF file for extracting topic-related words.
num_topics (int): The number of topics to consider.
words_per_topic (int): The number of words per topic to include.
Returns:
str: A response generated by the language model based on the provided
topic words.
"""
# Extract topics and convert to string
list_of_topicwords = get_topic_lists_from_pdf(file, num_topics,
words_per_topic)
string_lda = ""
for list in list_of_topicwords:
string_lda += str(list) + "\n"
# Create the template
template_string = '''Describe the topic of each of the {num_topics}
double-quote delimited lists in a simple sentence and also write down
three possible different subthemes. The lists are the result of an
algorithm for topic discovery.
Do not provide an introduction or a conclusion, only describe the
topics. Do not mention the word "topic" when describing the topics.
Use the following template for the response.
1: <<<(sentence describing the topic)>>>
- <<<(Phrase describing the first subtheme)>>>
- <<<(Phrase describing the second subtheme)>>>
- <<<(Phrase describing the third subtheme)>>>
2: <<<(sentence describing the topic)>>>
- <<<(Phrase describing the first subtheme)>>>
- <<<(Phrase describing the second subtheme)>>>
- <<<(Phrase describing the third subtheme)>>>
...
n: <<<(sentence describing the topic)>>>
- <<<(Phrase describing the first subtheme)>>>
- <<<(Phrase describing the second subtheme)>>>
- <<<(Phrase describing the third subtheme)>>>
Lists: """{string_lda}""" '''
# LLM call
prompt_template = ChatPromptTemplate.from_template(template_string)
chain = LLMChain(llm=llm, prompt=prompt_template)
response = chain.run({
"string_lda" : string_lda,
"num_topics" : num_topics
})
return response
En la función anterior, la lista de palabras se convierte en una cadena. Luego, se crea un mensaje usando el Plantilla de solicitud de chat objeto de LangChain; tenga en cuenta que el mensaje define la estructura de la respuesta. Finalmente, la función llama al modelo chatgpt-3.5 Turbo. El valor de retorno es la respuesta dada por el modelo LLM.
Ahora es el momento de llamar a las funciones. Primero configuramos la clave API. tsu articulo ofrece instrucciones sobre cómo conseguir uno.
openai_key = "sk-p..."
llm = OpenAI(openai_api_key=openai_key, max_tokens=-1)
A continuación llamamos al temas_de_pdf función. Elijo los valores para el número de temas y el número de palabras por tema. Además, seleccioné un dominio publico libro, La Metamorfosis de Franz Kafka, para realizar pruebas. El documento se almacena en mi disco personal y se descarga utilizando la biblioteca gdown.
!gdown https://drive.google.com/uc?id=1mpXUmuLGzkVEqsTicQvBPcpPJW0aPqdLfile = "./the-metamorphosis.pdf"
num_topics = 6
words_per_topic = 30
summary = topics_from_pdf(llm, file, num_topics, words_per_topic)
El resultado se muestra a continuación:
1: Exploring the transformation of Gregor Samsa and the effects on his family and lodgers
- Understanding Gregor's metamorphosis
- Examining the reactions of Gregor's family and lodgers
- Analyzing the impact of Gregor's transformation on his family2: Examining the events surrounding the discovery of Gregor's transformation
- Investigating the initial reactions of Gregor's family and lodgers
- Analyzing the behavior of Gregor's family and lodgers
- Exploring the physical changes in Gregor's environment
3: Analyzing the pressures placed on Gregor's family due to his transformation
- Examining the financial strain on Gregor's family
- Investigating the emotional and psychological effects on Gregor's family
- Examining the changes in family dynamics due to Gregor's metamorphosis
4: Examining the consequences of Gregor's transformation
- Investigating the physical changes in Gregor's environment
- Analyzing the reactions of Gregor's family and lodgers
- Investigating the emotional and psychological effects on Gregor's family
5: Exploring the impact of Gregor's transformation on his family
- Analyzing the financial strain on Gregor's family
- Examining the changes in family dynamics due to Gregor's metamorphosis
- Investigating the emotional and psychological effects on Gregor's family
6: Investigating the physical changes in Gregor's environment
- Analyzing the reactions of Gregor's family and lodgers
- Examining the consequences of Gregor's transformation
- Exploring the impact of Gregor's transformation on his family
¡El resultado es bastante decente y solo tomó unos segundos! Extrajo correctamente las ideas principales del libro.
Este enfoque también funciona con libros técnicos. Por ejemplo, Los fundamentos de la geometría por David Hilbert (1899) (también en el dominio público):
1: Analyzing the properties of geometric shapes and their relationships
- Exploring the axioms of geometry
- Analyzing the congruence of angles and lines
- Investigating theorems of geometry2: Studying the behavior of rational functions and algebraic equations
- Examining the straight lines and points of a problem
- Investigating the coefficients of a function
- Examining the construction of a definite integral
3: Investigating the properties of a number system
- Exploring the domain of a true group
- Analyzing the theorem of equal segments
- Examining the circle of arbitrary displacement
4: Examining the area of geometric shapes
- Analyzing the parallel lines and points
- Investigating the content of a triangle
- Examining the measures of a polygon
5: Examining the theorems of algebraic geometry
- Exploring the congruence of segments
- Analyzing the system of multiplication
- Investigating the valid theorems of a call
6: Investigating the properties of a figure
- Examining the parallel lines of a triangle
- Analyzing the equation of joining sides
- Examining the intersection of segments
Conclusión
La combinación del algoritmo LDA con LLM para la extracción de temas de documentos de gran tamaño produce buenos resultados y, al mismo tiempo, reduce significativamente tanto el costo como el tiempo de procesamiento. Hemos pasado de cientos de llamadas API a solo una y de minutos a segundos.
La calidad del resultado depende en gran medida de su formato. En este caso, una lista con viñetas anidadas funcionó bien. Además, la cantidad de temas y la cantidad de palabras por tema son importantes para la calidad del resultado. Recomiendo probar diferentes indicaciones, cantidad de temas y cantidad de palabras por tema para encontrar lo que funciona mejor para un documento determinado.
El código se puede encontrar en este enlace.