Гайд по продвинутым вопросам для разработчика LLM

Введение

Собеседования на позицию разработчика больших языковых моделей (LLM) в топовых AI-компаниях предъявляют высокие требования к знаниям. Кандидату необходимо понимать устройство архитектуры трансформеров, владеть методами эффективного обучения и инференса, разбираться в оптимизациях памяти и скорости (таких как LoRA, FlashAttention, vLLM, ZeRO), знать тонкости распределённого тренинга, принципов LLMOps (MLOps для больших моделей) и нюансов продакшн-развертывания LLM. Также часто проверяют умение решать реальные задачи: от проектирования пайплайна для Sparse MoE до анализа проблем с памятью на GPU, понимания различий между методами обучения с подкреплением (RLHF vs DPO) и способов масштабирования моделей.

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

Кстати, если вам реально хочется изучать Data Science, массу годного контента вы найдёте в моём тг канале — это разборы заданий с собесов и масса полезных инструментов. А вот телеграм канал для тех, кто хочет изучить машинное обучение — нейронные сети, машинное обучение, Python. Вот ещё папка с годными ресурсами по Python — поможет в подготовке к собесу. А здесь мы разобрали – 100 вопросов c собесов в Data Science и ML

Архитектура трансформеров: основы для LLM-разработчика

Что проверяют на собеседовании: Базовое понимание того, как устроены современные LLM-модели на основе трансформеров. Интервьюер ожидает, что кандидат уверенно объяснит механизм self-attention (внимания) и multi-head attention, роль Q, K, V матриц, зачем нужны позиционные эмбеддинги, и как трансформеры справляются с длительными зависимостями. Также важно понимать отличия архитектур (например, энкодер vs декодер, или почему GPT-модели – декодеры без энкодер-части). Это фундамент: без этих знаний сложно обсуждать оптимизации или проблемы масштабирования.

Пример вопроса: «Объясните, как работает механизм многоголового внимания (multi-head attention) в трансформере и зачем нужны несколько голов внимания?» Или: «Как трансформер учитывает позицию слов в предложении, если в нём нет рекуррентности?»

Разбор ответа: Трансформеры основываются на механизме селф-аттеншен (самовнимания), который позволяет моделям фокусироваться на важных частях входной последовательности при обработке каждого элемента. В классическом трансформере каждый токен преобразуется через линейные слои в три вектора: Query (Q), Key (K) и Value (V). Затем считается внимательность: каждое Q сравнивается со всеми K, вычисляя вес (через скалярное произведение и softmax) – насколько токен «внимателен» к другим токенам. Эти веса используются для взвешенного суммирования V-векторов и получения нового представления токена[1]. Формула внимания для одного головы:

$$\text{Attention}(Q, K, V) = \text{softmax}\Big(\frac{Q K^T}{\sqrt{d}}\Big) V,$$

где $d$ – размерность. Многоголовое внимание (multi-head attention) повторяет этот процесс параллельно в нескольких проекциях: модель линейно преобразует входы в несколько наборов Q,K,V меньшей размерности и считает несколько «голов» внимания. Это нужно, чтобы модель могла фокусироваться на разных аспектах последовательности одновременно – каждая голова может захватывать различные взаимосвязи (например, одна голова может соответствовать согласованию по числу между подлежащим и сказуемым, другая – дистанционным зависимостям и т.д.). Результаты нескольких голов конкатенируются и проходят через очередной линейный слой, смешиваясь в единое представление[2][3]. Такой подход обогащает модель, позволяя ей учитывать разные «типологии» связей между токенами.

Помимо механизма внимания, важна позиционная информация. Поскольку само внимание не зависит от позиции токенов (оно инвариантно к перестановкам входов), трансформеру нужны позиционные эмбеддинги – добавленные к токенам признаки позиции. Классический подход – синусоидальные позиционные энкодинги (фиксированные), либо обучаемые позиционные эмбеддинги. Это обеспечивает, что модель различает, в каком порядке идут слова[4]. В современных LLM применяются усовершенствования: например, Rotary Positional Embeddings (RoPE) в LLaMA или ALiBi – они улучшены для больших контекстов.

Интервьюер может углубиться: например, спросить, почему сложность self-attention квадратична $O(n^2)$ по длине последовательности. Здесь важно отметить, что нужно вычислять внимания между каждой парой токенов в последовательности, что требует операций пропорционально $n^2$ и хранения матрицы внимания $n \times n$. Это становится узким местом для очень длинных последовательностей (контекстов). Поэтому существуют специализированные архитектуры для длинного контекста (Longformer, BigBird, Reformer и др.), использующие разреженные или рекуррентные схемы внимания, но это выход за рамки базового вопроса.

Хороший ответ должен также упомянуть структуру трансформер-слоя: помимо multi-head attention, там есть FFN-подслой (двухслойный перцептрон с нелинейностью), резидуальные соединения и слой-ном (Layer Normalization). Нужно понимать, почему используется, например, LayerNorm (для стабильности градиентов). Также часто спрашивают различие между трансформером-энкодером (как BERT) и декодером (GPT): энкодер смотрит на всю последовательность с двухсторонним вниманием (нет маски – токен видит и слева, и справа), а декодер – авторегрессионный, с masked attention (каждый токен видит только предыдущие, т.е. причинность соблюдается). GPT-модели – это чистые декодеры, оптимизированные для генерации текста в автокомплите режиме.

Trade-off и нюансы: Интервьюер может ожидать понимания, почему трансформеры вытеснили рекуррентные сети: благодаря механизму внимания трансформер параллелизует обработку последовательности (нет шагового состояния, все позиции обрабатываются одновременно в матричных операциях) и лучше улавливает дальние зависимости (в RNN они затухали). Однако за это платим ценой квадратичной сложности по памяти/времени. Современный фокус ресерча – снизить эту сложность (FlashAttention, разреженное внимание и др., см. ниже), а также увеличить контекстные окна (например, GPT-4 уже оперирует десятками тысяч токенов). Кандидату полезно упомянуть, что FlashAttention решает проблему памяти и скорости внимания (подробнее обсудим далее), а, например, позиционный кэш (KV-кэш) позволяет эффективно использовать авторегрессию для длинных последовательностей.

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

Эффективное тонкое обучение LLM: LoRA и методы PEFT

Что проверяют на собеседовании: Знание современных подходов для Parameter-Efficient Fine-Tuning (PEFT) – то есть способов адаптации больших моделей под новые задачи, не обучая их «с нуля» и не обновляя все миллиарды параметров. Одной из самых популярных техник является LoRA (Low-Rank Adaptation). Интервьюер может проверять, понимаете ли вы, как LoRA работает и почему она позволяет обучать большие модели с меньшими ресурсами, избегая катастрофического забывания. Также смежно могут спрашивать про Adapters, Prefix Tuning, Prompt Tuning – но LoRA часто на слуху и ожидается, что вы объясните именно её суть и trade-off.

Пример вопроса: «Расскажите, что такое LoRA в контексте дообучения LLM. Как она позволяет быстро адаптировать большую модель к новой задаче? В чём преимущества и ограничения LoRA по сравнению с полным fine-tuning?»

Разбор ответа: LoRA – это метод тонкой настройки больших моделей, при котором большинство параметров модели замораживается, а в некоторые слои добавляются небольшие дополнительные веса низкого ранга, которые и обучаются[5]. Идея основана на том, что изменение, необходимое для адаптации модели к новой задаче, имеет низкую внутрисвязность (low intrinsic rank). Вместо того чтобы слегка подправлять каждый из миллионов весов трансформера, LoRA предполагает, что эти изменения можно выразить через два маленьких матрицы $A$ и $B$ низкого ранга $r$, которые вставляются в слой.

Как это выглядит на практике: например, для матрицы весов $W$ размера $d \times k$ в модели (скажем, в проекции внимания или в FFN), LoRA добавляет разложение $W + \Delta W$, где $\Delta W = A \times B$. Здесь $A$ имеет размер $d \times r$, а $B$ – $r \times k$, причем $r$ значительно меньше исходных размеров ($r$ – гиперпараметр, типично 4, 8, 16…). Изначально $A$ и $B$ инициализируются так, что $\Delta W = 0$ (например, $B = 0$, а $A$ случайная). Во время обучения только $A$ и $B$ обучаются, а основной вес $W$ остаётся неизменным[6][7]. Благодаря малому $r$, количество обучаемых параметров резко сокращается (на несколько порядков). Например, для 7-миллиардной модели с LoRA $r=8$ можно обучать всего несколько миллионов параметров вместо всех 7B.

Преимущества LoRA: Во-первых, низкие требования к памяти и вычислениям – раз в десятки уменьшенное число обучаемых параметров означает, что градиенты и оптимизатор хранят мало данных, модель можно дообучивать на одной GPU, даже если полное fine-tuning требовало бы нескольких. Во-вторых, LoRA предотвращает катастрофическое забывание старых знаний модели, поскольку базовые веса не меняются (модель сохраняет изначальные умения, а новые знания добавляются как надстройка)[8][9]. В-третьих, удобно хранить и распространять результаты адаптации: вместо целой модели достаточно сохранить дельта-матрицы $A$ и $B$ (вес LoRA обычно составляет считанные проценты от полной модели). Это решает проблему, когда одна большая модель нужна в нескольких вариантах для разных доменов – мы можем держать одну общую модель и несколько LoRA-«плагинов» к ней, подключая нужный при инференсе[10][11].

Пример кода (псевдо): При использовании Hugging Face Transformers c библиотекой PEFT подключение LoRA может выглядеть так:

from peft import LoraConfig, get_peft_model
config = LoraConfig(r=8, lora_alpha=16, target_modules=[“W_q”, “W_v”], bias=”none”)
model = AutoModelForCausalLM.from_pretrained(“llama-7b”)
model = get_peft_model(model, config)
# model.forward теперь внутри добавляет Low-Rank матрицы к указанным модулям

Здесь target_modules – имена слоёв, где применится LoRA (например, матрицы проекции Query и Value в self-attention). После этого вы обучаете модель стандартно, но только параметры LoRA обновляются – их можно напечатать model.print_trainable_parameters() для проверки. После fine-tuning сохраняете веса LoRA отдельно (несколько мегабайт), а при инференсе загружаете базовую модель и применяете LoRA поверх неё.

