#python #scikit-learn #multiprocessing #joblib
Вопрос:
Допустим, у меня есть простой способ обучить модель с N ядрами:
def train(cores = 1):
reg = RandomForestRegressor(n_jobs=cores).fit(X, y)
return reg
и простой набор данных:
X = np.random.random(size=[25_000, 10])
y = np.sum(X, axis = 1)
Обучение на 2 ядрах занимает 7,6 секунды, а на 8 ядрах-3,4 секунды. Поэтому, как и ожидалось, когда я тренирую модель 4 раза с 8 ядрами, это занимает 13,6 секунды:
models = []
for i in range(4):
models.append(train(8))
Однако, когда я пытаюсь обучить их одновременно с помощью ProcessPoolExecutor, это занимает 14,2 секунды (из которых 0,2 секунды-время отправки задания).:
start = time()
models = []
with ProcessPoolExecutor(max_workers=4) as executor:
for i in range(4):
models.append(executor.submit(train, 2))
print("Submission Time:", time() - start)
print(time() - start)
Я понимаю, что есть накладные расходы, и я потенциально мог бы попробовать использовать joblib или какой-либо другой способ распараллеливания моделей. Однако я ожидаю, что если на 2 ядра потребуется 7,6 секунды, то обучение 4 на 4 рабочих (на машине с 8 ядрами) займет менее 10 секунд, а не 14 секунд.
Действительно ли замедление связано с накладными расходами на обработку? Или есть какая-то избыточная подписка, которую я не замечаю? Или, возможно, возможно, что модели пытаются использовать одни и те же ядра (я использовал htop, и, похоже, все ядра максимальны). Я не совсем уверен, что здесь происходит и как это исправить или лучше обучать модели параллельно. Цель состоит в том, чтобы тренироваться на машинах с более чем 100 ядрами и более чем 100 моделями параллельно, это не будет работать при замедлениях такого масштаба.
Примечание Я также попробовал joblib, который также занял 14 секунд:
start = time()
result = Parallel(n_jobs=4)(delayed(train)(2) for _ in range(4))
print(time() - start)
РЕДАКТИРОВАТЬ Вот еще один полный сценарий на этот раз 16 моделей либо по 1 модели на ядро, либо по 8 ядер на модель последовательно:
import numpy as np
from time import time, sleep
from sklearn.ensemble import RandomForestRegressor
from concurrent.futures import ProcessPoolExecutor
X = np.random.random(size=[25_000, 10])
y = np.sum(X, axis=1)
def train(cores=1):
reg = RandomForestRegressor(n_jobs=cores).fit(X, y)
return reg
start = time()
models = []
with ProcessPoolExecutor(max_workers=8) as executor:
for i in range(16):
models.append(executor.submit(train, 1))
print("Submission Time:", time() - start)
print(time() - start)
start = time()
models = []
for i in range(16):
models.append(train(8))
print(time() - start)
Дальнейшее РЕДАКТИРОВАНИЕ:
Я также попробовал бэкэнд loky joblib:
from joblib import parallel_backend, Parallel, delayed
start = time()
with parallel_backend("loky", n_jobs=4):
Parallel()(delayed(train)(2, X.copy(), y.copy()) for _ in range(8))
print("joblib loky 8 Models:", time() - start)
Комментарии:
1. размер=[25000, 10] ?
2. Случайный набор данных, который я сгенерировал, имеет размер 25 000 строк и 10 столбцов.
3. И как вы запускаете код?
4. В настоящее время я запускаю его на ipython, не совсем понимаю, что вы имеете в виду. Я могу запустить его где угодно.
5. В том-то и дело. Несколько процессов Python не могут работать в интерактивном режиме.
Ответ №1:
Чтобы разделить работу, необходимо потратить некоторое время на настройку отдельных процессов python. Я удивлен, что параллельное наказание не выше. Если ваша работа займет всего 14 секунд, вы не увидите преимуществ неловких параллельных вычислений.
Другой момент заключается в том, что ваши ядра, вероятно, переподписаны. Мы замечаем, что инструменты sklearn часто называют библиотеки многопроцессорной обработкой. Когда вы используете joblib, эти требования противоречат друг другу.
Возможно, sklearn parallel_backend-правильный инструмент для вас. https://scikit-learn.org/stable/modules/generated/sklearn.utils.parallel_backend.html