Перейти к содержимому

Нейросети простым языком

Привет!

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

Постарался написать свои объяснения, которые были бы не было слишком упрощены, но при этом по возможности понятны.

Статья на 10 процентов скомпилирована из других статей, на 30 процентов скомпилирована из множества диалогов с разными LLM и на 60 процентов “написана от руки” на основании статей и ответов.

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

Модель обучена на текстах «вопрос→ответ», поэтому при виде вопроса дальше генерируются не слова самого вопроса, а наиболее вероятное продолжение — ответ.

Во время инструкционного обучения (SFT) и RLHF её «поощряют» за полезные ответы, а не за повтор формулировки, поэтому параметры сдвигаются в сторону ответов.

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

Retrieval-Augmented Generation (RAG):
Модель при запросе ищет в актуальной внешней базе (поиск по векторному или текстовому индексу) релевантные документы и использует их контекст при генерации ответа. Так можно получать свежую информацию без перетренировки основной сети

Параметр-эффективное дообучение (LoRA, Adapters):
Вместо полного переобучения модели встраивают небольшие адаптеры или low-rank матрицы (LoRA), которые обучаются на новых данных. Это позволяет быстро и недорого «научить» модель новым фактам или доменам

Целевое редактирование весов (Model Editing):
Алгоритмы типа MEMIT/ROME локально корректируют веса модели для добавления или обновления конкретных фактов, не затрагивая остальную часть знаний

Отдельные базы знаний и графы:
Вместо хранения фактов внутри параметров LLM выносите их в внешние KB или графы знаний, которые регулярно обновляются, а модель лишь запрашивает у них информацию

Интеграция с веб-поиском и API:
Подключение к реальному времени через веб-браузерные плагины, сторонние API и сервисы поиска (например, ChatGPT Plugins, Bing Search API) напрямую возвращает свежее содержимое.

Модель “понимает” разные языки благодаря тому, что в процессе обучения она видит тексты на многих из них и учится предсказывать следующий фрагмент независимо от языка. Основные моменты:

Большой мультиязычный корпус:
Для обучения собирают тексты (википедия, книги, веб-страницы из Common Crawl и прочие) на десятках и сотнях языков. Например, в открытой модели BLOOM было около 46 языков, причём доля каждого зависит от объёма доступных данных

Общая токенизация subword:
Используются алгоритмы вроде BPE или SentencePiece, которые разбивают слова на фрагменты (subword) и включают в словарь символы и последовательности из разных алфавитов. Так модель оперирует единым набором токенов для всех языков

Универсальная архитектура трансформера:
В трансформере одни и те же веса участвуют при обработке любых языков. Поэтому при обучении на разных языках модель находит общие паттерны (синтаксис, семантика) и использует перекрёстное перенесение знаний (cross-lingual transfer)

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

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

Шаги работы нейросети:

LLM не работает со словами напрямую — она работает с токенами (частями слов или символами), преобразованными в числа.

Пример:

Текст пользователя:
"Привет, мир"


ID токена в словаре модели для слова:
"Привет" = 1123
"," = 15
"мир" = 345


Получаем массив ID токенов:
["Привет", ",", "мир"] = [1123, 15, 345]

Как это достигается:
Используется алгоритм вроде Byte Pair Encoding (BPE), Unigram, WordPiece или SentencePiece.
BPE-токенизатор находит наиболее частые пары символов.
WordPiece строит токены на основе вероятности иерархических разбиений.
Часто токены — это не отдельные слова, а части слов.

