Введение

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

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

Лучший гиперпараметр субъективен и отличается для каждого датасета. Библиотека Scikit-Learn в Python имеет набор гиперпараметров по умолчанию, которые достаточно хорошо работают для всех моделей, но они не обязательно являются наилучшими для каждой из проблем.

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

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

Существуют две популярные техники, используемые для оптимизации гиперпараметров, — сеточный и случайный поиск.

Поиск по сетке

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

Затем используется метод поиска по сетке для размещения этих гиперпараметров в матрице, и модель обучается на каждой комбинации значений гиперпараметров.

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

Случайный поиск

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

Эта техника вместо исчерпывающего поиска делает случайную выборку из сетки гиперпараметров.

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

Теперь, когда у вас есть базовое понимание того, как работают случайный поиск и поиск по сетке, я покажу, как реализовать эти методы с помощью библиотеки Scikit-Learn.

Оптимизация классификатора Random Forest (случайный лес) с помощью поиска по сетке и случайного поиска

Шаг 1: Загрузка набора данных

Загрузите датасет Wine Quality на сайте Kaggle и введите следующие строки кода, чтобы считать его с помощью библиотеки Pandas:

import pandas as pd

df = pd.read_csv('winequality-red.csv')
df.head()

Верхняя часть датафрейма выглядит следующим образом:

Шаг 2: Предварительная обработка данных

Целевая переменная "quality" (качество) содержит значения в диапазоне от 1 до 10.

Мы превратим ее в задачу бинарной классификации, присвоив всем пунктам данных со значением качества меньше или равным 5 значение 0, а остальным результатам — значение 1:

import numpy as np

df['target'] = np.where(df['quality']>5, 1, 0)

Давайте разделим зависимые и независимые переменные в этом датафрейме:

df2 = df.drop(['quality'],axis=1)
X = df2.drop(['target'],axis=1)
y = df2[['target']]

Шаг 3: Построение модели

Теперь давайте создадим классификатор случайного леса. Мы будем выполнять настройку гиперпараметров этой модели, для создания наилучшего алгоритма под наш датасет:

from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier()

Шаг 4: Реализация поиска по сетке с помощью Scikit-Learn

Определение пространственного диапазона гиперпараметров

Сейчас мы попробуем настроить следующий набор гиперпараметров этой модели:

  1. "Max_depth": Этот гиперпараметр представляет максимальный уровень (глубину) каждого дерева в модели случайного леса. Более глубокое дерево показывает хорошие результаты и собирает много информации об обучающих данных, но плохо поддается обобщению для тестовых данных. По умолчанию в библиотеке Scikit-Learn это значение установлено в "None", что значит, такие деревья оставлены для полного расширения.

  2. "Max_features": Максимальное количество признаков, которое модели случайного леса разрешается опробовать при каждом разбиении. По умолчанию в Scikit-Learn это значение устанавливается равным квадратному корню из общего числа переменных в наборе данных.

  3. "N_estimators": Количество деревьев решений в лесу. По умолчанию в Scikit-Learn число оценщиков (estimators) равно 10.

  4. "Min_samples_leaf": Минимальное количество образцов, необходимое для нахождения в листовом узле каждого дерева. В Scikit-Learn значение по умолчанию равно 1.

  5. "Min_samples_split": Минимальное количество образцов, необходимое для разбиения внутреннего узла каждого дерева. В Scikit-Learn значение по умолчанию равно 2.

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

grid_space={'max_depth':[3,5,10,None],
              'n_estimators':[10,100,200],
              'max_features':[1,3,5,7],
              'min_samples_leaf':[1,2,3],
              'min_samples_split':[1,2,3]
           }

Запуск поиска по сетке

Теперь нам нужно выполнить поиск, чтобы найти наилучшую комбинацию гиперпараметров для модели:

from sklearn.model_selection import GridSearchCV

grid = GridSearchCV(rf,param_grid=grid_space,cv=3,scoring='accuracy')
model_grid = grid.fit(X,y)

Оценка результатов моделирования

Наконец, давайте выведем самую высокую степень точности модели, а также набор гиперпараметров, которые обеспечили такой результат:

print('Best hyperparameters are: '+str(model_grid.best_params_))
print('Best score is: '+str(model_grid.best_score_))

Лучшая модель дала оценку точности приблизительно 0,74, а ее гиперпараметры следующие:

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

Шаг 5: Реализация случайного поиска с помощью Scikit-Learn

Определение области значений гиперпараметров

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

