Otimize os hiperparâmetros com o Hyperopt

Concluído

Hyperopt é uma biblioteca Python de código aberto 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, siga estas etapas:

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

Definir uma função objetiva

A hiperoptia funciona chamando iterativamente uma função (muitas vezes referida como a função objetiva) que retorna um valor numérico e ajustando os parâmetros passados para a função para que o valor de retorno seja minimizado, uma abordagem comumente referida como otimização. O primeiro requisito, portanto, é encapsular sua 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âmetro.
  • 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 forma que melhorar o desempenho do modelo reduza o valor de retorno.

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

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 é uma lista 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.

A função então avalia o modelo treinado para calcular sua métrica de precisão , que é um valor entre 0,0 e 1,0 indicando a proporção de previsões que o modelo fez que estavam corretas.

Finalmente, a função retorna um valor que a Hyperopt deve minimizar para melhorar o modelo. Neste caso, a métrica alvo é a precisão, para a qual um valor mais alto indica um modelo melhor; Assim, a função retorna o negativo desse valor (portanto, quanto maior a precisão, menor o valor de retorno).

Definir o espaço de pesquisa de hiperparâmetros

Cada vez que a função objetiva é chamada, ela requer um parâmetro contendo os valores de hiperparâmetro a serem tentados. Para especificar o conjunto completo de combinações de valores que podem ser tentadas, você precisa definir um espaço de pesquisa a partir do qual o Hyperopt pode selecionar os valores a serem usados em cada avaliação.

O Hyperopt fornece expressões que você pode usar para definir um intervalo de valores para cada hiperparâmetro, incluindo:

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

Gorjeta

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

O código de exemplo a seguir 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 do espaço de pesquisa e tentar otimizar a função objetiva. Há duas opções principais em como o Hyperopt fará a amostragem no espaço de pesquisa:

  • hyperopt.tpe.suggest: Tree of Parzen Estimators (TPE), uma abordagem bayesiana que seleciona adaptativamente novas configurações de hiperparâmetros com base em resultados anteriores.
  • hyperopt.rand.suggest: Pesquisa aleatória, uma abordagem não adaptativa que coleta 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 Hyperopt fmin

Finalmente, para executar uma execução Hyperopt, você pode usar a função fmin, que repetidamente chama 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 objetive definida anteriormente. O espaço de pesquisa e o algoritmo definidos em exemplos anteriores são usados, e a função será avaliada até 100 vezes antes que a função fmin retorne a combinação de valor de parâmetro de melhor desempenho que foi 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}