Считаем пары, затем заменяем
BPE итеративно объединяет частые соседние единицы. В оригинальной NMT-работе L02 это character/subword units внутри слов. Здесь используется упрощённый byte-level поток, включая пробелы: это реальный merge-алгоритм, но не точная копия production tokenizer.
На каждой итерации считаем все соседние пары текущих IDs. Выбираем максимальную частоту, при равенстве — меньшую numeric-пару (leftID,rightID). Новый token получает ID256+номер merge. Вхождения заменяем слева направо без перекрытий. Если частота лучшей пары меньше2, останавливаемся.
Для corpus banana banana пары(a,n) и(n,a) встречаются по4раза. Tie-break выбирает(97,110), новый ID256 означает bytes an. После первого merge banana представляется[98,256,256,97]. После четырёх merges всё выражение становится[259,32,259], где259 — banana.
Тонкость overlap. В aaa пара(a,a) имеет частоту2, но за проход можно сделать только одну неперекрывающуюся замену. Частота кандидата и число выполненных замен — разные поля трассы.
Исследовать. Повторите корпус и сравните первую пару, её count и фактические replacements. Смена tie-break может изменить IDs и последующую сегментацию. Поэтому список merges и их порядок являются частью сохраняемой модели tokenizer, а не случайным оформлением отчёта.