Image

Объяснение RAG: понимание вложений, сходства и поиска

Давайте подробнее рассмотрим, как работает механизм поиска.

Делиться

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

В своих последних публикациях я рассмотрел создание простого конвейера RAG с использованием API OpenAI, LangChain и локальных файлов, а также эффективное разделение больших текстовых файлов на фрагменты. В этих публикациях рассматриваются основы настройки конвейера RAG, способного генерировать ответы на основе содержимого локальных файлов.

96a597d25c64ac4ef160150b47fa59d0

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

🍨 DataCream — это новостная рассылка с историями и обучающими материалами по искусственному интеллекту, данным и технологиям. Если вам интересны эти темы, подпишитесь здесь.

Итак, в этой публикации мы пойдём ещё дальше, подробно изучив механизм поиска и проанализировав его. Как и в предыдущей публикации, в качестве примера я буду использовать текст «Войны и мира», лицензированный как общественное достояние и легко доступный через Project Gutenberg.

А как насчет встраивания?

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

Эмбеддинг — это векторное представление данных (в нашем случае текста), отражающее их семантическое значение. Каждое слово или предложение исходного текста сопоставляется с многомерным вектором. Модели эмбеддинга, используемые для выполнения этого преобразования, разработаны таким образом, что сходные значения приводят к векторам, близким друг к другу в векторном пространстве. Например, векторы для слов «happy» и «joyful» будут близки друг к другу в векторном пространстве, тогда как вектор для слова «sad» будет далёк от них.

Для создания высококачественных встраиваемых моделей, эффективно работающих в конвейере RAG, необходимо использовать предобученные модели встраиваемых моделей, например, модели OpenAI. Существуют различные типы встраиваемых моделей, которые можно создать, и соответствующие им модели. Например:

  • Эмбеддинги слов : при эмбеддингах слов каждое слово имеет фиксированный вектор независимо от контекста. Популярные модели для создания таких эмбеддингов — Word2Vec и GloVe.
  • Контекстные встраивания : Контекстные встраивания учитывают, что значение слова может меняться в зависимости от контекста. Возьмём, например, берег реки и открытие банковского счёта. Для создания контекстных встраиваний можно использовать модели BERT и OpenAI (например, text-embedding-ada-002).
  • Встроенные предложения : это вложения, передающие смысл полных предложений. Популярной моделью для создания встраиваемых предложений является Sentence-BERT.

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

Например, мы могли бы представить себе небольшой словарь, состоящий из слов «король», «королева», «женщина» и «мужчина», и присвоить каждому из них произвольный вектор.

король = [0,25, 0,75] королева = [0,23, 0,77] мужчина = [0,15, 0,80] женщина = [0,13, 0,82]

Затем мы могли бы попробовать выполнить некоторые векторные операции, например:

король — мужчина + женщина = [0,25, 0,75] — [0,15, 0,80] + [0,13, 0,82] = [0,23, 0,77] ≈ королева 👑

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

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

f7eb4e06d31c05858efc7ba2e6ac3041

Разница между этими простыми векторными примерами и реальными векторами, созданными моделями встраивания, заключается в том, что реальные модели встраивания генерируют векторы с сотнями измерений . Двумерные векторы полезны для построения интуитивного представления о том, как значение может быть отображено в векторном пространстве, но они слишком низкоразмерны, чтобы охватить сложность реального языка и лексики. Вот почему реальные модели встраивания работают с гораздо более высокими измерениями, часто в сотнях или даже тысячах. Например, Word2Vec создает 300-мерные векторы, в то время как BERT Base создает 768-мерные векторы. Эта более высокая размерность позволяет встраиваниям фиксировать множественные измерения реального языка, такие как значение, использование, синтаксис и контекст слов и фраз. В конечном счете, этот упрощенный двумерный пример позволяет нам построить некоторое интуитивное представление о том, что такое встраивание — тем не менее, оно довольно упрощенное и не обязательно то, что следует ожидать увидеть в реальных моделях.

Оценка сходства вложений