Trade-offs и ограничения: Во-первых, LoRA добавляет параметры и не изменяет основной модельный вес – иногда это может ограничивать максимальное качество тонкой настройки, если новое знание не удаётся выразить малым рангом. Однако на практике для многих задач LoRA достигает почти того же качества, что полный fine-tuning, при значительно меньших затратах[12]. Во-вторых, на этапе инференса LoRA немного увеличивает количество операций (надо добавить $\Delta W$ умножение), но это незначительно, а при желании эти допслои можно «слить» с основными весами после обучения (алгебраически $W_{\text{new}} = W + A B$) для получения эквивалентной модели без внешних зависимостей.

Интервьюеру можно также упомянуть, что LoRA – лишь один из PEFT-методов. Были адаптеры (вставки небольших слоёв в каждую субслой трансформера, обучаемых на новом таске)[13][7], prompt tuning (добавление обучаемых токенов-префиксов к входу), P-Tuning v2 и др. Ключевая идея у всех – не менять 100% весов огромной модели. LoRA получила широкую популярность благодаря простоте реализации и эффективности, особенно в сообществе, тонко дообучающем LLaMA, GPT-J и прочие открытые модели для пользовательских нужд.

Ограничения: LoRA предполагает, что необходимое изменение низкоранговое. Есть случаи, где адаптация сложнее (например, кардинально новый домен, требующий более существенной перестройки модели) – тогда, возможно, придётся увеличить ранг или комбинировать с другими методами. Но в большинстве интервью ожидается знание именно LoRA как примера современного подхода к fine-tuning LLM.

Распределённое и масштабируемое обучение LLM: ZeRO, модельная параллельность и др.

Что проверяют на собеседовании: Умение масштабировать обучение модели, когда она не помещается на одной GPU или одном узле. Кандидату нужно знать про методы распределения вычислений и памяти: Data Parallel (классический), Model Parallel (разбиение модели по устройствам, включая tensor model parallel и pipeline parallel), а также современные гибриды вроде ZeRO (Zero Redundancy Optimizer) из Microsoft DeepSpeed. Вас могут спросить, как вы будете тренировать модель с сотнями миллиардов параметров, или в чём отличия разных стадий ZeRO, или как сочетать параллелизмы (3D parallelism). Также ожидается понимание ограничений: например, как влияет параллелизация на коммуникации между GPU, где узкие места.

Пример вопроса: «У вас модель на 100 млрд параметров, которая не помещается в память 80-GB A100. Какие подходы вы примените, чтобы обучить такую модель? Расскажите про ZeRO и другие техники распределённого обучения больших моделей.» Или: «В чём разница между Data Parallel, Model Parallel и Zero Redundancy Optimizer?»

Разбор ответа: Распределённое обучение больших моделей – обширная тема. Начнём с основ: при Data Parallelism вы копируете полную модель на каждую из N GPU, каждая GPU получает разный мини-батч данных, считает градиенты, которые затем агрегируются (суммируются) и обновляются общие веса. Это хорошо параллелит по данным, но проблема – полное дублирование памяти модели на всех GPU. Если модель огромна, просто раздать её копию на 8 GPU не спасает, ведь каждая всё равно должна уместить все параметры. Также большие оптимизаторы (типа Adam) хранят для каждого параметра моменты, удваивая-троя размеры памяти.

Model Parallelism – альтернатива, где сам модельный граф делится между устройствами. Например, tensor model parallel разбивает крупные матрицы весов слоя между несколькими GPU: каждая хранит только часть весов и вычисляет свою часть умножения, а затем результаты собираются. Pipeline parallel делит последовательность слоёв по разным GPU: GPU1 хранит первые L слоёв, GPU2 – следующие L слоёв и т.д., и обработка батча конвейерится через них. Комбинируя data, tensor, pipeline параллелизм, появилось понятие «3D parallelism» – сложная, но эффективная схема, позволяющая тренировать триллионные модели. Минусы: модельная параллельность требует много коммуникаций между GPU (например, при каждом матмуль нужно пересылать части активаций или градиентов), а конвейер – усложняет логику (микро-батчи, заполнение/опорожнение конвейера, приводит к bubble overhead). Эти сложности сейчас скрыты в библиотеках (Megatron-LM, DeepSpeed, FairScale), но от кандидата ожидают понимания принципов.

ZeRO (Zero Redundancy Optimizer) – это подход, появившийся в DeepSpeed, который сохраняет удобство Data Parallel (простота кода – каждое обновление происходит локально), но устраняет избыточное дублирование данных между GPU. ZeRO имеет три стадии (Stage 1, 2, 3), последовательно убирающие разные виды дублирования:

  • ZeRO Stage 1 – оптимизаторные состояния без дублирования: вместо того, чтобы на каждой GPU хранить свои копии моментов Adam (вектор скорости, переменной), все GPU шарят их – каждый держит только свою часть оптимизаторных данных и отвечает за обновление этой части[14][15]. Весы и градиенты при этом пока ещё дублируются на всех. Выигрыш – примерно в 2 раза по памяти (optimizer state разделён).
  • ZeRO Stage 2 – плюс разделение градиентов: здесь помимо состояний оптимизатора, градиенты также делятся между процессами. Когда вы делаете backward, каждый GPU вычисляет градиент только за свой шард параметров, а не за все, и по завершении all-reduce собирает нужные части кому нужно для обновления. Таким образом, каждый GPU хранит только градиенты своего шарда, а не всей модели[16][17]. Это даёт ещё рост экономии памяти (до 4-8х суммарно относительно Stage 0, т.е. обычного Data Parallel).
  • ZeRO Stage 3 – шардируются сами параметры модели: это самый продвинутый режим. Параметры каждой модели тоже разделены между GPUs, то есть ни одна GPU не хранит всех весов[18][19]. Во время форварда, когда нужно вычислить операцию с каким-то весом, DeepSpeed собирает нужные части веса по коммуникации, проводит вычисление, потом может даже убрать их из памяти (стриминг). Аналогично на бэкварде для градиентов. Таким образом, модель целиком распределена, и теоретически можно масштабировать линейно: 1/64 модели на каждой из 64 GPU – память на веса уменьшается в 64 раза[20][21]. Конечно, коммуникации резко возрастают (нужно перед каждой линейной операцией пересылать параметры или активации). На практике Stage 3 вводит ~50% оверхеда по обмену данными[22][23], но это цена за возможность обучать сотни миллиардов параметров. Кроме того, Stage 3 открывает путь к технологиям ZeRO-Infinity, где уже и GPU не обязаны хранить весь шард: веса выгружаются на CPU RAM или NVMe SSD по необходимости[24][25]. С таким подходом было показано, например, что можно тонко настроить 40-миллиардную модель на одной GPU с достаточным SSD, просто медленнее[25].

В результате ZeRO даёт удобство: вы пишете модель как обычно, а DeepSpeed прозрачно распределяет её и данные. Таблица иллюстрирует различия стадий ZeRO[16]:

Параметры распределены?Градиенты распределены?Оптимизаторные состояния распределены?Экономия памяти
ZeRO-1Нет (копии на каждом GPU)Нет (копии)Да (opt state шардирован)
ZeRO-2НетДа (градиенты шардированы)Да
ZeRO-3Да (веса шардированы между GPU)ДаДа

(Примечание: экономия 4×, 8× условна, точные цифры зависят от пропорции optimizer states к весам, обычно Adam хранит 2 состояния, потому 2×, а с градиентами 3×, и т.д. Но порядок величины такой.)

Практический пример: Допустим, у нас 8 GPU, модель 60 млрд параметров (~120 GB в fp16). Ни на одной GPU 120 GB не поместится, но с ZeRO-3 каждая хранит лишь 1/8 параметров (~15 GB), а optimizer+градиенты тоже распределены – это уже реально запускается на 8×32GB GPU при хорошей реализации. Без ZeRO-3 пришлось бы применить модельную параллель, разрезая модель вручную или Megatron-LM, что сложнее.

Кодовая сторона: В DeepSpeed достаточно включить режим ZeRO в конфиге. Например, JSON-конфиг:

“zero_optimization”: {
    “stage”: 2,
    “offload_param”: …,
    “allgather_partitions”: true,
    “reduce_scatter”: true
}

Это автоматически задействует нужные стратегии разделения. В PyTorch тоже есть аналог – Fully Sharded Data Parallel (FSDP), который по сути реализует идеи ZeRO (особенно Stage 3). Понимание, что FSDP = ZeRO-3 (плюс-минус), будет плюсом.

Trade-offs: Распределённое обучение не бывает бесплатным. Хотя ZeRO Stage 1/2 почти не влияют на время (коммуникаций не больше, чем в Data Parallel), Stage 3 добавляет заметный overhead на синхронизацию и передачу параметров[19][23]. В больших масштабах это компенсируется тем, что иначе модель вовсе не обучить. Важный момент – минимизация коммуникаций: DeepSpeed и NVIDIA придумали техники, как улучшить обмен (например, «интерлейвинг» all-reduce с вычислениями, чтобы скрыть задержки, или использование инфинитибенд/NVLink оптимально). Для MoE (Mixture of Experts, о чём ниже) была проблема all-to-all обмена – тоже придумали tree reduction алгоритмы для улучшения[26][27].

Также интервьюер может спросить, как сочетаются разные параллелизмы. Тут надо упомянуть, что ZeRO (шардинг) совмещается с data parallel и модельным параллелизмом – например, можно запустить несколько групп GPU, каждая хранит shard модели (ZeRO-3), внутри шарда ещё разбить слои по 2 GPU (tensor parallel), плюс pipeline parallel – это и называют 3D parallelism[28]. В реальных сверхбольших проектах (GPT-3, PaLM) так и делали. Но за деталями, скорее всего, лезть не придётся – достаточно показать, что вы знаете опции.

Ещё техники: Помимо ZeRO, стоит упомянуть градиентный чекпоинтинг (gradient checkpointing) – когда вы не сохраняете активации всех слоёв для backprop, а лишь некоторых, и при необходимости пересчитываете их при обратном проходе. Это экономит память (в 2-3 раза меньше активаций), но требует дополнительных вычислений. Обычно в больших моделях это всегда включают. Ещё – offloading: менее активные данные (оптимизатор или даже веса) можно выгружать в CPU RAM или NVMe и подгружать, когда понадобится (DeepSpeed ZeRO-Offload, ZeRO-Infinity)[24]. Это позволяет тренировать модели, которые суммарно не помещаются даже во все GPU вместе, ценой замедления. Например, можно обучать 30B модель на паре 16-гигабайтных GPU, если постоянно подкачивать параметры с SSD – будет очень медленно, но возможно.

Когда спросят про pipeline: объясните, что он даёт почти линейное ускорение по слоям, но требует аккуратного управления: например, если 4 микробатча, 4 GPU, можно обеспечить, что пока первый микробатч обрабатывается на GPU4, GPU1 уже занят 5-м микробатчем. Это сложнее синхронизировать, чем Data Parallel, но инструменты (например, PyTorch PipelineModule, DeepSpeed pipe) скрывают детали.

Вывод для интервью: показываем знание, что большие модели требуют комбинации решений: шардинг (ZeRO) – для памяти, параллелизм – для вычислительной нагрузки, plus memory-saving трюки (чекпоинтинг, half precision и др.). Если называется ZeRO, хорошо бы назвать ключевой эффект: разбиение состояния модели по GPU вместо дублирования[29]. Это ключевое слово (Zero Redundancy). И не забываем про FP16/BF16 – почти всегда обучения LLM идут в половинной точности (и даже NF4 для 4-бит), что тоже de facto метод оптимизации памяти/скорости.

Разреженные модели и Mixture-of-Experts (MoE)

Что проверяют на собеседовании: Понимание альтернативного подхода к масштабированию моделей – через разреженные активируемые параметры, а не плотные. Концепция Mixture-of-Experts (MoE) предполагает, что у модели есть несколько «экспертов» (подсетей), и на каждый входной токен активируется только небольшой поднабор экспертов, вместо использования всех параметров модели. Интервьюер может спросить, как MoE увеличивает эффективность LLM, либо дать задачу спроектировать обучение Sparse MoE модели (как указано в примерах вопросов). Нужно знать, как работает MoE-слой, что делает gating-функция, какие проблемы возникают (балансировка загрузки экспертов, коммутация all-to-all между GPU), а также быть знакомым с фреймворками (DeepSpeed-MoE, Tutel).

Пример вопроса: «Опишите, как вы спроектируете пайплайн обучения модели с Mixture-of-Experts (например, трансформер с MoE-слоями). С какими трудностями вы столкнётесь и как их решить?» Или: «Как использование Mixture-of-Experts позволяет увеличить емкость модели без пропорционального роста вычислительной стоимости?»

Разбор ответа: Mixture-of-Experts – архитектурный шаблон, в котором вместо одного крупного блока (например, FFN слоя) у нас есть несколько параллельных «экспертов» – скажем, $E$ разных feed-forward сети. Для каждого входного токена специальный роутер (gating network) выбирает, к каким $k$ экспертам отправить этот токен. Каждый выбранный эксперт обрабатывает токен (проходит через свою небольшую сетку), и выходы объединяются. Если $k$ значительно меньше $E$ (чаще всего $k=1$ или $2$), то в вычислениях каждый токен использует лишь малую долю всех параметров модели[30][31]. Таким образом, совокупное число параметров может быть огромным ($E$ экспертов по $N$ параметров = $E \times N$), но каждый отдельный пример вовлекает только ~$k \times N / E$ параметров.

Почему это выгодно: Представим модель с 100 экспертами по 1 млрд параметров – итого 100 млрд весов. В плотной модели 100B, каждая обработка токена затрагивает все 100B, а в sparse MoE с $k=1$ – только 1 млрд весов (1 эксперт). Флоп-эффективность на параметр растёт: мы повысили общую емкость модели, но затраты на один образец значительно меньше, чем если бы всё было плотно[32][33]. В итоге за тот же вычислительный бюджет можно обучить модель с бóльшим числом параметров, что потенциально даёт лучшее качество. Это особенно важно, если мы ограничены по времени или оборудованию – MoE позволяет достичь качества крупной модели ценой малых дополнительных вычислений (хотя требует больше памяти для хранения всех экспертов).

Трудности и решения: Главное – роутинг. Gating-слой (обычно небольшой линейный или софтмакс на токен) решает, какие эксперты получают токен. Нужно избегать ситуации, когда все токены идут к одному эксперту, а другие простаивают. Обычно вводят load balancing регуляризаторы: добавляют в функцию потерь штраф за дисбаланс или проектируют гейт так, чтобы распределять токены более равномерно. Например, в Google Switch Transformer использовали ограничение, что у каждого эксперта есть capacity – сколько токенов он может обработать в одном шаге; лишние токены либо отправляются к запасному, либо отбрасываются (overflow)[34][35]. Для предотвращения отбрасывания важных токенов есть алгоритм Batch Priority Routing: он сортирует токены по важности (по сумме топ-$k$ гейт-скороров) и назначает экспертов сначала ключевым токенам, чтобы менее важные отбрасывались первыми, если превышена емкость[35][36]. Это улучшает использование экспертов и качество при ограниченной capacity.

Распределённый аспект: Обычно количество экспертов $E$ больше, чем число GPU. Эксперты можно разместить по разным GPU: например, 16 экспертов на 4 GPU = по 4 эксперта на каждую. Тогда возникает коммуникация: после gating нужно переслать токены к соответствующим экспертам (если токен назначен эксперту, который живёт на другой карте) – это делается через операцию All-to-All: каждый GPU отправляет части своих данных другим и получает от других те, что предназначены его локальным экспертам. Это коммуникационно тяжёлая операция, особенно если GPU много. Для масштабирования DeepSpeed и Microsoft Tutel реализовали оптимизированные all-to-all: например, деревьями (сначала внутри узла, потом между узлами, уменьшая количество hops)[26][27]. Эта оптимизация уменьшает задержки при большом количестве узлов (пример: 64 GPU, обычный all-to-all мелкими сообщениями неэффективен, а иерархический – лучше).

Проектирование пайплайна обучения MoE: Можно описать по шагам: 1. Архитектура модели: Вы решили, в каких слоях будут MoE. Часто делают: каждая $n$-ая FFN-слой заменяется MoE-слоем (например, через один или только в середине сети). Определяете $E$ (общее число экспертов) и $k$ (сколько активируется на токен). 2. Размещение экспертов: Если модель большая, экспертов размещаем по GPU. Допустим, у нас 8 GPU и 32 эксперта – можно по 4 на GPU. Параллельно включаем ZeRO для остальных плотных параметров, чтобы основной трансформер (без экспертов) шардингом лежал на всех. DeepSpeed как раз поддерживает комбинацию «E + D + Z» – experts + data parallel + ZeRO[37], и даже «E + D + Z + M» (ещё и модельный parallel если нужно) – это сложные конфигурации, но DeepSpeed старается упростить через API. 3. Инициализация и gating: Чаще всего эксперты – одинаковые сети (например, однотипный MLP, инициализированные по-разному). Gating-слой – обычно простой linear + softmax, обучается вместе с моделью. Добавляем auxiliary loss для балансировки (Literature: Google использовали Load Loss). 4. Backward: При обратном проходе важно, что градиенты от экспертов тоже нужно собрать всем, чей токен там был – это обратный all-to-all. DeepSpeed-MoE делает это автоматически. 5. Оптимизация обучения: Добавляем noise или random token selection для обучения экспертов. Например, DeepSpeed ввёл Random Token Selection – техника, при которой в батче случайно уменьшается нагрузка на экспертов, чтобы избежать одновременной конкуренции экспертов за все токены[38]. Это улучшает сходимость. 6. Логирование: мониторим, сколько токенов попало к каждому эксперту (распределение нагрузки). Если кто-то не участвует, может надо отрегулировать гиперпараметры роутера (температуру софтмакса, коэффициент баланса).

Пример кода: С DeepSpeed MoE API можно оборачивать слои легко. Для PyTorch модели вместо nn.Linear используем deepspeed.moe.layer.MoE. Пример из документации DeepSpeed:

from deepspeed.moe.layer import MoE
# допустим, есть определение класса эксперта:
class ExpertMLP(nn.Module):
    def __init__(self, hidden_size):
        super().__init__()
        self.ffn = nn.Sequential(nn.Linear(hidden_size, 4*hidden_size),
                                 nn.ReLU(),
                                 nn.Linear(4*hidden_size, hidden_size))
    def forward(self, x):
        return self.ffn(x)
# вставляем MoE слой:
self.moe_ffn = MoE(hidden_size=H, expert=ExpertMLP(H), num_experts=32, k=1, ep_size=4)

Здесь num_experts=32, ep_size=4 значит: у нас 32 эксперта, объединённые в группы по 4 эксперта на каждую экспертовую параллельную группу (обычно соответствует 4 GPU, если world_size=4). DeepSpeed сам разобьёт 32 эксперта на 4 группы, расположив на каждом GPU по 8 экспертов (для ep_size=4, world=4, он считает num_experts/ep_size = 8 экспертов на GPU)[39][40]. Дальше при инициализации DeepSpeed мы указываем оптимизатору специальные группы параметров для экспертов (есть функция split_params_into_different_moe_groups_for_optimizer)[41] – это нужно, чтобы применить разные правила (например, отдельный LR для экспертов, если надо). Далее deepspeed.initialize под капотом развернёт MoE.

На инференсе: MoE-модель тоже выгодна – но только для достаточно больших батчей. Дело в том, что если вы обрабатываете один токен, он всё равно активирует $k$ экспертов из $E$, остальные простаивают – потенциал параллелизма не раскрыт. В режиме же большой нагрузки (много запросов или длинный генеративный пасс) MoE позволяет в том же времени обслужить больше токенов. Также MoE снижает латентность первого токена в больших моделях: при длинном контексте узкое место – вычисления в FFN на каждом шаге. Если вместо одного 8k-нейронного FFN (как в GPT-3) вы задействуете два 4k-нейронных эксперта, каждый токен проходит меньше нейронов, значит ответ придёт быстрее[42]. NVIDIA в блоге отмечает, что MoE снижает latency при большом количестве входных токенов, потому что вычисления распределены и упрощены[42].

Trade-offs: MoE-модели сложнее обучать: нужно правильно настроить роутинг, чтобы эксперты обучались равномерно. Возможны нестабильности: один эксперт может получать слишком много градиента и становиться «специалистом», а другие ничего не учат. Это лечат либо жёсткими ограничениями (capacity + дропаут токенов), либо регуляризаторами. Ещё MoE тратит больше памяти, хранение всех экспертов требует памяти пропорционально суммарным параметрам (которые гораздо больше, чем в плотной модели того же FLOPs). Например, 100B MoE с 1B активных – нужно хранить 100B весов, хотя за шаг используем 1B. В GPU памяти это критично. Тут помогает то, что эксперты легко шардингом раскладываются (каждый GPU хранит несколько экспертов). Также на инференсе можно подгружать экспертов динамически (например, хранить на CPU и загружать, если гейт выбрал того или иного эксперта – но это скорее исследовательская идея, не mainstream).

Ещё момент – scaling law: исследования (Fedus et al. 2022 «Scaling MoE») показали, что при равном числе параметров плотная модель часто чуть лучше разреженной, если обучены на одинаковом объёме данных[43]. То есть MoE даёт выигрыш в том, что за тот же compute можно поднять параметры и получить лучший перплекcити, но если дать плотной модели тот же параметр-бюджет, она, возможно, будет лучше. Поэтому MoE – про увеличение качества в условиях ограниченного вычислительного бюджета, а не магическое улучшение перплекcити просто за счёт добавления «мёртвых» параметров.

Резюме: Интервьюеру важно услышать, что вы знаете: (a) как работает MoE (эксперты + гейт)[31], (b) почему быстрее (активируем частично, flop-экономия)[44], (c) что сложно (роутинг, балансировка, коммуникация) и (d) что есть решения (DeepSpeed-MoE, алгоритмы BPR, etc.)[45]. Если вспомните цифры – например, MixtraL 8x7B от Mistral AI – можно упомянуть: это открытая MoE-модель (8 экспертов по 7B), показывающая на 30% меньше потерь, чем плотная 7B на некоторых задачах, почти догоняя 70B, но работающая быстрее плотной 70B. NVIDIA в блоге указывает, что MixtraL 8x7B (46B total) использует за токен только 12B параметров (2 эксперта из 8 активны), что значительно дешевле полного 46B или плотного 46B[46]. Такие примеры демонстрируют понимание преимущества.

Оптимизации инференса: KV-кэш, FlashAttention, vLLM

Что проверяют на собеседовании: Умение ускорять вывод (генерацию) LLM-моделей, особенно на длинных последовательностях и при многопользовательском сценарии. Это включает понимание механизма KV-кэша (Key-Value cache) для авторегрессивной генерации, алгоритма FlashAttention для быстрого внимания, и таких систем как vLLM (PagedAttention), которые повышают пропускную способность сервиса. Интервьюер может спросить, почему без этих оптимизаций инференс GPT-3 крайне медленный, и что можно сделать, чтобы ускорить ответы модели или поддержать длинный контекст без взрыва памяти. Конкретные вопросы: «Что такое KV-cache и зачем он нужен?», «Как FlashAttention ускоряет вычисления?», «Знаете ли вы, как работает vLLM и PagedAttention?».

Пример вопроса: «Объясните, как реализован KV-кеш при генерации текста и почему он ускоряет инференс LLM.» Или: «Что такое FlashAttention и в чём его преимущества при долгих последовательностях?» Также: «Мы столкнулись с тем, что инференс LLM тратит очень много GPU-памяти на большие промпты. Какие есть решения этой проблемы?»

KV-кэш (кэш ключей и значений): В автодополнении текста модель генерирует токены по одному, на каждом шаге беря предыдущий выход как часть входа. Наивная реализация: на шаг t+1 мы пропускаем через трансформер всю последовательность длины t+1 (все предыдущие токены + новый). Это крайне неэффективно – каждый раз пересчитывается внимание и все внутренние представления для старых токенов. KV-кеш исправляет это: мы сохраняем результаты вычисления K и V матриц внимания для каждого токена на каждом слое из предыдущих шагов, чтобы не пересчитывать их заново[47][48]. На новом шаге модель вычисляет только представления для последнего токена, извлекает из кэша матрицы $K_{\text{prev}}$ и $V_{\text{prev}}$ (для всех старых токенов) и строит внимание между новым токеном и старыми с использованием закэшированных значений[49][50]. Таким образом, затраты на шаг остаются примерно постоянными (не растут линейно с длиной контекста).

Например, в GPT-2 без кэша генерация 1000 токенов ~ в 1000 раз медленнее, чем одного токена, а с KV-кэшем – ускоряется почти до O(n) общей сложности вместо O(n^2). Поэтому все современные фреймворки реализуют KV-cache. Важно: KV-кеш требует памяти – он хранит для каждого сгенерированного токена векторы размерности порядка скрытого размера на каждый слой. Для больших моделей и длинных контекстов это гигантские объёмы: в одном примере оценили, что для batch=512, контекст=2048, GPT-3 KV-кеш занимает 3 ТБ памяти (!), что втрое больше памяти весов[51]. В реальности на 80GB A100 такие большие батчи не держат, но масштаб понятен. То есть KV-кеш – trade-off между временем и памятью. Интервьюер может уточнить: «А можно ли сэкономить на памяти кэша?». Есть идеи частичного кэша, только для последних N токенов, но они редки. Популярнее – сжать тип данных: хранить в FP16/INT8. Некоторые библиотеки применяют 16-бит для кеша, даже если весы BF16.

Пример кода KV-кеша: В PyTorch/HF-Transformers при генерации модель возвращает past_key_values – кортеж тензоров K,V для каждого слоя, размерностью [batch, heads, seq_len, head_dim]. Их нужно подать обратно в модель при следующем вызове, чтобы она их не считала заново. Это и есть KV-кеш. Если писать вручную:

outputs = model(input_ids, use_cache=True)
past = outputs.past_key_values
# при следующем шаге:
outputs = model(next_token_id, past_key_values=past, use_cache=True)
past = outputs.past_key_values

Модель внутри объединит past с новыми K,V и продвинется.

FlashAttention: Это оптимизация вычисления внимания, предложенная Tri Dao et al. FlashAttention переписывает алгоритм внимания так, чтобы не создавать огромных промежуточных матриц и лучше использовать память GPU. Стандартное внимание делает: $QK^T$ (матрица размеров seq_len × seq_len), потом softmax, потом умножение на V – и хранит эти большие промежуточные результаты. FlashAttention выполняет эквивалентные вычисления блочно: оно разбивает последовательность на блоки (тайлы) и обрабатывает их поочередно, так чтобы в каждый момент хранить в памяти данные только для одного блока (например, 128 × 128 токенов), а не для всей матрицы внимания сразу[1]. Плюс, FlashAttention использует low-level возможности GPU (например, транзакции чтения/записи из HBM в shared memory) более эффективно. В итоге память, занимаемая операцией внимания, сокращается с $O(n^2)$ до $O(n)$ (нет необходимости хранить всю матрицу, softmax можно вычислять на лету)[1]. Это снимает ограничение на длину контекста и ускоряет работу за счёт уменьшения обращения к медленной HBM-памяти. Авторы сообщают 2–4× ускорение wall-clock по сравнению с naîve вычислением внимания[1]. Сейчас FlashAttention внедрён во многие библиотеки (например, PyTorch с определённой версии, HuggingFace opt-in, JAX).

Как объяснить интервьюеру: «FlashAttention – это алгоритм внимания, оптимизированный по памяти и I/O. Он читает данные Q,K,V чанками в быстрые регистры/SharedMemory GPU, считает partial softmax и не пишет большие промежуточные результаты в глобальную память. Это позволяет достичь существенного ускорения – на H100 теоретически FlashAttn-2 выжимает 35% от пиковой скорости, а FlashAttn-3 уже 75% (вышла новая версия)»[52][53]. Упоминание, что FlashAttention стал стандартом (в контексте GPT-4 увеличения контекста до 32k – без него не обойтись). Если знаете: FlashAttention-2/3 добавляют ещё асинхронность тензорных ядер и FP8 поддержку[54][55]. Но даже FlashAttn-1 – уже must-know.

Пример сравнения: Скажем, у нас контекст 8k токенов на 32 голову. Обычный self-attention потребует хранить матрицу 8k×8k = 64 млн элементов на каждую голову – сотни миллионов чисел, это гигабайты. FlashAttention этого не делает – он загружает, например, по 256 токенов, считает частично softmax, потом следующий блок и т.д. Только небольшие блоки (размер подбирается под GPU) находятся в памяти, а в конце получается тот же результат.

vLLM и PagedAttention: vLLM – это высокопроизводительный inference-движок (от группы Berkeley, 2023), бьющий рекорды по throughput. Ключевая идея – улучшить управление памятью KV-кэша при обслуживании множества потоков запросов. Как отмечалось, KV-кеш – динамическая структура, и когда приходят запросы разной длины и параллельно, обычные системы (HuggingFace Transformers, или DeepSpeed-Inference) неэффективно используют память: они часто резервируют много лишней памяти под максимальный возможный контекст или страдают от фрагментации (когда короткие и длинные последовательности хранятся вперемешку, возникают «дыры»). Авторы vLLM показывают, что стандартные подходы теряют 60–80% GPU памяти впустую из-за такого неупорядоченного размещения KV-кэша[56]. vLLM решает это с помощью PagedAttention – аналогия с виртуальной памятью в ОС[57]. Они режут KV-кеш каждого запроса на фиксированные страницы (блоки) – например, по 128 токенов – и хранят их не непрерывно, а где есть место, ведя таблицу страниц[58]. Это значит, что блоки не требуют смежного размещения, можно эффективно переиспользовать свободные куски памяти[57][59]. Итог – почти нет потерь на фрагментации, менее 4% памяти тратится впустую[60] (в отличие от 60–80% ранее). Это позволяет на той же GPU хранить намного больше активных последовательностей и батчировать их гибко.

Кроме того, PagedAttention облегчает шэринг памяти между запросами: например, если у вас один и тот же префикс у нескольких запросов (что бывает при параллельной генерации нескольких вариантов ответа), vLLM хранит его KV-страницы единажды и помечает, что они принадлежат нескольким sequence IDs[61]. При ветвлении генерации (sampling N вариаций) это экономит до 50+% памяти и ускоряет, ведь не нужно повторять вычисления на префиксе[62].

Благодаря этому vLLM добивается феноменальной пропускной способности: на A100 в тестах throughput вырос в 14–24 раза относительно обычного HF Transformers сервера[63][64], и в несколько раз относительно оптимизированного HuggingFace Text Generation Inference[65]. Это значит, что используя vLLM, можно обслуживать, например, в 20 раз больше запросов в секунду на том же железе без потери скорости на запрос, за счёт более эффективного батчинга и памяти.

Что сказать интервьюеру: vLLM – пример того, как системная оптимизация (не изменение модели) даёт огромный выигрыш. Важно упомянуть, что бутылочное горлышко инференса – память и ее фрагментация[66][67]. vLLM это устраняет: <ins>«PagedAttention хранит KV-кеш в отдельных блоках-страницах, как в ОС, тем самым добиваясь почти оптимального использования GPU памяти (менее 4% потерь) и позволяя гибко батчить разнородные запросы»</ins>[57][60]. За счёт этого на практике получили до 24x увеличение Throughput без изменения моделей[63]. В продакшене это значит меньшие затраты: небольшая команда LMSYS, создатели Vicuna-бота, смогла с vLLM обслуживать миллионы запросов, чего HF backend не тянул[68][69].

Пример ситуации: Представьте, есть короткие запросы (на 10 токенов) и длинные (на 1000 токенов) вперемешку. Обычная библиотека должна зарезервировать для каждого запроса память под максимальный возможный буфер (1000 токенов), иначе не знает заранее. В vLLM же память подстраивается по мере роста последовательности и страницы разных запросов могут вперемешку лежать – мы не резервируем впустую. Когда короткие запросы завершаются, их страницы освобождаются и могут быть реюзаны. Это похоже на аллокатор памяти, оптимизированный под последовательности.

Вывод: Для инференса большая часть инноваций – про уменьшение памяти (FlashAttention уменьшает память внутри вычислений, PagedAttention уменьшает память хранения KV) и про увеличение параллелизма/батчинга (динамическое групирование запросов разной длины, шэринг префиксов). Интервьюеру желательно показать знание хотя бы на концептуальном уровне, почему эти методы важны.

Отдельный сценарий: «OOM на A100 при infill-инференсе»: Заданный пример подразумевает ситуацию, когда модель должна выполнить infill (вставку текста в середине, а не только дописывание в конец). Например, у нас есть префикс и суффикс, и нужно сгенерировать середину. Если попытаться сделать это на обычной GPT-модели, могут возникнуть проблемы: модель обучена на left-to-right задачу, она не умеет условиться на суффиксе. Один подход – скормить префикс + маску-дырку + суффикс как один длинный контекст и заставить модель выдать текст на месте дырки (как сделали в Fill-in-the-Middle (FIM) подходе OpenAI[70]). Это требует специальной маскировки внимания: токены суффикса не влияют на предшествующие, но видны последующим. Если реализовать неправильно, можно получить взрыв памяти: т.к. модель пытается учитывать очень длинный контекст (префикс+суффикс) на каждом шаге генерации, ещё и, возможно, пересчитывает его многократно. На A100 памяти может не хватить, особенно если суффикс большой (он как будто добавляется к контексту все время).

Как ответить: «Генерация с заполнением середины сложна для декодера. Если делать в лоб, приходиться держать в памяти ключи/значения и префикса, и суффикса, что может удвоить объем KV-кэша. Вероятно, переполнение памяти связано с тем, что инференс шел с полным суффиксом в контексте. Решения: либо использовать специально обученную модель на infill (как T5 или обученная по FIM, умеющая смотреть на суффикс), либо разбить задачу на несколько этапов. Например, можно сначала сгенерировать черновой текст фиксированной длины, а потом отранковать/продлить, или постепенно «подгонять» к суффиксу. Но лучший путь – обучить модель предсказывать текст между <|prefix|> и <|suffix|> токенами, тогда она за один проход сгенерирует вставку. В любом случае, без такой подготовки обычный GPT будет либо игнорировать суффикс, либо требовать чудовищной памяти для внимания на весь префикс+суффикс контекст.»

Если проблема чисто про память: «Я бы проверил, не сохраняются ли промежуточные градиенты или не используется ли избыточный precision. Для инференса можно перейти на FP16 или даже INT8 для кеша. Если KV-кеш слишком велик (для длинного суффикса), есть опция сократить контекст, возможно генерировать по частям.»

В общем, показать умение рассуждать: infill – нестандартная задача, решается либо специальными моделями, либо тратой ресурсов. Скорее всего, интервьюер ожидает упоминание FIM-подхода OpenAI: они преобразовали корпус, чтобы обучить модель вставлять текст, что позволяет затем без увеличения памяти выполнять infill (потому что модель сама генерирует внутри, не требуя внимания на будущее).

Квантование моделей (Quantization)

Что проверяют на собеседовании: Знание методов снижения разрядности весов и активаций для ускорения и облегчения моделей. Квантование – ключевой инструмент для внедрения LLM на ограниченном железе (например, 8-битное интференс на GPU, 4-бит на CPU). Интервьюер может спросить про виды квантования (пост-обучебное PTQ vs aware training QAT, пер-тензор vs пер-канал), про сложности квантования трансформеров (например, outlier-активности, скачки ошибок при 8→4 бит). Также могут упомянуть конкретные методы: SmoothQuant, GPTQ, LLM.int8() и пр., ожидая, что кандидат знаком с текущими достижениями: что 8-бит уже норма, 4-бит частично возможна.

Пример вопроса: «Объясните, зачем квантуют весы LLM и с какими проблемами сталкиваются при квантовании больших трансформеров.» Или: «Чем отличается пост-обучебное квантование от квантования с дообучением? Какие подходы позволяют квантовать модель до 4 бит без сильной потери качества?»

Разбор ответа: Квантование – это представление весов (и/или активаций) модели не в стандартном FP16/FP32, а в числах с более низкой разрядностью, например 8 бит (INT8) или 4 бита. Цель – уменьшить размер модели в памяти (и пропорционально ускорить вычисления, если железо поддерживает низкоразрядные операции)[71][72]. Для больших LLM это критично: 175B модель в FP16 – ~350 ГБ, а в INT8 – вдвое меньше (~175 ГБ), что может быть решающим для размещения модели на оборудовании. Кроме того, на современных GPU есть специализированные ядра для INT8 (NVIDIA Tensor Cores работают с INT8xINT8 или INT4xINT4 очень быстро), так что можно получить ускорение инференса. Например, перевод матриц в INT8 дает прирост 2–4× в скорости, если не упираемся в память.

Виды квантования:Пост-обучебное (Post-Training Quantization, PTQ) – мы берём готовую обученную модель и просто уменьшаем разрядность весов (и, опционально, активностей), выбирая шаг квантизации по диапазону значений. Это делается быстро, без повторного обучения, но может снизить точность модели[73]. – Квантование с дообучением (Quantization-Aware Training, QAT) – во время обучения или дообучения модели имитируются эффекты квантования (например, веса округляются до INT8 в forward проходе, но хранятся в full precision для градиентов), и модель учится быть устойчивой к этому округлению[74]. QAT обычно даёт лучше качество, но требует более сложного процесса обучения (и доступа к исходным данным). Часто применяют QAT на уже предобученной модели коротким циклом с небольшим лернингрейтом, чтобы модель адаптировалась к низкой точности.

Основные проблемы при квантовании LLM: 1. Outliers в активациях: В трансформерах, особенно на больших моделях, наблюдаются отдельные активации (например, значения на выходе некоторых слоёв или отдельных нейронов) очень большого масштаба, выбивающиеся из распределения[75]. Если мы квантим всё равномерно 8-бит, эти выбросы приводят к диспропорции – чтобы их вместить, шкала должна быть большой, но тогда большая часть остальных (малых) значений слипнется к нулю. Деттмерс и др. обнаружили, что у моделей >6B появляются такие outlier-каналы, что прямое 8-бит квантование сильно бьёт по качеству[76][77]. Решение: не квантовать outlierы или отделить их. Метод LLM.int8() (Dettmers, 2022) хранит активации полноточно, если они выше определённого порога, или использует смешанную точность – например, оставляет некоторые матрицы в FP16, другие в INT8[78]. В LLM.int8 авторы использовали 16-бит для «проблемных» каналов активаций (те самые outlier features), а остальные 8-бит[79]. Это позволило добиться почти нулевой деградации качества при переводе 175B моделей на 8-бит, тратя минимально больше памяти. 2. Квантование до 4 бит: 8-бит уже относительно решена задача, но хочется ещё в 2 раза ужать – до 4 бит. Тут проблемы усиливаются: квантование веса до 4 бит – шаг квантования крупный, ошибка апроксимации растёт. Однако появились методы: GPTQ (Gradient Post-Training Quantization) – алгоритм пост-фактум квантования, который проходит по весам слой за слоем и минимизирует ошибку активаций, учитывая второй порядок (Гесссиан)[80][81]. GPTQ позволяет сжать, например, OPT-175B до 3–4 бит с минимальной потерей качества, но только веса, без активаций[82]. Другой метод SmoothQuant (Xiao et al. 2022) – они «разглаживают» распределение, перенося часть разброса от активаций к весам: делят каждую активацию на определённый масштаб и умножают им же вес – получается, что активации становятся менее разнородны (outliers сглажены), а весам не страшно, их потом квантанут. Это позволило успешно INT8-квантовать и активации, и веса модели 175B почти без потерь[83][84]. 3. Аппаратные ограничения: На GPU нельзя выполнять произвольные битовые операции быстро – есть поддержка 8-bit матриц, 4-bit – только на новых (H100). Иногда 4-bit имитируется, но на самом деле считается в 8 бит, упаковывая два 4-бит числа. Или INT4 x FP16 вообще нет ядра – приходится кастовать. Поэтому бывает, что квантование хотя и экономит память, но не ускоряет, если нет оптимизированных ядер[85]. Интервьюер может спросить: «Почему при квантовании не всегда видим ускорение?» – ожидается ответ, что узкое место может быть память (модель стала меньше, но теперь на каждую операцию требуется де-квантизация или сборка, которая CPU-bound), или что GPU ядра не идеально работают с такой низкой точностью без потери параллелизма.

Пер-канальное vs пер-тензорное квантование: Пер-канальное (per-channel) – это когда каждый канал (например, каждая выходная фича слоя или каждая колонка весов) квантуется со своим scale. Пер-тензорное – один scale на весь тензор. Для LLM важнее пер-канальное, т.к. значения распределены неравномерно по разным нейронам[86]. Пер-канальное уменьшает ошибку, особенно в свёрточных и fully-connected слоях, но требует хранить больше метаданных (scale для каждого канала).

Пример: Пусть матрица весов 4096×4096. Пер-тензорное квантование определит общий max/min и равномерно разобьет на 256 уровней (для 8-bit). Если один столбец матрицы имел низкие значения, а другой очень высокие, то первый столбец «потеряет разрешение» (все его веса могут прижаться к нулю). Пер-колонное квантование даст каждому столбцу свой масштаб – маленький для того где все веса мелкие, большой для того где большие – так оба столбца сохранят относительную точность. Поэтому пер-channel квантование обычно предпочтительнее для трансформеров[86][87].

Современные результаты: Уже успешно применяют 4-битное квантование для инференса: Meta выпустила LLM.int4 и QLoRA (это 4-бит веса + LoRA адаптация в 16-бит) – оказалось, что LLaMA 65B в 4-бит почти не теряет в качестве, а запуск на 1–2 GPU становится реальностью. Однако 4-бит требует осторожности: обычно квантуют только веса, а активности оставляют в FP16 (Weight-only quantization, W4A16)[88]. Есть исследования здесь-0 (AWQ, ZeroQuant) которые показывают: можно на этапе PTQ подобрать, какие каналы квантовать грубее, а какие точнее (например, оставить 6 бит там, где чувствительно, и 4 там, где нормально)[89][90].

Что сказать про trade-off: – Квантование 8-bit почти не вредит качеству (с современными методами) и даёт 2x экономию памяти[71]. – 4-bit даёт ~4x экономию, но может чуть просадить особенно генерацию кода, математики (где точность важнее). – Меньше 4 бит для LLM пока экзотика (2-bit, 1-bit – сильная деградация, применяется разве что к отдельным слоям или в комбинации с сетью ошибок). – QAT для LLM редко используют, т.к. обучить 175B заново с квантованием – почти нереально; обычно обходятся умными PTQ методами. – Упомянуть можно квантование и distillation: их можно сочетать – сначала перегнать знание в меньшую модель (distill), а ту ещё и заквантовать.

Интервьюер может поинтересоваться знанием конкретных алгоритмов: «Что такое GPTQ?», «Как работает SmoothQuant?», «Что сделали в LLM.int8()?». Кратко ответим: GPTQ – пост-обучение квант, минимизирующий ошибку второго порядка, SmoothQuant – перенос масштаба от актов к весам для равномерности, LLM.int8 – смешанная прецизионность, держащая outliers в FP16[91].

Практический нюанс: Обслуживание INT8 моделей на GPU требует специальных Kernel (NVIDIA дает API cublasLt для INT8 GEMM с разными alpha scaling). В PyTorch 2.0+ есть support – torch.quantization – но для LLM лучше использовать готовые библиотеки: например, bitsandbytes (от Dettmers) позволяет загрузить модель сразу в 8- или 4-бит для inference. Пример:

import transformers, bitsandbytes as bnb
model = AutoModelForCausalLM.from_pretrained(“bigscience/bloom”, device_map=”auto”, load_in_8bit=True)

это сделает модель 8-битной на уровне линейных слоёв, используя кастомные 8-bit оптимизированные слои из bitsandbytes.

В резюме: квантование – ключевой способ ускорить LLM без изменения архитектуры. Но чтобы успешно квантовать большие модели, надо обходить проблемы outliers и потери точности с помощью умных алгоритмов[90][92].

LLMOps: эксплуатация LLM в продакшене

Что проверяют на собеседовании: Понимание того, как разворачивать и поддерживать LLM-модели в реальных продуктах. Это включает аспекты MLOps, специфичные для LLM – развёртывание кластеров для инференса, мониторинг качества и поведенческих отклонений, управление версиями и обновлениями модели, работа с подсказками (промптами) и цепочками LLM (LLM chaining), а также оптимизация затрат. Интервьюер может спрашивать: чем LLMOps отличается от классического MLOps, какие новые проблемы возникают (например, hallucinations, необходимость цензуры/модерации ответов, контроль качества диалогов), и какие инструменты используются для решения этих задач.

Пример вопроса: «Что такое LLMOps и чем он отличается от традиционного MLOps?» Или: «Представьте, вы деплоите новую языковую модель в продукте чата. Как вы будете отслеживать её поведение и качество ответов в продакшене, и какие метрики/алерты важны?»

Разбор ответа: LLMOps – это, по сути, «MLOps для больших языковых моделей». Многие принципы те же (версионирование моделей, CI/CD для их обновления, мониторинг latency и ошибок, сбор логов), но есть ряд особенностей[93][94]:

  • Работа с промптами (prompt management): В классическом ML модель получает числовые фичи, а здесь – текстовую подсказку, которая определяет поведение. В продакшене часто требуется управлять шаблонами промптов, версионировать их, тонко настраивать. Инструменты LLMOps предоставляют хранение и отслеживание промптов и их эффективности[95]. Например, может вестись база: промпт версии 1.2 -> какая доля успеха/неуспеха ответов. Если меняем формулировку промпта или system message, это почти как менять модель – надо зафиксировать и наблюдать, не стало ли хуже. Такого понятия в обычном MLOps нет, там input distribution контролируют иначе.
  • LLM chaining (многошаговые пайплайны): Часто приложение строится как последовательность вызовов LLM, иногда с логикой между ними[96]. Например, сначала модель извлекает намерение, затем другой вызов генерирует ответ. Эти цепочки нужно также оркестировать, мониторить, где узкие места, где модель может сбиться. Появляются фреймворки (LangChain, PromptEngine etc.), и LLMOps должен интегрироваться с ними. В отличие от обычных микросервисных ML API, где один запрос – один ответ, здесь один high-level запрос пользователя может разбиться на серию LLM-вызовов с разными подсказками. LLMOps включает управление такими многошаговыми сценариями – логирование промежуточных шагов, таймауты, fallback-ы, кеширование результатов повторяющихся шагов.
  • Мониторинг и обсервабилити: У LLM нет одной метрики качества типа accuracy, и ошибки могут быть тонкими (смещенные или галлюцинирующие ответы). Нужно собирать логии запросов и ответов[97], метрики типа длины промптов, длины ответов, времени инференса, использования токенов. А также бизнес-метрики: удовлетворённость пользователей, частота эскалаций к оператору, процент ответов, нарушающих политику и т.д. LLMOps-система должна в реальном времени позволять видеть примеры неудачных ответов, настроить автоматические проверки (например, прогонять ответы через классификатор токсичности) и алертить, если модель стала отвечать хуже ожидаемого[98]. Например, при обновлении модели до новой версии мы хотим A/B-тест: сравнить, не вырос ли процент галлюцинаций. В MLOps классическом тоже есть мониторинг дрифта данных, но тут акцент на содержании текстов, что сложнее – требует NLP метрик (BLEU, rougel) или human feedback.
  • Масштабирование инференса и стоимость: LLM очень требовательны. LLMOps включает оптимизации: например, автобатчинг запросов – если поток запросов большой, нужно пакетировать их для GPU (вспомним vLLM), но при этом следить за latency. Или шардирование модели на несколько GPU – LLMOps должен скрывать эти детали (например, с помощью Elastic Inference сервера, который прозрачно использует 2 GPU на одну модель). Также аспект cost monitoring: один ответ GPT-3 может стоить центов, и важно трекать, сколько токенов генерится, может ли модель «болтать лишнее». Иногда внедряют ограничения: обрезать ответы, если превысили N токенов, – это всё задачи на стыке инженеринга и ops.
  • Этичность и контроль контента: В продакшене необходимо отлавливать токсичные или утечные ответы. LLMOps может включать интеграцию с контент-модерацией: например, настроить второй модель-классификатор, который на лету проверяет ответ LLM перед отдачей и в случае чего заменяет или метит его. Это тоже часть пайплайна. Кроме того, храня логи разговоров, нужно уметь эффективно по ним искать, если случился инцидент (say, пользователь получил неподобающий ответ, надо быстро найти, почему – по логам).
  • Обновление моделей: Процесс выкладки новой версии LLM сложнее, т.к. модель больше и требует длительного развёртывания (может, несколько минут только на загрузку). Поэтому практикуется канареечный деплой: сначала новую модель на долю трафика, сравнить метрики, потом заменить полностью. LLMOps платформы должны поддерживать версионирование моделей и откат. Например, система могла бы позволять адресовать конкретную версию (embedding v1 vs v2). Это похоже на MLOps, но масштабы больше.

Отличия от MLOps: Коротко: LLMOps требует новых инструментов для работы с промптами и цепочками, значительно больших вычислительных ресурсов для инференса, и фокусируется на мониторинге качественных показателей текста, а не просто числовых метрик. Хороший ответ: «LLMOps – подмножество MLOps, адаптированное к жизненному циклу больших языковых моделей от обучения до деплоя и поддержки. Отличается тем, что помимо привычных задач (Continuous Integration, Deployment, мониторинг) добавляются новые – мониторинг содержимого ответов, управление подсказками, масштабирование инференса под долгие последовательности, и быстрое реагирование на нежелательные выходки модели. Цель LLMOps – сделать внедрение LLM в продукты столь же системным и надёжным, как обычные ML сервисы»[93].

Инструменты: Можно упомянуть, что появились стартапы и open-source решения: например, LangSmith (от LangChain) для отслеживания промпт-цепочек, WhyLabs для LLM data observability, Arize AI добавил поддержку vector embeddings мониторинга (чтобы отследить, как распределение запросов меняется), DeepSpeed MII для оптимизированного деплоя моделей на инференс. Платформы вроде Ray Serve позволяют легко масштабировать LLM inference с Autobatching. И конечно, HuggingFace Text Generation Inference – специализ. сервер для LLM.

Подготовка данных: В LLMOps попадает и работа с данными для обучения/тонкой настройки. Например, нужно хранить и версионировать огромные датасеты (вторичное обучение), следить за качеством данных (чистка, удаление личной информации – иначе модель начнёт её выдавать).

MLOps vs LLMOps кратко: – MLOps: CI/CD моделей, мониторинг метрик (точность, latency, drift), автоматизация тренинга, оркестрация экспериментов. – LLMOps: то же + управление промптами, многоэтапными вызовами, мониторинг качества текстов (не просто цифр), контроль токсичности, и оптимизация высоконагруженного инференса (batching, caching).

Интервьюер, возможно, просто хочет услышать, что вы в курсе этих модных слов и понимаете проблемы: «Inference LLM – дорого, долго; нужно кешировать результаты, например, отвечать повторно на одинаковые запросы из кеша (тоже LLMOps задача, встроить кеш системы); модель может деградировать со временем (drift в диалогах) – значит, надо периодически дообучать её на свежих данных или корректировать промпты.»

Вывод: LLMOps – это про доведение LLM из лаборатории до реального приложения. Хорошо продемонстрировать примерами: «Если в MLOps у нас pipeline: собрали данные -> обучили модель -> задеплоили -> отслеживаем AUC, то в LLMOps pipeline: взяли предобученную гигантскую модель -> возможно, дообучили на задачах (SFT, RLHF) -> развернули на кластер -> настроили промпты и memory для чата -> отслеживаем логи диалогов, проводим периодические human eval, быстро катим патчи (например, отключаем опасные запросы)». То есть очень интердисциплинарная область, охватывающая и системный и исследовательский кругозор.

RLHF и альтернативы: сравнение RLHF vs DPO

Что проверяют на собеседовании: Знание подходов к alignment (выравнивание моделей с человеческими предпочтениями) – в частности, Reinforcement Learning from Human Feedback (RLHF), лежащего в основе ChatGPT, и новых методов вроде Direct Preference Optimization (DPO). Кандидату стоит понимать, почему возник RLHF, как он реализуется (этапы: SFT -> обучение модели-награды -> PPO/RL), какие проблемы у RLHF (нестабильность, «reward hacking»), и что предлагает DPO как упрощённая альтернатива. Могут спросить про отличия алгоритмически и в практической реализации.

Пример вопроса: «Расскажите, чем Direct Preference Optimization (DPO) отличается от классического RLHF при дообучении языковой модели. Почему некоторые предпочитают DPO вместо RL?»

Разбор ответа: RLHF – это методика, когда мы тонко настраиваем LLM, чтобы она следовала человеческим предпочтениям, формализованным через функцию награды. В классическом RLHF pipeline три этапа: 1. Собрать данные предпочтений: для ряда запросов люди (или crowdworkers) оценивают ответы модели, выбирая лучший из 2–3 вариантов. 2. Обучить модель-награды (reward model) $R(x, y)$, которая по запросу $x$ и ответу $y$ предсказывает скаляр, насколько ответ хорош (эта модель обучается как регрессор/классификатор по человеческим оценкам). 3. Использовать RL (обычно PPO), чтобы оптимизировать основную LLM (политику $\pi$) на максимизацию награды $R(x, y)$ с регуляризацией (не уходить далеко от изначальной модели). То есть, мы генерируем моделью ответы, получаем оценку reward-модели, и обновляем веса модели через градиент политики, поощряя более высокую награду[99][100]. В формуле цель: максимизировать $E_{y \sim \pi}[R(x,y)]$ минус коэффициент KL между $\pi$ и первоначальной политикой (чтобы не деградировала речь, не появилось несвязного текста)[101][102].

Проблемы RLHF: Он сложен в реализации: – Награда приходит от модели-награды, а та не идеальна – может переобучиться, у нее свои biases. Если reward-модель несовершенна, policy может выучить эксплойты (т.н. reward hacking) – генерировать тексты, которые накручивают оценку $R$ не тем способом, что хотели люди[103]. – RL нестабильное: язык – дискретный, сигналы награды могут быть редкими, PPO требует тщательно подбирать лернингрейт, коэффициент KL, иначе модель либо “сломается” (станет отвечать шаблонно «Извините, не могу…» на всё), либо не научится новому. Это сложный цикл с кучей гиперпараметров[104][105]. – Время и вычисления: надо много семплировать, считать награды, обновлять – дорого, особенно на больших моделях.

DPO (Direct Preference Optimization) – предложен как упрощение: вместо явного RL, формулируем задачу предпочтений как прямое обучение с учителем на парах ответов. В DPO берём пару ответов $(y_w, y_l)$ на запрос $x$ – один предпочтен человеком (winner), другой проиграл (loser). Хотим настроить политику $\pi_\theta$ так, чтобы $\pi_\theta(y_w|x) > \pi_\theta(y_l|x)$, т.е. вероятность у модели для лучшего ответа была выше, чем для худшего. Они выводят аналитически, что оптимальный по RLHF политик $\pi$ пропорционален $\pi_{\text{ref}}(y|x) \exp(\beta r(y))$[106], где $\pi_{\text{ref}}$ – начальная модель, $r(y)$ – та же reward. Из этого и модели Брэдли-Терри для предпочтений получается loss DPO:

$$L_{\text{DPO}} = -\mathbb{E}{(x, y_w, y_l)} \log \sigma(\beta [\log \pi(y_l|x) + C(x)])$$ }(y_w|x) – \log \pi_{\theta

(грубо говоря) где $C(x)$ – константа, включающая логи реф модели[107][108]. Важно то, что это логистическая регрессия по паре: мы настраиваем модель $\pi_\theta$ выдавать бóльшую вероятность хорошему ответу по сравнению с плохим, с темперированием $\beta$. То есть сводим к оптимизации с учителем – нет петли генерации, нет модели-награды (в отличие от RLHF, где отдельная reward model; тут её роль как бы встроена через данные пар).

Отличия и преимущества DPO:Простота: DPO – по сути обычный fine-tuning по датасету пар предпочтений. Нет need для тренировки reward model и нет RL-цикла с PPO. То есть избавляемся от сложного этапа, экономим время и вычисления[109][110]. – Стабильность: DPO оптимизирует лог-правдоподобие предпочтений, что более гладко, не приводит к резким сдвигам модели. Там всё дифференцируемо и решается стандартными методами оптимизации (Adam, и т.д.). RLHF же – с дискретным семплированием, высоким риском дивергенции политики без аккуратного KL-штрафа. DPO, по сообщениям, легче воспроизводится и не «ломает» речь модели[111]. – Отсутствие reward model: В DPO не нужен отдельный модель-награды. На практике, правда, предпочтения часто изначально получены через модель-награды (так быстрее собрать много пар, прогнав модель по корпусу), но концептуально можно брать human-labeled пары сразу. Исключив reward model, мы убираем риск её ошибок (например, reward hacking) – мы напрямую обучаемся от человеческих сравнений. – Связь с RLHF: Можно сказать, что при малом $\beta$ DPO решение приближается к RLHF решению (они доказали эквивалентность в пределе). Но DPO – это именно альтернативная loss функция, без RL. – Применимость: DPO хорошо работает, когда у нас есть бинарные предпочтения. Если фидбек другой (скажем, оценка по шкале или сложный сигнал), RLHF может быть гибче (там можно любую reward функцию). DPO заточен под пары ответов. Но в LLM-альянсе обычно и собирают пары «лучше-хуже».

Практические результаты: Антропик и др. отмечают, что DPO fine-tuning дает сопоставимое качество диалоговой модели, как RLHF, но проще. Однако есть и исследования (см. arXiv Oct 2023) показывающие, что PPO всё же может чуть лучше выжать качество, если хорошо настроить, особенно на кодовых задачах[112].

Что ответить кратко: «RLHF – это через обучение с подкреплением при помощи модели-награды, а DPO – оптимизирует модель напрямую на человеческие предпочтения как на обучающие данные, без явного RL. DPO проще (не надо настраивать сложный RL loop) и более стабильна, но требует набора пар предпочтений. RLHF более универсален, но сложнее и дороже. DPO можно рассматривать как упрощение, заменяющее этап PPO-шагов простым логистическим обучением модели подгонять вероятности под предпочтения»[113][114].

Нюансы: Можно упомянуть KL-регуляризацию. В RLHF явно добавляют $D_{\text{KL}}(\pi || \pi_{\text{ref}})$ штраф, чтобы модель не ушла от исходного языка. В DPO это автоматически учитывается через присутствие $\pi_{\text{ref}}$ в выведенной формуле для $\pi$ (по сути, $\pi_{\text{ref}}$ играет роль baseline)[106][107]. То есть DPO тоже не позволяет модели слишком отклониться от изначального стиля (если $\beta$ не огромный). Это плюс: меньше риска получения «потерянной в пространстве» модели, как бывает при RL без достаточного KL.

Еще отличия: RLHF может оптимизировать любой сложный критерий, не обязательно заданный парами. DPO привязан к парам. Например, если хотим учесть штраф за длину, или какую-то custom функцию, RLHF гибче – просто включаем это в reward. DPO придётся генерировать пары, отражающие этот критерий.

Но в контексте интервью, скорее всего, ждут: DPO – новый метод Anthropic (или др.), решающий ту же задачу, что RLHF, но без RL, с большим упором на теорию предпочтений.

Заключение: Объяснив DPO vs RLHF, хорошо также показать понимание практики RLHF: мол, OpenAI по сути применил RLHF для ChatGPT – благодаря этому модель научилась следовать инструкциям, быть вежливой, не токсичной. Но это большой труд (сбор данных, настройка). DPO – попытка облегчить задачу.

Можно упомянуть термин «версия, дружественная к программистам»: DPO легче интегрировать – он выглядит как обычный model.train() по датасету (x, предпочтение). RLHF – нужно написать собственный training loop на основе PPO (есть библиотеки типа TRLX). Так что для инженерной культуры DPO привлекательнее.

Масштабирование моделей: параметры vs данные vs архитектуры

Что проверяют на собеседовании: Понимание законов масштабирования и различных подходов к улучшению качества модели при увеличении ресурсов. Часто обсуждается, как увеличить мощность LLM: – наращивать число параметров (больше нейронов, слоёв), – тренировать на большем объёме данных или дольше (больше шагов), – менять архитектуру (MoE, добавлять память, retrieval), – либо использовать более эффективное использование параметров (distillation, etc.). Интервьюер может попросить сравнить, что даст бóльший эффект в рамках заданного бюджета: увеличить модель в 2 раза или собрать в 2 раза больше данных, например. Ожидается знакомство со статьями про Scaling Laws (например, работа OpenAI 2020, DeepMind «Chinchilla» 2022), а также понимание trade-off: что модель с избытком параметров без достаточных данных – недообучена, а с лишними данными без параметров – недокомпилирует всё знание.

Пример вопроса: «Допустим, у вас есть в распоряжении условно 10^24 операций (compute budget) для обучения модели. Как бы вы распределили его между размером модели и объемом данных? Что говорят scaling laws по этому поводу?» Или: «Сравните подходы масштабирования: увеличить размер плотной модели vs перейти к sparse MoE vs добавить внешний Retrieval. Какие trade-offs у каждого?»

Разбор ответа: Законы масштабирования (scaling laws), впервые формально описанные в работе OpenAI (Kaplan et al. 2020), показывают, что перплексия/ошибка модели масштабно примерно падает как степень от увеличения: – числа параметров, – количества обучающих токенов, – вычислительного бюджета.

Важно: для данного фиксированного compute $C$ есть оптимальный баланс между количеством параметров $N$ и числом токенов данных $D$[115]. Исследование DeepMind «Chinchilla» (2022) выявило, что GPT-3 (175B, 300 млрд токенов) был переразмерен – слишком много параметров на тот объем данных, и что модель в 4 раза меньше (70B) обученная на 4 раза большем корпусе (1.4T токенов) превзошла GPT-3 по качеству, затратив тот же compute. Это стало новым правилом: «нет смысла увеличивать параметры без пропорционального роста данных». И наоборот – если данных много, но модель маленькая, она недоиспользует данные (не успевает все выучить).

Так что, отвечая: «Чтобы эффективно потратить фиксированный compute, нужно подобрать размер модели и число токенов по формуле ~N ~ D (линейная или чуть сублинейная зависимость). Например, Chinchilla law предложил ~20 tokens per parameter как оптимум». Идея: вместо одной гигантской модели недоученной, лучше чуть меньше модель, но обучить ее до сходимости на больше данных[116].

Масштабирование параметров (Dense): Увеличение параметров (больше нейронов, голов, слоёв) обычно улучшает качество (модель больше выражает сложных функций). Но returns diminishing: чтобы снизить ошибку на фикс. данных, приходится удваивать размер многократно. Закон Каплана: потеря ~ пропорциональна $N^{-0.076}$ (очень слабая степень). Поэтому гиганты дают выигрыш, но с убывающей эффективностью.

Масштабирование данных: Сначала эффективней – если модель недоучена, добавка данных сильно улучшит качество (ошибка падает ~ как $D^{-0.27}$). Но когда модель полностью обучилась на distribution, новые данные начинают меньше помогать. + Слишком много данных – время обучения растет линейно, а качество – сублинейно.

Compute-optimal frontier: Пары (N, D) дающие лучший результат за $C$. DeepMind показал, что GPT-3 имел N слишком большой относительно D. Chinchilla (70B, 1.4T tokens) был compute-optimal для 2022.

Sparse (MoE) vs Dense: Sparse scaling (MoE) позволяет увеличить N (параметры) почти без увеличения compute per token (потому что не все используются). Это означает, что при фиксированном compute мы можем позволить гораздо больше N, что может дать лучшее качество при достаточных данных. Но scaling law для MoE чуть другой: Fedus et al. (2022) «M4» показали, что при равном $C$ MoE выигрывает немного, но не драматично, и при этом MoE-модели при равных параметрах уступают плотным. Поэтому MoE хорош, когда compute ограничен: он расширяет «virtual capacity» модели[117]. Trade-off: MoE сложнее, требовательнее к памяти, а в режиме небольших batch может не использовать все эксперты. Однако, он даёт подъем качества на фиксированном C.

Retrieval (RAG) scaling: Вместо наращивания параметров можно дать модели доступ к внешней памяти – например, поисковому индексу или базе знаний. Это иной подход: модель остаётся относительно небольшой, но при каждом запросе она ищет релевантные документы и читает их. Для прикладных задач (вопрос-ответ) это резко повышает фактические возможности без увеличения размера модели. То есть, «scaling via knowledge». Trade-off: требует инфраструктуры (хранить базу, обновлять её), модель должна уметь читать и использовать контекст (многие LLM умеют). В интервью можно упомянуть: «OpenAI добавляет retrieval plugins, потому что бесконечно масштабировать GPT-4 – дорого, а подключив поиск по интернету, можно ответить на вопросы, не заучивая всю базу внутрь параметров».

Distillation as scaling: Knowledge Distillation позволяет взять очень большую, медленную модель и обучить меньшую воспроизводить её поведение[118]. Фактически, это «масштабирование вниз» – попытка передать знания большого размера в меньший размер без сильной потери. Это не классическое «scale up», но упомянуть можно: «Если latency критична, мы можем distill 100B модель в 10B – частично компенсируя качество, но сильно выиграв в скорости. То есть масштабирование качества – не всегда про наращивать, иногда лучше взять мощную основу и сжать её».

Latency-aware inference scaling: Если речь про «latency-aware», можно сказать: «Для снижения задержек мы можем пожертвовать немного качеством ради скорости: применить квантование, минимизировать глубину модели (distill, prune), использовать MoE (сокращает вычисление per token), или даже слайсинг по времени (см. Speculative decoding* – когда модель частично генерит на малой модели, а большая проверяет). Все эти трюки – также аспекты масштабирования под ограничения реального времени». *

Пример сравнения: – Увеличить модель с 6B до 60B параметров: качество, скажем, в general QA поднимется (может, примерно соотв. переходу GPT-3 -> GPT-3 XL), но затраты 10x, а данных нужно тоже 10x больше. – Вместо этого оставить 6B, но натренировать на 10x больше данных (если есть): возможно, 6B saturated на меньших данных уже, и лишние данные улучшат, но не так, как 60B. 6B может просто не вместить всю сложность новых данных. – MoE: допустим, 6B dense vs MoE 6B x 10 experts (60B total, но sparsely). MoE, если обучен правильно, даст лучше perplexity на такой же compute примерно, особенно если data обширны (он effectively видел больше параметров). – Retrieval: 6B модель + база знаний: на фактические вопросы о реальных фактах может превзойти и 60B, потому что 60B всё не помнит, а 6B + поиск найдёт ответ. Но retrieval не помогает там, где нужно именно рассуждать по внутренней логике или знать язык – там нужны параметры.

Интервьюеру важно увидеть, что кандидат понимает trade-off: – Больше параметров = умнее, но требует больше данных и compute, – Больше данных = более общеобразованный модель, но если параметров мало, она не сможет всё усвоить, – Сменить архитектуру (MoE) = чит немного, но усложняет систему. – Есть фундаментальные ограничения: некоторые задачи требуют качественно нового подхода, не просто масштабирования (скажем, математика – тут scale помогает, но может потребоваться chain-of-thought prompting, external tools и т.п.).

Scaling law эмпирика: Можно привести числа: «Перплексия падает примерно как N^-0.1 при scale параметров, так что 10x параметры – ~30% снижение перплексии. А если 10x данных – ~45% снижение (D^-0.27). Поэтому сначала стоит увеличить данные, пока модель не начнет недообучиваться.» И про Chinchilla: «Оптимальная модель на compute ~10^23 FLOPs – ~60B params, 1.3T tokens. Если сделать 175B, 300B токенов, она недоучена; если 10B, 3T токенов – переучена (data saturates smaller model?).»

Заключение: При ограниченных ресурсах нужно сбалансированно масштабировать все факторы. Тенденция последних лет: – 2018–2020 гнались за параметрами (GPT-2,3), – 2021–2022 поняли, что лучше больше данных (Chinchilla, Gopher vs GPT-3), – 2023 – сочетание: LLaMA показала, что можно обучить 65B на много данных и получить отличную модель без триллиона параметров; MoE Mistral показал, что sparse может дать выигрыш; плюс QLoRA – эффективно использовать уже обученные модели (fine-tune cheaply).

Интервьюер, возможно, хотел услышать именно про Chinchilla scaling law и MoE. Если упомянут DPO vs RLHF ранее, сравнение scaling методов завершает картину: RLHF улучшает выходы по качеству взаимодействия, а scaling улучшает базовые способности (знание, логика).

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

[1] [52] [53] [54] [55] FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision

https://www.together.ai/blog/flashattention-3

[2] Ace AI Interview Series 17 — Flash Attention: Revolutionizing Efficient Attention Mechanisms in LLMs | by AI SageScribe | Medium

https://medium.com/@aisagescribe/ace-ai-interview-series-17-flash-attention-revolutionizing-efficient-attention-mechanisms-in-be48eebaa2da

[3] [4] [8] [9] [118] Large Language Model (LLM) Interview Questions | by Sanjay Kumar PhD | Medium

https://skphd.medium.com/large-language-model-llm-interview-questions-ded6264547f1

[5] [12] [71] [72] [74] [83] [84] [86] [87] [88] [89] [90] [92] Large Language Model Optimization Interview Questions and Answers | by Sanjay Kumar PhD | Medium

https://skphd.medium.com/topllm-models-optimization-interview-questions-and-answers-2ddf523c6d87

[6] [7] [10] [11] [13] LoRA Explained: Low-Rank Adaptation for Fine-Tuning LLMs | by Zilliz | Medium

https://medium.com/@zilliz_learn/lora-explained-low-rank-adaptation-for-fine-tuning-llms-066c9bdd0b32

[14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [28] [29] DeepSpeed ZeRO1 2 3 Differences Explained

https://www.byteplus.com/en/topic/407298

[26] [27] [34] [35] [36] [45] [51] [73] [75] [76] [77] [78] [79] [80] [81] [82] [85] [91] Large Transformer Model Inference Optimization | Lil’Log

https://lilianweng.github.io/posts/2023-01-10-inference-optimization

[30] [31] [32] [33] [42] [43] [44] [46] [115] [116] [117] Applying Mixture of Experts in LLM Architectures | NVIDIA Technical Blog

[37] [38] [39] [40] [41] Mixture of Experts – DeepSpeed

https://www.deepspeed.ai/tutorials/mixture-of-experts

[47] [48] [49] [50] Understanding KV Cache and Paged Attention in LLMs: A Deep Dive into Efficient Inference | by Dewang Sultania | My musings with LLMs | Medium

https://medium.com/my-musings-with-llms/understanding-kv-cache-and-paged-attention-in-llms-a-deep-dive-into-efficient-inference-62fa372432ce

[56] [57] [58] [59] [60] [61] [62] [63] [64] [65] [66] [67] [68] [69] vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention | vLLM Blog

https://blog.vllm.ai/2023/06/20/vllm.html

[70] Filling in the middle for great good – Sparse Notes

https://jmschndev.github.io/jekyll/update/2023/07/05/fim.html

[93] [94] [95] [96] [97] [98] What is LLMOps, and how is it different from MLOps?

https://www.pluralsight.com/resources/blog/ai-and-data/what-is-llmops

[99] [100] [101] [102] [103] [104] [105] [106] [107] [108] [111] Simplifying Alignment: From RLHF to Direct Preference Optimization (DPO)

https://huggingface.co/blog/ariG23498/rlhf-to-dpo

[109] [112] RLHF vs. DPO: Choosing the Method for LLMs Alignment Tuning

https://medium.com/@baicenxiao/rlhf-vs-dpo-choosing-the-method-for-llm-alignment-tuning-66f45ef3d4b5

[110] RLHF vs. DPO: Comparing LLM Feedback Methods – LLM Directory

https://llmmodels.org/blog/rlhf-vs-dpo-comparing-llm-feedback-methods

[113] Direct Preference Optimization (DPO): a lightweight counterpart to …

https://toloka.ai/blog/direct-preference-optimization

[114] RLHF and DPO Compared – CROWDWORKS Blog

+1
0
+1
1
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *