Búsqueda de sitios web

Técnicas de optimización de hiperparámetros para mejorar el rendimiento de su modelo de aprendizaje automático


Cuando trabajas en un proyecto de aprendizaje automático, debes seguir una serie de pasos hasta alcanzar tu objetivo.

Uno de los pasos que debe realizar es la optimización de hiperparámetros en el modelo seleccionado. Esta tarea siempre viene después del proceso de selección de modelo en el que se elige el modelo que funciona mejor que otros modelos.

¿Qué es la optimización de hiperparámetros?

Antes de definir la optimización de hiperparámetros, es necesario comprender qué es un hiperparámetro.

En resumen, los hiperparámetros son diferentes valores de parámetros que se utilizan para controlar el proceso de aprendizaje y tienen un efecto significativo en el rendimiento de los modelos de aprendizaje automático.

Un ejemplo de hiperparámetros en el algoritmo Random Forest es el número de estimadores (n_estimators), la profundidad máxima (max_profundidad) y el criterio. Estos parámetros son ajustables y pueden afectar directamente qué tan bien se entrena un modelo.

Entonces, la optimización de hiperparámetros es el proceso de encontrar la combinación correcta de valores de hiperparámetros para lograr el máximo rendimiento de los datos en un período de tiempo razonable.

Este proceso juega un papel vital en la precisión de la predicción de un algoritmo de aprendizaje automático. Por lo tanto, la optimización de hiperparámetros se considera la parte más complicada de la creación de modelos de aprendizaje automático.

La mayoría de estos algoritmos de aprendizaje automático vienen con los valores predeterminados de sus hiperparámetros. Pero los valores predeterminados no siempre funcionan bien en diferentes tipos de proyectos de aprendizaje automático. Es por eso que necesita optimizarlos para obtener la combinación correcta que le brinde el mejor rendimiento.

Una buena elección de hiperparámetros realmente puede hacer que un algoritmo brille.

Existen algunas estrategias comunes para optimizar los hiperparámetros. Veamos cada uno en detalle ahora.

Cómo optimizar los hiperparámetros

Búsqueda de cuadrícula

Este es un método tradicional y ampliamente utilizado que realiza un ajuste de hiperparámetros para determinar los valores óptimos para un modelo determinado.

La búsqueda de cuadrícula funciona probando todas las combinaciones posibles de parámetros que desee probar en su modelo. Esto significa que llevará mucho tiempo realizar toda la búsqueda, lo que puede resultar muy costoso desde el punto de vista computacional.

Puede obtener más información sobre cómo implementar Grid Search aquí.

Búsqueda aleatoria

Este método funciona de forma un poco diferente: se utilizan combinaciones aleatorias de los valores de los hiperparámetros para encontrar la mejor solución para el modelo construido.

El inconveniente de la búsqueda aleatoria es que a veces puede pasar por alto puntos (valores) importantes en el espacio de búsqueda.

Puede obtener más información sobre cómo implementar la búsqueda aleatoria aquí.

Técnicas alternativas de optimización de hiperparámetros

Ahora les presentaré algunas técnicas/métodos de optimización de hiperparámetros alternativos y avanzados. Estos pueden ayudarle a obtener los mejores parámetros para un modelo determinado.

Examinaremos las siguientes técnicas:

  1. hiperóptica
  2. Optimizar Scikit
  3. optuna

hiperóptica

Hyperopt es una potente biblioteca de Python para la optimización de hiperparámetros desarrollada por James Bergstra.

Utiliza una forma de optimización bayesiana para el ajuste de parámetros que le permite obtener los mejores parámetros para un modelo determinado. Puede optimizar un modelo con cientos de parámetros a gran escala.

Hyperopt tiene cuatro características importantes que necesita conocer para ejecutar su primera optimización.

Espacio de búsqueda

Hyperopt tiene diferentes funciones para especificar rangos de parámetros de entrada. Estos se denominan espacios de búsqueda estocástica. Las opciones más comunes para un espacio de búsqueda son:

  • hp.choice(label, options): se puede utilizar para parámetros categóricos. Devuelve una de las opciones, que debería ser una lista o tupla.
    Ejemplo: hp.choice("criterion", ["gini","entropy",])
  • hp.randint(label, lower): esto se puede utilizar para parámetros enteros. Devuelve un número entero aleatorio en el rango (0, superior).
    Ejemplo: hp.randint("max_features",50)
  • hp.uniform(label, low, high): devuelve un valor uniforme entre low y high.
    Ejemplo: hp.uniform("max_leaf_nodes",1,10)