После преобразования текста в вложения, вывод становится векторной математикой. Именно это позволяет нам идентифицировать и извлекать релевантные документы на этапе извлечения в рамках фреймворка RAG. Преобразовав запрос пользователя и документы базы знаний в векторы с помощью модели вложения, мы можем вычислить степень их сходства, используя подходящую метрику, например, косинусное сходство, евклидово расстояние (расстояние L2) или скалярное произведение.

Косинусное сходство — это мера сходства двух векторов (вложений). Для двух векторов A и B косинусное сходство вычисляется следующим образом:

4cd18c9a9a32616a807202dccd9dad5b

Проще говоря, косинусное подобие рассчитывается как косинус угла между двумя векторами и принимает значения от 1 до -1. Более конкретно:

  • 1 указывает на то, что векторы семантически идентичны (например, car и automobile).
  • 0 указывает на то, что векторы не имеют семантической связи (например, банан и правосудие).
  • -1 указывает на то, что векторы совершенно противоположны, но на практике вложения не приводят к отрицательным сходствам, даже для таких антонимов, как «горячий» и «холодный».

Это связано с тем, что даже семантически противоположные слова (например, «горячо» и «холодно») часто встречаются в схожих контекстах (например, «становится жарко» и «становится холодно»). Чтобы косинусное сходство достигало -1, сами слова и их контексты должны быть абсолютно противоположными, чего в естественном языке обычно не происходит. В результате даже противоположные слова обычно имеют вложения, которые всё ещё довольно близки по значению. На практике оценки сходства обычно положительны.

Помимо косинусного сходства, к другим метрикам сходства относятся скалярное произведение (внутреннее произведение) и евклидово расстояние (расстояние L2). В отличие от косинусного сходства, скалярное произведение и евклидово расстояние зависят от величины, а это означает, что длина вектора влияет на результат. Чтобы использовать скалярное произведение в качестве меры сходства, эквивалентной косинусному сходству, необходимо сначала нормализовать векторы до единичной длины. Это связано с тем, что косинусное сходство математически равно скалярному произведению двух нормализованных векторов. Таким образом, аналогично косинусному сходству, более похожие векторы будут иметь большее скалярное произведение.

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

Возвращаясь к нашему конвейеру RAG, вычисляя оценки сходства между встраиваниями запроса пользователя и встраиваниями базы знаний, мы можем определить фрагменты текста, которые наиболее похожи (и, следовательно, контекстно релевантны) вопросу пользователя, извлечь их, а затем использовать для генерации ответа.

Поиск первых k похожих фрагментов

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

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

Для ускорения этого процесса часто используется поиск по приближенному методу ближайшего соседа (ANN). ANN находит векторы, наиболее похожие друг на друга, выдавая результаты, близкие к истинному топ-N, но гораздо быстрее, чем методы точного поиска. Конечно, точный поиск точнее; тем не менее, он также требует больших вычислительных затрат и может плохо масштабироваться в реальных приложениях, особенно при работе с большими наборами данных.

Кроме того, к оценкам сходства может быть применено пороговое значение, чтобы отфильтровать фрагменты, не соответствующие минимальному уровню релевантности. Например, в некоторых случаях фрагмент может быть рассмотрен только в том случае, если его оценка сходства превышает определённый порог (например, косинусное сходство > 0,3).

Так кто же такая Анна Павловна?

В примере «Войны и мира», как показано в моей предыдущей публикации, мы разбиваем весь текст на фрагменты и затем создаём соответствующие вложения для каждого фрагмента. Затем, когда пользователь отправляет запрос, например, «Кто такая Анна Павловна?», мы также создаём соответствующие вложения для текста запроса пользователя.

