Привет, Хабр! Меня зовут Лиза, я разработчик направления AI/ML/DS. Вместе с командой мы решаем задачи, связанные с обработкой текстовых данных. В работе с большими языковыми моделями и классическим NLP я постоянно ищу баланс между скоростью, качеством и достоверностью — и в этой статье поделюсь нашими кейсами и практическим опытом, который помогает создавать надёжные и эффективные решения в условиях быстро меняющегося мира нейросетей.

Обычный рабочий день: заварил кофе, созвонов нет, можно и поработать. Заходишь в Jira, а там новая таска. Срок исполнения — вчера. Данные в хаосе, от прямых ответов на вопросы про данные заказчик уклоняется.

И тут возникает соблазн — отдать всё LLM-ке. Пишем промпт, слёзно просим несколько производственных видеокарт и кидаем всё в LLM, она ж явно больше нас повидала. И простое программистское: докручиваем API-шку, пишем тесты, но счастье не случается. Где-то получаем значение, которого нет, где-то ответ с первого взгляда правильный, но если рассмотреть глубже - не подходит. 

Судорожно добавляем новые вводные в промпт, а результат в некоторых кейсах всё равно как в анекдоте:

— Муж, купи батон хлеба, если будут яйца — возьми десяток.

Муж возвращается из магазина с десятью батонами.

— Ты зачем столько хлеба купил?

— Так ведь яйца были...

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

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

При хорошем понимании предметной области большую задачу по типу: "Хочу, чтоб оно работало так, на входе много различных форматов, на выходе вот такая структура" можно декомпозировать в понятные подзадачи, которые при должном кропотливом сборе данных решаются через дешёвые в инференсе и предсказуемые модели, например, на основе архитектуры BERT, а также эвристик. Если нет времени, но есть вычислительный ресурс — с декомпозированными задачами большие LLM справляются хорошо, и результат проще контролировать.

Приведу пример: из вопроса к системе «Какой номер телефона у Иванова» информацию можно получить несколькими способами:

  • Попросить извлечь все сущности LLM, чтобы понять, как сформировать поисковой запрос. С этой задачей справится и несложный промпт.

  • Извлечь сущности через модель NER, обученную, например, на основе архитектуры BERT. Но необходимо знать, с какими запросами может приходить пользователь, собрать данные, обучить модель.

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

Эвристики

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

Примеры разнообразны и зависят от конкретной задачи. Например, можно сразу понять, что с большой долей вероятности в документе не содержится нужной информации на основе ключевых слов, и не запускать дорогие в инференсе модели. Чем выше доверие к эвристикам, тем меньше будет тратиться вычислительных ресурсов на вызов «дорогих» моделей. И соответственно, чем меньше доверие, тем чаще будет происходить обращение к дорогостоящим моделям. Это регулируется при помощи threshold — порогового значения, обозначающего уверенность в решении.

Перейду непосредственно к обсуждению подходов.

LLM

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

  • Дорогой инференс  — при большом количестве пользователей и запросов стоимость вычислений может быть существенной. Например, для обработки 1000 одновременных запросов может потребоваться десяток мощных GPU.

  • Галлюцинации — модель может генерировать правдоподобно звучащую, но фактически неверную информацию.

  • Все равно нужно разобраться в предметной области — без понимания контекста невозможно настроить модель правильно.

  • Если ваша задача очень сложная и специфичная может понадобиться обучение: адаптер дешевле, но сравнимо с тюнингом машины; полное обучение очень ресурсозатратно, стоимость обучения зачастую измеряется в миллионах.

Если не хочется заморачиваться, можно использовать внешние LLM, но наряду с ограничениями выше появляются следующие:

  • Конфиденциальность данных не гарантирована — ваши запросы могут использоваться для дообучения модели.

  • Мы не контролируем серверы, на которых происходит инференс — это риск для критичных бизнес-процессов.

  • Часто есть ограничения по количеству запросов в единицу времени — rate limits могут замедлить работу системы. 

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

Чек-лист для качественного итогового решения:

1. Грамотно подобранная модель с учётом данных, на которых она училась, и мировой практики применения в различных типах задач. Не всегда учитываются только SOTA или самые высокие метрики на общедоступных датасетах. Обычно всё решают тесты: запускаем модель, смотрим бизнес-метрики, тайминги и т.д.

2. Чёткий промпт, учитывающий особенности предметной области, но без лишних деталей. Скорее всего, придётся перебрать десятки, а то и сотни промптов. Здесь всё то же — грамотно составленные тесты помогут понять, какой промпт подходит.

3. Если галлюцинации имеют критическое значение — необходимы дополнительные методы проверки ответов и совпадения их с исходным текстом. Например, если это система ответов на вопросы, проверить, что исходные числа взяты из текста и т.д.