В словарь включены специальные маркеры начала слова (пробел, “##” и т. п.), поэтому ни один субтокен не пересекает границу двух слов.
Токенизация всегда идёт «жадно» — берётся максимально длинный совпадающий с началом оставшейся строки токен.

Пример:

Текст пользователя:
"невероятно"


ID токена в словаре модели для части слов:
"не" = 24
"вероят" = 126
"но" = 36
Получаем массив ID токенов:
["не", "вероят", "но"] = [24, 126, 36]


Либо еще более которкий вариант: 
("не»", "вер", "##оятно")

Разные по смыслу слова могут иметь общие токены, например «привет» и «пример» могут иметь общий токен «при»:
Хотя «при» повторяется в обоих словах, модель сразу смотрит не только на этот кусочек, но и на соседние токены и на всю фразу (об этом дальше в статье).
Сначала «при» превращается в вектор — просто набор чисел, описывающих эту часть слова.
Затем трансформер (многослойная сеть) смешивает этот вектор с векторами соседних токенов («вет» или «мер») и добавляет информацию о позиции в предложении.
В итоге в первом слое «при» в «привет» уже отличается от «при» в «пример», потому что туда «подмешался» разный контекст.
То есть общий кусочек «при» сам по себе нейтральный, а смысл формируется дальше по слоям на основе окружения.

Почему токены, а не слова:
Меньше словарь = экономия памяти.
Лучше обрабатываются редкие и составные слова.
Позволяет модели “учиться” понимать структуру слов.

Размер словаря, или “vocab_size”, определяет, сколько уникальных токенов может обрабатывать модель.
Больше словарь = меньше разбиений слов на части, но больше объём embedding-слоя.

Слово “программирование” может быть целиком одним токеном в модели с крупным словарём,
а может разбиться на части (“пр”, “ограм”, “мирование”) в модели с меньшим словарём.

Размеры словаря популярных моделей, количество токенов:

Модель                 | Размер словаря |
-----------------------|----------------|
LLaMA 1/2              | 32 000         |
LLaMA 3                | 128 000        |
Gemini 1.0/1.5         | ~4 МБ          |
Gemma 1/2              | 256 000        |
Gemma 3                | 262 000        |
Qwen 1.5               | 151 936        |
Qwen 2                 | 152 064        |
Qwen 2.5               | 152 064        |
DeepSeek LLM           | 102 400        |
DeepSeek-Coder         | 32 256         |
DeepSeek V2/V3         | 102 400        |
DeepSeek-R1            | 129 280        |

В отличие от текстовой токенизации (где токены — это слова, сабворды, символы), в изображениях токены — это фрагменты изображения или представления признаков. Ниже рассмотрены основные подходы.

Для изображений вместо токенов получают сразу матрицу (или тензор) пикселей.

В классических свёрточных сетях (CNN) маленькие участки картинки (например, 3×3 или 5×5 пикселей) скользят по изображению, и для каждого участка свёртка с набором фильтров выдаёт вектор признаков. Эти векторы собираются в карты признаков и передаются дальше.

В современных трансформерах для изображений (Vision Transformer) картинку разбивают на «пэчи» (квадраты, скажем, 16×16 пикселей), каждый пэтч выравнивают в вектор и тоже проецируют через матрицу эмбеддингов в вектор-представление, как токен в NLP.

Основные способы токенизации изображений:

Patch Embedding (разбиение на патчи) — классический подход ViT:
Изображение делится на сетку квадратных патчей, например, 16×16 пикселей.
Каждый патч разворачивается в вектор (flatten), потом линейно проецируется в эмбеддинг фиксированной размерности (например, 768).
В результате получается последовательность токенов: один токен на патч.

Пример:
224×224 RGB изображение с патчами 16×16 → 14×14 = 196 токенов + [CLS] токен.
Каждый токен: вектор размером 768.

CNN Feature Maps как токены:
Используются свёрточные сети (ResNet, ConvNext) для извлечения признаков.
Пространственные признаки (feature map) на выходе можно интерпретировать как токены, где каждый элемент сетки — вектор.
Применяется в CLIP и других гибридных моделях.

VQ-VAE / VQ-GAN токенизация (дискретная):
Кодировщик (энкодер) преобразует изображение в карту признаков и затем квантует её в дискретные токены (индексы из словаря).
Каждый токен — это индекс в словаре визуальных патчей.
Используется в DALL·E, Imagen, LLaVA и других мультимодальных генеративных моделях.
Плюсы: модель работает с «словами» визуального языка.
Минусы: потеря точности, неустойчивая генерация.

Segment/Region-based токены (DETR, Region Attention):
Изображение разбивается на смысловые регионы (сегментация, объекты).
Каждый регион преобразуется в токен с помощью агрегации признаков.
Используется в задачах детекции объектов и визуального ответа на вопросы (VQA).

Patch + Positional Encoding:
Как и в NLP, каждому патчу добавляется позиционная информация (абсолютная, относительная или learnable), чтобы сохранить пространственную структуру изображения.

Контекст — это оперативная память модели, размер контекста это максимальное количество токенов (слов, символов или их частей), которое языковая модель может обработать за один запрос.
Это «объём памяти», который модель может видеть одновременно, чтобы сформировать ответ. Всё, что выходит за пределы этого окна, модель забывает или не видит напрямую.

Размер контекста напрямую влияет на способность модели помнить предыдущие сообщения в диалоге.

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

Максимальная длина контекста популярных моделей:

| Модель                  | Максимальная длина контекста |
|-------------------------|------------------------------|
| LLaMA 1/2               |2 048 / 4 096                 |
| LLaMA 3                 |8 192                         |
| Gemma 1/2               |8 192                         |
| Gemma 3                 |128 000                       |
| Qwen 1.5                |32 768                        |
| Qwen 2                  |32 768                        |
| Qwen 2.5                |131 072                       |
| DeepSeek LLM            |4 096                         |
| DeepSeek-Coder          |16 384                        |
| DeepSeek V2/V3          |128 000                       |
| DeepSeek-R1             |131 072                       |
| Gemini 1.0/1.5          |32 768 / 1 000 000            |

В LLM модели для кажого ID токена хранится Token Embedding, или массив цифр. Эти числа описывают, что означает этот токен, как если бы ты переводил слово в математическую форму.

Изначально token embedding имеет фиксированное значение для каждого токена, но затем уточняется по мере прохождения через слои.

Когда Token Embedding проходит через несколько слоёв трансформера он становится контекстуализированным: он учитывает значение всей фразы. На выходе мы получаем вектор, который содержит “смысл” слова в контексте.

Связи между векторами задаются матрицами весов между слоями.

Изначально эти веса инициализируются случайно или по специальному правилу (например, Xavier или He), а потом во время обучения для каждого веса вычисляют градиент потери (насколько ошибка растёт или падает при его изменении).

Алгоритм обратного распространения (“backpropagation”) рассчитывает, как изменение каждого веса повлияет на итоговую ошибку, а затем градиентным спуском (или его вариациями) корректирует эти веса.

Так постепенно усиливаются связи между теми компонентами векторов, которые вместе дают полезную информацию, и ослабевают все ненужные — получается набор взаимосвязанных векторов, оптимальных для решения задачи.

Количество измерений у token embedding фиксировано для всей модели и зависит от её архитектуры.

Пример:

Текст пользователя:
"Привет"


ID токена в словаре модели для слова:
"Привет" = 1123


Token embedding для ID токена 1123 = массив значений в формате числа с плавающей точкой из d_model= 4096 элементов:
[0.034, 0.120, 0.905, ..., 0.028]

Размер embedding size популярных моделей:

Модель                 | d_model |
-----------------------|---------|
LLaMA 1 (7B)           | 4096    |
LLaMA 2 (13B)          | 5120    |
LLaMA 2 (70B)          | 8192    |
LLaMA 3 (8B)           | 4096    |
LLaMA 3 (70B)          | 8192    |
Gemini 1.0 Pro         | 6144    |
Gemini 1.0 Ultra       | 8192    |
Gemini 1.5 Pro         | 8192    |
Gemma 1 (2B)           | 2048    |
Gemma 1 (7B)           | 3072    |
Gemma 2 (9B)           | 4096    |
Gemma 2 (27B)          | 6144    |
Gemma 3 (12B)          | 5120    |
Gemma 3 (27B)          | 8192    |
Qwen 1.5 (7B)          | 4096    |
Qwen 2 (7B)            | 4096    |
Qwen 2.5 (7B)          | 4096    |
DeepSeek LLM (7B)      | 4096    |
DeepSeek LLM (67B)     | 8192    |
DeepSeek-Coder (6.7B)  | 4096    |
DeepSeek-Coder (33B)   | 8192    |
DeepSeek-R1            | 8192    |

Количество элементов вектора смысла влияет на:
Больше размерность = больше “места” для хранения семантики, синтаксиса, контекста.
Это позволяет различать более тонкие смыслы между токенами.
Размер памяти всех весов и активаций растёт квадратично с embedding size.

embedding size = d_model = 4096 
занимает в 4 раза больше памяти, чем 
embedding size = d_model = 2048

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

Обычно модели обучаются на полной точности FP32 (float32), то есть вектор состоит из чисел размерностью 32 бита.

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

Формат: FP32 (float32), 32 бита = 4 байта
Предназначение: Полная точность. Используется при обучении моделей, а также при точном инференсе.
Обеспечивает максимальную точность, но требует много памяти и вычислительных ресурсов.
Бинарное значение: 01000000 01001001 00001111 11011011
Фактическое значение: 3.14159
Возможные типы квантования: Не используется — это полный (не квантованный) формат.


Формат: FP16 (float16), 16 бит = 2 байта
Предназначение: Половинная точность. Используется для ускоренного обучения и вывода на GPU (например, NVIDIA Tensor Cores).
Быстрее и в 2 раза экономичнее по памяти по сравнению с FP32.
Бинарное значение: 01000000 10010000
Фактическое значение: ≈ 3.1406
Возможные типы квантования: QFloat16 (если используется адаптивное смешивание в mixed precision).


Формат: BF16 (bfloat16), 16 бит = 2 байта
Предназначение: Альтернатива FP16, используемая в TPU и некоторых GPU. Имеет ту же экспоненту, что и FP32, но укороченную мантиссу.
Быстрее и компактнее, при этом сохраняет диапазон FP32.
Бинарное значение: 01000000 10010000
Фактическое значение: ≈ 3.140625
Возможные типы квантования: QBFloat16 (редко используется напрямую, но встречается в TPU-инференсе).


Формат: INT8 (8 бит), scale = 0.125
Предназначение: Квантованное целое число. Используется в оптимизированных моделях для инференса на CPU и мобильных устройствах.
Требует восстановления масштаба (scale) и смещения (zero_point).
Квантизированное значение: 25
Бинарное значение: 00011001
Фактическое значение: 25 × 0.125 = 3.125
Возможные типы квантования: Q8_0, Q8_1, Int8Affine, PerChannelQuant (ONNX), dynamic/int8 (TensorFlow Lite)


Формат: INT4 (Q4), 4 бита, scale = 0.5 (2 числа в 1 байте)
Предназначение: Очень сжатый формат для языковых моделей. Используется в llama.cpp, GGUF и других системах.
Обеспечивает существенное уменьшение размера модели. Требует восстановления (деквантования) при запуске.
Квантизированное значение:
7 (максимальное значение для signed 4-битного int: -8…+7)
Бинарное значение: 0111
Фактическое значение: 7 × 0.5 = 3.5
Возможные типы квантования:
Q4_0, Q4_1, Q4_K, Q4_G, Q4_M (llama.cpp, GGUF)


Формат: INT2 (Q2), 2 бита, scale = 1.0 (4 числа в 1 байте)
Предназначение: Экстремально сжатый формат для использования в LLM на устройствах с ограниченными ресурсами.
Используется в некоторых вариантах GGUF, MLC, а также в экспериментах с экстремальным квантованием.
Квантизированное значение: 1 (максимум среди значений: -2…+1)
Бинарное значение: 01
Фактическое значение: 1 × 1.0 = 1.0
Возможные типы квантования: Q2_K (llama.cpp, GGUF)


Формат: INT1 (Q1), 1 бит, scale = 2.0 (8 чисел в 1 байте)
Предназначение: Минимально возможная точность. Используется в бинарных нейросетях и прототипах.
Обычно значения -1 или +1. Применяется редко в LLM, но может быть полезен в BNN (Binary Neural Networks).
Квантизированное значение: 1
Бинарное значение: 1
Фактическое значение: 1 × 2.0 = 2.0
Возможные типы квантования: Q1, BinaryNet, XNOR-Net (чаще в академических/экспериментальных BNN)

Также у моделей могут быть дополнительные параметры квантования:

_K
- Обозначает "K-блочную" квантизацию (K-Block Quantization).
- Веса разбиваются на блоки фиксированной длины (например, по 32 или 64 значения).
- Внутри каждого блока используется общий scale и zero_point.
- Это позволяет существенно уменьшить размер модели, сохраняя более высокую точность по сравнению с простой квантизацией.
- Примеры форматов: Q2_K, Q4_K, Q6_K, Q8_K


_0, _1
- Показывают, какую схему квантования применяют:
  - _0: базовая схема, без смещений (bias), один scale на блок
  - _1: улучшенная схема, с дополнительными bias или scale-сдвигами
- Используются в форматах: Q4_0, Q4_1, Q5_0, Q5_1, Q8_0, Q8_1
- Как правило, _1 обеспечивает лучшую точность при незначительном увеличении размера


Метки уровня сжатия/точности: K_L, K_M, K_S, K_G


K_L
- Kvantization Low
- Низкий размер модели, минимальная точность
- Подходит для устройств с крайне ограниченными ресурсами
- Максимально агрессивная квантизация


K_M
- Kvantization Medium
- Средний компромисс между точностью и объёмом
- Подходит для большинства локальных задач


K_S
- Kvantization Small
- Модель максимально сжата по размеру, даже в ущерб качеству
- Часто используется как референс для экстремального сжатия


K_G
- Kvantization General
- Сбалансированная модель: разумный компромисс между скоростью, качеством и размером
- Хорошо работает на большинстве CPU


Пример названия модели:
"mistral-7b.Q4_K_M.gguf" — это означает:
- Модель Mistral 7B
- Используется квантование Q4_K (4-битная K-блочная)
- Уровень компромисса: Medium

QAT (Quantization-Aware Training) квантование:
это метод квантования нейронных сетей, при котором квантование учитывается уже во время обучения модели. Он позволяет добиться почти такой же точности, как и у оригинальной модели с float-параметрами, при этом модель будет использовать более компактные int8 или другие низкоразрядные форматы, пригодные для эффективного запуска на устройствах с ограниченными ресурсами (например, смартфонах или микроконтроллерах).

Как работает QAT — поэтапно:

Модель в float32:
Обучение начинается с обычной модели, использующей числа с плавающей точкой (обычно float32). Это обеспечивает высокую точность и стабильность обучения.
Имитация квантования во время forward pass (fake quantization):
При каждом проходе вперёд значения (веса, активации) эмулируются как квантованные, т.е. они преобразуются в int8, а затем обратно в float32. Это даёт возможность модели «увидеть» ошибки квантования ещё на стадии обучения.

float32 → int8 → float32

Таким образом, во время обратного распространения (backpropagation) градиенты считаются по float32-версии, но ошибки из-за квантования всё равно влияют на обучение.
Обновление параметров (backward pass):
Градиенты считаются как обычно, но с учётом искажений от fake quantization. Это позволяет модели адаптироваться к тому, что веса и активации будут впоследствии использоваться в низкой точности.
Экспорт финальной модели в int8:
После завершения обучения веса действительно квантуются в int8, и модель может быть скомпилирована и запущена в производственном окружении.

Что именно квантуется:
Веса (weights) — float32 → int8
Активации (activations) — float32 → int8
(Иногда также квантуются градиенты и промежуточные состояния, но это редкость.)

Зачем использовать QAT:
Выше точность, чем у post-training quantization (PTQ)
Низкое потребление памяти
Быстрее выполнение на CPU/GPU/NPUs с поддержкой int8
Особенно важно для мобильных и embedded-устройств (например, Android)

Далее модель отправляется в трансформер.

Архитектура нейросети, которая стала основой для современных языковых моделей, таких как ChatGPT, BERT, LLaMA, Gemma и многие другие.
Он был впервые описан в научной статье 2017 года под названием “Attention is All You Need”.
Проще говоря, трансформер — это “умная машина”, которая умеет читать и понимать текст, обрабатывая все слова одновременно, а не по одному, как это делали предыдущие модели (например, RNN, LSTM).

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

Один слой (или “трансформер-блок”) включает в себя:

Self-Attention:
токен “смотрит” на другие токены и решает, кто важен.

Feed-Forward Network:
уточнение и преобразование каждого токена.

Layer Normalization или LayerNorm:
стабилизация вычислений.

Residual Connections:
чтобы модель не “забыла” начальную информацию.

LayerNorm может быть до или после Residual (Pre-LN vs Post-LN).

и другие этапы.

Каждый слой трансформера (Transformer Block) — это отдельный набор параметров, которые:
независимо обучаются,
независимо применяются к входным данным,
дают всё более “глубокое” понимание смысла и контекста.

Что значит “32 слоя”:
каждый токен проходит через 32 такие операции, последовательно одну за другой, после каждого слоя токен становится всё более информированным, т.е. его представление (вектор) всё глубже отражает контекст

Количество слоев моделей (transformer layers):

| Модель                | Количество слоёв |
|-----------------------|------------------|
| LLaMA 1 (7B)          | 32               |
| LLaMA 2 (7B)          | 32               |
| LLaMA 2 (13B)         | 40               |
| LLaMA 2 (70B)         | 80               |
| LLaMA 3 (8B)          | 32               |
| LLaMA 3 (70B)         | 80               |
| Gemini 1.0 Pro        | 32               |
| Gemini 1.0 Ultra      | 64               |
| Gemini 1.5 Pro        | 64               |
| Gemma 1 (2B)          | 18               |
| Gemma 1 (7B)          | 28               |
| Gemma 2 (9B)          | 32               |
| Gemma 2 (27B)         | 40               |
| Gemma 3 (12B)         | 36               |
| Gemma 3 (27B)         | 48               |
| Qwen 1.5 (7B)         | 32               |
| Qwen 2 (7B)           | 28               |
| Qwen 2.5 (7B)         | 28               |
| DeepSeek LLM (7B)     | 30               |
| DeepSeek LLM (67B)    | 95               |
| DeepSeek-Coder (6.7B) | 32               |
| DeepSeek-Coder (33B)  | 64               |
| DeepSeek-R1			| 95               |

это промежуточные выходы нейросети после применения функций и слоев).
Можно сказать, что активации это данные, которые “живут внутри сети” на каждом этапе прохода входа через модель.

