Compartilhar via


Tutorial: criar, treinar e avaliar um modelo de elevação

Este tutorial apresenta um exemplo de ponta a ponta de um fluxo de trabalho da Ciência de Dados do Synapse, no Microsoft Fabric. Você aprende como criar, treinar e avaliar modelos de elevação e aplicar técnicas de modelagem de elevação.

Pré-requisitos

Acompanhar em um notebook

Você pode acompanhar o processo em um Notebook de duas maneiras:

  • Abra e execute o Notebook interno na experiência de Ciência de Dados Synapse
  • Carregue seu Notebook do GitHub para a experiência de ciência de dados Synapse

Abrir o Notebook interno

O exemplo de Notebook de modelagem Uplift acompanha este tutorial. Visite Para abrir o notebook de exemplo incorporado do tutorial na experiência de Synapse Data Science:1. Vá para a página inicial de Synapse Data Science. 1. Selecione Usar um exemplo. 1. Selecione o exemplo correspondente:* Na guia padrão Fluxos de trabalho de ponta a ponta (Python), se o exemplo for para um tutorial do Python. * Na guia Fluxos de trabalho de ponta a ponta (R), se o exemplo for para um tutorial do R. * Na guia Tutoriais rápidos, se o exemplo for para um tutorial rápido.1. Anexe um lakehouse ao notebook antes de começar a executar o código. para obter mais informações sobre como acessar notebooks incorporados de exemplo para tutoriais.

Para abrir o Notebook de amostra interno do tutorial na experiência de Synapse Data Science:

  1. Vá para a home page de Synapse Data Science.

  2. Selecione Usar um exemplo

  3. Selecione o exemplo correspondente:

    1. Na guia padrão de fluxos de trabalho de ponta a ponta (Python), se o exemplo for para um tutorial do Python.
    2. Na guia de fluxos de trabalho de ponta a ponta (R), se o exemplo for para um tutorial do R.
    3. Na guia Tutoriais rápidos, se o exemplo for para um tutorial rápido.
  4. Anexe um lakehouse ao notebook antes de começar a executar o código

Importar o Notebook do GitHub

O AIsample - Uplift Modeling.ipynb é o notebook que acompanha esse tutorial.

Para abrir o notebook que acompanha este tutorial, siga as instruções em Preparar seu sistema para tutoriais de ciência de dados para importar os notebooks para seu workspace.

Caso prefira copiar e colar o código dessa página, você pode criar um novo notebook.

Certifique-se de anexar um lakehouse ao notebook antes de começar a executar o código.

Etapa 1: carregar os dados

Dataset

O Criteo AI Lab criou o conjunto de dados. Esse conjunto de dados tem 13 milhões de linhas. Cada linha representa um usuário. Cada linha também inclui 12 recursos, um indicador de tratamento e dois rótulos binários que incluem conversão e visita.

Captura de tela mostrando a estrutura do conjunto de dados do Criteo AI Lab.

  • f0 - f11: valores de recursos (valores densos e flutuantes)
  • tratamento: se um usuário foi ou não aleatoriamente alvo de tratamento (por exemplo, publicidade) (1 = tratamento, 0 = controle)
  • conversão: se ocorreu uma conversão (por exemplo, fez uma compra) para um usuário (binário, rótulo)
  • visita: se ocorreu uma conversão (por exemplo, fez uma compra) para um usuário (binário, rótulo)

Citação

O conjunto de dados usado para esse notebook requer esta citação BibTex:

@inproceedings{Diemert2018,
author = {{Diemert Eustache, Betlei Artem} and Renaudin, Christophe and Massih-Reza, Amini},
title={A Large Scale Benchmark for Uplift Modeling},
publisher = {ACM},
booktitle = {Proceedings of the AdKDD and TargetAd Workshop, KDD, London,United Kingdom, August, 20, 2018},
year = {2018}
}

Dica

Ao definir os parâmetros a seguir, você pode aplicar esse notebook em diferentes conjuntos de dados com facilidade.

IS_CUSTOM_DATA = False  # If True, the user must upload the dataset manually
DATA_FOLDER = "Files/uplift-modelling"
DATA_FILE = "criteo-research-uplift-v2.1.csv"

# Data schema
FEATURE_COLUMNS = [f"f{i}" for i in range(12)]
TREATMENT_COLUMN = "treatment"
LABEL_COLUMN = "visit"

EXPERIMENT_NAME = "aisample-upliftmodelling"  # MLflow experiment name

Importar bibliotecas

Antes do processamento, você precisa importar as bibliotecas necessárias: Spark e do SynapseML. Você também deve importar uma biblioteca de visualização de dados, por exemplo, Seaborn, uma biblioteca de visualização de dados do Python. Uma biblioteca de visualização de dados fornece uma interface de alto nível para criar recursos visuais em dataframes e matrizes. Saiba mais sobre Spark, SynapseML e Seaborn.

import os
import gzip

import pyspark.sql.functions as F
from pyspark.sql.window import Window
from pyspark.sql.types import *

import numpy as np
import pandas as pd

import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.style as style
import seaborn as sns

%matplotlib inline

from synapse.ml.featurize import Featurize
from synapse.ml.core.spark import FluentAPI
from synapse.ml.lightgbm import *
from synapse.ml.train import ComputeModelStatistics

import mlflow

Fazer o download do conjunto de dados e fazer o upload para o lakehouse

Esse código faz o download de uma versão disponível publicamente do conjunto de dados e, em seguida, armazena esse recurso de dados em um lakehouse do Fabric.

Importante

Certifique-se de Adicionar um lakehouse ao notebook antes de executá-lo. Se você não fizer isso, receberá um erro.

if not IS_CUSTOM_DATA:
    # Download demo data files into lakehouse if not exist
    import os, requests

    remote_url = "http://go.criteo.net/criteo-research-uplift-v2.1.csv.gz"
    download_file = "criteo-research-uplift-v2.1.csv.gz"
    download_path = f"/lakehouse/default/{DATA_FOLDER}/raw"

    if not os.path.exists("/lakehouse/default"):
        raise FileNotFoundError("Default lakehouse not found, please add a lakehouse and restart the session.")
    os.makedirs(download_path, exist_ok=True)
    if not os.path.exists(f"{download_path}/{DATA_FILE}"):
        r = requests.get(f"{remote_url}", timeout=30)
        with open(f"{download_path}/{download_file}", "wb") as f:
            f.write(r.content)
        with gzip.open(f"{download_path}/{download_file}", "rb") as fin:
            with open(f"{download_path}/{DATA_FILE}", "wb") as fout:
                fout.write(fin.read())
    print("Downloaded demo data files into lakehouse.")

Comece a gravar o runtime desse notebook.

# Record the notebook running time
import time

ts = time.time()

Configurar o acompanhamento de experimentos do MLflow

Para ampliar as capacidades de registro em log do MLflow, o registro automático em log captura automaticamente os valores dos parâmetros de entrada e as métricas de saída de um modelo de machine learning à medida que ele está sendo treinado. Essas informações são, então, registradas no log do espaço de trabalho, onde você pode acessá-lo e visualizá-lo usando as APIs do MLflow ou o experimento correspondente no espaço de trabalho. Visite este recurso para obter mais informações sobre o log automático.

# Set up the MLflow experiment
import mlflow

mlflow.set_experiment(EXPERIMENT_NAME)
mlflow.autolog(disable=True)  # Disable MLflow autologging

Observação

Para desabilitar o log automático do Microsoft Fabric em uma sessão do notebook, chame mlflow.autolog() e configure disable=True.

Ler dados do lakehouse

Leia os dados brutos da seção Arquivos do lakehouse e adicione mais colunas para diferentes partes da data. As mesmas informações são usadas para criar uma tabela delta particionada.

raw_df = spark.read.csv(f"{DATA_FOLDER}/raw/{DATA_FILE}", header=True, inferSchema=True).cache()

Etapa 2: análise exploratória de dados

Use o comando display para exibir suas estatísticas de alto nível do conjunto de dados. Também é possível mostrar as exibições de Gráficos para visualizar com facilidade os subconjuntos do conjunto de dados.

display(raw_df.limit(20))

Examine a porcentagem dos usuários que visitam, a porcentagem de usuários que convertem e a porcentagem dos visitantes que convertem.