Он случайным образом выбирает гиперпараметры, чтобы найти лучшие из них, и это означает, что в отличие от сеточного поиска, случайный поиск может быстро перебрать большое количество значений.

from scipy.stats import randint

rs_space={'max_depth':list(np.arange(10, 100, step=10)) + [None],
              'n_estimators':np.arange(10, 500, step=50),
              'max_features':randint(1,7),
              'criterion':['gini','entropy'],
              'min_samples_leaf':randint(1,4),
              'min_samples_split':np.arange(2, 10, step=2)
         }

Запуск случайного поиска

Выполните следующие строки кода для запуска случайного поиска модели: (Обратите внимание, что мы указали n_iter=500, значит, случайный поиск будет выполняться 500 раз, прежде чем будет выбрана лучшая модель. Вы можете поэкспериментировать с другим числом итераций, чтобы увидеть, сколько из них дает оптимальные результаты. Имейте в виду, что большое количество итераций приведет к улучшению качества производительности, но при этом будет достаточно трудоемким).

from sklearn.model_selection import RandomizedSearchCV

rf = RandomForestClassifier()
rf_random = RandomizedSearchCV(rf, space, n_iter=500, scoring='accuracy', n_jobs=-1, cv=3)
model_random = rf_random.fit(X,y)

Оценка результатов моделирования

Теперь выполните следующие строки кода, чтобы вывести лучшие гиперпараметры, найденные методом случайного поиска, а также самую высокую точность самой удачной модели:

print('Best hyperparameters are: '+str(model_random.best_params_))
print('Best score is: '+str(model_random.best_score_))

Лучшие гиперпараметры, найденные методом случайного поиска, следующие:

Самая высокая точность из всех построенных моделей также составляет примерно 0,74.

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

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

Полный код

Вот весь код, использованный в этом руководстве:

# imports

import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from scipy.stats import randint
from sklearn.model_selection import RandomizedSearchCV

# reading the dataset 

df = pd.read_csv('winequality-red.csv')

# preprocessing 
df['target'] = np.where(df['quality']>5, 1, 0)
df2 = df.drop(['quality'],axis=1)
X = df2.drop(['target'],axis=1)
y = df2[['target']]

# initializing random forest
rf = RandomForestClassifier()

# grid search cv
grid_space={'max_depth':[3,5,10,None],
              'n_estimators':[10,100,200],
              'max_features':[1,3,5,7],
              'min_samples_leaf':[1,2,3],
              'min_samples_split':[1,2,3]
           }

grid = GridSearchCV(rf,param_grid=grid_space,cv=3,scoring='accuracy')
model_grid = grid.fit(X,y)

# grid search results
print('Best grid search hyperparameters are: '+str(model_grid.best_params_))
print('Best grid search score is: '+str(model_grid.best_score_))

# random search cv
rs_space={'max_depth':list(np.arange(10, 100, step=10)) + [None],
              'n_estimators':np.arange(10, 500, step=50),
              'max_features':randint(1,7),
              'criterion':['gini','entropy'],
              'min_samples_leaf':randint(1,4),
              'min_samples_split':np.arange(2, 10, step=2)
          }

rf = RandomForestClassifier()

rf_random = RandomizedSearchCV(rf, rs_space, n_iter=500, scoring='accuracy', n_jobs=-1, cv=3)
model_random = rf_random.fit(X,y)

# random random search results
print('Best random search hyperparameters are: '+str(model_random.best_params_))
print('Best random search score is: '+str(model_random.best_score_))

Поиск по сетке и случайный поиск — какой из них использовать?

Если вы когда-нибудь окажетесь перед выбором между поиском по сетке и случайным поиском, вот несколько советов, которые помогут вам решить, какой из них использовать:

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

  2. Применяйте случайный поиск в широком диапазоне значений, если у вас еще нет представления о параметрах, которые будут хорошо работать в вашей модели. Случайный поиск быстрее, чем поиск по сетке, и его всегда следует использовать, когда у вас большая область значений параметров.

  3. Для получения наилучших результатов целесообразно использовать как случайный поиск, так и поиск по сетке.

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


Материал подготовлен в рамках специализации Machine Learning. Всех желающих приглашаем на открытое занятие, на котором поговорим о том, чем занимаются специалисты по машинному обучению и где используются данные методы. Обсудим, чем методы машинного обучения отличаются от классического программирования, какие задачи решают в ML. В результате вы познакомитесь с основными понятиями и определениями, изучите pipeline построения моделей и типологию задач ML. Регистрация открыта по ссылке.

Комментарии (0)