Как использовать RandomState со Sklearn RandomizedSearchCV на нескольких ядрах - PullRequest
0 голосов
/ 11 декабря 2018

Я озадачен правильным способом использования 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_))

У меня есть несколько вопросов.

  1. Почему я получаю воспроизводимые результаты, несмотря на асинхронный аспект?

  2. Документация для 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}  
    
  3. В целом, это правильный способ установкиup RandomizedSearchCV для воспроизводимости?

  4. Использует ли единственный экземпляр RandomState нормально, или я должен использовать отдельные экземпляры для train_test_split, GradientBoostingClassifier, StratifiedKFold иRandomizedSearchCV?Также в документации np.random.seed говорится, что начальное число устанавливается при инициализации RandomState.Как это взаимодействует с RandomizedSearchCV установкой начального числа?

  5. Когда n_jobs настроено на использование меньше, чем все ядра, я все еще вижу активность на всех ядрах, хотя использованиеуровень на ядро ​​увеличивается, а истекшее время уменьшается с увеличением количества ядер.Это просто sklearn и / или macOS, оптимизирующие использование машины?

Я использую macOS 10.14.2, Python 3.6.7, Numpy 1.15.4, Scipy 1.1.0 и Sklearn0.20.1.

1 Ответ

0 голосов
/ 11 декабря 2018

Кандидаты в параметры генерируются перед переходом к многопоточным функциям с использованием объекта ParameterSampler .Поэтому для воспроизводимости RandomizedSearchCV достаточно только одного random_state.

Обратите внимание, что я сказал "reproducibility of RandomizedSearchCV".Для оценщиков, используемых внутри него (base_clf здесь), каждый оценщик должен иметь свои random_state, как вы сделали.

Теперь, говоря о a single instance of RandomState, он прекрасно подходит для последовательного кода.Единственный случай для беспокойства - это когда мультипроцессор включается. Итак, давайте проанализируем шаги, которые происходят во время выполнения вашей программы.

  1. Вы создали RandomState объект с начальным числом.Теперь он имеет состояние.
  2. Внутри train_test_split, используется StratifiedShuffleSplit (потому что вы использовали stratify param), который будет использовать переданный объект RandomState для разделения и генерации перестановок в поездах итестовые данные.Таким образом, внутреннее состояние RandomState теперь изменилось.Но это последовательно и не о чем беспокоиться.
  3. Теперь вы установили этот random_state объект в skf.Но расщепление не происходит до тех пор, пока не будет вызвано fit() в RandomizedSearchCV.Таким образом, состояние не изменяется.
  4. После этого, когда вызывается search_clf.fit, происходит следующее :

    1. _run_search(), который будет использовать random_state для генерации всех комбинаций параметров одновременно (согласно заданному n_iters).Так что до сих пор не происходит многопоточности, и все хорошо.
    2. 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)))
      
    3. Часть после parallel(delayed(_fit_and_score) все еще последовательная, которая обрабатывается родительским потоком.

      • cv.split() будет использовать random_state (изменить его состояние) для генерации тестовых разбиений поезда
      • clone(estimator) будет клонировать все параметры оценщика (random_state также).Таким образом, измененное состояние объекта RandomState из cv.split становится базовым состоянием в estimator
      • Два вышеупомянутых шага происходят несколько раз (количество разбиений x раз комбинаций параметров) из родительского потока (без асинхронности),И каждый раз оригинал RandomState клонируется, чтобы служить оценщику.Таким образом, результаты воспроизводимы.
      • Таким образом, когда запускается фактическая многопоточная часть, оригинал RandomState не используется, но каждый оценщик (поток) будет иметь свою собственную копию RandomState

Надеюсь, что это имеет смысл, и отвечает на ваш вопрос.Scikit-learn явно просит пользователя настроить так:

import numpy as np
np.random.seed(42)

, чтобы сделать все исполнение воспроизводимым, но то, что вы делаете, также будет делать.

IЯ не совсем уверен в вашем последнем вопросе, так как я не могу воспроизвести его в моей системе.У меня есть 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 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...