raw_df.select(
    F.mean("visit").alias("Percentage of users that visit"),
    F.mean("conversion").alias("Percentage of users that convert"),
    (F.sum("conversion") / F.sum("visit")).alias("Percentage of visitors that convert"),
).show()

A análise indica que 4,9% dos usuários do grupo de tratamento (usuários que receberam o tratamento ou publicidade) visitaram a loja online. Apenas 3,8% dos usuários do grupo de controle (usuários que nunca receberam o tratamento ou nunca receberam a oferta ou foram expostos à publicidade) fizeram o mesmo. Além disso, 0,31% de todos os usuários do grupo de tratamento converteram ou fizeram uma compra, enquanto que apenas 0,19% dos usuários do grupo de controle o fizeram. Como resultado, a taxa de conversão dos visitantes que fizeram uma compra, que também eram membros do grupo de tratamento, é de 6,36%, em comparação com apenas 5,07%** para os usuários do grupo de controle. Com base nesses resultados, o tratamento pode potencialmente melhorar a taxa de visitas em cerca de 1% e a taxa de conversão de visitantes em cerca de 1,3%. O tratamento leva a uma melhora significativa.

Etapa 3: definir o modelo para treinamento

Preparar os conjuntos de dados de treinamento e teste

Aqui, você ajusta um transformador Featurize para o DataFrame raw_df para extrair recursos das colunas de entrada especificadas e enviá-los para uma nova coluna denominada features.

O DataFrame resultante é armazenado em um novo DataFrame denominado df.

transformer = Featurize().setOutputCol("features").setInputCols(FEATURE_COLUMNS).fit(raw_df)
df = transformer.transform(raw_df)
# Split the DataFrame into training and test sets, with a 80/20 ratio and a seed of 42
train_df, test_df = df.randomSplit([0.8, 0.2], seed=42)

# Print the training and test dataset sizes
print("Size of train dataset: %d" % train_df.count())
print("Size of test dataset: %d" % test_df.count())

# Group the training dataset by the treatment column, and count the number of occurrences of each value
train_df.groupby(TREATMENT_COLUMN).count().show()

Preparar os conjuntos de dados de tratamento e controle

Depois de criar os conjuntos de dados de treinamento e teste, você também deve formar os conjuntos de dados de tratamento e controle para treinar os modelos de machine learning para medir o aumento.

# Extract the treatment and control DataFrames
treatment_train_df = train_df.where(f"{TREATMENT_COLUMN} > 0")
control_train_df = train_df.where(f"{TREATMENT_COLUMN} = 0")

Agora que você preparou seus dados, você pode proceder para treinar um modelo com LightGBM.

Modelagem de Elevação: T-Learner com LightGB

Metas-aprendizado são um conjunto de algoritmos construídos em cima de algoritmos de aprendizado de máquina, como LightGBM, Xgboost etc. Eles ajudam a estimar o efeito médio condicional do tratamento, ou CATE. T-learner é um meta-aprendizado que não usa um único modelo. Em vez disso, o T-learner usa um modelo por variável de tratamento. Portanto, dois modelos são desenvolvidos e nos referimos à meta-aprendizado como T-learner. O T-learner usa vários modelos de machine learning para superar o problema de descartar totalmente o tratamento, forçando o learner a primeiro dividir nele.

mlflow.autolog(exclusive=False)
classifier = (
    LightGBMClassifier(dataTransferMode="bulk")
    .setFeaturesCol("features")  # Set the column name for features
    .setNumLeaves(10)  # Set the number of leaves in each decision tree
    .setNumIterations(100)  # Set the number of boosting iterations
    .setObjective("binary")  # Set the objective function for binary classification
    .setLabelCol(LABEL_COLUMN)  # Set the column name for the label
)

# Start a new MLflow run with the name "uplift"
active_run = mlflow.start_run(run_name="uplift")

# Start a new nested MLflow run with the name "treatment"
with mlflow.start_run(run_name="treatment", nested=True) as treatment_run:
    treatment_run_id = treatment_run.info.run_id  # Get the ID of the treatment run
    treatment_model = classifier.fit(treatment_train_df)  # Fit the classifier on the treatment training data

# Start a new nested MLflow run with the name "control"
with mlflow.start_run(run_name="control", nested=True) as control_run:
    control_run_id = control_run.info.run_id  # Get the ID of the control run
    control_model = classifier.fit(control_train_df)  # Fit the classifier on the control training data
     

Usar o conjunto de dados de teste para uma previsão

Aqui, você usa o treatment_model e control_model, ambos definidos anteriormente, para transformar o conjunto de dados de teste test_df. Em seguida, você calcula o aumento previsto. Você define o aumento previsto como a diferença entre o resultado do tratamento previsto e o resultado de controle previsto. Quanto maior for essa diferença de aumento previsto, maior a eficácia do tratamento (por exemplo, publicidade) em um indivíduo ou subgrupo.

getPred = F.udf(lambda v: float(v[1]), FloatType())

# Cache the resulting DataFrame for easier access
test_pred_df = (
    test_df.mlTransform(treatment_model)
    .withColumn("treatment_pred", getPred("probability"))
    .drop("rawPrediction", "probability", "prediction")
    .mlTransform(control_model)
    .withColumn("control_pred", getPred("probability"))
    .drop("rawPrediction", "probability", "prediction")
    .withColumn("pred_uplift", F.col("treatment_pred") - F.col("control_pred"))
    .select(TREATMENT_COLUMN, LABEL_COLUMN, "treatment_pred", "control_pred", "pred_uplift")
    .cache()
)

# Display the first twenty rows of the resulting DataFrame
display(test_pred_df.limit(20))

Realizar avaliação do modelo

Como o aumento real não pode ser observado para cada indivíduo, meça o aumento em um grupo de indivíduos. Use uma curva de aumento que represente em gráfico o aumento cumulativo e real na população.

Captura de tela de um gráfico mostrando uma curva normalizada do modelo de aumento versus o tratamento aleatório.

O eixo x representa a razão da população selecionada para o tratamento. Um valor de 0 sugere nenhum grupo de tratamento: ninguém recebe a oferta nem é exposto ao tratamento. Um valor de 1 sugere um grupo de tratamento completo: todo mundo recebeu a oferta e foi exposto ao tratamento. O eixo y mostra a medida de aumento. O objetivo é descobrir o tamanho do grupo de tratamento, ou a porcentagem da população que receberia a oferta ou seria exposta ao tratamento (por exemplo, publicidade). Essa abordagem otimiza a seleção do alvo para otimizar o resultado.

Primeiro, classifique a ordem dos testes do DataFrame pelo aumento previsto. O aumento previsto é a diferença entre o resultado do tratamento previsto e o resultado de controle previsto.

# Compute the percentage rank of the predicted uplift values in descending order, and display the top twenty rows
test_ranked_df = test_pred_df.withColumn("percent_rank", F.percent_rank().over(Window.orderBy(F.desc("pred_uplift"))))

display(test_ranked_df.limit(20))

Em seguida, calcule a porcentagem cumulativa de visitas nos dois grupos: tratamento e controle.

# Calculate the number of control and treatment samples
C = test_ranked_df.where(f"{TREATMENT_COLUMN} == 0").count()
T = test_ranked_df.where(f"{TREATMENT_COLUMN} != 0").count()

# Add columns to the DataFrame to calculate the control and treatment cumulative sum
test_ranked_df = (
    test_ranked_df.withColumn(
        "control_label",
        F.when(F.col(TREATMENT_COLUMN) == 0, F.col(LABEL_COLUMN)).otherwise(0),
    )
    .withColumn(
        "treatment_label",
        F.when(F.col(TREATMENT_COLUMN) != 0, F.col(LABEL_COLUMN)).otherwise(0),
    )
    .withColumn(
        "control_cumsum",
        F.sum("control_label").over(Window.orderBy("percent_rank")) / C,
    )
    .withColumn(
        "treatment_cumsum",
        F.sum("treatment_label").over(Window.orderBy("percent_rank")) / T,
    )
)

# Display the first 20 rows of the dataframe
display(test_ranked_df.limit(20))

Finalmente, em cada porcentagem, calcule o aumento do grupo como a diferença entre a porcentagem acumulada de visitas entre os grupos de tratamento e controle.

