Accelerate documentation
Устранение неполадок
Устранение неполадок
В этом руководстве представлены решения некоторых проблем, с которыми вы можете столкнуться при использовании Accelerate. Не все ошибки описаны, поскольку Accelerate — это активно развивающаяся библиотека, которая постоянно совершенствуется, а сценариев использования и конфигураций распределённого обучения существует множество. Если описанные здесь решения не помогут с вашей конкретной ошибкой, обратитесь к разделу Обращение за помощью, чтобы узнать, где и как можно получить поддержку.
Логирование
Логирование помогает определить источник ошибки. В распределённой среде с несколькими процессами ведение логов может быть сложной задачей, но Accelerate предоставляет утилиту logging(), которая обеспечивает синхронизацию логов.
Чтобы устранить проблему, используйте logging() вместо стандартного модуля logging Python. Установите уровень детализации (INFO, DEBUG, WARNING, ERROR, CRITICAL) с помощью параметра log_level, после чего вы можете:
- Экспортируйте
log_levelв качестве переменной окруженияACCELERATE_LOG_LEVEL. - Передайте
log_levelнепосредственно вget_logger.
Например, чтобы установить log_level="INFO":
from accelerate.logging import get_logger
logger = get_logger(__name__, log_level="DEBUG")По умолчанию лог вызывается только на основных процессах. Чтобы вызвать его на всех процессах, передайте main_process_only=False.
Если лог должен вызываться на всех процессах и в определённом порядке, также передайте in_order=True.
from accelerate.logging import get_logger
logger = get_logger(__name__, log_level="DEBUG")
# log all processes
logger.debug("thing_to_log", main_process_only=False)
# log all processes in order
logger.debug("thing_to_log", main_process_only=False, in_order=True)Зависание кода и ошибки тайм-аута
Существует множество причин, по которым ваш код может зависать. Рассмотрим, как решить некоторые из самых распространённых проблем, которые могут приводить к зависанию кода.
Несовпадение форм тензоров
Несовпадение форм тензоров — распространённая проблема, которая может приводить к длительной блокировке кода в распределённой среде.
При запуске скриптов в распределённой конфигурации функции, такие как Accelerator.gather() и Accelerator.reduce(), необходимы для получения тензоров с разных устройств для коллективного выполнения операций над ними. Эти (и другие) функции полагаются на torch.distributed для выполнения операции gather, которая требует, чтобы тензоры имели строго одинаковую форму во всех процессах. Если формы тензоров не совпадают, ваш код зависнет, и в конечном счёте возникнет исключение по таймауту.
Вы можете использовать режим операционной отладки Accelerate, чтобы сразу обнаружить эту проблему. Мы рекомендуем включить этот режим при настройке accelerate config, но его также можно активировать из командной строки, через переменную окружения или вручную отредактировав файл config.yaml.
accelerate launch --debug {my_script.py} --arg1 --arg2Если вы включаете режим отладки с помощью переменной окружения, вызывать accelerate launch не нужно.
ACCELERATE_DEBUG_MODE="1" torchrun {my_script.py} --arg1 --arg2Добавьте debug: true в ваш файл config.yaml.
compute_environment: LOCAL_MACHINE
debug: trueПосле включения режима отладки вы получите трассировку, указывающую на проблему с несоответствием форм тензоров.
Traceback (most recent call last):
File "/home/zach_mueller_huggingface_co/test.py", line 18, in <module>
main()
File "/home/zach_mueller_huggingface_co/test.py", line 15, in main
broadcast_tensor = broadcast(tensor)
File "/home/zach_mueller_huggingface_co/accelerate/src/accelerate/utils/operations.py", line 303, in wrapper
accelerate.utils.operations.DistributedOperationException:
Cannot apply desired operation due to shape mismatches. All shapes across devices must be valid.
Operation: `accelerate.utils.operations.broadcast`
Input shapes:
- Process 0: [1, 5]
- Process 1: [1, 2, 5]Ранняя остановка
Для раннего останова в распределённом обучении, если у каждого процесса есть собственное условие останова (например, валидационная потеря), оно может не быть синхронизировано между всеми процессами. В результате оператор break может сработать в процессе 0, но не в процессе 1, из-за чего ваш код будет зависать бесконечно до наступления таймаута.
Если у вас есть условия ранней остановки, используйте методы set_trigger и check_trigger, чтобы убедиться, что все процессы завершаются корректно.
# Assume `should_do_breakpoint` is a custom-defined function that returns a conditional,
# and that conditional might be true only on process 1
if should_do_breakpoint(loss):
accelerator.set_trigger()
# Later in the training script when we need to check for the breakpoint
if accelerator.check_trigger():
breakНизкие версии ядра в Linux
На Linux с версией ядра < 5.5 сообщалось о зависании процессов. Чтобы избежать этой проблемы, обновите систему до более поздней версии ядра.
MPI
Если ваша задача распределённого обучения на CPU с использованием MPI зависает, убедитесь, что между узлами настроен SSH без пароля (с использованием ключей). Это означает, что для всех узлов в вашем hostfile вы должны иметь возможность подключаться по SSH с одного узла на другой без запроса пароля.
Далее выполните команду mpirun для проверки работоспособности. Например, команда ниже должна вывести имена узлов для каждого из них.
mpirun -f hostfile -n {number of nodes} -ppn 1 hostnameНехватка памяти
One of the most frustrating errors when it comes to running training scripts is hitting “Out-of-Memory” on devices like CUDA, XPU or CPU. The entire script needs to be restarted and any progress is lost.
Для решения этой проблемы Accelerate предоставляет утилиту find_executable_batch_size(), которая в значительной степени основана на toma. Эта утилита повторяет выполнение кода, который завершается с ошибкой из-за нехватки памяти (OOM), и автоматически уменьшает размеры батчей. При каждом срабатывании OOM алгоритм уменьшает размер батча вдвое и повторяет выполнение кода до тех пор, пока он не выполнится успешно.
Чтобы использовать find_executable_batch_size(), перестройте функцию обучения, включив в неё внутреннюю функцию с find_executable_batch_size и создав загрузчики данных внутри неё. Минимально для этого потребуется всего 4 новые строки кода.
Внутренняя функция должна принимать размер батча в качестве первого параметра, но при вызове мы не передаём его. Обёртка обработает это за вас. Любой объект (модели, оптимизаторы), который использует память устройства и передаётся в Accelerator, также должен быть объявлен внутри внутренней функции.
def training_function(args):
accelerator = Accelerator()
+ @find_executable_batch_size(starting_batch_size=args.batch_size)
+ def inner_training_loop(batch_size):
+ nonlocal accelerator # Ensure they can be used in our context
+ accelerator.free_memory() # Free all lingering references
model = get_model()
model.to(accelerator.device)
optimizer = get_optimizer()
train_dataloader, eval_dataloader = get_dataloaders(accelerator, batch_size)
lr_scheduler = get_scheduler(
optimizer,
num_training_steps=len(train_dataloader)*num_epochs
)
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
)
train(model, optimizer, train_dataloader, lr_scheduler)
validate(model, eval_dataloader)
+ inner_training_loop()Несовпадение результатов при разных конфигурациях устройств
Если вы изменили конфигурацию устройств и наблюдаете различия в производительности модели, скорее всего, вы не обновили скрипт при переходе от одной конфигурации к другой. Даже если вы используете один и тот же скрипт с тем же размером батча, результаты будут различаться на TPU, в конфигурации с несколькими GPU и с одним GPU.
Например, если вы обучали модель на одном GPU с размером батча 16 и переходите к конфигурации с двумя GPU, вам нужно изменить размер батча на 8, чтобы сохранить тот же эффективный размер батча. Это связано с тем, что при обучении с помощью Accelerate размер батча, передаваемый в dataloader, является размером батча на GPU.
Чтобы убедиться, что результаты воспроизводимы между различными конфигурациями, используйте один и тот же seed, соответствующим образом отрегулируйте batch size и рассмотрите возможность масштабирования learning rate.
Для получения подробной информации и краткой справки по размерам пакетов ознакомьтесь с руководством Сравнение производительности на разных конфигурациях устройств.
Проблемы с производительностью на разных GPU
Если ваша многопроцессорная конфигурация включает различные GPU, вы можете столкнуться с некоторыми проблемами производительности:
- Возможна неравномерность объёма памяти GPU между графическими процессорами. В этом случае GPU с меньшим объёмом памяти будет ограничивать размер батча или размер модели, которую можно загрузить на GPU.
- Если вы используете GPU с различными профилями производительности, общая производительность будет определяться самым медленным GPU, поскольку остальные GPU будут вынуждены ждать завершения его нагрузки.
Значительно различающиеся по характеристикам GPU в рамках одной конфигурации могут стать причиной узких мест в производительности.
Попросить о помощи
Если ни одно из решений и советов, приведённых здесь, не помогло решить вашу проблему, вы всегда можете обратиться за помощью к сообществу и команде Accelerate.
Обратитесь за помощью на форумах Hugging Face, разместив свой вопрос в категории Accelerate. Убедитесь, что ваш пост содержит подробное описание проблемы, релевантный контекст о вашей конфигурации и воспроизводимый код, чтобы максимально повысить шансы на решение вашей задачи!
Задайте вопрос в Discord, и команда и сообщество помогут вам.
Создайте Issue в репозитории GitHub Accelerate, если вы считаете, что нашли ошибку, связанную с библиотекой. Укажите контекст проблемы и детали вашей распределённой конфигурации, чтобы мы могли точнее определить причину сбоя и найти способ её исправить.