Вопрос или проблема
Я сталкиваюсь с проблемами при получении свободного порта в блоке настройки DDP в PyTorch для параллелизации моей задачи обучения глубокому обучению на нескольких GPU в кластере HPC под управлением Linux.
Я пытаюсь отправить задачу обучения глубокому обучению в кластер HPC под управлением Linux, используя скрипт SLURM. Обучение требует параллелизации модели и данных на 4 GPU. Я использую модуль Distributed Data Parallel (DDP) в PyTorch для распределения модели и данных на 4 GPU. Я определил блок настройки DDP следующим образом:
def ddp_setup(rank, world_size):
"""
Args:
rank: Уникальный идентификатор каждого процесса
world_size: Общее количество процессов
"""
try:
# Найти свободный порт динамически
def find_free_port():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('', 0))
s.listen(1)
port = s.getsockname()[1]
return port
# Уничтожить любые существующие группы процессов
if dist.is_initialized():
dist.destroy_process_group()
# Установить переменные окружения с динамическим портом
os.environ['MASTER_ADDR'] = 'localhost'
master_port = find_free_port()
os.environ['MASTER_PORT'] = str(master_port)
# Инициализировать группу процессов с дополнительной обработкой ошибок
init_process_group(
backend="nccl",
rank=rank,
world_size=world_size,
timeout=timedelta(minutes=60),
init_method='env://'
)
# Установить устройство GPU
torch.cuda.set_device(rank)
print(f"Группа процессов успешно инициализирована для ранга {rank}")
except RuntimeError as e:
print(f"Ошибка при инициализации группы процессов: {e}")
# Предоставить более специфическую обработку ошибок
if "Адрес уже используется" in str(e):
print("Порт уже используется. Убедитесь, что предыдущие процессы не работают.")
raise
except Exception as e:
print(f"Неожиданная ошибка в ddp_setup: {e}")
raise
Но проблема, с которой я сталкиваюсь каждый раз при запуске кода, заключается в том, что выделенный порт отображается как “уже используется”, независимо от его номера. Я приложил блок ошибок ниже:
[W socket.cpp:426] [c10d] Серверный сокет не смог прослушивать [::]:12356 (errno: 98 – Адрес уже используется).
[W socket.cpp:426] [c10d] Серверный сокет не смог связаться с 0.0.0.0:12356 (errno: 98 – Адрес уже используется).
[E socket.cpp:462] [c10d] Серверный сокет не смог прослушивать ни один локальный сетевой адрес. Traceback (most recent call last): File “/scratch/j20240138/final_rdnet.py”, line 401, in mp.spawn(main, args=(world_size, args.save_every, args.total_epochs, args.batch_size), nprocs=world_size), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/multiprocessing/spawn.py”, line 239, in spawn return start_processes(fn, args, nprocs, join, daemon, start_method=’spawn’) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/multiprocessing/spawn.py”, line 197, in start_processes while not context.join(): ^^^^^^^^^^^^^^ File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/multiprocessing/spawn.py”, line 160, in join raise ProcessRaisedException(msg, error_index, failed_process.pid) torch.multiprocessing.spawn.ProcessRaisedException: — Процесс 0 завершился с следующей ошибкой: Traceback (most recent call last): File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/multiprocessing/spawn.py”, line 69, in _wrap fn(i, *args) File “/scratch/j20240138/final_rdnet.py”, line 369, in main ddp_setup(rank, world_size) File “/scratch/j20240138/final_rdnet.py”, line 195, in ddp_setup init_process_group(backend=”nccl”, rank=rank, world_size=world_size, timeout=timedelta(minutes=60)) File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/distributed/distributed_c10d.py”, line 900, in init_process_group store, rank, world_size = next(rendezvous_iterator) ^^^^^^^^^^^^^^^^^^^^^^^^^ File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/distributed/rendezvous.py”, line 245, in _env_rendezvous_handler store = _create_c10d_store(master_addr, master_port, rank, world_size, timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/distributed/rendezvous.py”, line 176, in _create_c10d_store return TCPStore( ^^^^^^^^^ RuntimeError: Серверный сокет не смог прослушивать ни один локальный сетевой адрес. Серверный сокет не смог прослушивать на [::]:12356 (errno: 98 – Адрес уже используется). Серверный сокет не смог связаться с 0.0.0.0:12356 (errno: 98 – Адрес уже используется). srun: error: gpu010: задача 0: Завершено с кодом выхода 1 Traceback (most recent call last): File “/scratch/j20240138/final_rdnet.py”, line 401, in mp.spawn(main, args=(world_size, args.save_every, args.total_epochs, args.batch_size), nprocs=world_size), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/multiprocessing/spawn.py”, line 239, in spawn return start_processes(fn, args, nprocs, join, daemon, start_method=’spawn’) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/multiprocessing/spawn.py”, line 197, in start_processes while not context.join(): ^^^^^^^^^^^^^^ File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/multiprocessing/spawn.py”, line 160, in join raise ProcessRaisedException(msg, error_index, failed_process.pid) torch.multiprocessing.spawn.ProcessRaisedException: — Процесс 0 завершился с следующей ошибкой: Traceback (most recent call last): File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/multiprocessing/spawn.py”, line 69, in _wrap fn(i, *args) File “/scratch/j20240138/final_rdnet.py”, line 369, in main ddp_setup(rank, world_size) File “/scratch/j20240138/final_rdnet.py”, line 195, in ddp_setup init_process_group(backend=”nccl”, rank=rank, world_size=world_size, timeout=timedelta(minutes=60)) File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/distributed/distributed_c10d.py”, line 932, in init_process_group _store_based_barrier(rank, store, timeout) File “/home/j20240138/miniconda3/envs/projenv/lib/python3.11/site-packages/torch/distributed/distributed_c10d.py”, line 469, in _store_based_barrier raise RuntimeError( RuntimeError: Превышено время ожидания при инициализации группы процессов в барьере на основе хранилища для ранга: 0, для ключа: store_based_barrier_key:1 (world_size=2, worker_count=3, timeout=1:00:00) srun: error: gpu010: задача 1: Завершено с кодом выхода 1
Мне нужна помощь в решении этой проблемы.
Ответ или решение
Решение проблемы с занятыми портами в DDP Пайторч
Введение
При параллельном обучении глубоких нейронных сетей с использованием Distributed Data Parallel (DDP) в PyTorch, может возникнуть проблема с получением свободного порта на кластере HPC с системой SLURM. Нарушение работы, связанное с тем, что порты уже заняты, может стать серьезным препятствием для эффективного использования распределенных вычислений. Давайте разберем, как обойти эту проблему.
Понимание проблемы
Соседняя ошибка, которую вы наблюдаете ("Address already in use"), указывает на то, что хотя вы пытаетесь динамически назначить порт, он оказывается уже занятым другой сущностью. Это может произойти по нескольким причинам, включая:
- Предыдущие процессы: предыдущие запуски распределенной системы не завершили корректно.
- Конфликты в конфигурации SLURM: SLURM может запустить несколько экземпляров с одинаковыми параметрами.
- Неэффективное определение свободного порта: метод, который вы используете для нахождения свободного порта, может не быть достаточно надежным, если несколько процессов запрашивают порт одновременно.
Рекомендации по решению
Приведем несколько рекомендаций, которые могут помочь в решении проблемы с портами в DDP:
1. Убедитесь, что предыдущие процессы завершены
Перед запуском нового задания, проверьте, что все старые процессы освободили порты. Это можно сделать с помощью команды:
ps aux | grep python
Затем завершите процессы:
kill -9 <pid>
2. Задание портов вручную
Предварительно задайте порты для каждого ранка. Использование фиксированных портов может помочь избежать конфликтов. Для этого можно модифицировать SLURM-скрипт следующим образом:
# Установка портов для каждого ранга
export MASTER_PORT=12345 # Можно выбрать порт выше 1024
Затем в вашем DDP коде:
os.environ['MASTER_PORT'] = str(12345 + rank)
К примеру, если у вас 4 ранга, порты будут 12345, 12346, 12347 и 12348.
3. Используйте командные аргументы
Передавайте порты как аргументы при запуске скрипта. Это можно сделать через аргументы командной строки, что даст вам больше контроля. Например:
srun --job-name=myjob --ntasks=4 --cpus-per-task=1 --gpus=4 \
python your_script.py --port 12345
4. Более надежный метод поиска свободного порта
Если вы хотите продолжить использовать динамический метод, то можно улучшить его с помощью многократного поиска, например, повторяйте попытку несколько раз, если первый запрос не удался:
def find_free_port(start=12345, retries=10):
port = start
for _ in range(retries):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.bind(('', port))
return port
except OSError:
port += 1
raise RuntimeError("No free port found")
Заключение
Проблемы с занятыми портами в DDP в PyTorch можно успешно преодолеть, следуя вышеперечисленным рекомендациям. Убедитесь, что порты не заняты старыми процессами, рассмотрите возможность использования фиксированных портов и улучшайте методы поиска свободных портов. Эти шаги помогут вам оптимизировать вашу работу и решения задач машинного обучения на кластерах HPC.