test_ranked_df = test_ranked_df.withColumn("group_uplift", F.col("treatment_cumsum") - F.col("control_cumsum")).cache()
display(test_ranked_df.limit(20))

Agora, trace a curva de aumento na previsão do conjunto de dados de teste. Você precisa converter o DataFrame do PySpark em DataFrame do Pandas antes de criar um gráfico.

def uplift_plot(uplift_df):
    """
    Plot the uplift curve
    """
    gain_x = uplift_df.percent_rank
    gain_y = uplift_df.group_uplift
    # Plot the data
    fig = plt.figure(figsize=(10, 6))
    mpl.rcParams["font.size"] = 8

    ax = plt.plot(gain_x, gain_y, color="#2077B4", label="Normalized Uplift Model")

    plt.plot(
        [0, gain_x.max()],
        [0, gain_y.max()],
        "--",
        color="tab:orange",
        label="Random Treatment",
    )
    plt.legend()
    plt.xlabel("Porportion Targeted")
    plt.ylabel("Uplift")
    plt.grid()

    return fig, ax


test_ranked_pd_df = test_ranked_df.select(["pred_uplift", "percent_rank", "group_uplift"]).toPandas()
fig, ax = uplift_plot(test_ranked_pd_df)

mlflow.log_figure(fig, "UpliftCurve.png")

Captura de tela de um gráfico mostrando uma curva normalizada do modelo de aumento versus o tratamento aleatório.

O eixo x representa a razão da população selecionada para o tratamento. Um valor de 0 sugere nenhum grupo de tratamento: ninguém recebe a oferta nem é exposto ao tratamento. Um valor de 1 sugere um grupo de tratamento completo: todo mundo recebeu a oferta e foi exposto ao tratamento. O eixo y mostra a medida de aumento. O objetivo é descobrir o tamanho do grupo de tratamento, ou a porcentagem da população que receberia a oferta ou seria exposta ao tratamento (por exemplo, publicidade). Essa abordagem otimiza a seleção do alvo para otimizar o resultado.

Primeiro, classifique a ordem dos testes do DataFrame pelo aumento previsto. O aumento previsto é a diferença entre o resultado do tratamento previsto e o resultado de controle previsto.

# Compute the percentage rank of the predicted uplift values in descending order, and display the top twenty rows
test_ranked_df = test_pred_df.withColumn("percent_rank", F.percent_rank().over(Window.orderBy(F.desc("pred_uplift"))))

display(test_ranked_df.limit(20))

Em seguida, calcule a porcentagem cumulativa de visitas nos dois grupos: tratamento e controle.

# Calculate the number of control and treatment samples
C = test_ranked_df.where(f"{TREATMENT_COLUMN} == 0").count()
T = test_ranked_df.where(f"{TREATMENT_COLUMN} != 0").count()

# Add columns to the DataFrame to calculate the control and treatment cumulative sum
test_ranked_df = (
    test_ranked_df.withColumn(
        "control_label",
        F.when(F.col(TREATMENT_COLUMN) == 0, F.col(LABEL_COLUMN)).otherwise(0),
    )
    .withColumn(
        "treatment_label",
        F.when(F.col(TREATMENT_COLUMN) != 0, F.col(LABEL_COLUMN)).otherwise(0),
    )
    .withColumn(
        "control_cumsum",
        F.sum("control_label").over(Window.orderBy("percent_rank")) / C,
    )
    .withColumn(
        "treatment_cumsum",
        F.sum("treatment_label").over(Window.orderBy("percent_rank")) / T,
    )
)

# Display the first 20 rows of the dataframe
display(test_ranked_df.limit(20))

Finalmente, em cada porcentagem, calcule o aumento do grupo como a diferença entre a porcentagem acumulada de visitas entre os grupos de tratamento e controle.

test_ranked_df = test_ranked_df.withColumn("group_uplift", F.col("treatment_cumsum") - F.col("control_cumsum")).cache()
display(test_ranked_df.limit(20))

Agora, trace a curva de aumento na previsão do conjunto de dados de teste. Você precisa converter o DataFrame do PySpark em DataFrame do Pandas antes de criar um gráfico.

