Как суммировать большие документы с помощью LangChain и OpenAI

При объединении очень больших документов по-прежнему существуют некоторые ограничения. Вот несколько способов смягчить эти последствия.

Перевод книги « Как суммировать большие документы с помощью LangChain и OpenAI» , автор Усама Джамиль.

Большие языковые модели упрощают многие задачи, например создание чат-ботов, языковой перевод, обобщение текста и т. д. Раньше мы писали модели для обобщения, но потом всегда возникали проблемы с производительностью. Теперь мы можем легко это сделать, используя большие языковые модели (LLM) . Например, современные LLM (SOTA) уже могут обрабатывать целые книги в своем контекстном окне. Но при объединении очень больших документов все еще существуют некоторые ограничения.

Ограничения LLM при обобщении больших документов

Ограничение контекста или длина контекста в LLM относится к количеству токенов, которые может обрабатывать модель. Каждая модель имеет собственную длину контекста, также известную как максимальная отметка или предел отметок. Например, длина контекста стандартной модели GPT-4 составляет 128 000 токенов. Он теряет информацию для большего количества тегов. Некоторые SOTA LLM имеют контекстные ограничения до 1 миллиона тегов. Однако по мере увеличения контекстуальных ограничений LLM страдает от таких ограничений, как новизна и первенство. Мы также можем изучить способы смягчения этих последствий.

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

Оба этих эффекта смещают модель в сторону определенных частей входных данных. Модель может пропускать важную информацию в середине последовательности.

Второй вопрос – стоимость. Мы можем решить первую проблему контекстных ограничений, разделив текст, но мы не можем напрямую передать в модель всю книгу. Это будет стоить дорого. Например, если бы у нас была книга с 1 миллионом тегов и мы передали ее непосредственно в модель GPT4, наши общие затраты составили бы около 90 долларов США (теги подсказки и завершения). Нам нужно было найти эклектичный способ резюмировать наш текст, принимая во внимание цену, контекстуальные ограничения и полный контекст книги.

В этом уроке вы узнаете, как обобщить всю книгу, учитывая цену и контекстные ограничения модели. Давайте начнем.

Настройте среду

Чтобы следовать этому руководству, вам понадобится следующее:

  • Python установлен
  • IDE (работает VS Code)

Чтобы установить зависимости, откройте терминал и введите следующую команду:

pip install langchain openai tiktoken fpdf2 pandas

Эта команда установит все необходимые зависимости.

Загрузить книги

Вы будете использовать книгу Чарльза Диккенса «Дэвид Копперфильд», опубликованную для использования в этом проекте. Давайте загрузим эту книгу, используя PyPDFLoader, предоставленный LangChain.

from langchain.document_loaders import PyPDFLoader

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

Он загрузит всю книгу, но нас интересует только содержательная часть. Мы можем пропустить такие страницы, как предисловие и введение.

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

Теперь у нас есть контент. Давайте напечатаем первые 200 символов.

предварительная обработка

Удалим из текста ненужное содержимое, такое как непечатаемые символы, лишние пробелы и т.д.

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)

После очистки данных мы можем углубиться в сводную задачу.

Загрузить API OpenAI

Прежде чем использовать API OpenAI, нам необходимо настроить его здесь и предоставить учетные данные.

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

Введите здесь свой ключ API, и он установит переменную среды.

Давайте посмотрим, сколько тегов в этой книге:

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

В книге более 466 000 маркеров, и если бы мы передали их все напрямую в LLM, это стоило бы нам больших денег. Поэтому, чтобы снизить затраты, мы реализуем кластеризацию K-средних для извлечения важных фрагментов из книги.

Примечание . Решение использовать кластеризацию K-средних было вдохновлено руководством эксперта по данным Грега Камрадта .

Чтобы получить важные части книги, давайте сначала разделим книгу на разные части.

Разделить контент на документы

Мы будем использовать утилиту SemanticChunker от LangChain, чтобы разделить содержимое книги на документы.

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

SemanticChunker получает два параметра, первый — модель внедрения. Вложения, созданные этой моделью, используются для разделения текста на основе семантики. Второй —breakpoint_threshold_type, определяющий точку, в которой текст должен быть разбит на разные фрагменты на основе семантического сходства.

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

Найдите вставки для каждого документа

