stackoverflow.com/q/47561759/3666197

редложено вэтот ответЯ пытался использовать joblib для параллельного обучения нескольких моделей scikit-learn.

import joblib
import numpy
from sklearn import tree, linear_model

classifierParams = {
                "Decision Tree": (tree.DecisionTreeClassifier, {}),''
                "Logistic Regression" : (linear_model.LogisticRegression, {})
}


XTrain = numpy.array([[1,2,3],[4,5,6]])
yTrain = numpy.array([0, 1])

def trainModel(name, clazz, params, XTrain, yTrain):
    print("training ", name)
    model = clazz(**params)
    model.fit(XTrain, yTrain)
    return model


joblib.Parallel(n_jobs=4)(joblib.delayed(trainModel)(name, clazz, params, XTrain, yTrain) for (name, (clazz, params)) in classifierParams.items())

Однако вызов последней строки занимает много времени без использования процессора, фактически он просто блокируется и никогда ничего не возвращает. В чем моя ошибка?

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

 collector29 нояб. 2017 г., 15:21
@ user3666197 Вот почему это комментарий, а не ответ. Я также прямо заявляю об этой разнице в своем комментарии.
 sascha29 нояб. 2017 г., 14:10
Ну, кажется, мои комментарии являются образовательными (с учетом ваших правок). Твой фрагмент работает на моем конце. Таким образом, вы, вероятно, теперь знаете, что вам нужно искать (после потенциальных обновлений для всех используемых библиотек): взаимодействие joblib-OS (проблемы с github!). По моему эмпирическому и субъективному опыту: OS X чаще всего подвержена этому.
 sascha29 нояб. 2017 г., 13:41
Понимаю. Сейчас меня критикуют за неправильные предположения, которые я сделал, прежде чем редактировать ваш вопрос, чтобы дать больше информации. Использование небольших данных на самом деле не объясняет вашу блокировку, но небольшие данные также никогда не принесут вам никакой пользы при такой грубой параллелизации. Самая большая проблема в этом вопросе - отсутствие деталей. Он не воспроизводится, не показывает, что распараллелено, и информация об ОС также не предоставляется. Видя эти проблемы, я бы снова догадался: OS X.
 collector29 нояб. 2017 г., 13:13
Некоторые из моделей sklearn позволяют вам пройтиn_jobs аргумент напрямую. Например.:sklearn.ensemble.RandomForestClassifier(n_estimators=10, criterion=’gini’, max_depth=None, n_jobs=-1) Это не будет обучать несколько моделей одновременно, но это будет обучать одну модель на нескольких ядрах, потенциально ускоряя вашу реализацию.
 sascha29 нояб. 2017 г., 13:20
Это базовый CS, верно? До завершения копирования работать нечего. Если частое копирование необходимо; Процессор голодает (IOпомедленнее чем процессор). Если эти копии раздувают вашу оперативную память, происходит перебивание, что приводит к тому же эффекту. Что неясно? (конечно, это просто догадки, но с таким редким и неподготовленным вопросом)

Ответы на вопрос(1)

В конвейерах машинного обучения промышленного уровня загрузка ЦП примерно такая же, почти 24/7/365:

Проверьте обаCPU% а также цифры состояния других ресурсов через этот узел.

В чем моя ошибка?

Прочитав ваш профиль был потрясающий момент, сэр:

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

ПроблемаЯВЛЯЕТСЯ глубоко определяетсяуважение элементарным информатика + правила алгоритма.

ПроблемаНЕ ЯВЛЯЕТСЯ требуясильный научный опыт, ноздравый смысл.

ПроблемаНЕ ЯВЛЯЕТСЯ Любыеособенно большие данные но требует пахнутькак все на самом деле работает.

факты
или же
Эмоции? ... это вопрос!(Трагедия Гамлета, принца Дании)Могу ли я быть честным? Давайте предпочитаем ФАКТЫ, всегда:

Шаг 1:
Никогда не нанимайте и не увольняйте каждого консультанта, которыйне уважает факты (ответ, упомянутый выше, ничего не предлагал, тем более не давал никаких обещаний). Игнорирование фактов может бытьуспешный грех"в PR / MARCOM / Реклама / СМИ(если Клиент терпит такую ​​нечестность и / или манипулятивную привычку), но не в научно обоснованных количественных областях.Это непростительно.

Шаг 2:
Никогда не нанимайте и не увольняйте всех консультантов, которые утверждают, что имеют опыт работы в архитектуре программного обеспечения,особенно о решениях для ... больших данных но не обращает внимания на накопленную сумму всех дополнительных накладных расходов, которые будут вводиться каждым из соответствующих элементов архитектуры системы, как только обработка начнет распределяться по некоторому пулу аппаратных и программных ресурсов. Это непростительно.

Шаг 3:
Никогда не нанимайте и не увольняйте ни одного консультанта, который становится пассивно агрессивным, когда факты не соответствуют ее / его желаниям, и начинает обвинять других знающих людей, которые уже оказали помощь, скорее«улучшить (их)навыки коммуникации" вместо того, чтобы учиться на ошибках. Конечно, умение может помочь выразить очевидные ошибки каким-то другим способом, но гигантские ошибки останутся гигантскими ошибками, и каждый ученый, справедливый по отношению к своему научному названию, долженНИКОГДА прибегайте к нападению на помогающего коллегу, а лучше начинайте искать причину ошибок, одну за другой. Этот ---

@sascha ...Могу ли я предложить вам немного отдохнуть от переполнения стека, чтобы охладиться, немного поработать над своими навыками межличностного общения

--- был просто прямой и интеллектуально неприемлемый противный фол@sascha.

Далее игрушки
Факты архитектуры, ресурсов и планирования процессов, которые имеют значение:

Императивная формаСинтаксический конструктор запускает огромное количество действий для запуска:

joblib.Parallel( n_jobs = <N> )( joblib.delayed( <aFunction> )
                                               ( <anOrderedSetOfFunParameters> )
                                           for ( <anOrderedSetOfIteratorParams> )
                                           in    <anIterator>
                                 )

Чтобы хотя бы угадать, что происходит, с научной точки зрения справедливым подходом было бы протестировать несколько репрезентативных случаев, сравнить их фактическое выполнение, собрать количественно подтвержденные факты и выдвинуть гипотезу о модели поведения и ее основных зависимостях от CPU_core-count, от объема RAM на<aFunction>-сложность и распределение ресурсов и т. д.

Контрольный пример A:
def a_NOP_FUN( aNeverConsumedPAR ):
    """                                                 __doc__
    The intent of this FUN() is indeed to do nothing at all,
                             so as to be able to benchmark
                             all the process-instantiation
                             add-on overhead costs.
    """
    pass

##############################################################
###  A NAIVE TEST BENCH
##############################################################
from zmq import Stopwatch; aClk = Stopwatch()
JOBS_TO_SPAWN =  4         # TUNE:  1,  2,  4,   5,  10, ..
RUNS_TO_RUN   = 10         # TUNE: 10, 20, 50, 100, 200, 500, 1000, ..
try:
     aClk.start()
     joblib.Parallel(  n_jobs = JOBS_TO_SPAWN
                      )( joblib.delayed( a_NOP_FUN )
                                       ( aSoFunPAR )
                                   for ( aSoFunPAR )
                                   in  range( RUNS_TO_RUN )
                         )
except:
     pass
finally:
     try:
         _ = aClk.stop()
     except:
         _ = -1
         pass
print( "CLK:: {0:_>24d} [us] @{1: >3d} run{2: >5d} RUNS".format( _,
                                                                 JOBS_TO_SPAWN,
                                                                 RUNS_TO_RUN
                                                                 )
        )