def uplift_plot(uplift_df):
    """
    Plot the uplift curve
    """
    gain_x = uplift_df.percent_rank
    gain_y = uplift_df.group_uplift
    # Plot the data
    fig = plt.figure(figsize=(10, 6))
    mpl.rcParams["font.size"] = 8

    ax = plt.plot(gain_x, gain_y, color="#2077B4", label="Normalized Uplift Model")

    plt.plot(
        [0, gain_x.max()],
        [0, gain_y.max()],
        "--",
        color="tab:orange",
        label="Random Treatment",
    )
    plt.legend()
    plt.xlabel("Porportion Targeted")
    plt.ylabel("Uplift")
    plt.grid()

    return fig, ax


test_ranked_pd_df = test_ranked_df.select(["pred_uplift", "percent_rank", "group_uplift"]).toPandas()
fig, ax = uplift_plot(test_ranked_pd_df)

mlflow.log_figure(fig, "UpliftCurve.png")

Captura de tela de um gráfico mostrando uma curva normalizada do modelo de aumento versus o tratamento aleatório.

A análise e a curva de aumento mostram que os 20% mais significativos da população, conforme a previsão, teriam um grande ganho se recebessem o tratamento. Isso significa que os 20% mais significativos da população representam o grupo dos que são mais facilmente persuadidos. Portanto, você pode definir a classificação limiar para o tamanho desejado do grupo de tratamento em 20%, para identificar os clientes-alvo de seleção para o maior impacto.

cutoff_percentage = 0.2
cutoff_score = test_ranked_pd_df.iloc[int(len(test_ranked_pd_df) * cutoff_percentage)][
    "pred_uplift"
]

print("Uplift scores that exceed {:.4f} map to Persuadables.".format(cutoff_score))
mlflow.log_metrics(
    {"cutoff_score": cutoff_score, "cutoff_percentage": cutoff_percentage}
)

Etapa 4: registrar o modelo final de ML

Use o MLflow para acompanhar e registrar em log todos os experimentos para os grupos de tratamento e controle. Esse acompanhamento e o registro em log incluem os parâmetros, as métricas e os modelos correspondentes. Todas essas informações são registradas em log no nome do experimento, no espaço de trabalho, para uso posterior.

# Register the model
treatment_model_uri = "runs:/{}/model".format(treatment_run_id)
mlflow.register_model(treatment_model_uri, f"{EXPERIMENT_NAME}-treatmentmodel")

control_model_uri = "runs:/{}/model".format(control_run_id)
mlflow.register_model(control_model_uri, f"{EXPERIMENT_NAME}-controlmodel")

mlflow.end_run()

Para exibir seus experimentos:

  1. No painel esquerdo, selecione seu espaço de trabalho.
  2. Encontre e selecione o nome do experimento, neste caso, aisample-upliftmodelling.

Captura de tela que mostra os resultados do experimento aisample uplift modeling.

Etapa 5: salvar os resultados da previsão

O Microsoft Fabric oferece PREDICT: uma função escalável compatível com pontuação em lote em qualquer mecanismo de computação. Permite que os clientes operacionalizem modelos de machine learning. Você pode criar previsões em lote diretamente de um notebook ou da página de um item para um modelo específico. Visite este recurso para saber mais sobre o PREDICT e como usá-lo no Microsoft Fabric.

# Load the model back
loaded_treatmentmodel = mlflow.spark.load_model(treatment_model_uri, dfs_tmpdir="Files/spark")
loaded_controlmodel = mlflow.spark.load_model(control_model_uri, dfs_tmpdir="Files/spark")

# Make predictions
batch_predictions_treatment = loaded_treatmentmodel.transform(test_df)
batch_predictions_control = loaded_controlmodel.transform(test_df)
batch_predictions_treatment.show(5)
# Save the predictions in the lakehouse
batch_predictions_treatment.write.format("delta").mode("overwrite").save(
    f"{DATA_FOLDER}/predictions/batch_predictions_treatment"
)
batch_predictions_control.write.format("delta").mode("overwrite").save(
    f"{DATA_FOLDER}/predictions/batch_predictions_control"
)
# Determine the entire runtime
print(f"Full run cost {int(time.time() - ts)} seconds.")