Нейромережі простими словами
Привіт!
В інтернеті можна знайти різні пояснення того, як працюють нейромережі, але ті, що мені траплялися, були або надто специфічні й орієнтовані на фахівців, або надто спрощені.
Спробував написати свої пояснення, які були б не надто спрощені, але при цьому за можливості зрозумілі.
Стаття на 10 відсотків скомпільована з інших статей, на 30 відсотків скомпільована з багатьох діалогів з різними LLM і на 60 відсотків “написана від руки” на підставі статей і відповідей.
Вхідні дані
На вході нейромережа отримує вхідні дані у вигляді запиту користувача. До запиту користувача також додається загальна інформація, яка дозволяє отримати точнішу відповідь. Якщо нейромережа підтримує RAG (Retrieval-Augmented Generation), то до запиту додаються також:
-
Дані з векторної бази, якщо в неї попередньо додали те, що потрібно для генерації актуальнішої відповіді
-
Витягнуті з інтернет сторінки або документа — для того, щоб скоротити їхній розмір, часто використовується спеціалізована embedding нейромережа, вона знаходить усі відповідні шматки тексту й додає до контексту запиту
Приклад налаштувань embedding нейромережі для Chrome розширення Page Assist

Як працює RAG
Система складається з двох основних компонентів
Retrieval (витягнення):
Шукає релевантну інформацію із зовнішньої бази знань (найчастіше — векторна база, наприклад, FAISS, Qdrant, Weaviate). Ця база зазвичай побудована заздалегідь з текстових документів (pdf, markdown, html та ін.), перетворених на ембединги за допомогою моделі (наприклад, BERT, Instructor, або SentenceTransformer).
Generation (генерація):
LLM (GPT, LLaMA, Mistral та ін.) отримує вихідний запит + знайдені документи і генерує фінальну відповідь.
Не плутайте два різні «embedding» у цій статті: тут ідеться про окрему embedding-модель, яка перетворює цілий фрагмент тексту на один вектор, щоб шукати схожі фрагменти; далі, у кроці 2, — про token embedding, вектори окремих токенів усередині самої LLM. Механіка схожа, але це різні моделі й різні вектори.
Про RAG і function calling у мене є окрема стаття:
І окрема — про векторні бази:
У деяких нейромережах для відокремлення запиту користувача, контексту, отриманого за допомогою RAG, і загального повідомлення використовується розмітка.
Приклад:
context:
це дані, отримані з інтернет за допомогою embedding нейромережі або з векторного сховища і відсортовані за збігом score у міру спадання
{ "context": [ "Пеніцилін — це перший відкритий антибіотик, вироблений із цвілевого грибка роду Penicillium. Він використовується для лікування бактеріальних інфекцій, таких як ангіна, сифіліс і пневмонія. Пеніцилін руйнує клітинну стінку бактерій, що призводить до їх загибелі.", "Деякі люди мають алергію на пеніцилін. Це може викликати серйозні реакції, включно з анафілаксією, тому перед призначенням препарату важливо уточнити наявність алергії у пацієнта." ], "instructions": "Відповідай простими словами, використовуючи лише інформацію з контексту. Якщо відповіді немає — напиши 'Інформацію не знайдено'.", "question": "Що таке пеніцилін і для чого він застосовується?"}З чого складається модель
Трансформер (Transformer)
Архітектура нейромережі, яка стала основою для сучасних мовних моделей, таких як ChatGPT, BERT, LLaMA, Gemma і багатьох інших.
Він був уперше описаний у науковій статті 2017 року під назвою “Attention is All You Need”.
Простіше кажучи, трансформер — це “розумна машина”, яка вміє читати й розуміти текст, обробляючи всі слова одночасно, а не по одному, як це робили попередні моделі (наприклад, RNN, LSTM).
Шари трансформера (Transformer Layers)
це повторювані блоки, крізь які проходить кожен токен усередині моделі.
Один шар (або “трансформер-блок”) включає в себе:
*Self-Attention:* токен “дивиться” на інші токени й вирішує, хто важливий.
Feed-Forward Network:
уточнення й перетворення кожного токена.
Layer Normalization або LayerNorm:
стабілізація обчислень.
Residual Connections:
щоб модель не “забула” початкову інформацію.
LayerNorm може бути до або після Residual (Pre-LN vs Post-LN). Практично всі сучасні LLM (Llama, Qwen, Gemma, DeepSeek) використовують Pre-LN, а замість класичного LayerNorm — його спрощений варіант RMSNorm. Кроки далі в статті описані за класичною схемою Post-LN, так наочніше.
та інші етапи.
Кожен шар трансформера (Transformer Block) — це окремий набір параметрів, які:
незалежно навчаються,
незалежно застосовуються до вхідних даних,
дають дедалі “глибше” розуміння змісту й контексту.
Що означає “32 шари”:
кожен токен проходить крізь 32 такі операції, послідовно одну за одною, після кожного шару токен стає дедалі більш поінформованим, тобто його представлення (вектор) дедалі глибше відображає контекст
Кількість шарів популярних відкритих моделей (transformer layers, за офіційними конфігураціями, середина 2026 року):
| Модель | Кількість шарів ||---------------------------|------------------|| Llama 3.1 (8B) | 32 || Llama 3.1 (70B) | 80 || Llama 4 Scout (109B) | 48 || Gemma 3 (12B) | 48 || Gemma 3 (27B) | 62 || Gemma 4 (31B) | 60 || Qwen 3 (8B) | 36 || Qwen 3 (32B) | 64 || Qwen 3 (235B-A22B) | 94 || Qwen 3.5 (9B) | 32 || DeepSeek V3 / R1 / V4 Pro | 61 || DeepSeek V4 Flash | 43 || GPT-OSS (20B) | 24 || GPT-OSS (120B) | 36 |У MoE-моделей (Mixture of Experts — Llama 4, Qwen 3 235B-A22B, DeepSeek V3/V4, GPT-OSS) усередині кожного шару замість одного FFN-блоку лежить набір «експертів», з яких для кожного токена активується лише невелика частина: у Qwen 3 235B-A22B з 235 мільярдів параметрів на кожен токен працюють лише 22 мільярди.
Активація (Activation)
це проміжні виходи нейромережі після застосування функцій і шарів.
Можна сказати, що активації це дані, які "живуть усередині мережі" на кожному етапі проходу входу крізь модель.
Тензор (Tensor)
у нейромережі це багатовимірний масив чисел, з яким працюють шари моделі.
Вхідні дані:
Текст → токени → ембединги → тензор batch_size × seq_len × embedding_dim.
Ваги моделі:
Вагові матриці в шарах — теж тензори.
Проміжні представлення (активації):
Вихід кожного шару (наприклад, LayerNorm, Attention) — тензор.
Градієнти:
При навчанні модель рахує градієнти (тензори) для оновлення ваг.
Кроки роботи нейромережі
Крок 1: Токенізація (з тексту / зображення в токени)
Токенізація тексту
LLM не працює зі словами напряму — вона працює з токенами (частинами слів або символами), перетвореними на числа.
Приклад:
Текст користувача:"Привіт, світ"
ID токена у словнику моделі для слова:"Привіт" = 1123"," = 15"світ" = 345
Отримуємо масив ID токенів:["Привіт", ",", "світ"] = [1123, 15, 345]
Як це досягається:
Використовується алгоритм на кшталт Byte Pair Encoding (BPE), Unigram, WordPiece або SentencePiece.
BPE-токенізатор знаходить найчастіші пари символів.
WordPiece будує токени на основі ймовірності ієрархічних розбиттів.
Часто токени — це не окремі слова, а частини слів.
У словнику є спеціальні маркери меж слів: у BPE-словниках пробіл кодується на початку токена, а в WordPiece маркером “##” позначаються токени-продовження слова. Тому жоден субтокен не перетинає межу двох слів.
Сам розбір рядка залежить від алгоритму: WordPiece іде "жадібно" — бере максимально довгий токен, що збігається з початком решти рядка, BPE послідовно застосовує вивчені правила злиття пар, а Unigram обирає найімовірніше розбиття цілком.
Приклад:
Текст користувача:"неймовірно"
ID токена у словнику моделі для частин слів:"не" = 24"ймовір" = 126"но" = 36Отримуємо масив ID токенів:["не", "ймовір", "но"] = [24, 126, 36]
У WordPiece-словнику те саме слово могло б розбитися як:("не", "##ймовірно")де "##" позначає продовження словаРізні за змістом слова можуть мати спільні токени, наприклад "привіт" і "приклад" можуть мати спільний токен "при":
Хоча «при» повторюється в обох словах, модель одразу дивиться не тільки на цей шматочок, а й на сусідні токени і на всю фразу (про це далі в статті).
Спочатку «при» перетворюється на вектор — просто набір чисел, що описують цю частину слова.
Потім трансформер (багатошарова мережа) змішує цей вектор з векторами сусідніх токенів («віт» або «клад») і додає інформацію про позицію в реченні.
У підсумку в першому шарі «при» в «привіт» уже відрізняється від «при» в «приклад», тому що туди «підмішався» різний контекст.
Тобто спільний шматочок «при» сам по собі нейтральний, а зміст формується далі по шарах на основі оточення.
Чому токени, а не слова:
Менший словник = економія пам’яті.
Краще обробляються рідкісні й складені слова.
Дозволяє моделі “вчитися” розуміти структуру слів.
Розмір словника, або “vocab_size”, визначає, скільки унікальних токенів може обробляти модель.
Більший словник = менше розбиттів слів на частини, але більший обсяг embedding-шару.
Слово “програмування” може бути цілком одним токеном у моделі з великим словником,
а може розбитися на частини (“пр”, “ограму”, “вання”) у моделі з меншим словником.
Практичний нюанс для читачів цього блогу: кирилиця токенізується «дорожче» за латиницю. Токенізатори навчені здебільшого на англійській, тому російське або українське слово частіше розбивається на більшу кількість токенів, ніж англійське тієї самої довжини. Той самий текст українською займає помітно більше токенів: швидше закінчується контекст, а в платних API це буквально дорожче.
Розміри словника популярних відкритих моделей (за офіційними конфігураціями, середина 2026 року):
Модель | Розмір словника |--------------------------|-----------------|Llama 2 (2023) | 32 000 |Llama 3.1 | 128 256 |Llama 4 | 202 048 |Gemma 2 | 256 128 |Gemma 3 / Gemma 4 | 262 144 |Qwen 3 | 151 936 |Qwen 3.5 | 248 320 |DeepSeek V3 / R1 / V4 | 129 280 |GPT-OSS (20B / 120B) | 201 088 |Добре видно, як словник зростає від покоління до покоління. У закритих моделей (GPT, Claude, Gemini) словник та інші деталі архітектури не публікуються.
Спеціальні токени та chat-шаблон
Крім токенів звичайного тексту у словнику є службові токени: початок і кінець послідовності, межі реплік, у нових моделей — маркери виклику інструментів і блоків міркувань. Модель бачила їх на донавчанні й спирається на них, щоб розуміти, де чия репліка і коли час зупинитися.
Тому діалог перед токенізацією загортається в chat-шаблон — прийняту в конкретної моделі розмітку з ролями «система», «користувач» і «асистент». Спрощено це виглядає так:
<|system|>Ти корисний асистент.<|end|><|user|>Що таке пеніцилін?<|end|><|assistant|>Модель просто продовжує текст після маркера асистента, а закінчує генерацію своїм службовим токеном кінця репліки.
У кожного сімейства моделей шаблон свій, і переплутати його — найчастіший спосіб «зламати» локальний запуск: модель починає відповідати за користувача, не зупиняється вчасно або сипле службовими токенами прямо в текст. Готові бекенди на кшталт llama.cpp і Ollama зазвичай беруть правильний шаблон з метаданих GGUF-файлу автоматично.
Токенізація зображень
На відміну від текстової токенізації (де токени — це слова, сабворди, символи), у зображеннях токени — це фрагменти зображення або представлення ознак. Нижче розглянуто основні підходи.
Для зображень замість токенів отримують одразу матрицю (або тензор) пікселів.
У класичних згорткових мережах (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) на виході можна інтерпретувати як токени, де кожен елемент сітки — вектор.
Застосовується в ResNet-варіантах CLIP та інших гібридних моделях; неперервні ознаки візуального енкодера використовують і мультимодальні LLM на кшталт LLaVA.
VQ-VAE / VQ-GAN токенізація (дискретна):
Кодувальник (енкодер) перетворює зображення на карту ознак і потім квантує її в дискретні токени (індекси зі словника).
Кожен токен — це індекс у словнику візуальних патчів.
Використовується в DALL·E першої версії, Parti, Chameleon та інших генеративних моделях.
Плюси: модель працює зі «словами» візуальної мови.
Мінуси: втрата точності, нестійка генерація.
Segment/Region-based токени (DETR, Region Attention):
Зображення розбивається на змістові регіони (сегментація, об’єкти).
Кожен регіон перетворюється на токен за допомогою агрегації ознак.
Використовується в завданнях детекції об’єктів і візуальної відповіді на питання (VQA).
Patch + Positional Encoding:
Як і в NLP, кожному патчу додається позиційна інформація (абсолютна, відносна або learnable), щоб зберегти просторову структуру зображення.
Максимальна довжина контексту
Контекст — це оперативна пам’ять моделі, розмір контексту це максимальна кількість токенів (слів, символів або їхніх частин), яку мовна модель може обробити за один запит.
Це «обсяг пам’яті», який модель може бачити одночасно, щоб сформувати відповідь. Усе, що виходить за межі цього вікна, модель забуває або не бачить напряму.
Розмір контексту напряму впливає на здатність моделі пам’ятати попередні повідомлення в діалозі.
Модель не має вбудованої довготривалої пам’яті — вона не "пам’ятає" вас як людина. Вона просто обробляє весь попередній діалог як вхідний текст (токени), що передається при кожному запиті. Це й називається контекст.
Максимальна довжина контексту популярних відкритих моделей (за офіційними конфігураціями, середина 2026 року):
| Модель | Максимальна довжина контексту||-------------------------|------------------------------|| Llama 3.1 | 131 072 || Llama 4 Scout | 10 000 000 || Gemma 3 | 131 072 || Gemma 4 | 262 144 || Qwen 3 | 40 960 (131 072 з YaRN) || Qwen 3.5 | 262 144 || DeepSeek V3.1 / R1 | 131 072 || DeepSeek V4 | 1 048 576 || GPT-OSS | 131 072 |У закритих моделей контекст того самого порядку: наприклад, Gemini 2.5 Pro тримає близько мільйона токенів.
Чому довгий контекст дорогий
Плата за контекст подвійна. По-перше, увага порівнює кожен токен з кожним: при подвоєнні довжини послідовності обсяг обчислень зростає приблизно вчетверо. По-друге, для кожного обробленого токена модель зберігає його K- і V-вектори в усіх шарах (KV-кеш, про нього в кроці про ітерацію генерації) — на довгих контекстах цей кеш займає гігабайти й може їсти більше пам’яті, ніж самі ваги.
Тому навколо довгого контексту стільки інженерних обхідних шляхів: Gemma і GPT-OSS чергують шари повної уваги з шарами «ковзного вікна», які дивляться лише на найближчу тисячу-другу токенів, а DeepSeek стискає ключі та значення в компактний латентний вектор (MLA). Саме такі прийоми роблять контекст у сотні тисяч і мільйони токенів практично можливим.
Крок 2: Token embedding (з токена у вектор)
У LLM моделі для кожного ID токена зберігається Token Embedding, або масив цифр. Ці числа описують, що означає цей токен, як якби ти перекладав слово в математичну форму.
Спочатку token embedding має фіксоване значення для кожного токена, але потім уточнюється в міру проходження крізь шари.
Коли Token Embedding проходить через кілька шарів трансформера, він стає контекстуалізованим: він враховує значення всієї фрази. На виході ми отримуємо вектор, який містить “зміст” слова в контексті.
Зв’язки між векторами задаються матрицями ваг між шарами.
Спочатку ці ваги ініціалізуються випадково або за спеціальним правилом (наприклад, Xavier або He), а потім під час навчання для кожної ваги обчислюють градієнт втрати (наскільки помилка зростає або спадає при її зміні).
Алгоритм зворотного поширення (“backpropagation”) розраховує, як зміна кожної ваги вплине на підсумкову помилку, а потім градієнтним спуском (або його варіаціями) коригує ці ваги.
Так поступово посилюються зв’язки між тими компонентами векторів, які разом дають корисну інформацію, і слабшають усі непотрібні — виходить набір взаємопов’язаних векторів, оптимальних для розв’язання завдання.
Розмірність вектора (Embedding Size, d_model) на кожному шарі трансформера
Кількість вимірів у token embedding фіксована для всієї моделі й залежить від її архітектури.
Приклад:
Текст користувача:"Привіт"
ID токена у словнику моделі для слова:"Привіт" = 1123
Token embedding для ID токена 1123 = масив значень у форматі числа з рухомою комою з d_model= 4096 елементів:[0.034, 0.120, 0.905, ..., 0.028]Розмір embedding size популярних відкритих моделей (за офіційними конфігураціями, середина 2026 року):
Модель | d_model |--------------------------|---------|Llama 3.1 (8B) | 4096 |Llama 3.1 (70B) | 8192 |Llama 4 Scout (109B) | 5120 |Gemma 3 (12B) | 3840 |Gemma 3 (27B) | 5376 |Gemma 4 (31B) | 5376 |Qwen 3 (8B) | 4096 |Qwen 3 (32B) | 5120 |Qwen 3 (235B-A22B) | 4096 |Qwen 3.5 (9B) | 4096 |DeepSeek V3 / R1 / V4 Pro | 7168 |DeepSeek V4 Flash | 4096 |GPT-OSS (20B / 120B) | 2880 |Кількість елементів вектора змісту впливає на:
Більша розмірність = більше “місця” для зберігання семантики, синтаксису, контексту.
Це дозволяє розрізняти тонші змісти між токенами.
Ціною пам’яті: квадратично зі зростанням d_model зростають внутрішні матриці проєкцій (їхній розмір d_model × d_model), а шар ембедингів і активації зростають лінійно.
матриця проєкції при d_model = 4096займає в 4 рази більше пам’яті, ніжматриця проєкції при d_model = 2048Числа, з яких складаються ці вектори й ваги, можуть зберігатися з різною точністю — про формати чисел і квантування див. розділ наприкінці статті.
Крок 3: Positional Encoding / Embeddings
Без додаткової інформації фрази:
“Кіт їсть рибу”
“Рибу їсть кіт”
могли б сприйматися однаково, адже набір слів однаковий.
Щоб дати моделі відчуття порядку, кожному токену (слову або частині слова) додається позиційний вектор — набір чисел, який повідомляє моделі, на яких позиціях розташовані слова.
Token embedding + Positional Encoding / Embeddings = Сумарний векторТак робили класичні трансформери, і на цій схемі найнаочніше пояснювати. У сучасних моделей позиція вноситься інакше — всередині механізму уваги, поворотом векторів (RoPE, про нього нижче в цьому кроці); сумарний вектор у них просто дорівнює ембедингу токена.
Positional Encoding / Embedding це:
Вектор такої самої розмірності, як у токена (d)
Представляє позицію в послідовності
Може бути заданий формулою (sin/cos) або навчаний
Об’єднується з вектором токена на вході в модель
Який тип використовується в моделях:
| Метод | Модель/сімейство | Опис ||-----------------------------|-----------------------------|-----------------------------------------------|| Sinusoidal Encoding | Transformer | Не навчаються, засновані на синусах і || | (Vaswani et al., 2017) | косинусах з різною частотою ||-----------------------------|-----------------------------|-----------------------------------------------|| Learnable Embeddings | BERT, GPT-2/GPT-3, | Навчувана таблиця позицій, схожа на || | OPT, ELECTRA | ембединги слів ||-----------------------------|-----------------------------|-----------------------------------------------|| Rotary Positional Embedding | майже всі сучасні LLM: | Обертання векторів — зберігає відносні || (RoPE) | Llama 2/3/4, Qwen, Gemma, | позиції між токенами || | Mistral, DeepSeek, GPT-OSS | ||-----------------------------|-----------------------------|-----------------------------------------------|| ALiBi | BLOOM, MPT | Лінійний 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]аналогічно для решти токенівОбчислення Sinusoidal Position Encoding
Синусоїдальне позиційне кодування використовується в 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) = 0PE(0, 1) = cos(0 / 10000^(2*0/4)) = cos(0) = 1
i = 1:PE(0, 2) = sin(0 / 10000^(2*1/4)) = sin(0) = 0PE(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.8415PE(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.01PE(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 - iU, 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])Як працює RoPE
Практично всі сучасні LLM використовують Rotary Positional Embedding (RoPE). Ідея: замість того щоб додавати вектор позиції до ембединга на вході, координати векторів Q і K розбивають на пари й повертають кожну пару на кут, пропорційний позиції токена. У кожної пари своя «частота» обертання, від швидкої до повільної — приблизно як частоти синусоїд вище.
Що це дає:
Позиція вноситься всередині кожного шару уваги, а не один раз на вході.
Скалярний добуток двох повернутих векторів залежить лише від різниці їхніх позицій — модель автоматично отримує відносні позиції, без окремих таблиць зміщень.
Вектори V і самі ембединги не чіпаються: позиція впливає рівно там, де порівнюються токени.
З RoPE пов’язані й прийоми розтягування контексту. Якщо модель навчена на 40 тисячах токенів, а потрібно 130 — частоти обертання перераховують так, щоб дальші позиції «вмістилися» у звичний для моделі діапазон кутів. Так працюють NTK-масштабування і YaRN: те саме «131 072 з YaRN» з таблиці контекстів — не окреме навчання на довгому контексті, а математичне розтягування RoPE з легким донавчанням.
Крок 4: Attention, Self-Attention (Вектор Уваги, Q,K,V-проєкції) і Розбиття на Голови Уваги (Multi-Head Attention)
Тепер, коли токени мають не тільки “значення”, а й “позицію”, вони проходять через механізм self-attention, завдяки якому кожне слово дивиться на інші слова, щоб вирішити, на кого з них мені звернути увагу, щоб краще зрозуміти свій зміст.
Це схоже на те, як людина читає речення: вона може повернутися очима до попередніх слів, щоб правильно зрозуміти контекст.
Self-Attention відповідає за розуміння контексту — які токени важливі один для одного, і дозволяє кожному токену зважено дивитися на всі інші в послідовності та визначати, на що звернути увагу при побудові змісту.
Softmax
це функція, яка бере на вхід вектор чисел і перетворює його на розподіл імовірностей, де всі значення невід’ємні й у сумі дають 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 * Вага шару WqWk (Key Projection):
Створює “ключ” — що кожен токен пропонує іншим.
Використовується для порівняння з Q (наскільки Qᵢ “схожий” на Kⱼ).
K токена = Сумарний вектор токена X * Вага шару WkWv (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):*
Оптимізована версія уваги, що використовується в PaLM, Falcon, StarCoder та інших, щоб знизити навантаження на пам’ять і пришвидшити інференс.
Що відбувається:
По одній Wq на голову
Тільки один Wk і один Wv
Усі голови використовують одні й ті самі ключі та значення
Плюси:
Менше пам’яті: K і V зберігаються в одному примірнику
Швидша генерація: менше даних зберігається між кроками
Мінуси:
Менше гнучкості (усі голови “дивляться” на однакові K і V)
Може трохи погіршити якість на складних завданнях
Архітектура Grouped Query Attention (GQA):
комбінований підхід з попередніх двох. Саме GQA використовується в більшості сучасних моделей: Llama 3/4, Qwen 3, Gemma 2/3/4, GPT-OSS.
Приклад:
Наприклад, в архітектурі нейромережі Gemma 3 використовується Grouped Query Attention (GQA) — компроміс між стандартним Multi-Head Attention (MHA) і Multi-Query Attention (MQA). У цій схемі матриці Wq (для запитів) різні для кожної голови, тоді як матриці Wk (для ключів) і Wv (для значень) можуть бути спільними для груп голів.
У цій нейромережі:
Wq: кожна голова має свою унікальну матрицю Wq, що дозволяє кожній голові фокусуватися на різних аспектах вхідної послідовності.
Wk і Wv: голови діляться на групи, і всередині кожної групи використовується спільна матриця Wk і Wv. Це знижує обсяг обчислень і пам’яті, необхідних для зберігання ключів і значень.
У Gemma 3 27B — 32 голови запитів (Wq) і 16 KV-голів: голови діляться на 16 груп по 2, і кожна група використовує спільні матриці ключів і значень (Wk і Wv).
У підсумку в кожному з 62 шарів моделі Gemma 3 розміром 27B будуть:
32 різних Wq (Query Projection)
16 різних Wk (Key Projection)
16 різних Wv (Value Projection)
1 спільна Wo (Output Projection)
1 MLP зі своїми вагами (Feed-Forward Network)
свої параметри нормалізації
— і в кожного з 62 шарів цей набір ваг свій.
Кількість голів уваги (attention heads; запити Q / KV-голови) популярних відкритих моделей:
| Модель | Q-голови / KV-голови ||-------------------------|------------------------|| Llama 3.1 (8B) | 32 / 8 || Llama 3.1 (70B) | 64 / 8 || Llama 4 Scout | 40 / 8 || Gemma 3 (12B) | 16 / 8 || Gemma 3 (27B) | 32 / 16 || Gemma 4 (31B) | 32 / 16 || Qwen 3 (8B) | 32 / 8 || Qwen 3 (32B) | 64 / 8 || Qwen 3 (235B-A22B) | 64 / 4 || Qwen 3.5 (9B) | 16 / 4 || DeepSeek V3 / R1 / V4 | 128 (MLA) || GPT-OSS (20B / 120B) | 64 / 8 |У DeepSeek замість класичного GQA використовується MLA (Multi-Head Latent Attention): ключі та значення стискаються в спільний латентний вектор, тому поділ на KV-голови до нього незастосовний.
*Каузальне маскування (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, K, V для кожного токена і для кожної голови уваги: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.694exp(0.2296) ≈ 1.258exp(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
Вихід уваги для токена "Привіт": [0.4062, 0.4621, 0.5129]
Цей вектор — вихід уваги (attention output), нове представлення токена “Привіт”, у якому враховано його контекст: і сам він, і сусіди. Саме такі вектори потім ідуть або в наступний attention-шар, або на вихід моделі. Важливо не плутати його з attention score: score — це скалярні ваги з softmax (0.36, 0.27, 0.37 вище), а тут — зважена ними сума V-векторів.
Зверніть увагу: для простоти приклад пораховано без каузальної маски — “Привіт” тут «бачить» і токен “світ”, що стоїть після нього. У справжній LLM з causal masking токен збирав би інформацію лише з себе та попередніх токенів.
Порівняння токена самого із собою потрібне тому, що в self-attention кожен токен “дивиться” на всі токени, включно із самим собою.
Це необхідно для того, щоб:
Зберегти інформацію про сам токен — інакше він би “губився” на тлі решти.
Навчитися “підсилювати” або “пригнічувати” себе — наприклад, у деяких мовних ситуаціях токен важливий сам по собі (наприклад, особовий займенник), а іноді його контекст важливіший.
Матриця уваги — квадратна (n × n), і на її діагоналі стоїть якраз self-to-self attention; за каузальної маски від неї залишається нижній трикутник.
Формально attention — це ваги, за якими токен агрегує інформацію від інших токенів (зокрема від самого себе), і w1 — це увага “Привіт” до самого себе. Якщо його не рахувати — токен “Привіт” взагалі не брав би участі у своєму власному виході.
Приклад:
Він сказав, що він прийде.
Коли модель обробляє токен “він”, їй потрібно:
“подивитися” на інші токени — щоб зрозуміти контекст,
але й сам токен “він” теж важливий, щоб не втратити інформацію про те, про кого йдеться.
Матеріали:
Wikipedia: Attention (machine learning)
Understanding Q,K,V In Transformer( Self Attention)
What is Query, Key, and Value (QKV) in the Transformer Architecture and Why Are They Used?
Крок 5: Об’єднання голів уваги (Head Concatenation, Concatenated Multi-Head Attention)
Попередні розрахунки відбувалися для кожної з голів уваги, тепер необхідно об’єднати результати. Нагадаю, результати різні через різні Wq, Wk, Wv для різних голів.
З точки зору математики:
h - кількість голів увагиd_k - розмірність виходу уваги кожної головиHeadOutput_i - вихід уваги i-ї голови.
Concatenation (Об’єднання векторів в один вектор за координатами):
concat = [HeadOutput_1, HeadOutput_2, ..., HeadOutput_h]Приклад:
Вхідні дані:Припустимо, розмірність: 6 (2 голови × по 3 значення)
Кількість голів уваги h = 2Розмірність вектора d_k = 3"Привіт" для голови 1 HeadOutput_1 = [0.4062, 0.4621, 0.5129]"Привіт" для голови 2 HeadOutput_2 = [0.22, 0.33, 0.44]
Обчислення:
Concatenation (Об’єднання векторів в один вектор за координатами):
concat[0] = HeadOutput_1[0] = 0.4062concat[1] = HeadOutput_1[1] = 0.4621concat[2] = HeadOutput_1[2] = 0.5129concat[3] = HeadOutput_2[0] = 0.22concat[4] = HeadOutput_2[1] = 0.33concat[5] = HeadOutput_2[2] = 0.44
concat = [0.4062, 0.4621, 0.5129, 0.22, 0.33, 0.44]Крок 6: Output Projection (Wo)
Після об’єднання 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]Крок 7: Додавання Residual (залишкового з’єднання)
це підсумовування входу шару з його виходом. Ми складаємо вихід із вхідним вектором, який подавався на вхід блоку (input embedding або вихід попереднього шару). У реальній моделі вхід і вихід блоку мають однакову розмірність d_model і складаються поелементно; у нашому наскрізному прикладі вхід був 4-вимірним, а вихід — 3-вимірним, тому для ілюстрації візьмемо перші три компоненти входу.
Це використовується, щоб:
Зберегти вихідну інформацію (градієнти легше передаються назад).
Уникнути “згасання” сигналу крізь безліч шарів.
Полегшити навчання навіть дуже глибоких нейромереж.
Вхідні дані:
Вхідний векторinput = [0.035, 0.207, 0.473]
Вихідний векторoutput = [0.2972, 0.2045, 0.3602]
Обчислення:
residual = output + input = residual = [0.3322, 0.4115, 0.8332]Крок 8: Нормалізація шару (Layer Normalization)
нормалізує значення всередині одного вектора ознак, тобто за розмірністю ознак (feature dimension).
Навіщо це потрібно:
Усуває зміщення й масштабні відмінності між ознаками.
Робить навчання стабільнішим.
Пришвидшує збіжність нейромережі.
Краще працює при маленьких батчах, на відміну від BatchNorm.
З точки зору математики:
Обчислюємо середнє значення (mean):μ = (1/n) * ∑ xₙ
Обчислюємо стандартне відхилення (std):σ = sqrt((1/n) * ∑ (xₙ - μ)^2 + ε)Де ε - маленьке число, щоб уникнути ділення на нуль.
Нормалізуємо кожен елемент:̂xₙ = (xₙ - μ) / σ
Опційно масштабуємо та зміщуємо (навчувані параметри):yₙ = γ * ̂xₙ + βПриклад:
Вхідні дані:
Візьмемо вихід residual з попереднього кроку:x = [0.3322, 0.4115, 0.8332]
Обчислення:
Обчислимо середнє:μ = (0.3322 + 0.4115 + 0.8332) / 3 = 1.5769 / 3 ≈ 0.5256
Обчислимо стандартне відхилення:σ = sqrt(((0.3322 - 0.5256)^2 + (0.4115 - 0.5256)^2 + (0.8332 - 0.5256)^2) / 3) = sqrt((0.0374 + 0.0130 + 0.0946) / 3) = sqrt(0.0483) ≈ 0.22
Нормалізуємо:̂x₁ = (0.3322 - 0.5256) / 0.22 ≈ -0.88̂x₂ = (0.4115 - 0.5256) / 0.22 ≈ -0.52̂x₃ = (0.8332 - 0.5256) / 0.22 ≈ 1.40
Якщо навчувані параметри γ = 1 і β = 0, це і є результат:[-0.88, -0.52, 1.40]Крок 9: FFN (Feed-Forward Network) і MLP (Multilayer Perceptron)
FFN (Feed-Forward Network):
це компонент трансформера, який обробляє кожне слово (або токен) окремо, без урахування інших токенів.
Він застосовується незалежно до кожного токена після шару уваги і являє собою двошарову нейромережу з нелінійною функцією активації.
Це дозволяє моделі вловлювати складніші залежності.
MLP (Multilayer Perceptron):
це тип нейронної мережі, що складається з кількох шарів повністю пов’язаних нейронів. У контексті LLM і трансформерів MLP часто означає ту саму Feed-Forward Network (FFN).
MLP = загальна назва архітектури,
FFN = окремий випадок MLP, що використовується всередині трансформерів.
З точки зору математики:
x - вхідний вектор розмірності dW1-матриця ваг першого лінійного шару розмірності (d_ff × d)b1-зміщення першого шару розмірності d_ffW2-матриця ваг другого лінійного шару розмірності (d × d_ff)b2-зміщення другого шару розмірності df-функція активації (наприклад, 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.0y[1] = 0 * x[0] + 1 * x[1] + 0 * x[2] + 0 * x[3] = 0 - 2.0 + 0 + 0 = -2.0y[2] = 0 * x[0] + 0 * x[1] + 1 * x[2] + 0 * x[3] = 0 + 0 + 0.5 + 0 = 0.5y[3] = 0 * x[0] + 0 * x[1] + 0 * x[2] + 1 * x[3] = 0 + 0 + 0 + 3.0 = 3.0y[4] = 1 * x[0] + 1 * x[1] + 1 * x[2] + 1 * x[3] = 1.0 - 2.0 + 0.5 + 3.0 = 2.5y[5] = 1 * x[0] + (-1) * x[1] + 1 * x[2] + (-1) * x[3] = 1.0 + 2.0 + 0.5 - 3.0 = 0.5y[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.25y[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 трохи складніший за описаний: використовується gated-варіант (SwiGLU) з трьома матрицями — дві готують «кандидата» і «ворота», які перемножуються поелементно, — а замість ReLU беруть гладку активацію SiLU/GELU; внутрішня розмірність при цьому не 4×, а близько 2.7× від d_model. Суті це не змінює: як і раніше, пара лінійних шарів і нелінійність, що застосовуються до кожного токена окремо.
У MoE-моделей саме на місці FFN стоїть набір «експертів»: невеликий роутер обирає для кожного токена кількох із них (наприклад, 8 зі 128), і працюють лише обрані — так Qwen 3 235B-A22B витрачає на токен лише 22 зі своїх 235 мільярдів параметрів.
Крок 10: Residual + LayerNorm (другий шар нормалізації)
Після FFN до результату знову додають вхід цього блоку (вихід кроку 8 — те, що подавалося у FFN) і знову нормалізують вектор — це допомагає зберегти інформацію та стабілізувати розрахунки
Крок 11: Подача виходу блоку в наступний трансформер-блок
Отримавши на виході одного блоку трансформера нормалізований вектор, модель передає його на вхід наступному блоку. Таких блоків може бути десятки — кожен додає дедалі більше «розуміння» контексту
Крок 12: Фінальна LayerNorm нормалізація після останнього блоку
Після останнього шару ще раз застосовують LayerNorm — це заключний штрих перед генерацією логітів, щоб згладити розкид значень
Крок 13: Лінійна проєкція в логіти (Logits Projection) + Softmax
Нормалізований вектор з останнього шару множать на матрицю ембедингів (або окремий лінійний шар), щоб отримати «сирі» оцінки (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), часто опускається для економії.у частині моделей застосовується weight tying — Wᵀ береться з того самого шару, що й ембединги на вході; це економить пам’ять. Так роблять здебільшого компактні моделі (Gemma, молодші Qwen), а, наприклад, великі Llama і DeepSeek тримають окрему вихідну матрицю.
При генерації це перетворення роблять лише для останньої позиції: щоб обрати наступний токен, потрібні логіти одного останнього hidden state — рахувати сотні тисяч логітів для кожного токена промпту нема сенсу.
Крок 14: Вибір наступного токена (sampling)
Отримані logits проходять через ланцюжок семплерів: спочатку застосовуються штрафи за повтори й температура, потім фільтри (top-k, top-p та ін.), а наприкінці — фінальний семплер (міростат, жадібний вибір або розподіл). Саме цей токен додають у контекст, і генерація повторюється з кроку 1 до досягнення кінця або потрібної довжини.
У бекенді LLama.cpp можна складати ланцюжки семплерів, спочатку проміжні, потім ланцюжок має завершуватися фінальним семплером.
Порядок застосування семплерів у llama.cpp:
1. Штрафи за повтори: - repeat_penalty - frequency_penalty - presence_penalty - DRY (штраф за повтор n-грам)
2. Фільтрація токенів: - top_k - typical_p - top_p - min_p - XTC
3. Масштабування логітів: - temperature
4. Граматичні обмеження: - grammar
5. Фінальний вибір токена: - mirostat (v1 або v2) або - greedy / dist (випадкова вибірка)Приклад з 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-Kllama_sampler_chain_add(smpl, llama_sampler_init_typical (0.95f, 1)); // Locally Typicalllama_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.05f, 1)); // Min-Pllama_sampler_chain_add(smpl, llama_sampler_init_xtc (0.9f, 1.0f, 1, LLAMA_DEFAULT_SEED)); // XTCllama_sampler_chain_add(smpl, llama_sampler_init_top_n_sigma(1.5f)); // Top-nσllama_sampler_chain_add(smpl, llama_sampler_init_temp (0.7f)); // Temperaturellama_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_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 V1llama_sampler_chain_add(smpl, llama_sampler_init_mirostat_v2(LLAMA_DEFAULT_SEED, 5.0f, 0.1f)); // Mirostat V2llama_sampler_chain_add(smpl, llama_sampler_init_greedy()); // Greedyllama_sampler_chain_add(smpl, llama_sampler_init_dist(LLAMA_DEFAULT_SEED)); // Dist
// після генерації не забути звільнити ланцюжок:llama_sampler_free(smpl);Про llama.cpp у мене є окрема стаття:
Приклад сторінки налаштування семплерів із застосунку https://github.com/a-ghorbani/pocketpal-ai


Проміжні семплери
Temperature (temp)
керує ступенем випадковості при виборі наступного токена з розподілу ймовірностей, обчисленого моделлю. Температура звужує або розширює "лійку вибору" наступного слова. Чим вона нижча — тим вужча лійка, чим вища — тим ширша.
Після того як модель передбачила логіти (сирі значення ймовірностей) для всіх можливих токенів, вони проходять через softmax-функцію. Температура впливає на цю функцію:
P(token) = softmax(logits / temperature)Ефект параметра temperature:
T = 1 логіти залишаються як є.
T < 1 (наприклад, 0.7) модель посилює відмінності між токенами — ймовірні токени стають ще ймовірнішими. Поведінка стає передбачуванішою.
T > 1 (наприклад, 1.5) відмінності між токенами згладжуються — зростає шанс вибрати менш імовірний токен. Поведінка стає різноманітнішою й ризикованішою.
T → 0 softmax перетворюється на argmax, і завжди обирається один найімовірніший токен.
Вихідні логіти:Token A: 3.0Token B: 2.5Token C: 1.0
Після softmax без змін (temp = 1.0):A: 57%B: 35%C: 8%
Зі зниженою температурою (temp = 0.5):A: 72%B: 27%C: 1%
З підвищеною температурою (temp = 1.5):A: 51%B: 36%C: 13%Top-k
модель обирає наступний токен тільки з k найімовірніших. Усі інші токени відкидаються, незалежно від їхньої абсолютної ймовірності.
є 6 токенів з такими ймовірностями:A: 0.35B: 0.30C: 0.15D: 0.10E: 0.06F: 0.04
Якщо top_k = 3, то залишимо лише:A: 0.35B: 0.30C: 0.15Top-p (nucleus sampling)
обирається мінімальна множина токенів з найбільшими ймовірностями, сума яких перевищує заданий поріг p. Решта токенів повністю відсікаються, навіть якщо вони мали високий ранг.
модель видала ймовірності токенів:A: 0.50B: 0.25C: 0.15D: 0.08E: 0.02
При top_p = 0.9 набираємо токени з найбільшими ймовірностями,доки їхня сума не досягне порога:0.50 + 0.25 + 0.15 = 0.90 — залишаються A, B, CD і E будуть відкинуті — потім імовірності тих, що залишилися, нормалізуються заново.Min-p
відсікаються всі токени, чия ймовірність нижча за поріг, заданий як частка p від імовірності найімовірнішого токена. Наприклад, при min_p = 0.1 та ймовірності лідера 0.50 поріг становитиме 0.05 — усе, що нижче, відкидається. Поріг відносний: коли модель упевнена, фільтр жорсткіший, коли розподіл плаский — м’якший.
Typical sampling
обирає токени, близькі до «типового» рівня несподіваності (surprisal), відсікаючи надто передбачувані й надто рідкісні. На відміну від top-k і top-p, він орієнтується на медіанне значення surprisal, а не на ймовірність токенів.
Repetition penalties, Frequency penalty, Presence penalty
знижує шанси повторного вибору вже згенерованих токенів, зменшуючи їхні логіти. Це допомагає уникнути нав’язливих повторів у тексті й робить вивід різноманітнішим.
Logit bias
метод, що дозволяє вручну змінити ймовірність окремих токенів перед застосуванням softmax, додаючи або віднімаючи значення з їхніх логітів. Це дає точковий контроль: можна, наприклад, змусити модель уникати певних слів або, навпаки, частіше їх обирати.
Tail Free Sampling (TFS)
відкидає довгий «хвіст» розподілу, регулюючи щільність імовірностей.
DRY (Don't Repeat Yourself)
штрафує токени, які продовжили б уже наявні в тексті n-грами: що довший повтор виходить, то сильніший штраф. Добре прибирає зациклення на цілих фразах, не чіпаючи поодинокі повтори слів.
Grammar
використовує правила граматики (зазвичай у вигляді CFG) для жорсткого обмеження допустимих токенів на кожному кроці генерації. Замість зміни логітів, він просто забороняє всі токени, що не відповідають поточному допустимому стану граматики, забезпечуючи строго структурований вивід.
та інші
Фінальні семплери
Greedy
найпростіший спосіб генерації, при якому модель завжди обирає токен з найвищою ймовірністю (максимальний логіт). Він швидкий і детермінований, але часто призводить до одноманітного й передбачуваного тексту.
Random
вибір наступного токена випадково, пропорційно його ймовірності після всіх застосованих семплерів (top-k, top-p тощо). Це протилежність жадібного вибору (greedy), де завжди береться токен з максимальною ймовірністю.
Dist
так у llama.cpp називається фінальна випадкова вибірка: токен обирається випадково, пропорційно підсумковому розподілу ймовірностей після всіх фільтрів — те саме, що Random вище, з фіксованим seed для відтворюваності.
Mirostat v1
підтримує заданий рівень «здивування» (перплексії), динамічно коригуючи вибір слів. Він допомагає уникнути надмірних повторів (пастка нудьги) і незв’язності (пастка плутанини), забезпечуючи збалансовану та якісну генерацію тексту.
Mirostat v2
підтримує заданий рівень несподіваності (перплексії) за допомогою точнішого керування, ніж Mirostat v1. Він використовує розширений механізм зворотного зв’язку, що дозволяє динамічно коригувати вибір слів для досягнення стабільної якості тексту.
та інші
Крок 15: Ітерація генерації
Після того як модель обрала наступний токен, його ID додається в кінець вхідної послідовності — повторна токенізація не потрібна, обраний токен уже є токеном.
Потім цикл повторюється: модель рахує увагу вже з урахуванням нового токена й передбачає наступний, і так доти, доки не будуть виконані умови припинення.
Перераховувати при цьому всі попередні токени заново не потрібно: їхні K- і V-вектори зберігаються в KV-кеші, і на кожній ітерації по-справжньому обробляється лише новий токен — саме тому кроки генерації займають приблизно однаковий час.
Звідси дві фази роботи моделі, знайомі кожному за відчуттям «подумала — і рівно друкує». Спочатку prefill: увесь промпт обробляється одним паралельним проходом, заповнюючи KV-кеш, — це пауза до першого слова, і вона тим довша, чим довший промпт. Потім decode: токени генеруються по одному, кожен приблизно за однаковий час. Тому швидкість моделі описують двома різними числами — час до першого токена і токени за секунду.
Такий цикл дозволяє по одному токену будувати всю вихідну послідовність.
Умови завершення
Генерація завершується, коли модель видає спеціальний токен кінця послідовності (EOS) або коли досягається заздалегідь задана максимальна довжина послідовності.
Це обмеження запобігає нескінченним або надто довгим відповідям.
Також генерацію зупиняють стоп-послідовності: заздалегідь задані рядки (наприклад, маркер початку наступної репліки діалогу), з появою яких вивід обривається.
Крок 16: Перетворення токенів на слова
Після того як модель згенерувала потрібну кількість токенів, їх перетворюють назад на текст — це називається детокенізація. Токенізатор бере послідовність чисел (токенів) і переводить їх у слова та розділові знаки. На практиці чат-інтерфейси роблять це по ходу — це і є стримінг: кожен обраний токен одразу детокенізується й дописується на екран, тому відповідь з’являється по словах, а не цілком наприкінці.
Як модель навчали
Усе описане вище — робота вже готової моделі. Сама вона виходить у кілька етапів.
Попереднє навчання (pretraining):
Модель трильйони разів розв’язує одну й ту саму задачу — передбачити наступний токен у тексті з величезного корпусу (веб, книги, код). Жодної розмітки не потрібно: правильна відповідь — це просто справжній наступний токен. На цьому етапі модель вивчає мову, факти й закономірності, але вміє лише продовжувати текст.
Інструкційне донавчання (SFT, Supervised Fine-Tuning):
Модель донавчають на прикладах «запит → гарна відповідь», написаних людьми або відібраних з генерацій. Після цього вона відповідає на запитання, а не продовжує їх.
Вирівнювання (RLHF, DPO):
Люди порівнюють пари відповідей моделі й обирають кращу; на цих уподобаннях модель донавчають давати корисні та безпечні відповіді. Класичний спосіб — навчання з підкріпленням за людськими оцінками (RLHF); простіша сучасна альтернатива — DPO, де вподобання використовуються напряму як навчальні приклади.
Reasoning-моделі
«Думаючі» моделі з таблиць вище (DeepSeek R1, Qwen 3 у режимі міркувань, Gemma 4) механічно нічим не відрізняються: це ті самі трансформери, що генерують ті самі токени. Різниця в навчанні: їх додатково вчили з підкріпленням на задачах з перевірюваною відповіддю, нагороджуючи за правильний результат, — і модель сама навчилася перед відповіддю писати довгий ланцюжок міркувань, чернетку, яку інтерфейс потім ховає за спойлер. Платять за це токенами: «думаюча» відповідь може бути в рази довшою і повільнішою за звичайну.
Звідки беруться галюцинації
Прямо з механіки генерації. Модель зобов’язана обрати наступний токен завжди — у неї немає вбудованого стану «не знаю», є лише розподіл імовірностей, з якого щось буде обрано. Якщо знань про предмет у вагах мало, розподіл усе одно видасть правдоподібне на слух продовження: правильну форму відповіді з вигаданим змістом. Тому галюцинації — не баг, який колись полагодять, а властивість самого підходу; їх пригнічують навчанням (модель вчать відмовлятися від відповіді), інструментами на кшталт RAG і вебпошуку, але повністю не усувають.
Цікаві питання та відповіді
Чи можливо в кількох реченнях описати, як працює нейромережа?
Спробую. Модель — це величезна функція, навчена на одну задачу: за початком тексту передбачити, який токен (шматочок слова) найімовірніше йде наступним. Ваше запитання перетворюється на послідовність токенів, модель обчислює ймовірності всіх можливих продовжень, з них обирається один токен, дописується до тексту — і все повторюється, токен за токеном, доки не складеться відповідь. А все «розуміння» — у тому, що за час навчання на величезному корпусі текстів ваги моделі навчилися передбачати продовження з урахуванням змісту й контексту всієї фрази, а не просто за частотою слів.
Чому в LLM моделях на вході дається питання, а на виході виходить відповідь, а не перефразоване питання?
Модель навчена на текстах «питання→відповідь», тому, побачивши питання, далі генеруються не слова самого питання, а найімовірніше продовження — відповідь.
Під час інструкційного навчання (SFT) і RLHF її «заохочують» за корисні відповіді, а не за повтор формулювання, тому параметри зсуваються в бік відповідей.
У термінах імовірностей: для питання ймовірність наступного токена-відповіді вища, ніж токена, що повторює питання, і декодер обирає саме його.
Дані, на яких навчається LLM модель, застарівають, які є можливості для того, щоб модель генерувала актуальні відповіді?
Retrieval-Augmented Generation (RAG):
Модель при запиті шукає в актуальній зовнішній базі (пошук за векторним або текстовим індексом) релевантні документи і використовує їхній контекст при генерації відповіді. Так можна отримувати свіжу інформацію без перетренування основної мережі
Параметр-ефективне донавчання (LoRA, Adapters):
Замість повного перенавчання моделі вбудовують невеликі адаптери або low-rank матриці (LoRA), які навчаються на нових даних. Це дозволяє швидко й недорого «навчити» модель новим фактам або доменам
Про LoRA я зробив окрему статтю:
Цільове редагування ваг (Model Editing):
Алгоритми типу MEMIT/ROME локально коригують ваги моделі для додавання або оновлення конкретних фактів, не зачіпаючи решту знань
Окремі бази знань і графи:
Замість зберігання фактів усередині параметрів LLM виносите їх у зовнішні KB або графи знань, які регулярно оновлюються, а модель лише запитує в них інформацію
Інтеграція з вебпошуком і API:
Підключення моделі до живих джерел — вбудований вебпошук у чат-ботах, виклик зовнішніх API та інструментів (function calling, MCP-сервери) — напряму повертає свіжий вміст.
Яким чином LLM модель розуміє різні мови?
Модель “розуміє” різні мови завдяки тому, що в процесі навчання вона бачить тексти багатьма з них і вчиться передбачати наступний фрагмент незалежно від мови. Основні моменти:
Великий багатомовний корпус:
Для навчання збирають тексти (вікіпедія, книги, вебсторінки з Common Crawl та інші) десятками й сотнями мов. Наприклад, у відкритій моделі BLOOM було близько 46 мов, причому частка кожної залежить від обсягу доступних даних
Спільна токенізація subword:
Використовуються алгоритми на кшталт BPE або SentencePiece, які розбивають слова на фрагменти (subword) і включають до словника символи й послідовності з різних абеток. Так модель оперує єдиним набором токенів для всіх мов
Універсальна архітектура трансформера:
У трансформері одні й ті самі ваги беруть участь при обробці будь-яких мов. Тому при навчанні на різних мовах модель знаходить спільні патерни (синтаксис, семантика) і використовує перехресне перенесення знань (cross-lingual transfer)
Не всі мови і не в усіх обсягах:
Навчають лише на тих мовах, де є достатній обсяг текстів. Рідкісні або низькоресурсні мови потрапляють в окремі донавчання або отримують меншу частку даних, тому якість генерації на них нижча
Спеціальні донавчання та адаптери:
Щоб покращити знання маловідомих мов, застосовують донавчання (continual pretraining) на локальних даних або вставляють адаптери (adapters, LoRA), які тонко коригують знання моделі під конкретну мову.
Додаткова інформація
Точність вектора
Також числа з плаваючою точкою, з яких складається вектор, можуть мати різну точність:
Менша точність зменшує розмір моделі й пришвидшує обробку.
Зазвичай моделі навчаються на повній точності FP32 (float32), тобто вектор складається з чисел розмірністю 32 біти.
Квантування (Quantization)
для зменшення точності для полегшення моделі використовується квантування — процес перетворення чисел з плаваючою точкою (наприклад, FP32) на компактніші цілочисельні представлення (наприклад, INT8), зі збереженням наближеного значення.
При цьому вводиться невелика втрата точності, але вона часто не впливає критично на якість виводу.
Формат: FP32 (float32), 32 біти = 4 байтиПризначення: Повна точність. Використовується під час навчання моделей, а також при точному інференсі.Забезпечує максимальну точність, але потребує багато пам’яті та обчислювальних ресурсів.Бінарне значення: 01000000 01001001 00001111 11011011Фактичне значення: 3.14159Можливі типи квантування: Не використовується — це повний (не квантований) формат.
Формат: FP16 (float16), 16 біт = 2 байтиПризначення: Половинна точність. Використовується для пришвидшеного навчання та виведення на GPU (наприклад, NVIDIA Tensor Cores).Швидше та вдвічі економніше за пам’яттю порівняно з FP32.Бінарне значення: 01000010 01001000Фактичне значення: ≈ 3.140625У GGUF-файлах неквантовані ваги такої точності позначаються як F16.
Формат: BF16 (bfloat16), 16 біт = 2 байтиПризначення: Альтернатива FP16, що використовується в TPU та деяких GPU. Має ту саму експоненту, що й FP32, але вкорочену мантису.Швидше та компактніше, при цьому зберігає діапазон FP32.Бінарне значення: 01000000 01001001(це просто перші два байти FP32-представлення — мантису вкорочено)Фактичне значення: ≈ 3.140625У GGUF-файлах позначається як BF16.
Формат: 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_S, Q4_K_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)Реальний Q2_K зберігає ще блокові масштаби, тому фактично витрачає близько 2.6 біта на вагу.
Формат: INT1 (Q1), 1 біт, scale = 2.0 (8 чисел в 1 байті)Призначення: Мінімально можлива точність. Використовується в бінарних нейромережах та прототипах.Зазвичай значення -1 або +1. Застосовується рідко в LLM, але може бути корисним у BNN (Binary Neural Networks).Квантизоване значення: 1Бінарне значення: 1Фактичне значення: 1 × 2.0 = 2.0Можливі типи квантування: BinaryNet, XNOR-Net (частіше в академічних/експериментальних BNN)Сучасний приклад екстремального квантування LLM — BitNet b1.58 з тернарними вагами -1/0/+1 (~1.58 біта на вагу).Також моделі можуть мати додаткові параметри квантування:
_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_S, K_M, K_L
K_S- Small — найменший і найагресивніше стиснутий варіант- Максимальна економія пам’яті на шкоду якості
K_M- Medium — середній компроміс між точністю та обсягом- Найпопулярніший вибір для локального запуску
K_L- Large — найбільший і найточніший з K-варіантів- Трохи більше пам’яті, трохи вища якість
Приклад назви моделі:"mistral-7b.Q4_K_M.gguf" — це означає:- Модель Mistral 7B- Використовується квантування Q4_K (4-бітна K-блокова)- Варіант розміру: Medium
Крім K-квантів у llama.cpp є новіші I-кванти(IQ2_XXS, IQ3_S, IQ4_XS та ін.) — вони дають кращу якістьза того самого розміру завдяки складнішій схемі кодування.Продуктове квантування (Product Quantization)
Метод, який дозволяє сильно стиснути вектори ознак (ембединги), розбиваючи їх на частини й кодуючи кожну частину через найближчий шаблон (кластер). Використовується у векторних базах і пошуку найближчих сусідів (наприклад, FAISS); ваги самих LLM ним зазвичай не стискають.
Як це працює:
Є вектор (наприклад, розміром 128 чисел). Це може бути ембединг тексту або зображення від нейромережі.
Розбиваємо вектор на шматки — наприклад, 8 частин по 16 чисел (128 / 8 = 16).
Для кожної позиції шматка навчаємо свій кодувальник (квантайзер) — на великих даних заздалегідь обираються, які "шаблони" (центри кластерів) схожі на можливі шматки.
Наприклад:
перший шматок може бути схожий на шаблон №12,
другий — на шаблон №3,
третій — на шаблон №88,
і так далі.
Зберігаємо лише номери цих шаблонів. Замість зберігання 128 чисел (float32 = 512 байт), зберігаємо, наприклад, 8 чисел (по 1 байту на шаблон) — усього 8 байт.
*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)
Скільки пам’яті потрібно моделі
Груба формула: кількість параметрів × байт на вагу, плюс KV-кеш, плюс трохи на активації.
Модель 7-8B:FP16 (2 байти на вагу) ≈ 14-16 ГБQ8 (1 байт на вагу) ≈ 7-8 ГБQ4 (~0.6 байта на вагу) ≈ 4-5 ГБ
Модель 27-32B у Q4 ≈ 16-20 ГБМодель 70B у Q4 ≈ 40+ ГБ
Плюс KV-кеш: залежить від довжини контексту,на десятках тисяч токенів — ще гігабайти.Практичне правило для локального запуску: дивитися не на «мільярди параметрів», а на розмір конкретного GGUF-файлу — він чесно показує, скільки пам’яті займуть ваги, — і залишати запас на контекст.
Способи запуску (бекенди) для LLM моделей
vLLM:
Основний серверний рушій LLM-інференсу: безперервний батчинг запитів і PagedAttention для економного KV-кешу; стандарт де-факто для розгортання відкритих моделей на GPU.
Формати: ваги Hugging Face (.safetensors)
SGLang:
Швидкозростаюча альтернатива vLLM з перевикористанням KV-кешу між запитами (RadixAttention); сильний на структурованій генерації та агентних навантаженнях.
Формати: ваги Hugging Face (.safetensors)
NVIDIA TensorRT-LLM:
Спеціалізована LLM-надбудова над TensorRT: компілює модель під конкретні GPU NVIDIA, максимальна швидкість ціною гнучкості.
Формат: скомпільований рушій
ONNX Runtime:
Кросплатформний рушій для виконання моделей у форматі ONNX.
Формат файлу: .onnx
TensorFlow (Inference API):
Стандартний рушій для моделей TensorFlow, підтримує SavedModel і заморожений граф.
Формати: папка SavedModel (saved_model.pb + variables/), єдиний файл .pb
LiteRT (колишній 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.
Формати: .mlpackage, застарілий .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 Sentis (раніше Barracuda):
Рушій для запуску нейромереж в іграх Unity, підтримує ONNX-моделі.
Формати: .onnx, .sentis
MLC LLM:
ML-компілятор і рушій для LLM, компілює ваги у свій IR і шардує їх.
Формат: каталог …-MLC з .bin-шардами та JSON-конфігами (наприклад, mlc-chat-config.json)
MediaPipe:
Фреймворк для створення мультимодальних пайплайнів (детекція, сегментація та ін.), спирається на TFLite.
Формати: .tflite (модель) + .pbtxt (граф)
llama.cpp:
C++-рушій для LLaMA-подібних моделей з підтримкою квантування на CPU/GPU.
Формат: .gguf (старий GGML-формат .bin застарів)
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 (опис мережі)
Застосунки для запуску LLM моделей
LM Studio:
Десктоп-застосунок для Windows, macOS і Linux з GUI для запуску локальних LLM (GPT-подібних).
Формати моделей: .gguf, .bin
Ollama:
Легкий CLI/GUI-клієнт для Windows, macOS і Linux, працює «з коробки» з LLM.
Формати моделей: .gguf, .bin
Jan:
Відкритий десктоп-клієнт для Windows, macOS і Linux у дусі LM Studio, але повністю open source; працює поверх llama.cpp.
Формати моделей: .gguf
Open WebUI:
Вебінтерфейс до Ollama і будь-яких OpenAI-сумісних серверів: чати, RAG за своїми документами, кілька користувачів; ставиться локально або на свій сервер.
Формати моделей: ті, що підтримує підключений бекенд
KoboldCpp:
Однофайлова збірка llama.cpp з вебінтерфейсом і багатими налаштуваннями семплерів, популярна для творчої генерації.
Формати моделей: .gguf
llama.cpp (prebuilt binaries):
Готові виконувані файли для Windows/macOS/Linux, дозволяють запускати LLaMA-подібні моделі без встановлення залежностей.
Формати моделей: .bin, .gguf
Окрема група — застосунки для генерації зображень (Stable Diffusion), не LLM:
Automatic1111 Stable Diffusion WebUI:
Найпопулярніший локальний вебінтерфейс для генерації зображень на Windows/macOS/Linux.
Формати моделей: .ckpt, .safetensors
DiffusionBee:
Настільний застосунок для macOS (є бета-збірки для Windows), «все в одному» для Stable Diffusion.
Формати моделей: .ckpt, .safetensors
InvokeAI:
Кросплатформний пакет з CLI і WebUI для генерації зображень на базі Stable Diffusion.
Формати моделей: .ckpt, .safetensors
Формат зберігання LLM моделей
.pt / .pth
Використовуються в PyTorch для збереження навчених моделей
Зберігають ваги й структуру моделі (або тільки ваги).
Ґрунтуються на серіалізації Python (pickle), що робить їх не дуже безпечними.
Підходять для навчання й донавчання.
Не оптимізовані для мобільного виводу або зовнішнього інференсу
Можуть займати багато пам’яті (FP32).
.safetensors
Альтернатива .pt, безпечний і швидкий формат для PyTorch.
Не використовує pickle, а отже безпечний при завантаженні.
Підтримує паралельне завантаження, що пришвидшує роботу.
Тільки для зберігання ваг (структура — окремо).
Підходить для інференсу й донавчання.
Підтримується HuggingFace, PyTorch, JAX.
.bin
Універсальний або частковий бінарний формат для ваг моделей, особливо в HuggingFace і старому GGML.
Може бути неформалізований (структура залежить від фреймворка).
Може містити повні ваги або квантовані дані.
Підходить для завантаження в кастомні рушії (наприклад, llama.cpp).
Вимагає точного знання, як інтерпретувати вміст.
Часто використовується в старих проєктах або кастомних пайплайнах.
.gguf
Стандарт екосистеми GGML для запуску LLM у llama.cpp, Ollama, LM Studio.
Включає все в одному файлі: ваги, словник, токенізатор, параметри моделі.
Підтримує різні типи квантування (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 квантування.
Працює з інтерпретатором LiteRT (колишній TensorFlow Lite), легко вбудовується в Android.
Не можна навчати або донавчати — тільки запуск.
Спочатку був розрахований на компактні CNN/RNN-моделі, але зараз через LiteRT і MediaPipe LLM Inference на ньому запускають і невеликі LLM (наприклад, Gemma).
Формат MLC LLM
Запуск LLM на Android/iOS/GPU через компіляцію; єдиного файлу-контейнера немає — модель зберігається як каталог із шардами ваг і JSON-конфігами плюс скомпільована бібліотека.
Модель попередньо компілюється в ефективний байткод.
Підтримує Vulkan/Metal/OpenCL для прискорення.
Використовується для Gemma, Mistral, LLaMA на телефоні.
Вимагає складної підготовки (TVM, скрипти, налаштування).
Чудово підходить для мобільних застосунків без серверів.