import os from langchain.chat_models import ChatOpenAI from langchain.document_loaders import TextLoader from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import FAISS from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.docstore.document import Document api_key = 'your_api_key' # инициализация LLM llm = ChatOpenAI(openai_api_key=api_key, model=»gpt-4o-mini», temperature=0.3) # инициализация встраиваний model embeddings = OpenAIEmbeddings(openai_api_key=api_key) # загрузка документов для использования в RAG text_folder = «RAG files» documents = [] for filename in os.listdir(text_folder): if filename.lower().endswith(«.txt»): file_path = os.path.join(text_folder, filename) loader = TextLoader(file_path) documents.extend(loader.load()) splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) split_docs = [] для doc в documents: chunks = splitter.split_text(doc.page_content) для chunk в chunks: split_docs.append(Document(page_content=chunk)) documents = split_docs # создать базу данных векторов w FAISS vector_store = FAISS.from_documents(documents, embeddings) retriever = vector_store.as_retriever() def main(): print(«Добро пожаловать в RAG Assistant. Введите 'exit' для выхода.n») while True: user_input = input(«Вы: «).strip() if user_input.lower() == «exit»: print(«Выход…») break # получить соответствующие документы relevant_docs = retriever.invoke(user_input) retrieved_context = «nn».join([doc.page_content for doc in relevant_docs]) # системное приглашение system_prompt = ( «Вы полезный помощник. » «Используйте ТОЛЬКО следующий контекст базы знаний, чтобы ответить пользователю. » «Если ответа нет в контексте, скажите, что вы не знаете.nn» f»Context:n{retrieved_context}» ) # сообщения для LLM messages = [ {«role»: «system», «content»: system_prompt}, {«role»: «user», «content»: user_input} ] # сформировать ответ response = llm.invoke(messages) assistant_message = response.content.strip() print(f»nAssistant: {assistant_message}n») если __name__ == «__main__»: main()

В этом скрипте я использую объект-ретривер LangChain: retriver = vector_store.as_retriever(), который по умолчанию использует метрику сходства базового индекса FAISS. FAISS предоставляет два индекса:

  • IndexFlatL2 использует расстояние L2. При использовании LangChain с FAISS (как мы) индекс по умолчанию обычно — IndexFlatL2.
  • IndexFlatIP, который использует скалярное произведение (внутреннее произведение)

Таким образом, в исходном скрипте фрагменты данных извлекаются с использованием расстояния L2 в качестве метрики. Этот скрипт также по умолчанию извлекает k=4 наиболее похожих фрагментов. Другими словами, мы извлекаем k наиболее релевантных запросу пользователя фрагментов данных на основе расстояния L2.

Таким образом, чтобы использовать косинусное сходство в качестве метрики поиска вместо L2, которая использовалась по умолчанию, нам нужно немного доработать наш исходный код. В частности, нам нужно нормализовать вложения (как вложения запроса пользователя, так и вложения базы знаний) и настроить хранилище векторов на использование скалярного произведения (внутреннего произведения) в качестве меры сходства вместо расстояния L2. Чтобы нормализовать вложения базы знаний, мы можем добавить следующий фрагмент после этапа фрагментации:

… documents = split_docs # нормализовать вложения базы знаний import numpy as np def normalize(vectors): vectors = np.array(vectors) norms = np.linalg.norm(vectors, axis=1, keepdims=True) return vectors / norms doc_texts = [doc.page_content for doc in documents] doc_embeddings = embeddings.embed_documents(doc_texts) doc_embeddings = normalize(doc_embeddings) # индекс faiss со внутренним произведением import faiss dimension = doc_embeddings.shape[1] index = faiss.IndexFlatIP(dimension) # индекс внутреннего произведения index.add(doc_embeddings) # создать базу данных векторов w FAISS vector_store = FAISS(embedding_function=embeddings, index=index, docstore=None, index_to_docstore_id=None) vector_store.docstore = {i: doc для i, doc в enumerate(documents)} retriever = vector_store.as_retriever() …

Поскольку мы делаем всё вручную, мы можем пока опустить функцию Retriever = Vector_store.as_retriever(). Также нам нужно добавить следующую часть в нашу функцию main(), чтобы нормализовать запрос пользователя:

… if user_input.lower() == «exit»: print(«Выход…») break # встраивание + нормализация запроса query_embedding = embeddings.embed_query(user_input) query_embedding = normalize([query_embedding]) # поиск по индексу FAISS D, I = index.search(query_embedding, k=2) # получение релевантных документов relevant_docs = [vector_store.docstore[i] for i in I[0]] retrieved_context = «nn».join([doc.page_content for doc in relevant_docs]) …