Собрав достаточно репрезентативные данные по этому NOP-случаю в достаточно масштабном 2D-ландшафте[ RUNS_TO_RUN, JOBS_TO_SPAWN]-cartesian-space DataPoints, чтобы генерировать, по крайней мере, некоторый непосредственный опыт фактических системных затрат при запуске служебных нагрузок фактически пустых процессов, связанных с обязательным инструктажемjoblib.Parallel(...)( joblib.delayed(...) )-синтаксический конструктор, порождающий в системный планировщик всего несколькоjoblib-удалосьa_NOP_FUN() экземпляров.

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

Таким образом, из этого простейшего случая следует научно обоснованная, тщательная работа, уже показывающая сравнительные затраты на все связанные с этим накладные расходы на установку.самый маленький когда-либоjoblib.Parallel() штраф синус-нон-нон вперед в направлении, где живут алгоритмы реального мира -Лучший с участиемзатем добавляем все большую и большую «полезную нагрузку»-размеры в цикл тестирования:

Тест-кейс B:
def a_NOP_FUN_WITH_JUST_A_MEM_ALLOCATOR( aNeverConsumedPAR ):
    """                                                 __doc__
    The intent of this FUN() is to do nothing but
                             a MEM-allocation
                             so as to be able to benchmark
                             all the process-instantiation
                             add-on overhead costs.
    """
    import numpy as np              # yes, deferred import, libs do defer imports
    SIZE1D    = 1000                # here, feel free to be as keen as needed
    aMemALLOC = np.zeros( ( SIZE1D, #       so as to set
                            SIZE1D, #       realistic ceilings
                            SIZE1D, #       as how big the "Big Data"
                            SIZE1D  #       may indeed grow into
                            ),
                          dtype = np.float64,
                          order = 'F'
                          )         # .ALLOC + .SET
    aMemALLOC[2,3,4,5] = 8.7654321  # .SET
    aMemALLOC[3,3,4,5] = 1.2345678  # .SET

    return aMemALLOC[2:3,3,4,5]

Очередной раз,
собрать достаточно представительные количественные данные о затратах фактических MEM-распределений удаленного процесса, запустивa_NOP_FUN_WITH_JUST_A_MEM_ALLOCATOR() в течение некоторого разумного широкого пейзажаSIZE1D-scaling,
очередной раз
над достаточно масштабным 2D-ландшафтом[ RUNS_TO_RUN, JOBS_TO_SPAWN]-Cartesian-Space DataPoints, чтобы коснуться нового измерения в масштабировании производительности, в рамках расширенного черного ящика PROCESS_under_TEST внутри экспериментаjoblib.Parallel() инструмент, оставив свою магию еще не открытой.

Тест-кейс C:
def a_NOP_FUN_WITH_SOME_MEM_DATAFLOW( aNeverConsumedPAR ):
    """                                                 __doc__
    The intent of this FUN() is to do nothing but
                             a MEM-allocation plus some Data MOVs
                             so as to be able to benchmark
                             all the process-instantiation + MEM OPs
                             add-on overhead costs.
    """
    import numpy as np              # yes, deferred import, libs do defer imports
    SIZE1D    = 1000                # here, feel free to be as keen as needed
    aMemALLOC = np.ones(  ( SIZE1D, #       so as to set
                            SIZE1D, #       realistic ceilings
                            SIZE1D, #       as how big the "Big Data"
                            SIZE1D  #       may indeed grow into
                            ),
                          dtype = np.float64,
                          order = 'F'
                          )         # .ALLOC + .SET
    aMemALLOC[2,3,4,5] = 8.7654321  # .SET
    aMemALLOC[3,3,4,5] = 1.2345678  # .SET

    aMemALLOC[:,:,:,:]*= 0.1234567
    aMemALLOC[:,3,4,:]+= aMemALLOC[4,5,:,:]
    aMemALLOC[2,:,4,:]+= aMemALLOC[:,5,6,:]
    aMemALLOC[3,3,:,:]+= aMemALLOC[:,:,6,7]
    aMemALLOC[:,3,:,5]+= aMemALLOC[4,:,:,7]

    return aMemALLOC[2:3,3,4,5]
Взрыв, Проблемы, связанные с архитектурой, начинают медленно появляться:

Вскоре можно заметить, что важны не только статические размеры, но и MEM-транспорт.ПРОПУСКНАЯ СПОСОБНОСТЬ (аппаратно-аппаратный) начнет вызывать проблемы, так как перемещение данных из / в ЦПУ в / из MEM стоит дорого~ 100 .. 300 [ns]это намного больше, чем любое умное перемешивание нескольких байтов «внутри» CPU_core, {CPU_core_private | CPU_core_shared | CPU_die_shared} - архитектура иерархии кэша в одиночку (и любая нелокальная передача NUMA демонстрирует такую ​​же боль дополнительного порядка).

Все вышеперечисленные тест-кейсы пока не потребовали от CPU особых усилий

Итак, начнем сжигать масло!

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

Тест-кейс D:
def a_CPU_1_CORE_BURNER_FUN( aNeverConsumedPAR ):
    """                                                 __doc__
    The intent of this FUN() is to do nothing but
                             add some CPU-load
                             to a MEM-allocation plus some Data MOVs
                             so as to be able to benchmark
                             all the process-instantiation + MEM OPs
                             add-on overhead costs.
    """
    import numpy as np              # yes, deferred import, libs do defer imports
    SIZE1D    = 1000                # here, feel free to be as keen as needed
    aMemALLOC = np.ones(  ( SIZE1D, #       so as to set
                            SIZE1D, #       realistic ceilings
                            SIZE1D, #       as how big the "Big Data"
                            SIZE1D  #       may indeed grow into
                            ),
                          dtype = np.float64,
                          order = 'F'
                          )         # .ALLOC + .SET
    aMemALLOC[2,3,4,5] = 8.7654321  # .SET
    aMemALLOC[3,3,4,5] = 1.2345678  # .SET

    aMemALLOC[:,:,:,:]*= 0.1234567
    aMemALLOC[:,3,4,:]+= aMemALLOC[4,5,:,:]
    aMemALLOC[2,:,4,:]+= aMemALLOC[:,5,6,:]
    aMemALLOC[3,3,:,:]+= aMemALLOC[:,:,6,7]
    aMemALLOC[:,3,:,5]+= aMemALLOC[4,:,:,7]

    aMemALLOC[:,:,:,:]+= int( [ np.math.factorial( x + aMemALLOC[-1,-1,-1] )
                                               for x in range( 1005 )
                                ][-1]
                            / [ np.math.factorial( y + aMemALLOC[ 1, 1, 1] )
                                               for y in range( 1000 )
                                ][-1]
                              )

    return aMemALLOC[2:3,3,4,5]

По-прежнему нет ничего необычного по сравнению с обычным классом полезных нагрузок в области многомерного пространства машинного обучения, где все измерения{ aMlModelSPACE, aSetOfHyperParameterSPACE, aDataSET }-state-space влияет на объем требуемой обработки (некоторые имеютO( N ), что-то другоеO( N.logN ) сложность), гдепочти сразу, где хорошо спроектировано более чем один CPU_core скоро будет задействован даже при выполнении одного «задания».

По-настоящему неприятный запах начинается, когда наивные (несогласованные по чтению ресурсы используются) смеси нагрузок на ЦП выходят на дорогу, а когда смеси нагрузок на ЦП начинают смешиваться с наивными (считываются нескоординированными при использовании ресурсов) ) Процессы O / S-планировщика оказываются в борьбе за общие (прибегающие к простой политике совместного использования) ресурсы - то есть MEM (представляя SWAPs как АД), CPU (вводящие ошибки кэша и повторные выборки MEM (да, с помощью SWAP) добавлены штрафы), не говоря уже о выплате более~ 15+ [ms] плата за задержку, если кто-то забывает и позволяет процессу коснутьсяfileIO- (5 (!) - на порядок медленнее + поделился + быть чистым[SERIAL]По натуре) -устройство. Здесь никакие молитвы не помогают (в том числе SSD, всего на несколько порядков меньше, но все равно это ад, который невероятно быстро делит и запускает устройство в его износ + могилу слез).

