Otimizar hiperparâmetros com o Hyperopt

Concluído

O Hyperopt é uma biblioteca de código aberto do Python para ajuste de hiperparâmetros. O Hyperopt é instalado automaticamente quando você cria um cluster com uma variante de ML do Databricks Runtime. Para usá-lo ao treinar um modelo, execute as seguintes etapas:

  1. Defina uma função objetivo para treinar e avaliar um modelo.
  2. Defina o espaço de pesquisa do hiperparâmetro.
  3. Especifique o algoritmo de pesquisa.
  4. Execute a função fmin do Hyperopt para otimizar a função de treinamento.

Definir uma função objetiva

O Hyperopt funciona chamando iterativamente uma função (geralmente chamada de função objetiva) que retorna um valor numérico e ajustando os parâmetros passados para a função para que o valor retornado seja minimizado; uma abordagem normalmente conhecida como otimização. Portanto, o primeiro requisito é encapsular a lógica de treinamento e avaliação do modelo em uma função que:

  • Aceita um parâmetro que contém uma lista de valores de hiperparâmetros.
  • Treina um modelo usando os valores de hiperparâmetros fornecidos.
  • Avalia o modelo com base em uma métrica de destino para desempenho preditivo.
  • Retorna um valor numérico que reflete a métrica de desempenho de modo que a melhora do desempenho do modelo reduza o valor retornado.

Por exemplo, a função a seguir treina um modelo de aprendizado de máquina usando o algoritmo LogisticRegression da biblioteca MLlib do Spark.

def objective(params):
    from pyspark.ml.classification import LogisticRegression
    from pyspark.ml.evaluation import MulticlassClassificationEvaluator
    from hyperopt import STATUS_OK

    data_df = get_training_data() # This is just an example!
    splits = data_df.randomSplit([0.7, 0.3])
    training_df = splits[0]
    validation_df = splits[1]

    # Train a model using the provided hyperparameter values
    lr = LogisticRegression(labelCol="label", featuresCol="features",
                            maxIter=params['Iterations'],
                            regParam=params['Regularization'])
    model = lr.fit(training_df)

    # Evaluate the model
    predictions = model.transform(validation_df)
    eval = MulticlassClassificationEvaluator(labelCol="label",
                                             predictionCol="prediction",
                                             metricName="accuracy")
    accuracy = eval.evaluate(predictions)
    
    # Hyperopt *minimizes* the function, so return *negative* accuracy.
    return {'loss': -accuracy, 'status': STATUS_OK}

Neste exemplo, o parâmetro params é um dicionário que contém valores para dois valores nomeados: Iterações e Regularização. Esses valores são atribuídos aos hiperparâmetros maxIter e regParam do algoritmo de regressão logística usado para treinar o modelo.

Em seguida, a função avalia o modelo treinado para calcular a métrica de precisão dele, que é um valor entre 0,0 e 1,0 que indica a proporção de previsões feitas pelo modelo que estavam corretas.

Por fim, a função retorna um valor que o Hyperopt deve minimizar a fim de aprimorar o modelo. Nesse caso, a métrica de destino é a precisão, em que valores mais altos indicam modelos melhores; portanto, a função retorna o negativo desse valor (portanto, quanto maior a precisão, menor o valor retornado).

Definir o espaço de pesquisa de hiperparâmetro

Cada vez que a função objetiva é chamada, ela exige um parâmetro que contenha os valores de hiperparâmetros a serem testados. Para experimentar todas as combinações de valores possíveis, você precisa definir um espaço de pesquisa para o Hyperopt para selecionar valores de cada avaliação.

O Hyperopt fornece expressões que podem ser usadas para definir um intervalo de valores para cada hiperparâmetro, incluindo:

  • hp.choice(label, options): retorna um dos options que você listou.
  • hp.randint(label, upper): retorna um inteiro aleatório no intervalo [0, upper].
  • hp.uniform(label, low, high): retorna um valor uniforme entre low e high.
  • hp.normal(label, mu, sigma): retorna um valor real que normalmente é distribuído com média mu e desvio padrão sigma.

Dica

Para obter a lista completa de expressões, confira a Documentação do Hyperopt.

O seguinte código de exemplo define um espaço de pesquisa para os hiperparâmetros usados no exemplo anterior:

from hyperopt import hp

search_space = {
    'Iterations': hp.randint('Iterations', 10),
    'Regularization': hp.uniform('Regularization', 0.0, 1.0)
}

Especificar o algoritmo de pesquisa

O Hyperopt usa um algoritmo de pesquisa para selecionar valores de hiperparâmetros no espaço de pesquisa e tentar otimizar a função objetiva. Há duas opções principais de como a Hyperopt é amostrada no espaço de pesquisa:

  • hyperopt.tpe.suggest: TPE (Árvore de Avaliadores de Parzen), uma abordagem bayesiana que seleciona de maneira adaptativa novas configurações de hiperparâmetros com base em resultados anteriores.
  • hyperopt.rand.suggest: pesquisa aleatória, uma abordagem não adaptativa que obtém amostras aleatoriamente no espaço de pesquisa.

O código de exemplo a seguir especifica o algoritmo TPE.

from hyperopt import tpe

algo = tpe.suggest

Execute a função fmin do Hyperopt

Por fim, para executar uma execução do Hyperopt, você pode usar a função fmin, que chama repetidamente a função objetiva usando combinações de hiperparâmetros do espaço de pesquisa com base no algoritmo de pesquisa. O objetivo da função fmin é minimizar o valor retornado pela função objetiva (e, portanto, otimizar o desempenho do modelo).

O código de exemplo a seguir usa a função fmin para chamar a função objetiva definida anteriormente. O espaço de pesquisa e o algoritmo definidos em exemplos anteriores são usados e a função é avaliada até 100 vezes antes que a função fmin retorna a combinação de valor de parâmetro de melhor desempenho encontrada.

from hyperopt import fmin

argmin = fmin(
  fn=objective,
  space=search_space,
  algo=algo,
  max_evals=100)

print("Best param values: ", argmin)

A saída do código anterior é semelhante ao exemplo a seguir.

Best param values:  {'Iterations': 6, 'Regularization': 0.5461699702338606}