Многие распределенные работники бездействуют после одной оценки или никогда не получают работу, когда есть больше задач - PullRequest
3 голосов
/ 06 апреля 2019

Мы используем dask для оптимизации архитектур с углубленным изучением (DL) путем создания проектов и последующей отправки их работникам dask, которые, в свою очередь, используют pytorch для обучения. Мы наблюдаем, что некоторые из рабочих, кажется, не запускаются, а те, кто завершает оценку DL, не сразу начинают оценивать следующее ожидающее DL.

Мы реализовали это на суперкомпьютере Summit Oak Ridge National Laboratory. Для нашего прототипа мы отправляем пакетное задание, которое распределяет 92 узла, раскручивает планировщик dask и 92 работника dask, с одним рабочим, выделенным для каждого узла. Каждый узел имел 6 Nvidia Volta V100, два IBM Power9 и 512 ГБ памяти DDR4 + 96 ГБ HMB @. Затем каждый работник использует pytorch для обучения DL и возвращает его точность проверки как «пригодность». Однако, если поставленная архитектура DL нежизнеспособна, генерируется исключение, и соответствующая приспособленность становится -MAXINT.

В начальных испытаниях, в которых участвовали только два работника, мы отметили, что, если работник оценил неправильно сформированный проект DL, ему немедленно будет назначен новый DL для оценки. Ни один из этих двух рабочих не оставался без дела до конца пробега.

Это сжатая версия фактического кода.

from dask.distributed import Client, as_completed

client = Client(scheduler_file=’scheduler.json’)

# posed_dl_designs is a list of random DL architectures, 
# eval_dl is the entry point for the pytorch training
worker_futures = client.map(eval_dl, posed_dl_designs)

for res in as_completed(worker_futures):
    evaluated_dl = res.result()

    # pool is Queue of evaluated DLs sorted by validation    
    # accuracy; so update_pool() replaces the least accurate DL
    # with the newly evaluated DL
    update_pool(evaluated_dl, pool)

    # Let the workers drain down if we meet some kind of budget
    # for generated DL designs; otherwise generate a new DL and
    # give it to a worker for training/evaluation
    if not stop():

        # create_new_dl() selects one of the better DLs from 
        # the pool, clones it, and alters it slightly, thereby 
        # creating a new DL design
        new_dl = create_new_dl(pool)

        # Now evaluate/train the new DL
        new_future = client.submit(eval_dl, new_dl)
        iterator.add(new_future)

И вот как мы вызывали планировщик и рабочих:

# The scheduler doesn't need GPUs. It just needs one lonely core to run on.
jsrun --gpu_per_rs 0 --nrs 1 --tasks_per_rs 1 --cpu_per_rs 1 --rs_per_host 1 dask-scheduler --interface ib0 --no-bokeh --no-show --scheduler-file $SCHEDULER_FILE &

# Spin up an individual task for each worker. Since dask does not use MPI, specify smpiargs none.
for i in {0..91}; do

    jsrun --smpiargs="none" --nrs 1 -e individual --stdio_stdout ${RUN_DIR}/worker_out.%h.%j.%t.%p --stdio_stderr ${RUN_DIR}/worker_error.%h.%j.%t.%p   --tasks_per_rs 1 --cpu_per_rs 14 --gpu_per_rs 6 --rs_per_host 1 dask-worker --nthreads 1 --nprocs 1 --memory-limit 512e9 --interface ib0 --no-bokeh --reconnect --scheduler-file $SCHEDULER_FILE --resources "MEM=512e9" &

done

# Invocation for the controller process elided

Когда мы расширили пробеги, чтобы нанять 92 рабочих, мы обнаружили, что через несколько минут бегали только пять или шесть - это соответствовало рабочим, у которых были жизнеспособные DL для обучения, как их первые кандидаты на проектирование DL.

Свободные работники подразделяются на две категории. Большинство бездействующих работников, по-видимому, оценили сломанный проект DL и верно вернули соответствующее специальное значение, указывающее это; но тогда новый DL так и не был переназначен этому свободному работнику. Другой класс работников никогда не оценивал никаких DL, и следующие данные типизируют их вывод:

distributed.nanny - INFO -         Start Nanny at: 'tcp://10.41.18.55:45941'
distributed.diskutils - INFO - Found stale lock file and directory '/gpfs/alpine/csc342/proj-shared/may/delemera_first_trial_run/worker-c4o0rsb3', purging
distributed.worker - INFO -       Start worker at:    tcp://10.41.18.55:44107
distributed.worker - INFO -          Listening to:    tcp://10.41.18.55:44107
distributed.worker - INFO -              nanny at:          10.41.18.55:45941
distributed.worker - INFO - Waiting to connect to:     tcp://10.41.18.54:8786
distributed.worker - INFO - -------------------------------------------------
distributed.worker - INFO -               Threads:                          1
distributed.worker - INFO -                Memory:                  512.00 GB
distributed.worker - INFO -       Local Directory: /gpfs/alpine/csc342/proj-shared/may/delemera_first_trial_run/worker-kqf62513
distributed.worker - INFO - -------------------------------------------------
distributed.worker - INFO -         Registered to:     tcp://10.41.18.54:8786
distributed.worker - INFO - -------------------------------------------------
distributed.core - INFO - Starting established connection

Итак, для этого класса незанятых работников возникла проблема с установлением связи с планировщиком. Мы не заметили никаких других связанных сообщений.

1 Ответ

1 голос
/ 08 апреля 2019

Оказывается, проблема была не в dask, а в том, как мы вызывали код.

То есть в нашем скрипте верхнего уровня есть параметр времени выполнения, который определяет размер начальной популяции, отправляемой рабочим. Наша первоначальная реализация использовала бы значение по умолчанию, если оно не было указано, и в нашем скрипте отправки задания мы исключили соответствующий аргумент командной строки, что означало, что для размера исходного населения использовалось значение по умолчанию, равное пяти.

Способ, которым мы настроили нашу настройку, как отмечалось в приведенном выше фрагменте кода, заключался в том, что независимо от количества выделенных работников или размера пула обновленных лиц первоначальный размер населения будет диктовать движение вперед по числу. фактических занятых работников. То есть, поскольку мы использовали значение по умолчанию пять, это были первые задачи, заданные планировщиком. Затем, когда каждый работник оценивал DL, он добавлялся в пул, а другому работнику назначался новый DL для оценки. (И необязательно тот же работник, который только что закончил оценивать последний DL, который был первоначальным источником путаницы.) Никогда не было возможности назначить больше работы оставшемуся балансу свободных рабочих.

Мы удалили значение по умолчанию для этого параметра, чтобы заставить пользователей указывать эту критическую информацию для каждого запуска.

Некоторые уроки включают в себя:

  • предоставление значений по умолчанию для аргументов командной строки может стать молчаливым и затруднить поиск источника ошибки
  • не торопятся, чтобы добавить проверки работоспособности для параметров времени выполнения (например, код теперь жалуется и завершается ошибкой, если начальный размер популяции меньше размера пула)
  • не думайте, что это ошибка зависимого пакета; ошибка может быть в вашем собственном коде
  • дисковое пространство дешево, поэтому действительно включите ведение журнала, чтобы получить соответствующую и полезную диагностическую информацию
  • сделать многократные частичные прогоны, чтобы наблюдать патологические паттерны поведения (например, мы отметили, что независимо от количества работников и размера пула оцененных DL только пять работников были когда-либо заняты)
...