Ingestão de dados

Explore mais o que o RAG Cookbook tem a oferecer para obter insights mais aprofundados das soluções RAG atuais

Um leadspace recolorido usando o leadspace do Watson para atenção ao cliente como base.
Visão geral

A ingestão é o processo de análise de informações de documentos de origem para que elas possam ser incorporadas em um espaço de pesquisa para recuperação posterior. Embora este seja um processo simples para texto simples, surgem complicações quando os documentos de origem estão em formatos que não são de "texto", por exemplo, Microsoft Word ou PDF e quando contêm formatação complexa, como cabeçalhos e rodapés repetidos, texto em várias colunas ou tabelas.

Esta seção contém dicas, técnicas, aceleradores e referências a recursos que ajudam a superar esses desafios.

Por onde começar?

 

Se você não possui conhecimentos técnicos, seus documentos são relativamente simples e você precisa de uma solução no-code, use o watsonx Orchestrate.

 

Se você é um usuário técnico, seus documentos são relativamente simples e você tem acesso ao Watson Discovery, comece a partir daí. O Watson Discovery tem uma IU agradável com necessidade mínima de programação. Para documentos complexos, você pode usar o watson discovery para anotar e extrair partes específicas de seus documentos, seja para o pipeline de back-end da RAG ou para front-end e destacar os textos, obtendo a posição exata de um parágrafo dentro de seus documentos.

 

No entanto, o Watson Discovery tem suas limitações. Se seus documentos tiverem mais de 50 MB, você não poderá carregá-los no Watson Discovery. Se o documento for muito complexo (ou seja, inclui tabelas aninhadas ou formatos de tabela irregulares), o Watson Discovery pode não capturar toda a estrutura do documento. Talvez você queira implementar um pipeline de ingestão de dados personalizado usando bibliotecas de código aberto para esses casos.

 

Com relação às bibliotecas de código aberto, o LangChain e o LlamaIndex são muito semelhantes em termos de recursos de ingestão de dados. No entanto, o LangChain seria mais fácil de usar do que o LlamaIndex em termos de documentação e integração com bancos de dados de vetores e outras aplicações. Tanto o LangChain quanto o LlamaIndex possuem funções auxiliares que podem converter um objeto Document de um para o outro, permitindo alternar entre LangChain e LlamaIndex em qualquer etapa do seu pipeline.

Ingestão de documentos com tabelas complexas

As notas a seguir incluem diversas lições aprendidas sobre a ingestão de documentos que contêm tabelas complexas e diferentes abordagens que foram testadas durante um dos projetos. Veja que algumas das observações podem ser diferentes de um trabalho para outro, mas a maioria das conclusões deve ser semelhante para trabalhos que incluem documentos com tabelas aninhadas e complexas.

Watson Discovery

O Watson Discovery não suporta documentos maiores que 50 MB. Nos casos em que o tamanho grande se deve ao fato de os documentos originais estarem no formato Word, precisamos converter os documentos do Word em PDF para usar o Watson Discovery. No entanto, usando essa abordagem, o Watson Discovery às vezes não consegue capturar formatos de tabela de forma adequada, especialmente em casos de tabelas complexas ou aninhadas.

Para ingestão usando o Watson Discovery, duas abordagens diferentes podem ser testadas: um modelo pré-treinado e um modelo de usuário treinado anotando manualmente algumas páginas por meio do Smart Document Understanding do Discovery. A seguir, são discutidas as lições aprendidas com essas duas abordagens em um dos projetos que envolvia tabelas aninhadas complexas.

  • modelo pré-treinado
    • Prós: 
      • A saída HTML do Discovery muitas vezes pode capturar a estrutura das tabelas melhor do que um modelo treinado pelo usuário (SDU)
    • Contras: 
      • O Discovery ainda tinha dificuldade em detectar tabelas aninhadas. Em alguns casos, ele veria apenas a tabela mais interna como tabela e capturaria as tabelas externas como texto, perdendo a estrutura de dados.
    • Ao usar o Watson Discovery, o uso do texto bruto pode resultar em melhores resultados em comparação com o uso de saídas HTML.  

  • modelo definido pelo usuário
    • Prós: 
      • Muito fácil de anotar e ideal para documentos simples em que você deseja filtrar seções específicas posteriormente.
    • Contras:
      • Não foi possível detectar estruturas de tabelas aninhadas, mesmo após anotarmos todas as tabelas contidas dentro de outras tabelas.
      • Mesmo para as tabelas simples, houve problemas para detectá-las em novos documentos; mesmo após anotar manualmente 20 páginas de mais de 7 documentos.