в нейросети это многомерный массив чисел, с которым работают слои модели.
Входные данные:
Текст → токены → эмбеддинги → тензор batch_size × seq_len × embedding_dim.
Веса модели:
Весовые матрицы в слоях — тоже тензоры.
Промежуточные представления (активации):
Выход каждого слоя (например, LayerNorm, Attention) — тензор.
Градиенты:
При обучении модель считает градиенты (тензоры) для обновления весов.

Без дополнительной информации фразы:
“Кот ест рыбу”
“Рыбу ест кот”
могли бы восприниматься одинаково, ведь набор слов одинаков.

Чтобы дать модели ощущение порядка, каждому токену (слову или части слова) добавляется позиционный вектор — набор чисел, который сообщает модели на каких позициях находятся слова.

Token embedding + Positional Encoding / Embeddings = Суммарный вектор

Positional Encoding / Embedding это:
Вектор такой же размерности, как у токена (d)
Представляет позицию в последовательности
Может быть задан формулой (sin/cos) или обучаем
Объединяется с вектором токена на входе в модель

Какой тип используется в моделях:

| Метод                       | Модель/семейство            | Описание                                      |
|-----------------------------|-----------------------------|-----------------------------------------------|
| Sinusoidal Encoding         | Transformer                 | Не обучаются, основаны на синусах и           |
|                             | (Vaswani et al., 2017)      | косинусах с разной частотой                   |
|-----------------------------|-----------------------------|-----------------------------------------------|
| Learnable Embeddings        | BERT, GPT-2,                | Обучаемая таблица позиций, похожа на          |
|                             | DistilBERT, ELECTRA         | эмбеддинги слов                               |
|-----------------------------|-----------------------------|-----------------------------------------------|
| Rotary Positional Embedding | GPT-NeoX, LLaMA, LLaMA 2/3, | Вращение векторов — сохраняет относительные   |
| (RoPE)                      | ChatGLM, Mistral            | позиции между токенами                        |
|-----------------------------|-----------------------------|-----------------------------------------------|
| ALiBi                       | OPT, BLOOM, Phi-2           | Линейный bias, добавляется к attention score, |
|                             |                             | не требует хранения позиций                   |
|-----------------------------|-----------------------------|-----------------------------------------------|
| Relative Position Bias      | T5, DeBERTa, Transformer-XL,| Использует смещения между токенами вместо     |
|                             | Pegasus, LongT5             | абсолютных позиций                            |

Пример:

Текст пользователя:
"Привет, мир"


ID токена в словаре модели для слова:
"Привет" = 1123 
"," = 15  
"мир" = 345
Получаем массив ID токенов:
["Привет", ",", "мир"] = [1123, 15, 345]


Token embedding из базы модели для:
ID токена 1123 = [0.034, 0.120, 0.905, ..., 0.028]
ID токена 15 = [0.022, -0.010, -0.313, ..., 0.117]
ID токена 345 = [-0.102, 0.241, 0.543, ..., 0.055]


Позиция ID токенa слова в тексте пользователя:
[1123, 15, 345] = [позиции 0, позиции 1, позиции 2]


Вычисляем или получаем из базы модели вектор позиции:
Вектор позиции 0 = [0.001, 0.087, -0.432, ..., 0.019]
Вектор позиции 1 = [0.005, -0.013, 0.021, ..., -0.012]
Вектор позиции 2 = [-0.003, 0.099, -0.082, ..., 0.003]


Суммарный вектор для ID 1123:
Token embedding [0.034, 0.120, 0.905, ..., 0.028] +
Вектор позиции 0 [0.001, 0.087, -0.432, ..., 0.019] =
[0.035, 0.207, 0.473, ..., 0.047]
аналогично для остальных токенов

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

С точки зрения математики:

pos - позиция токена в последовательности (начиная с 0)
i - индекс измерения вектора позиционного кодирования (начиная с 0)
d_model - размерность вектора позиционного кодирования (размерность эмбеддинга)
PE(pos, i) - i-й элемент вектора позиционного кодирования для позиции pos.
10000 - это гиперпараметр. Он используется для масштабирования позиции и частоты синусоид. Выбор этого значения позволяет модели легко экстраполировать на последовательности, длиннее тех, на которых она была обучена.


Тогда i-й элемент вектора позиционного кодирования для позиции pos:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))

Формула использует разные частоты (длины волн) синусоидальных функций для разных измерений вектора позиционного кодирования.
Четные измерения используют синус, а нечетные — косинус. Это позволяет модели различать позиции по разным фазам и амплитудам.
Деление pos на 10000^(2i/d_model) уменьшает частоту синусоиды с увеличением индекса измерения i.
Это создает более медленные колебания для более высоких измерений, что позволяет модели различать позиции на разных масштабах.

Пример:

pos = 0 (первый токен)
d_model = 4 (размерность вектора позиционного кодирования)


Тогда вектор позиционного кодирования PE(0) будет иметь размерность 4.  
Вычислим каждый элемент:


i = 0:
PE(0, 0) = sin(0 / 10000^(2*0/4)) = sin(0) = 0
PE(0, 1) = cos(0 / 10000^(2*0/4)) = cos(0) = 1


i = 1:
PE(0, 2) = sin(0 / 10000^(2*1/4)) = sin(0) = 0
PE(0, 3) = cos(0 / 10000^(2*1/4)) = cos(0) = 1


PE(0) = [0, 1, 0, 1]


Теперь вычислим вектор позиционного кодирования для pos = 1:


i = 0:
PE(1, 0) = sin(1 / 10000^(2*0/4)) = sin(1) ≈ 0.8415
PE(1, 1) = cos(1 / 10000^(2*0/4)) = cos(1) ≈ 0.5403


