Lección 1 · 5 min · Gratis

Procesamiento y evaluación de informes financieros con LlamaParse

En esta lección, aprenderás a procesar y analizar informes financieros en formato PDF utilizando LlamaParse. Este proceso te permitirá extraer información clave, organizar el contenido en fragmentos manejables y generar palabras clave contextuales para mejorar la capacidad de búsqueda y recuperación de datos. Al final, podrás evaluar la efectividad de tu método de recuperación de información, lo cual es fundamental para aplicaciones como sistemas de preguntas y respuestas o análisis de documentos.

Para este ejemplo, utilizaremos un documento de informe financiero obtenido de https://github.com/patronus-ai/financebench/tree/main/pdfs.

Pasos:

  1. Analiza el documento del informe financiero (archivo PDF) usando LlamaParse.
  2. Divide el documento en fragmentos (chunks). Aquí usamos una división básica basada en tokens con un tamaño de fragmento constante, pero puedes usar cualquier otro método para la división.
  3. Genera palabras clave contextuales para cada fragmento.
  4. Crea preguntas relacionadas con fragmentos seleccionados aleatoriamente, ya que no hay un conjunto de pruebas predefinido disponible.
  5. Evalúa el método recuperando los cinco fragmentos más relevantes basándose en la similitud de coseno entre los embeddings del fragmento y la pregunta, verificando si el fragmento correcto está incluido.
  6. Para comparar los resultados con el contenido sin procesar (sin palabras clave), crea un nuevo índice en la sección "Crear índice" y modifica la estructura del documento reemplazando Document(text='#'+x['keywords']+'\n'+x['content'], .. ) con Document(text=x['content'], .. ).

Antes de comenzar, asegúrate de tener una clave de API válida para LlamaParse. Esta clave es necesaria para autenticar tus solicitudes y utilizar el servicio de análisis de documentos.

El primer paso es inicializar LlamaParse y cargar el documento PDF. Este proceso convierte el contenido del PDF en un formato estructurado que puede ser procesado.

# Install dependencies
!pip install tiktoken
!pip install torch
!pip install transformers
!pip install llama_parse
!pip install llama_index

import random
import os
import sys
import json
from llama_parse import LlamaParse
from llama_index.core import SimpleDirectoryReader
from llama_index.core.schema import Document
from config import LLAMAPARSE_API_KEY

# Enable nested async loops
import nest_asyncio
nest_asyncio.apply()

Una vez que el documento ha sido analizado, lo dividimos en fragmentos más pequeños. Esto es crucial para manejar documentos grandes y para que los modelos de lenguaje puedan procesar la información de manera más eficiente. Aquí, la división se basa en el número de tokens, asegurando que cada fragmento tenga un tamaño manejable.

# download the pdf file into ./data folder 
!wget -P data/ https://github.com/patronus-ai/financebench/raw/main/pdfs/AMAZON_2015_10K.pdf
!mkdir temp

# Parse pdf file
parser = LlamaParse(
    api_key=LLAMAPARSE_API_KEY,
    result_type="markdown"  # "markdown" and "text" are available
)
file_extractor = {".pdf": parser}
documents = SimpleDirectoryReader(input_files=['./data/AMAZON_2015_10K.pdf'], file_extractor=file_extractor).load_data()
print("pdf file pages:", len(documents))

A continuación, generamos palabras clave contextuales para cada fragmento. Estas palabras clave ayudan a resumir el contenido de cada fragmento y mejoran la capacidad de búsqueda, permitiendo una recuperación más precisa de la información relevante.

# Split into chunks (by tokens)
from helper import file_get_contents, file_put_contents, generate_contextual_keywords, get_llm_answer, generate_questions_bychunk
import tiktoken
enc = tiktoken.get_encoding("o200k_base")

def split_into_chunks(content, chunk_size):
	a = enc.encode(content)
	left, chunks = 0, []
	while left < len(a):
		arr = a[left : left+chunk_size]
		chunks.append(enc.decode(arr))
		left+=chunk_size
	return chunks
    
def generate_chunked_content(chunks):
    chunked_content = ""
    for idx, text in enumerate(chunks):
      chunked_content+=f"### Chunk {idx+1} ###\n{text}\n\n"
    return chunked_content
    

