Как использовать RandomState со Sklearn RandomizedSearchCV на нескольких ядрах
Я озадачен правильным способом использования np.random.RandomState
с sklearn.model_selection.RandomizedSearchCV
при работе на нескольких ядрах.
я использую RandomState
генерировать псевдослучайные числа, чтобы мои результаты были воспроизводимыми. Я даю RandomizedSearchCV
экземпляр RandomState
и установить n_jobs=-1
так что он использует все шесть ядер.
Работа на нескольких ядрах вводит асинхронный элемент. Я ожидаю, что это приведет к тому, что запросы на псевдослучайные числа от разных ядер будут выполняться в разных порядках в разных прогонах. Поэтому разные прогоны должны давать разные результаты, а не отображать воспроизводимость.
Но на самом деле результаты воспроизводимы. Для данного значения n_iter
(т. е. количество отрисовок из пространства параметров), наилучшие найденные значения гиперпараметров идентичны от одного прогона к следующему. Я также получаю те же значения, если n_jobs
это положительное число, которое меньше, чем количество ядер.
Чтобы быть конкретным, вот код:
import numpy as np
import scipy.stats as stats
from sklearn.datasets import load_iris
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import RandomizedSearchCV, StratifiedKFold, train_test_split
# Use RandomState for reproducibility.
random_state = np.random.RandomState(42)
# Get data. Split it into training and test sets.
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.4, random_state=random_state, stratify=y)
# Prepare for hyper-parameter optimization.
n_iter = 1_000
base_clf = GradientBoostingClassifier(
random_state=random_state, max_features='sqrt')
param_space = {'learning_rate': stats.uniform(0.05, 0.2),
'n_estimators': [50, 100, 200],
'subsample': stats.uniform(0.8, 0.2)}
# Generate data folds for cross validation.
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=random_state)
# Create the search classifier.
search_clf = RandomizedSearchCV(
base_clf, param_space, n_iter=n_iter, scoring='f1_weighted', n_jobs=-1,
cv=skf, random_state=random_state, return_train_score=False)
# Optimize the hyper-parameters and print the best ones found.
search_clf.fit(X_train, y_train)
print('Best params={}'.format(search_clf.best_params_))
У меня есть несколько вопросов.
Почему я получаю воспроизводимые результаты, несмотря на асинхронный аспект?
Документация для
RandomizedSearchCV
говорит оrandom_state
параметр: "Состояние генератора псевдослучайных чисел, используемое для случайной равномерной выборки из списков возможных значений вместо распределений scipy.stats". Означает ли это, что это не влияет на распределения в пространстве параметров? Достаточно ли приведенного выше кода для обеспечения воспроизводимости или мне нужно установитьnp.random.seed()
или, может быть, напишите что-то вроде этого:distn_learning_rate = stats.uniform(0.05, 0.2) distn_learning_rate.random_state = random_state distn_subsample = stats.uniform(0.8, 0.2) distn_subsample.random_state = random_state param_space = {'learning_rate': distn_learning_rate, 'n_estimators': [50, 100, 200], 'subsample': distn_subsample}
В целом, это правильный способ настройки
RandomizedSearchCV
для воспроизводимости?Использует один экземпляр
RandomState
хорошо, или я должен использовать отдельные экземпляры дляtrain_test_split
,GradientBoostingClassifier
,StratifiedKFold
, а такжеRandomizedSearchCV
? Также документацияnp.random.seed
говорит, что семя посажено, когдаRandomState
инициализируется. Как это взаимодействует сRandomizedSearchCV
засаживать семя?когда
n_jobs
настроено использовать меньше всех ядер, я все еще вижу активность на всех ядрах, хотя уровень использования на ядро увеличивается, а истекшее время уменьшается с увеличением количества ядер. Это просто sklearn и / или macOS, оптимизирующие использование машины?
Я использую macOS 10.14.2, Python 3.6.7, Numpy 1.15.4, Scipy 1.1.0 и Sklearn 0.20.1.
2 ответа
Кандидаты в параметры генерируются перед передачей в многопоточные функции с помощью объекта ParameterSampler. Так что только один random_state
достаточно для воспроизводимости RandomizedSearchCV.
Обратите внимание, что я сказал "reproducibility of RandomizedSearchCV"
, Для оценок, используемых внутри него (base_clf
здесь), каждый оценщик должен иметь свой собственный random_state
как вы сделали.
Сейчас говорим о a single instance of RandomState
, это прекрасно для кода, который является последовательным. Единственный случай для беспокойства - это когда мультипроцессор включается. Итак, давайте проанализируем шаги, которые происходят во время выполнения вашей программы.
- Вы создали
RandomState
объект с семенем. У него сейчас состояние. - внутри
train_test_split
,StratifiedShuffleSplit
используется (потому что вы использовалиstratify
param) который будет использовать пройденныйRandomState
объект для разделения и генерации перестановок в обучающих и тестовых данных. Так что внутреннее состояниеRandomState
изменилось сейчас. Но это последовательно и не о чем беспокоиться. - Теперь вы установите это
random_state
объект вskf
, Но расщепления не происходит, покаfit()
вRandomizedSearchCV
называется. Так что состояние не изменилось. После этого, когда
search_clf.fit
называется, происходит следующее:_run_search()
выполняется, который будет использоватьrandom_state
генерировать все комбинации параметров одновременно (согласно заданномуn_iters
). Так что до сих пор никакой части многопоточности не происходит, и все хорошо.evaluate_candidates()
называется. Интересная часть это:out = parallel(delayed(_fit_and_score)(clone(base_estimator), X, y, train=train, test=test, parameters=parameters, **fit_and_score_kwargs) for parameters, (train, test) in product(candidate_params, cv.split(X, y, groups)))
Часть после
parallel(delayed(_fit_and_score)
все еще последовательный, который обрабатывается родительским потоком.cv.split()
будет использоватьrandom_state
(изменить его состояние) для создания тестовых расколовclone(estimator)
будет клонировать все параметры оценщика, (random_state
также). Так изменилось состояниеRandomState
отcv.split
объект становится базовым состоянием вestimator
- Вышеупомянутые два шага происходят многократно (количество разбиений х раз комбинаций параметров) из родительского потока (без асинхронности). И каждый раз оригинал
RandomState
клонируется для обслуживания оценщика. Таким образом, результаты воспроизводимы. - Таким образом, когда фактическая многопоточность запускается, оригинал
RandomState
не используется, но каждый оценщик (поток) будет иметь свою собственную копиюRandomState
Надеюсь, что это имеет смысл, и отвечает на ваш вопрос. Scikit-learn явно запрашивает у пользователя настройку следующим образом:
import numpy as np
np.random.seed(42)
чтобы сделать все исполнение воспроизводимым, но то, что вы делаете, также будет делать.
Я не совсем уверен в вашем последнем вопросе, так как не могу воспроизвести его в моей системе. У меня 4 ядра и когда я установил n_jobs=2
или же 3
Я вижу только те многие ядра на 100% и оставшиеся на уровне около 20-30%. Мои системные характеристики:
System:
python: 3.6.6 |Anaconda, Inc.| (default, Jun 28 2018, 17:14:51) [GCC 7.2.0]
machine: Linux-4.15.0-20-generic-x86_64-with-debian-buster-sid
Python deps:
pip: 18.1
setuptools: 40.2.0
sklearn: 0.20.1
numpy: 1.15.4
scipy: 1.1.0
Cython: 0.29
pandas: 0.23.4
Что касается того, что он не использует все ядра вашего процессора:
У меня была такая же проблема, и я мог решить ее, выполнив две вещи.
Я написал свой собственный класс дистрибутива и понял, что из-за проблемы он был безумно медленным. Ускорение помогло.
я установил
pre_dispatch
к чему-то разумному, напримерpre_dispatch=10*os.cpu_count()
. Я думаю, что проблема была в том, что он подготавливает все данные, прежде чем начать подгонять их к другим ядрам.