
Привет, Хабр! Меня зовут Лиза, я разработчик направления 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:
Критична предсказуемость результата
Важна низкая стоимость инференса при большом количестве запросов
Есть время и ресурсы на сбор и разметку данных
Узкая специфичная задача с понятной структурой
Какой из подходов всё же выбрать? Тесты, тесты и ещё раз тесты. В нашем опыте много случаев, когда понимание предметной области и качественный вывод от тестов (подсвечивание проблемных кейсов, вывод метрик, сохранение информации о каждом эксперименте, описание целей) позволяло снизить стоимость инференса, убрав часть задач с видеокарт на более дешёвый в инференсе процессор, не потеряв в качестве решения и сделав его более предсказуемым.
На самом деле, что в итоге вы будете использовать в итоговом решении может меняться в процессе разработки, уточнения требований и т.д. Важным остается - понимание данных и зачем каждая из моделей вообще нужна, какую задачу она решает, какую функцию выполняет.
А как вы ищете баланс между качеством, скоростью и количеством моделей в своих проектах?