Построение потока данных RAG для полуструктурированных данных с использованием Langchain.

Создание потокового алгоритма RAG для полуструктурированных данных с применением Langchain.

Введение

Использование улучшенной генерации при поиске документов уже довольно долгое время. Вокруг этой концепции создаются множество инструментов и приложений, таких как векторные хранилища, фреймворки для поиска и языковые модели с лингвистической цепью, что делает возможным работу с пользовательскими документами, особенно с полуструктурированными данными с лингвистической цепью. Работа с длинными и плотными текстами никогда еще не была такой легкой и интересной. Традиционный RAG работает хорошо с неструктурированными файлами с большим объемом текста, такими как DOC, PDF и т. д. Однако этот подход не справляется с полуструктурированными данными, такими как вложенные таблицы в PDF.

При работе с полуструктурированными данными обычно возникают две проблемы.

  • Традиционные методы извлечения и разделения текста не учитывают таблицы в PDF. Как правило, они разбивают таблицы, что приводит к потере информации.
  • Вложенные таблицы могут не обеспечить точный семантический поиск.

Поэтому в этой статье мы построим конвейер для поиска и генерации документов для работы с полуструктурированными данными с лингвистической цепью, чтобы решить эти две проблемы с полуструктурированными данными.

Цели обучения

  • Понять разницу между структурированными, неструктурированными и полуструктурированными данными.
  • Небольшое воспоминание о Retrieval Augment Generation и лингвистической цепи.
  • Научиться создавать многовекторный поиск для обработки полуструктурированных данных с лингвистической цепью.

Эта статья опубликована как часть Data Science Blogathon.

Типы данных

Обычно существуют три типа данных: структурированные, полуструктурированные и неструктурированные.

  • Структурированные данные: Структурированные данные – это стандартизированные данные. Данные следуют предопределенной схеме, такой как строки и столбцы. SQL-базы данных, электронные таблицы, фреймы данных и т. д.
  • Неструктурированные данные: В отличие от структурированных данных неструктурированные данные не следуют модели данных. Данные могут быть совершенно случайными. Например PDF, тексты, изображения и т. д.
  • Полуструктурированные данные: Это комбинация предыдущих типов данных. В отличие от структурированных данных, у них нет жесткой предопределенной схемы. Однако данные по-прежнему сохраняют иерархический порядок на основе некоторых маркеров, в отличие от неструктурированных данных. Например, CSV, HTML, вложенные таблицы в PDF, XML и т. д.

Что такое RAG?

RAG означает Retrieval Augmented Generation. Это самый простой способ питать большие языковые модели новой информацией. Давайте сделаем небольшую вводную по RAG.

В типичном конвейере RAG у нас имеются источники знаний, такие как локальные файлы, веб-страницы, базы данных и т. д., модель векторизации, векторное хранилище и языковая модель. Мы собираем данные из различных источников, разбиваем документы, получаем векторные представления текстовых фрагментов и сохраняем их в векторном хранилище. Затем мы передаем векторные представления запросов в векторное хранилище, получаем документы из векторного хранилища и, наконец, генерируем ответы с помощью языковой модели.

Это рабочий процесс традиционного RAG и он хорошо работает с неструктурированными данными вроде текстов. Однако, когда речь идет о полуструктурированных данных, например вложенные таблицы в PDF, он часто работает не так хорошо. В этой статье мы узнаем, как обрабатывать эти вложенные таблицы.

Что такое Langchain?

Langchain – это открытая платформа для создания приложений на основе языковых моделей с лингвистической цепью. С момента своего запуска, проект получил широкое распространение среди разработчиков программного обеспечения. Он предоставляет единый набор инструментов и технологий для создания приложений искусственного интеллекта быстрее. В Langchain есть такие инструменты, как векторные хранилища, загрузчики документов, поисковые системы, модели векторизации, разделители текста и т. д. Это одно решение для создания приложений искусственного интеллекта. Но есть два ключевых преимущества, которые делают его выдающимся.

  • Цепочки LLM: Langchain предоставляет несколько цепей. Эти цепи объединяют несколько инструментов для выполнения одной задачи. Например, ConversationalRetrievalChain объединяет LLM, векторный поисковый систему, модель вложения и объект истории чата для генерации ответов на запросы. Эти инструменты хардкодированы и должны быть явно определены.
  • Агенты LLM: В отличие от цепей LLM, у ИИ-агентов нет жестко закодированных инструментов. Вместо того, чтобы связывать инструмент за инструментом, мы позволяем LLM решить, какой инструмент выбрать и когда, основываясь на текстовых описаниях инструментов. Это делает его идеальным для создания сложных приложений LLM, включающих рассуждение и принятие решений.

