Глава 9 из 17 30 мин
Корпус
Ростку нужна библиотека. Посмотрим, из каких текстов она собрана, как подготовить их к обучению и почему размер корпуса ещё не говорит, сколько текста модель прочитала.
В этой главе
- полистать три источника корпуса Ростка и прочитать ровно то, что читал он
- почистить и дедуплицировать текст так же, как это делает prepare.py, правило за правилом
- прикинуть, сколько токенов нужно модели заданного размера и почему Ростку подходит 330 миллионов
В прошлой главе мы собрали трансформер: восемь блоков и 17,31 миллиона параметров. Пока его веса случайны, он выдаёт бессмыслицу — даже самая удачная архитектура сама по себе не знает английского. Чтобы в этих числах появились закономерности языка, нужны примеры. От выбора текстов зависит, какие слова модель встретит часто, какие сюжеты научится продолжать и о чём почти ничего не узнает.
Представьте две одинаковые модели: одной досталась библиотека детских сказок, другой — смесь рецептов, форумов и технических инструкций. Перед ними стоят разные задачи. В сказках повторяются слова, герои и способы строить предложения; на этой сравнительно узкой территории маленькой модели проще научиться писать связно. Поэтому сначала выберем Ростку библиотеку, заглянем в неё и разберёмся, как подготовить текст к обучению.
Библиотека Ростка
Корпус Ростка — это три открытых набора данных, всего шесть миллионов документов. Выбирали мы их по одному признаку: они написаны простым, чистым и единообразным английским, который маленькая модель способна по-настоящему освоить. Откройте полки и прочитайте пару страниц. Переключатель над страницей показывает тот же документ глазами Ростка: сначала как токены, потом как голые числа, которые идут в обучение.
TinyStories — сердце коллекции. Это короткие сказки: GPT-4 попросили написать их словами, понятными трёх-четырёхлетнему ребёнку. У них узнаваемый ритм: «Once upon a time… One day… The end». Словарь крошечный, сюжеты простые, и каждая история доведена до конца. Мы берём вторую версию набора — в ней только истории, написанные GPT-4.
Истории SimpleStories тоже синтетические, но их генерировали по сетке параметров: тема, мотив, стиль, повествовательный приём и так далее. Английский по-прежнему простой, зато истории разнообразнее и длиннее: путешествия во времени, бабушкины коробки с фотографиями, рассказ от лица маяка. Мир Ростка с ними становится чуть шире.
SODA добавляет в библиотеку разговоры: это синтетические бытовые диалоги, сгенерированные с помощью InstructGPT по описаниям социальных ситуаций. Перед диалогом есть короткое вступление, а реплики мы записываем строками вроде Anna: I'm fine, thanks. Такие примеры знакомят Росток с чередованием говорящих и ответами на чужую реплику. В главе 12 этот опыт пригодится, когда мы будем дообучать его для чата.
Почему детские сказки?
Почему именно сказки, если в интернете столько текстов? У модели на 17 миллионов параметров ограниченная вместимость. В разнородном корпусе ей пришлось бы одновременно разбираться с кодом, спортивными репортажами, рецептами и рекламой. Сказки дают более посильную задачу: небольшой словарь, повторяющиеся конструкции и короткие сюжеты, начало и конец которых помещаются в контекст.
Авторы TinyStories задали обратный вопрос: а что, если не растить модель, а уменьшить мир? Взять язык, понятный маленькому ребёнку, со словарём четырёхлетки, и посмотреть, насколько маленькой может быть модель, которая всё ещё пишет связный текст. Оказалось — очень маленькой. Модели всего на несколько миллионов параметров научились писать гладкие грамматичные истории, в которых герои от начала до конца остаются самими собой.
У такого выбора есть цена. Синтетический текст несёт на себе отпечаток модели, которая его написала: любимые обороты GPT-4 («filled with joy», «learned a valuable lesson») повторяются в сотнях тысяч историй, и Росток переймёт их так же прилежно, как грамматику. В конце главы мы найдём следы этих привычек среди самых частых токенов корпуса.
Корпус задаёт модели круг примеров, из которых она учится. Она может соединять знакомые элементы по-новому и переносить закономерности на новые случаи, но вместе с полезными навыками усваивает повторы, ошибки и перекосы своих источников.
Смесь
После чистки и токенизации в корпусе 1,43 миллиарда токенов: 541,5 миллиона из TinyStories, 607,7 миллиона из SimpleStories и 285,2 миллиона из SODA. Пропорции проще увидеть, чем прочитать, поэтому ниже каждая клетка — миллион токенов, примерно один толстый роман.
Переключитесь на «Как в train.bin». Перед записью файла prepare.py режет каждый источник на куски примерно по миллиону токенов, сохраняя границы документов, и перемешивает куски. В результате источники чередуются по всему файлу. Это не гарантирует нужные пропорции в каждом коротком отрезке: внутри одного куска по-прежнему лежит один источник. При обучении окна выбираются случайно из всего файла, поэтому доли источников в среднем определяются их объёмом в токенах.
Теперь нажмите «Обучить». Наш запуск — это 10 070 шагов по 32 768 токенов, около 330 миллионов токенов суммарно. Это 23% длины обучающего файла, но не 23% уникально прочитанного текста: случайные окна иногда перекрываются. По приближённой оценке, около 79% позиций останутся непрочитанными, а примерно 2% встретятся дважды или чаще. Ниже разберём, почему для первого запуска мы остановились на таком бюджете.
Когда корпус гораздо больше прочитанной за запуск выборки, модель реже встречает один и тот же фрагмент. Это снижает повторяемость примеров, но не исключает запоминание: некоторые последовательности модель способна усвоить и с одного предъявления, а похожие фразы встречаются в разных документах. Поэтому качество проверяют отдельно. В следующей главе мы сравним ошибку на обучающих батчах с ошибкой на отложенном тексте.
Чистка
В сыром тексте всегда есть мусор, даже если его написала модель. Типографские кавычки “вот такие” и прямые "вот такие" значат одно и то же, но для токенизатора это разные байты, а значит, разные токены. Модели пришлось бы дважды учить, что кавычка открывает реплику. То же с тремя видами тире, символом многоточия, неразрывным пробелом и переводами строк в стиле Windows. Добавьте к этому лишние пробелы, груды пустых строк, обрывки, слишком короткие для истории, и тексты на других языках.
Наш чистильщик — одна маленькая функция. Попробуйте её на примерах или вставьте свой текст:
clean() из prepare.py, только в браузере. Память дедупликации заранее знает документы из библиотеки выше: пример «Дубликат» — один из них, набранный заглавными буквами.Вот эта функция — в точности такая, какой она записана в prepare.py. Это чистый Python, поэтому её можно запустить прямо здесь:
Последнее правило считает символы с кодом больше 126 и отбрасывает документ, если их доля превышает 0,2%. В тексте длиной 500 символов один такой символ допустим, два — уже нет. Это грубый фильтр, а не распознавание языка: он отсеивает многие тексты на русском или китайском, но может задеть и обычный английский текст с именем Chloé или словом café. Для этого эксперимента мы выбрали почти полностью ASCII-корпус; полезность других языков или символов из этого, конечно, не следует.
На наших данных чистильщику почти нечего было делать — это типично для синтетических текстов. Вот что именно он сделал (причины мы посчитали отдельно):
- TinyStories: 2 717 700 → 2 717 373 документа. 232 оказались слишком короткими, а в 95 было слишком много символов вне ASCII: в основном из-за слов café, piñata и Chloé; ещё попались несколько эмодзи, невидимые пробелы нулевой ширины и одна английская сказка с парой предложений по-китайски. Кавычки или пробелы пришлось поправить примерно в 338 тысячах историй, в каждой восьмой.
- SimpleStories: 2 115 696 → 2 115 670. Выброшены всего 26 точных дубликатов, зато пробелы или переводы строк пришлось поправить почти в 1,28 миллиона историй — в 60% всех.
- SODA: 1 186 423 → 1 184 876 (ещё 5 159 диалогов, у которых списки говорящих и реплик не совпадали по длине, пропущены при чтении). 1 545 диалогов отсеялись из-за чужих символов — в основном разговоры, где герои переходят на испанский или французский либо вспоминают «déjà vu». Два оказались слишком короткими.
С текстом из интернета всё наоборот: на этом этапе выбрасывается большая часть сырого веб-обхода, и чистка становится самой трудоёмкой частью всего проекта.
Дубликаты
Последний шаг чистки — убрать повторы. Если одна и та же история встречается в корпусе тысячу раз, модель видит её в тысячу раз чаще любой другой: вызубрит слово в слово, а её представление об «обычной» истории перекосится в сторону этой одной. К тому же дубликаты попросту съедают бюджет обучения.
Для поиска повторов prepare.py переводит очищенный текст в нижний регистр и вычисляет MD5 — короткий, 16-байтовый отпечаток. Уже встреченные отпечатки хранятся в множестве. Проверка принадлежности такому множеству в среднем занимает почти постоянное время, поэтому не нужно заново сравнивать документ со всей библиотекой. Здесь повторы удаляются внутри каждого источника отдельно; совпадения между источниками этот шаг не ищет. Как и у любого короткого хеша, у MD5 возможны коллизии, поэтому математической гарантии тождества текстов он не даёт.
Третий документ отличается от первого всего одним словом, но его хеш уже другой. Значит, эта проверка пропускает почти-дубликаты. Они бывают и в синтетических историях, и в веб-корпусах: например, одна статья может оказаться на сотне сайтов с разными меню. Их поиск требует отдельного метода; в подготовке корпуса Ростка такого шага нет.
Документы и окна
Какой длины документы? Росток читает текст окнами по 512 токенов — это его контекст. Если бы документы были длиннее, модель никогда не видела бы их начала и конца вместе; если бы были сильно короче, в одно окно попадало бы несколько не связанных между собой историй.
На всей проверочной выборке (30 088 документов) средняя история из TinyStories — 199 токенов, из SimpleStories — 286, диалог из SODA — 240. Почти все документы — 97% — целиком помещаются в 512 токенов. Значит, случайное окно в 512 токенов обычно захватывает конец одного документа, целиком второй и начало третьего.
Каждый документ в файле начинается с <|endoftext|>. Этот специальный токен служит разделителем: во время обучения модель получает много примеров того, что после него начинается новый текст. Сам по себе токен не стирает контекст и не запрещает вниманию смотреть на предыдущий документ. Он даёт выученный сигнал границы. Для генерации с нуля мы подаём только этот токен и просим модель продолжить.
График заодно объясняет приём из следующей главы. Первые 10% обучения Росток читает окна всего по 128 токенов, потом по 256 и только потом по 512. Короткие окна дешевле, потому что стоимость внимания растёт как квадрат длины окна, а в начале обучения модель всё равно учит локальные вещи: слова, пунктуацию, грамматику. Нажмите «128»: почти ни один документ в такое окно целиком не влезает, но для отдельных предложений его вполне хватает.
Из текста в train.bin
Обучение не должно тратить время на обработку текста, иначе видеокарта будет простаивать в ожидании токенизатора. Поэтому весь корпус токенизируется один раз, заранее, и результат хранится как плоский массив чисел. Вот часть prepare.py, которая это делает:
Несколько деталей заслуживают комментария:
- Параллельно. Документы режутся на пачки по 2 000 и раздаются пулу процессов — по одному на логическое ядро, кроме одного, оставленного системе. Каждый процесс загружает свою копию токенизатора и возвращает массив чисел. На нашем Mac все 1,43 миллиарда токенов закодировались примерно за минуту, а весь prepare.py, от сырых файлов до train.bin, отработал минут за пять.
- Проверка. Первые 0,5% документов каждого источника (список перед этим перемешан) уходят в
val.bin: 7,17 миллиона токенов, которые не используются для обновления весов языковой модели. Токенизатор при этом строится раньше разделения и может видеть часть этих документов. Поэтому это отложенная проверка языковой модели, а не полностью изолированный набор для всей цепочки обработки. - uint16. Каждый токен хранится в двух байтах. В два байта помещаются числа от 0 до $2^{16} - 1 = 65\,535$, а наш самый большой номер — 8 191, так что места с запасом. 1,43 миллиарда токенов занимают 2,85 ГБ — вдвое меньше, чем с обычными четырёхбайтовыми целыми.
Посмотрим, как это выглядит на уровне байтов и как обучение читает такой файл. Вот начало истории в виде номеров и случайные окна из него — так же, как их берёт класс Batches в train.py:
Пара x, y — весь секрет обучающих данных языковой модели. y — это x, сдвинутый на один токен: в каждой позиции модель видит всё до этой позиции включительно и должна угадать следующий токен. Одно окно в 512 токенов даёт сразу 512 таких вопросов, а один шаг обучения (32 768 токенов) — 32 768. Никаких меток и разметчиков: текст сам себе ответ.
Почему токены хранятся в uint16, а не в обычных 32-битных целых?
В два байта помещаются числа до 65 535. При словаре в 8 192 токена этого с запасом хватает, и train.bin занимает 2,85 ГБ вместо 5,7. При словаре больше 65 536 токенов uint16 уже недостаточно: можно использовать, например, 32-битные целые.
Сколько текста нужно?
У нас есть 1,43 миллиарда токенов, а учимся мы на 330 миллионах. Почему не на всех? И почему не на меньшем? Чтобы ответить, нужна мера стоимости.
Теперь можно поставить вопрос о бюджете: как распределить вычисления $C$ между числом параметров $N$ и количеством обучающих токенов $D$? Если взять слишком большую модель, на её обучение не хватит текста и шагов. Если долго обучать маленькую, улучшения могут стать настолько медленными, что выгоднее было бы увеличить её размер. В экспериментах DeepMind 2022 года оптимальный баланс менялся примерно пропорционально: больше параметров — больше токенов. Отсюда известный ориентир — около 20 токенов на параметр. Это эмпирическое правило для определённых условий, а не универсальная норма.
Для Ростка этот ориентир даёт $17{,}31 \text{ млн} \times 20 \approx 346$ миллионов токенов. Наши 330 миллионов — примерно 19 токенов на параметр. Числа близки, но это ещё не доказывает оптимальность: Росток меньше моделей в исходных экспериментах, и корпус у него другой. Мы использовали правило как начальную оценку, согласованную с бюджетом в несколько часов на одном Mac. Перетащите точку на графике и сравните варианты:
Для сравнения, Llama 3 8B обучали более чем на 15 триллионах токенов — это свыше тысячи токенов на параметр. Почему так далеко от двадцати? Правило Chinchilla ищет выгодное распределение обучающих вычислений. Если модель потом много раз используют, имеет значение и стоимость каждого ответа. Тогда может оказаться выгоднее дольше обучать небольшую модель: потратить больше один раз, чтобы затем дешевле её запускать. У Ростка другая цель — показать полный цикл обучения в пределах одного короткого эксперимента.
Что Росток читает чаще всего
Напоследок посмотрим на корпус глазами модели. Вот самые частые токены проверочного текста и то, какую долю всего текста они покрывают.
Десять самых частых токенов — точка, запятая, перевод строки и короткие слова вроде « the», « and», « a» и « to» — составляют больше четверти текста, а первая сотня — больше половины. Это похоже на неравномерное распределение частот, которое мы обсуждали вместе с законом Ципфа в главе 6. Частые токены дают много обучающих примеров, но угадать их не всегда просто: выбор между « a» и « the» тоже зависит от контекста. По мере обучения улучшаются и редкие продолжения, и использование знакомых слов в сложных случаях.
А теперь посмотрите, кто живёт в этом мире. « Lily» и « Tim» встречаются чаще большинства глаголов, « happy» — чаще, чем « sad», а « joy» и « together» входят в первые две сотни. Это отпечатки моделей, которые писали истории. Росток их унаследует: он будет добрым, немного сентиментальным и неравнодушным к имени Lily. Теперь вы знаете, откуда это.
Росток сейчас
Это настоящий Росток в полный рост: восемь блоков, шесть голов, 17,31 миллиона параметров. Только веса у него — случайные числа, и он не прочитал ни одного токена корпуса. Попросите его написать что-нибудь — и увидите случайную последовательность токенов. Спросите, насколько его удивляет настоящая история, — и ответ будет близок к $\ln 8192 \approx 9{,}01$ ната на токен: ровно столько сказала бы модель, которая размазывает вероятность поровну по всем 8 192 токенам. В следующей главе Росток начнёт читать свою библиотеку, и примерно за 1 500 шагов это число опустится ниже двух (к последнему шагу — до 1,55).