Como resumir documentos grandes usando LangChain e OpenAI

Ainda existem algumas limitações ao resumir documentos muito grandes. Aqui estão algumas maneiras de mitigar esses efeitos.

Traduzido de Como resumir documentos grandes com LangChain e OpenAI , autor Usama Jamil.

Grandes modelos de linguagem facilitam muitas tarefas, como criação de chatbots, tradução de idiomas, resumo de textos, etc. Costumávamos escrever modelos para fazer resumos e sempre havia problemas de desempenho. Agora podemos fazer isso facilmente usando modelos de linguagem grande (LLM) . Por exemplo, LLMs de última geração (SOTA) já podem processar livros inteiros dentro de sua janela de contexto. Mas ainda existem algumas limitações ao resumir documentos muito grandes.

Limitações do LLM no resumo de documentos grandes

O limite de contexto ou comprimento de contexto no LLM refere-se ao número de tokens que o modelo pode manipular. Cada modelo tem seu próprio comprimento de contexto, também conhecido como marca máxima ou limite de marca. Por exemplo, o comprimento do contexto do modelo GPT-4 padrão é de 128.000 tokens. Ele perde informações por mais do que esse número de tags. Alguns LLMs SOTA têm limites de contexto de até 1 milhão de tags. No entanto, à medida que as restrições contextuais aumentam, o LLM sofre de limitações como a atualidade e a primazia. Também podemos nos aprofundar em maneiras de mitigar esses efeitos.

  • O efeito de primazia no LLM significa que o modelo dá mais ênfase às informações apresentadas no início da sequência.
  • O efeito de atualidade ocorre quando um modelo enfatiza as informações mais recentes que processa.

Ambos os efeitos direcionam o modelo para partes específicas dos dados de entrada. O modelo pode pular informações importantes no meio da sequência.

A segunda questão é o custo. Podemos resolver o primeiro problema de restrições de contexto dividindo o texto, mas não podemos passar diretamente o livro inteiro para o modelo. Isso vai custar muito. Por exemplo, se tivéssemos um livro com 1 milhão de tags e o passássemos diretamente para o modelo GPT4, nosso custo total seria de cerca de US$ 90 (tags de dica e conclusão). Tivemos que encontrar uma forma eclética de resumir nosso texto, levando em conta o preço, as restrições contextuais e o contexto completo do livro.

Neste tutorial, você aprenderá como resumir um livro inteiro considerando o preço e as restrições contextuais de um modelo. vamos começar.

Configure o ambiente

Para seguir este tutorial, você precisará do seguinte:

  • Python instalado
  • Um IDE (código VS funciona)

Para instalar dependências, abra seu terminal e digite o seguinte comando:

pip install langchain openai tiktoken fpdf2 pandas

Este comando instalará todas as dependências necessárias.

Carregar livros

Você usará David Copperfield, de Charles Dickens, que foi publicado para uso neste projeto. Vamos carregar este livro usando PyPDFLoader fornecido pela LangChain.

from langchain.document_loaders import PyPDFLoader

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

Irá carregar o livro inteiro, mas estamos interessados ​​apenas na parte do conteúdo. Podemos pular páginas como o prefácio e a introdução.

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

Agora temos conteúdo. Vamos imprimir os primeiros 200 caracteres.

pré-processando

Vamos remover conteúdo desnecessário do texto, como caracteres não imprimíveis, espaços extras, 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)

Depois de limpar os dados , podemos mergulhar no problema do resumo.

Carregar API OpenAI

Antes de usar a API OpenAI, precisamos configurá-la aqui e fornecer credenciais.

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

Insira sua chave de API aqui e ela definirá a variável de ambiente.

Vamos ver quantas tags existem neste livro:

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

Existem mais de 466.000 marcadores no livro, e se passássemos todos diretamente para o LLM, isso nos cobraria muito. Portanto, para reduzir o custo, implementaremos o agrupamento K-means para extrair partes importantes do livro.

Nota : A decisão de usar clustering K-means foi inspirada em um tutorial do especialista em dados Greg Kamradt .

Para obter as partes importantes do livro, vamos primeiro dividir o livro em partes diferentes.

Divida o conteúdo em documentos

Usaremos o utilitário SemanticChunker do LangChain para dividir o conteúdo do livro em 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])

O SemanticChunker recebe dois parâmetros, o primeiro é o modelo de incorporação. Os embeddings gerados por este modelo são usados ​​para dividir o texto com base na semântica. O segundo é breakpoint_threshold_type, que determina o ponto em que o texto deve ser dividido em diferentes partes com base na semelhança semântica.

Nota: Ao processar esses pedaços menores e semanticamente semelhantes, pretendemos minimizar os efeitos de atualidade e primazia no LLM. Esta estratégia permite que o nosso modelo lide com cada pequeno contexto de forma mais eficiente, garantindo uma interpretação e geração de respostas mais equilibradas.

Encontre incorporações para cada documento