Otra opción que puedes utilizar son:

  • hp.normal(label, mu, sigma): devuelve un valor real que se distribuye normalmente con media mu y desviación estándar sigma.
  • hp.qnormal(label, mu, sigma, q): esto devuelve un valor como round(normal(mu, sigma)/q) * q
  • hp.lognormal(label, mu, sigma): devuelve un valor dibujado de acuerdo con exp(normal(mu, sigma))
  • hp.qlognormal(label, mu, sigma, q): esto devuelve un valor como round(exp(normal(mu, sigma))/q) * q

Puede obtener más información sobre las opciones del espacio de búsqueda aquí.

Solo una nota rápida: cada expresión estocástica optimizable tiene una etiqueta (por ejemplo, n_estimators) como primer argumento. Estas etiquetas se utilizan para devolver opciones de parámetros a la persona que llama durante el proceso de optimización.

Función objetiva

Esta es una función de minimización que recibe valores de hiperparámetros como entrada desde el espacio de búsqueda y devuelve la pérdida.

Esto significa que durante el proceso de optimización, entrenamos el modelo con valores de parámetros de haype seleccionados y predecimos la característica objetivo. Luego evaluamos el error de predicción y se lo devolvemos al optimizador.

El optimizador decidirá qué valores verificar e iterar nuevamente. Aprenderá a crear funciones objetivas en el ejemplo práctico.

fmin

La función fmin es la función de optimización que itera sobre diferentes conjuntos de algoritmos y sus hiperperámetros y luego minimiza la función objetivo.

fmin toma cinco entradas, que son:

  • La función objetivo para minimizar
  • El espacio de búsqueda definido
  • El algoritmo de búsqueda a utilizar, como búsqueda aleatoria, TPE (estimadores de árbol parzen) y TPE adaptativo.
    Nota: hyperopt.rand.suggest y hyperopt.tpe.suggest Proporcionar lógica para la búsqueda secuencial del espacio de hiperparámetros.
  • Número máximo de evaluaciones
  • Y el objeto de ensayos (opcional)

Ejemplo :

from hyperopt import fmin, tpe, hp,Trials

trials = Trials()

best = fmin(fn=lambda x: x ** 2,
    		space= hp.uniform('x', -10, 10),
    		algo=tpe.suggest,
    		max_evals=50,
    		trials = trials)

print(best)

Objeto de pruebas

El objeto Trials se utiliza para conservar todos los hiperparámetros, pérdidas y otra información. Esto significa que puede acceder a él después de ejecutar la optimización.

Además, las pruebas pueden ayudarle a guardar información importante y luego cargarla y luego reanudar el proceso de optimización. Aprenderá más sobre esto en el ejemplo práctico a continuación.

from hyperopt import Trials 

trials = Trials()

Ahora que comprende las características importantes de Hyperopt, veremos cómo usarlo. Seguirás estos pasos:

  • Inicializa el espacio sobre el que buscar.
  • Definir la función objetivo
  • Seleccione el algoritmo de búsqueda a utilizar
  • Ejecute la función hiperopt
  • Analizar los resultados de las evaluaciones almacenados en el objeto de pruebas

Hiperpot en la práctica

En este ejemplo práctico, usaremos el conjunto de datos de precios móviles. Nuestra tarea es crear un modelo que prediga qué tan alto será el precio de un dispositivo móvil: 0 (bajo costo), 1 (coste medio), 2 (costo alto) o 3 (costo muy alto).

Instalar hiperopt

Puede instalar hyperopt desde PyPI ejecutando este comando:

pip install hyperopt

Luego importe los siguientes paquetes importantes, incluido Hyperopt:

# import packages 
import numpy as np 
import pandas as pd 
from sklearn.ensemble import RandomForestClassifier 
from sklearn import metrics
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler 
from hyperopt import tpe, hp, fmin, STATUS_OK,Trials
from hyperopt.pyll.base import scope

import warnings
warnings.filterwarnings("ignore")

Conjunto de datos

Carguemos el conjunto de datos desde el directorio de datos. Para obtener más información sobre el conjunto de datos, léalo aquí.

# load data 
data = pd.read_csv("data/mobile_price_data.csv")

