Что такое SentencePiece и чем он отличается от BPE (например, в модели T5)? Как обрабатывает пробелы?
Краткий тезис
SentencePiece — это библиотека для создания субсловных токенизаторов от Google, которая работает с сырым текстом без предварительного разделения на слова. В отличие от стандартного BPE, SentencePiece рассматривает пробел как обычный символ, заменяя его на специальный токен (обычно _ или ▁). Это позволяет обучиться границам слов в процессе токенизации, что критически важно для языков без чётких word boundaries, например для русского. SentencePiece поддерживает два алгоритма: Unigram (используется в T5) и собственно BPE (применяется в Llama).
2. Отличие от BPE: работа с сырым текстом (пробел = символ _)
Классическая процедура BPE (например, в GPT или RoBERTa) предполагает следующие шаги:
- Разделить текст на слова по пробелам и знакам препинания (pre-tokenization).
- Заменить каждое слово последовательностью символов (например, с добавлением
##или</w>для отметки конца слова). - Итеративно объединять самые частые пары символов.
Таким образом, BPE работает с уже выделенными словами — пробелы теряются на этапе pre-tokenization. Это означает, что модель не может обучиться межсловным отношениям (например, что пробел обычно предшествует началу нового слова).
SentencePiece, наоборот, не делает предварительного разбиения по пробелам. Вместо этого:
- Пробел преобразуется в специальный символ (например,
_в ранних версиях или▁в современных). - Вся последовательность символов, включая пробелы, обрабатывается как единый поток.
- Токенизатор учится объединять символы, включая пары, содержащие пробел. Например, последовательность
_мможет стать отдельным токеном, который будет означать "пробел + символ 'м'".
Сравнение подходов на примере предложения "Hello world":
| Этап | Классический BPE | SentencePiece (c пробелом как _) |
|---|---|---|
| Исходный текст | "Hello world" | "Hello world" |
| Pre-tokenization | ["Hello", "world"] | ["_Hello", "_world"] (только для демонстрации, на самом деле не нужно) |
| Вход для BPE | "Hello", "world" | "▁Hello▁world" (как одна строка с символом ▁) |
| Возможные токены | "He", "llo", "wor", "ld" | "▁He", "llo", "▁wor", "ld" |
| Обратная конвертация | "He" + "llo" + " " + "wor" + "ld" (нужен отдельный шаг) | "▁He" → "He", "▁wor" → "wor" → на выходе "Hello world" |
Таким образом, SentencePiece инкапсулирует границу слова прямо в токене, что делает декодирование тривиальным: достаточно заменить все ▁ на пробелы (кроме первого, если токенизатор не добавляет ▁ в начало текста).
3. Поддерживает Unigram (T5) и BPE (Llama)
SentencePiece не привязан к одному алгоритму. Параметр model_type может быть:
'bpe'— стандартный BPE, но реализованный на последовательности символов, включая пробел.'unigram'— алгоритм на основе Unigram Language Model.
Unigram (используется в T5):
- Обучает вероятностную модель, которая для каждого входа находит токенизацию с максимальной правдоподобностью.
- Начинает с большого словаря (например, всех символов и их частых биграмм) и рекурсивно удаляет наименее вероятные субслова.
- Подробнее: каждый субсловный токен имеет вероятность, произведение вероятностей всех токенов в последовательности даёт общую вероятность. Оптимизация ведётся через EM-алгоритм.
- Преимущество: естественная регуляризация, возможность обучения токенизатора с заданным размером словаря.
BPE (используется в Llama и многих других):
- Итеративное слияние: на каждом шаге объединяется наиболее частая пара соседних символов (или токенов) во всём корпусе.
- Процесс продолжается, пока не будет достигнут желаемый размер словаря.
- В SentencePiece BPE работает на уровне байтов (дополнительно поддерживается опция
byte_fallback).
Сравнение подходов:
| Аспект | BPE | Unigram |
|---|---|---|
| Принцип | Детерминированное слияние пар | Вероятностная модель |
| Обучаемость | Жадный алгоритм | EM-оптимизация |
| Словарь | Фиксируется последовательностью слияний | Выбирается из большего начального набора |
| Использование в моделях | GPT, RoBERTa, Llama | T5, ALBERT |
4. Обработка пробелов: кодируется как токен, без word boundaries
Ключевое следствие: токенизатор SentencePiece не имеет понятия "граница слова" в классическом смысле. Токен может начинаться с символа пробела (▁), содержать несколько слов, или наоборот — разбивать одно слово с учётом пробела внутри (например, дефис или перенос строки).
Пример токенизации русского текста с помощью модели Unigram (аналог T5):
text = "Привет мир! Я изучаю NLP."
sp = ... # обученная модель SentencePiece
tokens = sp.encode(text, out_type=str)
print(tokens)
# ['▁Привет', '▁мир', '!', '▁Я', '▁изучаю', '▁N', 'LP', '.']
Обратите внимание: N и LP — это два разных токена, потому что заглавные буквы после пробела считаются разными символами. Символ ▁ (метка пробела) стоит только перед первым символом каждого "слова", но не в середине. Если бы текст содержал дефис, например "NLP-модель", то токенизатор мог бы выдать ['▁N', 'LP', '-', '▁модель'].
Как происходит обратное преобразование:
- Получить последовательность токенов-строк.
- Склеить их без разделителей.
- Заменить все вхождения
▁на пробел. - Если строка начинается с пробела (первый токен начинался с
▁), удалить его (обычно это настраиваемый параметр).
Пример кода для декодирования:
tokens = ['▁Привет', '▁мир', '!']
decoded = sp.decode(tokens)
print(repr(decoded))
# 'Привет мир!'
# (если модель обучена добавлять ▁ только между словами, то пробел между 'Привет' и 'мир' есть)
Важная деталь: в моделях на основе SentencePiece (например, T5) токенизатор не знает, что такое слово — он оперирует только субсловами с маркерами пробелов. Это упрощает обработку многоязычных текстов и текстов без пробелов (китайский, японский), но может приводить к неожиданным разбиениям для языков с составными словами (немецкий, русский сложные приставки).
Пет-проект для закрепления
Задача: обучить токенизатор SentencePiece на русскоязычном корпусе (например, на датасете новостей), используя оба алгоритма (BPE и Unigram), и сравнить результаты.
Инструменты:
- Python 3.8+
sentencepiece(PyPI)transformersилиtokenizersдля визуализации- Любой русский текстовый корпус (можно взять подмножество «Толока» или Lenta.ru)
Шаги:
- Подготовить корпус: очистить от лишних символов, оставить только текст (одно предложение на строку).
- Обучить две модели с одинаковым размером словаря (например, 32000):
spm.SentencePieceTrainer.train('--input=corpus.txt --model_prefix=bpe_model --vocab_size=32000 --model_type=bpe') spm.SentencePieceTrainer.train('--input=corpus.txt --model_prefix=unigram_model --vocab_size=32000 --model_type=unigram') - Загрузить обе модели и протестировать на одном предложении:
- Вывести токены (как строки) для фразы «Привет, мир! Я изучаю токенизаторы».
- Посчитать среднюю длину токенизации на тестовом корпусе.
- Сравнить результат:
- Какая модель даёт меньше токенов на одно предложение? (BPE обычно даёт более короткие последовательности, но это зависит от корпуса).
- Влияние алгоритма на покрытие редких слов.
- Попробовать декодировать обратно и проверить, восстановился ли пробел.
Ожидаемый результат:
- Понимание разницы между BPE и Unigram на практике.
- Умение объяснить, почему T5 использует Unigram, а Llama — BPE.
- Навык работы с SentencePiece и визуализации токенизации.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 284 | BPE vs WordPiece — сравнение алгоритмов субсловной токенизации |
| 927 | WordPiece — ещё один метод, близкий к BPE, но с вероятностной оценкой слияния |
Навигация
- Предыдущий: 927
- Следующий: 929
- Индекс: 00. Индекс разборов