Agora, vamos fazer a incorporação de cada documento gerado. Você usará o método padrão OpenAI para obter a incorporação.

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_embeddingsmétodo pode nos fornecer incorporações para todos os documentos.

A OpenAI lançou especificamente o método text-embedding-3-small, que é considerado mais barato e rápido.

Reorganização de dados

Em seguida, convertemos a lista de conteúdo do documento e suas incorporações em um DataFrame do pandas para facilitar o processamento e análise de dados .

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 eficiente usando Faiss

Agora convertemos os vetores de documentos em um formato compatível com Faiss , agrupamos-os em 50 grupos usando K-means e, em seguida, criamos um índice Faiss para pesquisas eficientes de similaridade 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) 

Este agrupamento K-means agrupa documentos em 50 grupos.

Nota : A razão para escolher o clustering K-means é que cada cluster terá conteúdo semelhante ou contexto semelhante, uma vez que todos os documentos no cluster possuem incorporações relacionadas, e selecionaremos o documento mais próximo do núcleo.

Selecione o documento de importação

Agora selecionaremos apenas os documentos mais importantes de cada cluster. Para fazer isso selecionaremos apenas o primeiro vetor mais próximo do centróide.

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

Este código usa um método de pesquisa no índice para encontrar o documento mais próximo de cada centróide em uma lista de centróides. Ele retorna duas matrizes:

  • D, que contém a distância do documento mais próximo ao seu respectivo centróide, e
  • I, que contém o índice desses documentos recentes. O segundo parâmetro 1 no método de pesquisa especifica que apenas o documento mais próximo é encontrado para cada centróide.

Agora precisamos classificar o índice do documento selecionado, pois os documentos estão na ordem do livro.

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

Obtenha um resumo de cada documento

A próxima etapa é usar o modelo GPT-4 para obter um resumo de cada documento e economizar dinheiro. Para usar o GPT-4, definimos o modelo.

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

Defina dicas e use LangChain para criar modelos de dicas para passá-los ao 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:
"""
)

Este modelo de prompt ajudará o modelo a resumir o documento de forma mais eficaz e eficiente.

A próxima etapa é definir a cadeia LangChain usando LangChain Expression Language (LCEL).

chain= (
    prompt
   | model
   |StrOutputParser() )

A cadeia de resumo usa StrOutputParser para analisar a saída. Existem outros analisadores de saída disponíveis para explorar.

Você pode finalmente aplicar uma cadeia definida em cada documento para obter o resumo.

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

O código acima aplica a cadeia a cada documento, um por um, e conecta cada resumo ao final_summary.

Salvar resumo como PDF

A próxima etapa é formatar o resumo e salvá-lo 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)

Então aqui está o resumo completo do livro em formato PDF.

para concluir

Neste tutorial, exploramos as complexidades do uso do LLM para resumir textos grandes, como livros inteiros, ao mesmo tempo que abordamos desafios relacionados a restrições de contexto e custos. Aprendemos as etapas de pré-processamento de texto e implementamos uma estratégia que combinava fragmentação semântica e agrupamento K-means para gerenciar com eficácia as restrições contextuais do modelo.

Ao usar clustering eficiente, extraímos efetivamente os parágrafos principais, reduzindo a sobrecarga do processamento direto de grandes quantidades de texto. Esta abordagem não só reduz significativamente os custos, minimizando o número de tokens processados, mas também mitiga os efeitos de atualidade e primazia inerentes ao LLM, garantindo uma consideração equilibrada de todas as passagens de texto.

O desenvolvimento de aplicações de IA através da API do LLM tem atraído muita atenção, nas quais os bancos de dados vetoriais desempenham um papel importante, fornecendo armazenamento eficiente e recuperação de incorporação de contexto.

MyScaleDB é um banco de dados vetorial projetado especificamente para aplicações de IA, levando em consideração todos os fatores, incluindo custo, precisão e velocidade. Sua interface amigável com SQL permite que os desenvolvedores comecem a desenvolver seus aplicativos de IA sem precisar aprender novos conhecimentos.

Este artigo foi publicado pela primeira vez em Yunyunzhongsheng ( https://yylives.cc/ ), todos são bem-vindos para visitar.

Decidi desistir do código aberto Hongmeng Wang Chenglu, o pai do código aberto Hongmeng: Hongmeng de código aberto é o único evento de software industrial de inovação arquitetônica na área de software básico na China - o OGG 1.0 é lançado, a Huawei contribui com todo o código-fonte. Google Reader é morto pela "montanha de merda de código" Ubuntu 24.04 LTS é lançado oficialmente Antes do lançamento oficial do Fedora Linux 40, desenvolvedores da Microsoft: o desempenho do Windows 11 é "ridiculamente ruim", Ma Huateng e Zhou Hongyi apertam as mãos, "eliminando rancores" Empresas de jogos conhecidas emitiram novos regulamentos: os presentes de casamento dos funcionários não devem exceder 100.000 yuans Pinduoduo foi condenado por concorrência desleal Compensação de 5 milhões de yuans
{{o.nome}}
{{m.nome}}

Acho que você gosta

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