Embora os resultados do Watson Discovery não tenham sido tão precisos quanto os de outras bibliotecas personalizadas mencionadas abaixo para tabelas complexas e aninhadas, os resultados incluíram muitos metadados, como números de página e coordenadas dos textos, que são úteis para exibir os destaques na IU. Esses metadados podem ser muito úteis para adicionar filtros extras durante a recuperação ou até mesmo para dividir os documentos com base em determinadas seções HTML.

PyPDFLoader x PyMuPDF

A biblioteca PyMuPDF é muito mais rápida e gera resultados mais precisos, especialmente em dados maiores. Usando as duas funções a seguir em um conjunto de dados de 50,6 MB, foram necessários 0,131 segundos para o PyMuPDF contra 0,366 segundos para o PypdfLoader da LangChain. Portanto, para arquivos grandes e se você não quiser extrair seções separadas do documento como seções separadas, use a biblioteca PyMuPdf.

def pdf_to_text(path: str,
                        start_page: int = 1,
                        end_page: Optional[int | None] = None) -> (list[str], list[str], list[dict]):
    """
    Converte PDF em texto simples.

    Parâmetros:
        path (str): caminho para o arquivo PDF.
        start_page (int): página para começar a obter texto.
        end_page (int): última página para obter texto.
    """
    logger.debug("Processing PDF %s".format(path))

    from langchain_community.document_loaders import PyPDFLoader

    loader = PyPDFLoader(path)
    pages = loader.load()
    total_pages = len(pages)
    logger.debug(f'Total pages: {total_pages}')
    _ids = []
    _metadata = []
    _file_name = Path(path).name

    if end_page is None:
        end_page = len(pages)

    _text_list = []
    for index in range(start_page - 1, end_page):
        text = pages[index].page_content
        text = text.replace('\n', ' ')
        text = re.sub(r'\s+', ' ', text)
        _text_list.append(text)
        _ids.append(f'page_{index + 1}')
        _metadata.append({
            "id": index + 1,
            "file_name": _file_name,
            "page": f'page {index + 1}'
        })

    return _text_list, _ids, _metadata

def large_pdf_to_text(path: str,
                        start_page: int = 1,
                        end_page: Optional[int | None] = None,
                        remove_string_list=None) -> (list[str], list[str], list[dict]):
    """
        Converte PDF em texto simples usando PyMuPDF, velocidade de execução impressionante, tornando-o adequado para
       processamento de PDF em grande escala. Embora não ofereça funcionalidade dedicada à extração de tabelas, pode
        ainda ser usado para analisar a estrutura do PDF e extrair dados tabulares com processamento adicional,
se necessário.

        50,6 MB -> 0,131 segundos (PyMuPDF) x 0,366 segundos (pypdf)

        Parâmetros:
            file_stream (stream): fluxo do arquivo PDF.
            path (str): caminho para o PDF
            start_page (int): página para começar a obter texto.
            end_page (int): última página para obter texto.
            remove_string_list (list[str]): remover lista de strings.
        """
    logger.debug("Processing PDF %s".format(path))
    if remove_string_list is None:
        remove_string_list = []

    import fitz

    # pdf_reader = fitz.open(stream = file_stream, filetype = "pdf")
    pdf_reader = fitz.open(path)
    if end_page is None:
        end_page = pdf_reader.page_count

    _text_list = []
    _ids = []
    _metadata = []
    _file_name = Path(path).name
    for index in range(start_page - 1, end_page):
        text = pdf_reader[index].get_text("text")
        text = text.replace('\n', ' ')
        text = re.sub(r'\s+', ' ', text)
        for remove_string in remove_string_list:
            re_compile = re.compile(remove_string)
            text = re_compile.sub('', text)
            _metadata.append({
                "id": index + 1,
                "file_name": _file_name,
                "page": f'page {index + 1}'
            })
        _ids.append(f'page_{index + 1}')
        _text_list.append(text)

    return _text_list, _ids, _metadata