i = 1:
PE(1, 2) = sin(1 / 10000^(2*1/4)) = sin(0.01) ≈ 0.01
PE(1, 3) = cos(1 / 10000^(2*1/4)) = cos(0.01) ≈ 1


PE(1) = [0.8415, 0.5403, 0.01, 1]

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

Относительные позиционные смещения:
Вместо того чтобы кодировать абсолютную позицию каждого токена, некоторые варианты трансформеров вводят относительные позиционные смещения. Это позволяет модели напрямую учитывать расстояние (и направление) между парами токенов, а не их «глобальную» позицию в последовательности.

Зачем нужны относительные смещения:
При генерации или обработке длинных текстов важно, как далеко друг от друга лежат слова, а не только их абсолютные индексы.
Абсолютные эмбеддинги плохо обобщаются на более длинные последовательности, чем те, на которых модель обучалась.
Относительные смещения дают большую гибкость: модель учится, например, «что на 3 позиции вправо может быть объект действия», независимо от того, где это предложение стоит в тексте.

Transformer-XL, относительные позиции через смещение ключей и запросов:

В классическом self-attention считаем:
score_{i,j} = (Q_i · K_j) / sqrt(d_k)


В Transformer-XL вводят два дополнительных набора эмбеддингов:
E^R[r] — эмбеддинг для относительного сдвига r = j - i
U, V — два вектора-сдвига


Итоговая формула:


Q_i·E^R[j-i] - контент-зависимая часть внимания, учитывающая относительное смещение
V·E^R[j-i] - контент-независимая часть, задающая базовый bias для данного смещения


score_{i,j} = (1/√d_k) * (Q_i·K_j + Q_i·E^R[j-i] + U·K_j + V·E^R[j-i])

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

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

Self-Attention отвечает за понимание контекста — какие токены важны друг для друга, и позволяет каждому токену взвешенно смотреть на все остальные в последовательности и определять, на что обратить внимание при построении смысла.

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

softmax(zᵢ) = exp(zᵢ) / ∑ⱼ exp(zⱼ)

Модель создаёт три представления для каждого токена:

Q (вопрос): что я ищу?
K (ключ): что я могу предложить?
V (значение): какую информацию я несу?

Каждое слово сравнивает свой Q с K всех остальных слов, чтобы узнать, на кого стоит смотреть. После этого оно собирает нужную информацию из V тех слов, которые оказались важными.

Для вычисления представлений используется матрица весов слоя- это обучаемый параметр нейросети, то есть она инициализируется случайно при создании модели и обучается вместе с остальными весами. Изначально случайная, потом становится «умной» за счёт обучения.

Как они обучаются:
На этапе обратного распространения (backpropagation) модель сравнивает свои предсказания с правильным ответом (например, следующий токен) и обновляет Wq, Wk, Wv по градиенту ошибки с помощью оптимизатора (например, Adam).

Параметры Wq, Wk и Wv могут быть общими или различными для каждой из голов, в зависимости от модели.

Wq (Query Projection):
Создаёт “вопрос” — что токен хочет найти в других токенах.
Определяет направление внимания.

Q токена = Суммарный вектор токена X * Вес слоя Wq

Wk (Key Projection):
Создаёт “ключ” — что каждый токен предлагает другим.
Используется для сравнения с Q (насколько Qᵢ “похож” на Kⱼ).

K токена = Суммарный вектор токена X * Вес слоя Wk

Wv (Value Projection):
Создаёт “информацию”, которую токен может передать, если на него обратили внимание.

V токена = Суммарный вектор токена X * Вес слоя Wv

Голова внимания (attention-head):
Также у моделей существуют Голова внимания (attention-head). Каждая голова внимания (attention-head) обрабатывает входной вектор по-своему, через свои Q, K, V проекции, и смотрит на разные аспекты предложения.

У каждой attention-head есть своя точка зрения:
одна голова может отслеживать грамматику (например, подлежащее и сказуемое),
другая — смысловые связи (например, кто на что действует),
третья — позиции, контекст и т. д.
Вместо одной “точки зрения” — сразу несколько.

Каждая голова видит весь текст, но — анализирует его по-своему, через проекцию и внимание.

Пример:
В предложении
“Мальчик, который держал мяч, убежал.”
Разные головы могут видеть
Голова 1: “мальчик” ↔ “убежал” → кто выполняет действие
Голова 2: “который” ↔ “держал” → вложенная грамматическая связь
Голова 3: “мяч” ↔ “держал” → объект действия
Каждая голова выдаёт своё представление для каждого токена, с учётом своих “наблюдений”.

Архитектура Multi-Head Attention (MHA):
Классическая реализация self-attention, как в оригинальной статье “Attention is All You Need”.
Что происходит:
Есть несколько голов
Каждая голова имеет свои Wq, Wk, Wv
Каждая голова по-своему анализирует весь контекст
Результаты всех голов объединяются и проходят через общую Wo
Плюсы:
Гибкость: каждая голова “смотрит” на вход по-своему
Отлично работает при больших вычислительных ресурсах
Минусы:
Очень дорого по памяти и скорости при большом количестве голов
Особенно при длинных последовательностях

Архитектура Multi-Query Attention (MQA):
Оптимизированная версия внимания, используемая в GPT-3.5, PaLM, Gemma и других, чтобы снизить нагрузку на память и ускорить инференс.
Что происходит:
По одной Wq на голову
Только один Wk и один Wv
Все головы используют одни и те же ключи и значения
Плюсы:
Меньше памяти: K и V хранятся в одном экземпляре
Быстрее генерация: меньше данных хранится между шагами
Минусы:
Меньше гибкости (все головы “смотрят” на одинаковые K и V)
Может слегка ухудшить качество на сложных задачах

Архитектура Grouped Query Attention (GQA):
комбинированный поход из предыдущих 2х

Пример:
Например, в архитектуре нейросети Gemma 3 используется Grouped Query Attention (GQA)- компромисс между стандартным Multi-Head Attention (MHA) и Multi-Query Attention (MQA). В этой схеме матрицы Wq (для запросов) различны для каждой головы, тогда как матрицы Wk (для ключей) и Wv (для значений) могут быть общими для групп голов.
В этой нейросети:
Wq: каждая голова имеет свою уникальную матрицу Wq, что позволяет каждой голове фокусироваться на различных аспектах входной последовательности.
Wk и Wv: головы делятся на группы, и внутри каждой группы используется общая матрица Wk и Wv. Это снижает объем вычислений и памяти, необходимых для хранения ключей и значений.
Это означает, что 8 голов запросов (Wq) делятся на 4 группы, каждая из которых использует общие матрицы ключей и значений (Wk и Wv).

В итоге, модель Gemma 3 размером 27B имеет
80 слоев и 64 головы, внутри будут:

64 разных Wq (Query Projection)
8 разных Wk (Key Projection)
8 разных Wv (Value Projection)
64 разных Wo (Output Projection)
80 MLP с разными весами (Feed-Forward Network)
80 LayerNorm с разными гамма/бета-параметрами

Количество голов внимания (attention heads) популярных моделей:

| Модель                  | Кол-во attention heads |
|-------------------------|------------------------|
| LLaMA 1/2               | 32                     |
| LLaMA 3                 | 32 / 64 / 128          |
| Gemini 1.0/1.5          | неизвестно             |
| Gemma 1/2               | 16                     |
| Gemma 3                 | 8                      |
| Qwen 1.5                | 40 (Q) / 8 (KV)        |
| Qwen 2                  | 40 (Q) / 8 (KV)        |
| Qwen 2.5                | 40 (Q) / 8 (KV)        |
| DeepSeek LLM            | 32 (7B) / GQA (67B)    |
| DeepSeek-Coder          | 16 / 32 / 56           |
| DeepSeek V2/V3          | 128                    |
| DeepSeek-R1             | 128                    |

Каузальное маскирование (Causal Masking):
применяется в некоторых моделях. Когда языковая модель (LLM) генерирует текст, она должна предсказывать следующее слово на основе только предыдущих слов.
Это означает, что Токен не должен иметь доступ к токенам, которые идут после него.
Чтобы обеспечить это ограничение, используется каузальное маскирование — это механизм, который “запрещает” вниманию видеть будущие токены.

Как это работает:
В обычном self-attention каждый токен “смотрит” на все токены в последовательности, включая и будущие.
При генерации это неприемлемо, потому что это было бы “жульничеством” — модель видит ответ заранее.
Чтобы это предотвратить, при вычислении attention применяют маску — специальную матрицу, называемую attention mask.
Для последовательности длины n создаётся треугольная маска, в которой:
Значения выше диагонали заменяются на -∞ или большое отрицательное число.
После этого применяется softmax, и эти значения превращаются в нулевое внимание.

Пример маски (для n = 4 токенов):
[
 [0, -∞, -∞, -∞],
 [0,  0, -∞, -∞],
 [0,  0,  0, -∞],
 [0,  0,  0,  0]
]


Это означает:
Токен 0 видит только себя.
Токен 1 видит себя и токен 0.
Токен 2 видит себя, токен 1 и токен 0.
Токен 3 видит всё, что до него.

Где используется Causal Masking:
GPT, LLaMA, Mistral, Gemma и прочие автогенеративные модели обязательно используют каузальное маскирование.
BERT, наоборот, использует bidirectional attention — токен может видеть весь контекст (в т.ч. будущее), потому что задача другая — не генерация, а понимание.

