Рассмотрим техники построения и улучшения RAG систем: от нарезания текстов на куски, до продвинутых способов улучшения качества ответа.
Этим блогом можно пользоваться как шпаргалкой для проектирования своего RAG-а и/или для подготовки к собеседованиям.
Все полезные ссылки и материалы, на которые я опирался будут в конце.
Что такое RAG и зачем нужен
RAG – это фреймворк взаимодействия предобученной LLM с базой знаний. То есть при ответе LLM на запрос пользователя модель отвечает используя актуальный контекст из базы и свои pre-trained знания.
Обогащение запрос контекстом позволяет модели дать более точный ответ без необходимости дообучения на этих данных.
RAG очень часто можно использовать для формирования отчетов, создания корпоративных и специализированных чат-ботов. Причем так как не нужно дополнительного дообучения на доменных данных, то использование RAG-а часто более дешевый и быстрый вариант, а также безопасный и интерпретируемый по сравнению с fine-tuning-ом.
Базовый пайплайн подготовки системы RAG:
-
Загрузить документы
-
Нарезать на куски
-
Построить базу данных
-
Подготовить ретривер и, возможно, эмбеддер
-
Развернуть LLM для инференса
Базовый пайплайн применения RAG:
-
Аутентифицировать пользователя
-
Обработать входной запрос
-
Найти релеватные куски из базы данных
-
Отранжировать контексты
-
Собрать промпт из запроса и контекстов
-
Запромптить LLM
-
Получить от LLM ответ на вопрос
-
Верифицировать и отдать пользователю
Метрики RAG-а:
-
Приверженность — то, как сильно ответ похож на контекст, поданный в модель. Чем ниже метрика, тем выше вероятность галлюцинаций.
-
Полнота — то, насколько полный ответ для заданного вопроса и предоставленного контекста.
-
Учёт контекстов — то, какая доля контекстов использовалась при ответе.
-
Утилизация контекстов — то, какую долю контекста модель использовала при ответе на вопрос.
-
токсичность ответа
-
тональность ответа — например, по категориям эмоций
Больше про метрики можно почитать тут
Загрузка документов
Это первый этап построения RAG системы.
Для своей базы знаний можно использовать разные источники: видео на ютубе, конспекты ноушен, эксель таблицы и др.
Если в документе есть таблицы, или картинки, то из них можно извлечь полезную информацию в том числе – воспользоваться OCR и / или TableTransformer.
Библиотека, которая сама все делает за вас
Также важно помнить, что у каждого документа есть метаданные: название, дата, автор и др.
Нарезка документов
Mastering RAG: Advanced Chunking Techniques for LLM Applications
Урок по разделению с помощью LangChain
Нарезка документов на куски нужна для того, чтобы не переполнять контекст LLM ненужной и шумной информацией, а максимально информативной – это нужно как для более точного ответа, так и для ускорения работы LLM.
На что влияет нарезка на куски:
-
качество контекстов, которые отдаем LLM
Чем меньше контекст, тем меньше там информации, которая может сбить с толку LLM, а также тем легче правильнее определить семантический смысл эмбеддеру при создании вектора представления
-
затраты на индекс кусков
Чем больше кусков, тем выше затраты, так как нужно хранить больше векторов
-
Скорость извлечения релевантных кусков из индекса
Чем больше кусков, тем дольше задержка
-
Скорость ответа LLM
Чем длиннее контекст, подаваемый в LLM, тем дольше она будет отвечать
Факторы, влияющие на разделения документа на куски:
-
Структура текстапунктуация, переносы строки, маркдаун верстка и др.
-
Контекстное окно LLM и эмбеддера
-
Сложность и специфика запросов
Параметра функции разделения документа на куски:
-
размер куска по символам или по токенам
-
размер пересечения (наложения) кусков
-
желаемый разделитель
Виды нарезок:
-
По символам / токенам без специфичного разделителя
Разделяем документ согласно длины контекста и размере пересечения.Работает быстро, но глупо.
-
По символам / токенам с желаемыми разделителями
Пытаемся разделять на куски, например, по переносу строки, по точке или хотя бы по пробелу. Но все равно есть ограничение на длину контекста и длину наложения.Чуть дольше, но намного умнее, потому что не обрывает слова или даже предложения.
-
По Маркдаун разметке
Примерно как предыдущий метод, только разделитель заголовки маркдаунаПолучается более структурированное разделение, а также обновляет метадату кусков – добавляет поле названия.
-
Семантически
Разделяем текст на предложения.Добавляем в кусок текста новое предложение, если оно похоже семантически на уже имеющийся кусок.Ограничиваем количество предложений в куске.Разделение довольно умное, но расчет более ресурсоемкий из-за модели схожести.
-
Переписываем текст как утверждение
С помощью специальной модели или через LLM переписываем исходные предложения так, чтобы каждой из них по отдельности имело смысл, было понятно, о чем идет речь и не могло больше разделится на более мелкое утверждение.Потенциально очень умное разделение, которое сразу же помогает отвечать на вопрос. Но есть вероятность испортить хороший текст, а также требует дополнительных ресурсов на обработку документов.
-
Мульти-векторная индексация.
Иногда полезно для каждого документа иметь несколько векторов представления. Например, поделить документ на куски и для каждого посчитать векторы, делать ретрив по векторам кусков, а возвращать сам документ, таким образом уменьшается влияние шума и разнообразие топиков в документе. Также можно заменить или добавить к вектору документа вектор представление суммаризации этого документа. Также частая практика это придумывать гипотетические вопросы, ответом на которые может быть документ, и складывать в индекс вектор представления этих вопросов.
Кстати, метадата документа должна наследоваться каждому его куску, а также иногда дополняться свойствами отдельного куска, например, заголовок маркдаун при соответствующем методе разделения.
Метрики оценки качества, на которые можно ориентироваться при выборе стратегии разбиения документов на куски:
-
Приверженность — то, как сильно ответ похож на контекст, поданный в модель. Чем ниже метрика, тем выше вероятность галлюцинаций.
-
Полнота — то, насколько полный ответ для заданного вопроса и предоставленного контекста.
-
Учёт контекстов — то, какая доля контекстов использовалась при ответе.
-
Утилизация контекстов — то, какую долю контекста модель использовала при ответе на вопрос.
Как подбирать параметры для нарезки на куски:
База данных
База данных это система, которая хранит, индексирует и позволяет обрабатывать запросы для неструктурированных данных, таких как текст, изображения и подобное через числовое представление в виде вектора.
С помощью таких векторов можно делать поиск похожих объектов в базе данных. В нашем случае обычно поиск похожих кусков текста на пользовательский запрос.
Ключевые факторы выбора базы данных:
-
Открытая ли база или закрытая
-
Язык программирования, на котором можно создать клиента для использования базы данных
-
Лицензия
-
Фичи для организаций:
-
лимиты
-
пользовательская аутентификация
-
многое другое
-
-
Продуктовые фичи:
-
точный поиск
-
приближенный поиск — когда можно пожертвовать качеством для ускорения и масштабирования
-
префильтрация — когда до векторного поиска нужно уменьшить количество кандидатов
-
постфильтрация — дополнительный фильтр для улучшения точности результатов
-
гибридный поиск
-
поддержка разреженных векторов
-
поддержка поиска напрямую по тексту, например, через bm25
-
-
Возможность инференса моделей эмбеддингов.Например, sentence transformers, Mixedbread, BGE, OpenAI.
-
Возможность инференса модели реранкера
-
Скорость добавление новых объектов
-
Скорость поиска
-
индексация
-
кэширование
-
другие оптимизации
-
-
Затраты на обслуживание
-
disk-based базы данных VS in-memory
-
Serverless базы данных
-
квантизация эмбеддингов
-
-
Поддержка, мониторинг, бэкапы и тд
Выбор эмбеддера
Эмбеддинг — это векторное представление текста (или картинки, звука и тд) в пространстве, в котором похожие тексты отображаются в похожие векторы.
Как можно использовать эмбеддинги:
-
кодировать вопросы и контексты эмбеддером, чтобы для вопроса находить самые релевантные куски информации
-
находить few-shot примеры для in context learning (ICL)
-
определять намерения пользователя, чтобы, например провести по какой-нибудь ветке заготовленного сценария общения, или вызвать какой-нибудь инструмент
На что смотреть при выборе эмбеддера:
-
размерность векторного пространства
-
размер модели
-
перфоманс модели на доменных или общих бенчмарках
-
открытая или закрытая модель
-
стоимость
-
поддержка языков
-
гранулярность: на уровне слов, предложений, длинных документов
Виды эмбеддингов:
-
dense векторы-классика
-
разряженные. Извлекают из текста только самую релевантную информацию, а в других размерностях значения просто 0.Часто используется в задачах со специфической терминологией. Работает примерно как bag-of-words, но обходит многие его недостатки.https://arxiv.org/abs/2109.10086
-
матрешка эмбеддинги. Позволяют выбирать размерность вектора на инференсе. Можно почитать тут лонгрид про них.
-
long-context эмбеддинги
Если мы можем эффективно и без потери качества кодировать более длинные куски текста, то будем уменьшать задержку при поиске и косты на хранение векторов, так как их будет меньше.
-
code эмбеддинги
специально натренированные модели для работы с кодом
Примеры как, на каких задачах измерить качество эмбеддингов:
Метрики рага, которые подходят и для метрик эмбеддера:
-
Приверженность – то, как сильно ответ похож на контекст, поданный в модель. Чем ниже метрика, тем выше вероятность галлюцинаций.
-
Полнота – то, насколько полный ответ для заданного вопроса и предоставленного контекста.
-
Учёт контекстов – то, какая доля контекстов использовалась при ответе.
-
Утилизация контекстов – то, какую долю контекста модель использовала при ответе на вопрос.
https://www.galileo.ai/blog/mastering-rag-improve-performance-with-4-powerful-metrics – больше про метрики можно почитать тут
Извлечение, поиск
Поиск – это процесс извлечения максимально релевантных кусков текста из базы данных, в которых потенциально находится информация необходимая для ответа на вопрос пользователя.
С одной стороны мы хотим найти как можно больше полезных кусков и предоставить максимально полную картину для LLM, поэтому мы хотим находить не только самые релевантные контексты, но и максимально разнообразные.
Но с другой стороны, чем больше текста вы извлекаем, тем более они зашумленные, менее релевантные, тем самым LLM будет проще галлюцинировать, поэтому важность этапа поиска нельзя недооценивать.
Техники для улучшения извлечения:
-
Hypothetical document embeddings (HyDE) https://arxiv.org/abs/2212.10496
-
На вопрос пользователя генерируем с помощью LLM такой текст, в котором гипотетически мог бы содержаться ответ на вопрос.Такой документ будет недостоверным в большинстве случаев, но зато текстовый энкодер сможет построить очень близкий эмбеддинг для реального контекста.
-
Maximal Marginal Relevance (MMR)
Техника для увеличения разнообразия в найденном множестве контекстов. То есть мы скорее отдадим предпочтение менее релевантному контексту, но еще незнакомому.
-
Autocut
Смотрим на скоры похожести контекста и определяем так называемые “прыжки” в них, находя самую оптимальную границу между релевантными и менее релевантными контекстами. https://weaviate.io/developers/weaviate/api/graphql/additional-operators#autocut
-
Recursive retrieval
Нарезаем контекст на более мелкие куски, ищем релевантный, но отдаем более крупный контекст.https://youtu.be/TRjq7t2Ms5I?si=D0z5sHKW4SMqMgSG&t=742 https://docs.llamaindex.ai/en/stable/examples/query_engine/pdf_tables/recursive_retriever.html Похожая техника Sentence window retrieval, где мы возвращаем не кусок, а окно, которое включает наш кусок текстаhttps://docs.llamaindex.ai/en/latest/examples/node_postprocessor/MetadataReplacementDemo.html
-
SelfQuery
Техника для таких вопросов, где полезно будет сделать фильтрацию по какому-нибудь атрибуту, например, по дате.
-
Сжатие контекстов
После извлечения самый релевантных контекстов, мы их суммаризируем при условии вопроса юзера с помощью LLM. Таким образом финальная LLM получает на вход более плотную информацию, с минимумом шума, но, скорее всего, довольно полную. Хотя тут мы делаем дополнительные вызовы суммаризатора.
-
Классический методы поиска
-
svm
-
tf-idf
-
и другие
-
-
Выбор LLM
При выборе LLM стоит опираться на то, на каких данных и задачах была обучена модель, с какими языками она хорошо работает. Желательно проверить несколько моделей самостоятельно перед выкаткой, также можно опираться на результаты бенчмарков.
-
Open-source или проприетарная модель
С одной стороны использование закрытых апи упрощает разработку системы, но с другой стороны возникает вопрос конфиденциальности данных, а также зависимости от внешней апи.
-
Размер модели
При большем размере растет качество, но растет задержка и падает пропускная способность.
-
Параметры генерации
Такие параметры как температура, top p, top k, могу сильно влиять на ответы моделей, их креативность и разнообразие.
-
Способ инференса
Этот вопрос может отпасть, если мы будем использовать закрытые апи, но в случае с моделями, которые мы сами хотим разворачивать и поддерживать, этот вопрос является очень существенным.Так как разные фреймворки инференса поддерживают разные модели, способы оптимизации и ускорения инференса.Основные решения: tensorrt-llm, vllm, tgi, deepspeed-mii.
-
Few-shot prompting
Показываем несколько примеров, как могут выглядеть ответы.Можно улучшить выбор этих нескольких примеров через поиск ближайших соседей.
-
Chain-Of-Thoughts
Заставлять LLM генерировать цепочку мыслей и только после размышления давать финальный ответ: “Take a deep breath and let’s think step by step”.
-
Map reduce
делаем суммаризацию каждого контекста, и только потом по всем суммаризациям генерируем ответ на вопрос.
-
Map refine
Начинаем с 1го контекста, отвечаем на вопрос по нему, затем обновляем ответ с учетом 2го контекста и так далее.
-
Thread of Thought
Разбиваем длинные куски текста на контексты, модель извлекает из них релевантную информацию, затем просим модель суммаризировать и проанализировать информацию, а не просто прочитать и понять.
-
Chain of Note
Примерно то же самое, что и ToT, но здесь для каждого извлеченного куска текста генерируем суммаризацию и оцениваем его релевантность касательно вопроса. И уже на основании таких заметок отвечаем.
-
Chain of Verification(CoVe)
На запрос генеририруем бейзлайн, далее подбираем проверочные вопросы, отвечаем на них и редактируем ответ.
-
Эмоциональное давление
Удивительно, но работает все: представь, что ты эксперт, я дам тебе 200 долларов за правильный ответ и др.)
-
Можно переписывать вопрос с учетом истории, чтобы поиск релевантных контекстов работал корректно.
-
Можно вместе с вопросом и релевантным контекстом также предоставить доступ LLM к истории чата.
-
Если чат разрастается, его можно суммаризировать: либо просто извлечь самое главное, либо суммаризировать при условии текущего вопроса, чтобы точно не потерять ничего важного из истории.
-
веду тг канал @rockaux
-
лонгриды в телетайпе по DL, NLP, LLM (посты: функции активации, оптимизаторы, регуляризация в DL и другие)
-
YouTube канал (видео: ML roadmap, DL roadmap)
-
выступление на митапе Контура по LLM alignment
Работают напрямую с текстом.
https://learn.deeplearning.ai/courses/langchain-chat-with-your-data/lesson/5/retrieval – урок по извлечению от LangChain
Question Answering / generation
На этапе генерации мы отдаем релевантные контексты вместе с вопросом пользователя в LLM, а от нее уже получаем ответ. Это центральный элемент RAG системы, поэтому тут также нужно аккуратно рассмотреть следующие пункты:
Отдельно затронем техники промпт-инжиниринга для улучшения ответов LLM. Они могут помочь сделать ответ информативнее, более персонализированным для юзера, а также уменьшить вероятность галлюцинаций.
Также для того, чтобы еще минимизировать риски галлюцинаций, можно ответ LLM проверять дополнительно на безопасность/адекватность той же LLM, или другими специализированными моделями, или просто регулярными выражениями.
Chat, user experience
Также для некоторых бизнес кейсов важно уметь помнить то, о чем шла речь в предыдущих сообщениях.
Это можно сделать, сохраняя предыдущие вопросы и ответы на них.
Так как RAG это в общение с пользователем, то для дальнейших улучшений, можно в сервис внедрить логику сбора обратной связи: через лайки/дизлайки или открытых форм.
В том числе для большей прозрачности работы сервиса можно давать пользователю доступ к извлеченным контекстам и цепочкам мыслей модели.
А для ускорения работы модели, можно делать кэширование и не нагружать модели по несколько раз.
А также использовать техники оптимизации инференса как эмбеддинг модели (например, вот лонгрид: https://teletype.in/@abletobetable/embeds_ops ), так и LLM (начиная от continuous batching до speculative decoding).
Заключение
Заключения не будет, я устал. Просто красивые схемы из статьи.
Полезные ссылки
Мой тг – @abletobetable
По RAG:
A Survey on Retrieval-Augmented Generation for Large Language Models:
https://arxiv.org/abs/2312.10997
HF blogposts:
RAG vs Fine-Tuning for LLMs: A Comprehensive Guide with Examples
Better RAG 2: Single-shot is not good enough
Better RAG 3: The text is your friend
Mastering RAG series:
How To Architect An Enterprise RAG System
RAG Vs Fine-Tuning Vs Both: A Guide For Optimizing LLM Performance
Advanced Chunking Techniques for LLM Applications
Improve RAG Performance With 4 Powerful RAG Metrics
LLM Prompting Techniques For Reducing Hallucinations
How to Select A Reranking Model
How to Select an Embedding Model
Choosing the Perfect Vector Database
Generate Synthetic Data for RAG in Just $10
Mastering RAG: 8 Scenarios To Evaluate Before Going To Production
DeepLearning.AI courses:
Автор: abletobetable