Carregador de PDF unstructured para ingestão de html UnstructuredPDFLoader do LangChain

  • Prós: 
    • Muitos metadados foram incluídos, como números de página e coordenadas x,y dos textos, semelhante ao Watson Discovery, o que poderia ser útil para destacar informações importantes posteriormente.
  • Contras: 
    • Não foi possível capturar muitas tabelas aninhadas ou complexas corretamente. Além disso, para essa abordagem, precisaríamos converter cada um dos formatos docx originais para PDF.

Carregador de documentos Word unstructured para ingestão de HTML UnstructuredWordDocumentLoader do LangChain

  • Prós: 
    • O uso do Docs resultou em uma melhor captura das tabelas e ajudou a criar um contexto melhor para os LLMs gerarem respostas.
  • Contras: 
    • Metadados como números de página não foram capturados corretamente, mesmo após adicionar quebras de página, em comparação com o carregador de PDF.

Resumo de tabelas HTML

Na tentativa de preservar a estrutura das tabelas, inserimos elementos de tabela extraídos de HTML (usando a biblioteca Unstructured) em um LLM (o Mixtral é uma boa opção para tabelas longas devido ao tamanho do seu contexto). Em seguida, podemos criar um prompt para resumir as tabelas ou usar few-shot learning para convertê-las em um formato que acreditamos ser mais compreensível para modelos de linguagem de grande porte.

  • Prós: 
    • O texto de saída seria mais fácil de fragmentar e ingerir, além de preservar a estrutura das tabelas
  • Contras: 
    • Para tabelas grandes que se estendem por várias páginas, ao usar um prompt de resumo podemos perder algumas informações; portanto, utilizar few-shot learning para converter as tabelas, em vez de resumi-las, pode resultar em saídas melhores.

Outras bibliotecas de leitura de tabelas, como Camelot e Tabula, também foram testadas; no entanto, essas bibliotecas não foram tão precisas quanto a Unstructured para tabelas complexas e, como apenas detectam tabelas dentro de um documento, pode ser necessário mais pré-processamento para criar segmentos significativos a serem enviados aos modelos de linguagem de grande porte.

Em resumo, embora o Watson Discovery seja uma maneira fácil e rápida de ingerir documentos e capturar muitos metadados adicionais que podem ser úteis na construção da IU (para destacar trechos, por exemplo), a biblioteca "unstructured" (que possui wrappers tanto no LlamaIndex quanto no LangChain) poderia capturar as tabelas complexas dentro dos documentos com mais precisão em comparação com o Watson Discovery. Portanto, uma combinação das duas abordagens pode ser útil para projetos que incluem tabelas complexas em seus documentos.

Ferramentas IBM

Ingestão com Watsonx Orchestrate

A ingestão sem uma interface gráfica do usuário pode ser difícil para os usuários corporativos, pois exige experiência e conhecimento técnico. O Watsonx Orchestrate elimina a frustração dos usuários corporativos ao implementar uma interface de usuário (IU) de arrastar e soltar. Ao clicar no botão azul de upload, é possível carregar e armazenar documentos diretamente no watsonx Discovery (também conhecido como Elasticsearch).

Captura de tela do portal de arrastar e soltar do watsonx Orchestrate

Isso permite que o usuário carregue diretamente sua documentação no Elasticsearch para criar sua própria base de conhecimento personalizada.