Verifique las primeras cinco filas del conjunto de datos de esta manera:

#read data 
data.head()

Como puede ver, en nuestro conjunto de datos tenemos diferentes características con valores numéricos.

Veamos la forma del conjunto de datos.

#show shape
data.shape

Obtenemos lo siguiente:

(2000, 21)

En este conjunto de datos tenemos 2000 filas y 21 columnas. Ahora comprendamos la lista de características que tenemos en este conjunto de datos.

#show list of columns 
list(data.columns)

Puede encontrar el significado del nombre de cada columna aquí.

Dividir el conjunto de datos en función de destino y funciones independientes

Este es un problema de clasificación. Así que ahora dividiremos la característica objetivo y las características independientes del conjunto de datos. Nuestra característica objetivo es price_range.

# split data into features and target 
X = data.drop("price_range", axis=1).values 
y = data.price_range.values

Preprocesamiento del conjunto de datos

A continuación, estandarizaremos las funciones independientes utilizando el método StandardScaler de scikit-learn.

# standardize the feature variables 
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

Definir espacio de parámetros para optimización

Usaremos tres hiperparámetros del algoritmo Random Forest: n_estimators, max_ Depth, y criterio.

space = {
    "n_estimators": hp.choice("n_estimators", [100, 200, 300, 400,500,600]),
    "max_depth": hp.quniform("max_depth", 1, 15,1),
    "criterion": hp.choice("criterion", ["gini", "entropy"]),
}

Hemos establecido diferentes valores en los hiperparámetros seleccionados anteriormente. Ahora definiremos la función objetivo.

Definición de una función a minimizar (función objetivo)

Nuestra función que queremos minimizar se llama hyperparamter_tuning. El algoritmo de clasificación para optimizar su hiperparámetro es Random Forest.

Utilizo la validación cruzada para evitar el sobreajuste y luego la función devolverá valores de pérdida y su estado.

# define objective function

def hyperparameter_tuning(params):
    clf = RandomForestClassifier(**params,n_jobs=-1)
    acc = cross_val_score(clf, X_scaled, y,scoring="accuracy").mean()
    return {"loss": -acc, "status": STATUS_OK}

Recuerde que hyperopt minimiza la función. Por eso agrego el signo negativo en la cuenta.

Ajustar el modelo

Finalmente, primero crearemos una instancia del objeto de prueba, ajustaremos el modelo y luego imprimiremos la mejor pérdida con sus valores de hiperparámetros.

# Initialize trials object
trials = Trials()

best = fmin(
    fn=hyperparameter_tuning,
    space = space, 
    algo=tpe.suggest, 
    max_evals=100, 
    trials=trials
)

print("Best: {}".format(best))

100%|██████████████████████████████████████ █████████ ██████████| 100/100 [10:30<00:00, 6,30 s/prueba, mejor pérdida: -0,8915] Mejor: {'criterio': 1, 'máx_profundidad': 11,0, 'n_estimadores': 2}.

Después de realizar la optimización de hiperparámetros, la pérdida es -0,8915. Esto significa que el rendimiento del modelo tiene una precisión del 89,15 % al utilizar n_estimators=300, max_ Depth=11, y criterio="entropía" en el clasificador de bosque aleatorio.

Analizar los resultados utilizando el objeto de pruebas.

El objeto de prueba puede ayudarnos a inspeccionar todos los valores de retorno que se calcularon durante el experimento.

(a) juicios.resultados
Esto muestra una lista de diccionarios devueltos por 'objetivo' durante la búsqueda.

trials.results

(b) juicios.losses()
Esto muestra una lista de pérdidas (flotante para cada prueba "correcta").

trials.losses()


[-0.8790000000000001, -0.877, -0.768, -0.8205, -0.8720000000000001, -0.883, -0.8554999999999999, -0.8789999999999 999, -0,595, -0,8765000000000001, -0,877, .........]

(c) juicios.statuses()
Esto muestra una lista de cadenas de estado.

trials.statuses()

Nota: Este objeto de prueba se puede guardar, pasar a las rutinas de trazado integradas o analizar con su propio código personalizado.

Ahora que sabes cómo implementar Hyperopt, aprendamos la segunda técnica alternativa de optimización de hiperparámetros llamada Scikit-Optimize.

Optimización de Scikit

Scikit-optimize es otra biblioteca Python de código abierto para la optimización de hiperparámetros. Implementa varios métodos para la optimización secuencial basada en modelos.

La biblioteca es muy fácil de usar y proporciona un conjunto de herramientas generales para la optimización bayesiana que se puede utilizar para el ajuste de hiperparámetros. También brinda soporte para ajustar los hiperparámetros de los algoritmos de aprendizaje automático que ofrece la biblioteca scikit-learn.

scikit-optimize está construido sobre Scipy, NumPy y Scikit-Learn.

Scikit-optimize tiene al menos cuatro características importantes que necesita conocer para ejecutar su primera optimización. Veámoslos en profundidad ahora.

Espacio

scikit-optimize tiene diferentes funciones para definir el espacio de optimización que contiene una o varias dimensiones. Las opciones más habituales para elegir un espacio de búsqueda son:

  • Real : se trata de una dimensión del espacio de búsqueda que puede adquirir cualquier valor real. Debe definir el límite inferior y el límite superior y ambos son inclusivos.
    Ejemplo: Real(low=0.2, high=0.9, name="min_samples_leaf")
  • Entero : esta es una dimensión del espacio de búsqueda que puede tomar valores enteros.
    Ejemplo: Integer(low=3, high=25, name=" max_features")
  • Categórico: esta es una dimensión del espacio de búsqueda que puede adoptar valores categóricos.
    Ejemplo: Categorical(["gini","entropy"],name ="criterio")

Nota: en cada espacio de búsqueda hay que definir el nombre del hiperparámetro a optimizar utilizando el argumento nombre.

BayesBuscarCV

La clase BayesSearchCV proporciona una interfaz similar a GridSearchCV o RandomizedSearchCV pero realiza optimización bayesiana sobre hiperparámetros.

BayesSearchCV implementa un método “ajuste” y “puntuación” y otros métodos comunes como predict(),predict_proba(), decision_function(), transform() y inverse_transform() si están implementados en el estimador utilizado .

A diferencia de GridSearchCV, no se prueban todos los valores de los parámetros. Más bien, se muestra un número fijo de configuraciones de parámetros de las distribuciones especificadas. El número de configuraciones de parámetros que se prueban viene dado por n_iter.

Tenga en cuenta que aprenderá cómo implementar BayesSearchCV en un ejemplo práctico a continuación.

Función objetiva

Esta es una función que será llamada por el procedimiento de búsqueda. Recibe valores de hiperparámetros como entrada del espacio de búsqueda y devuelve la pérdida (cuanto menor, mejor).

Esto significa que durante el proceso de optimización, entrenamos el modelo con valores de hiperparámetros seleccionados y predecimos la característica objetivo. Luego evaluamos el error de predicción y se lo devolvemos al optimizador.

El optimizador decidirá qué valores verificar y repetirá nuevamente. Aprenderá cómo crear una función objetivo en el ejemplo práctico a continuación.

Optimizador

Esta es la función que realiza el proceso de optimización de hiperparámetros bayesianos. La función de optimización itera en cada modelo y el espacio de búsqueda para optimizar y luego minimiza la función objetivo.

Existen diferentes funciones de optimización proporcionadas por la biblioteca scikit-optimize, como por ejemplo:

  • dummy_minimize: búsqueda aleatoria mediante muestreo uniforme dentro de los límites dados.
  • forest_minimize: optimización secuencial mediante árboles de decisión.
  • gbrt_minimize: optimización secuencial utilizando árboles potenciados por gradiente.
  • gp_minimize: optimización bayesiana mediante procesos gaussianos.
    Nota: implementaremos gp_minimize en el ejemplo práctico siguiente.

Otras características que debes aprender son las siguientes:

  • Transformadores espaciales
  • Funciones de utilidades
  • Funciones de trazado
  • Extensiones de aprendizaje automático para optimización basada en modelos

Optimización de Scikit en la práctica

Ahora que conoce las características importantes de scikit-optimize, veamos un ejemplo práctico. Usaremos el mismo conjunto de datos llamado Conjunto de datos de precios móviles que usamos con Hyperopt.

Instalar Scikit-Optimize

scikit-optimize requiere la siguiente versión y paquetes de Python:

  • Pitón >= 3.6
  • NumPy (>= 1.13.3)
  • Ciencia ficción (>= 0.19.1)
  • biblioteca de trabajo (>= 0,11)
  • aprendizaje-scikit >= 0,20
  • matplotlib >= 2.0.0

Puede instalar la última versión con este comando:

pip install scikit-optimize

Luego importe paquetes importantes, incluido scikit-optimize:

# import packages
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn import metrics
from sklearn.model_selection import cross_val_score 
from sklearn.preprocessing import StandardScaler
from skopt.searchcv import BayesSearchCV
from skopt.space import Integer, Real, Categorical 
from skopt.utils import use_named_args
from skopt import gp_minimize

import warnings

warnings.filterwarnings("ignore")

El primer enfoque

En el primer enfoque, utilizaremos BayesSearchCV para realizar una optimización de hiperparámetros para el algoritmo Random Forest.

Definir espacio de búsqueda

Ajustaremos los siguientes hiperparámetros del modelo Random Forest:

  • n_estimators : la cantidad de árboles en el bosque.
  • max_ Depth: la profundidad máxima del árbol.
  • criterio: la función para medir la calidad de una división.
# define search space 
params = {
    "n_estimators": [100, 200, 300, 400],
    "max_depth": (1, 9),
    "criterion": ["gini", "entropy"],
}

Hemos definido el espacio de búsqueda como un diccionario. Tiene nombres de hiperparámetros utilizados como clave y el alcance de la variable como valor.

Definir la configuración de BayesSearchCV

El beneficio de BayesSearchCV es que el procedimiento de búsqueda se realiza automáticamente, lo que requiere una configuración mínima.

La clase se puede utilizar de la misma forma que Scikit-Learn (GridSearchCV y RandomizedSearchCV).

# define the search
search = BayesSearchCV(
    estimator=rf_classifier,
    search_spaces=params,
    n_jobs=1,
    cv=5,
    n_iter=30,
    scoring="accuracy",
    verbose=4,
    random_state=42
)

Ajustar el modelo

Luego ejecutamos la búsqueda pasando las características preprocesadas y la característica objetivo (rango_precio).

# perform the search
search.fit(X_scaled,y)

Puede encontrar la mejor puntuación utilizando el atributo best_score y los mejores parámetros utilizando el atributo best_params de < fuerte>búsqueda.

# report the best result

print(search.best_score_)
print(search.best_params_)

Tenga en cuenta que la versión actual de scikit-optimize (0.7.4) no es compatible con las últimas versiones de scikit learn (0.23.1 y 0.23.2). Entonces, cuando ejecuta el proceso de optimización utilizando este enfoque, puede obtener errores como este:

TypeError: object.__init__() takes exactly one argument (the instance to initialize)

Puede encontrar más información sobre este error en su cuenta de GitHub.

  • https://github.com/scikit-optimize/scikit-optimize/issues/928
  • https://github.com/scikit-optimize/scikit-optimize/issues/924
  • https://github.com/scikit-optimize/scikit-optimize/issues/902

Espero que solucionen muy pronto este problema de incompatibilidad.

El segundo enfoque

En el segundo enfoque, primero definimos el espacio de búsqueda utilizando los métodos de espacio proporcionados por scikit-optimize, que son Categorical y Integer.

# define the space of hyperparameters to search
search_space = list()
search_space.append(Categorical([100, 200, 300, 400], name='n_estimators'))
search_space.append(Categorical(['gini', 'entropy'], name='criterion'))
search_space.append(Integer(1, 9, name='max_depth'))

Hemos establecido diferentes valores en los hiperparámetros seleccionados anteriormente. Luego definiremos la función objetivo.

Definición de una función a minimizar (función objetivo)

Nuestra función a minimizar se llama evalute_model y el algoritmo de clasificación para optimizar su hiperparámetro es Random Forest.

Utilizo validación cruzada para evitar el sobreajuste y luego la función devolverá valores de pérdida.

# define the function used to evaluate a given configuration

@use_named_args(search_space)
def evaluate_model(**params):
    # configure the model with specific hyperparameters
    clf = RandomForestClassifier(**params, n_jobs=-1)
    acc = cross_val_score(clf, X_scaled, y, scoring="accuracy").mean()

El decorador use_named_args() permite que su función objetivo reciba los parámetros como argumentos de palabras clave. Esto es particularmente conveniente cuando desea configurar los parámetros del estimador de scikit-learn.

Recuerde que scikit-optimize minimiza la función, por eso agrego un signo negativo en acc.

