Machine Learning: Откриване на оптималните параметри чрез библиотеката Hyperopt

Библиотеката Hyperopt е създадена специално за оптимизация на модели за машинно обучение и позволява бързо и лесно да открием кои са най-добрите параметри и техните стойности вместо да се опитваме да ги търсим чрез ръчни настройки.

В тази статия ще използваме Hyperopt, за да открием оптималните параметри на модел за машинно обучение в контекста на практическа задача и ще видим как се изменят резултатите след оптимизация.

Преглед на възможностите на библиотеката Hyperopt

Hyperopt използва Бейсовата оптимизация за откриване на оптималните параметри, т.е. подходът е вероятностен, като се преминава през пространство от параметри и техните стойности, за да се намери минимума на конкретна функция. За разлика от други методи като рандомизираното и решетъчното търсене на Scikit-learn, при Hyperopt се взимат под внимание предишни итерации, когато се търсят оптималнитe параметри.

Необходимо е да преминем през няколко стъпки, когато оптимизираме модел чрез библиотеката Hyperopt:

1/ Дефиниране на целевата функция

Целевата функция е тази, на която търсим минимума. Това често е функция на загубите (loss function). Например, ако искаме да намерим тези стойности на параметрите, при които средната квадратична грешка е най-ниска.

2/ Дефиниране на пространство от параметри и техните възможни стойности

Hyperopt предоставя много възможности за настройки на параметрите благодарение на функциите от модула hp. Поддържат се числови и нечислови стойности:

  • Нечислови параметри – hp.choice()
  • Цели числа – hp.randint(), hp.quniform(), hp.qloguniform() и hp.qlognormal()
  • Дробни числа – hp.normal(), hp.uniform(), hp.lognormal() и hp.loguniform()

За цели и дробни числа може да се използва и hp.choice(), когато искаме да изберем една конкретна стойност от определени такива в списък.

Употребата на тези функции следва малко по-надолу в практическия пример.

3/ Инициализиране на Trials обекта

В Trials обекта се съхраняват всички резултати от извършеното търсене на оптимални параметри. Получаваме ги под формата на списък от речници.

  • trials.trials – всички резултати от всяка една итерация
  • trials.results – успешно изчислените стойности, получени от целевата функция и статуса на конкретната итерация (дали е успешна или не)
  • trials.losses() – стойностите, получени от целевата функция
  • trials.statuses() – статусите на всяка итерация

4/ Избор на оптимизационен алгоритъм, който да търси в пространството от параметри

Hyperopt предлага няколко варианта за оптимизационен алгоритъм, като основен е Tree Parzen Estimator (TPE). При него се изгражда вероятностен модел, който е базиран на теоремата на Бейс.

Формулата е следната:

p(y\mid x) = \frac{p(x\mid y) * p(y)}{p(x)}
  • p(y|x) е вероятността на резултата на целевата функция по отношение на параметрите
  • p(x|y) е вероятността на параметрите по отношение на резултата на целевата функция
  • p(x) e вероятността на параметрите
  • p(y) e вероятността на резултата на целевата функция

Стойностите на параметрите се разделят на 2 части на базата на определен праг, с който се сравнява оценката на целевата функция. Едната част е l(x), където резултатите са хубави и стойността на прага е по-висока от оценката на целевата функция. Другата е g(x), където резултатите са лоши, а прагът е по-нисък от оценката.

Правят се последващи математически изчисления, като идеята е отношението между l(x) и g(x) да се максимизира. На базата на това се избират конкретни параметри и стойностите им, които да се използват, като се взимат под внимание и предишни итерации. Това допринася за по-добри резултати с течение на итерациите.

5/ Прилагане на функцията fmin()

В основата на библиотеката Hyperopt стои функцията fmin(), чрез която се прилага Бейсовата оптимизация. Нейните основни параметри са следните:

  • fn – целевата функция
  • space – пространството от параметри
  • trials – Trials обекта
  • algo – оптимизационният алгоритъм
  • max_evals – максимален брой итерации

Практически пример

За да видим на практика възможностите на Hyperopt, ще направим един пример, в който задачата е да определим дали даден клиент на банка ще се възползва от оферта за срочен депозит или не, т.е. имаме задача за бинарна класификация и двете възможни стойности за целевата променлива са да или не. Данните, които ще използваме са предварително изчистени – премахнати са ненужни колони и екстремни стойности, направен е визуален анализ и т.н.

Ако искате да научите повече за стъпките от етапа на предварителна обработка на данните, можете да прочетете в следните статии:

Примерът, който ще разгледаме по-надолу, можете да изтеглите от тук.

Оценка на модела без оптимизация

Ще използваме алгоритъма RandomForest при изграждане на модела за машинно обучение, като неговите параметри и техните подразбиращи се стойности можете да видите в таблицата по-долу:

ParameterDefault value
0criteriongini
1max_depthNone
2max_featuresauto
3min_samples_leaf1
4min samples_split2
5n_estimators100
# Създаване на модела с параметри по подразбиране
rf = RandomForestClassifier()

# Обучение на модела
rf.fit(X_train_res, y_train_res)

# Тест на модела
rf_pred = rf.predict(X_test)

Резултати:

MetricsValues
0Overall Accuracy0.714925
1Overall Error0.285075
2Precision0.665428
3Specificity0.769231
4Sensitivity0.639286
5F1_score0.652095

В 71% от всички случаи класификаторът е разпределил правилно обектите в съответните класове. Определил е, че в 67% от случаите хората ще приемат офертата за депозит и те в действителност биха приели. Открил е 77% от хората, които няма да се възползват от офертата и 64% от тези, които ще приемат.

Ако искате да научите по-подробно за тези метрики, можете да прочетете в статията Machine Learning: Метрики за оценка на класификационни модели.

# Откриване на разликата между определените от класификатора класове и тези в действителност
error = df_rf['Actual'].mean() - df_rf['Predicted'].mean()

41.79% в тестовата извадка са от положителния клас, а класификаторът е определил 40.14% правилно, т.е. грешката е 1.65%.

Оценка на модела след оптимизация

Първата стъпка, която трябва да предприемем при оптимизация на модела с библиотеката Hyperopt, е да създадем пространство от параметри и техните стойности.

# Създаване на речник с параметри и техни възможни стойности
params = {'criterion': hp.choice('criterion', ['entropy', 'gini']),
        'max_depth': hp.quniform('max_depth', 1, 11, 1),
        'max_features': hp.choice('max_features', ['sqrt','log2', None]),
        'min_samples_leaf': hp.choice('min_samples_leaf', [2, 5, 10, 14]),
        'min_samples_split' : hp.choice ('min_samples_split', [2, 4, 6, 8]),
        'n_estimators' : hp.choice('n_estimators', [200, 500])
    }

За всеки от параметрите са зададени конкретни възможни стойности, които след това можем да използваме, когато създаваме модел при дефиниране на целевата функция.

# Създаване на целева функция
def objective(params):
    # Създаване на модел
    rf_new = RandomForestClassifier(criterion = params['criterion'], 
                                 max_depth = params['max_depth'],
                                 max_features = params['max_features'],
                                 min_samples_leaf = params['min_samples_leaf'],
                                 min_samples_split = params['min_samples_split'],
                                 n_estimators = params['n_estimators'], 
                                 )

    # Изчисляване на обща точност
    accuracy = cross_val_score(rf_new, X_train_res, y_train_res, cv = 5).mean()

    # Полученият резултат трябва да е с отрицателен знак, защото се стремим да намерим максимум обща точност
    return {'loss': -accuracy, 'status': STATUS_OK }

Като метрика за оценка при целевата функция задаваме общата точност (accuracy). Тъй като целта е да се намерят тези стойности на параметрите, при които общата точност е най-висока, а при Hyperopt алгоритъмът се стреми да открие минимума на функцията, е необходимо да сменим знака на получения резултат с отрицателен.

Следващата стъпка е да използваме функцията fmin(), чрез която да открием оптималните параметри. На нея като параметри подаваме целевата функция objective, пространството от параметри params и trials обекта. Алгоритъмът за търсене задаваме да е tpe.suggest и броят итерации да бъде 80, т.е. ще се пробват 80 комбинации на параметрите и техните стойности и накрая ще получим като резултат най-добрата.

# Създаване на Trials обект
trials = Trials()