Обратите внимание, как мы можем явно определить количество извлеченных фрагментов k, теперь заданное как k=2.

Вдобавок к этому, чтобы вывести косинусные подобия, я также добавлю следующую часть в функцию main():

… retrieved_context = «nn».join([doc.page_content for doc in relevant_docs]) # D содержит оценки внутреннего произведения == косинусное сходство (после нормализации) print(«n5 лучших фрагментов и их оценки косинусного сходства:n») for rank, (idx, score) in enumerate(zip(I[0], D[0]), start=1): print(f»Фрагмент {rank}:») print(f»Косинусное сходство: {score:.4f}») print(f»Содержимое:n{vector_store.docstore[idx].page_content}n{'-'*40}») …

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

56f6977f947a5a170c9db987dbdb9f39

… но теперь мы также можем видеть текстовые фрагменты, на основе которых создан этот ответ, и соответствующие им оценки косинусного сходства…

a3edec5c4795724e69b5760cf397a859

Очевидно, что разные параметры могут приводить к разным результатам. Например, мы получим немного разные результаты при извлечении первых результатов с k=2, k=4 и k=10. Учитывая дополнительные параметры, используемые на этапе фрагментации, такие как размер фрагмента и перекрытие фрагментов, становится очевидно, что параметры играют решающую роль в получении хороших результатов от конвейера RAG.

• • •

Понравился этот пост? Давайте дружить! Присоединяйтесь ко мне:

📰 Substack 💌 Medium 💼 LinkedInКупите мне кофе!

• • •

Источник: towardsdatascience.com

✅ Найденные теги: новости, Объяснение
Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

Фото сгенерированных лиц: исследование показывает, что люди не могут отличить настоящие лица от сгенерированных
Нейросети построили капитализм за трое суток: 100 агентов Claude заперли…
Скетч: цифровой осьминог и виртуальный мир внутри компьютера с человечком.
Сцена с жестами пальцами, где один жест символизирует "VPN", а другой "KHP".
‼️Paramount купила Warner Bros. Discovery — сумма сделки составила безумные…
Скриншот репозитория GitHub "Claude Scientific Skills" AI для научных исследований.
Структура эффективного запроса Claude с элементами задачи, контекста и референса.
Эскиз и готовая веб-страница платформы для AI-дизайна в современном темном режиме.
ideipro logotyp
Image Not Found
Звёздное небо с галактиками и туманностями, космос, Вселенная, астрофотография.

Система оповещения обсерватории Рубина отправила 800 000 сигналов в первую ночь наблюдений.

Астрономы будут получать оповещения о небесных явлениях в течение нескольких минут после их обнаружения. Теренс О'Брайен, редактор раздела «Выходные». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной…

Мар 2, 2026
Женщина с длинными тёмными волосами в синем свете, нейтральный фон.

Расследование в отношении 61-фунтовой машины, которая «пожирает» пластик и выплевывает кирпичи.

Обзор компактного пресса для мягкого пластика Clear Drop — и что будет дальше. Шон Холлистер, старший редактор Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Мар 2, 2026
Черный углеродное волокно с текстурой плетения, отражающий свет.

Материал будущего: как работает «бессмертный» композит

Учёные из Университета штата Северная Каролина представили композит нового поколения, способный самостоятельно восстанавливаться после серьёзных повреждений.  Речь идёт о модифицированном армированном волокном полимере (FRP), который не просто сохраняет прочность при малом весе, но и способен «залечивать» внутренние…

Мар 2, 2026
Круглый экран с изображением замка и горы, рядом электронная плата.

Круглый дисплей Waveshare для креативных проектов

Круглый 7-дюймовый сенсорный дисплей от Waveshare создан для разработчиков и дизайнеров, которым нужен нестандартный экран.  Это IPS-панель с разрешением 1 080×1 080 пикселей, поддержкой 10-точечного ёмкостного сенсора, оптической склейкой и защитным закалённым стеклом, выполненная в круглом форм-факторе.…

Мар 2, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых