Токенизатор BPE
Алгоритм BPE (Byte-Pair Encoding, [1]) является одним из самых популярных методов подсловной токенизации в современных задачах обработки естественного языка. Изначально предложенный как алгоритм сжатия данных, он был успешно адаптирован для машинного перевода, а затем стал стандартом для обучения языковых моделей.
Алгоритм BPE состоит из двух этапов:
-
Этап обучения, на котором строится словарь токенов и определяются правила слияния подряд идущих токенов в более крупные по большому корпусу обучающих текстов.
-
Этап кодирования, при котором выученные правила применяются для перевода новых текстов в последовательности токенов из словаря.
Обучение токенизатора BPE
Суть обучения заключается в итеративном поиске наиболее часто встречающихся пар соседних токенов и их объединении в новый, более крупный токен. Процесс начинается с разбиения всего обучающего корпуса на минимальные базовые единицы, в качестве которых выступают отдельные символы.
Рассмотрим процесс обучения на простом примере.
Пусть наша исходная строка - _AB_ABC_AB_ABAB.
Базовый словарь на старте состоит из уникальных символов: A, B, C, _.
Процесс обучения выглядит следующим образом:
-
Алгоритм сканирует строку и находит самую частую пару соседних символов. Это пара
AиB(встречается 5 раз).- Формируется новое правило слияния:
A+B=[AB]. - В словарь добавляется новый токен:
[AB]. - Строка перезаписывается с учетом слияния:
_[AB]_[AB]C_[AB]_[AB][AB].
- Формируется новое правило слияния:
-
Теперь самая частая пара в обновленной строке - это символ
_и новый токен[AB](встречаются вместе 4 раза).- Добавляем следующее правило:
_+[AB]=[_AB]. - В словарь добавляется токен:
[_AB]. - Строка перезаписывается:
[_AB][_AB]C[_AB][_AB][AB].
- Добавляем следующее правило:
-
Далее самая частая пара — два подряд идущих токена
[_AB](встречается 2 раза).- Правило:
[_AB]+[_AB]=[_AB_AB] - В словарь добавляется токен:
[_AB_AB] - Строка перезаписывается:
[_AB_AB]C[_AB_AB][AB]
- Правило:
А общем случае алгоритм повторяет этот процесс до тех пор, пока не будет достигнут заранее заданный целевой размер словаря (гиперпараметр) или пока частота самых популярных пар не упадет ниже заданного порога.
В результате обучения мы получаем:
- Базовый словарь (
A,B,C,_) + новые токены ([AB],[_AB],[_AB_AB]). - Упорядоченный список правил слияния, который будет использоваться для токенизации новых текстов.
Кодирование новых текстов
На этапе применения токенизатор берет новый текст и применяет к нему выученные правила слияния в порядке их создания при обучении.
Возьмем новую строку: _AB_ABAB_AB.
- Сначала строка разбивается на базовые символы:
_AB_ABAB_AB. - Применяем правило 1 (
A+B[AB]): получаем_[AB]_[AB][AB]_[AB]. - Применяем правило 2 (
_+[AB][_AB]): получаем[_AB][_AB][AB][_AB]. - Применяем правило 3 (
[_AB]+[_AB][_AB_AB]): получаем итоговую последовательность[_AB_AB][AB][_AB].
В данном примере исходная строка из 11 символов превратилась в последовательность всего из 3 токенов, то есть мы сжали текст почти в 4 раза! Это повышает скорость обработки текста моделью, повышает её эффективный контекст и улучшает качество самой обработки, так как крупные токены несут в себе семантический смысл более высокого уровня, чем отдельные буквы.
Декодирование
Декодирование — это процесс обратного превращения последовательности токенов, сгенерированных моделью, в читаемый текст. Логически этот процесс представляет собой последовательную «распаковку» сложных токенов обратно в базовые символы путем применения правил слияния в обратном порядке.
Например, для сгенерированной строки [_AB_AB][AB][_AB]:
[_AB_AB]распадается на[_AB]+[_AB].[_AB]распадается на_+[AB].[AB]распадается наA+B. В итоге восстанавливается строка_AB_ABAB_AB.
На практике токенизатор не выполняет поэтапное расщепление токенов. Вместо этого он хранит прямой словарь соответствий токенов и их символьных расшифровок (например, [_AB_AB] "_AB_AB"). Благодаря этому распаковка даже самых длинных токенов в символы происходит за одну итерацию!
Пре-токенизация в BPE
Если запустить алгоритм BPE сразу на сыром тексте, возникнет серьезная проблема. Алгоритм начнет сливать слова со знаками препинания.
Например, если слово "кот" часто встречается в конце предложений, алгоритм может создать правило:
кот+.кот.. В итоге в словаре появятся отдельные токены длякот.,кот,,кот!,кот?вместо того, чтобы хранить один токенкоти отдельные токены для знаков препинания.
Это приводит к разрастанию словаря, лишённому семантического смысла.
Для решения этой проблемы применяется пре-токенизация (pre-tokenization). Перед тем как запустить алгоритм BPE, текст разбивается на изолированные фрагменты на основе простых эвристик:
- Отделяются последовательности букв и цифр (слова).
- Отделяются знаки препинания, спецсимволы и пробелы.
Главное правило пре-токенизации: слияниям BPE категорически запрещено пересекать границы этих фрагментов. Также в слияниях не участвуют специальные управляющие токены (такие как [BOS], [EOS]).
Пример пре-токенизации
Возьмем фразу: "Hello, world!!!"
- Этап разбиения (до BPE): текст разбивается на список изолированных фрагментов:
["Hello", ",", " world", "!!!"]. - Слияния идут только внутри фрагментов.
- Возможные слияния:
He+llo,wor+ld,!+!! - Невозможные слияния:
world+!!!,Hello+,
- Возможные слияния:
Таким образом, символы никогда не будут объединяться со знаками препинания.
А частоты встречаемости пар токенов теперь нужно считать по их встречаемости во всех образованных фрагментах текста.
Разделение слов и проблема суффиксов
Еще одна тонкость алгоритма заключается в неоднозначности кодирования пробелов и границ слов. Разные исходные фразы могут быть токенизированы абсолютно одинаковыми последовательностями.
Например, слово dropout (написанное слитно) и фраза drop out (написанная раздельно) без учета пробелов могут обе токенизироваться как [drop] [out]. В таком случае декодер не будет знать, нужно ли ставить пробел при восстановлении текста, а модель, обрабатывающая токен [out] не будет знать, обрабатывает ли она отдельное слово или часть слова.
Для однозначного декодирования в конец каждого слова перед обучением добавляется специальный маркер * (в оригинальной статье это был </w>):
-
Фраза: team dropped out of the competition
[team*][drop][ped*][out*][of*][the*][competit][ion*] -
Фраза: we added a dropout layer
[we*][add][ed*][a*][drop][out*][layer*]
При использование этого маркера декодирование токенов в исходный текст становится однозначным.
Дополнительно прочитать о BPE-токенизации и о её использовании в библиотеке transformers можно в [2].