# Използване на функцията fmin()
best = fmin(fn= objective,
            space= params,
            algo= tpe.suggest,
            max_evals = 80,
            trials= trials)

Резултат:

ParametersValues
0criterion1
1max_depth10
2max_features1
3min_samples_leaf0
4min_samples_split0
5n_estimators0

При тези параметри, които са зададени с hp.choice, получаваме индекс, показващ позицията на конкретната стойност в зададения списък. Необходимо е за тях да създадем отделни речници с конкретните индекси и техните съответстващи стойности.

criterion = {0: 'entropy', 1: 'gini'}
m_features = {0: 'sqrt', 1: 'log2', 2: None}
n_est = {0: 200, 1: 500}
split = {0: 2, 1: 4, 2: 6, 3: 8}
leaf = {0: 2, 1: 5, 2: 10, 3: 14}

Оптималните параметри, определени от функцията fmin(), можете да видите в таблицата по-надолу:

ParametersValues
0criteriongini
1max_depth10
2max_featureslog2
3min_samples_leaf2
4min_samples_split2
5n_estimators200

След като вече имаме оптималните параметри, можем да създадем новия модел.

# Създаване на модел с оптималните параметри
trainedforest = RandomForestClassifier(criterion = criterion[best['criterion']],   
                                       max_depth = best['max_depth'], 
                                       max_features = m_features[best['max_features']], 
                                       min_samples_leaf = leaf[best['min_samples_leaf']], 
                                       min_samples_split = split[best['min_samples_split']], 
                                       n_estimators = n_est[best['n_estimators']])

# Обучение на модела
trainedforest.fit(X_train_res, y_train_res)

# Тест на модела
rf_new_pred = trainedforest.predict(X_test)

Основните метрики за оценка на класификационни модели са със следните стойности:

MetricsValues
0Overall Accuracy0.756716
1Overall Error0.243284
2Precision0.736842
3Specificity0.833333
4Sensitivity0.65
5F1_score0.690702

Моделът е със 76% обща точност. В 74% от случаите правилно е определил обектите от положителния клас, които в действителност са положителни. Открил е 83% от отрицателния клас и 65% от положителния.

# Откриване на разликата между определените от класификатора класове и тези в действителност
error = df_rf_best['Actual'].mean() - df_rf_best['Predicted'].mean()

41.79% от обектите в тестовата извадка спадат към положителния клас, а класификаторът е определил правилно 36.86% от тях. Имаме 4.93% грешка.

Сравнение на резултатите

В следната сравнителна таблица можете да видите разликата в стойностите на различните метрики за оценка на класификационни модели за модела с параметри по подразбиране и след оптимизация.

MetricsRF DefaultRF Best Params
0Overall Accuracy0.7149250.756716
1Overall Error0.2850750.243284
2Precision0.6654280.736842
3Specificity0.7692310.833333
4Sensitivity0.6392860.65
5F1_score0.6520950.690702

Наблюдава се подобрение във всичките метрики след извършване на оптимизация. Това може да се види и в графиките на матрицата на неточностите и на ROC и Precision-Recall кривите.

confusion matrix hyperopt параметри срещу подразбиращи се

Моделът с оптимални параметри има по-добри резултати, защото стойностите на False Positive и False Negative са по-ниски от тези, изобразени на матрицата на модела с параметри по подразбиране. Правилно е определил 325 от общо 390 обекта от отрицателния клас и 182 от 280 от положителния клас.

Съдейки по двете графики, оптимизираният модел дава по-добри резултати от този с параметри по подразбиране. Неговата ROC крива е с по-голяма площ под кривата и е по-далеч от нулевия класификатор, а Precision-Recall кривата му е по-близко до горния десен ъгъл на графиката отколкото тази на другия модел.

Извод

Библиотеката Hyperopt предоставя много възможности за оптимизация на модел за машинно обучение, най-вече при задаване на пространството от параметри и техните стойности. При нея предимство е това, че взима под внимание предишни итерации при търсене на най-добрите стойности за параметрите, което ѝ позволява по-бързо да открие тези, които са оптимални.

Искате да научите повече за машинното обучение?

Включете се в курса по машинно обучение и анализ на данни с Python.

Научете повече

Автор: Десислава Христова