Почему это важно, без causal masking:
Модель при обучении будет “подглядывать” на правильный ответ (следующий токен).
Это приведёт к плохой генерации при использовании модели в inference, когда будущее неизвестно.

Роль позиции в Self-Attention:
Без Position Encoding, Self-Attention видит только смысл слов, но не их порядок. С добавленным Position Encoding, Self-Attention начинает учитывать не только, что написано, но и где это находится:
“Мальчик” раньше “читал” → возможно, он субъект
“Книга” рядом с “читал” → скорее всего, объект

Что в итоге делает трансформер:
Каждое слово получает информацию о своей позиции
Через self-attention каждое слово “спрашивает” все остальные:
“Что вы значите для меня в этом контексте?”
Модель складывает эти ответы и получает глубокое понимание смысла всей фразы
Повторяет это на каждом слое (обычно 12–40 раз), углубляя “понимание”

Текст пользователя:
"Привет, мир"


ID токена из словаре модели:
"Привет" = 1123 
"," = 15 
"мир" = 345
Получаем массив ID токенов:
["Привет", ",", "мир"] = [1123, 15, 345]


Token embedding или вектор внимания из базы модели:
"Привет" = ID 1123 = [0.034, 0.120, 0.905, ..., 0.028]
"," = ID 15 = [0.022, -0.010, -0.313, ..., 0.117]
"мир" = ID 345 = [-0.102, 0.241, 0.543, ..., 0.055]


Вычисляем или получаем из базы модели вектор позиции:
Вектор позиции "Привет" = [0.001, 0.087, -0.432, ..., 0.019]
Вектор позиции "," = [0.005, -0.013, 0.021, ..., -0.012]
Вектор позиции "мир" = [-0.003, 0.099, -0.082, ..., 0.003]


Для "Привет":
Token embedding [0.034, 0.120, 0.905, ..., 0.028] +
Вектор позиции [0.001, 0.087, -0.432, ..., 0.019] =
Суммарный вектор X [0.035, 0.207, 0.473, ..., 0.047]


Вычисляем матрицу весов для слоя для каждого токена и для каждой головы внимания:
Q = X * Wq головы
K  = X * Wk головы
V = X * Wv головы
где X это суммарный вектор для токена
Wq, Wk, Wv - общие и одинаковые для всех токенов в этом слое,
но разные или частично разные для каждой головы


Например:
Wq головы = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9], [1.0, 1.1, 1.2]]
Wk головы = [[0.12, 0.22, 0.32], [0.42, 0.52, 0.62], [0.72, 0.82, 0.92], [1.02, 1.12, 1.22]]
Wv головы = [[0.11, 0.21, 0.31], [0.41, 0.51, 0.61], [0.71, 0.81, 0.91], [1.01, 1.11, 1.21]]


Тогда:


Q "Привет" = x * Wq = [0.035, 0.207, 0.473, 0.047] * Wq


Q[0] = 0.035 * 0.1 + 0.207 * 0.4 + 0.473 * 0.7 + 0.047 * 1.0
     ≈ 0.0035 + 0.0828 + 0.3311 + 0.047
     ≈ 0.4644


Q[1] = 0.035*0.2 + 0.207*0.5 + 0.473*0.8 + 0.047*1.1
     = 0.007 + 0.1035 + 0.3784 + 0.0517
     ≈ 0.5406


Q[2] = 0.035*0.3 + 0.207*0.6 + 0.473*0.9 + 0.047*1.2
     = 0.0105 + 0.1242 + 0.4257 + 0.0564
     ≈ 0.6168


Q = [0.4644, 0.5406, 0.6168]


аналогичым образом вычисляем для "Привет":
K = [0.47964, 0.55684, 0.63204]
V = [0.47299, 0.54822, 0.62442]
и для других токенов

Вычисление Attention Score между токенами:
На этом этапе мы получили Q, K и V для одного токена и одной головы внимания. Далее можно перейти к вычислению attention score между токенами, например, между “Привет” и “мир”, с использованием формулы:

С точки зрения математики:

Q - матрица запросов (Queries)
K - матрица ключей (Keys)
V - матрица значений (Values)
d_k - размерность вектора


Attention(Q, K, V) = softmax((Q * Kᵀ) / √d_k + mask) * V


Q * Kᵀ - матричное умножение Q на транспонированную матрицу K. 
Это вычисляет "сырые" веса внимания между каждым запросом и каждым ключом.


√d_k - корень квадратный из размерности вектора ключа (dimension of key vectors). 
Используется для масштабирования, чтобы предотвратить слишком большие значения 
в softmax, что может привести к проблемам с градиентами. 
Это называется Scaled Dot-Product Attention.


mask - это матрица, которая используется для ограничения внимания.
В генеративных моделях применяется causal mask (каузальное маскирование) —
она не позволяет токену "видеть вперёд" при вычислении внимания. 
Это критично для задач, где модель предсказывает следующий токен.


softmax(...) - функция softmax применяется к результату деления Q ⋅ Kᵀ 
на √d_k (и добавления mask, если она есть). Это нормализует веса внимания 
так, чтобы они суммировались в 1 для каждого запроса.


Умножение результата softmax на V дает взвешенную сумму значений, 
где веса определяются attention score.

Пример:

Входные данные:


| Токен    | Q-вектор                 | K-вектор                 |  V-вектор              |
|----------|--------------------------|--------------------------|------------------------|
| "Привет" | [0.4644, 0.5406, 0.6168] | [0.4796, 0.5568, 0.6320] |  [0.473, 0.548, 0.624] |
|----------|--------------------------|--------------------------|------------------------|
| ","      |[0.2, 0.3, 0.1]           | [0.25, 0.35, 0.15]       |  [0.12, 0.09, 0.04]    |
|----------|--------------------------|--------------------------|------------------------|
| "мир"    | [0.55, 0.33, 0.77]       | [0.5213, 0.5967, 0.6721] |  [0.55, 0.65, 0.75]    |

  
Для простоты используем размерность d_k для Q, K = 3 (для примера)


Вычисление:


Привет–Привет: 0.4644*0.4796 + 0.5406*0.5568 + 0.6168*0.6320 ≈ 0.2228 + 0.3011 + 0.3899 = 0.9138


Привет–",": 0.4644*0.25 + 0.5406*0.35 + 0.6168*0.15 ≈ 0.1161 + 0.1892 + 0.0925 = 0.3978


Привет–мир: 0.4644*0.5213 + 0.5406*0.5967 + 0.6168*0.6721 ≈ 0.2422 + 0.3225 + 0.4147 = 0.9794


","–Привет: 0.2*0.4796 + 0.3*0.5568 + 0.1*0.6320 ≈ 0.0959 + 0.1670 + 0.0632 = 0.3261


","–",": 0.2*0.25 + 0.3*0.35 + 0.1*0.15 = 0.05 + 0.105 + 0.015 = 0.17


","–мир: 0.2*0.5213 + 0.3*0.5967 + 0.1*0.6721 ≈ 0.1043 + 0.1790 + 0.0672 = 0.3505


мир–Привет: 0.55*0.4796 + 0.33*0.5568 + 0.77*0.6320 ≈ 0.2638 + 0.1837 + 0.4876 = 0.9351


мир–",": 0.55*0.25 + 0.33*0.35 + 0.77*0.15 ≈ 0.1375 + 0.1155 + 0.1155 = 0.3685


мир–мир: 0.55*0.5213 + 0.33*0.5967 + 0.77*0.6721 ≈ 0.2867 + 0.1969 + 0.5185 = 1.0021


Делим на √d_k = √3 ≈ 1.732


| From \ To | Привет                  | ","                     | мир                     |
| --------- | ----------------------- | ----------------------- | ----------------------- |
| Привет    | 0.9138 / 1.732 ≈ 0.5275 | 0.3978 / 1.732 ≈ 0.2296 | 0.9794 / 1.732 ≈ 0.5652 |
| ","       | 0.3261 / 1.732 ≈ 0.1882 | 0.17 / 1.732 ≈ 0.0982   | 0.3505 / 1.732 ≈ 0.2023 |
| мир       | 0.9351 / 1.732 ≈ 0.5397 | 0.3685 / 1.732 ≈ 0.2127 | 1.0021 / 1.732 ≈ 0.5786 |

  
Применим экспоненту для "Привет":
exp(0.5275) ≈ 1.694
exp(0.2296) ≈ 1.258
exp(0.5652) ≈ 1.759


Сумма всех экспонент для "Привет":


1.694 + 1.258 + 1.759 ≈ 4.711


softmax = exp(x) / сумма всех exp(x)


Softmax вес для "Привет" относительно других токенов:


| Токен  | Softmax вес          |
| ------ | -------------------- |
| Привет | 1.694 / 4.711 ≈ 0.36 |
| ","    | 1.258 / 4.711 ≈ 0.27 |
| мир    | 1.759 / 4.711 ≈ 0.37 |

  
Когда "Привет" генерирует своё представление (на выходе attention слоя), он:
берёт 36% информации от самого себя,
27% от ",",
и 37% от слова "мир".


Взвешенное суммирование:


Output= 0.36 ∗ V("Привет") + 0.27 ∗ V(",") + 0.37 ∗ V("мир")


