03 / 37 · Основы работы с текстом
Токенизация
Почему не годятся ни слова, ни символы; как byte-pair encoding выучивает словарь подслов, сливая частые пары, и почему это жадное сжатие; как обученный токенизатор кодирует новый текст; чем отличаются WordPiece и SentencePiece; и какой компромисс в выборе размера словаря наследует каждая следующая модель.
На этой странице 10
Предыдущая заметка закончилась двумя проблемами словных единиц: слово, которого не было в обучающем корпусе, может стать лишь [UNK], а большинство типов слов в словаре с длинным хвостом встречается слишком редко, чтобы выучить вектор для каждого. В этой заметке мы выводим стандартное решение — токенизацию по подсловам (subword tokenization), — отталкиваясь от двух крайностей, между которыми она лежит; основной алгоритм — byte-pair encoding.
Обозначения. Токен — единица словаря размера ; токенизатор переводит строку в токены и обратно. Корпус, разбитый на слова, описывается счётчиком каждого типа слова (word type) . Сегментация — последовательность токенов, конкатенация которых равна . Для единиц величина считает вхождения , а — вхождения , за которым непосредственно следует ; оба счётчика взвешены частотами слов. Длина корпуса в токенах — (после слияний пишем ), а — среднее число токенов на слово. Модель видит последовательность из идентификаторов токенов и переводит каждый в вектор ширины .
Маршрут чтения. Ядро заметки — отправная точка, разобранный пример BPE, кодирование нового текста, что определяет число слияний и специальные токены. Аргумент о сжатии, оценка WordPiece (поточечная взаимная информация) и униграммная модель (обучается EM-алгоритмом, декодируется динамическим программированием) дают глубину, и при первом чтении их можно пропустить.
Отправная точка: слова или символы#
Каждая следующая модель получает целочисленные идентификаторы , где , поэтому токенизатор фиксирует два числа, которые тянут в противоположные стороны: и длину данного текста.
Слова. Последовательности короткие, и каждый токен несёт смысл, но словарь открыт: каждая опечатка, имя, число и словоформа — новый тип, поэтому в отложенном (held-out) тексте всегда найдутся слова, которые схлопнутся в [UNK]. Родственные формы вроде sing, sings и singer получают не связанные между собой идентификаторы, а стемминг из предыдущей заметки латает это для каждого языка отдельно, выбрасывая информацию. Сильнее всего страдают флективные языки: у русского существительного до двенадцати форм падежа и числа.
Символы. Словарь крошечный и закрытый, поэтому в пределах алфавита неизвестных токенов нет. Но средняя длина английского слова около пяти букв, поэтому вырастает примерно в пять раз, за что каждая следующая модель платит вычислениями (квадратично — в self-attention), а отдельный символ почти не несёт смысла.
| Уровень слов | Уровень символов | |
|---|---|---|
| Словарь | Открытый, – | Закрытый, – |
| Неизвестные токены | Часто | Никогда (в пределах алфавита) |
| Длина последовательности | Короткая | Примерно в 5 раз длиннее |
| Смысл на токен | Высокий | Почти никакого |
Одна фраза, четыре словаря
Byte-pair encoding#
Byte-pair encoding (BPE) появился как алгоритм сжатия, который заменяет самую частую пару соседних байтов неиспользованным байтом (Gage, 1994); Sennrich, Haddow and Birch (2016) применили этот цикл к символам внутри слов.
Обучение начинается с нормализованного текста, предварительно токенизированного (pre-tokenization) на слова, чтобы ни один токен не пересекал границу слова; важны только счётчики слов:
-
Разбиваем каждое слово на символы; это . Начальный словарь — алфавит.
-
На -м шаге слияния (merge) считаем каждую пару соседних единиц на сегментациях , взвешивая по частоте слова:
-
Выбираем самую частую пару с фиксированным правилом для равных счётчиков.
-
Добавляем в словарь, записываем правило под номером и заменяем каждое вхождение пары, проходя слева направо; получаем .
-
Повторяем, пока словарь не достигнет целевого размера или пока ни одна пара не будет встречаться больше одного раза.
Результат обучения — словарь и упорядоченный список слияний; порядок — часть модели. Sennrich et al. (2016) также добавляют маркер конца слова, чтобы можно было восстановить пробелы.
Разобранный пример#
Возьмём счётчики слов sing 6, singer 3, sings 4, ring 5, rings 2, song 3, для наглядности без маркера конца слова. Это 23 словоупотребления и символа над алфавитом , так что в начале .
Каждое слово содержит n g, поэтому . Каждое слово, кроме song, содержит i n, поэтому . Остальные пары реже: s i 13, r i 7, g s 6, а e r, g e, o n, s o — по 3. Первое слияние — n + g → ng, оно сокращает корпус со 104 до 81 токена:
Восемь слияний BPE на корпусе из шести слов
Итоговый словарь — e g i n o r s ng ing sing ring sings er ong song, а корпус — 28 токенов на 23 слова: 1.22 токена на слово против 4.52 символа на слово. Решает только частота: sings (4 вхождения) стало одним токеном, а rings (2) осталось ring s. Общие куски появляются сами (ing, sing er). Когда счётчики становятся маленькими, равенства между ними встречаются часто; подходит любое детерминированное правило их разрешения, а другое правило на шаге 6 дало бы другой, но столь же допустимый словарь.
Почему самая частая пара#
Дополнительный материал: почему BPE — жадный компрессор своего обучающего корпуса. Остальная часть заметки на него не опирается.
Каждое вхождение слитой пары превращает два токена в один, поэтому
Тождество требует, чтобы вхождения не перекрывались, а это нарушается только при : в e e e пара e e посчитана дважды, но слита один раз. В разобранном примере каждое уменьшение равно счётчику: , , и так до .
Каждое слияние укорачивает корпус на счётчик слитой пары
Итак, BPE — жадный компрессор: каждый шаг добавляет токен, который сильнее всего укорачивает обучающий корпус. Жадность не означает оптимальности: раннее слияние может забрать символы, которые понадобились бы более выгодному позднему слиянию.
Кодирование нового текста#
Обученный токенизатор BPE кодирует новое слово, повторяя слияния по порядку на его символах. Реализации вроде GPT-2 вместо этого многократно сливают соседнюю пару с наименьшим номером правила. Обе процедуры дают одно и то же: слияние создаёт новые соседние пары только вокруг новой единицы , а каждое правило с участием выучено позже самой , поэтому наименьший присутствующий номер никогда не уменьшается и цикл встречает правила в возрастающем порядке. Так слово, которого не было в корпусе, получает осмысленные куски: singers становится sing er s.
Кодирование слов, которых не было в корпусе
Три следствия:
- Способ кодирования — часть словаря. Жадный поиск самого длинного совпадения (greedy longest match) по тем же 15 записям разбивает
sneeringкакs n e er ing; повторение слияний даётs n e e ring. - Неизвестные символы по-прежнему возможны. Символ вне базового алфавита всё равно становится
[UNK]. Байтовый BPE (byte-level BPE) (Radford et al., 2019) снимает проблему, беря за основу 256 значений байта UTF-8: 50 257 записей GPT-2 — это 256 байтов, 50 000 слияний и один токен конца текста. - Куски — частые строки, а не морфемы. Они часто совпадают с суффиксами (
ing,er,s), потому что суффиксы часты, ноrisingстановитсяr i sing.
Что определяет число слияний#
Число слияний — главный гиперпараметр: , и чем больше слияний, тем короче последовательности. У обеих сторон есть цена:
- Таблица эмбеддингов содержит параметров: 38.4 млн при и и 263 млн у Llama 3.2 1B (128 256 токенов, ).
- Выходной слой вычисляет логитов в каждой позиции, работы на токен, и добавляет ещё одну матрицу , если её веса не связаны с (weight tying).
- Записи поздних слияний редки, поэтому их эмбеддинги видят мало данных.
- Более длинные последовательности — это последовательных шагов рекуррентной модели, на слой self-attention и меньше слов в контекстном окне фиксированной длины.
Число токенов на слово падает с ростом словаря
Каждое удвоение даёт всё меньший выигрыш. Практические словари гораздо больше, чем в этом небольшом эксперименте: 30 522 записи у BERT, 50 257 у GPT-2, 100 000–250 000 у современных многоязычных моделей.
WordPiece и SentencePiece#
WordPiece (Schuster and Nakajima, 2012; Wu et al., 2016) — токенизатор BERT (Devlin et al., 2019). Он выполняет цикл слияний BPE с двумя отличиями.
Первое — критерий слияния. Исходный метод добавляет единицу, которая сильнее всего увеличивает правдоподобие обучающих данных при языковой модели над единицами; широко используемая реконструкция (Hugging Face, LLM Course, ch. 6) ранжирует пары по величине
Если всего единиц, а вероятности оцениваются относительными частотами , , то
С точностью до это отношение, логарифм которого — поточечная взаимная информация (pointwise mutual information) и . BPE предпочитает частые пары; WordPiece — связные (cohesive), части которых редко встречаются порознь. BERT помечает каждый неначальный кусок префиксом ##. На корпусе разобранного примера два критерия расходятся с первого шага:
BPE и WordPiece выбирают разные первые слияния
Процедура обучения WordPiece в библиотеке tokenizers игнорирует эту величину и сливает пары по частоте, как BPE.
Второе отличие — кодирование. WordPiece берёт самую длинную запись словаря, совпадающую с началом слова, затем самое длинное совпадение с ## для остатка и так далее; если в какой-то позиции подходящего куска нет, всё слово становится [UNK].
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("bert-base-uncased") # |V| = 30,522
tok("Random text to encode").tokens()
# ['[CLS]', 'random', 'text', 'to', 'en', '##code', '[SEP]']
Токенизатор BERT с учётом регистра (cased) так же разбивает stationery на station + ##ery, а 10,925 — на 10, ,, 92, ##5.
SentencePiece (Kudo and Richardson, 2018) убирает предварительную токенизацию. Он обучается на сыром тексте, в котором пробел — обычный символ ▁, поэтому ему не нужен разделитель на слова, он работает с письменностями без пробелов, такими как китайская, и восстанавливает текст при детокенизации в точности, с поправкой лишь на нормализацию. Помимо BPE, SentencePiece поддерживает униграммную языковую модель (unigram language model) (Kudo, 2018).
Униграммная модель работает в противоположном направлении: она прореживает большой словарь кандидатов. У каждого токена есть вероятность , а вероятность сегментации считается так, как будто её токены независимы:
Обучение чередует повторное оценивание EM-алгоритмом с удалением тех токенов, без которых правдоподобие корпуса снижается меньше всего, всегда сохраняя одиночные символы. Кодирование выбирает самую вероятную сегментацию динамическим программированием (алгоритм Витерби): лучшее значение для префикса — максимум по с от лучшего значения для , умноженного на . Взяв , мы превращаем произведение в сумму, так что это самый дешёвый путь через решётку (lattice), дуги которой — записи словаря стоимостью . С игрушечными вероятностями на singer:
Самая вероятная сегментация — кратчайший путь
Лучший путь, sing er, — не жадное самое длинное совпадение singe r: алгоритм Витерби хранит одну лучшую стоимость для каждой позиции и никогда не фиксирует префикс заранее, а работа составляет для символов и токенов длиной не более символов.
Поскольку униграммная модель оценивает каждую сегментацию, во время обучения она может их сэмплировать; такая аугментация данных называется регуляризацией подслов (subword regularization). BPE-dropout (Provilkov, Emelianenko and Voita, 2020) получает тот же эффект для BPE, пропуская каждое слияние с вероятностью только во время обучения.
Три алгоритма рядом (Hugging Face, Tokenization algorithms):
| BPE | WordPiece | Unigram (SentencePiece) | |
|---|---|---|---|
| Обучение | Сливать самую частую пару | Сливать пару с наибольшим (реконструкция); по частоте в HF tokenizers | Прореживать кандидатов по потере правдоподобия |
| Кодирование | Повторять слияния по порядку | Жадный поиск самого длинного совпадения | Самая вероятная сегментация (Витерби) |
| Метка границы слова | Маркер конца слова или Ġ для пробела перед словом (байтовый) | ## на неначальных кусках (BERT) | ▁ для пробела перед словом |
| Где используется | GPT-2 (байтовый), Llama, Qwen2 | BERT, DistilBERT, ELECTRA | T5, BigBird, Pegasus |
Специальные токены, паддинг и маски#
Токенизатор — это конвейер, в котором обучается только этап модели, и его результат — больше, чем список идентификаторов:
От строки к батчу идентификаторов
[CLS]/<s>отмечает начало последовательности, а[SEP]/</s>— её конец или границу сегментов (заметка о BERT). Единственный токен GPT-2<|endoftext|>разделяет обучающие документы и завершает генерацию (заметка о GPT).[UNK]заменяет слово, которое словарь не может записать своими кусками. Словари, выпущенные вместе с контрольными точками BERT от Google, держат специальные токены на фиксированных идентификаторах:[UNK]— 100,[CLS]— 101,[SEP]— 102,[MASK]— 103. Токенизатор многоязычного BERT без учёта регистра (uncased) со словарём из 105 879 записей превращает We are very happy to show you the 🤗 Transformers library. в 14 идентификаторов, которые начинаются со 101 и заканчиваются на 102; для эмодзи в словаре нет куска, и оно становится 100. Модель видит, что на этом месте что-то стояло, но не видит что.token_type_idsзадают сегмент каждого токена для входа[CLS] A [SEP] B [SEP]из заметки о BERT: 0 для первого сегмента, 1 для второго и одни нули для одного текста.[PAD]дополняет короткие последовательности до самой длинной в батче — тензоре формы[batch, max_len]; маска внимания (attention mask) той же формы содержит 1 для настоящих токенов и 0 для паддинга, который последующие слои игнорируют.truncation=True, max_length=T_maxобрезает более длинные последовательности: в заметке о переводе, 128 для AG News в заметке о трансформерах.
tok(["First text to encode", "Second text"], padding=True,
max_length=3, truncation=True,
return_token_type_ids=False, return_tensors="pt")
# input_ids: tensor([[101, 2034, 102], [101, 2117, 102]])
# attention_mask: tensor([[1, 1, 1], [1, 1, 1]])
Остаётся только первое слово, потому что ограничение учитывает специальные токены (у [CLS] идентификатор 101, у [SEP] — 102). Токенизатор также запоминает, из какого слова пришёл каждый токен; распознавание именованных сущностей использует это, чтобы перенести одну метку слова на все токены этого слова.
Что изменилось#
В сравнении с конвейером на уровне слов из предыдущей заметки:
| Конвейер на уровне слов | Токенизатор подслов | |
|---|---|---|
| Размер словаря | Свойство корпуса | Гиперпараметр |
| Новые слова | [UNK] | Разбиваются на известные куски; на уровне байтов [UNK] не возникает |
| Морфология | Теряется при стемминге или хранится как отдельные типы | Общие куски (sing, sing er, sings) |
| Зависимость от языка | Стоп-слова и стеммеры для каждого языка | Обучается по данным; байтовому BPE алфавит не нужен |
| Длина последовательности | 1 токен на слово | 1–2 на английское слово при обычных размерах; больше для других языков |
Заметка об n-граммных моделях обучает n-граммные модели на словных токенах и на токенах подслов русского корпуса и сообщает перплексии на токен около 40 000 и 2700 для униграммных моделей (около 30 000 и 3900 для биграммных). Эти числа несравнимы: в тексте из подслов больше токенов, и каждый из них предсказать легче. Суммарный логарифм правдоподобия нужно нормировать на общую единицу — слова, символы или байты. Если в тестовом тексте токенов и слов, то
Игрушечный пример: перплексия на токен 20 при 1.5 токена на слово — это перплексия на слово .
Пересчёт может поменять порядок моделей. Один анекдот корпуса содержит 11 слов и 16 кусков подслов помимо знаков препинания; при таком отношении 2700 на токен — это на слово против примерно 40 000 у словной модели. Это лишь приближение (приведённые перплексии — средние перплексий отдельных анекдотов, и знаки препинания сохраняют только токены подслов), поэтому эти числа не решают, какой токенизатор лучше.
Ограничения#
- Жадность и зависимость от частоты. Изменение одной буквы может поменять всю сегментацию, а в байтовых схемах слово получает разные токены с пробелом перед ним и без. Униграммная модель не уступает BPE или превосходит его в экспериментах с предобучением (pretraining) (Bostrom and Durrett, 2020); Mielke et al. (2021) делают обзор альтернатив.
- Смещение в пользу одних языков и письменностей. Языки, редкие в обучающем корпусе токенизатора, режутся на большее число кусков, поэтому то же содержание стоит больше вычислений и контекста, и модели справляются с ним хуже (Rust et al., 2021; Petrov et al., 2023). Байтовый BPE закладывает это смещение ещё до первого слияния:
tokenзанимает 5 байтов UTF-8, атокен— 10. - Числа и символы. Цифры разбиваются по частоте, а не по разрядам (
10 , 92 ##5), а буквы внутри многосимвольного токена модели не видны, поэтому арифметика, написание слов и подсчёт символов сложнее, чем кажется. - Токенизатор и модель связаны. Модель понимает только идентификаторы своего токенизатора. Добавленные токены получают необученные строки эмбеддингов, как токен
[PAD], который заметка о дообучении (fine-tuning) добавляет к GPT-2. - Нормализация вшита в токенизатор.
bert-base-uncasedне может отличитьUSотus.
Что нужно запомнить#
- Токенизатор выбирает компромисс между размером словаря (стоимость эмбеддингов и softmax, данные на токен) и длиной последовательности (вычисления и контекст).
- BPE многократно сливает самую частую пару соседних единиц и кодирует, повторяя слияния по порядку. Каждое слияние укорачивает обучающий корпус на счётчик пары, поэтому BPE — жадное сжатие.
- WordPiece кодирует жадным поиском самого длинного совпадения с
##; униграммная модель выбирает самую вероятную сегментацию — кратчайший путь, найденный алгоритмом Витерби. - Байтовый BPE никогда не выдаёт
[UNK]. SentencePiece обходится без предварительной токенизации, считая пробел символом▁. - Языковые модели с разными токенизаторами сравнивают в расчёте на слово, символ или байт, но никогда не на токен.
- Специальные токены, паддинг и маска внимания — часть контракта токенизатора с моделью.
Ноутбук#
К этой заметке относятся четыре ноутбука курса NLP. Номера ячеек считают все ячейки, включая текстовые, начиная с 1. Приведённые числа взяты из прогона, сохранённого в курсе; у ячеек, изменённых с тех пор под текущие версии библиотек, сохранённого выхода нет.
week03_lm.ipynbтокенизирует корпус коротких русских анекдотов с помощью пакетаbpe(ячейки 7–11). Несмотря на название, пакет не выучивает слияний: он оставляет целыми 2498 самых частых слов, берёт в качестве подслов 47 500 самых частых символьных n-грамм длины 1–6 и кодирует остальные слова жадным поиском самого длинного совпадения, как WordPiece. Если переписать его маркеры__sow/__eowкак##, один анекдот сокращается с 27 до 19 токенов (ячейки 12–16): 16 кусков подслов на его 11 слов и три знака препинания — то самое отношение, по которому в разделе Что изменилось перплексия пересчитывается на слово. Ячейки 42–43 обучают n-граммные модели, сравнённые в том же разделе; словарь идентификаторов корпуса содержит 37 580 записей (ячейка 49).week04_lstm_attention_transformer.ipynb, ячейки 13–35, загружает предобученные токенизаторы WordPiece для английского (bert-base-uncased, 30 522 записи) и немецкого (31 102) и показывает граничные токены, маски, паддинг и обрезку (en+##code— в ячейке 21, батч сmax_length=3— в ячейке 25). Гистограммы длин фиксируют максимальную длину 32.week05_transformer.ipynb, ячейки 17–27, обучает на AG News токенизатор WordPiece на 4000 записей (слияния по частоте, кодирование поиском самого длинного совпадения, специальные токены с идентификаторами 0–4) и сохраняет батчи формы[256, 128]. Ячейка 29 берётPAD_IDизpad_token_id— идентификатора[PAD]: это 0, потому что[PAD]указан первым среди специальных токенов. Первая версия ноутбука читалаpad_token_type_id— идентификатор сегмента для паддинга, — который здесь тоже равен 0, поэтому ошибка ни на что не влияла.week07_nlp_tasks.ipynb, ячейки 11–16:bert-base-casedпревращает предложение CoNLL-2003 из 35 слов в 44 токена, аword_ids()сопоставляет каждому токену его слово.
Пример с [UNK] есть в ноутбуке о LLM курса HSE-LAMBDA Deep Generative Models, Transformers-LangChain.ipynb (ячейки нумеруются так же, выходы сохранены). Ячейка 26 кодирует это предложение токенизатором nlptown/bert-base-multilingual-uncased-sentiment и печатает input_ids [101, 11312, 10320, 12495, 19308, 10114, 11391, 10855, 10103, 100, 58263, 13299, 119, 102], token_type_ids из одних нулей и attention_mask из одних единиц; десятый идентификатор, 100, — это эмодзи. Ячейка 28 кодирует два предложения одним батчем с padding=True, truncation=True, max_length=512 и return_tensors="pt".
Дальше#
Теперь токены — целые числа из фиксированного словаря. Прежде чем строить модели, нужно договориться, что значит «модель работает»: на каких данных её можно обучать и настраивать, какие числа подытоживают её ошибки и какие ловушки делают хорошее число бессмысленным. Следующая заметка один раз задаёт эти правила, чтобы каждое последующее сравнение было честным.
Литература#
- P. Gage. A new algorithm for data compression. The C Users Journal, 12(2):23–38, 1994. Исходный алгоритм сжатия заменой пар байтов.
- R. Sennrich, B. Haddow, A. Birch. Neural Machine Translation of Rare Words with Subword Units. ACL, 2016. §3.2, BPE как токенизатор.
- M. Schuster, K. Nakajima. Japanese and Korean voice search. ICASSP, 2012. Критерий WordPiece.
- Y. Wu et al. Google’s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation. arXiv, 2016. WordPiece в промышленной системе перевода, §4.1.
- J. Devlin, M.-W. Chang, K. Lee, K. Toutanova. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. NAACL, 2019. Словарь WordPiece в BERT.
- T. Kudo, J. Richardson. SentencePiece: A simple and language independent subword tokenizer and detokenizer for Neural Text Processing. EMNLP (demo), 2018.
- T. Kudo. Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates. ACL, 2018. Токенизатор на униграммной языковой модели.
- I. Provilkov, D. Emelianenko, E. Voita. BPE-Dropout: Simple and Effective Subword Regularization. ACL, 2020.
- A. Radford, J. Wu, R. Child, D. Luan, D. Amodei, I. Sutskever. Language Models are Unsupervised Multitask Learners. OpenAI, 2019. §2.2, байтовый BPE.
- K. Bostrom, G. Durrett. Byte Pair Encoding is Suboptimal for Language Model Pretraining. Findings of EMNLP, 2020.
- P. Rust, J. Pfeiffer, I. Vulić, S. Ruder, I. Gurevych. How Good is Your Tokenizer? On the Monolingual Performance of Multilingual Language Models. ACL, 2021.
- A. Petrov, E. La Malfa, P. H. S. Torr, A. Bibi. Language Model Tokenizers Introduce Unfairness Between Languages. NeurIPS, 2023.
- S. J. Mielke et al. Between words and characters: A Brief History of Open-Vocabulary Modeling and Tokenization in NLP. arXiv, 2021. Обзор всего пространства решений.
- Hugging Face. Tokenization algorithms. Transformers documentation. BPE, байтовый BPE, WordPiece, Unigram и SentencePiece рядом, с моделями, которые используют каждый из них.
- Hugging Face. LLM Course, chapter 6: the Tokenizers library. Обучение токенизаторов; критерий слияния WordPiece и кодирование поиском самого длинного совпадения — в разделе 6.
- S. Axelbrooke. python-bpe. Пакет
bpe, используемый в ноутбуке.