Головна / Статті / Від промпту до токена: як LLM перетворює текст на числа й назад

Від промпту до токена: як LLM перетворює текст на числа й назад

Простежмо шлях короткого промпту через токенізацію, ембединги, attention, feed-forward-шари, softmax і семплювання, щоб побачити, як модель насправді обирає кожне наступне слово.

1615 слів

Усередині моделі немає англійської

Натисніть Enter, і GPU почне перемелювати мільярди операцій із рухомою комою ще до появи першого токена. Жодна з цих операцій не торкається англійського слова.

Візьмімо знайомий фрагмент:

"The cat sat on the "

На момент, коли на екрані з'явиться "mat", цей фрагмент уже розібрано на частини, перетворено на геометрію з тисячами вимірів і проведено крізь десятки однакових математичних шарів.

Ми говоримо про це недбало. Модель «прочитала» промпт, «зрозуміла» контекст і «обрала» розумне продовження. Нічого з цього буквально не відбувається. У запущеній програмі немає англійської, немає граматичних правил, що відокремлюють підмет від присудка, і немає збережених словникових визначень. Відбувається конвеєр тензорних операцій: рядок стає цілими числами, числа — координатами в просторі, координати перемішуються в десятках шарів, а наприкінці один раз кидається зважений гральний кубик.

Щоб по-справжньому зрозуміти мовні моделі, відкиньте метафори й простежте за даними. Ось маршрут, який проходить ваш текст.

Крок 1: токенізатор

Нейромережі не вміють працювати із сирими символами: з літерою «c» жодної алгебри не зробиш. Їм потрібні числа. Тому ще до того, як промпт наблизиться до моделі, його обробляє окрема програма — токенізатор.

Токенізатор ріже рядок на шматки, які називають токенами. Токеном може бути ціле слово, склад, префікс або окремий розділовий знак. Наша фраза акуратно ділиться на п'ять:

  • "The"
  • " cat"
  • " sat"
  • " on"
  • " the"

Зверніть увагу на пробіли на початку. Це не друкарські помилки. Сучасні токенізатори приклеюють пробіл перед словом до токена, тому "cat" і " cat" — два різні токени.

Кожен токен відповідає одному цілому числу у фіксованій таблиці словника. Зі словником, скажімо, на 50257 токенів фраза перетворюється на п'ять чисел:

[464, 3797, 3372, 319, 262]

Жоден шар мовної моделі ще не запускався, а рядок уже зник. Лишилося тільки п'ять цілих чисел.

Крок 2: таблиця ембедингів

Тепер до справи береться сама мережа, і її перше завдання — розв'язати базову проблему. Цілі числа марні для матаналізу. Число 3797, що позначає « cat», анітрохи не ближче до «dog», ніж до «airplane». Це довільні мітки, а з мітками геометрією не займешся.

Рішення — перетворити кожне число на щільний список чисел із рухомою комою, вектор ембедингу. Уявіть гігантську таблицю пошуку в пам'яті GPU. Припустімо, у словнику 32 000 токенів, а прихована розмірність — 4096. Тоді таблиця — це матриця з 32 000 рядків на 4096 стовпців, і модель просто шукає в ній:

  • Токен 464 дістає рядок 464.
  • Токен 3797 дістає рядок 3797.
  • Токен 3372 дістає рядок 3372.
  • Токен 319 дістає рядок 319.
  • Токен 262 дістає рядок 262.

На цьому етапі немає жодної арифметики. Це звичайне читання з пам'яті. На виході — матриця форми [5, 4096]: п'ять рядків по 4096 неперервних значень.

Тепер кожен токен — точка в 4096-вимірному просторі, де напрям несе зміст, а положення кодує зв'язки, вивчені на мільярдах навчальних прикладів.

Перш ніж вектори підуть далі, додається ще один інгредієнт — інформація про позицію. Матричні операції самі по собі можуть перемішати порядок слів, тому модель підмішує в координати сигнали позиції. Без цього «The cat sat on the» і «The on sat cat the» мали б для мережі однаковий вигляд.

Крок 3: стек трансформера

Матриця [5, 4096] входить у трансформер. У типовій архітектурі на 7 млрд параметрів стек складається з 32 однакових блоків. Матриця зберігає форму на кожному з них: входить у шар 1 і виходить із шару 32 як [5, 4096]. Змінюється інформація, що зберігається в цих числах.

Одразу після таблиці ембедингів кожен вектор існує сам по собі. Вектор для " sat" знає, що означає «сидів», але не може сказати, хто сидів, де й чому. Це виправляють два механізми всередині кожного блоку.

Attention

Тут вектори спілкуються. Attention дає кожній позиції змогу озирнутися на попередні й витягти з них те, що важливо.