Ajustar el modelo

Finalmente, ajustamos el modelo utilizando el método gp_minimize (utiliza optimización basada en procesos gaussianos) de scikit-optimize. Luego imprimimos la mejor pérdida con sus valores de hiperparámetros.

# perform optimization

result = gp_minimize(
    func=evaluate_model,
    dimensions=search_space,
    n_calls=30,
    random_state=42,
    verbose=True,
    n_jobs=1,
)

Salida:
Iteración nº: 1 iniciada. Evaluando la función en un punto aleatorio.
Iteración nº: 1 finalizada. Evaluación realizada en un punto aleatorio.
Tiempo empleado: 8,6910
Valor de la función obtenido: -0,8585
Mínimo actual: -0,8585
Iteración nº: 2 iniciada. Evaluando la función en un punto aleatorio.
Iteración nº: 2 finalizada. Evaluación realizada en un punto aleatorio.
Tiempo empleado: 4,5096
Valor de la función obtenido: -0,7680
Mínimo actual: -0.8585 ………………….

No es que se ejecute hasta llegar a la última iteración. Para nuestro proceso de optimización, el número total de iteraciones es 30.

Luego podemos imprimir la mejor precisión y los valores de los hiperparámetros seleccionados que utilizamos.

# summarizing finding:

print('Best Accuracy: %.3f' % (result.fun)) 
print('Best Parameters: %s' % (result.x))
Best Accuracy: -0.882
Best Parameters: [300, 'entropy', 9]

Después de realizar la optimización de hiperparámetros, la pérdida es -0,882. Esto significa que el rendimiento del modelo tiene una precisión del 88,2% utilizando n_estimators=300, max_ Depth=9, y criterio=“entropía” en el clasificador Random Forest.

Nuestro resultado no es muy diferente al de Hyperopt en la primera parte (precisión del 89,15%).

Imprimir valores de función

Puede imprimir todos los valores de la función en cada iteración utilizando el atributo func_vals del objeto OptimizeResult (resultado).

print(result.func_vals)

Salida:
array([-0.8665, -0.7765, -0.7485, -0.86, -0.872, -0.545, -0.81,
-0.7725, - 0,8115, -0,8705, -0,8685, -0,879, -0,816, -0,8815,
-0,8645, -0,8745, -0,867, -0,8785, -0,878, -0,878, -0,8785,
-0,874 5, -0,8785, -0,868, -0,8815, -0,877, -0,879,
-0,8705, -0,8745])

Trazar trazas de convergencia

Podemos utilizar el método plot_convergence de scikit-optimize para trazar uno o varios rastros de convergencia. Solo necesitamos pasar el objeto OptimizeResult (resultado) en el método plot_convergence.

# plot convergence 

from skopt.plots import plot_convergence

plot_convergence(result) 

El gráfico muestra los valores de la función en diferentes iteraciones durante el proceso de optimización.

Ahora que sabe cómo implementar scikit-optimize, aprendamos la tercera y última técnica alternativa de optimización de hiperparámetros llamada Optuna.

optuna

Optuna es otro marco Python de código abierto para la optimización de hiperparámetros que utiliza el método bayesiano para automatizar el espacio de búsqueda de hiperparámetros. El marco fue desarrollado por una empresa japonesa de inteligencia artificial llamada Preferred Networks.

Optuna es más fácil de implementar y usar que Hyperopt. También puede especificar cuánto tiempo debe durar el proceso de optimización.

Optuna tiene al menos cinco características importantes que debes conocer para ejecutar tu primera optimización.

Buscar espacios

Optuna ofrece diferentes opciones para todos los tipos de hiperparámetros. Las opciones más comunes a elegir son las siguientes:

  • Parámetros categóricos : utiliza el método trials.suggest_categorical(). Debe proporcionar el nombre del parámetro y sus opciones.
  • Parámetros enteros - usa trials.suggest_int() método. Debe proporcionar el nombre del parámetro, el valor bajo y alto.
  • Parámetros flotantes : usa trials.suggest_float() método. Debe proporcionar el nombre del parámetro, el valor bajo y alto.
  • Parámetros continuos : usa trials.suggest_uniform() método. Debe proporcionar el nombre del parámetro, el valor bajo y alto.
  • Parámetros discretos: usa trials.suggest_discrete_uniform() método. Debe proporcionar el nombre del parámetro, el valor bajo, el valor alto y el paso de discretización.

Métodos de optimización (Samplers)

Optuna tiene diferentes formas de realizar el proceso de optimización de hiperparámetros. Los métodos más comunes son:

  • GridSampler - Utiliza una búsqueda en cuadrícula. Los ensayos sugieren todas las combinaciones de parámetros en el espacio de búsqueda dado durante el estudio.
  • RandomSampler - Utiliza muestreo aleatorio. Este muestreador se basa en un muestreo independiente.
  • TPESampler - Utiliza el algoritmo TPE (Estimador Parzen estructurado en árbol).
  • CmaEsSampler - Utiliza el algoritmo CMA-ES.

Función objetiva

La función objetivo funciona de la misma manera que en las técnicas hyperopt y scikit-optimize. La única diferencia es que Optuna te permite definir el espacio de búsqueda y el objetivo en una sola función.

Ejemplo :

def objective(trial):
    # Define the search space
    criterions = trial.suggest_categorical('criterion', ['gini', 'entropy'])
    max_depths = trial.suggest_int('max_depth', 1, 9, 1)
    n_estimators = trial.suggest_int('n_estimators', 100, 1000, 100)

    clf = sklearn.ensemble.RandomForestClassifier(n_estimators=n_estimators,
                                 criterion=criterions,
                                 max_depth=max_depths,
                                 n_jobs=-1)
                                 
    score = cross_val_score(clf, X_scaled, y, scoring="accuracy").mean()

    return score

(d) Estudio

Un estudio corresponde a una tarea de optimización (un conjunto de pruebas). Si necesita iniciar el proceso de optimización, debe crear un objeto de estudio y pasar la función objetivo a un método llamado optimize() y establecer el número de pruebas de la siguiente manera :

study = optuna.create_study()
study.optimize(objective, n_trials=100)


El método create_study() le permite elegir si desea maximizar o <.minimizar su función objetivo.

Esta es una de las características más útiles que me gustan de optuna porque tienes la posibilidad de elegir la dirección del proceso de optimización.

Tenga en cuenta que aprenderá cómo implementar esto en el ejemplo práctico a continuación.

Visualización

El módulo de visualización de Optuna proporciona diferentes métodos para crear figuras para el resultado de la optimización. Estos métodos le ayudan a obtener información sobre las interacciones entre parámetros y le permiten saber cómo avanzar.

Éstos son algunos de los métodos que puede utilizar.

  • plot_contour() : este método traza la relación de parámetros como un gráfico de contorno en un estudio.
  • plot_intermidiate_values() - Este método traza los valores intermedios de todos los ensayos de un estudio.
  • plot_optimization_history() - Este método traza el historial de optimización de todas las pruebas de un estudio.
  • plot_param_importances() - Este método traza la importancia de los hiperparámetros y sus valores.
  • plot_edf() - Este método traza el valor objetivo EDF (función de distribución empírica) de un estudio.

Usaremos algunos de los métodos mencionados anteriormente en el ejemplo práctico a continuación.

Optuna en la práctica

Ahora que conoce las características importantes de Optuna, en este ejemplo práctico usaremos el mismo conjunto de datos (Conjunto de datos de precios móviles) que usamos en los dos métodos anteriores.

Instalar Optuna

Puede instalar la última versión con:

pip install optuna

Luego importe los paquetes importantes, incluido optuna:

# import packages 
import numpy as np 
import pandas as pd 
from sklearn.ensemble import RandomForestClassifier 
from sklearn import metrics 
from sklearn.model_selection import cross_val_score 
from sklearn.preprocessing import StandardScaler 
import joblib 

import optuna 
from optuna.samplers import TPESampler


import warnings
warnings.filterwarnings("ignore")

Definir el espacio de búsqueda y el objetivo en una sola función.

Como expliqué anteriormente, Optuna te permite definir el espacio de búsqueda y el objetivo en una sola función.

Definiremos los espacios de búsqueda para los siguientes hiperparámetros del modelo Random Forest:

  • n_estimators : la cantidad de árboles en el bosque.
  • max_ Depth: la profundidad máxima del árbol.
  • criterio: la función para medir la calidad de una división.
# define the search space and the objecive function


def objective(trial):
    # Define the search space
    criterions = trial.suggest_categorical('criterion', ['gini', 'entropy'])
    max_depths = trial.suggest_int('max_depth', 1, 9, 1)
    n_estimators = trial.suggest_int('n_estimators', 100, 1000, 100)

    clf = RandomForestClassifier(n_estimators=n_estimators,
                                 criterion=criterions,
                                 max_depth=max_depths,
                                 n_jobs=-1)
    score = cross_val_score(clf, X_scaled, y, scoring="accuracy").mean()

    return score

Usaremos el método trial.suggest_categorical() para definir un espacio de búsqueda para criterio y <.trial.suggest_int() tanto para max_profundidad como para n_estimators.

Además, utilizaremos validación cruzada para evitar el sobreajuste y luego la función devolverá la precisión media.

Crear un objeto de estudio

A continuación creamos un objeto de estudio que corresponde a la tarea de optimización. El método create-study() nos permite proporcionar el nombre del estudio, la dirección de la optimización (maximizar o minimizar), y el método de optimización que queremos utilizar.

# create a study object 

study = optuna.create_study(study_name="randomForest_optimization",
                            direction="maximize",
                            sampler=TPESampler())

En nuestro caso llamamos a nuestro objeto de estudio randomForest_optimization. La dirección de la optimización es maximizar (lo que significa que cuanto mayor sea la puntuación, mejor) y el método de optimización a utilizar es TPESampler(). fuerte>

Ajustar el modelo

Para ejecutar el proceso de optimización, necesitamos pasar la función objetivo y el número de pruebas en el método optimize() del objeto de estudio que hemos creado.

Hemos establecido el número de pruebas en 10 (pero puede cambiar el número si desea realizar más pruebas).

# pass the objective function to method optimize()

study.optimize(objective, n_trials=10)

Salida:

Luego podemos imprimir la mejor precisión y los valores de los hiperparámetros seleccionados utilizados.

Para mostrar los mejores valores de hiperparámetros seleccionados:

print(study.best_params)

Salida: {'criterio': 'entropía', 'profundidad_máxima': 8, 'n_estimadores': 700}

Para mostrar la mejor puntuación o precisión:

print(study.best_value)

Salida: 0,8714999999999999.

Nuestra mejor puntuación ronda el 87,15%.

Historial de optimización de trazado

Podemos utilizar el método plot_optimization_history() de Optuna para trazar el historial de optimización de todas las pruebas de un estudio. Solo necesitamos pasar el objeto de estudio optimizado en el método.

optuna.visualization.plot_optimization_history(study)

El gráfico muestra los mejores valores en diferentes pruebas durante el proceso de optimización.

Trazar las importancias de los hiperparámetros

Optuna proporciona un método llamado plot_param_importances() para trazar la importancia de los hiperparámetros. Solo necesitamos pasar el objeto de estudio optimizado en el método.

En la figura anterior se puede ver que la profundidad máxima es el hiperparámetro más importante.

Guardar y cargar búsquedas de hiperparámetros

Puede guardar y cargar las búsquedas de hiperparámetros utilizando el paquete joblib.

Primero, guardaremos las búsquedas de hiperparámetros en el directorio optuna_searches.

# save your hyperparameter searches 

joblib.dump(study, 'optuna_searches/study.pkl')

Luego, si desea cargar las búsquedas de hiperparámetros desde el directorio optuna_searches, puede utilizar el método load() de joblib.

# load your hyperparameter searches

study = joblib.load('optuna_searches/study.pkl')

Terminando

¡Felicitaciones, ha llegado al final del artículo!

Echemos un vistazo a las puntuaciones generales y los valores de hiperparámetros seleccionados por las tres técnicas de optimización de hiperparámetros que hemos analizado en este artículo.

Los resultados que presenta cada técnica no son tan diferentes entre sí. El número de iteraciones o pruebas seleccionadas marca la diferencia.

Para mí, Optuna es fácil de implementar y es mi primera opción en técnicas de optimización de hiperparámetros. ¡Por favor dejame saber lo que tu piensas!

Puede descargar el conjunto de datos y todos los cuadernos utilizados en este artículo aquí:
https://github.com/Davisy/Hyperparameter-Optimization-Techniques

Si aprendiste algo nuevo o disfrutaste leyendo este artículo, compártelo para que otros puedan verlo. Hasta entonces, ¡nos vemos en mi próximo artículo!. También me pueden contactar en Twitter @Davis_McDavid

Artículos relacionados