Построение конвейера RAG

Теперь, когда у нас есть представление о понятиях. Давайте обсудим подход к построению конвейера. Работа с полуструктурированными данными может быть сложной, поскольку она не следует конвенциональной схеме для хранения информации. И для работы с неструктурированными данными нам требуются специализированные инструменты, созданные для извлечения информации. Так что в этом проекте мы будем использовать один такой инструмент, называемый «неструктурированный»; он является открытым инструментом для извлечения информации из различных неструктурированных форматов данных, таких как таблицы в PDF, HTML, XML и т. д. Неструктурированный использует Tesseract и Poppler для обработки нескольких форматов данных в файлах. Итак, давайте настроим наше окружение и установим необходимые зависимости, прежде чем перейти к части кодирования.

Настройка окружения разработчика

Как и в любом другом проекте Python, откройте среду Python и установите Poppler и Tesseract.

!sudo apt install tesseract-ocr!sudo apt-get install poppler-utils

Теперь установите зависимости, которые нам понадобятся в нашем проекте.

!pip install "unstructured[all-docs]" Langchain openai

Извлечение с помощью неструктурированного

Теперь, когда мы установили зависимости, мы извлечем данные из файла PDF.

from unstructured.partition.pdf import partition_pdfpdf_elements = partition_pdf(    "mistral7b.pdf",     chunking_strategy="by_title",     extract_images_in_pdf=True,     max_characters=3000,    new_after_n_chars=2800,    combine_text_under_n_chars=2000,    image_output_dir_path="./"    )

Запуск этого кода установит несколько зависимостей, таких как YOLOx, которые необходимы для OCR, и вернет объекты типов, основанные на извлеченных данных. Включение параметра extract_images_in_pdf позволит неструктурированному извлекать встроенные изображения из файлов. Это может помочь реализовать мультимодальные решения.

Теперь давайте изучим категории элементов из нашего PDF.

# Создаем словарь для хранения количества каждого типакатегории_счётчики = {}for элемент in элементы_пдф:    категория = str(type(элемент))    if категория в category_counts:        category_counts[category] += 1    else:        category_counts[category] = 1# Уникальные_категории будут содержать уникальные элементыunique_categories = set(category_counts.keys())category_counts

Запуск этого кода выведет категории элементов и их количество.

Теперь мы разделяем элементы для удобной обработки. Мы создаем тип Element, который наследуется от типа Document в Langchain. Это гарантирует более организованные данные, с которыми легче работать.

from unstructured.documents.elements import CompositeElement, Tablefrom langchain.schema import Documentclass Element(Document):    type: str# Категоризируем по типуcategorized_elements = []for element in pdf_elements:    if isinstance(element, Table):        categorized_elements.append(Element(type="table", page_content=str(element)))    elif isinstance(element, CompositeElement):        categorized_elements.append(Element(type="text", page_content=str(element)))# Таблицыtable_elements = [e for e in categorized_elements if e.type == "table"]# Текстtext_elements = [e for e in categorized_elements if e.type == "text"]

Множественный векторный поисковик

У нас есть таблицы и текстовые элементы. Теперь у нас есть два способа обработки этих элементов. Мы можем сохранить исходные элементы в хранилище документов или сохранить их краткую сводку текстов. Таблицы могут представлять сложность для семантического поиска; в этом случае мы создаем сводки таблиц и сохраняем их в хранилище документов вместе с исходными таблицами. Для этого мы будем использовать множественный векторный поисковик (MultiVectorRetriever). Этот поисковик будет управлять векторным хранилищем, где мы будем хранить вектора сводок текстов, и простым хранилищем документов в памяти для хранения исходных документов.

Во-первых, построим цепь сводки, чтобы суммировать таблицы и текстовые данные, которые мы ранее извлекли.

