Patterns for Building LLM-based Systems & Products
Юджин Ян (Eugene Yan) разбирает семь ключевых паттернов для интеграции LLM в системы и продукты, расположенных по осям «улучшение качества vs. снижение стоимости/риска» и «ближе к данным vs. ближе к пользователю». Это: Evals (оценки) для измерения качества, RAG для добавления свежих внешних знаний, fine-tuning для специализации на задачах, кэширование для снижения задержки и стоимости, guardrails для контроля качества вывода, защитный UX (Defensive UX) для аккуратной обработки ошибок и сбор обратной связи пользователей для построения «маховика данных». Автор подробно разбирает метрики (BLEU, ROUGE, BERTScore, MoverScore) и их слабую корреляцию с человеческими оценками, чувствительность бенчмарков вроде MMLU к реализации, а также растущий тренд использования сильной LLM (например, GPT-4) как оценщика — G-Eval показал корреляцию по Спирмену 0,514 с людьми. По разделу RAG прослеживается история от DPR, RAG, FiD и RETRO до практических советов по гибридному поиску и эмбеддингам. По fine-tuning рассматриваются PEFT-техники (prompt/prefix tuning, adapters, LoRA, QLoRA), а также UX-гайдлайны Microsoft, Google и Apple.
Паттерны для построения систем и продуктов на основе LLM
[ llm engineering production 🔥 ] · 66 мин чтения
Обсуждения на HackerNews, Twitter и LinkedIn
«Есть большой класс задач, которые легко вообразить и для которых легко собрать демо, но из которых крайне трудно сделать продукт. Например, беспилотные автомобили: легко показать демо, где машина сама проезжает вокруг квартала, но превратить это в продукт занимает десятилетие». — Karpathy
Эта статья — о практических паттернах интеграции больших языковых моделей (LLM) в системы и продукты. Мы будем опираться на академические исследования, отраслевые ресурсы и практический опыт специалистов, дистиллируя их в ключевые идеи и практики.
Существует семь ключевых паттернов. Они также выстроены вдоль спектра «улучшение качества vs. снижение стоимости/риска» и «ближе к данным vs. ближе к пользователю».
Evals: чтобы измерять качество RAG: чтобы добавлять свежие внешние знания Fine-tuning: чтобы лучше справляться с конкретными задачами Кэширование: чтобы снижать задержку и стоимость Guardrails: чтобы обеспечивать качество вывода Защитный UX: чтобы предвосхищать и аккуратно обрабатывать ошибки Сбор обратной связи пользователей: чтобы строить маховик данных
(См. также это дополнение о том, как сопоставить эти паттерны LLM с потенциальными проблемами.)
Паттерны LLM: от данных к пользователю, от обороны к атаке (см. связи между паттернами)
Evals: чтобы измерять качество
Оценки (evals) — это набор измерений, используемых для оценки качества работы модели на задаче. Они включают эталонные данные и метрики. Из комментария на HackerNews:
То, насколько важны оценки для команды, — главное отличие тех, кто выпускает откровенный мусор второпях, от тех, кто всерьёз строит продукты в этой области.
Зачем нужны evals?
Оценки позволяют измерять, насколько хорошо работает наша система или продукт, и выявлять любые регрессии. (Система или продукт могут состоять из нескольких компонентов, таких как LLM, шаблоны промптов, извлечённый контекст и параметры вроде температуры.) Репрезентативный набор оценок приближает нас к измерению изменений в системе в масштабе. Без оценок мы бы летели вслепую или были бы вынуждены визуально проверять вывод LLM при каждом изменении.
Подробнее об оценках
В области языкового моделирования есть множество бенчмарков. Некоторые из заметных:
MMLU: набор из 57 задач, охватывающих элементарную математику, историю США, информатику, право и многое другое. Чтобы показывать хороший результат, модели должны обладать обширными знаниями о мире и способностью решать задачи. EleutherAI Eval: единый фреймворк для тестирования моделей в режимах zero/few-shot на 200 задачах. Включает большое число оценок, в том числе BigBench, MMLU и др. HELM: вместо конкретных задач и метрик HELM предлагает комплексную оценку LLM по множеству областей. Метрики включают точность, калибровку, устойчивость, справедливость, предвзятость, токсичность и др. Задачи включают вопрос-ответ, информационный поиск, суммаризацию, классификацию текста и др. AlpacaEval: автоматизированный фреймворк оценки, измеряющий, как часто сильная LLM (например, GPT-4) предпочитает вывод одной модели выводу референсной модели. Метрики включают win rate, предвзятость, задержку, цену, дисперсию и др. Подтверждено, что он хорошо согласуется с 20 тыс. человеческих аннотаций.
Метрики можно разделить на две категории: контекстно-зависимые и контекстно-независимые.
Контекстно-зависимые: учитывают контекст. Они часто предлагаются под конкретную задачу; переиспользование их для других задач потребует определённой адаптации. Контекстно-независимые: не привязаны к контексту при оценке сгенерированного вывода; они лишь сравнивают вывод с предоставленными эталонными ответами. Поскольку они не зависят от задачи, их проще применять к широкому спектру задач.
Чтобы лучше понять эти метрики (и их возможные недостатки), рассмотрим несколько часто используемых метрик, таких как BLEU, ROUGE, BERTScore и MoverScore.
BLEU (Bilingual Evaluation Understudy) — это метрика, основанная на точности (precision): она подсчитывает число n-грамм в сгенерированном выводе, которые также встречаются в эталоне, и делит его на общее число слов в выводе. Она преимущественно используется в машинном переводе и остаётся популярной благодаря своей экономичности.
Сначала вычисляется precision для различных значений \(n\):
\(Count_{clip}(\text{n-gram})\) обрезается по максимальному числу раз, которое n-грамма встречается в любом соответствующем эталонном предложении.
После того как мы вычислили precision при различных \(n\), итоговый показатель BLEU-N вычисляется как среднее геометрическое всех значений \(precision_n\).
Однако, поскольку precision опирается исключительно на n-граммы и не учитывает длину сгенерированного вывода, вывод, содержащий лишь одну униграмму распространённого слова (например, стоп-слова), получил бы идеальный precision. Это может вводить в заблуждение и поощрять вывод с меньшим числом слов ради повышения BLEU. Чтобы противодействовать этому, добавляется штраф за краткость (brevity penalty), наказывающий чрезмерно короткие предложения.
Таким образом, итоговая формула:
ROUGE (Recall-Oriented Understudy for Gisting Evaluation): в отличие от BLEU, ROUGE ориентирована на полноту (recall). Она подсчитывает число слов в эталоне, которые также встречаются в выводе. Обычно её используют для оценки задач автоматической суммаризации.
Существует несколько вариантов ROUGE. ROUGE-N наиболее похожа на BLEU, поскольку также подсчитывает число совпадающих n-грамм между выводом и эталоном.
Другие варианты включают:
ROUGE-L: измеряет наибольшую общую подпоследовательность (LCS) между выводом и эталоном. Она учитывает структурное сходство на уровне предложения и фокусируется на самой длинной серии совместно встречающихся последовательных n-грамм. ROUGE-S: измеряет skip-биграммы между выводом и эталоном. Skip-биграммы — это пары слов, сохраняющие свой порядок в предложении независимо от слов, которые могут оказаться между ними.
BERTScore — это метрика на основе эмбеддингов, которая использует косинусное сходство для сравнения каждого токена или n-граммы сгенерированного вывода с эталонным предложением. У BERTScore три компонента:
Recall: среднее косинусное сходство между каждым токеном эталона и наиболее близким к нему токеном в сгенерированном выводе. Precision: среднее косинусное сходство между каждым токеном сгенерированного вывода и ближайшим к нему токеном в эталоне. F1: гармоническое среднее recall и precision.
BERTScore полезна тем, что может учитывать синонимы и перефразирование. Более простые метрики вроде BLEU и ROUGE не способны на это из-за опоры на точные совпадения. Показано, что BERTScore лучше коррелирует с человеческими оценками в таких задачах, как описание изображений и машинный перевод.
MoverScore также использует контекстуализированные эмбеддинги для вычисления расстояния между токенами в сгенерированном выводе и эталоне. Но в отличие от BERTScore, основанной на сопоставлении токенов один-к-одному (или «жёстком выравнивании»), MoverScore допускает сопоставление многие-к-одному (или «мягкое выравнивание»).
BERTScore (слева) vs. MoverScore (справа; источник)
MoverScore позволяет сопоставлять семантически связанные слова в одной последовательности с их аналогами в другой. Она делает это, решая задачу оптимизации с ограничениями, которая находит минимальные усилия для преобразования одного текста в другой. Идея в том, чтобы измерить расстояние, на которое словам пришлось бы переместиться, чтобы преобразовать одну последовательность в другую.
Однако у использования этих традиционных бенчмарков и метрик есть несколько подводных камней.
Во-первых, наблюдается слабая корреляция между этими метриками и человеческими суждениями. BLEU, ROUGE и другие демонстрировали отрицательную корреляцию с тем, как люди оценивают беглость. Они также показывали умеренную или слабую корреляцию с человеческими оценками адекватности. В частности, BLEU и ROUGE имеют низкую корреляцию с задачами, требующими креативности и разнообразия.
Во-вторых, эти метрики часто плохо адаптируются к более широкому спектру задач. Применять метрику, предложенную для одной задачи, к другой не всегда разумно. Например, метрики точного совпадения вроде BLEU и ROUGE плохо подходят для задач вроде абстрактивной суммаризации или диалога. Поскольку они основаны на перекрытии n-грамм между выводом и эталоном, они не имеют смысла для диалоговой задачи, где возможен широкий спектр ответов. Вывод может иметь нулевое перекрытие n-грамм с эталоном и при этом быть хорошим ответом.
В-третьих, эти метрики обладают плохой воспроизводимостью. Даже для одной и той же метрики в разных исследованиях сообщается о высокой дисперсии, возможно, из-за различий в сборе человеческих суждений или в настройках параметров метрики. Другое исследование показателей ROUGE по 2000 работам обнаружило, что результаты трудно воспроизвести, сложно сравнивать и часто они некорректны, потому что оценки нередко проводились с непротестированными, неправильными реализациями ROUGE.
Измерения моделей с помощью ROUGE по различным параметрам (источник)
И даже с недавними бенчмарками вроде MMLU одна и та же модель может получить существенно разные результаты в зависимости от реализации оценки. Huggingface сравнил оригинальную реализацию MMLU с реализациями HELM и EleutherAI и обнаружил, что один и тот же пример может иметь разные промпты у разных провайдеров.
Разные промпты для одного и того же вопроса в разных реализациях MMLU (источник)
Более того, подход к оценке различался во всех трёх бенчмарках:
Оригинальный MMLU: сравнивает предсказанные вероятности только по вариантам ответа (A, B, C, D). HELM: использует вероятности следующего токена от модели и выбирает токен с наибольшей вероятностью, даже если он не является одним из вариантов. EleutherAI: вычисляет вероятность полной последовательности ответа (т. е. буквы, за которой следует текст ответа) для каждого варианта. Затем выбирает ответ с наибольшей вероятностью.
Разные способы оценки одного и того же вопроса в разных реализациях MMLU (источник)
В результате даже для одной и той же оценки как абсолютные результаты, так и ранжирование моделей могут сильно колебаться в зависимости от реализации. Это означает, что метрики моделей по-настоящему несопоставимы — даже для одной и той же оценки — если реализация оценки не идентична вплоть до мельчайших деталей вроде промптов и токенизации. Аналогично, автор QLoRA обнаружил, что MMLU чрезмерно чувствителен, и заключил: «не работайте с показателями MMLU, не сообщайте их и не доверяйте им».
Помимо традиционных оценок, упомянутых выше, зарождается тренд использовать сильную LLM как безэталонную метрику для оценки генераций других LLM. Это означает, что нам, возможно, не понадобятся человеческие суждения или эталонные ответы для оценки.
G-Eval — это фреймворк, применяющий LLM с цепочкой рассуждений (Chain-of-Thought, CoT) и парадигмой заполнения формы для оценки вывода LLM. Сначала авторы дают LLM введение в задачу и критерии оценки и просят сгенерировать CoT из шагов оценки. Затем, чтобы оценить связность в суммаризации новостей, они соединяют промпт, CoT, новостную статью и краткое содержание и просят LLM выдать оценку от 1 до 5. Наконец, они используют вероятности выходных токенов LLM, чтобы нормализовать оценку, и берут их взвешенную сумму как итоговый результат.
Обзор G-Eval (источник)
Они обнаружили, что GPT-4 в роли оценщика имел высокую корреляцию по Спирмену с человеческими суждениями (0,514), превзойдя все предыдущие методы. Он также превзошёл традиционные метрики по таким аспектам, как связность, согласованность, беглость и релевантность. На задаче topical chat он показал себя лучше традиционных метрик, таких как ROUGE-L, BLEU-4 и BERTScore, по нескольким критериям: естественность, связность, увлекательность и обоснованность.
В статье про Vicuna применили похожий подход. Авторы начинают с определения восьми категорий (письмо, ролевая игра, извлечение, рассуждение, математика, программирование, STEM и гуманитарные/социальные науки), а затем разрабатывают по 10 вопросов для каждой категории. Далее они сгенерировали ответы от пяти чат-ботов: LLaMA, Alpaca, ChatGPT, Bard и Vicuna. Наконец, они попросили GPT-4 оценить качество ответов по полезности, релевантности, точности и детальности.
В целом они обнаружили, что GPT-4 не только давал согласованные оценки, но и мог приводить подробные объяснения этих оценок. В рамках парадигмы оценки одиночного ответа GPT-4 показал большее согласие с людьми (85%), чем люди между собой (81%). Это говорит о том, что суждения GPT-4 тесно согласуются с человеческими оценщиками.
QLoRA также использовала одну LLM для оценки вывода другой LLM. Авторы попросили GPT-4 оценить качество различных моделей в сравнении с gpt-3.5-turbo на бенчмарке Vicuna. Получив ответы от gpt-3.5-turbo и другой модели, GPT-4 предлагалось выставить обоим оценку по 10-балльной шкале и объяснить свои оценки. Они также измеряли качество через прямые сравнения между моделями, упрощая задачу до трёхклассовой схемы оценки, включавшей ничьи.
Чтобы валидировать автоматизированную оценку, они собрали человеческие суждения на бенчмарке Vicuna. С помощью Mechanical Turk они привлекли двух аннотаторов для сравнений с gpt-3.5-turbo и трёх аннотаторов для попарных сравнений. Они обнаружили, что ранжирование моделей людьми и GPT-4 во многом совпадало, с ранговой корреляцией Спирмена 0,55 на уровне моделей. Это даёт ещё один аргумент в пользу того, что автоматизированные оценки на основе LLM могут быть экономичной и разумной альтернативой человеческим оценкам.
Как применять evals?
Построение надёжных оценок должно быть отправной точкой для любой системы или продукта на основе LLM (как и для традиционных систем машинного обучения).
К сожалению, классические метрики вроде BLEU и ROUGE не имеют смысла для более сложных задач, таких как абстрактивная суммаризация или диалог. Кроме того, мы убедились, что бенчмарки вроде MMLU (и метрики вроде ROUGE) чувствительны к тому, как они реализованы и измерены. И, откровенно говоря, если ваша система на LLM не готовится к школьному экзамену, использовать MMLU в качестве оценки не вполне разумно.
Поэтому вместо готовых бенчмарков мы можем начать со сбора набора оценок под конкретную задачу (т. е. промпт, контекст, ожидаемый вывод в качестве эталонов). Затем эти оценки будут направлять prompt engineering, выбор модели, fine-tuning и так далее. И по мере обновления систем мы можем прогонять эти оценки, чтобы быстро измерять улучшения или регрессии. Считайте это разработкой через оценки (Eval Driven Development, EDD).
Помимо набора данных для оценки, нам также нужны полезные метрики. Они помогают свести изменения качества к одному числу, сопоставимому между прогонами оценок. И если мы можем упростить задачу, мы можем выбрать метрики, которые проще вычислять и интерпретировать.
Простейшая задача — это, вероятно, классификация: если мы используем LLM для задач классификационного типа (например, обнаружение токсичности, категоризация документов) или экстрактивного вопрос-ответа без диалога, мы можем опираться на стандартные классификационные метрики, такие как recall, precision, PRAUC и др. Если у задачи нет единственно верного ответа, но есть эталоны (например, машинный перевод, экстрактивная суммаризация), мы можем опираться на эталонные метрики, основанные на совпадении (BLEU, ROUGE) или семантическом сходстве (BERTScore, MoverScore).
Однако эти метрики могут не работать для более открытых задач, таких как абстрактивная суммаризация, диалог и других. Но сбор человеческих суждений может быть медленным и дорогим. Поэтому мы можем сделать выбор в пользу автоматизированных оценок с помощью сильной LLM.
По сравнению с человеческими суждениями, которые обычно зашумлены (из-за различающихся предвзятостей аннотаторов), суждения LLM, как правило, менее зашумлены (поскольку их предвзятость более систематична), но более предвзяты. Тем не менее, поскольку мы осознаём эти предвзятости, мы можем соответствующим образом их смягчать:
Предвзятость к позиции: LLM склонны отдавать предпочтение ответу на первой позиции. Чтобы смягчить это, мы можем оценивать одну и ту же пару ответов дважды, меняя их порядок местами. Если один и тот же ответ предпочтён в обоих порядках, мы засчитываем это как победу; иначе — ничья. Предвзятость к многословию: LLM склонны предпочитать более длинные, многословные ответы более лаконичным, даже если последние яснее и качественнее. Возможное решение — следить, чтобы сравниваемые ответы были схожи по длине. Предвзятость к самоусилению: у LLM есть лёгкий уклон в пользу собственных ответов. GPT-4 предпочитает себя с win rate на 10% выше, тогда как Claude-v1 предпочитает себя с win rate на 25% выше. Чтобы противодействовать этому, не используйте одну и ту же LLM для задач оценки.
Ещё один совет: вместо того чтобы просить LLM о прямой оценке (через выставление балла), попробуйте дать ей эталон и попросить о сравнении. Это помогает снизить шум.
Наконец, иногда лучшая оценка — это человеческая оценка, она же vibe check (проверка по ощущениям). (Не путать с неудачно названным бенчмарком оценки кода HumanEval.) Как было сказано в подкасте Latent Space с MosaicML (34-я минута):
Оценку по ощущениям нельзя недооценивать. … Одной из наших оценок было просто иметь набор промптов и наблюдать за ответами по мере обучения моделей, чтобы видеть, меняются ли они. Честно говоря, я не очень верю, что какие-либо из этих метрик оценки улавливают то, что нам важно. Один из наших промптов был «предложи игры, в которые могут поиграть трёхлетний и семилетний ребёнок», и было гораздо ценнее наблюдать, как менялся ответ в ходе обучения. — Jonathan Frankle
См. также этот глубокий разбор оценок для абстрактивной суммаризации. Он охватывает метрики на основе эталона, контекста и предпочтений, а также обсуждает обнаружение галлюцинаций.
Retrieval-Augmented Generation: чтобы добавлять знания
Retrieval-Augmented Generation (RAG) извлекает релевантные данные извне базовой модели и дополняет ими вход, предоставляя более богатый контекст для улучшения вывода.
Зачем нужен RAG?
RAG помогает снизить галлюцинации, заземляя модель на извлечённый контекст и тем самым повышая фактологичность. Кроме того, поддерживать актуальность индексов поиска дешевле, чем непрерывно дообучать LLM. Эта экономичность упрощает предоставление LLM доступа к свежим данным через RAG. Наконец, если нам нужно обновить или удалить данные, например предвзятые или токсичные документы, обновить индекс поиска проще (по сравнению с fine-tuning или промптингом LLM, чтобы она не генерировала токсичный вывод).
Короче говоря, RAG применяет зрелые и более простые идеи из области информационного поиска для поддержки генерации LLM. В недавнем опросе Sequoia 88% респондентов считают, что поиск (retrieval) станет ключевым компонентом их стека.
Подробнее о RAG
Прежде чем погружаться в RAG, полезно иметь базовое понимание текстовых эмбеддингов. (Смело пропускайте этот раздел, если вы знакомы с темой.)
Текстовый эмбеддинг — это сжатое, абстрактное представление текстовых данных, где текст произвольной длины может быть представлен как вектор чисел фиксированного размера. Обычно он выучивается из корпуса текста, например Википедии. Считайте их универсальной кодировкой текста, где похожие объекты расположены близко друг к другу, а непохожие — дальше друг от друга.
Хороший эмбеддинг — это тот, который хорошо справляется с целевой задачей, например с извлечением похожих объектов. Massive Text Embedding Benchmark (MTEB) от Huggingface оценивает различные модели на разнообразных задачах, таких как классификация, кластеризация, поиск, суммаризация и др.
Небольшая ремарка: хотя здесь мы в основном обсуждаем текстовые эмбеддинги, эмбеддинги могут охватывать множество модальностей. Например, CLIP мультимодален и встраивает изображения и текст в одно пространство, позволяя находить изображения, наиболее похожие на входной текст. Мы также можем встраивать товары на основе поведения пользователей (например, кликов, покупок) или графовых связей.
RAG берёт начало в открытом вопрос-ответе (open-domain Q&A). Ранняя статья Meta показала, что извлечение релевантных документов через TF-IDF и предоставление их в качестве контекста языковой модели (BERT) улучшало качество на задаче открытого вопрос-ответа. Они преобразовывали каждую задачу в cloze-утверждение и запрашивали у языковой модели пропущенный токен.
Вслед за этим Dense Passage Retrieval (DPR) показал, что использование плотных эмбеддингов (вместо разреженного векторного пространства вроде TF-IDF) для извлечения документов может превзойти сильные базлайны, такие как Lucene BM25 (65,2% против 42,9% для top-5 accuracy). Они также показали, что более высокая точность извлечения транслируется в более высокую точность вопрос-ответа от начала до конца, что подчёркивает важность качественного извлечения на входе.
Чтобы выучить эмбеддинг DPR, они дообучили два независимых энкодера на основе BERT на существующих парах вопрос-ответ. Энкодер пассажей (\(E_p\)) встраивает текстовые фрагменты в векторы, а энкодер запросов (\(E_q\)) встраивает вопросы в векторы. Затем эмбеддинг запроса используется для извлечения \(k\) пассажей, наиболее похожих на вопрос.
Они обучали энкодеры так, чтобы скалярное произведение служило хорошей функцией ранжирования, и оптимизировали функцию потерь как отрицательное логарифмическое правдоподобие правильного пассажа. Эмбеддинги DPR оптимизированы под максимальное внутреннее произведение между векторами вопроса и релевантного пассажа. Цель — выучить такое векторное пространство, где пары вопросов и их релевантных пассажей расположены близко друг к другу.
Для инференса они встраивают все пассажи (через \(E_p\)) и индексируют их в FAISS офлайн. Затем, получив вопрос во время запроса, они вычисляют эмбеддинг вопроса (через \(E_q\)), извлекают топ-\(k\) пассажей через приближённый поиск ближайших соседей и передают их языковой модели (BERT), которая выдаёт ответ на вопрос.
Retrieval Augmented Generation (RAG), от которой этот паттерн и получил название, обозначила недостатки предобученных LLM. К ним относятся невозможность расширять или пересматривать память, отсутствие объяснений сгенерированного вывода и галлюцинации.
Чтобы устранить эти недостатки, авторы представили RAG (он же полупараметрические модели). Извлечение по плотным векторам выступает непараметрическим компонентом, а предобученная LLM — параметрическим компонентом. Они переиспользовали энкодеры DPR для инициализации ретривера и построения индекса документов. В качестве LLM они использовали BART, модель seq2seq с 400 млн параметров.
Обзор Retrieval Augmented Generation (источник)
Во время инференса они соединяют вход с извлечённым документом. Затем LLM генерирует \(\text{token}_i\) на основе исходного входа, извлечённого документа и предыдущих \(i-1\) токенов. Для генерации они предложили два подхода, различающихся тем, как извлечённые пассажи используются для порождения вывода.
В первом подходе, RAG-Sequence, модель использует один и тот же документ для генерации всей последовательности. Таким образом, для \(k\) извлечённых документов генератор порождает по выводу для каждого документа. Затем вероятность каждой выходной последовательности маргинализируется (суммируется вероятность каждой выходной последовательности по \(k\) и взвешивается по вероятности извлечения каждого документа). Наконец, выбирается выходная последовательность с наибольшей вероятностью.
С другой стороны, RAG-Token может генерировать каждый токен на основе разного документа. Получив \(k\) извлечённых документов, генератор порождает распределение для следующего выходного токена для каждого документа, прежде чем маргинализировать (агрегировать все отдельные распределения токенов). Затем процесс повторяется для следующего токена. Это означает, что при генерации каждого токена он может извлекать разный набор из \(k\) релевантных документов на основе исходного входа и ранее сгенерированных токенов. Таким образом, документы могут иметь разные вероятности извлечения и вносить разный вклад в следующий сгенерированный токен.
Fusion-in-Decoder (FiD) также использует извлечение вместе с генеративными моделями для открытого вопрос-ответа. Он поддерживает два метода извлечения — BM25 (Lucene с параметрами по умолчанию) и DPR. FiD назван так из-за того, как он выполняет слияние (fusion) извлечённых документов только в декодере.
Обзор Fusion-in-Decoder (источник)
Для каждого извлечённого пассажа заголовок и пассаж соединяются с вопросом. Эти пары обрабатываются в энкодере независимо. Они также добавляют специальные токены, такие как question:, title: и context:, перед соответствующими секциями. Декодер применяет внимание к конкатенации этих извлечённых пассажей.
Поскольку он обрабатывает пассажи в энкодере независимо, он может масштабироваться на большое число пассажей, так как ему нужно выполнять self-attention лишь по одному контексту за раз. Таким образом, вычисления растут линейно (а не квадратично) с числом извлечённых пассажей, что делает его более масштабируемым, чем альтернативы вроде RAG-Token. Затем, во время декодирования, декодер обрабатывает закодированные пассажи совместно, что позволяет ему лучше агрегировать контекст по нескольким извлечённым пассажам.
Retrieval-Enhanced Transformer (RETRO) применяет схожий паттерн, объединяя замороженный ретривер BERT, дифференцируемый энкодер и поблочное перекрёстное внимание (chunked cross-attention) для порождения вывода. Отличие в том, что RETRO выполняет извлечение на всём этапе предобучения, а не только во время инференса. Кроме того, релевантные документы извлекаются на основе блоков входа. Это позволяет вести более тонкозернистое, повторяющееся извлечение во время генерации, а не извлекать лишь один раз на запрос.
Для каждого входного блока (\(C_u\)) \(k\) извлечённых блоков \(RET(C_u)\) подаются в энкодер. Выходом являются закодированные соседи \(E^{j}_{u}\), где \(E^{j}_{u} = \text{Encoder}(\text{RET}(C_{u})^{j}, H_{u}) \in \mathbb{R}^{r \times d_{0}}\). Здесь кодирование каждого блока обусловлено \(H_u\) (промежуточными активациями) и активациями блока \(C_u\) через слои перекрёстного внимания. Короче говоря, кодирование извлечённых блоков зависит от активаций входного блока, к которым применено внимание. Затем \(E^{j}_{u}\) используется для обуславливания генерации следующего блока.
Обзор RETRO (источник)
Во время извлечения RETRO разбивает входную последовательность на блоки по 64 токена. Затем он находит текст, похожий на предыдущий блок, чтобы предоставить контекст для текущего блока. Индекс извлечения состоит из двух смежных блоков токенов, \(N\) и \(F\). Первый — это блок-сосед (64 токена), который используется для вычисления ключа, а второй — блок-продолжение (64 токена) в исходном документе.
Извлечение основано на приближённом поиске \(k\) ближайших соседей по расстоянию \(L_2\) (евклидову) на эмбеддингах BERT. (Любопытное отступление от обычного косинусного сходства или скалярного произведения.) Индекс извлечения, построенный на SCaNN, может выполнять запрос к базе из 2 трлн токенов за 10 мс.
Они также продемонстрировали, как RETRO-фитить существующие базовые модели. Заморозив предобученные веса и обучая только параметры поблочного перекрёстного внимания и энкодера соседей (< 10% весов для модели на 7B), они могут дополнить трансформеры извлечением, требуя при этом всего 6 млн обучающих последовательностей (3% от последовательностей предобучения). RETRO-фитнутые модели смогли превзойти качество базовых моделей и достичь качества, близкого к RETRO, обученной с нуля.
Качество от RETRO-фитинга предобученной модели (источник)
LLM, дополненные интернетом предлагают использовать скромную «готовую» поисковую систему для дополнения LLM. Сначала они извлекают набор релевантных документов через Google Search. Поскольку эти извлечённые документы обычно длинные (средняя длина 2056 слов), их разбивают на абзацы по шесть предложений каждый. Наконец, они встраивают вопрос и абзацы через TF-IDF и применяют косинусное сходство для ранжирования наиболее релевантных абзацев под каждый запрос.
Обзор LLM, дополненных интернетом (источник)
Извлечённые абзацы используются для обуславливания LLM через few-shot-промптинг. Они применяют традиционный \(k\)-shot-промптинг (\(k=15\)) из вопрос-ответа с закрытой книгой (где предоставляются только пары вопрос-ответ) и расширяют его абзацем-свидетельством, так что каждый контекст представляет собой тройку «свидетельство, вопрос, ответ».
В качестве генератора они использовали Gopher — модель на 280B параметров, обученную на 300B токенов. Для каждого вопроса они генерировали по четыре кандидата-ответа на основе каждого из 50 извлечённых абзацев. Наконец, они выбирают лучший ответ, оценивая вероятность ответа несколькими методами, включая прямой инференс, RAG, инференс по зашумлённому каналу (noisy channel) и Product-of-Experts (PoE). PoE стабильно показывал наилучший результат.
RAG также применялся к не-QA-задачам, таким как генерация кода. Хотя CodeT5+ можно использовать как самостоятельный генератор, в сочетании с RAG он значительно превосходит аналогичные модели в генерации кода.
Чтобы оценить влияние RAG на генерацию кода, они тестируют модель в трёх режимах:
На основе извлечения: берётся top-1 фрагмент кода в качестве предсказания. Только генерация: код выдаётся только декодером. С дополнением извлечением: top-1 фрагмент кода добавляется ко входу энкодера перед генерацией кода декодером.
Обзор RAG для CodeT5+ (источник)
В качестве качественного примера они показали, что извлечённый код предоставляет важный контекст (например, использовать urllib3 для HTTP-запроса) и направляет процесс генерации к более правильным предсказаниям. В противоположность этому подход только с генерацией возвращает некорректный вывод, который улавливает лишь понятия «скачать» и «сжать».
А что, если у нас нет суждений о релевантности для пар запрос-пассаж? Без них мы не смогли бы обучить би-энкодеры, которые встраивают запросы и документы в одно эмбеддинг-пространство, где релевантность представлена внутренним произведением. Гипотетические эмбеддинги документов (HyDE) предлагают решение.
Обзор HyDE (источник)
Получив запрос, HyDE сначала промптит LLM, например InstructGPT, сгенерировать гипотетический документ. Затем неконтролируемый (unsupervised) энкодер, например Contriver, кодирует документ в эмбеддинг-вектор. Наконец, вычисляется внутреннее произведение между гипотетическим документом и корпусом, и извлекаются наиболее похожие реальные документы.
Ожидание состоит в том, что плотное «бутылочное горлышко» энкодера работает как сжатие с потерями, и лишние, нефактологические детали исключаются через эмбеддинг. Это переформулирует задачу моделирования релевантности из задачи обучения представлениям в задачу генерации.
Как применять RAG
Из опыта работы с Obsidian-Copilot я обнаружил, что гибридный поиск (традиционный поисковый индекс + поиск на основе эмбеддингов) работает лучше, чем каждый из них по отдельности. Там я дополнил классический поиск (BM25 через OpenSearch) семантическим поиском (e5-small-v2).
Почему не только поиск на основе эмбеддингов? Хотя во многих случаях он отлично работает, есть ситуации, где он не справляется, например:
Поиск имени человека или названия объекта (например, Eugene, Kaptir 2.0) Поиск аббревиатуры или фразы (например, RAG, RLHF) Поиск идентификатора (например, gpt-3.5-turbo, titan-xlarge-v1.01)
Но и у поиска по ключевым словам есть свои ограничения. Он моделирует лишь простые частоты слов и не улавливает семантическую или корреляционную информацию. Поэтому он плохо справляется с синонимами или гиперонимами (т. е. словами, представляющими обобщение). Именно здесь сочетание с семантическим поиском оказывается взаимодополняющим.
Кроме того, с традиционным поисковым индексом мы можем использовать метаданные для уточнения результатов. Например, мы можем использовать фильтры по дате, чтобы отдавать приоритет более новым документам, или сузить поиск до определённого периода. А если поиск связан с e-commerce, полезны фильтры по среднему рейтингу или категориям. Наконец, наличие метаданных удобно для последующего ранжирования, например для приоритизации чаще цитируемых документов или поднятия товаров по объёму продаж.
Что касается эмбеддингов, кажущийся популярным подход — использовать text-embedding-ada-002. Его преимущества — простота использования через API и отсутствие необходимости поддерживать собственную инфраструктуру эмбеддингов или самостоятельно хостить модели эмбеддингов. Тем не менее личный опыт и свидетельства других указывают, что для извлечения есть варианты получше.
Классические подходы к эмбеддингам включают Word2vec и fastText. FastText — это open-source, лёгкая библиотека, позволяющая пользователям задействовать предобученные эмбеддинги или обучать новые модели эмбеддингов. Она поставляется с предобученными эмбеддингами для 157 языков и крайне быстра, даже без GPU. Это мой выбор для ранних proof of concept.
Ещё один хороший базлайн — sentence-transformers. Он упрощает вычисление эмбеддингов для предложений, абзацев и даже изображений. Он основан на проверенных трансформерах вроде BERT и RoBERTa и доступен более чем на 100 языках.
Совсем недавно instructor-модели показали SOTA-качество. Во время обучения эти модели предваряют текст описанием задачи. Затем, встраивая новый текст, нам достаточно описать задачу, чтобы получить эмбеддинги под конкретную задачу. (По моему мнению, не так уж отличается от instruction tuning для моделей эмбеддингов.)
Пример — семейство моделей E5. Для открытого вопрос-ответа и информационного поиска мы просто предваряем документы в индексе префиксом passage:, а запросы — префиксом query:. Если задача симметрична (например, семантическое сходство, поиск перефразирований) или если мы хотим использовать эмбеддинги как признаки (например, для классификации, кластеризации), мы используем просто префикс query:.
Модель Instructor идёт ещё дальше, позволяя пользователям настраивать предваряющий промпт: «Represent the domain task_type for the task_objective:» Например, «Represent the Wikipedia document for retrieval:». (Домен и цель задачи опциональны.) Это привносит концепцию prompt tuning в область текстовых эмбеддингов.
Наконец, по состоянию на 1 августа топовой моделью эмбеддингов в MTEB Leaderboard является семейство моделей GTE от Alibaba DAMO Academy. Размер лучшей по качеству модели вдвое меньше, чем у следующей за ней e5-large-v2 (0,67 ГБ против 1,34 ГБ). На втором месте — gte-base с размером модели всего 0,22 ГБ и размерностью эмбеддинга 768. (Спасибо Nirant.)
Чтобы извлекать документы с низкой задержкой в масштабе, мы используем приближённый поиск ближайших соседей (ANN). Он оптимизирует скорость извлечения и возвращает приближённые (а не точные) топ-\(k\) наиболее похожих соседей, разменивая небольшую потерю точности на значительный прирост скорости.
ANN-индексы эмбеддингов — это структуры данных, позволяющие эффективно выполнять ANN-поиск. На высоком уровне они строят разбиения эмбеддинг-пространства, чтобы мы могли быстро фокусироваться на конкретной области, где находится вектор запроса. К популярным техникам относятся:
Locality Sensitive Hashing (LSH): основная идея — создавать хеш-функции так, чтобы похожие объекты с большой вероятностью попадали в одну и ту же хеш-корзину. Проверяя только релевантные корзины, мы можем эффективно выполнять ANN-запросы. Facebook AI Similarity Search (FAISS): использует сочетание квантования и индексации для эффективного извлечения, поддерживает как CPU, так и GPU и может работать с миллиардами векторов благодаря эффективному использованию памяти. Hierarchical Navigable Small Worlds (HNSW): вдохновлённый «шестью рукопожатиями», он строит иерархическую графовую структуру, воплощающую феномен тесного мира. Здесь большинство узлов достижимы из любого другого узла за минимальное число переходов. Эта структура позволяет HNSW начинать запросы с более широких, грубых приближений и постепенно сужать поиск на нижних уровнях. Scalable Nearest Neighbors (ScaNN): работает в два этапа. Сначала грубое квантование сокращает пространство поиска. Затем выполняется тонкозернистый поиск внутри сокращённого набора. Лучший компромисс recall/latency, который я видел.
При оценке ANN-индекса стоит учесть некоторые факторы:
Recall: насколько он хорош по сравнению с точным поиском ближайших соседей? Задержка/пропускная способность: сколько запросов в секунду он может обработать? Объём памяти: сколько RAM требуется для обслуживания индекса? Простота добавления новых элементов: можно ли добавлять новые элементы без переиндексации всех документов (LSH) или индекс нужно перестраивать (ScaNN)?
Ни один фреймворк не превосходит все остальные во всех аспектах. Поэтому начните с определения своих функциональных и нефункциональных требований, прежде чем проводить бенчмаркинг. Лично я нашёл, что ScaNN выдаётся по компромиссу recall-latency (см. график бенчмарка здесь).
Fine-tuning: чтобы лучше справляться с конкретными задачами
Fine-tuning — это процесс взятия предобученной модели (которая уже обучена на огромном объёме данных) и её дальнейшей доработки под конкретную задачу. Цель — задействовать знания, которые модель уже приобрела во время предобучения, и применить их к конкретной задаче, обычно с использованием меньшего, специфичного для задачи набора данных.
Термин «fine-tuning» используется довольно вольно и может относиться к нескольким понятиям, таким как:
Продолженное предобучение (continued pre-training): на данных конкретной области применяется тот же режим предобучения (предсказание следующего токена, masked language modeling) к базовой модели. Instruction fine-tuning: предобученная (базовая) модель дообучается на примерах пар «инструкция-вывод», чтобы следовать инструкциям, отвечать на вопросы, быть вайфу и т. д. Single-task fine-tuning: предобученная модель оттачивается под узкую и конкретную задачу, такую как обнаружение токсичности или суммаризация, подобно BERT и T5. Обучение с подкреплением на основе обратной связи человека (RLHF): сочетает instruction fine-tuning с обучением с подкреплением. Оно требует сбора человеческих предпочтений (например, попарных сравнений), которые затем используются для обучения модели вознаграждения. Модель вознаграждения затем используется для дальнейшего дообучения инструктированной LLM с помощью RL-техник, таких как proximal policy optimization (PPO).
Здесь мы сосредоточимся в основном на single-task fine-tuning и instruction fine-tuning.
Зачем нужен fine-tuning?
Fine-tuning открытой LLM становится всё более жизнеспособной альтернативой использованию сторонней облачной LLM по нескольким причинам.
Качество и контроль: fine-tuning может улучшить качество готовой базовой модели и даже превзойти стороннюю LLM. Он также обеспечивает больший контроль над поведением LLM, что приводит к более надёжной системе или продукту. В целом fine-tuning позволяет строить продукты, отличающиеся от простого использования сторонних или открытых LLM.
Модуляризация: single-task fine-tuning позволяет нам использовать целую армию меньших моделей, каждая из которых специализируется на своей задаче. При такой схеме систему можно модуляризировать на отдельные модели для задач вроде модерации контента, извлечения, суммаризации и т. д. Кроме того, поскольку каждой модели нужно сосредоточиться лишь на узком наборе задач, мы можем обойти «налог на выравнивание» (alignment tax), при котором дообучение модели на одной задаче снижает качество на других задачах.
Снижение зависимостей: дообучая и хостя собственные модели, мы можем снизить юридические опасения по поводу попадания проприетарных данных (например, PII, внутренних документов и кода) во внешние API. Это также обходит ограничения, сопутствующие сторонним LLM, такие как rate-limiting, высокая стоимость или чрезмерно строгие фильтры безопасности. Дообучая и хостя собственные LLM, мы можем гарантировать, что данные не покидают нашу сеть, и масштабировать пропускную способность по мере необходимости.
Подробнее о fine-tuning
Зачем вообще дообучать базовую модель? Рискуя чрезмерно упростить: базовые модели в основном оптимизированы под предсказание следующего слова на корпусе, на котором они обучены. Поэтому они от природы не очень хорошо следуют инструкциям или отвечают на вопросы. Когда им задают вопрос, они склонны отвечать новыми вопросами. Поэтому мы выполняем instruction fine-tuning, чтобы они научились реагировать должным образом.
Однако fine-tuning не лишён сложностей. Во-первых, нам нужен значительный объём демонстрационных данных. Например, в статье про InstructGPT использовали 13 тыс. образцов «инструкция-вывод» для supervised fine-tuning, 33 тыс. сравнений выводов для моделирования вознаграждения и 31 тыс. промптов без человеческих меток в качестве входа для RLHF.
Кроме того, fine-tuning сопровождается налогом на выравнивание — этот процесс может приводить к снижению качества на некоторых критически важных задачах. (Бесплатного сыра, в конце концов, не бывает.) Та же статья про InstructGPT обнаружила, что RLHF приводил к регрессиям качества (относительно базовой модели GPT-3) на публичных NLP-задачах вроде SQuAD, HellaSwag и WMT 2015 (перевод с французского на английский). (Обходной путь — иметь несколько меньших, специализированных моделей, отлично справляющихся с узкими задачами.)
Fine-tuning схож с понятием transfer learning. Как определено в Википедии: «Transfer learning — это техника в машинном обучении, при которой знания, полученные на одной задаче, переиспользуются для повышения качества на связанной задаче». Несколько лет назад transfer learning позволил мне легко применить модели ResNet, обученные на ImageNet, чтобы классифицировать модные товары и построить поиск по изображениям.
ULMFit — одна из ранних статей, применивших transfer learning к тексту. Они утвердили протокол самоконтролируемого предобучения (на неразмеченных данных) с последующим fine-tuning (на размеченных данных). Они использовали AWS-LSTM — вариант LSTM с дропаутом на различных гейтах.
Обзор ULMFit (источник)
Во время предобучения (предсказание следующего слова) модель обучается на wikitext-103, который содержит 28,6 статей Википедии и 103 млн слов. Затем, во время fine-tuning под целевую задачу, языковая модель дообучается на данных из области конкретной задачи. Наконец, во время fine-tuning классификатора модель дополняется двумя дополнительными линейными блоками и дообучается под целевые задачи классификации, включающие анализ тональности, классификацию вопросов и классификацию тем.
С тех пор парадигма «предобучение, затем fine-tuning» обеспечила значительный прогресс в языковом моделировании. Bidirectional Encoder Representations from Transformers (BERT; только энкодер) был предобучен на masked language modeling и предсказании следующего предложения на английской Википедии и BooksCorpus. Затем он был дообучен на специфичных для задачи входах и метках для классификации одиночных предложений, классификации пар предложений, разметки одиночных предложений и вопрос-ответа.
Обзор BERT (источник)
Generative Pre-trained Transformers (GPT; только декодер) был сначала предобучен на BooksCorpus через предсказание следующего токена. За этим следовал single-task fine-tuning для задач вроде классификации текста, текстового следования (entailment), сходства и вопрос-ответа. Интересно, что они обнаружили: включение языкового моделирования как вспомогательной цели помогало модели лучше обобщать и быстрее сходиться во время обучения.
Обзор GPT (источник)
Text-to-text Transfer Transformer (T5; энкодер-декодер) был предобучен на Colossal Clean Crawled Corpus (C4) — очищенной версии Common Crawl от апреля 2019 года. Он использовал ту же denoising-цель, что и BERT, а именно masked language modeling. Затем он был дообучен на задачах вроде классификации текста, абстрактивной суммаризации, вопрос-ответа и машинного перевода.
Обзор T5 (источник)
Но в отличие от ULMFiT, BERT и GPT, которые использовали разные классификационные «головы» для целевых задач, T5 представлял целевые задачи исключительно в формате text-to-text. Например, задача перевода имела бы входной текст, начинающийся с Translation English to German:, тогда как задача суммаризации могла бы начинаться с Summarize: или TL;DR:. Префикс по сути стал гиперпараметром (первый случай prompt engineering?). Это решение позволило им использовать одну дообученную модель для целого ряда целевых задач.
InstructGPT расширил эту идею single-task fine-tuning до instruction fine-tuning. Базовой моделью был GPT-3, предобученный на интернет-данных, включая Common Crawl, WebText, книги и Википедию. Затем был применён supervised fine-tuning на демонстрациях желаемого поведения (инструкция и вывод). Далее обучалась модель вознаграждения на наборе сравнений. Наконец, инструктированная модель оптимизировалась относительно модели вознаграждения через PPO, причём этот последний этап был сосредоточен скорее на выравнивании, чем на качестве конкретной задачи.
Обзор этапов fine-tuning в InstructGPT (источник)
Далее перейдём от дообученных моделей к техникам fine-tuning.
Soft prompt tuning предваряет входные эмбеддинги модели обучаемым тензором, по сути создавая «мягкий промпт». В отличие от дискретных текстовых промптов, мягкие промпты можно обучать через обратное распространение, то есть их можно дообучать, вбирая сигналы из любого числа размеченных примеров.
Далее есть prefix tuning. Вместо добавления мягкого промпта ко входу модели он предваряет обучаемыми параметрами скрытые состояния всех блоков трансформера. Во время fine-tuning исходные параметры языковой модели остаются замороженными, а параметры префикса обновляются.
Обзор prefix-tuning (источник)
Статья показала, что это достигает качества, сопоставимого с полным fine-tuning, несмотря на необходимость обновлять лишь 0,1% параметров. Более того, в условиях ограниченных данных и при экстраполяции на новые темы он превосходил полный fine-tuning. Одна из гипотез в том, что обучение меньшего числа параметров помогало снизить переобучение на меньших целевых наборах данных.
Есть также техника adapter. Этот метод дважды добавляет слои полносвязной сети к каждому блоку трансформера — после слоя внимания и после слоя feed-forward сети. На GLUE он способен достичь качества в пределах 0,4% от полного fine-tuning, добавляя всего 3,6% параметров на задачу.
Обзор адаптеров (источник)
Low-Rank Adaptation (LoRA) — это техника, в которой адаптеры спроектированы как произведение двух матриц низкого ранга. Она была вдохновлена работой Aghajanyan et al., которая показала, что при адаптации к конкретной задаче предобученные языковые модели имеют низкую внутреннюю размерность и всё ещё могут эффективно учиться, несмотря на случайную проекцию в меньшее подпространство. Поэтому LoRA выдвинула гипотезу, что обновления весов во время адаптации также имеют низкий внутренний ранг.
Обзор LoRA (источник)
Подобно prefix tuning, авторы обнаружили, что LoRA превосходила несколько базлайнов, включая полный fine-tuning. И снова гипотеза в том, что LoRA, благодаря своему сниженному рангу, обеспечивает неявную регуляризацию. В противоположность этому полный fine-tuning, обновляющий все веса, может быть склонен к переобучению.
QLoRA развивает идею LoRA. Но вместо использования полной 16-битной модели во время fine-tuning он применяет 4-битную квантованную модель. Он представил несколько нововведений, таких как 4-bit NormalFloat (для квантования моделей), двойное квантование (для дополнительной экономии памяти) и paged optimizers (которые предотвращают ошибки OOM, перенося данные в CPU RAM, когда у GPU заканчивается память).
Обзор QLoRA (источник)
В результате QLoRA снижает средние требования к памяти для fine-tuning модели на 65B с > 780 ГБ памяти до более управляемых 48 ГБ, не ухудшая время выполнения или предсказательное качество по сравнению с 16-битным полностью дообученным базлайном.
(Забавный факт: на встрече с Tim Dettmers, одним из авторов QLoRA, он пошутил, что двойное квантование было «немного глупой идеей, но работает идеально». Что ж, если работает — значит работает.)
Как применять fine-tuning?
Первый шаг — собрать демонстрационные данные/метки. Они могут быть для простых задач вроде классификации документов, извлечения сущностей или суммаризации, либо более сложных, таких как вопрос-ответ или диалог. Способы сбора этих данных включают:
Через экспертов или краудсорсинговых разметчиков: хотя это дорого и медленно, обычно это приводит к более качественным данным при наличии хороших инструкций. Через обратную связь пользователей: это может быть так же просто, как просить пользователей выбрать атрибуты, описывающие товар, оценивать ответы LLM лайком или дизлайком (например, ChatGPT) или логировать, какие изображения пользователи выбирают для скачивания (например, Midjourney). Запрос к более крупным открытым моделям с разрешительными лицензиями: с помощью prompt engineering мы можем суметь извлечь разумные демонстрационные данные из более крупной модели (Falcon 40B Instruct), которые можно использовать для дообучения меньшей модели. Переиспользование open-source данных: если вашу задачу можно сформулировать как задачу natural language inference (NLI), мы могли бы дообучить модель выполнять NLI с использованием данных MNLI. Затем мы можем продолжить дообучение модели на внутренних данных, чтобы классифицировать входы как следование (entailment), нейтральность или противоречие.
Примечание: некоторые условия использования LLM запрещают пользователям применять их вывод для разработки других моделей.
Условия использования OpenAI (Раздел 2c, iii): Вы не можете использовать вывод Сервисов для разработки моделей, конкурирующих с OpenAI. Лицензионное соглашение сообщества LLaMA 2 (Раздел 1b-v): Вы не будете использовать материалы Llama или любой вывод или результаты материалов Llama для улучшения любой другой большой языковой модели (за исключением Llama 2 или производных от неё работ).
Следующий шаг — определить метрики оценки. Мы обсуждали это в предыдущем разделе.
Затем выберите предобученную модель. Есть несколько открытых LLM с разрешительными лицензиями на выбор. Если исключить Llama 2 (поскольку она не полностью свободна для коммерческого использования), Falcon-40B известна как лучшая по качеству модель. Тем не менее я нашёл её неудобной для дообучения и обслуживания в продакшене, учитывая, насколько она тяжёлая.
Вместо этого я склонен использовать меньшие модели вроде Falcon-7B. А если мы можем упростить и сформулировать задачу более узко, то BERT (340M параметров), RoBERTA (355M параметров) и BART (406M параметров) — солидный выбор для задач классификации и natural language inference. Помимо этого, Flan-T5 (варианты на 770M и 3B) — надёжный базлайн для перевода, абстрактивной суммаризации, генерации заголовков и т. д.
Нам также может понадобиться обновить архитектуру модели, например когда архитектура предобученной модели не соответствует задаче. К примеру, нам может потребоваться обновить классификационные «головы» на BERT или T5, чтобы они соответствовали нашей задаче. Совет: если задача — простая бинарная классификация, NLI-модели могут работать «из коробки». Следование (entailment) сопоставляется с положительным классом, противоречие — с отрицательным, а нейтральная метка может указывать на неопределённость.
Затем выберите подход к fine-tuning. LoRA и QLoRA — хорошие отправные точки. Но если ваш fine-tuning более интенсивный, например продолженное предобучение на новых знаниях предметной области, вам может потребоваться полный fine-tuning.
Наконец, базовая настройка гиперпараметров. Как правило, большинство статей фокусируются на скорости обучения, размере батча и числе эпох (см. LoRA, QLoRA). А если мы используем LoRA, нам, возможно, захочется настроить параметр ранга (хотя статья про QLoRA обнаружила, что разные ранг и alpha приводят к схожим результатам). К другим гиперпараметрам относятся длина входной последовательности, тип функции потерь (контрастная потеря vs. совпадение токенов) и пропорции данных (например, смесь данных предобучения и демонстрационных данных или соотношение положительных и отрицательных примеров и др.).
Кэширование: чтобы снижать задержку и стоимость
Кэширование — это техника хранения данных, которые ранее были извлечены или вычислены. Так будущие запросы на те же данные могут обслуживаться быстрее. В области обслуживания генераций LLM популяризованный подход — кэшировать ответ LLM по ключу, которым служит эмбеддинг входного запроса. Затем для каждого нового запроса, если получен семантически похожий запрос, мы можем выдать закэшированный ответ.
Для некоторых практиков это звучит как «катастрофа, которая только и ждёт случиться». Я склонен согласиться. Поэтому я думаю, что ключ к внедрению этого паттерна — выяснить, как кэшировать безопасно, а не полагаться исключительно на семантическое сходство.
Зачем нужно кэширование?
Кэширование может значительно снизить задержку для ответов, которые уже обслуживались ранее. Кроме того, устраняя необходимость снова и снова вычислять ответ на один и тот же вход, мы можем сократить число запросов к LLM и тем самым сэкономить. Также есть определённые сценарии использования, которые не допускают задержку порядка секунд. Поэтому предвычисление и кэширование могут быть единственным способом обслуживать такие сценарии.
Подробнее о кэшировании
Кэш — это высокоскоростной слой хранения, который хранит подмножество данных, обращения к которым происходят чаще. Это позволяет нам обслуживать такие запросы быстрее через кэш, а не через основное хранилище данных (например, поисковый индекс, реляционную базу данных). В целом кэширование обеспечивает эффективное переиспользование ранее извлечённых или вычисленных данных. (Подробнее о кэшировании и лучших практиках.)
Пример кэширования для LLM — GPTCache.
Обзор GPTCache (источник)
Когда поступает новый запрос:
Генератор эмбеддингов: встраивает запрос с помощью различных моделей, таких как text-embedding-ada-002 от OpenAI, FastText, Sentence Transformers и др. Оценщик сходства: вычисляет сходство запроса через векторное хранилище, а затем выдаёт метрику расстояния. Векторное хранилище может быть локальным (FAISS, Hnswlib) или облачным. Оно также может вычислять сходство с помощью модели. Хранилище кэша: если запрос похож, закэшированный ответ извлекается и выдаётся. LLM: если запрос недостаточно похож, он передаётся LLM, которая затем генерирует результат. Наконец, ответ выдаётся и кэшируется для будущего использования.
Redis также поделился похожим примером, упомянув, что некоторые команды доходят до того, что предвычисляют все запросы, которые они ожидают получить. Затем они задают порог сходства, определяющий, какие запросы достаточно похожи, чтобы оправдать закэшированный ответ.
Как применять кэширование?
Нам следует начать с хорошего понимания паттернов запросов пользователей. Это позволяет нам продуманно спроектировать кэш, чтобы его можно было применять надёжно.
Сначала рассмотрим не-LLM пример. Представьте, что мы кэшируем цены товаров для e-commerce-сайта. Во время оформления заказа безопасно ли показывать (возможно, устаревшую) закэшированную цену? Вероятно, нет, поскольку цена, которую покупатель видит при оформлении, должна совпадать с итоговой суммой, которую с него спишут. Кэширование здесь неуместно, так как нам нужно обеспечить согласованность для покупателя.
Теперь вернёмся к ответам LLM. Представьте, что мы получаем запрос на краткое содержание «Миссия невыполнима 2», которое семантически достаточно похоже на «Миссия невыполнима 3». Если мы ищем по кэшу на основе семантического сходства, мы можем выдать неверный ответ.
Нам также нужно учесть, эффективно ли кэширование для данного паттерна использования. Один из способов это количественно оценить — через коэффициент попаданий в кэш (процент запросов, обслуженных напрямую из кэша). Если паттерн использования равномерно случаен, кэш потребовал бы частых обновлений. Тогда усилия на поддержание актуальности кэша могли бы свести на нет любую его выгоду. С другой стороны, если использование следует степенному закону, при котором небольшая доля уникальных запросов составляет большинство трафика (например, поисковые запросы, просмотры товаров), то кэширование может быть эффективной стратегией.
Помимо семантического сходства, мы могли бы также рассмотреть кэширование на основе:
ID объектов: применимо, когда мы предвычисляем краткие выжимки отзывов о товарах или генерируем краткое содержание для целой кинотрилогии. Пар ID объектов: например, когда мы генерируем сравнения двух фильмов. Хотя это выглядит как \(O(N^2)\), на практике небольшое число комбинаций обеспечивает основную долю трафика, например сравнение популярных фильмов одной серии или жанра. Ограниченного входа: например, переменные вроде жанра фильма, режиссёра или ведущего актёра. К примеру, если пользователь ищет фильмы конкретного режиссёра, мы могли бы выполнить структурированный запрос и прогнать его через LLM, чтобы изящнее оформить ответ. Другой пример — генерация кода на основе выбора из выпадающих списков: если код проверен и работает, мы можем закэшировать его для надёжного переиспользования.
Кроме того, кэширование не обязательно должно происходить «на лету». Как поделился Redis, мы можем предвычислять генерации LLM офлайн или асинхронно, прежде чем их выдавать. Обслуживая из кэша, мы смещаем задержку с генерации (обычно секунды) на поиск в кэше (миллисекунды). Предвычисление пакетами также может помочь снизить стоимость по сравнению с обслуживанием в реальном времени.
Хотя перечисленные здесь подходы могут быть не такими гибкими, как семантическое кэширование на естественно-языковых входах, я думаю, что они дают хороший баланс между эффективностью и надёжностью.
Guardrails: чтобы обеспечивать качество вывода
В контексте LLM guardrails (ограждения) валидируют вывод LLM, обеспечивая, что вывод не просто хорошо звучит, но и синтаксически корректен, фактологичен и свободен от вредоносного контента. Это также включает защиту от состязательного (adversarial) ввода.
Зачем нужны guardrails?
Во-первых, они помогают обеспечить, чтобы выводы модели были достаточно надёжными и согласованными для использования в продакшене. Например, нам может требоваться вывод в определённой JSON-схеме, чтобы он был машиночитаемым, или нам нужно, чтобы сгенерированный код был исполняемым. Guardrails могут помочь с такой синтаксической валидацией.
Во-вторых, они обеспечивают дополнительный слой безопасности и поддерживают контроль качества над выводом LLM. Например, чтобы проверить, уместен ли сгенерированный контент для выдачи, мы можем захотеть убедиться, что вывод не вреден, проверить его на фактическую точность или обеспечить согласованность с предоставленным контекстом.
Подробнее о guardrails
Один из подходов — управлять ответами модели через промпты. Например, Anthropic поделилась о промптах, спроектированных для направления модели к генерации ответов, которые полезны, безвредны и честны (helpful, harmless, and honest — HHH). Они обнаружили, что fine-tuning на Python с HHH-промптом привёл к лучшему качеству по сравнению с fine-tuning с RLHF.
Пример HHH-промпта (источник)
Более распространённый подход — валидировать вывод. Пример — пакет Guardrails. Он позволяет пользователям добавлять требования к структуре, типу и качеству вывода LLM через валидацию в стиле Pydantic. И если проверка не проходит, он может запустить корректирующее действие, например отфильтровать проблемный вывод или сгенерировать другой ответ.
Большая часть логики валидации находится в validators.py. Интересно посмотреть, как они реализованы. В широком смысле его валидаторы делятся на следующие категории:
Валидация одиночного выходного значения: включает проверку того, что вывод (i) является одним из заранее заданных вариантов, (ii) имеет длину в определённом диапазоне, (iii) если числовой, попадает в ожидаемый диапазон, и (iv) является полным предложением. Синтаксические проверки: включают проверку того, что сгенерированные URL валидны и доступны, а код на Python и SQL не содержит ошибок. Семантические проверки: проверяют, что вывод согласован с эталонным документом или что экстрактивная выжимка близко соответствует исходному документу. Эти проверки можно выполнять через косинусное сходство или техники нечёткого сопоставления. Проверки безопасности: обеспечивают, что сгенерированный вывод свободен от неуместной лексики или что качество переведённого текста высоко.
NeMo-Guardrails от Nvidia следует схожему принципу, но спроектирован для направления диалоговых систем на основе LLM. Вместо фокуса на синтаксических ограждениях он делает упор на семантические. Это включает обеспечение того, чтобы ассистент держался подальше от политически заряженных тем, предоставлял фактологически корректную информацию и мог обнаруживать попытки джейлбрейка.
Таким образом, подход NeMo несколько иной: вместо использования более детерминированных проверок, таких как проверка наличия значения в списке или инспекция кода на синтаксические ошибки, NeMo сильно опирается на использование другой LLM для валидации выводов (вдохновлено SelfCheckGPT).
В их примере для проверки фактов и предотвращения галлюцинаций они просят саму LLM проверить, согласован ли самый последний вывод с данным контекстом. Чтобы проверить факты, у LLM спрашивают, истинен ли ответ на основе документов, извлечённых из базы знаний. Чтобы предотвратить галлюцинации, поскольку база знаний недоступна, они заставляют LLM сгенерировать несколько альтернативных завершений, которые служат контекстом. Базовое предположение в том, что если LLM выдаёт несколько завершений, которые противоречат друг другу, то исходное завершение, вероятно, является галлюцинацией.
Пример модерации следует похожему подходу: ответ проверяется на вредоносный и неэтичный контент с помощью LLM. Учитывая нюансы этики и вредоносного контента, эвристики и традиционные техники машинного обучения здесь не дотягивают. Поэтому LLM необходима для более глубокого понимания намерения и структуры диалога.
Помимо использования guardrails для проверки вывода LLM, мы также можем напрямую направлять вывод к соответствию определённой грамматике. Пример этого — Guidance от Microsoft. В отличие от Guardrails, которая навязывает JSON-схему через промпт, Guidance обеспечивает соблюдение схемы, внедряя токены, составляющие структуру.
Можно думать о Guidance как о предметно-ориентированном языке (DSL) для взаимодействий с LLM и их вывода. Он черпает вдохновение из Handlebars — популярного шаблонизатора, используемого в веб-приложениях, который позволяет пользователям выполнять интерполяцию переменных и логическое управление.
Однако Guidance отличается от обычных шаблонизаторов тем, что выполняется линейно. Это значит, что он сохраняет порядок генерируемых токенов. Поэтому, вставляя токены, являющиеся частью структуры, — вместо того чтобы полагаться на корректную генерацию их самой LLM, — Guidance может диктовать конкретный формат вывода. В своих примерах они показывают, как генерировать всегда валидный JSON, генерировать сложные форматы вывода с несколькими ключами, обеспечивать, чтобы LLM играли правильные роли, и заставлять агентов взаимодействовать друг с другом.
Они также представили концепцию под названием token healing — полезную функцию, помогающую избегать тонких багов, возникающих из-за токенизации. Простыми словами, она откатывает генерацию на один токен назад до конца промпта, а затем ограничивает первый сгенерированный токен так, чтобы он имел префикс, совпадающий с последним токеном промпта. Это избавляет от необходимости беспокоиться о границах токенов при составлении промптов.
Как применять guardrails?
Хотя концепция guardrails для LLM в индустрии всё ещё в зачаточном состоянии, есть несколько сразу полезных и практичных стратегий, которые мы можем рассмотреть.
Структурное направление (structural guidance): применяйте guidance везде, где это возможно. Оно даёт прямой контроль над выводом и предлагает более точный метод обеспечить соответствие вывода определённой структуре или формату.
Синтаксические guardrails: включают проверку того, что категориальный вывод входит в набор допустимых вариантов, или что числовой вывод попадает в ожидаемый диапазон. Также, если мы генерируем SQL, они могут проверить, что он свободен от синтаксических ошибок, а также убедиться, что все столбцы в запросе соответствуют схеме. То же касается генерации кода (например, Python, JavaScript).
Guardrails безопасности контента: проверяют, что вывод не содержит вредоносного или неуместного контента. Это может быть так же просто, как сверка со Списком грязных, непристойных, нецензурных и иных плохих слов или использование моделей обнаружения ненормативной лексики. (Запуск классификаторов модерации на выводе — обычное дело.) Более сложный и нюансированный вывод может опираться на LLM-оценщик.
Семантические/фактологические guardrails: подтверждают, что вывод семантически релевантен входу. Скажем, мы генерируем краткое содержание фильма из двух предложений на основе его синопсиса. Мы можем проверить, семантически ли похоже полученное содержание на вывод, или поручить (другой) LLM установить, точно ли содержание отражает предоставленный синопсис.
Guardrails ввода: ограничивают типы ввода, на которые модель будет отвечать, помогая снизить риск того, что модель ответит на неуместные или состязательные промпты, ведущие к генерации вредоносного контента. Например, вы получите ошибку, если попросите Midjourney сгенерировать NSFW-контент. Это может быть так же прямолинейно, как сравнение со списком строк или использование классификатора модерации.
Пример guardrail на вводе в Midjourney
Защитный UX: чтобы предвосхищать и аккуратно обрабатывать ошибки
Защитный UX (Defensive UX) — это стратегия проектирования, признающая, что во время взаимодействия пользователей с продуктами на основе машинного обучения или LLM могут происходить нежелательные вещи, такие как неточности или галлюцинации. Поэтому цель — предвосхищать и управлять ими заранее, прежде всего направляя поведение пользователя, предотвращая неправильное использование и аккуратно обрабатывая ошибки.
Зачем нужен защитный UX?
Машинное обучение и LLM несовершенны — они могут выдавать неточный вывод. Кроме того, они по-разному реагируют на один и тот же вход с течением времени, например когда поисковые системы показывают разные результаты из-за персонализации, или когда LLM генерируют разнообразный вывод при более творческих настройках с высокой температурой. Это может нарушать принцип согласованности, который выступает за согласованный UI и предсказуемое поведение.
Защитный UX может помочь смягчить вышеперечисленное, обеспечивая:
Повышенную доступность: помогая пользователям понять, как работают ML/LLM-функции и каковы их ограничения, защитный UX делает их более доступными и удобными. Повышенное доверие: когда пользователи видят, что функция способна аккуратно справляться со сложными сценариями и не выдаёт вредоносный вывод, они с большей вероятностью будут ей доверять. Лучший UX: проектируя систему и UX так, чтобы справляться с неоднозначными ситуациями и ошибками, защитный UX прокладывает путь к более гладкому и приятному пользовательскому опыту.
Подробнее о защитном UX
Чтобы узнать больше о защитном UX, можно обратиться к гайдлайнам по взаимодействию человека и ИИ от Microsoft, Google и Apple.
Guidelines for Human-AI Interaction от Microsoft основаны на обзоре 168 потенциальных рекомендаций. Они были собраны из внутренних и внешних отраслевых источников, академической литературы и публичных статей. После объединения схожих рекомендаций, отсева слишком расплывчатых, слишком конкретных или не специфичных для ИИ, а также раунда эвристической оценки они сузили их до 18 рекомендаций.
Рекомендации по взаимодействию человека и ИИ на протяжении пользовательского пути (источник)
Эти рекомендации следуют определённому стилю: каждая из них — это сжатое правило-действие из 3–10 слов, начинающееся с глагола. Каждое правило сопровождается однострочным пояснением, устраняющим возможные неоднозначности. Они организованы по их вероятному применению в ходе взаимодействия с пользователем:
В начале: проясните, что система может делать (G1), проясните, насколько хорошо система может делать то, что она умеет (G2). Во время взаимодействия: соотносите подачу сервисов с контекстом (G3), смягчайте социальные предвзятости (G6). Когда ошибка: поддерживайте удобное отклонение (G8), поддерживайте удобное исправление (G9). Со временем: учитесь на поведении пользователя (G13), предоставляйте глобальные элементы управления (G17).
People + AI Guidebook от Google укоренён в данных и выводах, полученных от продуктовой команды Google и академических исследований. В отличие от рекомендаций Microsoft, организованных вокруг пользователя, Google организует свои рекомендации вокруг концепций, которые разработчику нужно держать в уме.
Есть 23 паттерна, сгруппированных вокруг распространённых вопросов, возникающих в процессе разработки продукта, включая:
Как мне начать с человекоцентричного ИИ: определить, добавляет ли ИИ ценность, рано вложиться в хорошие практики работы с данными (например, оценки). Как мне онбордить пользователей в новые ИИ-функции: сделать исследование безопасным, опереться на знакомое, автоматизировать поэтапно. Как мне помочь пользователям выстроить доверие к моему продукту: задать правильные ожидания, быть прозрачным, автоматизировать сильнее, когда риск низок.
Human Interface Guidelines for Machine Learning от Apple отличается от восходящего подхода академической литературы и пользовательских исследований. Вместо этого её первичный источник — знания и опыт практиков. Поэтому она не включает много ссылок или данных, а вместо этого фокусируется на давних принципах дизайна Apple. Это даёт уникальную перспективу, отличающую её от двух других гайдлайнов.
Документ фокусируется на том, как принципы дизайна Apple могут быть применены к продуктам, насыщенным ML, делая упор на аспекты UI, а не на функциональность модели. Он начинается с просьбы к разработчикам обдумать роль ML в их приложении и работать в обратную сторону от пользовательского опыта. Это включает вопросы вроде того, является ли ML:
Критичным или дополняющим: например, Face ID не может работать без ML, но клавиатура всё ещё может работать без QuickType. Проактивным или реактивным: Siri Suggestions проактивны, тогда как автокоррекция реактивна. Динамическим или статическим: рекомендации динамичны, тогда как распознавание объектов в Photos улучшается только с каждым релизом iOS.
Затем он углубляется в несколько паттернов, разделённых на входы и выходы системы. Входы фокусируются на явной обратной связи, неявной обратной связи, калибровке и исправлениях. Этот раздел направляет проектирование того, как ИИ-продукты запрашивают и обрабатывают данные и взаимодействия пользователей. Выходы фокусируются на ошибках, множественных вариантах, уверенности, атрибуции и ограничениях. Цель — обеспечить, чтобы вывод модели был представлен понятным и полезным образом.
Различия между тремя гайдлайнами поучительны. Google делает больший упор на соображения о тренировочных данных и разработке модели, вероятно, из-за своей инженерно-ориентированной культуры. Microsoft больше фокусируется на ментальных моделях, что, вероятно, является артефактом академического исследования в области HCI. Наконец, подход Apple сосредоточен вокруг обеспечения бесшовного UX, и этот фокус, вероятно, обусловлен её культурными ценностями и принципами.
Как применять защитный UX?
Вот несколько паттернов, основанных на приведённых выше гайдлайнах. (Дисклеймер: я не дизайнер.)
Задавайте правильные ожидания. Этот принцип единообразен во всех трёх гайдлайнах:
Microsoft: проясните, насколько хорошо система может делать то, что она умеет (помогите пользователю понять, как часто ИИ-система может ошибаться). Google: задавайте правильные ожидания (будьте прозрачны с пользователями о том, что ваш ИИ-продукт может и не может делать). Apple: помогите людям сформировать реалистичные ожидания (опишите ограничение в маркетинговых материалах или в контексте самой функции).
Это может быть так же просто, как добавить краткий дисклеймер над результатами, сгенерированными ИИ, как у Bard, или выделить ограничения нашего приложения на его лендинге, как это делает ChatGPT.
Пример дисклеймера на результатах Google Bard (Примечание: nrows не является допустимым аргументом.)
Будучи прозрачными о возможностях и ограничениях нашего продукта, мы помогаем пользователям откалибровать ожидания относительно его функциональности и вывода. Хотя в краткосрочной перспективе это может заставить пользователей доверять ему меньше, в долгосрочной перспективе это помогает укрепить доверие — пользователи менее склонны переоценивать наш продукт и впоследствии сталкиваться с разочарованием.
Обеспечьте удобное отклонение. Это явно упомянуто в Рекомендации 8 от Microsoft: поддерживайте удобное отклонение (сделайте лёгким отклонение или игнорирование нежелательных сервисов ИИ-системы).
Например, если пользователь перемещается по нашему сайту и всплывает чат-бот, спрашивающий, нужна ли помощь, пользователю должно быть легко отклонить чат-бот. Это гарантирует, что чат-бот не помешает, особенно на устройствах с меньшими экранами. Аналогично, GitHub Copilot позволяет пользователям удобно игнорировать предложения кода, просто продолжая печатать. Хотя в краткосрочной перспективе это может снизить использование ИИ-функции, это предотвращает её превращение в раздражитель и потенциальное снижение удовлетворённости клиентов в долгосрочной перспективе.
Предоставляйте атрибуцию. Это указано во всех трёх гайдлайнах:
Microsoft: проясните, почему система сделала то, что сделала (дайте пользователю возможность получить объяснение, почему ИИ-система повела себя так, как повела). Google: добавляйте контекст из человеческих источников (помогите пользователям оценить ваши рекомендации с учётом сведений из сторонних источников). Apple: рассмотрите использование атрибуции, чтобы помочь людям различать результаты.
Цитаты становятся всё более распространённым элементом дизайна. Возьмём, к примеру, BingChat. Когда мы делаем запрос, он включает в свои ответы цитаты, обычно из авторитетных источников. Это не только показывает, откуда взялась информация, но и позволяет пользователям оценить качество источников. Аналогично, представьте, что мы используем LLM, чтобы объяснить, почему пользователю может понравиться товар. Наряду с объяснением, сгенерированным LLM, мы могли бы включить цитату из реального отзыва или упомянуть рейтинг товара.
Контекст от экспертов и сообщества также повышает доверие пользователей. Например, если пользователь ищет рекомендации по пешему маршруту, упоминание, что предложенный маршрут настоятельно рекомендован соответствующим сообществом, может многое значить. Это не только добавляет ценность рекомендации, но и помогает пользователям откалибровать доверие через человеческую связь.
Пример атрибуции через социальное доказательство (источник)
Наконец, гайдлайны Apple включают популярные атрибуции вроде «Потому что вы читали нон-фикшн», «Новые книги авторов, которых вы читали». Эти описания не только персонализируют опыт, но и предоставляют контекст, усиливая понимание и доверие пользователя.
Опирайтесь на знакомое. Знакомя пользователей с новым ИИ-продуктом или функцией, полезно направлять их с помощью знакомых UX-паттернов и элементов. Это облегчает пользователям сосредоточение на основной задаче и помогает начать завоёвывать доверие к нашему новому продукту. Сопротивляйтесь искушению демонстрировать новые и «магические» функции через экзотические элементы UI.
В схожем ключе функции на основе чата становятся всё более распространёнными из-за растущей популярности ChatGPT. Например, чат с вашими документами, чат для запросов к данным, чат для покупки продуктов. Однако я сомневаюсь, что чат — правильный UX для большинства пользовательских опытов — он требует слишком много усилий по сравнению со знакомым UX кликов по тексту и изображениям.
Более того, увеличение усилий пользователя ведёт к более высоким ожиданиям, которые труднее оправдать. Netflix поделился, что у пользователей более высокие ожидания к рекомендациям, возникающим в результате явных действий, таких как поиск. В целом, чем больше усилий пользователь вкладывает (например, чат, поиск), тем выше его ожидания. Сравните это с менее затратными взаимодействиями, такими как прокрутка по подборкам рекомендаций или клик по товару.
Таким образом, хотя чат предлагает больше гибкости, он также требует больше усилий от пользователя. Более того, использование окна чата менее интуитивно, поскольку в нём недостаёт указателей (signifiers) на то, как пользователи могут корректировать вывод. В целом я думаю, что придерживаться знакомого и ограниченного UI проще для навигации пользователей по нашему продукту; чат стоит рассматривать лишь как вторичный или третичный вариант.
Сбор обратной связи пользователей: чтобы строить маховик данных
Сбор обратной связи пользователей позволяет нам узнавать их предпочтения. Применительно к продуктам на LLM обратная связь пользователей способствует построению оценок, fine-tuning и guardrails. Если задуматься, данные — такие как корпус для предобучения, демонстрации, созданные экспертами, человеческие предпочтения для моделирования вознаграждения — это один из немногих «рвов» (moats) для LLM-продуктов. Поэтому мы хотим осознанно думать о сборе обратной связи пользователей, проектируя наш UX.
Обратная связь может быть явной или неявной. Явная обратная связь — это информация, которую пользователи предоставляют в ответ на запрос нашего продукта; неявная обратная связь — это информация, которую мы узнаём из взаимодействий пользователей, не требуя от них намеренно предоставлять обратную связь.
Зачем собирать обратную связь пользователей
Обратная связь пользователей помогает нашим моделям улучшаться. Узнавая, что пользователям нравится, не нравится или на что они жалуются, мы можем улучшать наши модели, чтобы лучше удовлетворять их потребности. Это также позволяет нам адаптироваться к индивидуальным предпочтениям. Рекомендательные системы — яркий пример. По мере того как пользователи взаимодействуют с объектами, мы узнаём, что им нравится и не нравится, и со временем лучше угождаем их вкусам.
Кроме того, цикл обратной связи помогает нам оценивать общее качество нашей системы. Хотя оценки могут помочь нам измерить качество модели/системы, обратная связь пользователей предлагает конкретную меру удовлетворённости пользователей и эффективности продукта.
Как собирать обратную связь пользователей
Сделайте так, чтобы пользователям было легко предоставлять обратную связь. Это перекликается во всех трёх гайдлайнах:
Microsoft: поощряйте детализированную обратную связь (дайте пользователю возможность предоставлять обратную связь, указывающую на его предпочтения, в ходе обычного взаимодействия с ИИ-системой). Google: позвольте пользователям давать обратную связь (дайте пользователям возможность обучения в реальном времени, обратной связи и исправления ошибок). Apple: предоставляйте действенную информацию, которую ваше приложение может использовать для улучшения контента и опыта, который оно представляет людям.
ChatGPT — один из таких примеров. Пользователи могут ставить лайк/дизлайк на ответы или выбрать перегенерацию ответа, если он совсем плох или бесполезен. Это полезная обратная связь о человеческих предпочтениях, которую затем можно использовать для дообучения LLM.
Midjourney — ещё один хороший пример. После генерации изображений пользователи могут сгенерировать новый набор изображений (отрицательная обратная связь), подправить изображение, запросив вариацию (положительная обратная связь), или повысить разрешение и скачать изображение (сильная положительная обратная связь). Это позволяет Midjourney собирать богатые данные сравнений по сгенерированным выводам.
Пример сбора обратной связи пользователей как части UX
Учитывайте и неявную обратную связь. Неявная обратная связь — это информация, которая возникает по мере взаимодействия пользователей с нашим продуктом. В отличие от конкретных ответов, которые мы получаем из явной обратной связи, неявная обратная связь может предоставить широкий спектр данных о поведении и предпочтениях пользователей.
Ассистенты в стиле Copilot — яркий пример. Пользователи указывают, было ли предложение полезным, либо полностью принимая его (сильная положительная обратная связь), либо принимая и внося мелкие правки (положительная обратная связь), либо игнорируя его (нейтральная/отрицательная обратная связь). В качестве альтернативы они могут обновить комментарий, приведший к сгенерированному коду, что говорит о том, что исходная генерация кода не отвечала их потребностям.
Чат-боты, такие как ChatGPT и BingChat, — ещё один пример. Как менялось ежедневное использование со временем? Если продукт «залипчив» (sticky), это говорит о том, что пользователям он нравится. Также: какова средняя длина разговора? Это может быть непросто интерпретировать: разговор длиннее, потому что он был увлекательным и плодотворным? Или хуже, потому что пользователю потребовалось больше времени, чтобы получить нужное?
Другие паттерны, распространённые в машинном обучении
Помимо семи паттернов выше, есть и другие паттерны в машинном обучении, которые также релевантны для систем и продуктов на LLM. К ним относятся:
Маховик данных (data flywheel): непрерывный сбор данных улучшает модель и ведёт к лучшему пользовательскому опыту. Это, в свою очередь, способствует росту использования, которое даёт больше данных для дальнейшей оценки и дообучения моделей, создавая благотворный цикл. Каскад (cascade): вместо того чтобы поручать LLM одну сложную задачу, мы можем упростить её и разбить так, чтобы ей приходилось обрабатывать лишь задачи, в которых она преуспевает, такие как рассуждение или красноречивое изложение. RAG — пример этого. Вместо того чтобы полагаться на LLM в извлечении и ранжировании объектов на основе её внутренних знаний, мы можем дополнить LLM внешними знаниями и сосредоточиться на применении способностей LLM к рассуждению. Мониторинг (monitoring): помогает продемонстрировать ценность, добавляемую ИИ-системой, или её отсутствие. Кто-то поделился историей о запуске решения для клиентской поддержки на основе LLM в продакшене на две недели, прежде чем его отключили — A/B-тест показал, что потери были в 12 раз больше при использовании LLM в качестве замены их команды поддержки!
(Подробнее о паттернах проектирования для кода машинного обучения и систем.)
Также вот что сказали другие:
Разделение ответственности / декомпозиция задач — иметь отдельные промпты для отдельных подзадач и связывать их вместе помогает со вниманием и надёжностью (но вредит задержке). У нас были трудности с одновременным заданием жёсткой структуры вывода И переменного содержания ответа, поэтому мы разделили задачи. — Erick Enriquez
Несколько других вещей, которые понадобятся: ролевой контроль доступа (role based access control): кто к чему может получить доступ; безопасность: если я использую БД с LLM, как мне обеспечить нужные меры безопасности. — Krishna
Согласованный формат вывода: приведение выводов к стандартизированному формату вроде JSON; дополнение инструментами (tool augmentation): передача задач более специализированным, проверенным, надёжным моделям. — Paul Tune
Безопасность: смягчение отравления кэша, валидация ввода, смягчение prompt injection, происхождение тренировочных данных, вывод неуязвимого кода, смягчение вредоносного ввода, нацеленного на влияние на запросы, используемые инструментами (ИИ-агент), смягчение отказа в обслуживании (стресс-тестирование llm), и это лишь некоторые из них :) — Anderson Darario
Ещё кое-что, связанное с ux/ui: стимулируйте пользователей давать обратную связь по сгенерированным ответам (неявную или явную). Неявная может быть чем-то вроде стиля «призрачного текста» (ghost text) у copilot: если принято по TAB — это означает положительную обратную связь и т. д. — Wen Yang
Отличный список. Я бы добавил проверки согласованности вроде self-consistency sampling, цепочки и декомпозиции задач, а также ансамблирование выводов нескольких моделей. Применяю каждое из этого почти ежедневно. Dan White
Guardrails сверхрелевантны для построения аналитических инструментов, где llm выступает переводчиком с естественного языка на язык программирования. — m_voitko
Заключение
Это самый длинный пост, который я когда-либо писал. Если вы всё ещё со мной — спасибо! Надеюсь, чтение об этих паттернах было полезным и что приведённая ниже схема 2x2 имеет смысл.
Паттерны LLM по осям «от данных к пользователю» и «от обороны к атаке».
Мы всё ещё в самом начале пути к построению систем и продуктов на основе LLM. Есть ли какие-то другие ключевые паттерны или ресурсы? Что вы нашли полезным или бесполезным? Я бы с удовольствием услышал о вашем опыте. Пожалуйста, свяжитесь со мной!
Источники
Hendrycks, Dan, et al. «Measuring massive multitask language understanding.» arXiv preprint arXiv:2009.03300 (2020).
Gao, Leo, et al. «A Framework for Few-Shot Language Model Evaluation.» v0.0.1, Zenodo, (2021), doi:10.5281/zenodo.5371628.
Liang, Percy, et al. «Holistic evaluation of language models.» arXiv preprint arXiv:2211.09110 (2022).
Dubois, Yann, et al. «AlpacaFarm: A Simulation Framework for Methods That Learn from Human Feedback.» (2023)
Papineni, Kishore, et al. «Bleu: a method for automatic evaluation of machine translation.» Proceedings of the 40th annual meeting of the Association for Computational Linguistics. 2002.
Lin, Chin-Yew. «Rouge: A package for automatic evaluation of summaries.» Text summarization branches out. 2004.
Zhang, Tianyi, et al. «Bertscore: Evaluating text generation with bert.» arXiv preprint arXiv:1904.09675 (2019).
Zhao, Wei, et al. «MoverScore: Text generation evaluating with contextualized embeddings and earth mover distance.» arXiv preprint arXiv:1909.02622 (2019).
Sai, Ananya B., Akash Kumar Mohankumar, and Mitesh M. Khapra. «A survey of evaluation metrics used for NLG systems.» ACM Computing Surveys (CSUR) 55.2 (2022): 1-39.
Grusky, Max. «Rogue Scores.» Proceedings of the 61st Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers). 2023.
Liu, Yang, et al. «Gpteval: Nlg evaluation using gpt-4 with better human alignment.» arXiv preprint arXiv:2303.16634 (2023).
Fourrier, Clémentine, et al. «What’s going on with the Open LLM Leaderboard?» (2023).
Zheng, Lianmin, et al. «Judging LLM-as-a-judge with MT-Bench and Chatbot Arena.» arXiv preprint arXiv:2306.05685 (2023).
Dettmers, Tim, et al. «Qlora: Efficient finetuning of quantized llms.» arXiv preprint arXiv:2305.14314 (2023).
Swyx et al. MPT-7B and The Beginning of Context=Infinity (2023).
Fradin, Michelle, Reeder, Lauren «The New Language Model Stack» (2023).
Radford, Alec, et al. «Learning transferable visual models from natural language supervision.» International conference on machine learning. PMLR, 2021.
Yan, Ziyou. «Search: Query Matching via Lexical, Graph, and Embedding Methods.» eugeneyan.com, (2021).
Petroni, Fabio, et al. «How context affects language models’ factual predictions.» arXiv preprint arXiv:2005.04611 (2020).
Karpukhin, Vladimir, et al. «Dense passage retrieval for open-domain question answering.» arXiv preprint arXiv:2004.04906 (2020).
Lewis, Patrick, et al. «Retrieval-augmented generation for knowledge-intensive nlp tasks.» Advances in Neural Information Processing Systems 33 (2020): 9459-9474.
Izacard, Gautier, and Edouard Grave. «Leveraging passage retrieval with generative models for open domain question answering.» arXiv preprint arXiv:2007.01282 (2020).
Borgeaud, Sebastian, et al. «Improving language models by retrieving from trillions of tokens.» International conference on machine learning. PMLR, (2022).
Lazaridou, Angeliki, et al. «Internet-augmented language models through few-shot prompting for open-domain question answering.» arXiv preprint arXiv:2203.05115 (2022).
Wang, Yue, et al. «Codet5+: Open code large language models for code understanding and generation.» arXiv preprint arXiv:2305.07922 (2023).
Gao, Luyu, et al. «Precise zero-shot dense retrieval without relevance labels.» arXiv preprint arXiv:2212.10496 (2022).
Yan, Ziyou. «Obsidian-Copilot: An Assistant for Writing & Reflecting.» eugeneyan.com, (2023).
Bojanowski, Piotr, et al. «Enriching word vectors with subword information.» Transactions of the association for computational linguistics 5 (2017): 135-146.
Reimers, Nils, and Iryna Gurevych. «Making Monolingual Sentence Embeddings Multilingual Using Knowledge Distillation.» Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing, Association for Computational Linguistics, (2020).
Wang, Liang, et al. «Text embeddings by weakly-supervised contrastive pre-training.» arXiv preprint arXiv:2212.03533 (2022).
Su, Hongjin, et al. «One embedder, any task: Instruction-finetuned text embeddings.» arXiv preprint arXiv:2212.09741 (2022).
Johnson, Jeff, et al. «Billion-Scale Similarity Search with GPUs.» IEEE Transactions on Big Data, vol. 7, no. 3, IEEE, 2019, pp. 535–47.
Malkov, Yu A., and Dmitry A. Yashunin. «Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs.» IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 42, no. 4, IEEE, 2018, pp. 824–36.
Guo, Ruiqi, et al. «Accelerating Large-Scale Inference with Anisotropic Vector Quantization.» International Conference on Machine Learning, (2020)
Ouyang, Long, et al. «Training language models to follow instructions with human feedback.» Advances in Neural Information Processing Systems 35 (2022): 27730-27744.
Howard, Jeremy, and Sebastian Ruder. «Universal language model fine-tuning for text classification.» arXiv preprint arXiv:1801.06146 (2018).
Devlin, Jacob, et al. «Bert: Pre-training of deep bidirectional transformers for language understanding.» arXiv preprint arXiv:1810.04805 (2018).
Radford, Alec, et al. «Improving language understanding with unsupervised learning.» (2018).
Raffel, Colin, et al. «Exploring the limits of transfer learning with a unified text-to-text transformer.» The Journal of Machine Learning Research 21.1 (2020): 5485-5551.
Lester, Brian, Rami Al-Rfou, and Noah Constant. «The power of scale for parameter-efficient prompt tuning.» arXiv preprint arXiv:2104.08691 (2021).
Li, Xiang Lisa, and Percy Liang. «Prefix-tuning: Optimizing continuous prompts for generation.» arXiv preprint arXiv:2101.00190 (2021).
Houlsby, Neil, et al. «Parameter-efficient transfer learning for NLP.» International Conference on Machine Learning. PMLR, 2019.
Hu, Edward J., et al. «Lora: Low-rank adaptation of large language models.» arXiv preprint arXiv:2106.09685 (2021).
Dettmers, Tim, et al. «Qlora: Efficient finetuning of quantized llms.» arXiv preprint arXiv:2305.14314 (2023).
Williams, Adina, et al. «A Broad-Coverage Challenge Corpus for Sentence Understanding through Inference.» Proceedings of the 2018 Conference of the North American Chapter of the Association for Computational Linguistics: Human Language Technologies, Volume 1 (Long Papers), Association for Computational Linguistics, (2018).
GPTCache (2023).
Bai, Yuntao, et al. «Training a helpful and harmless assistant with reinforcement learning from human feedback.» arXiv preprint arXiv:2204.05862 (2022).
Guardrails (2023)
NeMo-Guardrails (2023)
Manakul, Potsawee, Adian Liusie, and Mark JF Gales. «Selfcheckgpt: Zero-resource black-box hallucination detection for generative large language models.» arXiv preprint arXiv:2303.08896 (2023).
Guidance (2023).
Amershi, Saleema, et al. «Guidelines for human-AI interaction.» Proceedings of the 2019 chi conference on human factors in computing systems. 2019.
People + AI Guidebook (2023).
Schendel, Zachary A., Faraz Farzin, and Siddhi Sundar. «A Human Perspective on Algorithmic Similarity.» Proceedings of the 14th ACM Conference on Recommender Systems. 2020.
Если вы нашли это полезным, пожалуйста, цитируйте эту статью так:
Yan, Ziyou. (Jul 2023). Patterns for Building LLM-based Systems & Products. eugeneyan.com. https://eugeneyan.com/writing/llm-patterns/.
или
@article{yan2023llm-patterns, title = {Patterns for Building LLM-based Systems & Products}, author = {Yan, Ziyou}, journal = {eugeneyan.com}, year = {2023}, month = {Jul}, url = {https://eugeneyan.com/writing/llm-patterns/} }
Присоединяйтесь к 11 800+ читателей, получающих обновления о машинном обучении, RecSys, LLM и инженерии.