Візьмімо п'ятий токен, " the". Він вимірює схожість із "The", " cat", " sat" і " on". Оскільки йому треба допомогти передбачити, який іменник іде після прийменника, його вектор активно спирається на дію та на підмет. Через скалярні добутки між цими позиціями шар attention оновлює кожен вектор контекстом сусідів.

Feed-forward-мережа

Після того як токени обмінялися інформацією, кожен рядок окремо проходить крізь feed-forward-мережу (MLP). Якщо attention — це розмова токенів між собою, то цей шар — кожен токен, що рахує своє. Він проєктує щойно зібраний контекст крізь тисячі вивчених ваг, що дає моделі змогу вмикати фактичні асоціації, збережені в її параметрах.

Усе це повторюється 32 рази. На шарі 32 п'ятий вектор уже не загальний замінник слова " the": він увібрав історію всього речення. Тепер він позначає означений артикль, що йде після кота, який сидить.

Зворотна проєкція (unembedding)

Для наступного слова перші чотири рядки вже не потрібні, тож модель бере лише останній: один вектор із 4096 чисел. Тепер задача ембедингу розв'язується у зворотний бік. У моделі є точка в 4096-вимірному просторі, а потрібне слово.

Друга матриця, матриця unembedding (часто це просто транспонована таблиця ембедингів), множиться на цей вектор і розгортає його 4096 значень назад на весь словник. Результат — одна ненормована оцінка для кожного токена словника, наприклад:

Нагорі ви побачили б " mat" із 16,2 та " rug" із 13,8, за ними " floor" із 12,1 та " couch" із 10,4. Далеко внизу списку — "LeBron James" із -12,5.

Ці сирі оцінки називають логітами. Що вищий логіт, то сильніше вивчені параметри мережі знайшли геометричну відповідність між підсумковим станом промпту й цим кандидатом.

Softmax

Логіти не можна використовувати як є. Деякі великі й додатні, інші від'ємні, а їхня сума довільна. Тому модель проганяє весь вектор крізь softmax: для кожного елемента береться e в степені його оцінки й ділиться на суму таких експонент за всіма елементами.

Softmax робить дві речі. Експонента робить кожне значення строго додатним. Ділення на суму змушує все в сумі давати 1,0. Виходить справжній розподіл імовірностей за всіма 32 000 токенами, приблизно такий:

Лідер, " mat", отримує 68,4%. " rug" — 17,1%, " floor" — 9,2%, " couch" — 4,1%. На дальньому кінці " LeBron James" лишається з 0,000001%.

Семплювання

Усе до цього моменту детерміноване. За однакових ваг і однакового входу ймовірності не розійдуться навіть в одному знаку після коми. Непередбачуваність LLM з'являється на останньому кроці — семплюванні.

Якби система завжди брала найімовірніший токен, тут " mat" із 68,4%, це було б жадібне декодування. Воно детерміноване, але на довгих відповідях схильне скочуватися в одноманітні, неприродні цикли. Тому програма кидає зважений кубик. Приблизно у 68% випадків випадає " mat", приблизно в 17% — " rug".

Параметр temperature у виклику API змінює форму цієї кривої до кидка. Низька температура посилює розриви й може підштовхнути " mat" до 95%. Висока температура згладжує криву, і токени нижче в рейтингу отримують набагато більше шансів бути обраними.

Кубик кинуто, і перемагає токен з ID 2603.

Детокенізація та цикл

Програма шукає ID 2603 у зворотній карті словника, знаходить рядок " mat" і надсилає його у ваш термінал чи браузер.

Тут виявляється головний факт інференсу: модель жодної миті не знає, що йде після " mat". Вона не може планувати на три слова вперед і не зберігає план абзацу, який пише. Щоб просунутися далі, вона додає нове слово до входу й формує цілком новий промпт:

"The cat sat on the mat"

Вона токенізує всі шість слів, отримує ембединги всіх шести рядків, проганяє всі шість позицій крізь усі 32 шари, обчислює логіти для шостої позиції, застосовує softmax, кидає кубик і обирає сьомий токен. Цей повний цикл, від рядка до тензора й до кидка кубика, повторюється для кожного токена, що з'являється на вашому екрані.

Запитання, які викликає цей конвеєр

Якщо дивитися очима інженера, ця механіка викликає незручні запитання. Схоже, кожне згенероване слово вимагає прогнати весь накопичений діалог крізь мільярди операцій, тож довгі чати мали б ставити залізо на коліна. (Чому цього не стається, пояснює кешування в пам'яті — це тема для окремої розмови.)

Ще загадковіший перший крок — стрибок від людської мови до чисел. Ще до спрацювання attention і запуску бодай одного шару промпт уже було нарізано на довільні числові шматки. Чому підслова, а не символи, цілі слова чи сирі байти? Чому незвичні рядки або рядки коду на Python змушують кількість токенів поводитися непередбачувано? Токенізація — перший блок у ланцюжку, і саме його варто відкрити наступним.