# Byte Pair Encoding (BPE) Algorithm > **Документ актуален для Simple-LLM v1.0 (июль 2025)** --- **Краткое summary:** Этот документ подробно описывает алгоритм Byte Pair Encoding (BPE) — как он используется для токенизации текста, как устроен процесс обучения словаря и как происходит энкодинг/декодинг текста. Документ предназначен для пользователей Simple-LLM и всех, кто хочет понять внутреннюю механику BPE. --- **Структура документа:** - Введение - Основные понятия - Алгоритм работы (обучение словаря) - Псевдокод - Пример работы - Алгоритм энкодинга (токенизации) - Алгоритм декодирования - Типовые ошибки и их решения --- ## Введение Byte Pair Encoding (BPE) - это алгоритм компрессии данных, адаптированный для токенизации текста в обработке естественного языка. В контексте языковых моделей BPE используется для создания эффективного словаря подстрок (токенов). ## Основные понятия - **Токен** - элементарная единица текста (символ или последовательность символов) - **Словарь** - набор уникальных токенов, используемых для представления текста - **Частота пары** - количество раз, когда два токена встречаются вместе в тексте ## Алгоритм работы ### 1. Инициализация ```python Исходный текст → Разбить на символы → Первоначальный словарь ``` Пример: ``` "мама" → ['м', 'а', 'м', 'а'] ``` ### 2. Основной цикл ```mermaid graph TD A[Подсчет частот пар] --> B[Выбор наиболее частой пары] B --> C[Создание нового токена] C --> D[Обновление последовательности] D --> E{Достигнут лимит словаря?} E -->|Нет| A E -->|Да| F[Конец] ``` ### 3. Детализация шагов #### Шаг 1: Подсчет частот пар Для текущей последовательности токенов подсчитываем все пары соседних токенов: ``` Текст: "мама мыла" Токены: ['м', 'а', 'м', 'а', ' ', 'м', 'ы', 'л', 'а'] Пары: ('м','а'), ('а','м'), ('м','а'), ('а',' '), (' ','м'), ('м','ы'), ('ы','л'), ('л','а') ``` #### Шаг 2: Выбор пары для слияния Находим пару с максимальной частотой. При равенстве частот выбираем пару, которая встречается раньше в тексте. #### Шаг 3: Слияние Объединяем выбранную пару в новый токен и заменяем все её вхождения в тексте: ``` Выбранная пара: ('м', 'а') Новый токен: 'ма' Обновленная последовательность: ['ма', 'ма', ' ', 'м', 'ы', 'л', 'а'] ``` #### Шаг 4: Обновление словаря Добавляем новый токен в словарь: ``` Словарь: ['м', 'а', ' ', 'ы', 'л', 'ма'] ``` ### 4. Критерии остановки 1. Достижение заданного размера словаря 2. Отсутствие пар для слияния (все возможные пары уже добавлены) 3. Достижение максимального числа итераций ## Псевдокод ```python def train_bpe(text, vocab_size): # Инициализация tokens = list(text) vocab = set(tokens) while len(vocab) < vocab_size: # Подсчет пар pairs = get_pairs(tokens) if not pairs: break # Выбор наиболее частой пары best_pair = max(pairs, key=pairs.get) # Слияние new_tokens = [] i = 0 while i < len(tokens): if i < len(tokens)-1 and (tokens[i], tokens[i+1]) == best_pair: new_tokens.append(best_pair[0] + best_pair[1]) i += 2 else: new_tokens.append(tokens[i]) i += 1 tokens = new_tokens # Обновление словаря vocab.add(best_pair[0] + best_pair[1]) return vocab ``` ## Пример работы **Исходный текст**: "мама мыла раму" **Итерация 1**: - Пара ('м','а') встречается 2 раза - Новый токен: 'ма' - Текст: ['ма', 'ма', ' ', 'м', 'ы', 'л', 'а', ' ', 'р', 'а', 'м', 'у'] **Итерация 2**: - Пара ('ма',' ') встречается 1 раз - Новый токен: 'ма ' - Текст: ['ма ', 'ма', 'мы', 'л', 'а', ' ', 'р', 'а', 'м', 'у'] **Результирующий словарь** (частично): ['м', 'а', ' ', 'ы', 'л', 'р', 'у', 'ма', 'ма ', 'мы'] ## Алгоритм энкодинга (токенизации) После обучения BPE-модели (создания словаря), энкодинг текста происходит по следующему алгоритму: ```mermaid graph TD A[Начало] --> B[Разбить текст на символы] B --> C[Инициализировать пустой список токенов] C --> D[Установить i=0 'начало последовательности'] D --> E{i < длины текста?} E -->|Нет| F[Заменить токены на их ID] E -->|Да| G[Найти все токены в словаре, начинающиеся с text_i] G --> H[Выбрать самый длинный подходящий токен] H --> I[Добавить токен в результат] I --> J[Увеличить i на длину токена] J --> E F --> K[Конец] ``` Пошаговое описание: 1. **Инициализация**: - Исходный текст разбивается на символы - Создается пустой список для результата - Указатель `i` устанавливается в 0 2. **Основной цикл**: - Для текущей позиции `i` находим все токены в словаре, которые: - Начинаются с символа `text[i]` - Совпадают с подстрокой `text[i:i+len(token)]` - Из подходящих токенов выбираем самый длинный - Добавляем найденный токен в результат - Сдвигаем указатель `i` на длину добавленного токена 3. **Завершение**: - Когда весь текст обработан, заменяем токены на их ID из словаря Пример: ```python Словарь: ['ма', 'мама', ' ', 'мыл', 'а', 'раму'] Текст: "мама мыла раму" Энкодинг: 1. Находим самый длинный токен, начинающийся с 'м' -> 'мама' 2. Добавляем 'мама', i += 4 3. Токен ' ' (пробел), i += 1 4. Токен 'мыл', i += 3 5. Токен 'а', i += 1 6. Токен ' ', i += 1 7. Токен 'раму', i += 4 Результат: ['мама', ' ', 'мыл', 'а', ' ', 'раму'] ``` ## Алгоритм декодирования ### Принцип работы 1. Преобразование списка ID обратно в текст 2. Замена каждого ID на соответствующий токен 3. Обработка неизвестных ID (`[UNK]`) ```mermaid flowchart TD A[Список ID] --> B{ID ∈ словарь?} B -->|Да| C[Добавить токен] B -->|Нет| D[Добавить UNK] C --> E[Следующий ID] D --> E E --> F{Конец списка?} F -->|Нет| B F -->|Да| G[Объединить токены] ``` Пример: ```python decode([0,1,2]) → "абра" # Для словаря {0:"а", 1:"б", 2:"ра"} ``` ## Применение в языковых моделях 1. Эффективное представление редких слов 2. Снижение размерности входных данных 3. Возможность обработки OOV (Out-of-Vocabulary) слов ## Ограничения 1. Чувствительность к регистру (можно решить предварительной нормализацией) 2. Зависимость от обучающего корпуса 3. Не всегда выделяет лингвистически осмысленные морфемы ## Дополнительные материалы 1. [Original BPE paper](https://arxiv.org/abs/1508.07909) 2. [BPE in HuggingFace](https://huggingface.co/docs/transformers/tokenizer_summary) 3. [Practical guide to BPE](https://towardsdatascience.com/byte-pair-encoding-subword-based-tokenization-algorithm-77828a70bee0)