Этот сайт использует файлы cookies. Продолжая просмотр страниц сайта, вы соглашаетесь с использованием файлов cookies. Если вам нужна дополнительная информация, пожалуйста, посетите страницу Политика файлов Cookie
Subscribe
Прямой эфир
Cryptocurrencies: 9917 / Markets: 81992
Market Cap: $ 2 341 562 930 395 / 24h Vol: $ 71 455 291 493 / BTC Dominance: 53.420007436143%

Н Новости

Обработка данных для RAG-систем

Всем привет, меня зовут Андрей Шахов, я Python-разработчик и Lead Backend Developer в wpp.digital[ссылка удалена мод.]. ML-направлением, а точнее LLM в компании я начал заниматься только в конце 2023 года. Сейчас на задачи подобного рода у меня уходит примерно 40% рабочего времени.

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

Судя по контенту на habr, который я успел просмотреть к этому времени, один из самых популярных способов использования LLM – построение RAG-систем. Далее об этом.

Retrieval Augmented Generation (RAG) – это система, которая передает данные в LLM не в сыром виде от пользователя, а обогащает их контекстом. Система позволяет расширить знание модели, без сложного и долгого обучения. В некоторых случаях даже может хватить работы с удаленным API того же OpenAI, соответственно, ваш бекенд будет маленьким и быстрым. Альтернатива — дообучать модель своими данными, но ответы все еще будут неточными из-за размеров модели, и ресурсов на такое обучение нужно очень много.

Сейчас я расскажу, как готовить данные для RAG-систем. Здесь не будет инструкции, как собрать очередную RAG на каком-нибудь GPT — подобных статей на habr много, без труда найдете подходящую именно для вас. Тут про предварительный шаг – обработку данных, а именно мой опыт обработки отдельно взятой закрытой корпоративной вики.

Флоу процесса обработки

Вот так я выстроил первичный процесс обработки данных после выгрузки:

  1. Считал файл страницы;

  2. Очистил контент от шума;

  3. Разделил очищенный контент на части;

  4. Превратил части в векторы с помощью embeddings;

  5. Загрузил векторы в БД qdrant, снабдив мета-информацией.

Что такое embedding

Это функция, которая преобразует текст в вектор из чисел. Благодаря координатам можно выявлять схожесть одного текста с другим – чем дальше расстояние, тем менее один текст похож с другим с точки зрения смысла. И соответственно наоборот.

Особенность векторизации

Важная особенность RAG-систем – способ, которым мы превращаем текст в вектор, то есть в определенный порядок координат в многомерном пространстве. Нужно помнить, что для запросов пользователя нужно применить тот же самый embedding, что и к контексту. Это нужно, чтобы векторы были корректными относительно друг друга с точки зрения кодирования смысла, иначе система просто не будет работать. Иногда такие очевидные вещи ускользают из внимания, особенно при смене embedding'ов для обработки запросов пользователя.

Что такое qdrant

Это векторная база данных, которая позволяет хранить векторы, вместе с meta-информацией. Каждая запись обязательно должна быть одной и той же "размерности" вектора, чтобы по ней можно было делать корректный поиск. Каждой записи можно добавить любую другую, добавочную информацию – например, соответствующий вектору текст, источник этого "кусочка", страницу где он расположен и проч.

Дальнейшие шаги сильно зависят от качества контента, поэтому из всех шагов я расскажу, как я строил работу на 2-м шаге.

Мне было дано множество страниц в Яндекс. Вики. Все оформлены в разных форматах, с разным контентом и разными редакторами в основе. Страницы были как разводящие, то есть хранящие в себе только ссылки на другие страницы, так и с нужными нам данными. Всего у меня получилось выгрузить порядка 130 страниц в формате Markdown.

Почему markdown?

Markdown отдает наиболее "чистый" текст с точки зрения нужной информации. html-формат, который тоже отдается вики, имеет больше шума. Его обработка даже с помощью beautifulsoup занимает много больше времени, чем чистка markdown. Ранее также у меня был опыт парсинга pdf (как одно-, так и двухколоночного формата), все свелось к извлечению именно текста, с полным игнорированием других элементов.

С другими форматами дела обстоят примерно также – везде много шума, везде от него нужно избавиться. Поэтому выбирать формат, в первую очередь, стоит с точки зрения простоты/быстроты приведения его к чистому тексту.

Например, код одной из страниц (другие содержат корпоративную информацию и не могут быть разглашены, а эта отражает наглядно суть) на html:

<ol>  
<li>  
<p data-line="0"><strong>Ctrl + C</strong> - Копировать</p>  
</li>  
<li>  
<p data-line="2"><strong>Ctrl + X</strong> - Вырезать</p>  
</li>  
<li>  
<p data-line="4"><strong>Ctrl + V</strong> - Вставить</p>  
</li>  
<li>  
<p data-line="6"><strong>Ctrl + Z</strong> - Отменить</p>  
</li>  
<li>  
<p data-line="8"><strong>Ctrl + Y</strong> - Повторить</p>  
</li>  
<li>  
<p data-line="10"><strong>Ctrl + A</strong> - Выделить все</p>  
</li>  
<li>  
<p data-line="12"><strong>Ctrl + S</strong> - Сохранить</p>  
</li>  
<li>  
<p data-line="14"><strong>Ctrl + N</strong> - Создать новый документ/окно</p>  
</li>  
<li>  
<p data-line="16"><strong>Ctrl + O</strong> - Открыть файл</p>  
</li>  
<li>  
<p data-line="18"><strong>Ctrl + P</strong> - Печать</p>  
</li>  
</ol>

И теперь та же страница в Markdown:

1. **Ctrl + C** - Копировать  
2. **Ctrl + X** - Вырезать  
3. **Ctrl + V** - Вставить  
4. **Ctrl + Z** - Отменить  
5. **Ctrl + Y** - Повторить  
6. **Ctrl + A** - Выделить все  
7. **Ctrl + S** - Сохранить  
8. **Ctrl + N** - Создать новый документ/окно  
9. **Ctrl + O** - Открыть файл  
10. **Ctrl + P** - Печать  

Пустые страницы

В вики нашлось несколько нужных страниц, но абсолютно пустых. Такие страницы cчитаются "корневыми" — через них строятся семантические связи между страницами, удалять их нельзя, иначе сломается структура. Были страницы, которые становились пустыми в процессе очистки. Такие страницы сразу удалялись при выгрузке, чтобы не тормозить дальнейший ход работы.

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

  1. Очистить контент от markdown-вставок изображений с помощью регулярки {%(.*)%};

  2. Очистить контент от ссылок в markdown формате регуляркой \[(.*)]\((.*)\);

  3. Если элемент содержит менее 4 символов после этого – пропустить его;

  4. Если элемент содержит больше 4 символов — это, с большой вероятностью, какой-то разумный текст, оставить.

О минусах

Такой подход не оказался идеальным, были минусы.

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

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

Чуда не случилось — фокус внимания модели наоборот размазывался. Если она вместе с вопросом получала несколько страниц контекста, ответ чаще всего был неверным, грубо говоря вообще про другое. Регулярно получал ответы вида "bot bot bot..", что является повтором начального токена в промпте, а не корректным ответом. Листинг такого решения следующий:

import re  
from pathlib import Path  

from llama_index.schema import Document  
from marko import Markdown  
from marko.block import BlankLine, BlockElement  
from marko.element import Element  
from tqdm import tqdm  

from utils import get_index  


def get_docs() -> list[Document]:  
	md = Markdown()  
	pages = []  
	for file in Path('pages/').iterdir():  
    	content = file.read_text()  
    	document = md.parse(content)  
    	for block in document.children:  
        	for text in get_text(block):  
            	doc = Document(text=text, metadata={'file_path': file.name})  
            	doc.excluded_embed_metadata_keys = ['file_path']  
            	pages.append(doc)  
	return pages  

  
def get_text(element: BlockElement | Element) -> list[str]:  
	if type(element) is BlankLine:  
        return []  

	if type(element.children) is str:  
    	text = str(element.children)  
     	match_insert = re.match(r'{%(.*)%}', text)  
    	match_image = re.match(r'!\[(.*)](.*)', text)  
    	if match_insert or match_image or len(text) < 4:  
            return []  
    	return [text]  
	texts = []  

	for el in element.children:  
    	texts.extend(get_text(el))  
	return texts  


def main():  
	docs = get_docs()  
	index = get_index()  
	for doc in tqdm(docs):  
    	index.insert(doc)  


if __name__ == "__main__":  
	main()

Флоу процесса обработки №2

На следующем этапе я пересмотрел весь процесс обработки контента и сделал следующие изменения:

  1. Очистка контента регулярками стала происходить при загрузке — так файлов стало меньше еще на несколько штук;

  2. Добавлена новая регулярка замены: [\n]+ заменялось на один перенос \n;

  3. Отказался от marko в пользу деления контента через переносы.

В результате этого в запрос к LLM стали попадать только осмысленные части текста, которые вернула БД в ответ на запрос.

Такие изменения помогли решить проблемы с вопросами, которые могут встречаться на разных страницах.

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

Итоговый листинг получился следующим:

import re  
from pathlib import Path  
 
from llama_index.core.schema import Document  
from tqdm import tqdm  
 
from config import settings  
from utils import get_index  
 

def get_docs() -> list[Document]:  
	pages = []  
	for file in Path(settings.pages_path).iterdir():  
    	content = file.read_text()  
    	content = re.sub(r'{%(.*)%}', "", content)  
    	content = re.sub(r'\[(.*)]\((.*)\)', "", content)  
    	content = re.sub('[\n]+', '\n', content)  
    	pages.extend(get_doc_by_text(content, file))  
	return pages  


def get_doc_by_text(content: str, file: Path) -> list[Document]:  
	pages = []  
	for block in content.split('\n'):  
        doc = Document(text=block, metadata={'file_path': file.name})  
    	doc.excluded_embed_metadata_keys = ['file_path']  
    	if not is_doc_valid(doc):  
        	continue
        pages.append(doc)  
	return pages  


def is_doc_valid(doc: Document) -> bool:  
	if type(doc.text) is not str:  
        return False  
	return len(doc.text.strip()) > 4  


def main():  
	docs = get_docs()  
	index = get_index()  
	for doc in tqdm(docs):  
        index.insert(doc)  
 
 
if __name__ == "__main__":  
	main()

К итогам

Из очевидных истин:

  1. Чем меньше размер запроса к LLM, тем меньше частота неточных/неверных ответов;

  2. Качество ответов зависит также от промпта, поэтому дальше нужно тюнить уже их.

Теперь к моим личным выводам:

  • чем проще формат данных, тем меньше потребуется ресурсов для очищения контента от шума;

  • подобрать для всех документов единый 100% рабочий рецепт очистики невозможно, но стремиться к нему нужно, чтобы не очищать данные до второго пришествия;

  • декомпозиция процесса позволяет находить слабые стороны процесса очистки и оптимизировать/заменять их.

Welcome в комментарии, буду рад конструктивной критике.

Источник

  • 07.09.23 16:24 CherryTeam

    Cherry Team atlyginimų skaičiavimo programa yra labai naudingas įrankis įmonėms, kai reikia efektyviai valdyti ir skaičiuoti darbuotojų atlyginimus. Ši programinė įranga, turinti išsamias funkcijas ir patogią naudotojo sąsają, suteikia daug privalumų, kurie padeda supaprastinti darbo užmokesčio skaičiavimo procesus ir pagerinti finansų valdymą. Štai keletas pagrindinių priežasčių, kodėl Cherry Team atlyginimų skaičiavimo programa yra naudinga įmonėms: Automatizuoti ir tikslūs skaičiavimai: Atlyginimų skaičiavimai rankiniu būdu gali būti klaidingi ir reikalauti daug laiko. Programinė įranga Cherry Team automatizuoja visą atlyginimų skaičiavimo procesą, todėl nebereikia atlikti skaičiavimų rankiniu būdu ir sumažėja klaidų rizika. Tiksliai apskaičiuodama atlyginimus, įskaitant tokius veiksnius, kaip pagrindinis atlyginimas, viršvalandžiai, premijos, išskaitos ir mokesčiai, programa užtikrina tikslius ir be klaidų darbo užmokesčio skaičiavimo rezultatus. Sutaupoma laiko ir išlaidų: Darbo užmokesčio valdymas gali būti daug darbo jėgos reikalaujanti užduotis, reikalaujanti daug laiko ir išteklių. Programa Cherry Team supaprastina ir pagreitina darbo užmokesčio skaičiavimo procesą, nes automatizuoja skaičiavimus, generuoja darbo užmokesčio žiniaraščius ir tvarko išskaičiuojamus mokesčius. Šis automatizavimas padeda įmonėms sutaupyti daug laiko ir pastangų, todėl žmogiškųjų išteklių ir finansų komandos gali sutelkti dėmesį į strategiškai svarbesnę veiklą. Be to, racionalizuodamos darbo užmokesčio operacijas, įmonės gali sumažinti administracines išlaidas, susijusias su rankiniu darbo užmokesčio tvarkymu. Mokesčių ir darbo teisės aktų laikymasis: Įmonėms labai svarbu laikytis mokesčių ir darbo teisės aktų, kad išvengtų baudų ir teisinių problemų. Programinė įranga Cherry Team seka besikeičiančius mokesčių įstatymus ir darbo reglamentus, užtikrindama tikslius skaičiavimus ir teisinių reikalavimų laikymąsi. Programa gali dirbti su sudėtingais mokesčių scenarijais, pavyzdžiui, keliomis mokesčių grupėmis ir įvairių rūšių atskaitymais, todėl užtikrina atitiktį reikalavimams ir kartu sumažina klaidų riziką. Ataskaitų rengimas ir analizė: Programa Cherry Team siūlo patikimas ataskaitų teikimo ir analizės galimybes, suteikiančias įmonėms vertingų įžvalgų apie darbo užmokesčio duomenis. Ji gali generuoti ataskaitas apie įvairius aspektus, pavyzdžiui, darbo užmokesčio paskirstymą, išskaičiuojamus mokesčius ir darbo sąnaudas. Šios ataskaitos leidžia įmonėms analizuoti darbo užmokesčio tendencijas, nustatyti tobulintinas sritis ir priimti pagrįstus finansinius sprendimus. Pasinaudodamos duomenimis pagrįstomis įžvalgomis, įmonės gali optimizuoti savo darbo užmokesčio strategijas ir veiksmingai kontroliuoti išlaidas. Integracija su kitomis sistemomis: Cherry Team programinė įranga dažnai sklandžiai integruojama su kitomis personalo ir apskaitos sistemomis. Tokia integracija leidžia automatiškai perkelti atitinkamus duomenis, pavyzdžiui, informaciją apie darbuotojus ir finansinius įrašus, todėl nebereikia dubliuoti duomenų. Supaprastintas duomenų srautas tarp sistemų padidina bendrą efektyvumą ir sumažina duomenų klaidų ar neatitikimų riziką. Cherry Team atlyginimų apskaičiavimo programa įmonėms teikia didelę naudą - automatiniai ir tikslūs skaičiavimai, laiko ir sąnaudų taupymas, atitiktis mokesčių ir darbo teisės aktų reikalavimams, ataskaitų teikimo ir analizės galimybės bei integracija su kitomis sistemomis. Naudodamos šią programinę įrangą įmonės gali supaprastinti darbo užmokesčio skaičiavimo procesus, užtikrinti tikslumą ir atitiktį reikalavimams, padidinti darbuotojų pasitenkinimą ir gauti vertingų įžvalgų apie savo finansinius duomenis. Programa Cherry Team pasirodo esanti nepakeičiamas įrankis įmonėms, siekiančioms efektyviai ir veiksmingai valdyti darbo užmokestį. https://cherryteam.lt/lt/

  • 08.10.23 01:30 davec8080

    The "Shibarium for this confirmed rug pull is a BEP-20 project not related at all to Shibarium, SHIB, BONE or LEASH. The Plot Thickens. Someone posted the actual transactions!!!! https://bscscan.com/tx/0xa846ea0367c89c3f0bbfcc221cceea4c90d8f56ead2eb479d4cee41c75e02c97 It seems the article is true!!!! And it's also FUD. Let me explain. Check this link: https://bscscan.com/token/0x5a752c9fe3520522ea88f37a41c3ddd97c022c2f So there really is a "Shibarium" token. And somebody did a rug pull with it. CONFIRMED. But the "Shibarium" token for this confirmed rug pull is a BEP-20 project not related at all to Shibarium, SHIB, BONE or LEASH.

Для участия в Чате вам необходим бесплатный аккаунт pro-blockchain.com Войти Регистрация
Есть вопросы?
С вами на связи 24/7
Help Icon