Обработка длинного текста
При работе с файлами, такими как PDF, вероятно, вы столкнетесь с текстом, который превышает контекстное окно вашей языковой модели. Чтобы обработать этот текст, рассмотрите следующие стратегии:
- Изменить LLM Выберите другую LLM, которая поддерживает более большое контекстное окно.
- Грубая сила Разбейте документ на части и извлеките содержимое из каждой части.
- RAG Разбейте документ на части, проиндексируйте их и извлеките содержимое только из подмножества частей, которые выглядят "релевантными".
Имейте в виду, что эти стратегии имеют разные компромиссы, и лучшая стратегия, вероятно, зависит от разрабатываемого вами приложения!
Настройка
Нам нужны примеры данных! Давайте загрузим статью о машинах с Википедии и загрузим ее как документ LangChain.
import re
import requests
from langchain_community.document_loaders import BSHTMLLoader
response = requests.get("https://en.wikipedia.org/wiki/Car")
with open("car.html", "w", encoding="utf-8") as f:
f.write(response.text)
loader = BSHTMLLoader("car.html")
document = loader.load()[0]
document.page_content = re.sub("\n\n+", "\n", document.page_content)
print(len(document.page_content))
78967
Определение схемы
Здесь мы определим схему для извлечения ключевых событий из текста.
from typing import List, Optional
from langchain.chains import create_structured_output_runnable
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
class KeyDevelopment(BaseModel):
"""Информация о событии в истории автомобилей."""
year: int = Field(
..., description="Год, когда произошло важное историческое событие."
)
description: str = Field(
..., description="Что произошло в этом году? Каково было событие?"
)
evidence: str = Field(
...,
description="Повторите дословно предложение(я), из которых были извлечены информация о годе и описании.",
)
class ExtractionData(BaseModel):
"""Извлеченная информация о ключевых событиях в истории автомобилей."""
key_developments: List[KeyDevelopment]
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Вы эксперт в выявлении ключевых исторических событий в тексте. " "Извлеките только важные исторические события. Не извлекайте ничего, если в тексте не найдется важной информации.",
),
("human", "{text}"),
]
)
llm = ChatOpenAI(
model="gpt-4-0125-preview",
temperature=0,
)
extractor = prompt | llm.with_structured_output(
schema=ExtractionData,
method="function_calling",
include_raw=False,
)
/home/eugene/.pyenv/versions/3.11.2/envs/langchain_3_11/lib/python3.11/site-packages/langchain_core/_api/beta_decorator.py:86: LangChainBetaWarning: Функция `with_structured_output` находится в бета-версии. Над ней активно работают, поэтому API может измениться.
warn_beta(
Подход "грубая сила"
Разделите документ на части так, чтобы каждая часть соответствовала контекстному окну LLMs.
from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(
chunk_size=2000,
chunk_overlap=20,
)
texts = text_splitter.split_text(document.page_content)
Используйте функциональность .batch
, чтобы запустить извлечение параллельно через каждую часть!
tip
Вы часто можете использовать .batch(), чтобы параллельно обрабатывать извлечения! batch
использует пул потоков под капотом, чтобы помочь вам параллелизовать рабочие нагрузки.
Если ваша модель доступна через API, это вероятно ускорит ваш поток извлечения!
first_few = texts[:3]
extractions = extractor.batch(
[{"text": text} for text in first_few],
{"max_concurrency": 5}, # ограничить параллелизм, передав максимальную конкурентность!
)
Объединение результатов
После извлечения данных из различных фрагментов хотелось бы объединить извлечения вместе.
ключевые_события = []
for извлечение in извлечения:
ключевые_события.extend(извлечение.ключевые_события)
ключевые_события[:20]
[KeyDevelopment(year=1966, description="The Toyota Corolla began production, recognized as the world's best-selling automobile.", evidence="The Toyota Corolla has been in production since 1966 and is recognized as the world's best-selling automobile."),
KeyDevelopment(year=1769, description='Nicolas-Joseph Cugnot built the first steam-powered road vehicle.', evidence='French inventor Nicolas-Joseph Cugnot built the first steam-powered road vehicle in 1769.'),
KeyDevelopment(year=1808, description='François Isaac de Rivaz designed and constructed the first internal combustion-powered automobile.', evidence='French-born Swiss inventor François Isaac de Rivaz designed and constructed the first internal combustion-powered automobile in 1808.'),
KeyDevelopment(year=1886, description='Carl Benz patented his Benz Patent-Motorwagen, inventing the modern car.', evidence='The modern car—a practical, marketable automobile for everyday use—was invented in 1886, when German inventor Carl Benz patented his Benz Patent-Motorwagen.'),
KeyDevelopment(year=1908, description='The 1908 Model T, an affordable car for the masses, was manufactured by the Ford Motor Company.', evidence='One of the first cars affordable by the masses was the 1908 Model T, an American car manufactured by the Ford Motor Company.'),
KeyDevelopment(year=1881, description='Gustave Trouvé demonstrated a three-wheeled car powered by electricity.', evidence='In November 1881, French inventor Gustave Trouvé demonstrated a three-wheeled car powered by electricity at the International Exposition of Electricity.'),
KeyDevelopment(year=1888, description="Bertha Benz undertook the first road trip by car to prove the road-worthiness of her husband's invention.", evidence="In August 1888, Bertha Benz, the wife of Carl Benz, undertook the first road trip by car, to prove the road-worthiness of her husband's invention."),
KeyDevelopment(year=1896, description='Benz designed and patented the first internal-combustion flat engine, called boxermotor.', evidence='In 1896, Benz designed and patented the first internal-combustion flat engine, called boxermotor.'),
KeyDevelopment(year=1897, description='Nesselsdorfer Wagenbau produced the Präsident automobil, one of the first factory-made cars in the world.', evidence='The first motor car in central Europe and one of the first factory-made cars in the world, was produced by Czech company Nesselsdorfer Wagenbau (later renamed to Tatra) in 1897, the Präsident automobil.'),
KeyDevelopment(year=1890, description='Daimler Motoren Gesellschaft (DMG) was founded by Daimler and Maybach in Cannstatt.', evidence='Daimler and Maybach founded Daimler Motoren Gesellschaft (DMG) in Cannstatt in 1890.'),
KeyDevelopment(year=1902, description='A new model DMG car was produced and named Mercedes after the Maybach engine.', evidence='Two years later, in 1902, a new model DMG car was produced and the model was named Mercedes after the Maybach engine, which generated 35 hp.'),
KeyDevelopment(year=1891, description='Auguste Doriot and Louis Rigoulot completed the longest trip by a petrol-driven vehicle using a Daimler powered Peugeot Type 3.', evidence='In 1891, Auguste Doriot and his Peugeot colleague Louis Rigoulot completed the longest trip by a petrol-driven vehicle when their self-designed and built Daimler powered Peugeot Type 3 completed 2,100 kilometres (1,300 mi) from Valentigney to Paris and Brest and back again.'),
KeyDevelopment(year=1895, description='George Selden was granted a US patent for a two-stroke car engine.', evidence='After a delay of 16 years and a series of attachments to his application, on 5 November 1895, Selden was granted a US patent (U.S. patent 549,160) for a two-stroke car engine.'),
KeyDevelopment(year=1893, description='The first running, petrol-driven American car was built and road-tested by the Duryea brothers.', evidence='In 1893, the first running, petrol-driven American car was built and road-tested by the Duryea brothers of Springfield, Massachusetts.'),
KeyDevelopment(year=1897, description='Rudolf Diesel built the first diesel engine.', evidence='In 1897, he built the first diesel engine.'),
KeyDevelopment(year=1901, description='Ransom Olds started large-scale, production-line manufacturing of affordable cars at his Oldsmobile factory.', evidence='Large-scale, production-line manufacturing of affordable cars was started by Ransom Olds in 1901 at his Oldsmobile factory in Lansing, Michigan.'),
KeyDevelopment(year=1913, description="Henry Ford began the world's first moving assembly line for cars at the Highland Park Ford Plant.", evidence="This concept was greatly expanded by Henry Ford, beginning in 1913 with the world's first moving assembly line for cars at the Highland Park Ford Plant."),
KeyDevelopment(year=1914, description="Ford's assembly line worker could buy a Model T with four months' pay.", evidence="In 1914, an assembly line worker could buy a Model T with four months' pay."),
KeyDevelopment(year=1926, description='Fast-drying Duco lacquer was developed, allowing for a variety of car colors.', evidence='Only Japan black would dry fast enough, forcing the company to drop the variety of colours available before 1913, until fast-drying Duco lacquer was developed in 1926.')]
It looks like there's no specific text to translate at the moment. If you have any technical material or content in English that needs to be translated into Russian, feel free to share it with me and I'll be happy to assist with the translation. Just let me know how I can help!
Подход на основе RAG
Еще одна простая идея - разделить текст на куски, но вместо извлечения информации из каждого куска сосредоточиться только на самых значимых кусках.
предупреждение
Может быть сложно определить, какие куски являются значимыми.
Например, в статье про автомобиль
, которую мы используем здесь, большая часть статьи содержит ключевую информацию о развитии. Поэтому, используя RAG, мы скорее всего упустим много важной информации.
Мы предлагаем провести эксперименты с вашим конкретным случаем и определить, подходит ли этот метод или нет.
Вот простой пример, который использует векторное хранилище FAISS
.
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document
from langchain_core.runnables import RunnableLambda
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
texts = text_splitter.split_text(document.page_content)
vectorstore = FAISS.from_texts(texts, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever(
search_kwargs={"k": 1}
) # Извлекать только из первого документа
В этом случае извлекатель RAG смотрит только на верхний документ.
rag_extractor = {
"text": retriever | (lambda docs: docs[0].page_content) # извлекаем содержимое верхнего документа
} | extractor
results = rag_extractor.invoke("Ключевые события, связанные с автомобилями")
for key_development in results.key_developments:
print(key_development)
year=1924 description="Первый автомобиль массового производства в Германии, Opel 4PS Laubfrosch, был выпущен, сделав Opel ведущим производителем автомобилей в Германии с долей рынка 37.5%." evidence="Первый автомобиль массового производства в Германии, Opel 4PS Laubfrosch (Древесная лягушка), сошел с конвейера в Рюссельсхайме в 1924 году, вскоре сделав Opel ведущим производителем автомобилей в Германии с долей рынка 37,5%."
year=1925 description='Morris имел 41% от общего объема производства автомобилей в Великобритании, доминируя на рынке.' evidence='в 1925 году Morris имел 41% от общего объема производства автомобилей в Великобритании.'
year=1925 description='Citroën, Renault и Peugeot произвели 550 000 автомобилей во Франции, доминируя на рынке.' evidence="То же сделала Citroën во Франции, пришедшая к автомобилям в 1919 году; между ними и другими дешевыми автомобилями в ответ, такими как 10CV Renault и 5CV Peugeot, они произвели 550 000 автомобилей в 1925 году."
year=2017 description='Производство автомобилей с бензиновым двигателем достигло пика.' evidence='Производство автомобилей с бензиновым двигателем достигло пика в 2017 году.'
Общие проблемы
Различные методы имеют свои преимущества и недостатки, связанные с затратами, скоростью и точностью.
Обратите внимание на следующие проблемы:
- Разделение содержимого означает, что модель LLM может не извлечь информацию, если она распределена по нескольким кускам.
- Большое перекрытие кусков может привести к извлечению одной и той же информации дважды, поэтому будьте готовы к удалению дубликатов!
- Модели LLM могут выдумывать данные. Если вы ищете один факт в большом тексте и используете грубый метод, вы можете получить больше выдуманных данных.