# Generate contextual keywords
path = "./temp/chunks2.json"
if not os.path.exists(path):
    print("Generating keywords..")
    document_content, chunks, chunks2 = "", [], []
    for doc in documents: document_content+=doc.text+"\n"
    chunks1 = split_into_chunks(document_content, 400) #400 -- defaulf value
    for i, chunk in enumerate(chunks1):
        chunks.append(chunk)
        if (len(chunks) > 10 or (i==len(chunks1)-1) and len(chunks)>2):
            chunked_content = generate_chunked_content(chunks)
            keywords = generate_contextual_keywords(chunked_content)        
            print("page_end:", i+1, keywords, len(keywords), len(chunks))            
            assert len(keywords) >= len(chunks)
            for j in range(len(chunks)): chunks2.append( {"idx":j, "keywords":keywords[j], "content":chunks[j]} )
            chunks = []
    file_put_contents(path, json.dumps(chunks2))
else:
    chunks2 = json.loads(file_get_contents(path)) #it has content, keywords, idx


# Generate questions
path = "./temp/chunks3.json"
if not os.path.exists(path):
    print("Generating questions..")
    chunks3 = generate_questions_bychunk(chunks2) 
    file_put_contents(path, json.dumps(chunks3))
else:
    chunks3 = json.loads(file_get_contents(path)) #it has content, keywords, questions, idx now

Dado que no disponemos de un conjunto de pruebas predefinido, creamos preguntas relacionadas con fragmentos seleccionados aleatoriamente. Esto nos permite simular un escenario de uso real y evaluar qué tan bien nuestro sistema puede recuperar la información correcta basándose en una pregunta.

# Create Index    
from llama_index.core import GPTVectorStoreIndex, StorageContext, load_index_from_storage, Settings
from embedding import LocalJinaEmbedding #locally run the jinai embedding model

INDEX_DIR = "./temp/local_index_cache"
if not os.path.exists(INDEX_DIR):
    print("Creating new index ...")    
    Settings.embed_model = LocalJinaEmbedding()
    Settings.llm = None
    documents2 = [Document(text='#'+",".join(x['keywords'])+'\n'+x['content'], metadata={"id": str(x["idx"])}) for x in chunks3] 
    index = GPTVectorStoreIndex.from_documents(documents2)
    index.storage_context.persist(persist_dir=INDEX_DIR)
else:
    storage_context = StorageContext.from_defaults(persist_dir=INDEX_DIR)
    index = load_index_from_storage(storage_context)
query_engine = index.as_query_engine(similarity_top_k=5)

# Run tests
count, correct = 0, 0
for test in chunks3[:]:
    if not "questions" in test: continue
    idx = test["idx"]
    for question in test["questions"]:
        count+=1
        response = query_engine.query(question)
        print("\n\n--- Test:", question, "idx:", idx)
        for result in response.source_nodes[:]:
            print(result.node.metadata) #prompt+=f"\n\n<Document>\n                     
            if result.node.metadata['id'] == str(idx): correct+=1                

print("Test correct, all:", correct, count)

Finalmente, evaluamos el método. Recuperamos los cinco fragmentos más relevantes para cada pregunta utilizando la similitud de coseno entre los embeddings de la pregunta y los fragmentos. Luego, verificamos si el fragmento original (el "correcto") está entre los recuperados. Esta métrica nos da una idea de la precisión de nuestro sistema de recuperación.

--- Test: What is the fiscal year end date for Adobe Systems Incorporated's annual report filed with the SEC in 2015? idx: 0
{'id': '7'}
{'id': '0'}
{'id': '0'}
{'id': '4'}
{'id': '2'}


--- Test: What is the Commission File Number for Adobe Systems Incorporated? idx: 0
{'id': '1'}
{'id': '0'}
{'id': '10'}
{'id': '468'}
{'id': '397'}


--- Test: In which state is Adobe Systems Incorporated incorporated or organized? idx: 0
{'id': '10'}
{'id': '280'}
{'id': '9'}
{'id': '1'}
{'id': '8'}


--- Test: How is Adobe's business organized? idx: 13
{'id': '10'}
{'id': '280'}
{'id': '10'}
{'id': '9'}
{'id': '5'}


