Функция потерь, градиентный спуск, масштаб
Только что созданная модель — это груда случайных чисел. Попросите её продолжить «Кот сел на», и каждый из ~200 000 токенов словаря получит примерно одинаковую крошечную вероятность. Обучение — это процесс изменения этих чисел, пока предсказания не станут хорошими. Для него нужны две вещи:
- Одно число, которое говорит, насколько плохи предсказания прямо сейчас: функция потерь (loss).
- Способ изменить миллиарды параметров так, чтобы это число уменьшилось: градиентный спуск.
Что меняет обучение
Эти случайные числа — параметры, их ещё называют весами; из них состояли последние четыре главы. Это каждая ячейка таблицы эмбеддингов, матриц запросов, ключей и значений в каждой голове внимания, двух матриц MLP в каждом блоке и матрицы обратного эмбеддинга наверху. Каждый параметр — одно обычное число, например 0,013 или −0,41. У модели 7B их около семи миллиардов; рисунок 6.6 подсчитывает, где они лежат.
Запуск модели — длинное, но неизменное вычисление: на входе номера токенов, на выходе по вероятности на каждый токен словаря, а параметры — константы внутри этого вычисления. Больше в модели меняться нечему, так что «учиться» может означать только одно: менять параметры.
Каждый параметр немного влияет на результат. Возьмём самый последний шаг: логит токена «·ковёр» — это скалярное произведение финального вектора на строку матрицы обратного эмбеддинга, принадлежащую «·ковёр». Чуть-чуть измените одно число в этой строке — логит сдвинется, softmax перераспределит вероятности, и шанс «·ковёр» немного вырастет или упадёт. Вес глубоко внутри третьего блока влияет так же, только через гораздо более длинную цепочку вычислений. Обучению нужно выяснить для каждого из миллиардов параметров, в какую сторону его подтолкнуть. Но сначала нужно точно определить, что значит «лучше».
Как измерить «неправильно»: перекрёстная энтропия
Возьмём предложение из обучающего текста, спрячем следующий токен и попросим у модели распределение вероятностей. Функция потерь смотрит только на одно число из этого распределения: на вероятность , которую модель дала токену, действительно стоявшему следующим.
Это и есть перекрёстная энтропия (cross-entropy). Если модель дала «·ковёр» вероятность 0,2, потери равны . Поднимите эту вероятность до 0,9 — потери упадут до 0,11; опустите до 0,001 — подскочат до 6,9. Рисунок начинается с 0,2: подвигайте ползунок, чтобы увидеть, почему форма «минус логарифм» — удачный выбор.
Кот сел на ___ правильный следующий токен: ·ковёр
В основном мимо: штраф быстро растёт.
Стоит заметить несколько вещей:
- В формулу входит только вероятность правильного токена, но так как все вероятности в сумме дают 1, её рост автоматически уменьшает неправильные.
- Модель, которая угадывает равномерно по словарю из 200 000 токенов, получает . Примерно такие потери видны на нулевом шаге настоящего обучения.
- Перплексия равна : «модель так же неуверенна, как если бы выбирала из стольких токенов». Потери 2 означают перплексию около 7,4.
На практике потери усредняются по всем позициям всех последовательностей в батче — часто это миллионы токенов сразу. Один текст из 1000 токенов уже даёт 1000 отдельных предсказаний следующего токена, на которых можно учиться.
Это среднее и есть потери: одно число, которое оценивает всю модель на этом батче. Если батч зафиксирован, потери зависят только от параметров. Измените любой из них — изменятся вероятности, а вместе с ними и потери. Обучение — это поиск таких значений параметров, при которых потери малы.
Скатываемся под горку
Начнём с одного параметра, а все остальные пока не трогаем. Чуть-чуть увеличим его, скажем на 0,001, и снова измерим потери. Пусть потери выросли на 0,002: они растут со скоростью 2 на единицу этого параметра. Эта скорость — наклон параметра, здесь , и он говорит: «чтобы уменьшить потери, сдвинь этот параметр вниз». Наклон сказал бы: «сдвинь вверх, он важен вчетверо меньше». Наклон около 0 значит, что сейчас этот параметр почти ни на что не влияет.
Соберите по наклону для каждого параметра — получится градиент : список из миллиардов чисел, который, если читать его как одно направление, указывает, куда потери растут быстрее всего. Значит, шагаем в обратную сторону — каждый параметр сдвигается против своего наклона, пропорционально ему:
Здесь обозначает все параметры, а (эта) — скорость обучения (learning rate), размер каждого шага. При наклоне и параметр сдвинется вниз на 0,02. Измерять наклоны, подталкивая параметры по одному, значило бы запускать модель целиком на каждый параметр — миллиарды запусков на шаг; обратное распространение ошибки, следующая глава, получает их все разом. Пока просто считайте, что они у нас есть.
Обычно это представляют как ландшафт. Каждый возможный набор параметров — это место на карте, а потери — высота земли в этом месте. Обучение означает дойти до низины, но модель не видит ландшафт, только уклон у себя под ногами, поэтому спускается шаг за шагом. У настоящей модели по одному направлению на каждый параметр, и такой ландшафт не нарисовать. А у модели всего с двумя параметрами — можно: ниже два параметра — это два горизонтальных направления поверхности, а высота — потери. Три оптимизатора — правила, по которым градиент превращается в шаг (о них чуть ниже), — стартуют из одной точки.
Три оптимизатора
- SGD (стохастический градиентный спуск) делает шаг ровно по формуле выше. «Стохастический» — потому что каждый шаг использует градиент случайного батча, а не всего набора данных, так что направление — шумная оценка.
- Импульс (momentum) хранит накопленную сумму прошлых градиентов, как тяжёлый шар, который продолжает катиться. Он разгоняется вдоль устойчивых направлений и гасит зигзаги в узких долинах.
- Adam добавляет второе скользящее среднее — квадрата градиента — и делит шаг каждого параметра на его корень. Параметры с большими шумными градиентами делают осторожные шаги, «тихие» — шаги побольше. Adam и его вариант AdamW с затуханием весов (weight decay — небольшим притяжением каждого веса к нулю на каждом шаге) обучают почти все современные LLM.
Чтобы все три оптимизатора управлялись одним ползунком, импульс использует четверть скорости обучения, а Adam — десятую часть: нормированные шаги Adam и так примерно равны скорости обучения, а шаги SGD растут вместе с градиентом.
Почему настоящий ландшафт дружелюбнее, чем кажется
У настоящих поверхностей потерь не два измерения, а миллиарды. Звучит безнадёжно, но на деле это помогает: настоящему локальному минимуму нужно, чтобы земля изгибалась вверх во всех направлениях сразу, а это редкость. Большинство плоских мест — седловые точки, где ещё остаётся путь вниз, а шум от случайных батчей не даёт оптимизатору застыть. Большие модели при правильных настройках приходят к очень похожим значениям потерь из разных случайных стартов.
Настоящее обучение
Ещё две детали превращают учебную формулу в то, как на самом деле обучают LLM.
Расписание скорости обучения. В начале веса случайны, а градиенты большие и беспорядочные, поэтому полноразмерный шаг может забросить модель куда-то очень далеко. Поэтому обучение начинается с прогрева (warmup): скорость обучения линейно растёт от нуля на протяжении первых нескольких тысяч шагов. Затем она медленно убывает, обычно по косинусоиде, чтобы последние шаги были маленькими и точными.
Размер батча. Градиент по батчу — это среднее по его примерам. Большие батчи дают более чистую оценку (шум убывает как ) и позволяют тысячам GPU работать параллельно, но ценой меньшего числа шагов на каждый увиденный токен.
Итоговые потери ≈ 2,62. Маленькие батчи дают шумную, рваную кривую; без прогрева первые шаги взрывают потери, прежде чем они начнут падать.
Форма кривой потерь — сначала крутой спад, потом длинный пологий хвост — удивительно стабильна от запуска к запуску. Именно эта стабильность делает возможным следующий раздел.
Законы масштабирования
Если нанести итоговые потери множества обучений против потраченных ими вычислений (общего числа арифметических операций), проявится поразительно чёткая закономерность: потери падают по гладкому степенному закону — каждое удесятерение вычислений уменьшает потери примерно в одно и то же число раз. (Строго говоря, так убывает часть потерь над неустранимым минимумом; формула ниже выписывает этот минимум явно.) В логарифмических осях (обе размечены 1, 10, 100, …) это прямая линия на протяжении многих порядков. Лаборатории подгоняют такие кривые на маленьких дешёвых запусках и с их помощью предсказывают, как поведёт себя модель в сто раз больше, ещё до того, как тратить деньги.
У вычислений на обучение простая формула; считают их во FLOP (операциях с плавающей точкой — отдельных сложениях и умножениях). Для модели с параметрами, обученной на токенах:
(около 2 операций на параметр и токен на прямой проход, то есть вычисление модели на входе, и 4 — на обратный проход, который считает градиенты). Значит, при фиксированном бюджете есть компромисс: модель покрупнее увидит меньше токенов. Какое разделение даёт минимальные потери? Исследование Chinchilla (Hoffmann и др., 2022) описало потери формулой
где — неустранимая энтропия самого текста (его непредсказуемость, которую не уберёт никакая модель), а два других слагаемых — штрафы за слишком маленькую модель и за слишком мало данных. Если минимизировать это выражение при условии , оказывается, что обе величины должны расти вместе, примерно как , что даёт около 20 токенов обучения на один параметр.
566 млрд токенов обучения
≈ 19 токенов на параметр
итоговые потери 2,032
4,53 трлн токенов обучения
≈ 1230 токенов на параметр
итоговые потери 2,093 · цена одного токена ответа ×0,13
В 8,0 раза дешевле в работе ценой +0,061 к потерям. При миллиардах запросов это обычно выгодно.
До Chinchilla многие большие модели были слишком велики для того объёма данных, который они видели. Сама Chinchilla — 70 миллиардов параметров на 1,4 триллиона токенов — обошла модель на 280 миллиардов параметров, обученную за те же вычисления.
Почему современные модели нарушают это правило. «Оптимум по вычислениям» учитывает только цену обучения. Потом развёрнутая модель генерирует триллионы токенов, и каждый из них стоит около FLOP. Модель поменьше, обученная на гораздо большем объёме данных, чуть хуже оптимума для своего бюджета обучения, зато намного дешевле на всё время работы. Поэтому открытые модели теперь обучают далеко за пределами 20 токенов на параметр: Llama 3 8B от Meta увидела больше 15 триллионов токенов — около 2000 на параметр. Потери продолжают падать, просто медленнее.
Они предсказывают потери — среднее по всему тексту. Конкретные умения, например многошаговая арифметика или следование новому формату, всё ещё могут появляться довольно внезапно по мере улучшения потерь, а результаты на бенчмарках (стандартных наборах тестовых задач для сравнения моделей) прогнозировать гораздо труднее, чем сами потери.
Обучение в большом масштабе
Обучение передовой модели тратит порядка FLOP и больше. Одному GPU не хватило бы на это жизни, и ни у одного GPU нет памяти, чтобы вместить такую модель. Это становится возможным благодаря двум приёмам: более дешёвым числам и множеству GPU.
Смешанная точность
Каждый вес, активация (промежуточное значение, вычисленное внутри модели) и градиент — число с плавающей точкой. Меньше бит на число — меньше памяти, меньше данных для пересылки и гораздо более быстрые матричные блоки. Подвох в том, что формат делит свои биты между порядком (экспонентой: насколько большим или маленьким может быть число) и мантиссой (сколько значащих цифр сохраняется). Введите число и посмотрите, что сохранит каждый формат.
BF16 сохраняет 8 бит порядка от FP32, поэтому в него помещаются и крошечные градиенты, и большие активации; плата — всего около 3 значащих цифр. FP16 точнее, но переполняется выше 65 504 и обнуляет очень малые числа, поэтому обучение в FP16 требует «масштабирования потерь».
Современные LLM обучают со смешанной точностью (mixed precision): большие матричные умножения выполняются в BF16 — 16-битном формате с диапазоном обычного 32-битного FP32, но с меньшей точностью (а всё чаще в FP8, с отдельным масштабом на каждый блок чисел, чтобы значения не выходили за диапазон), а основная копия весов и состояние оптимизатора остаются в FP32, чтобы крошечные обновления не терялись при округлении. С Adam это стоит около 16 байт на параметр: 2 на веса в BF16, 2 на градиенты, 4 на основные веса в FP32 и 8 на два скользящих средних Adam. Модели на 70 миллиардов параметров нужно больше терабайта памяти ещё до учёта активаций.
Распределяем работу по GPU
Этот терабайт и сами вычисления делят между тысячами GPU тремя взаимодополняющими способами:
Каждый GPU хранит полную копию модели и получает свою часть батча.
- Параллелизм по данным (data parallelism) — самый простой: у каждого GPU вся модель и своя часть батча, а после каждого шага градиенты усредняются операцией all-reduce (обменом, после которого у каждого GPU одно и то же среднее). Его современная форма, ZeRO или FSDP (fully sharded data parallel), ещё и шардирует веса, градиенты и состояние оптимизатора — делит их на куски, по одному на GPU, — и собирает каждый слой только тогда, когда он нужен, так что ни один GPU не хранит все 16 байт на параметр.
- Тензорный параллелизм режет каждую матрицу весов на блоки по столбцам или строкам (тензор — массив чисел любой размерности; матрица — двумерный тензор). Тогда каждый слой требует обмена частичными результатами, поэтому его используют между 4–8 GPU внутри одного сервера, соединёнными очень быстрыми каналами.
- Конвейерный параллелизм (pipeline) отдаёт каждой группе GPU диапазон слоёв и пропускает через них микробатчи, как по конвейеру. Его цена — «пузырь»: пока конвейер заполняется и опустошается, часть стадий простаивает.
В последних моделях появились ещё две оси: параллелизм экспертов размещает разных экспертов слоя смеси экспертов на разных GPU, а параллелизм по контексту (по последовательности) делит очень длинные последовательности между GPU при обучении на длинном контексте.
Данные
Классическое машинное обучение проходит по набору данных много раз; каждый проход называется эпохой. Предобучение LLM — первый и самый большой этап обучения, предсказание следующего токена на сыром тексте, — обычно видит большинство документов примерно один раз. Поэтому бюджет считают в токенах: небольшие открытые модели обучают на многих триллионах токенов. В таком масштабе качество данных важно не меньше их количества, и на него уходит огромная часть инженерной работы:
- Дедупликация. Интернет полон копий. Точные и почти точные дубликаты (их находят, сравнивая короткие «отпечатки» текстов, — хешированием вроде MinHash) удаляют, потому что повторяющийся текст тратит вычисления впустую и подталкивает модель к запоминанию.
- Фильтрация. Спам, шаблонный мусор и машинно сгенерированный хлам выбрасывают. Всё чаще небольшие модели-классификаторы (модели, раскладывающие входы по категориям) оценивают каждую страницу по образовательной ценности, и остаётся только лучшая доля.
- Смешивание. Итоговая смесь — тщательно подобранный рецепт из веб-страниц, кода, книг, научных статей, математики и множества языков; пропорции подбирают на маленьких пробных запусках.
- Синтетические данные. Существующие модели пишут учебники, упражнения, разборы решений и переформулировки хороших документов. При аккуратном использовании это добавляет качественные токены там, где естественного текста мало.
- Отжиг (annealing). Ближе к концу предобучения, пока скорость обучения убывает до нуля, смесь сдвигают в сторону самых качественных данных (отобранная математика, код, рассуждения). Эти последние шаги непропорционально сильно влияют на итоговую модель. Некоторые лаборатории называют такую отдельную фазу mid-training; в ней же часто увеличивают длину контекста.
Итоги
- Параметры — это числа в таблицах и матрицах весов модели. Ничего другого обучение не меняет.
- Функция потерь для предсказания следующего токена — перекрёстная энтропия: вероятности, данной правильному токену, усреднённый по батчу.
- Наклон параметра показывает, как отзовутся потери, если этот параметр чуть подвинуть; градиент — список всех наклонов. Градиентный спуск сдвигает каждый параметр на маленький шаг против его наклона; размер шага задаёт скорость обучения. LLM обучают с AdamW, прогревом и убывающим расписанием.
- Потери падают по степенному закону от вычислений. При фиксированном бюджете минимум потерь даёт около 20 токенов на параметр, но развёрнутые модели обучают гораздо дольше, потому что маленькие модели дешевле в работе.
- Обучение в большом масштабе держится на смешанной точности BF16/FP8, на параллелизме по данным, тензорном и конвейерном между тысячами GPU и на тщательно очищенных от дублей, отфильтрованных и смешанных данных.
Остался один вопрос: как при миллиардах параметров получить градиент для каждого из них? Это обратное распространение ошибки.