Координата 1 = 0.36∗0.473+0.27∗0.12+0.37∗0.55≈0.1703+0.0324+0.2035 ≈ 0.4062
Координата 2 = 0.36∗0.548+0.27∗0.09+0.37∗0.65≈0.1973+0.0243+0.2405 ≈ 0.4621
Координата 3 = 0.36∗0.624+0.27∗0.04+0.37∗0.75≈0.2246+0.0108+0.2775 ≈ 0.5129


Attention Score для токена "Привет": [0.4062, 0.4621, 0.5129]

Этот вектор Attention Score — новое представление токена “Привет”, в котором учтён его контекст: и сам он, и соседи. Именно такие векторы потом идут либо в следующий attention-слой, либо на выход модели.

Сравнение токена сам с собой нужно потому, что в self-attention каждый токен “смотрит” на все токены, включая сам себя.
Это необходимо для того, чтобы:
Сохранить информацию о самом токене — иначе он бы “терялся” на фоне остальных.

Научиться “усиливать” или “подавлять” себя — например, в некоторых языковых ситуациях токен важен сам по себе (например, личное местоимение), а иногда его контекст важнее.
Обеспечить симметрию — матрица внимания всегда квадратная (n × n), и по диагонали — это как раз self-to-self attention.

Формально attention — это веса, по которым токен агрегирует информацию от других токенов (в т.ч. от самого себя) и w1 — это внимание “Привет” к самому себе. Если его не считать — токен “Привет” вообще не участвовал бы в своем собственном выходе.

Пример:
Он сказал, что он придет.
Когда модель обрабатывает токен “он”, ей нужно:
“посмотреть” на другие токены — чтобы понять контекст,
но и сам токен “он” тоже важен, чтобы не потерять информацию о том, о ком идет речь.

Материалы:

Wikipedia: Attention (machine learning)

Wikipedia: Softmax function

Understanding Q,K,V In Transformer( Self Attention)

What is Query, Key, and Value (QKV) in the Transformer Architecture and Why Are They Used?

Предыдущие рассчеты происходили для каждой из голов внимания, теперь необходимо объединить результаты. Напомню, результаты разные из за разных Wq, Wk, Wv для разных голов.

С точки зрения математики:

h - количество голов внимания
d_k - размерность вектора Attention Score для каждой головы
AttentionScore_i - вектор Attention Score для i-й головы.


Concatenation (Объединение векторов в один вектор по координатам):


concat = [AttentionScore_1, AttentionScore_2, ..., AttentionScore_h]

Пример:

Входные данные: 
Допустим, размерность: 6 (2 головы × по 3 значения)


Количество голов внимания h = 2
Размерность вектора d_k = 3
"Привет" для головы 1 AttentionScore_1 = [0.4062, 0.4621, 0.5129]
"Привет" для головы 2 AttentionScore_2 = [0.22, 0.33, 0.44]


Вычисление:


Concatenation (Объединение векторов в один вектор по координатам):


concat[0] = AttentionScore_1[0] = 0.4062
concat[1] = AttentionScore_1[1] = 0.4621
concat[2] = AttentionScore_1[2] = 0.5129
concat[3] = AttentionScore_2[0] = 0.22
concat[4] = AttentionScore_2[1] = 0.33
concat[5] = AttentionScore_2[2] = 0.44


concat = [0.4062, 0.4621, 0.5129, 0.22, 0.33, 0.44]

После объединения attention-выходов с разных голов, они обычно пропускаются через линейный слой (Dense Layer), чтобы вернуть их в исходное пространство размерности модели.

С точки зрения математики:

concat ∈ ℝ⁶ - входной вектор
Wₒ ∈ ℝ⁶ˣ³ - матрица весов проекции
output ∈ ℝ³ - выходной вектор после проекции


Умножение:
output = concat • Wₒ


Подробная формула для каждой компоненты выходного вектора:
output[0] = concat[0]*W_o[0][0] + concat[1]*W_o[1][0] + concat[2]*W_o[2][0] + concat[3]*W_o[3][0] + concat[4]*W_o[4][0] + concat[5]*W_o[5][0]


Альтернативно, в виде суммы:
output_j = ∑_{i=1}^{6} concat_i ⋅ W_o[i][j]     для j = 1, 2, 3


Матрично:
output = concat (1x6) ⋅ W_o (6x3) = (1x3)

Пример:

Входные данные: 
Размерность модели d_model = 3


Значит, проекционная матрица W_o будет иметь 
размерность (6, 3), т.е. 6 входов → 3 выхода
W_o = [
  [0.1, 0.2, 0.3],
  [0.0, 0.1, 0.0],
  [0.2, 0.0, 0.1],
  [0.1, 0.2, 0.2],
  [0.0, 0.1, 0.3],
  [0.3, 0.0, 0.1]
]


Входной вектор после concat:
concat = [0.4062, 0.4621, 0.5129, 0.22, 0.33, 0.44]


Вычисление:


x = 0.4062*0.1 + 0.4621*0.0 + 0.5129*0.2 + 0.22*0.1 + 0.33*0.0 + 0.44*0.3
  = 0.04062 + 0 + 0.10258 + 0.022 + 0 + 0.132
  = 0.2972


y = 0.4062*0.2 + 0.4621*0.1 + 0.5129*0.0 + 0.22*0.2 + 0.33*0.1 + 0.44*0.0
  = 0.08124 + 0.04621 + 0 + 0.044 + 0.033 + 0
  = 0.2045


z = 0.4062*0.3 + 0.4621*0.0 + 0.5129*0.1 + 0.22*0.2 + 0.33*0.3 + 0.44*0.1
  = 0.12186 + 0 + 0.05129 + 0.044 + 0.099 + 0.044
  = 0.3602


Финальный output вектор:
output = [0.2972, 0.2045, 0.3602]

это суммирование входа слоя с его выходом. Мы складываем выход с входным вектором, который подавался на вход блока (input embedding или выход предыдущего слоя).

Это используется, чтобы:
Сохранить исходную информацию (градиенты легче передаются назад).
Избежать “затухания” сигнала через множество слоёв.
Облегчить обучение даже очень глубоких нейросетей.

Входные данные:


Входной вектор
input = [0.035, 0.207, 0.473]


Выходной вектор
output = [0.2972, 0.2045, 0.3602]


Вычисление:


residual = output + input = residual = [0.3322, 0.4115, 0.8332]

нормализует значения внутри одного вектора признаков, то есть по размерности признаков (feature dimension).

Зачем это нужно:
Устраняет смещение и масштабные различия между признаками.
Делает обучение более стабильным.
Ускоряет сходимость нейросети.
Лучше работает при маленьких батчах, в отличие от BatchNorm.

С точки зрения математики:

Вычисляем среднее значение (mean):
μ = (1/n) * ∑ xₙ


Вычисляем стандартное отклонение (std):
σ = sqrt((1/n) * ∑ (xₙ - μ)^2 + ε)
Где ε - маленькое число, чтобы избежать деления на ноль.


Нормализуем каждый элемент:
̂xₙ = (xₙ - μ) / σ


Опционально масштабируем и смещаем (обучаемые параметры):
yₙ = γ * ̂xₙ + β

Пример:

Входные данные:


x = [0.5972, 0.3045, 0.5602]


Вычисление:


Вычислим среднее:
μ = (0.5972 + 0.3045 + 0.5602) / 3 = 1.4619 / 3 ≈ 0.4873


Вычислим стандартное отклонение:
σ = sqrt(((0.5972 - 0.4873)^2 + (0.3045 - 0.4873)^2 + (0.5602 - 0.4873)^2) / 3)
   = sqrt((0.0121 + 0.0334 + 0.0053) / 3)
   = sqrt(0.0169) ≈ 0.13


Нормализуем:
̂x₁ = (0.5972 - 0.4873) / 0.13 ≈ 0.845
̂x₂ = (0.3045 - 0.4873) / 0.13 ≈ -1.405
̂x₃ = (0.5602 - 0.4873) / 0.13 ≈ 0.561


После округления и применения масштабирования и смещения (если есть):
≈ [0.87, -1.13, 0.26]

FFN (Feed-Forward Network):
это компонент трансформера, который обрабатывает каждое слово (или токен) по отдельности, без учета других токенов.

Он применяется независимо к каждому токену после слоя внимания и представляет собой двухслойную нейросеть с нелинейной функцией активации.

Это позволяет модели улавливать более сложные зависимости.

MLP (Multilayer Perceptron):
это тип нейронной сети, состоящий из нескольких слоев полностью связанных нейронов. В контексте LLM и трансформеров MLP часто означает ту же самую Feed-Forward Network (FFN).

MLP = общее название архитектуры,
FFN = частный случай MLP, используемый внутри трансформеров.

С точки зрения математики:

x - входной вектор размерности d
W1-матрица весов первого линейного слоя размерности (d_ff × d)
b1-смещение первого слоя размерности d_ff
W2-матрица весов второго линейного слоя размерности (d × d_ff)
b2-смещение второго слоя размерности d
f-функция активации (например, ReLU или GELU)
d-размерность входа/выхода
d_ff-размерность скрытого слоя (обычно d_ff = 4 × d)


Основная формула FFN:
FFN(x) = W2 · f(W1 · x + b1) + b2


Пошаговое разложение:


Линейное преобразование, размерность z1: (d_ff × 1):
z1 = W1 · x + b1


Нелинейная активация, применяется поэлементно, размерность сохраняется:
z2 = f(z1)


Второе линейное преобразование, итоговый вектор того же размера, что и x: (d × 1):
y = W2 · z2 + b2