--- Test: What are Adobe's two strategic growth opportunities? idx: 13
{'id': '0'}
{'id': '3'}
{'id': '13'}
{'id': '1'}
{'id': '6'}


--- Test: What are the three reportable segments of Adobe's business? idx: 13
{'id': '7'}
{'id': '10'}
{'id': '184'}
{'id': '13'}
{'id': '9'}


--- Test: What is Adobe Digital Publishing Solution used for? idx: 16
{'id': '3'}
{'id': '9'}
{'id': '3'}
{'id': '10'}
{'id': '16'}


--- Test: What are some examples of mobile apps offered by Adobe's Digital Media business? idx: 16
{'id': '5'}
{'id': '3'}
{'id': '16'}
{'id': '0'}
{'id': '9'}


--- Test: What is the main purpose of Adobe's Document Cloud business? idx: 16
{'id': '4'}
{'id': '6'}
{'id': '5'}
{'id': '3'}
{'id': '0'}


--- Test: What is Adobe's strategy to accelerate the adoption of Creative Cloud? idx: 18
{'id': '8'}
{'id': '10'}
{'id': '18'}
{'id': '3'}
{'id': '174'}


--- Test: How does Adobe's subscription model benefit new customers? idx: 18
{'id': '9'}
{'id': '0'}
{'id': '6'}
{'id': '5'}
{'id': '18'}


--- Test: What is driving the increase in Adobe's recurring revenue over the next several years? idx: 18
{'id': '2'}
{'id': '6'}
{'id': '4'}
{'id': '18'}
{'id': '1'}


--- Test: What is Adobe Stock and how is it integrated with Creative Cloud apps? idx: 29
{'id': '29'}
{'id': '3'}
{'id': '10'}
{'id': '8'}
{'id': '4'}


--- Test: How do Adobe's mobile apps extend the capabilities of Creative Cloud desktop tools? idx: 29
{'id': '6'}
{'id': '29'}
{'id': '4'}
{'id': '8'}
{'id': '5'}


--- Test: What licensing options are available for Creative Cloud, and how can individuals and teams subscribe to the service? idx: 29
{'id': '6'}
{'id': '174'}
{'id': '10'}
{'id': '18'}
{'id': '8'}


--- Test: Who are the main competitors of Adobe in the electronic signatures market? idx: 48
{'id': '48'}
{'id': '3'}
{'id': '5'}
{'id': '7'}
{'id': '6'}


--- Test: What companies compete with Adobe Marketing Cloud in the digital marketing space? idx: 48
{'id': '7'}
{'id': '48'}
{'id': '1'}
{'id': '3'}
{'id': '6'}


--- Test: How do Adobe's competitors deliver their solutions to customers in the digital marketing market? idx: 48
{'id': '48'}
{'id': '7'}
{'id': '1'}
{'id': '6'}
{'id': '1'}


--- Test: What was Michael Dillon's role at Sun Microsystems from April 2006 to January 2010? idx: 67
{'id': '67'}
{'id': '3'}
{'id': '2'}
{'id': '0'}
{'id': '77'}


--- Test: Which company
… (salida recortada)

Resumen

  • Hemos utilizado LlamaParse para analizar un informe financiero en formato PDF, extrayendo su contenido de manera estructurada.
  • Dividimos el documento analizado en fragmentos manejables, lo cual es esencial para el procesamiento eficiente de grandes volúmenes de texto.
  • Generamos palabras clave contextuales para cada fragmento, lo que enriquece la representación del contenido y mejora la capacidad de búsqueda.
  • Creamos preguntas sintéticas para evaluar la capacidad de nuestro sistema de recuperar información relevante.
  • Evaluamos la efectividad del método de recuperación de información, midiendo la precisión con la que se identifican los fragmentos correctos.
Lección del curso «Llama Cookbook (use cases)» de Meta, publicado con licencia MIT. Traducción y adaptación al español de IA con Clase. IA con Clase no está afiliado a Meta. Ver el original · Licencia
Esta lección es gratuita. El resto del curso se abre con la Membresía de IA con Clase, que incluye todos los cursos del catálogo. Ver precios