Captura de tela de upload de documentos no watsonx Orchestrate

Em vez de ingerir diretamente os documentos no watsonx Discovery, você também tem a opção de se conectar ao Watson Discovery por meio do watsonx Orchestrate. Você pode encontrar mais detalhes sobre como fazer ingestão de dados por meio do Watson Discovery abaixo.

Watson Discovery

Uma das outras ferramentas internas que podem ser usadas para ingestão é o Watson Discovery. O Watson Discovery não é puramente uma ferramenta de ingestão e, com ele, você pode ingerir, normalizar, enriquecer e pesquisar seus dados não estruturados. A imagem abaixo mostra os principais componentes e recursos do Watson Discovery.

Ilustração das funções principais do Watson Discovery

Como você pode ver acima, um dos principais recursos do Watson Discovery é a ingestão de dados estruturados e não estruturados em diferentes formatos, como JSON, HTML, PDF, Word e outros, além do Smart Document Understanding.

Se você tiver o Watson Discovery provisionado em seu ambiente, poderá ingerir documentos facilmente criando primeiro um projeto e, em seguida, uma coleção, onde será possível enviar seus dados a partir de um armazenamento local ou de um local na nuvem, como um object storage bucket.

Depois de criar a coleção, você pode acessar a guia Manage Collections (Gerenciar coleções) no menu hambúrguer, em Identity Fields (Campos de identidade) e escolher o método que deseja usar para processar os documentos.

Captura de tela de processamento de documentos do Watson Discovery

Há três opções:

  • Extração somente de texto: extrai apenas o texto dos documentos.
  • Modelos treinados pelo usuário: em que você pode anotar diferentes partes do seu documento com tags personalizadas e treinar seus próprios modelos com base em padrões visuais repetidos em seus documentos
  • Modelos pré-treinados: que podem extrair texto e identificar tabelas, listas e seções usando modelos pré-treinados da IBM.

Pode demorar um pouco até que todos os seus documentos sejam processados. Depois disso, você poderá usar a API do Watson Discovery para ler e usar os dados em seu código.

Abaixo está um exemplo de código mostrando como você pode usar a API para ler os dados da sua coleção:

from langchain.docstore.document import Document
import os
import logging
import time
from ibm_watson import DiscoveryV2
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator

WD_PAGE_SIZE = 200
MAX_RETRIES = 2
def get_documents_from_wd(collection_ids=[WD_COLLECTION_ID]):
    
    print("Buscando documentos no Watson Discovery")
    documents = []
    
    print("Configurando o cliente do WD")
    authenticator = IAMAuthenticator(WD_API_KEY)
    wd_client = DiscoveryV2(
        version="2023-03-31",
        authenticator=authenticator
    )
    
    print("Definindo a URL do serviço do WD")
    wd_client.set_service_url(WD_SERVICE_URL)
    
    print("Buscando documentos no WD")
    page_id = 0
    retries = 0
    while True:
        try:
            print("Buscando página: " + str(page_id))
            response = wd_client.query(
                project_id=WD_PROJECT_ID,
                collection_ids=collection_ids,
                return_=["text"],
                count=WD_PAGE_SIZE,
                offset=page_id*WD_PAGE_SIZE
            ).get_result()
            if response is None or not isinstance(response, dict):
                print("Nenhum resultado da consulta")
                raise ValueError("Nenhum resultado da consulta")
            if "results" not in response or response["results"] is None or not isinstance(response["results"], list):
                print("Nenhum resultado da consulta 2")
                raise ValueError("Nenhum resultado da consulta")
            results = response["results"]
            if len(results) == 0:
                print("ão há mais resultados")
                break
            print("Obtidos " + str(len(results)) + " documentos")
            documents.extend(list(map(lambda result: Document(page_content=result["text"][0],  
                                                              metadata= {"collection_id": WD_COLLECTION_ID, "document_id" : result['document_id']}), results)))
            page_id += 1
        except Exception as error:
            logging.error("Falha ao buscar documentos no WD", str(error))
            retries += 1
            time.sleep(5)
            if retries > MAX_RETRIES:
                break
            print("Retrying...")
    print("Obtidos " + str(len(documents)) + " documentos")
    return documents

 