Пример:

Входные данные:


Размерность скрытого слоя d = 4,
Внутренняя размерность d_ff = 8,
Активация: ReLU,
Входной вектор: 
x = [x[0], x[1], x[2], x[3]] = [1.0, -2.0, 0.5, 3.0]


матрица 8x4 W1 = 
[
  [1, 0, 0, 0],
  [0, 1, 0, 0],
  [0, 0, 1, 0],
  [0, 0, 0, 1],
  [1, 1, 1, 1],
  [1, -1, 1, -1],
  [0.5, 0.5, 0.5, 0.5],
  [-1, -1, -1, -1]
]


Вычисление:


y[0] = 1 * x[0] + 0 * x[1] + 0 * x[2] + 0 * x[3] = 1.0 + 0 + 0 + 0 = 1.0
y[1] = 0 * x[0] + 1 * x[1] + 0 * x[2] + 0 * x[3] = 0 - 2.0 + 0 + 0 = -2.0
y[2] = 0 * x[0] + 0 * x[1] + 1 * x[2] + 0 * x[3] = 0 + 0 + 0.5 + 0 = 0.5
y[3] = 0 * x[0] + 0 * x[1] + 0 * x[2] + 1 * x[3] = 0 + 0 + 0 + 3.0 = 3.0
y[4] = 1 * x[0] + 1 * x[1] + 1 * x[2] + 1 * x[3] = 1.0 - 2.0 + 0.5 + 3.0 = 2.5
y[5] = 1 * x[0] + (-1) * x[1] + 1 * x[2] + (-1) * x[3] = 1.0 + 2.0 + 0.5 - 3.0 = 0.5
y[6] = 0.5 * x[0] + 0.5 * x[1] + 0.5 * x[2] + 0.5 * x[3] = 0.5 - 1.0 + 0.25 + 1.5 = 1.25
y[7] = -1 * x[0] + (-1) * x[1] + (-1) * x[2] + (-1) * x[3] = -1.0 + 2.0 - 0.5 - 3.0 = -2.5


Вектор после линейного слоя (до активации):
y = [1.0, -2.0, 0.5, 3.0, 2.5, 0.5, 1.25, -2.5]


Применяем ReLU:
ReLU(y[i]) = max(0, y[i])


ReLU(y) = [1.0, 0.0, 0.5, 3.0, 2.5, 0.5, 1.25, 0.0]

После FFN к результату снова прибавляют вход этого блока (результат шага 6) и снова нормализуют вектор — это помогает сохранить информацию и стабилизировать расчёты

Получив на выходе одного блока трансформера нормализованный вектор, модель передаёт его на вход следующему блоку. Таких блоков может быть десятки — каждый добавляет всё больше «понимания» контекста

После последнего слоя ещё раз применяют LayerNorm — это заключительный штрих перед генерацией логитов, чтобы сгладить разброс значений

Нормализованный вектор из последнего слоя умножают на матрицу эмбеддингов (или отдельный линейный слой), чтобы получить «сырые» оценки (logits) для каждого токена словаря. Затем эти оценки превращают в вероятности с помощью Softmax или сразу передают в семплеры для отборки токена

Что такое logits:
«сырые» значения, которые ещё не нормализованы в вероятности.
Например, если словарь состоит из 50 000 токенов, то logits — это просто 50 000 чисел, одно для каждого токена.

Для каждого токена в последовательности модель сформировала его представление (hidden state).

Теперь надо преобразовать этот hidden state в логиты по словарю — оценку вероятности появления каждого возможного следующего токена.

Преобразование через Linear Layer:

logits = hidden_state @ Wᵀ + b


hidden_state: последний вектор (или вся последовательность — но чаще интересует последний токен),
Wᵀ: транспонированная матрица эмбеддингов (размер [vocab_size, hidden_dim]),
b: сдвиг (bias), часто опускается для экономии.

почти во всех современных LLM применяется weight tying — то есть Wᵀ берётся из того же слоя, что и эмбеддинги на входе. Это экономит память и улучшает обобщающую способность модели.

Полученные logits проходят через цепочку семплеров: сначала применяются штрафы за повторения и температура, затем фильтры (top-k, top-p и др.), а в конце — финальный семплер (миростат, жадный выбор или распределение) . Именно этот токен добавляют в контекст, и генерация повторяется с шага 1 до достижения конца или нужной длины.

В бекенде LLama.cpp можно составлять цепочки семплеров, сначала промежуточные, затем цепочка должна завершаться финальным семплером.
Порядок применения семплеров в llama.cpp:

1. Штрафы за повторы:
   - repeat_penalty
   - frequency_penalty
   - presence_penalty


2. Масштабирование логитов:
   - temperature


3. Фильтрация токенов:
   - top_k
   - top_p
   - min_p
   - typical_p
   - tfs_z


4. Грамматические ограничения:
   - grammar


5. Финальный выбор токена:
   - mirostat (v1 или v2) или
   - greedy / random sampling

Пример из LLama.cpp (для разработчиков):

// подготовка параметров цепочки
struct llama_sampler_chain_params sparams = llama_sampler_chain_default_params();
llama_sampler * smpl = llama_sampler_chain_init(sparams);


// промежуточные семплеры:
llama_sampler_chain_add(smpl, llama_sampler_init_top_k (50)); // Top-K
llama_sampler_chain_add(smpl, llama_sampler_init_top_p (0.9f, 1)); // Top-P (nucleus)
llama_sampler_chain_add(smpl, llama_sampler_init_min_p (0.8f, 1)); // Min-P
llama_sampler_chain_add(smpl, llama_sampler_init_typical (0.95f, 1)); // Locally Typical
llama_sampler_chain_add(smpl, llama_sampler_init_temp (0.7f)); // Temperature
llama_sampler_chain_add(smpl, llama_sampler_init_temp_ext (0.7f, 0.1f, 1.5f));  // Extended temp
llama_sampler_chain_add(smpl, llama_sampler_init_xtc (0.9f, 1.0f, 1, LLAMA_DEFAULT_SEED)); // XTC
llama_sampler_chain_add(smpl, llama_sampler_init_top_n_sigma(1.5f)); // Top-nσ


llama_sampler_chain_add(smpl, llama_sampler_init_grammar(vocab, grammar, "root"));
llama_sampler_chain_add(smpl,llama_sampler_init_grammar_lazy_patterns(vocab, grammar, "root", patterns, 2, tokens, N));


// финальный семплер, только 1 вариант из:
llama_sampler_chain_add(smpl, llama_sampler_init_mirostat(32000, LLAMA_DEFAULT_SEED, 5.0f, 0.1f, 100)); // Mirostat V1
llama_sampler_chain_add(smpl, llama_sampler_init_mirostat_v2(LLAMA_DEFAULT_SEED, 5.0f, 0.1f)); // Mirostat V2
llama_sampler_chain_add(smpl, llama_sampler_init_greedy()); // Greedy
llama_sampler_chain_add(smpl, llama_sampler_init_dist(LLAMA_DEFAULT_SEED)); // Dist


// после генерации не забыть освободить цепочку:
llama_sampler_free(smpl);

Пример страницы настройки семплеров из приложения https://github.com/a-ghorbani/pocketpal-ai

управляет степенью случайности при выборе следующего токена из распределения вероятностей, вычисленного моделью. Температура сужает или расширяет «воронку выбора» следующего слова. Чем она ниже — тем уже воронка, тем выше — тем шире.

После того как модель предсказала логиты (сырые значения вероятностей) для всех возможных токенов, они проходят через softmax-функцию. Температура влияет на эту функцию:

P(token) = softmax(logits / temperature)

Эффект параметра temperature:
T = 1 логиты остаются как есть.
T < 1 (например, 0.7) модель усиливает различия между токенами — вероятные токены становятся ещё более вероятными. Поведение становится более предсказуемым.
T > 1 (например, 1.5) различия между токенами сглаживаются — возрастает шанс выбрать менее вероятный токен. Поведение становится более разнообразным и рискованным.
T → 0 softmax превращается в argmax, и всегда выбирается один самый вероятный токен.

Исходные логиты:
Token A: 3.0
Token B: 2.5
Token C: 1.0


После softmax без изменения (temp = 1.0):
A: 64%
B: 24%
C: 12%
  
  
С пониженной температурой (temp = 0.5):
A: 85%
B: 13%
C: 2%

  
С повышенной температурой (temp = 1.5):
A: 47%
B: 31%
C: 22%

модель выбирает следующий токен только из k наиболее вероятных. Все остальные токены отбрасываются, независимо от их абсолютной вероятности.

есть 6 токенов с такими вероятностями:
A: 0.35
B: 0.30
C: 0.15
D: 0.10
E: 0.06
F: 0.04


Если top_k = 3, то оставим только:
A: 0.35
B: 0.30
C: 0.15

выбирается минимальное множество токенов с наибольшими вероятностями, сумма которых превышает заданный порог p. Остальные токены полностью отсекаются, даже если они имели высокий ранг.

модель выдала вероятности токенов:
A: 0.50  
B: 0.25  
C: 0.15  
D: 0.08  
E: 0.02


При min_p = 0.1, остаются только:
A, B, C
D и E будут отброшены, даже если общая сумма теперь меньше 1.0 — потом оставшиеся нормализуются.

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

