Инициализация модуля задачи global в dask worker с помощью --preload?

Я пытался добиться чего-то похожего на эти вопросы (Инициализация состояния для рабочих, распределенных по dask, Настройка рабочего Dask с переменной), где у меня есть ( относительно) большая модель, которую я хочу предварительно инициализировать на подмножестве рабочих, которые будут принимать задачи, требующие эту модель. В идеале я не хочу, чтобы на клиентском компьютере даже была модель.

Моя первоначальная попытка, прежде чем найти эти вопросы, заключалась в том, чтобы определить задачу delayed в общем модуле, worker_task.model, и назначить глобальную переменную модуля (например, worker_tasks.model.model) в --preload скрипте рабочих для использования этой задачей; однако по какой-то причине это не сработало - переменная устанавливается в скрипте предварительной загрузки, но по-прежнему None при вызове задачи.

init_model_worker.py:

import logging
from uuid import uuid4

from worker_tasks import model


def dask_setup(worker):
    model.model = f'<mock model {uuid4()}>'

    logger = logging.getLogger('distributed')
    logger.warning(f'model = {model.model}')

worker_tasks / model.py:

import logging
import random
from time import sleep
from uuid import uuid4

import dask

model = None


@dask.delayed
def compute_clinical(inp):        
    if model is None:
        raise RuntimeError('Model not initialized.')

    sleep(random.uniform(3, 17))

    return {
        'result': random.choice((True, False)),
        'confidence': random.uniform(0, 1)
        }

Это рабочий журнал, когда я запускаю его и отправляю что-то в планировщик:

> dask-worker --preload init_model_worker.py tcp://scheduler:8786 --name model-worker
distributed.utils - INFO - Reload module init_model_worker from .py file                                  
distributed.nanny - INFO -         Start Nanny at: 'tcp://172.28.0.4:41743'                         
distributed.diskutils - INFO - Found stale lock file and directory '/worker-epptq9sh', purging      
distributed.utils - INFO - Reload module init_model_worker from .py file                                  
distributed - WARNING - model = <mock model faa41af0-d925-46ef-91c9-086093d37c71>                   
distributed.worker - INFO -       Start worker at:     tcp://172.28.0.4:37973                       
distributed.worker - INFO -          Listening to:     tcp://172.28.0.4:37973                       
distributed.worker - INFO -              nanny at:           172.28.0.4:41743                       
distributed.worker - INFO -              bokeh at:           172.28.0.4:37766                       
distributed.worker - INFO - Waiting to connect to:       tcp://scheduler:8786                       
distributed.worker - INFO - -------------------------------------------------                       
distributed.worker - INFO -               Threads:                          4                       
distributed.worker - INFO -                Memory:                    1.93 GB                       
distributed.worker - INFO -       Local Directory:           /worker-mhozo9ru                       
distributed.worker - INFO - -------------------------------------------------                       
distributed.worker - INFO -         Registered to:       tcp://scheduler:8786                       
distributed.worker - INFO - -------------------------------------------------                       
distributed.core - INFO - Starting established connection                                           
distributed.worker - WARNING -  Compute Failed                                                      
Function:  compute_clinical                                                                         
args:      ('mock')                                                                                 
kwargs:    {}                                                                                       
Exception: RuntimeError('Model not initialized.')                                                   

Вы можете видеть, что после перезагрузки сценария предварительной загрузки model становится <mock model faa41af0-d925-46ef-91c9-086093d37c71>; но когда пытаюсь вызвать его из задачи, получаю None.

Я постараюсь реализовать решение, основанное на ответах на другие вопросы, но у меня есть несколько вопросов, связанных с предварительной загрузкой воркеров:

  1. Почему модель None, когда я вызываю задачу, после того как я ее назначил в скрипте предварительной загрузки?
  2. Как правило, рекомендуется избегать подобных действий в скрипте worker --preload? Лучше вызвать инициализацию рабочего состояния от клиента? Если да, то почему?

person ipetrik    schedule 13.06.2019    source источник


Ответы (1)


Я подозреваю, что переменная модели сразу же включается в вашу функцию, однако Python сериализует функции. Вместо этого вы можете попробовать следующее:

@dask.delayed
def compute_clinical(inp):       
    from worker_tasks.model import model

    if model is None:
        raise RuntimeError('Model not initialized.')

Или вместо того, чтобы назначать переменные глобальной области видимости модуля (что может быть трудно понять в Python), возможно, попробуйте вместо этого назначить ее самому исполнителю.

from dask.distributed import get_worker

def dask_setup(worker):
    worker.model = f'<mock model {uuid4()}>'

@dask.delayed
def compute_clinical(inp):       
    if get_worker().model is None:
        raise RuntimeError('Model not initialized.')
person MRocklin    schedule 16.06.2019
comment
Часть меня любит использование worker для хранения этого типа контекста, но другая часть меня обеспокоена загрязнением пространства имен объекта и возможными будущими конфликтами, например, в случае, если вам (разработчику) потребуется добавить атрибут model почему-то в класс Worker. Не могли бы вы добавить атрибут context в класс Worker в качестве определенного места для хранения пользовательских конфигураций / состояний рабочих? Или, может быть, достаточно просто указать в документации, что этот атрибут будет зарезервирован для пользователя? - person ipetrik; 30.06.2019
comment
Вы можете защитить себя сколь угодно сложным именем. Вы можете создать свой собственный атрибут _user_data или что-то подобное, если хотите. Я не думаю, что эта практика достаточно распространена, чтобы ее систематизировать. - person MRocklin; 01.07.2019