Rastreamento web no Watson Discovery

Ao criar um projeto no Watson Discovery, você pode planejar um rastreamento da web para buscar informações de uma URL específica. Tudo o que você precisa fazer é selecionar o Web Crawl ao selecionar a fonte de dados e, em seguida, especificar a URL e o cronograma do Crawl.

Tela de configuração do rastreador da web do Watson Discovery.
Captura de tela do agendador do rastreador da web do Watson Discovery

Depois disso, você pode executar as etapas mencionadas acima para gerenciar sua coleção e usar a API para ler os dados dela. Para ver mais detalhes sobre como configurar o Web Crawl no Watson Discovery, você pode assistir a este vídeo.

Para mais informações e funcionalidades, você pode consultar a página do desenvolvedor do Watson Discovery.

Deep Search

O Deep Search é o toolkit de código aberto da IBM Research para ingestão. O Deep Search aproveita métodos de IA de última geração para coletar, converter, enriquecer e vincular continuamente grandes coleções de documentos. Você pode usá-lo para documentos PDF públicos e proprietários.

O Deep Search converte documentos PDF não estruturados em arquivos JSON estruturados com precisão e facilidade. Ele permite que você automatize a extração de conhecimento, bem como faça o ajuste fino de seus modelos de base e grandes modelos de linguagem proprietários.

Você pode encontrar mais informações sobre Deep Search aqui.

O Deep Search pode ser uma boa ferramenta interna para ler documentos complexos no lugar de bibliotecas não estruturadas ou outras bibliotecas de código aberto. Você também pode encontrar uma excelente documentação no repositório, com diferentes tipos de exemplos em notebooks Python:

Notebooks de exemplo para Deep Search

Ingestão de tabelas a partir de PDFs

O Deep Search oferece bons resultados ao extrair tabelas de PDFs no formato JSON.

Para os testes, utilizamos um documento PDF parcial do CIPP, que inclui três páginas, cada uma exibindo dados em forma de tabela. Neste caso, o melhor desempenho do RAG é obtido usando o Deep Search para extrair tabelas em formato JSON e, em seguida, convertê-las para HTML para uso no Watson Discovery / watsonx Discovery, em vez de usar PDFs diretamente, converter PDFs para HTML sem o Deep Search ou usar apenas o JSON do Deep Search no Watson Discovery / watsonx Discovery. Descobrimos que o Watson Discovery seleciona a resposta errada da célula errada na tabela em alguns casos, mas de forma mais rápida e, no mesmo caso, o Watson Discovery pode identificar a resposta exata dentro da grade da tabela.

IBM Datacap

O IBM Datacap é uma solução abrangente para captura de documentos e dados; oferecendo digitalização, classificação, reconhecimento, validação e exportação rápidos, precisos e econômicos de dados e imagens de documentos. Ele captura documentos, extrai dados relevantes e os integra aos processos de negócios subsequentes.

O Datacap obtém documentos em papel por meio de scanners, impressoras multifuncionais ou dispositivos móveis e importa documentos eletrônicos de sistemas de arquivos, fax ou servidores de e-mail. A ferramenta aprimora a extração de dados com funcionalidades de processamento de imagem, como correção de distorção e remoção de linhas, borrões e bordas.

O Datacap usa reconhecimento óptico de caracteres (OCR), reconhecimento inteligente de caracteres (ICR) para escrita à mão, reconhecimento óptico de marcas (OMR) para marcas e leitura de código de barras para extrair dados com eficiência.

Para obter instruções detalhadas de instalação e uso, consulte a documentação do IBM Datacap.

Ferramentas de código aberto

Carregadores de dados LangChain

