Параллельное обучение scikit-learn: выбрать один уровень n_jobs
Четыре модели обучаются параллельно, и каждая пытается занять все ядра. Вместо ожидаемого ускорения машина тратит время на конкуренцию и увеличивает расход памяти. Причина — несколько независимых механизмов параллелизма.
Назначить владельца вычислительного бюджета
n_jobs управляет тем параллелизмом, который реализован через joblib у конкретного estimator. Дополнительно численные библиотеки могут запускать собственные потоки. Поэтому n_jobs=-1 во всех местах не задаёт согласованный общий предел. Сначала выберите внешний уровень: независимые модели, части поиска или отдельные деревья одного леса.
В примере владельцем бюджета будет внешний Parallel с двумя процессами. Каждый RandomForest использует n_jobs=1, а parallel_config ограничивает поддерживаемые внутренние пулы потоков в рабочих процессах loky. Такой план проще объяснить: одновременно обучаются не более двух кандидатов, у каждого нет дополнительного joblib-пула деревьев.
Вернуть компактный результат из работника
Для учебного запуска функция возвращает число деревьев и несколько прогнозов. В настоящем поиске обученные модели могут занимать много памяти, поэтому стоит заранее решить, какие объекты возвращать в основной процесс, а какие сохранять локально как артефакты.
Пример проверяет выполнение двух реальных обучений с разными параметрами. Он не замеряет ускорение: число доступных ядер и объём данных у читателя другие. После выбора корректного бюджета измеряйте полное время и пиковую память своего процесса, включая создание работников и передачу входа.
from joblib import Parallel, delayed, parallel_config
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
X, y = load_iris(return_X_y=True)
def train(depth):
model = RandomForestClassifier(n_estimators=8, max_depth=depth,
n_jobs=1, random_state=19).fit(X, y)
return {"depth": depth, "trees": len(model.estimators_),
"predictions": model.predict(X[:3]).tolist()}
if __name__ == "__main__":
with parallel_config(backend="loky", n_jobs=2, inner_max_num_threads=1):
results = Parallel()(delayed(train)(depth) for depth in [2, 4])
assert [item["depth"] for item in results] == [2, 4]
assert all(item["trees"] == 8 for item in results)
assert all(len(item["predictions"]) == 3 for item in results)
print(results)Когда лучше параллелить внутри модели
Если обучается один крупный лес, внешний пул может быть не нужен: выделите бюджет n_jobs самому лесу. Если кандидатов много и они небольшие, внешний уровень часто удобнее. Сравнивайте эти планы на одинаковом задании; число работников не является универсальной мерой скорости.
Учитывайте память входа и моделей. Даже при совместном отображении больших массивов каждый работник создаёт собственные промежуточные объекты. Ограничение потоков помогает CPU, но не превращает четыре больших estimator в один по памяти. Для тяжёлых запусков уменьшите число одновременно живущих кандидатов.
Закрепить базовую операцию
В открытом уроке сначала выполните один fit и predict. Когда отдельный запуск понятен и воспроизводим, переходите к управляемому параллельному выполнению нескольких таких запусков. Откройте урок «Первый fit и predict на своих данных». Затем вернитесь к сценарию статьи и проверьте его на своём небольшом наборе. Посмотрите программу курса первой ML-модели, чтобы пройти путь от обучения к использованию результата.
Продолжить на практике
- Машинное обучение с нуля на Python: первая модель ↗
Бесплатный курс машинного обучения на Python: обучите первую модель в scikit-learn, проверьте её качество и решите 106 задач в браузере.