выбирает токены, близкие к «типичному» уровню неожиданности (surprisal), отсекая слишком предсказуемые и слишком редкие. В отличие от top-k и top-p, он ориентируется на медианное значение surprisal, а не на вероятность токенов.

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

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

отбрасывает длинный «хвост» распределения, регулируя плотность вероятностей.

и другие

самый простой способ генерации, при котором модель всегда выбирает токен с наивысшей вероятностью (максимальный логит). Он быстрый и детерминированный, но часто приводит к однообразному и предсказуемому тексту.

выбор следующего токена случайно, пропорционально его вероятности после всех применённых семплеров (top-k, top-p и т.д.). Это противоположность жадного выбора (greedy), где всегда берётся токен с максимальной вероятностью.

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

поддерживает заданный уровень «удивления» (перплексии), динамически корректируя выбор слов. Он помогает избежать чрезмерных повторов (ловушка скуки) и бессвязности (ловушка путаницы), обеспечивая сбалансированную и качественную генерацию текста.

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

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

и другие

После того как модель выбрала следующий токен, он добавляется в конец входной последовательности.

Контекст расширяется: теперь в качестве “исходного” текста для токенизации берутся все предыдущие токены плюс новосгенерированный.

Затем алгоритм возвращается к шагу 1 (токенизация), и процесс повторяется до тех пор, пока не будут выполнены условия прекращения.

Такой цикл позволяет по одному токену строить всю выходную последовательность.

Генерация завершается, когда модель выдаёт специальный токен конца предложения (EOS) или когда достигается заранее заданная максимальная длина последовательности.

Это ограничение предотвращает бесконечные или слишком длинные ответы.

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

После того как модель сгенерировала нужное количество токенов, их преобразуют обратно в текст — это называется детокенизация. Токенизатор берёт последовательность чисел (токенов) и переводит их в слова и знаки препинания. Обычно это делают уже после полной генерации или по ходу, если нужно получать текст «на лету».

ONNX Runtime:
Кроссплатформенный движок для выполнения моделей в формате ONNX.
Формат файла: .onnx

TensorFlow (Inference API):
Стандартный движок для моделей TensorFlow, поддерживает SavedModel и замороженный граф.
Форматы: папка SavedModel (saved_model.pb + variables/), единый файл .pb

TensorFlow Lite:
Лёгкий движок для мобильных и встраиваемых устройств, оптимизированный по размеру и скорости.
Формат: .tflite

PyTorch (TorchScript):
Позволяет сериализовать и запускать модели без зависимости от Python, с оптимизациями JIT.
Форматы: .pt, .pth

NVIDIA TensorRT:
Аппаратно-ускоренный движок для GPU NVIDIA, компилирует модели (обычно из ONNX) под конкретную карту.
Формат: скомпилированный движок .engine (или план UFF/ONNX → .engine)

Intel OpenVINO:
Оптимизирует и ускоряет сети на CPU и Intel GPU/VPU, конвертирует модели в IR-формат.
Форматы: .xml (структура) + .bin (веса)

Apple Core ML:
Фреймворк для запуска моделей на iOS/macOS, интегрируется с Xcode и ускоряется через Core ML Runtime.
Формат: .mlmodel

Microsoft ML.NET:
.NET-движок для инференса на CPU, подходит для C# и F#.
Формат: архив модели .zip

Apache TVM:
Компилирует и оптимизирует модели под разнообразное железо, создаёт нативные библиотеки.
Форматы: сериализованный Relay-модуль или скомпилированная библиотека (.so, .dll)

Alibaba MNN:
Мобильный нейронный движок с широкими оптимизациями для ARM, поддерживает серверный запуск.
Формат: .mnn

Tencent NCNN:
Компактный движок для мобильных CPU/GPU, без сторонних зависимостей.
Форматы: .param (структура) + .bin (веса)

OpenCV DNN:
Модуль компьютерного зрения с поддержкой разных форматов (ONNX, Caffe, TensorFlow, Darknet).
Форматы: зависит от исходного — .onnx, .pb, .caffemodel + .prototxt, .weights

Unity Barracuda:
Движок для запуска нейросетей в играх Unity, поддерживает ONNX-модели.
Формат: .nn

MLC LLM:
ML-компилятор и движок для LLM, компилирует веса в свой IR и шардирует их.
Формат: каталог …-MLC с .bin-шардами и JSON-конфигами (например, mlc-chat-config.json)

MediaPipe:
Фреймворк для создания мультимодальных пайплайнов (детекция, сегментация и др.), опирается на TFLite.
Форматы: .tflite (модель) + .pbtxt (граф)

llama.cpp:
C++-движок для LLaMA-подобных моделей с поддержкой квантования на CPU/GPU.
Форматы: GGML-модели (.bin, .gguf)

GGML:
Библиотека для эффективного инференса моделей (основа llama.cpp и др.).
Форматы: .gguf, .bin

NeuralMagic DeepSparse:
Оптимизированный движок для разреженных нейросетей на CPU.
Формат: .onnx

AWS Neuron SDK:
Движок для ускорения инференса на AWS Inferentia, компилирует модели под Neuron.
Форматы: Neuron-артефакты (.nef или библиотека)

Glow:
Компилятор и рантайм от Facebook для нейросетей, входной формат ONNX.
Форматы: промежуточный файл .bc или скомпилированная библиотека .so

Apache MXNet:
Фреймворк с собственным рантаймом для CPU/GPU, поддерживает Docker-развёртывание.
Форматы: .params (веса) + .json (сеть)

Caffe:
Классический движок для CV-сетей, часто используется в исследованиях.
Форматы: .caffemodel (веса) + .prototxt (описание сети)

LM Studio:
Десктоп-приложение для Windows и macOS с GUI для запуска локальных LLM (GPT-подобных).
Форматы моделей: .gguf, .bin

Ollama:
Лёгкий CLI/GUI-клиент для Windows и macOS, работает «из коробки» с LLM.
Форматы моделей: .gguf, .bin

Automatic1111 Stable Diffusion WebUI:
Самый популярный локальный веб-интерфейс для генерации изображений на Windows/macOS/Linux.
Форматы моделей: .ckpt, .safetensors

DiffusionBee:
Настольное приложение для macOS (есть бета-сборки для Windows), «всё в одном» для Stable Diffusion.
Форматы моделей: .ckpt, .safetensors

InvokeAI:
Кроссплатформенный пакет с CLI и WebUI для генерации изображений на базе Stable Diffusion.
Форматы моделей: .ckpt, .safetensors

llama.cpp (prebuilt binaries):
Готовые исполняемые файлы для Windows/macOS/Linux, позволяют запускать LLaMA-подобные модели без установки зависимостей.
Форматы моделей: .bin, .gguf

.pt / .pth
Используются в PyTorch для сохранения обученных моделей

Хранят веса и структуру модели (или только веса).
Основаны на сериализации Python (pickle), что делает их не очень безопасными.
Подходят для обучения и дообучения.
Не оптимизированы для мобильного вывода или внешнего инференса
Могут занимать много памяти (FP32).

.safetensors
Альтернатива .pt, безопасный и быстрый формат для PyTorch.

Не использует pickle, а значит безопасен при загрузке.
Поддерживает параллельную загрузку, что ускоряет работу.
Только для хранения весов (структура — отдельно).
Подходит для инференса и дообучения.
Поддерживается HuggingFace, PyTorch, JAX.

.bin
Универсальный или частный бинарный формат для весов моделей, особенно в HuggingFace и старом GGML.

Может быть неформализован (структура зависит от фреймворка).
Может содержать полные веса или квантованные данные.
Подходит для загрузки в кастомные движки (например, llama.cpp).
Требует точного знания, как интерпретировать содержимое.
Часто используется в старых проектах или кастомных пайплайнах.

.gguf
Новый стандарт от GGML для запуска LLM в llama.cpp, Ollama, MLC LLM.

Включает всё в одном файле: веса, словарь, токенизатор, параметры модели.
Поддерживает разные типы квантования (Q2_K, Q4_0, Q8_1 и т.д.).
Хорошо сжат и оптимизирован под запуск на CPU, GPU, Android.
Быстро загружается и легко обрабатывается C++ кодом.
Основной выбор для локального запуска LLM (Gemma, LLaMA, Mistral).

.onnx
Кросс-фреймворк формат для запуска модели в разных средах (Windows, Web, C#, Java и др.).

Стандартизирован, поддерживается многими фреймворками (PyTorch, TF, Keras).
Упрощает перенос моделей между платформами.
Подходит для инференса (не для обучения).
Оптимизируется средствами ONNX Runtime (сжатие, квантование).
Отличный выбор для запуска моделей в embedded-средах и .NET.

.tflite
Формат для запуска моделей TensorFlow на мобильных устройствах (Android, iOS).

Очень компактный и быстрый.
Поддерживает INT8 и FP16 квантование.
Работает с TensorFlow Lite Interpreter, легко встраивается в Android.
Нельзя обучать или дообучать — только запуск.
Поддерживает базовые CNN, RNN, Seq2Seq, но не LLM.

.mlc
Формат для MLC LLM — запуск LLM на Android/iOS/GPU через компиляцию.

Модель предварительно компилируется в эффективный байткод.
Поддерживает Vulkan/Metal/OpenCL для ускорения.
Используется для Gemma, Mistral, LLaMA на телефоне.
Требует сложной подготовки (TVM, скрипты, настройка).
Отлично подходит для мобильных приложений без серверов.

Copyright: Roman Kryvolapov