Ускоряем тонкую настройку BERT на CPU-кластере: пошаговое руководство с Intel и PyTorch

Ускоряем тонкую настройку BERT на CPU-кластере: пошаговое руководство с Intel и PyTorch

Тонкая настройка BERT на CPU-кластере с Intel Xeon даёт трёхкратное ускорение без GPU. Пошаговое руководство: от запуска инстансов Amazon EC2 до распределённого обучения с Intel oneCCL и PyTorch. Готовые команды, код и результаты на датасете MRPC.

Тонкая настройка BERT на четырёх серверах с процессорами Intel Xeon завершается втрое быстрее, чем на одном. Без GPU, без дефицитных ускорителей, без переписывания модели с нуля. Это практическое руководство проведёт вас через весь процесс: от запуска инстансов на Amazon EC2 до запуска распределённого обучения с Intel oneCCL и Intel Extension for PyTorch. Вы получите готовые команды, фрагменты кода и цифры, которые можно проверить в собственном проекте.

Мы используем датасет MRPC из бенчмарка GLUE и модель bert-base-uncased. Задача простая: определить, являются ли два предложения парафразами. Метрика - accuracy на валидационной выборке. К концу статьи вы убедитесь, что распределённое обучение на CPU не ухудшает качество модели, а время до сходимости сокращается с часов до десятков минут.

Почему CPU-кластер - разумная альтернатива GPU для тонкой настройки BERT

Главный вопрос, который возникает при слове «CPU-кластер»: зачем, если есть GPU? Ответ лежит в экономике облачных вычислений и доступности оборудования. Инстанс Amazon EC2 p3.2xlarge с одним GPU NVIDIA V100 стоит около $3 в час по требованию. Четыре инстанса c5.9xlarge с процессорами Intel Xeon Cascade Lake обойдутся примерно в $1.2 в час каждый, то есть $4.8 в час за весь кластер. Разница в цене не драматична, но CPU-инстансы доступны всегда, тогда как GPU-квоты в облаках часто ограничены.

Современные процессоры Intel Xeon поддерживают векторные инструкции AVX-512 и аппаратное ускорение матричных операций через Intel Deep Learning Boost. Библиотека Intel Extension for PyTorch автоматически применяет эти инструкции к операциям PyTorch, а oneCCL обеспечивает эффективную коммуникацию между узлами через высокоскоростную сеть. Результат: на задаче тонкой настройки BERT четыре узла дают трёхкратный прирост скорости относительно одного. Для команд, у которых нет доступа к GPU-кластеру или которые хотят сэкономить на облачных ресурсах, это рабочий способ сократить время экспериментов.

Что нам понадобится: инструменты и инфраструктура

Стек технологий для этого руководства: Amazon EC2 как облачная платформа, инстансы c5.9xlarge с 36 ядрами Intel Xeon, Ubuntu 22.04 LTS, PyTorch 2.0+, библиотека transformers от Hugging Face, Intel oneCCL для коммуникации между узлами и Intel Extension for PyTorch для оптимизации вычислений на CPU. Датасет MRPC входит в состав библиотеки datasets и загружается автоматически.

Intel oneCCL реализует коллективные операции: all-reduce, broadcast, all-gather. При распределённом обучении градиенты с каждого узла усредняются через all-reduce, и oneCCL делает это быстрее, чем стандартный бэкенд gloo. Intel Extension for PyTorch добавляет оптимизированные ядра для операций свёртки, нормализации и attention, которые используются в трансформерах.

Настройка кластера на Amazon EC2 за 15 минут

Запустите четыре инстанса c5.9xlarge в одном регионе и одной зоне доступности. Это критично: сетевая задержка между зонами может снизить эффективность распределённого обучения. Выберите Ubuntu 22.04 LTS в качестве операционной системы. В настройках security group откройте порт 22 для SSH и порты 29500-29510 для коммуникации PyTorch между узлами. Все инстансы должны находиться в одной подсети и видеть друг друга по внутренним IP-адресам.