O LangChain é compatível com diferentes tipos de carregadores de documentos que carregam dados de uma fonte como o objeto Document do LangChain. Um objeto Document é um trecho de texto e metadados associados que podem ser facilmente usados para fragmentação e recuperação posteriormente no pipeline.

Atualmente, o LangChain é compatível com carregadores para carregar um diretório inteiro ou diferentes tipos de arquivos, como:

  • CSV
  • HTML
  • JSON
  • Markdown
  • Microsoft Office
  • PDF

Você pode usar esses carregadores com poucas linhas de código Python. Por exemplo, para carregar um PDF, você pode usar o seguinte código:

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("example_data/layout-parser-paper.pdf")
pages = loader.load_and_split()

Consulte a documentação do LangChain para saber como usar esses carregadores.

Além dos mencionados no link acima, você pode encontrar carregadores adicionais que podem carregar dados diretamente de aplicações específicas, como e-mail, Github, Google Drive etc. Você pode encontrar a lista de todos os diferentes carregadores aqui.

Nos casos em que você tem documentos com estruturas, formatos e tabelas complexos, você pode usar os carregadores unstructured do LangChain. Esses carregadores utilizam internamente a biblioteca unstructured, que é uma das melhores bibliotecas para documentos complexos (em diferentes formatos, como PDF, MS Word, PowerPoint etc.) e para dividir os documentos em diferentes componentes: LangChain UnstructuredLoader

Carregadores de dados LlamaIndex

Semelhante ao LangChain, o LlamaIndex também é compatível com diferentes tipos de carregadores. Uma das maneiras simples de carregar dados usando o LlamaIndex é usar o SimpleDirectoryReader. O SimpleDirectoryReader é compatível com os seguintes tipos:

  • .csv - valores separados por vírgula
  • .docx - Microsoft Word
  • .epub - formato de ebook EPUB
  • .hwp - processador de texto Hangul
  • .ipynb - Jupyter Notebook
  • .jpeg, .jpg - Imagem JPEG
  • .mbox - arquivo de e-mail MBOX
  • .md - Markdown
  • .mp3, .mp4 - áudio e vídeo
  • .pdf - Portable Document Format
  • .png - Portable Network Graphics
  • .ppt, .pptm, .pptx - Microsoft PowerPoint

Você pode utilizar o SimpleDirectoryLoader com o seguinte trecho de código:

from llama_index.core import SimpleDirectoryReader

reader = SimpleDirectoryReader(input_dir="path/to/directory")
documents = reader.load_data()

Você também pode adicionar extensões específicas para ler somente essas extensões do diretório:

SimpleDirectoryReader(
   input_dir="path/to/directory", required_exts=[".pdf", ".docx"]
)

Para obter mais detalhes sobre como usar os carregadores do LlamaIndex, consulte a documentação.

Assim como no Huggingface, você pode criar seus próprios carregadores personalizados ou usar os carregadores personalizados de outras pessoas no llamahub.ai.

No entanto, uma desvantagem do LlamaIndex em comparação com o LangChain é que a documentação e o suporte da comunidade não são tão bons quanto o do LangChain, portanto, depurar seu código pode ser mais difícil.

Não estruturado

Nos casos em que você estiver carregando documentos complexos, você também pode usar diretamente a biblioteca Unstructured. A biblioteca Unstructured é muito boa para leitura e análise de tabelas.

A biblioteca Unstructured também é compatível com os wrappers LangChain e LlamaIndex, o que pode facilitar seu uso em pipelines RAG no futuro, já que criarão e retornarão automaticamente os objetos Document. No entanto, se o seu projeto precisar de mais personalização e você quiser usar diretamente a biblioteca unstructured, consulte a documentação a seguir: Unstructured.

Explorar mais

Obtenha os mais recentes padrões de tecnologia, arquiteturas de soluções e publicações de arquitetura da IBM.

  1. Acesse o IBM Architecture Center
Colaboradores

Sara Golestaneh, Vicky Kuo, Fahad Bhutta, Luke Major, Dean Sacoransky, Chris Kirby

Atualizado em: 15 de novembro de 2024