Ajuste de hiperparâmetros (pré-visualização)
O ajuste de hiperparâmetros é o processo de encontrar os valores ideais para os parâmetros que não são aprendidos pelo modelo de aprendizado de máquina durante o treinamento, mas sim definidos pelo usuário antes do início do processo de treinamento. Esses parâmetros são comumente referidos como hiperparâmetros, e exemplos incluem a taxa de aprendizagem, o número de camadas ocultas em uma rede neural, a força de regularização e o tamanho do lote.
O desempenho de um modelo de aprendizado de máquina pode ser altamente sensível à escolha de hiperparâmetros, e o conjunto ideal de hiperparâmetros pode variar muito, dependendo do problema específico e do conjunto de dados. O ajuste de hiperparâmetros é, portanto, uma etapa crítica no pipeline de aprendizado de máquina, pois pode ter um impacto significativo na precisão e no desempenho de generalização do modelo.
No Fabric, os cientistas de dados podem aproveitar FLAML
, uma biblioteca Python leve para automação eficiente de operações de aprendizado de máquina e IA, para seus requisitos de ajuste de hiperparâmetros. Nos notebooks Fabric, os usuários podem chamar flaml.tune
para ajuste económico de hiperparâmetros.
Importante
Este recurso está em visualização.
Ajustando o fluxo de trabalho
Há três etapas essenciais para usar flaml.tune
para concluir uma tarefa básica de ajuste:
- Especifique o objetivo de ajuste em relação aos hiperparâmetros.
- Especifique um espaço de pesquisa dos hiperparâmetros.
- Especifique restrições de ajuste, incluindo restrições no orçamento de recursos para fazer o ajuste, restrições nas configurações e/ou restrições em uma (ou várias) métrica(s) específica(s).
Objetivo de afinação
O primeiro passo é especificar seu objetivo de ajuste. Para fazer isso, você deve primeiro especificar seu procedimento de avaliação com relação aos hiperparâmetros em uma função definida pelo usuário evaluation_function
. A função requer uma configuração de hiperparâmetro como entrada. Ele pode simplesmente retornar um valor de métrica como um escalar ou retornar um dicionário de pares de nomes e valores de métricas.
No exemplo abaixo, podemos definir uma função de avaliação em relação a 2 hiperparâmetros denominados x
e y
.
import time
def evaluate_config(config: dict):
"""evaluate a hyperparameter configuration"""
score = (config["x"] - 85000) ** 2 - config["x"] / config["y"]
faked_evaluation_cost = config["x"] / 100000
time.sleep(faked_evaluation_cost)
# we can return a single float as a score on the input config:
# return score
# or, we can return a dictionary that maps metric name to metric value:
return {"score": score, "evaluation_cost": faked_evaluation_cost, "constraint_metric": config["x"] * config["y"]}
Espaço de busca
Em seguida, especificaremos o espaço de pesquisa de hiperparâmetros. No espaço de pesquisa, você precisa especificar valores válidos para seus hiperparâmetros e como esses valores são amostrados (por exemplo, de uma distribuição uniforme ou de uma distribuição log-uniforme). No exemplo abaixo, podemos fornecer o espaço de pesquisa para os hiperparâmetros x
e y
. Os valores válidos para ambos são inteiros que variam de [1, 100.000]. Esses hiperparâmetros são amostrados uniformemente nos intervalos especificados.
from flaml import tune
# construct a search space for the hyperparameters x and y.
config_search_space = {
"x": tune.lograndint(lower=1, upper=100000),
"y": tune.randint(lower=1, upper=100000)
}
# provide the search space to tune.run
tune.run(..., config=config_search_space, ...)
Com o FLAML, os usuários podem personalizar o domínio para um hiperparâmetro específico. Isso permite que os usuários especifiquem um de tipo e um intervalo válido para amostrar parâmetros. FLAML suporta os seguintes tipos de hiperparâmetros: float, inteiro e categórico. Você pode ver este exemplo abaixo para domínios comumente usados:
config = {
# Sample a float uniformly between -5.0 and -1.0
"uniform": tune.uniform(-5, -1),
# Sample a float uniformly between 3.2 and 5.4,
# rounding to increments of 0.2
"quniform": tune.quniform(3.2, 5.4, 0.2),
# Sample a float uniformly between 0.0001 and 0.01, while
# sampling in log space
"loguniform": tune.loguniform(1e-4, 1e-2),
# Sample a float uniformly between 0.0001 and 0.1, while
# sampling in log space and rounding to increments of 0.00005
"qloguniform": tune.qloguniform(1e-4, 1e-1, 5e-5),
# Sample a random float from a normal distribution with
# mean=10 and sd=2
"randn": tune.randn(10, 2),
# Sample a random float from a normal distribution with
# mean=10 and sd=2, rounding to increments of 0.2
"qrandn": tune.qrandn(10, 2, 0.2),
# Sample a integer uniformly between -9 (inclusive) and 15 (exclusive)
"randint": tune.randint(-9, 15),
# Sample a random uniformly between -21 (inclusive) and 12 (inclusive (!))
# rounding to increments of 3 (includes 12)
"qrandint": tune.qrandint(-21, 12, 3),
# Sample a integer uniformly between 1 (inclusive) and 10 (exclusive),
# while sampling in log space
"lograndint": tune.lograndint(1, 10),
# Sample a integer uniformly between 2 (inclusive) and 10 (inclusive (!)),
# while sampling in log space and rounding to increments of 2
"qlograndint": tune.qlograndint(2, 10, 2),
# Sample an option uniformly from the specified choices
"choice": tune.choice(["a", "b", "c"]),
}
Para saber mais sobre como pode personalizar domínios no seu espaço de pesquisa, visite a documentação do FLAML sobre como personalizar espaços de pesquisa.
Restrições de ajuste
A última etapa é especificar as restrições da tarefa de ajuste. Uma propriedade notável do flaml.tune
é que ele é capaz de concluir o processo de ajuste dentro de uma restrição de recursos necessária. Para fazer isso, um usuário pode fornecer restrições de recursos em termos de tempo de relógio de parede (em segundos) usando o argumento time_budget_s
ou em termos do número de tentativas usando o argumento num_samples
.
# Set a resource constraint of 60 seconds wall-clock time for the tuning.
flaml.tune.run(..., time_budget_s=60, ...)
# Set a resource constraint of 100 trials for the tuning.
flaml.tune.run(..., num_samples=100, ...)
# Use at most 60 seconds and at most 100 trials for the tuning.
flaml.tune.run(..., time_budget_s=60, num_samples=100, ...)
Para obter mais informações sobre restrições de configuração adicionais, pode visitar a documentação do FLAML para opções de afinação avançadas.
Juntando tudo
Depois de definirmos nossos critérios de ajuste, podemos executar o teste de ajuste. Para acompanhar os resultados de nossa avaliação, podemos aproveitar de registro automático MLFlow para capturar as métricas e os parâmetros de cada uma dessas execuções. Este código irá capturar todo o teste de ajuste de hiperparâmetros, destacando cada uma das combinações de hiperparâmetros que foram exploradas pelo FLAML.
import mlflow
mlflow.set_experiment("flaml_tune_experiment")
mlflow.autolog(exclusive=False)
with mlflow.start_run(nested=True, run_name="Child Run: "):
analysis = tune.run(
evaluate_config, # the function to evaluate a config
config=config_search_space, # the search space defined
metric="score",
mode="min", # the optimization mode, "min" or "max"
num_samples=-1, # the maximal number of configs to try, -1 means infinite
time_budget_s=10, # the time budget in seconds
)
Observação
Quando o registro automático do MLflow está habilitado, métricas, parâmetros e modelos devem ser registrados automaticamente à medida que o MLFlow é executado. No entanto, isso varia de acordo com o quadro. Métricas e parâmetros para modelos específicos podem não ser registrados. Por exemplo, nenhuma métrica será registrada para modelos XGBoost, LightGBM, Spark e SynapseML. Você pode saber mais sobre quais métricas e parâmetros são capturados de cada estrutura usando a documentação de registro automático MLFlow .
Sintonização paralela com o Apache Spark
A funcionalidade flaml.tune
suporta a afinação tanto do Apache Spark como dos modelos de aprendizagem de nó único. Além disso, ao ajustar alunos de nó único (por exemplo, alunos Scikit-Learn), você também pode paralelizar o ajuste para acelerar seu processo de ajuste definindo use_spark = True
. Para clusters Spark, por padrão, o FLAML iniciará uma avaliação por executor. Você também pode personalizar o número de avaliações simultâneas usando o argumento n_concurrent_trials
.
analysis = tune.run(
evaluate_config, # the function to evaluate a config
config=config_search_space, # the search space defined
metric="score",
mode="min", # the optimization mode, "min" or "max"
num_samples=-1, # the maximal number of configs to try, -1 means infinite
time_budget_s=10, # the time budget in seconds
use_spark=True,
)
print(analysis.best_trial.last_result) # the best trial's result
print(analysis.best_config) # the best config
Para saber mais sobre como paralelizar suas trilhas de ajuste, você pode visitar a documentação do FLAML para trabalhos paralelos do Spark.
Visualizar resultados
O módulo flaml.visualization
fornece funções utilitárias para plotar o processo de otimização usando Plotly. Ao aproveitar o Plotly, os usuários podem explorar interativamente os resultados de seus experimentos AutoML. Para usar essas funções de plotagem, basta fornecer seu objeto otimizado flaml.AutoML
ou flaml.tune.tune.ExperimentAnalysis
como entrada.
Pode utilizar as seguintes funções no seu bloco de notas:
-
plot_optimization_history
: Histórico de otimização de gráficos de todos os ensaios no experimento. -
plot_feature_importance
: Importância do gráfico para cada recurso no conjunto de dados. -
plot_parallel_coordinate
: Plotar as relações de parâmetros de alta dimensão no experimento. -
plot_contour
: Plotar a relação de parâmetros como gráfico de contorno no experimento. -
plot_edf
: Representar graficamente o valor objetivo EDF (função de distribuição empírica) da experiência. -
plot_timeline
: Traçar a linha do tempo do experimento. -
plot_slice
: Plotar a relação de parâmetros como gráfico de fatia em um estudo. -
plot_param_importance
: Traçar a importância do hiperparâmetro do experimento.