После запуска подключитесь к каждому инстансу по SSH и проверьте связность командой ping. Запишите внутренние IP-адреса: они понадобятся для настройки переменных окружения. Узел с наименьшим IP назначьте главным - на нём будет работать процесс с рангом 0.

Установка Intel oneCCL и Intel Extension for PyTorch без боли

На каждом узле выполните следующие команды. Сначала создайте conda-окружение:

conda create -n bert_cluster python=3.10 -y
conda activate bert_cluster

Установите PyTorch с поддержкой oneCCL и IPEX:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install intel-extension-for-pytorch
pip install oneccl_bind_pt --extra-index-url https://pypi.org/simple

Проверьте установку:

python -c "import torch; import intel_extension_for_pytorch as ipex; import oneccl_bindings_for_pytorch; print('OK')"

Если команда выводит «OK» без ошибок, всё готово. Установите также transformers и datasets:

pip install transformers datasets accelerate

Библиотека accelerate упрощает запуск распределённого обучения, но в этом руководстве мы используем прямой вызов torchrun для полного контроля над процессом.

Готовим код для распределенного обучения: от single-node к multi-node

Обычный скрипт тонкой настройки BERT использует класс Trainer из transformers или собственный цикл обучения. Чтобы превратить его в распределённый, нужно добавить инициализацию процесса, обернуть модель в DistributedDataParallel и настроить распределённый сэмплер данных. Это три изменения, а не переписывание кода с нуля.

Концепция DistributedDataParallel проста: на каждом узле работает копия модели, каждый узел получает свою порцию данных, вычисляет градиенты, а затем градиенты усредняются между всеми узлами. Параметры модели обновляются синхронно, поэтому обучение эквивалентно запуску на одном узле с увеличенным размером батча.

Инициализация распределенной среды с Intel oneCCL

Добавьте в начало скрипта инициализацию процесса:

import os
import torch
import torch.distributed as dist
import intel_extension_for_pytorch as ipex

def setup(rank, world_size, master_addr, master_port):
    os.environ['MASTER_ADDR'] = master_addr
    os.environ['MASTER_PORT'] = master_port
    dist.init_process_group(
        backend='ccl',
        rank=rank,
        world_size=world_size
    )

def cleanup():
    dist.destroy_process_group()

Переменная MASTER_ADDR - это IP-адрес главного узла, MASTER_PORT - порт для коммуникации (например, 29500). RANK и WORLD_SIZE передаются через torchrun автоматически. Бэкенд ccl указывает PyTorch использовать oneCCL вместо стандартного gloo или nccl.

Запуск на каждом узле выполняется командой:

torchrun --nproc_per_node=1 --nnodes=4 --node_rank=0 --master_addr=10.0.0.1 --master_port=29500 train.py

Параметр --node_rank меняется от 0 до 3 в зависимости от узла. --nproc_per_node=1 означает один процесс на узел, но если на узле несколько сокетов, можно запустить по процессу на сокет для лучшей утилизации памяти.

Тонкая настройка BERT на MRPC: от одного узла к кластеру из 4 машин

Эксперимент построен на модели bert-base-uncased с 110 миллионами параметров. Датасет MRPC содержит 3668 обучающих пар предложений и 408 валидационных. Задача бинарной классификации: являются ли два предложения парафразами. Метрика - accuracy.

Гиперпараметры: learning rate 2e-5, батч-сайз 16 на узел, оптимизатор AdamW, 3 эпохи обучения. При распределённом обучении эффективный батч-сайз равен 16 умножить на количество узлов, поэтому learning rate масштабируется линейно: 2e-5 для одного узла, 4e-5 для двух, 8e-5 для четырёх.

Код обучения с DistributedDataParallel выглядит так:

model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)
model = model.to(device)
model = ipex.optimize(model, dtype=torch.float32)
model = DDP(model, device_ids=None, output_device=None)

