Mixtral
Реализация:
llm/src/llm/models/mixtral/mixtral.py· классMixtralНоутбук:notebooks/mixtral.ipynb
Место в линейке: GPT-1 → GPT-2 → LLaMA → Mistral → Mixtral · Gemma
Что вы узнаете
Заголовок раздела «Что вы узнаете»- Чем Mixtral отличается от Mistral: MoE-слой из 8 SwiGLU-экспертов с top-2 роутингом вместо плотного FFN.
- Что показала статья: качество уровня Llama 2 70B при ≈13 млрд активных параметров и отсутствие тематической специализации экспертов.
- Как записать прямой проход Mixtral в формулах и где каждая формула реализована в коде.
- Как посчитать общее и активное число параметров (46,7 и 12,9 млрд) и проверить подсчёт без выделения памяти.
- Как загрузить веса HuggingFace и в чём реализация отличается от оригинала.
Предварительные знания
Заголовок раздела «Предварительные знания»- Mistral — вся часть модели, кроме FFN.
- Mixture-of-Experts — роутер, top-k, load-balancing loss, алгоритм dispatch/combine.
- Feed-forward сеть и активации — SwiGLU, из которого состоит каждый эксперт.
Mixtral 8x7B (Mistral AI, 2024, arXiv:2401.04088) — это Mistral с одним структурным изменением: плотный SwiGLU-FFN каждого блока заменён на слой Mixture-of-Experts (MoE) — 8 параллельных SwiGLU-экспертов, из которых на каждый токен работают только 2. Всё остальное — RMSNorm в pre-LN схеме, GQA, RoPE, словарь 32 000 — как у Mistral 7B.
Attention в оригинале — GQA + RoPE с плотным вниманием на весь контекст 32k: скользящее окно Mistral 7B v0.1 в Mixtral не используется (sliding_window=None в HF MixtralConfig). В этом репозитории Mixtral переиспользует GroupedQueryAttention; окно включается только ключом window_size, без него внимание плотное, как в оригинале.
Теория MoE — роутер, top-k, стоимость, load-balancing loss, алгоритм — подробно разобрана в главе Mixture-of-Experts. Здесь — как она применена в Mixtral и в коде репозитория.
Научный вклад
Заголовок раздела «Научный вклад»Статья Jiang et al. (2024) описывает разреженную MoE-модель с открытыми весами (лицензия Apache 2.0), которая по качеству сопоставима с крупнейшими плотными открытыми моделями того времени:
- Архитектура (табл. 1 статьи): , слоя, головы Q размера , голов K/V, скрытый размер эксперта , словарь , контекст 32 768 токенов, экспертов, на токен.
- Разреженность. Формула слоя — , где — вектор токена, — матрица роутера (ниже — ), оставляет два наибольших логита, а остальные заменяет на , — эксперт . Каждый токен имеет доступ к 47 млрд параметров, но использует около 13 млрд — стоимость вычислений как у плотной модели на 13 млрд (проверка — в разделе Подсчёт параметров).
- Качество. По результатам статьи Mixtral не уступает или превосходит Llama 2 70B и GPT-3.5 на большинстве рассмотренных бенчмарков, особенно в математике, генерации кода и многоязычных задачах, используя примерно в 5 раз меньше активных параметров, чем Llama 2 70B. Модель обучалась с контекстом 32k токенов. Вариант Mixtral 8x7B – Instruct дообучен с SFT и DPO.
- Анализ роутинга (разд. 5 статьи). Авторы смотрели, к каким экспертам попадают токены разных подмножеств датасета The Pile (ArXiv, PubMed, PhilPapers, Wikipedia, GitHub, DM Mathematics). Явной тематической специализации не обнаружилось: распределения по экспертам для статей ArXiv, биологии и философии почти одинаковы, заметно отличается лишь DM Mathematics. Зато роутер проявляет синтаксическую структуру: например, токен
selfв Python и словоQuestionв английском часто идут к одному и тому же эксперту, отступы в коде — к одним и тем же экспертам. Кроме того, последовательные токены заметно чаще, чем при случайном выборе, попадают к тем же экспертам, особенно в верхних слоях. Вывод: эксперты — не «специалисты по темам», а скорее по типам токенов и локальному контексту.
В статье нет описания load-balancing loss и capacity factor — их устройство берут из Switch Transformer и эталонных реализаций (HF MixtralForCausalLM); см. ниже.
Архитектура блока декодера
Заголовок раздела «Архитектура блока декодера»%%{init: {"flowchart": {"rankSpacing": 28, "nodeSpacing": 28}}}%%
flowchart TB
Ids(["token ids"]):::io --> TokEmb["Token Embedding"]:::blue
TokEmb --> Drop["Dropout"]:::gray
subgraph Dec["MixtralDecoder × num_layers · pre-RMSNorm"]
direction TB
X(["x"]):::io --> N1["RMSNorm"]:::gray
N1 --> Attn["Grouped Query Attention<br/>sliding window — только с window_size"]:::blue
R["RoPE<br/>cos/sin от позиции · без параметров<br/>один модуль на все слои"]:::rope
R -. "поворот Q и K" .-> Attn
Attn --> A1(("+")):::add
X -. residual .-> A1
A1 --> N2["RMSNorm"]:::gray
N2 --> FFN["MoE<br/>top-k из num_experts SwiGLU-экспертов"]:::purpleHl
FFN --> A2(("+")):::add
A1 -. residual .-> A2
end
Drop --> Dec
Dec --> NF["RMSNorm<br/>(финальный)"]:::gray --> Lin
Lin["Linear → vocab_size"]:::gray --> Out(["logits"]):::io
Out -. "generate(): softmax → выбор токена" .-> Next(["следующий токен"]):::io
style Dec fill:transparent,stroke:#82b366,stroke-width:2px,color:#5b9a3c
classDef io fill:#ffffff,stroke:#999999,color:#1a1a1a;
classDef add fill:#ffffff,stroke:#666666,color:#1a1a1a;
classDef blue fill:#dae8fc,stroke:#6c8ebf,color:#1a1a1a;
classDef blueHl fill:#dae8fc,stroke:#2f5f9e,stroke-width:3px,color:#1a1a1a;
classDef purple fill:#e1d5e7,stroke:#9673a6,color:#1a1a1a;
classDef purpleHl fill:#e1d5e7,stroke:#6a3d85,stroke-width:3px,color:#1a1a1a;
classDef gray fill:#f5f5f5,stroke:#666666,color:#1a1a1a;
classDef grayHl fill:#f5f5f5,stroke:#333333,stroke-width:3px,color:#1a1a1a;
classDef gold fill:#fff2cc,stroke:#d6b656,color:#1a1a1a;
classDef rope fill:#d5f0ec,stroke:#3a9e8f,color:#1a1a1a;
classDef ropeHl fill:#d5f0ec,stroke:#1f6f63,stroke-width:3px,color:#1a1a1a;
classDef dim fill:#f5f5f5,stroke:#bbbbbb,color:#999999,stroke-dasharray:4 3;
Как RoPE поворачивает Q и K — в разделе Attention с RoPE документа LLaMA и в главе positional-encoding.md.
Прямой проход в формулах
Заголовок раздела «Прямой проход в формулах»Вход — индексы токенов . Обозначим — скрытые состояния после блока (для одной последовательности; батч обрабатывается так же).
Эмбеддинги (embeddings.md):
где — матрица эмбеддингов, — её строка . Позиционных эмбеддингов нет — позиция попадает в attention через RoPE; масштабирования на , как у Gemma, тоже нет.
Блок (pre-LN, normalization.md):
где — состояние после attention-подслоя блока , — две нормализации блока со своими весами.
RMSNorm применяется к каждой строке :
где — обучаемый масштаб (инициализируется единицами, в коде — _w; в normalization.md он обозначен , здесь — , чтобы не путать с логитами роутера ниже), — rms_norm_eps (у Mixtral 8x7B ).
GQA с RoPE (attention.md). Для нормализованного входа , головы Q и номера её группы K/V , (в mistral.md — ):
где — номер группы K/V, — проекции головы Q и группы K/V (в коде склеены в _q, _k, _v), — выход головы , — выходная проекция (_layer), — causal-маска (masks.md); с window_size — ещё и скользящее окно. Четыре головы Q делят одну пару K/V при , .
MoE (mixture-of-experts.md) для каждой строки матрицы :
где — логиты роутера, — множество экспертов с наибольшими логитами, — вес эксперта (softmax только по выбранным), — роутер, , — веса эксперта .
Выход:
— логиты, — финальная нормализация, — отдельная матрица, не привязанная к эмбеддингам (так и в Mixtral 8x7B; bias — только при bias: true). Строка логитов — оценки следующего токена после (language-modeling.md).
MoE изнутри
Заголовок раздела «MoE изнутри»%%{init: {"flowchart": {"rankSpacing": 28, "nodeSpacing": 28}}}%%
flowchart TB
X(["x · один токен"]):::io --> Router["Router<br/>Linear(emb_size → num_experts)"]:::gray
Router --> TopK["top-k логитов<br/>k = top_k_experts"]:::gray
TopK --> W["softmax по выбранным k<br/>→ веса w₁ … w_k"]:::purple
TopK -- "индексы экспертов" --> Disp["dispatch:<br/>x → выбранные эксперты"]:::gray
X --> Disp
subgraph Experts[" "]
direction LR
E1["Expert 1<br/>(выбран)"]:::blue
E2["Expert 2"]:::dim
Ed["⋯"]:::dim
En["Expert N<br/>(выбран)"]:::blue
end
Disp --> E1
Disp --> En
E1 --> Sum["Σ wᵢ · Expertᵢ(x)"]:::gold
En --> Sum
W --> Sum
Sum --> Drop["Dropout"]:::gray --> Out(["out"]):::io
style Experts fill:transparent,stroke:#6c8ebf,stroke-dasharray:4 3
classDef io fill:#ffffff,stroke:#999999,color:#1a1a1a;
classDef add fill:#ffffff,stroke:#666666,color:#1a1a1a;
classDef blue fill:#dae8fc,stroke:#6c8ebf,color:#1a1a1a;
classDef blueHl fill:#dae8fc,stroke:#2f5f9e,stroke-width:3px,color:#1a1a1a;
classDef purple fill:#e1d5e7,stroke:#9673a6,color:#1a1a1a;
classDef purpleHl fill:#e1d5e7,stroke:#6a3d85,stroke-width:3px,color:#1a1a1a;
classDef gray fill:#f5f5f5,stroke:#666666,color:#1a1a1a;
classDef grayHl fill:#f5f5f5,stroke:#333333,stroke-width:3px,color:#1a1a1a;
classDef gold fill:#fff2cc,stroke:#d6b656,color:#1a1a1a;
classDef rope fill:#d5f0ec,stroke:#3a9e8f,color:#1a1a1a;
classDef ropeHl fill:#d5f0ec,stroke:#1f6f63,stroke-width:3px,color:#1a1a1a;
classDef dim fill:#f5f5f5,stroke:#bbbbbb,color:#999999,stroke-dasharray:4 3;
Кратко, что делает MoE.forward (подробный разбор — в mixture-of-experts.md):
- Роутер
nn.Linear(emb_size, num_experts)выдаёт логит на каждого эксперта для каждого токена. torch.topkвыбираетtop_k_expertsэкспертов; веса — softmax только по выбранным k, во float32.- Каждый эксперт — самостоятельный
SwiGLUи обрабатывает только выбравшие его токены; невыбранный эксперт не вызывается. - Выход — взвешенная сумма выходов выбранных экспертов, затем dropout.
Статья записывает веса как , HF — как softmax по всем экспертам → top-k → перенормировка. Это одно и то же: общий знаменатель сокращается (доказательство).
Алгоритм
Заголовок раздела «Алгоритм»Вместо цикла по токенам — цикл по экспертам: каждый эксперт получает все свои токены одним вызовом.
X = x.reshape(N, D) # N = batch · seq_lentopk_logits, topk_idx = topk(X @ W_r, K) # [N, K]W = softmax(float32(topk_logits)).to(dtype) # [N, K]Y = zeros(N, D)for e in 0 … E−1: tok, slot = where(topk_idx == e) # dispatch: токены эксперта e и место e в их top-k if tok пуст: continue Y.index_add_(0, tok, W[tok, slot, None] · Expert_e(X[tok])) # combinereturn dropout(Y).reshape(batch, seq_len, D)Каждый токен получает ровно слагаемых; всего эксперты обрабатывают строк — от «все эксперты на все токены» для Mixtral. Ёмкости эксперта и отбрасывания токенов нет (dropless), как в HF MixtralSparseMoeBlock и эталонном MoeLayer Mistral. Пошаговый разбор, пример dispatch и сравнение с capacity factor Switch Transformer — в mixture-of-experts.md. Корректность проверяет тест против наивного цикла по токенам (llm/tests/core/test_moe.py).
Load-balancing loss
Заголовок раздела «Load-balancing loss»Без ограничений роутер схлопывается на нескольких «любимых» экспертов. При обучении к loss языковой модели прибавляется вспомогательный loss (Switch Transformer, разд. 2.2; load_balancing_loss_func в HF Mixtral; в статье Mixtral не описан):
где:
- — доля токенов, у которых эксперт стоит на месте в top-k (фактическая загрузка, недифференцируема);
- — средняя по токенам вероятность эксперта в softmax роутера по всем экспертам;
- —
router_aux_loss_coef; - статистика собирается по всем слоям MoE и всем настоящим (не паддинговым) токенам сразу.
При равномерной загрузке вспомогательное слагаемое равно (для — ), перекос его увеличивает. Градиент идёт через с «весом» загрузки: логиты перегруженных экспертов уменьшаются. Вывод, численные примеры и тонкости — в mixture-of-experts.md.
В коде: MoE запоминает router_logits последнего прохода, load_balancing_loss в core/moe.py считает формулу (совпадает с HF до float), Mixtral.auxiliary_loss() возвращает router_aux_loss_coef · aux, а Trainer и HFGPTAdapter прибавляют его к loss при обучении (loss оценки — только языковой модели). По умолчанию коэффициент 0 — loss выключен, как и в HF, где он включается output_router_logits=True (коэффициент там по умолчанию 0.001).
Компоненты
Заголовок раздела «Компоненты»| Компонент | Класс | Файл |
|---|---|---|
| Токен-эмбеддинги | TokenEmbeddings | core/token_embeddings.py |
| Позиционное кодирование | RoPE | core/rope.py |
| Нормализация | RMSNorm | core/rms_norm.py |
| Attention | GroupedQueryAttention (тот же класс, что у Mistral) | core/group_query_attention.py |
| FFN | MoE (top-k роутинг по SwiGLU-экспертам) | core/moe.py |
| Блок декодера | MixtralDecoder (pre-LN) | core/mixtral_decoder.py |
| Модель целиком | Mixtral | models/mixtral/mixtral.py |
Разбор кода
Заголовок раздела «Разбор кода»MixtralDecoder
Заголовок раздела «MixtralDecoder»core/mixtral_decoder.py. Конструктор создаёт четыре модуля:
| Атрибут | Модуль | Формула |
|---|---|---|
_heads | GroupedQueryAttention(num_q_heads, num_kv_heads, emb_size, head_size, max_seq_len, window_size, rope, dropout, bias) | |
_ff | MoE(emb_size, num_experts, top_k_experts, dropout, hidden_dim=intermediate_size, bias) | |
_norm1, _norm2 | RMSNorm(emb_size, eps=norm_eps) | , |
forward(x, use_cache=True, cache=None) — дословно формулы блока:
norm1_out = self._norm1(x)attention, kv_caches = self._heads(norm1_out, use_cache=use_cache, cache=cache)out = attention + x # U^(l) = H^(l-1) + GQA(RMSNorm1(H^(l-1)))norm2_out = self._norm2(out)ffn_out = self._ff(norm2_out) # MoE(RMSNorm2(U^(l)))# возвращает (ffn_out + out, kv_caches) при use_cache, иначе (ffn_out + out, None)Та же схема, что у MistralDecoder, с заменой SwiGLU на MoE. Кэш слоя — тройка (K, V, next_pos) из GroupedQueryAttention (mistral.md). MoE в кэше не участвует: он применяется к каждому токену независимо, и при генерации роутер просто вызывается на новом токене.
Mixtral
Заголовок раздела «Mixtral»models/mixtral/mixtral.py, наследник BaseModel.
__init__(config):
resolve_head_size(config, "num_q_heads", rope=True)— размер головы:head_sizeиз конфига илиembed_dim // num_q_heads, с проверками (для RoPE — чётный);- читает необязательные
rms_norm_eps(по умолчанию1e-6),intermediate_size(None→4 · embed_dimвнутри SwiGLU),bias(True),router_aux_loss_coef(0.0; отрицательный —ValueError),rope_theta(10000),window_size(None); - создаёт
_token_embeddings(TokenEmbeddings), один модуль_position_embeddings(RoPE) на все слои,_dropout, список_decodersизnum_layersблоковMixtralDecoder, финальную_normи_linear = nn.Linear(embed_dim, vocab_size, bias=bias)— отдельную, без weight tying; - инициализирует веса как HF:
init_normal_—Linear(включая роутер и экспертов) иEmbeddingиз (ключinitializer_range), bias — нули.
Проверка top_k_experts в диапазоне 1 … num_experts делается в конструкторе MoE, так что неверный конфиг падает с ValueError при создании модели.
forward(x, use_cache=False, cache=None, attention_mask=None):
check_sequence_length— длина с учётом кэша не большеmax_position_embeddings;padding_from_attention_mask— маска ключей и позиции при паддинге в любом месте строки (masks.md).- Запоминает
self._aux_token_mask— плоскую маску настоящих новых токенов изattention_mask(для aux loss) илиNone. - :
self._dropout(self._token_embeddings(x)). - Цикл по
_decodersс передачей кэша своего слоя иpadding; приuse_cacheсобирает новый кэш. logits = self._linear(self._norm(out)); возвращает(logits, new_cache)или(logits, None).
auxiliary_loss():
if self._router_aux_loss_coef == 0: return Nonerouter_logits = [decoder._ff.router_logits for decoder in self._decoders]loss = load_balancing_loss(router_logits, self._num_experts, self._top_k_experts, self._aux_token_mask)return self._router_aux_loss_coef * lossМетод работает с данными последнего прямого прохода: логиты роутера хранит каждый MoE, маску — модель. Поэтому его вызывают сразу после forward на том же батче — так делают Trainer.train и HFGPTAdapter.forward (только в режиме обучения).
Подсчёт параметров
Заголовок раздела «Подсчёт параметров»Без bias (как в оригинале), с отдельной выходной проекцией:
где в скобках — веса двух RMSNorm блока. Эмбеддинги считаются «активными» условно: из матрицы берутся только строки токенов входа.
Mixtral 8x7B: , , , , , , , , .
| Часть | Формула | Параметров |
|---|---|---|
| attention одного слоя | 41 943 040 | |
| роутер одного слоя | 32 768 | |
| один эксперт | 176 160 768 | |
| 8 экспертов слоя | 1 409 286 144 | |
| RMSNorm слоя | 8 192 | |
| слой целиком | 1 451 270 144 | |
| 32 слоя | 46 440 644 608 | |
| эмбеддинги + | 262 144 000 | |
| финальная RMSNorm | 4 096 | |
| всего | 46 702 792 704 ≈ 46.7 млрд | |
| активных на токен | 12 879 925 248 ≈ 12.9 млрд |
Эксперты — 96.6% всех параметров (), attention — 2.9%, роутеры всех слоёв вместе — около миллиона. Результат совпадает с «47B всего, 13B активных» из статьи. Проверка на коде репозитория без выделения памяти — модель создаётся на meta-устройстве:
import torchfrom llm.models.mixtral import Mixtral
with torch.device("meta"): model = Mixtral({"vocab_size": 32000, "embed_dim": 4096, "num_q_heads": 32, "num_kv_heads": 8, "head_size": 128, "num_layers": 32, "max_position_embeddings": 32768, "num_experts": 8, "top_k_experts": 2, "dropout": 0.0, "intermediate_size": 14336, "bias": False, "rope_theta": 1e6, "rms_norm_eps": 1e-5})total = sum(p.numel() for p in model.parameters())one_expert = sum(p.numel() for p in model._decoders[0]._ff._experts[0].parameters())experts = sum(p.numel() for n, p in model.named_parameters() if "_experts" in n)print(total) # 46702792704print(total - experts + 32 * 2 * one_expert) # 12879925248Учебный конфиг mixtral_train.json: (BPE-словарь), , , , , , , , , bias включён. С bias каждый Linear получает ещё параметров:
| Часть | Параметров |
|---|---|
| attention слоя: | 197 376 |
| роутер слоя: | 2 056 |
| эксперт: | 788 736 |
| слой: | 6 509 832 |
| эмбеддинги + выход + финальная норма 256 | 513 256 |
| всего | 26 552 584 |
| активных: всего | 7 622 920 |
Отличия от Mixtral 8x7B
Заголовок раздела «Отличия от Mixtral 8x7B»Реализация учебная и сознательно маленькая, но часть отличий от оригинала меняет поведение модели. Подробности, воспроизведение и варианты исправления — в бэклоге (номера пунктов в скобках).
| Mixtral 8x7B | Здесь | |
|---|---|---|
| Внимание | плотное на весь контекст 32k | так же без ключа window_size; с ним — скользящее окно, как в Mistral 7B v0.1 (52) |
База RoPE (rope_theta) | 1 000 000 | 10 000 по умолчанию, задаётся ключом rope_theta (53) |
| Скрытый слой эксперта | hidden_dim = 14336 при dim = 4096 (3.5·d) | 4·d по умолчанию; intermediate_size: 14336 — как в оригинале (23, 30) |
| Bias | нет ни в одной проекции, включая роутер | во всех Linear, включая роутер, по умолчанию; bias: false — как в оригинале (24, 40) |
| Load-balancing loss | в HF-реализации при обучении (output_router_logits=True) | есть, ключ router_aux_loss_coef, по умолчанию выключен (37) |
| Dropout | нет | после эмбеддингов, в attention и один на выходе MoE (эксперты без собственного, 38); dropout: 0 убирает его полностью |
| Softmax роутера | во float32 (HF, эталон Mistral) | так же, явно во float32 с приведением к dtype входа (54) |
Конфигурация
Заголовок раздела «Конфигурация»Пример из experiments/llm_only/configs/mixtral_train.json — все ключи используются Mixtral.__init__; неверные сочетания (как у Mistral, а также top_k_experts вне 1 … num_experts) дают ValueError в конструкторе:
| Параметр | Значение в примере | Смысл |
|---|---|---|
vocab_size | (из токенизатора) | размер словаря |
embed_dim | 256 | размерность эмбеддингов |
num_q_heads | 4 | число Query-голов |
num_kv_heads | 2 | число Key/Value-голов |
head_size | 64 | необязательный размер головы; по умолчанию embed_dim // num_q_heads (тогда embed_dim обязан делиться на num_q_heads). Если задан, num_q_heads · head_size может не совпадать с embed_dim; для RoPE — чётный |
num_layers | 4 | число блоков MixtralDecoder |
max_position_embeddings | 512 | максимальная длина последовательности |
rms_norm_eps | (нет в примере) | необязательный eps всех RMSNorm, по умолчанию 1e-6; у Mixtral 8x7B — 1e-5 |
rope_theta | (нет в примере) | необязательная база частот RoPE, по умолчанию 10000; у Mixtral 8x7B — 1e6 (медленнее вращение, рассчитано на контекст 32k, см. llama.md) |
initializer_range | (нет в примере) | необязательное стандартное отклонение начальных весов Linear и Embedding, по умолчанию 0.02 — как в HF; см. training.md |
router_aux_loss_coef | (нет в примере) | необязательный коэффициент load-balancing loss роутера, по умолчанию 0 — выключен; в HF при включении — 0.001 |
num_experts | 8 | общее число экспертов MoE на слой |
top_k_experts | 2 | сколько экспертов активируется на токен |
window_size | (нет в примере) | необязательная ширина скользящего окна внимания, как в Mistral; без ключа окна нет — как в Mixtral 8x7B |
intermediate_size | (нет в примере) | необязательный скрытый размер каждого эксперта SwiGLU, по умолчанию 4 · embed_dim; у Mixtral 8x7B — 14336 |
bias | (нет в примере) | необязательный: bias во всех Linear, включая роутер и экспертов, по умолчанию true; в Mixtral 8x7B — false |
dropout | 0.1 | dropout после эмбеддингов, в attention и на выходе MoE (эксперты без собственного); в Mixtral 8x7B dropout нет — для соответствия оригиналу 0 |
Загрузка весов HuggingFace
Заголовок раздела «Загрузка весов HuggingFace»С ключами intermediate_size и "bias": false загружаются веса MixtralForCausalLM — той же функцией convert_hf_state_dict, что у LLaMA (реэкспорт в llm.models.mixtral); строки q_proj переставляются по num_attention_heads, k_proj — по num_key_value_heads; роутер block_sparse_moe.gate становится _ff._router, эксперты w1/w3/w2 — _gate/_up/_down.
from transformers import MixtralForCausalLMfrom llm.models.mixtral import Mixtral, convert_hf_state_dict
hf = MixtralForCausalLM.from_pretrained(...)c = hf.configconfig = {"vocab_size": c.vocab_size, "embed_dim": c.hidden_size, "num_q_heads": c.num_attention_heads, "num_kv_heads": c.num_key_value_heads, "head_size": c.head_dim or c.hidden_size // c.num_attention_heads, "num_layers": c.num_hidden_layers, "max_position_embeddings": c.max_position_embeddings, "num_experts": c.num_local_experts, "top_k_experts": c.num_experts_per_tok, "dropout": 0.0, "rms_norm_eps": c.rms_norm_eps, "rope_theta": c.rope_theta, "intermediate_size": c.intermediate_size, "bias": False}if c.sliding_window is not None: config["window_size"] = c.sliding_window - 1 # окно здесь на позицию шире, см. «Ширина окна: W + 1»model = Mixtral(config)model.load_state_dict(convert_hf_state_dict(hf.state_dict(), num_heads=c.num_attention_heads, num_kv_heads=c.num_key_value_heads))Роутер переносится без изменений: HF считает softmax по всем экспертам и перенормирует top-k, здесь — softmax по top-k логитам; веса совпадают (доказательство).
Сверено со случайными MixtralForCausalLM из transformers (GQA, 4 эксперта, top-2, без окна): логиты совпадают до ~1e-5, greedy-генерация с KV-кэшем дольше окна — токен в токен (llm/tests/models/test_mistral_mixtral_hf_parity.py). Настоящие веса (Mixtral 8x7B — около 90 ГБ) для проверки слишком велики.
Генерация
Заголовок раздела «Генерация»Mixtral.generate(...) — унифицированная сигнатура (см. gpt.md и generation.md). На каждом шаге генерации с KV-кэшем роутер каждого слоя вызывается на одном новом токене, и работают только его экспертов. Load-balancing loss при генерации не нужен: auxiliary_loss() вызывают только при обучении.
Дальше: Gemma
Заголовок раздела «Дальше: Gemma»Gemma возвращается к плотному FFN, но меняет другие детали той же базы RoPE + RMSNorm: GeGLU вместо SwiGLU, MQA в модели 2B, огромный словарь 256k с общей матрицей эмбеддингов и выхода и масштабирование эмбеддингов на .
- Mixtral 8x7B — это Mistral 7B, в котором каждый FFN заменён MoE-слоем: SwiGLU-экспертов, на токен работают с весами softmax по двум лучшим логитам роутера.
- Параметров 46,7 млрд, но на токен используется 12,9 млрд: вычисления как у плотной модели на 13 млрд, память — как у модели на 47 млрд.
- В оригинале внимание плотное на контекст 32k (без скользящего окна),
rope_theta = 1e6; в библиотеке окно включается только ключомwindow_size. - Load-balancing loss в статье не описан; здесь он включается
router_aux_loss_coef, как в HF. - Роутер переносится из HF без изменений: softmax по top-k логитам и перенормировка softmax по всем экспертам дают одинаковые веса.
Вопросы и упражнения
Заголовок раздела «Вопросы и упражнения»-
Почему Mixtral 8x7B содержит около 47, а не млрд параметров? Какая часть Mistral 7B «размножена» восемь раз?
Ответ
Размножен только FFN каждого слоя: 8 экспертов по млн. Attention (≈42 млн на слой), нормализации, эмбеддинги и выходная проекция общие и есть в одном экземпляре. Mistral 7B с теми же размерами содержит ≈7.24 млрд параметров, из них FFN — млн ≈ 5.64 млрд, остальное ≈1.60 млрд. Восемь копий Mistral дали бы ≈57.9 млрд, но не-FFN часть не копируется 7 лишних раз: млрд (плюс ≈1 млн параметров роутеров).
-
Сколько параметров добавилось бы к Mixtral 8x7B при экспертах (остальное прежнее)? Как изменилось бы число активных параметров при ?
Ответ
Добавляется параметров экспертов и параметров роутеров: всего млрд. Активных — почти столько же, млрд: выросли только роутеры.
-
Посчитайте долю FLOPs FFN-части одного слоя Mixtral 8x7B от FLOPs всего слоя без квадратичной части attention (на токен, число активных параметров слоя).
Ответ
Активные параметры слоя: attention 41 943 040, роутер 32 768, два эксперта 352 321 536 (нормализации пренебрежимы). Доля FFN: — около 89%.
-
В каком порядке в
MixtralDecoder.forwardприменяются нормализации и residual-связи? Запишите блок формулами и укажите, какая строка кода реализует каждую.Ответ
— строки
norm1_out = self._norm1(x),attention, kv_caches = self._heads(...),out = attention + x. —norm2_out = self._norm2(out),ffn_out = self._ff(norm2_out), возвратffn_out + out. -
Модель создана с
router_aux_loss_coef: 0.01. Вы вызываетеmodel(ids_a), затемmodel(ids_b), затемmodel.auxiliary_loss(). Для какого батча посчитан loss? ПочемуTrainerвызываетauxiliary_loss()сразу послеforward?Ответ
Для
ids_b: каждыйMoEперезаписываетself.router_logitsпри каждом проходе, модель —_aux_token_mask.Trainerвызываетauxiliary_loss()сразу послеforwardтого же батча, чтобы loss относился к нему и градиент прошёл по графу этого прохода. -
Почему MoE не участвует в KV-кэше, а attention — участвует?
Ответ
Attention нового токена смотрит на ключи и значения всех прошлых позиций — их и хранят в кэше. MoE, как и любой FFN, применяется к каждой позиции независимо: выход для нового токена зависит только от его собственного вектора, прошлые позиции не нужны.
-
(Код.) Создайте Mixtral 8x7B на
meta-устройстве (пример в разделе Подсчёт параметров) и посчитайте, какую долю параметров составляет attention. Затем повторите сnum_kv_heads: 32(MHA вместо GQA). На сколько выросла модель?Ответ
С GQA attention — , около 2.9%. С MHA K и V становятся : attention слоя , прирост — около 0.8 млрд, всего ≈47.5 млрд.
-
(Ноутбук.) В
notebooks/mixtral.ipynbобучите модель и после обучения посмотрите, как токены распределяются по экспертам в разных слоях (decoder._ff.router_logits). Видна ли «синтаксическая» специализация, о которой пишут авторы статьи, — например, одинаковые эксперты для знаков препинания?
Литература
Заголовок раздела «Литература»Основная статья:
- Jiang et al. Mixtral of Experts. 2024. arXiv:2401.04088
Компоненты:
- Shazeer et al. Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer. 2017. arXiv:1701.06538
- Fedus, Zoph, Shazeer. Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity. 2021. arXiv:2101.03961 — load-balancing loss для роутера
- Jiang et al. Mistral 7B. 2023. arXiv:2310.06825
- Ainslie et al. GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. 2023. arXiv:2305.13245
- Shazeer. GLU Variants Improve Transformer. 2020. arXiv:2002.05202 — SwiGLU
- Su et al. RoFormer: Enhanced Transformer with Rotary Position Embedding. 2021. arXiv:2104.09864
- Zhang, Sennrich. Root Mean Square Layer Normalization. 2019. arXiv:1910.07467
- Beltagy, Peters, Cohan. Longformer: The Long-Document Transformer. 2020. arXiv:2004.05150 — sliding window attention (в Mistral 7B; в Mixtral 8x7B не используется)