Но классика жива. Если не боитесь сбора датасетов, ломания головы о том, как подать данные, какой препроцессинг, постпроцессинг и другие «цессинги» сделать — можно рассмотреть этот вариант.

Классический NLP

Классический NLP преобразует одни данные в другие предсказуемо, ничего кардинально нового не генерируется. Выход модели строго определён входом и весами модели, такие модели ошибаются предсказуемо, ошибки исправляются расширением обучающей выборки и подбором размера, архитектуры, гиперпараметров модели. Для LLM важны все те же факторы, но из-за того, что модель генеративная, предсказуемость и объяснимость намного сложнее, а взаимосвязи между входом и выходом зачастую необъяснимы.

Для обучения качественной модели:

  • Требуется большой объём размеченных данных для обучения моделей. Спойлер: на первых итерациях можно грамотно создать синтетические данные с минимумом ручной разметки.

  • Модели узкоспециализированы под конкретную задачу. Можно делать более общую модель для домена, а в дальнейшем различные постобработки.

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

  • Нужно хорошо понимать предметную область.

Чек-лист для качественного итогового решения:

  • Достаточный набор разнообразных тестовых данных — чем больше edge cases покрыто, тем лучше.

  • Качественная предобработка данных: очистка данных, подготовка нужного формата данных (схожего с тем, на чём учится модель).

  • Регулярное обучение модели с учётом обратной связи от пользователей и проблемных кейсов.

  • Метрики качества с учётом бизнес-задачи, её особенностей, критериев качества наряду с F1, Accuracy... по различным моделям.

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

Бизнес-кейсы

Теория хороша, но в проде всё решают кейсы — что реально сработало

Начну с животрепещущего - вроде, структурированных, а по факту - нет, данных.

Кейс 1: Извлечение данных из таблиц

Представьте, что на входе у нас большая  огромная таблица с иерархией заголовков, ещё и не лучшего качества с точки зрения чёткости границ таблицы. Мы пропускаем её через сервис OCR и получаем текстовое представление.

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

Другой вариант в данной задаче — обучить модель NER (извлечение именованных сущностей из текста), правильно сделать предобработку и постобработку данных с учётом особенностей предметной области и данных, и получить на выходе всю интересующую информацию.

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

Выбор пути зависит от желаемых:

  • точности

  • стоимости инференса

  • допустимости галлюцинаций

  • контроля над результатом

Давайте разберем варианты решения на конкретном примере:

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

Таблицу пропускаем через модули для LayoutDetector, распознаем моделькой OCR и на выходе получаем текст с координатами и какой-то структурой. Например

 {
                    "id": "/page/0/Table/1",
                    "block_type": "Table",
                    "bbox": [
                        80.17,
                        338.47,
                        558.55,
                        661.58
                    ],
                    "children": [
                        [
                            {
                                "cell_id": 0,
                                "bbox": [
                                    80.17100755667506,
                                    339.4190250462628,
                                    721.0589470098,
                                    375.95827065173154
                                ],
                                "row_id": 0,
                                "rowspan": 1,
                                "within_row_id": 0,
                                "col_id": 0,
                                "colspan": 2,
                                "text": "какой-то текст",
                                "len": 16
                            },

И таких children-ов по количеству ячеек в таблице.

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

Вариант 2 (классический NLP): Для каждого значения таблицы строим путь к нему. Например, для таблицы выше один из примеров будет:

Текстурные характеристики | объем пор, см3/г | суммарный | 0,959

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

Кейс 2: Определение намерений пользователя

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

На вход приходит запрос пользователя. Сначала нужно определить, а чего же хочет пользователь? Задача усложняется тем, что вопросы “Где работает Крылов из документов” и “Где документ о работе Крылова?” - это запросы с разным намерением, но по набору слов очень похожи. Задача, на самом деле, нетривиальная. И решать ее все так же можно обучив классификатор, подготовив выборку для обучения или написать подробный промпт с описанием максимального количества нюансов. 

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

Что выбрать?

Когда стоит использовать LLM:

  • Быстрый MVP без доступа к обучающим данным

  • Задачи с высокой вариативностью входных данных

  • Когда важна гибкость, а вычислительные ресурсы не критичны

Когда стоит использовать внешние API (GPT-4, Claude и др.):

  • Прототипирование и тестирование идей

  • Небольшое количество запросов

  • Нет требований к конфиденциальности данных

Когда стоит использовать классический NLP:

  • Критична предсказуемость результата

  • Важна низкая стоимость инференса при большом количестве запросов

  • Есть время и ресурсы на сбор и разметку данных

  • Узкая специфичная задача с понятной структурой

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

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

А как вы ищете баланс между качеством, скоростью и количеством моделей в своих проектах?

Комментарии (0)