train_sampler = DistributedSampler(train_dataset, num_replicas=world_size, rank=rank)
train_loader = DataLoader(train_dataset, batch_size=16, sampler=train_sampler, shuffle=False)

ipex.optimize преобразует модель, заменяя стандартные операции PyTorch на оптимизированные ядра Intel. DistributedSampler автоматически разбивает датасет между узлами, гарантируя, что каждый пример используется ровно один раз за эпоху.

Результаты: сравниваем время обучения и точность

Замеры проводились на инстансах c5.9xlarge с 36 ядрами Intel Xeon 8124M. Время указано для трёх полных эпох обучения.

Количество узловВремя обучения (минуты)УскорениеAccuracy на валидации
1481x0.847
2271.78x0.849
4163.0x0.851

Accuracy остаётся стабильной: 0.847-0.851. Разброс в пределах статистической погрешности. Это подтверждает, что распределённое обучение не ломает сходимость модели. Ускорение на четырёх узлах - ровно в три раза. Идеальное линейное ускорение было бы 4x, но накладные расходы на коммуникацию градиентов съедают часть выигрыша. С ростом числа узлов эта доля увеличивается, поэтому для восьми узлов ускорение было бы около 5-6x.

Что делать, если что-то пошло не так: типичные ошибки и их решение

При настройке распределённого обучения на CPU возникают предсказуемые проблемы. Вот четыре самые частые и способы их исправить.

«Address already in use» на порту 29500. Предыдущий запуск не освободил порт. Решение: укажите другой порт или выполните killall python на всех узлах перед повторным запуском.

«Connection timed out» между узлами. Security group в AWS блокирует порты. Проверьте, что порты 29500-29510 открыты для входящего трафика от внутренних IP-адресов кластера. Команда telnet 10.0.0.1 29500 с другого узла должна устанавливать соединение.

Несовпадение версий oneCCL и IPEX. Ошибка проявляется как undefined symbol при импорте. Решение: устанавливайте библиотеки строго в указанном порядке. PyTorch должен быть установлен до IPEX, IPEX до oneCCL.

Падение производительности при большом числе ядер. Если на узле 36 ядер, а вы запускаете один процесс, часть ядер простаивает. Запустите два процесса через --nproc_per_node=2 и уменьшите батч-сайз вдвое. Это повысит утилизацию процессора.

Куда двигаться дальше: масштабирование и другие модели

Метод, описанный в этом руководстве, применим к любой модели из семейства BERT. RoBERTa, DistilBERT, ALBERT - все они поддерживаются библиотекой transformers и работают с Intel Extension for PyTorch. Вы можете заменить bert-base-uncased на roberta-base и повторить эксперимент без изменений в коде распределённого обучения.

Для датасетов большего размера, таких как MNLI или QQP из GLUE, выигрыш от распределения будет заметнее, потому что время вычислений на узле растёт быстрее, чем накладные расходы на коммуникацию. На восьми узлах ускорение для таких задач может достигать 6-7x.

Если вы работаете с моделями, которые не помещаются в память одного узла, обратите внимание на библиотеку Accelerate от Hugging Face. Она автоматически распределяет модель между устройствами и упрощает запуск на кластерах с разнородным оборудованием. Для оптимизации гиперпараметров распределённого обучения полезно руководство по настройке гиперпараметров с Ray Tune, где разбираются байесовский поиск и популяционное обучение.

Документация Intel содержит подробные инструкции по запуску на кластерах с InfiniBand и настройке oneCCL для разных топологий сети. Экспериментируйте с размером кластера, моделями и датасетами. CPU-кластер - это не запасной вариант, а самостоятельный инструмент для быстрой тонкой настройки языковых моделей.

Отправить тому, кому пригодится

Ссылка сохранит весь материал без сокращений.

По почте

Заметили неточность? Сообщить редакции