from langchain.chat_models import coherefrom langchain.prompts import ChatPromptTemplatefrom langchain.schema.output_parser import StrOutputParserprompt_text = """Вы - ассистент, задача которого состоит в сводке таблиц и текстовых данных. 
Дайте краткое изложение таблицы или текста. Элемент таблицы или текста: {element} """prompt = ChatPromptTemplate.from_template(prompt_text)model = cohere.ChatCohere(cohere_api_key="your_key")summarize_chain = {"element": lambda x: x} | prompt | model | StrOutputParser()tables = [i.page_content for i in table_elements]table_summaries = summarize_chain.batch(tables, {"max_concurrency": 5})texts = [i.page_content for i in text_elements]text_summaries = summarize_chain.batch(texts, {"max_concurrency": 5})

Я использовал Cohere LLM для сводки данных; вы можете использовать модели OpenAI, такие как GPT-4. Лучшие модели приведут к лучшим результатам. Иногда модели могут не совершенно точно улавливать детали таблицы. Поэтому лучше использовать способные модели.

Теперь мы создаем MultivectorRetriever.

from langchain.retrievers import MultiVectorRetrieverfrom langchain.prompts import ChatPromptTemplateimport uuidfrom langchain.embeddings import OpenAIEmbeddingsfrom langchain.schema.document import Documentfrom langchain.storage import InMemoryStorefrom langchain.vectorstores import Chroma# Векторное хранилище для индексирования дочерних фрагментовvectorstore = Chroma(collection_name="collection",                     embedding_function=OpenAIEmbeddings(openai_api_key="api_key"))# Слой хранения для родительских документовstore = InMemoryStore()id_key = ""id"# Искательretriever = MultiVectorRetriever(    vectorstore=vectorstore,    docstore=store,    id_key=id_key,)# Добавляем текстыdoc_ids = [str(uuid.uuid4()) for _ in texts]summary_texts = [    Document(page_content=s, metadata={id_key: doc_ids[i]})    for i, s in enumerate(text_summaries)]retriever.vectorstore.add_documents(summary_texts)retriever.docstore.mset(list(zip(doc_ids, texts)))# Добавляем таблицыtable_ids = [str(uuid.uuid4()) for _ in tables]summary_tables = [    Document(page_content=s, metadata={id_key: table_ids[i]})    for i, s in enumerate(table_summaries)]retriever.vectorstore.add_documents(summary_tables)retriever.docstore.mset(list(zip(table_ids, tables)))

Мы использовали векторное хранилище Chroma для сохранения сводных векторов текстов и таблиц, а также хранилище документов в памяти для хранения исходных данных.

RAG

Теперь, когда наш искатель готов, мы можем создать конвейер RAG с помощью языка выражений Langchain.

from langchain.schema.runnable import RunnablePassthrough# Шаблон запросатemplate = """Ответьте на вопрос, основываясь только на следующем контексте, который может включать текст и таблицы::{context}Вопрос: {question}"""prompt = ChatPromptTemplate.from_template(template)# Модель LLMmodel = ChatOpenAI(temperature=0.0, openai_api_key="api_key")# Конвейер RAGchain = (    {"context": retriever, "question": RunnablePassthrough()}    | prompt    | model    | StrOutputParser())

Теперь мы можем задавать вопросы и получать ответы на основе извлеченных векторов из хранилища векторов.

chain.invoke(input = "Каково значение MT bench для Llama 2 и Mistral 7B Instruct??")

Заключение

В полуструктурированном формате данных часто остается скрытая информация. И сложно извлекать и выполнять обычную RAG-обработку этих данных. В этой статье мы перешли от извлечения текстов и встроенных таблиц в PDF-файле к созданию многовекторного искателя и конвейера RAG с помощью Langchain. Вот основные выводы из статьи.

Основные выводы

  • Обычная RAG-обработка часто сталкивается с проблемами при работе с полуструктурированными данными, такими как разбиение таблиц при разделении текста и неточный семантический поиск.
  • Unstructured, открытый инструмент для полуструктурированных данных, позволяет извлекать встроенные таблицы из PDF или аналогичных полуструктурированных данных.
  • С помощью Langchain мы можем создать многовекторный искатель для хранения таблиц, текстов и сводок в хранилищах документов для более точного семантического поиска.

Часто задаваемые вопросы

Медиа, показанное в этой статье, не принадлежит Analytics Vidhya и используется по усмотрению автора.