Теперь давайте встроим каждый сгенерированный документ. Для получения встраивания вы будете использовать метод OpenAI по умолчанию.

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_embeddingsМетод может предоставить нам вложения для всех документов.

OpenAI специально выпустила метод text-embedding-3-small, который считается более дешевым и быстрым.

Перестановка данных

Далее мы преобразуем список содержимого документа и его внедрения в DataFrame pandas для упрощения обработки и анализа данных .

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

Эффективная кластеризация с использованием Faiss

Теперь мы конвертируем векторы документов в формат, совместимый с Фейссом , группируем их в 50 групп с использованием K-средних, а затем создаем индекс Фейсса для эффективного поиска сходства между документами.

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) 

Это K-средство кластеризации группирует документы в 50 групп.

Примечание . Причина выбора кластеризации K-средних заключается в том, что каждый кластер будет иметь схожий контент или аналогичный контекст, поскольку все документы в кластере имеют связанные вложения, и мы выберем документ, наиболее близкий к ядру.

Выберите импортный документ

Теперь мы выберем из каждого кластера только самые важные документы. Для этого выберем только первый ближайший к центроиду вектор.

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

Этот код использует метод поиска по индексу, чтобы найти ближайший документ к каждому центроиду в списке центроидов. Он возвращает два массива:

  • D, который содержит расстояние ближайшего документа до соответствующего центроида, и
  • I, который содержит указатель этих последних документов. Второй параметр 1 в методе поиска указывает, что для каждого центроида находится только один ближайший документ.

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

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

Получите краткое описание каждого документа

Следующим шагом будет использование модели GPT-4 для получения сводки по каждому документу и экономии денег. Чтобы использовать GPT-4, мы определяем модель.

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

Определите подсказки и используйте LangChain для создания шаблонов подсказок и передачи их в модель.

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:
"""
)

Этот шаблон подсказки поможет модели более эффективно и результативно резюмировать документ.

Следующим шагом является определение цепочки LangChain с использованием языка выражений LangChain (LCEL).

chain= (
    prompt
   | model
   |StrOutputParser() )

Цепочка сводки использует StrOutputParser для анализа выходных данных. Существуют и другие синтаксические анализаторы вывода, доступные для изучения.

Наконец, вы можете применить определенную цепочку к каждому документу, чтобы получить сводку.

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

Приведенный выше код применяет цепочку к каждому документу один за другим и соединяет каждое резюме с Final_summary.

Сохранить аннотацию в формате PDF

Следующий шаг — отформатировать резюме и сохранить его в формате 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)

Итак, вот полное содержание книги в формате PDF.

в заключение

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

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

Большое внимание привлекает разработка приложений искусственного интеллекта с помощью API LLM, в которой векторные базы данных играют важную роль, обеспечивая эффективное хранение и извлечение встраиваемого контекста.

MyScaleDB — это векторная база данных, разработанная специально для приложений искусственного интеллекта с учетом всех факторов, включая стоимость, точность и скорость. Его дружественный к SQL интерфейс позволяет разработчикам приступить к разработке своих приложений искусственного интеллекта без необходимости изучения новых знаний.

Эта статья была впервые опубликована на Yunyunzhongsheng ( https://yylives.cc/ ), приглашаем всех посетить ее.

Я решил отказаться от открытого исходного кода Hongmeng Ван Чэнлу, отец Hongmeng с открытым исходным кодом: Hongmeng с открытым исходным кодом — единственное мероприятие в области промышленного программного обеспечения, посвященное архитектурным инновациям в области базового программного обеспечения в Китае: выпущен OGG 1.0, Huawei предоставляет весь исходный код. Google Reader уничтожен «горой кодового дерьма» Официально выпущена Ubuntu 24.04 LTS Перед официальным выпуском Fedora Linux 40 разработчики Microsoft: производительность Windows 11 «смехотворно плоха», Ма Хуатэн и Чжоу Хунъи пожимают друг другу руки, «устраняя обиды» Известные игровые компании издали новые правила: свадебные подарки сотрудникам не должны превышать 100 000 юаней. Pinduoduo был осужден за недобросовестную конкуренцию. Компенсация в размере 5 миллионов юаней.
{{o.name}}
{{м.имя}}

рекомендация

отmy.oschina.net/u/6919515/blog/11054174
рекомендация