Что происходит, если все порожденные процессы не помещаются в физическую память?

Пейджинг виртуальной памяти и перестановки начинают буквально ухудшать остальную часть пока что «просто» по совпадению (читай: слабо скоординировано) -[CONCURRENTLY]-планированная обработка (читай: дальнейшее снижение индивидуальной производительности PROCESS-under-TEST).

Вещи могут так скоро разрушиться, если не будут находиться под должным контролем и контролем.

Опять же - факт имеет значение: легкий класс мониторинга ресурсов может помочь:

aResRECORDER.show_usage_since0() method returns:

ResCONSUMED[T0+   166036.311 (           0.000000)]
user=               2475.15
nice=                  0.36
iowait=                0.29
irq=                   0.00
softirq=               8.32
stolen_from_VM=       26.95
guest_VM_served=       0.00

Аналогично, более богато сконструированный монитор ресурсов может сообщать о более широком контексте O / S, чтобы увидеть, где дополнительные условия кражи ресурсов / конкуренции / гонок ухудшают фактически достигнутый поток процесса:

>>> psutil.Process( os.getpid()
                    ).memory_full_info()
                                      ( rss          =       9428992,
                                        vms          =     158584832,
                                        shared       =       3297280,
                                        text         =       2322432,
                                        lib          =             0,
                                        data         =       5877760,
                                        dirty        =             0
                                        )
           .virtual_memory()
                          (             total        =   25111490560,
                                        available    =   24661327872,
                                        percent      =             1.8,
                                        used         =    1569603584,
                                        free         =   23541886976,
                                        active       =     579739648,
                                        inactive     =     588615680,
                                        buffers      =             0,
                             ,           cached       =    1119440896
                                        )
           .swap_memory()
                       (                total        =    8455712768,
                                        used         =     967577600,
                                        free         =    7488135168,
                                        percent      =            11.4,
                                        sin          =  500625227776,
                                        sout         =  370585448448
                                        )

Wed Oct 19 03:26:06 2017
        166.445 ___VMS______________Virtual Memory Size  MB
         10.406 ___RES____Resident Set Size non-swapped  MB
          2.215 ___TRS________Code in Text Resident Set  MB
         14.738 ___DRS________________Data Resident Set  MB
          3.305 ___SHR_______________Potentially Shared  MB
          0.000 ___LIB_______________Shared Memory Size  MB
                __________________Number of dirty pages           0x
И последнее, но не менее важное: почему можно легко заплатить больше, чем заработать взамен?

Помимо постепенно накапливающихся свидетельств того, как реальные накладные расходы на развертывание системы накапливают затраты,недавно переформулированный закон Амдала, расширенный таким образом, чтобы покрыть как дополнительные накладные расходы, так и «атомарность процесса» при определении размеров дальнейших неделимых частей, определяет максимальный порог дополнительных затрат, который может быть разумно оплачен, если какая-либо распределенная обработка должна обеспечить какой-либо из указанных выше>= 1.00 ускорение вычислительного процесса.

Несоблюдение явной логики переформулированного Закона Амдала приводит к тому, что процесс идет хуже, чем если бы он был обработан в чистом виде.[SERIAL] планирование процесса (и иногда результаты плохой практики проектирования и / или эксплуатации могут выглядеть так, как если бы это был случай, когдаjoblib.Parallel()( joblib.delayed(...) ) метод«блокирует процесс» ).

 user366619730 нояб. 2017 г., 12:00
@sascha Как вы уже заметилиjoblib Возникли проблемы с OSX v?.?, может быть интересен опыт работы команды CRAY-Chapel с бэкэнд-компилятором по умолчанию до OSX 10.12+ (повторная компиляция из источника на 10.12+ и clang 9, как сообщалось, прекратили выставку действительно странные проблемы с производительностью, так что если это может также помочьjoblib сообщество, тем лучше).Подробности в >>> stackoverflow.com/q/47561759/3666197

Ваш ответ на вопрос