Självstudie: Skapa, träna och utvärdera en upplyftningsmodell
Den här självstudien presenterar ett komplett exempel på en Synapse Data Science-process i Microsoft Fabric. Du lär dig hur du skapar, tränar och utvärderar upplyftningsmodeller och tillämpar upplyftningsmodelleringstekniker.
Förutsättningar
Skaffa en Microsoft Fabric-prenumeration. Eller registrera dig för en kostnadsfri Microsoft Fabric-utvärderingsversion.
Logga in på Microsoft Fabric.
Använd upplevelseväxlaren längst ned till vänster på startsidan för att växla till Fabric.
- Familiaritet med Microsoft Fabric-anteckningsböcker
- Ett lakehouse för den här notebook-filen, för att lagra data i det här exemplet. Mer information finns i Lägg till ett sjöhus i din notebook-
Följ med i en anteckningsbok
Du kan följa med i en notebook på något av två sätt:
- Öppna och kör den inbyggda notebook-filen.
- Ladda upp din notebook-fil från GitHub.
Öppna den inbyggda notebook-filen
Exempelexemplet Uplift-modellering notebook-fil medföljer den här självstudien.
Om du vill öppna exempelanteckningsboken för den här självstudien följer du anvisningarna i Förbereda systemet för självstudier för datavetenskap.
Se till att bifoga ett lakehouse till anteckningsblock innan du börjar köra kod.
Importera anteckningsboken från GitHub
Den AIsample – Uplift Modeling.ipynb notebook medföljer denna handledning.
Om du vill öppna den medföljande anteckningsboken för den här handledningen följer du anvisningarna i Förbered ditt system för handledningar i datavetenskapför att importera anteckningsboken till din arbetsyta.
Du kan skapa en ny anteckningsbok om du föredrar att kopiera och klistra in koden från den här sidan.
Se till att bifoga en lakehouse till anteckningsboken innan du börjar köra kod.
Steg 1: Läs in data
Dataset
Criteo AI Lab skapade datauppsättningen. Datamängden har 13 miljoner rader. Varje rad representerar en användare. Varje rad har 12 funktioner, en behandlingsindikator och två binära etiketter som inkluderar besök och konvertering.
- f0 – f11: funktionsvärden (tät, flytande värden)
- behandling: om en användare blev slumpmässigt utvald för att involveras i behandling (till exempel reklam) (1 = behandling, 0 = kontroll)
- konvertering: om en konvertering har skett (till exempel gjort ett köp) för en användare (binär, etikett)
- besöker: om en konvertering har inträffat (till exempel gjort ett köp) för en användare (binär, etikett)
Citat
- Startsida för datauppsättning: https://ailab.criteo.com/criteo-uplift-prediction-dataset/
Den datauppsättning som används för den här notebook-filen kräver denna BibTex-källhänvisning:
@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}
}
Tips
Genom att definiera följande parametrar kan du enkelt använda den här notebook-filen på olika datauppsättningar.
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
Importera bibliotek
Innan du bearbetar måste du importera nödvändiga Spark- och SynapseML-bibliotek. Du måste också importera ett datavisualiseringsbibliotek – till exempel Seaborn, ett Python-datavisualiseringsbibliotek. Ett datavisualiseringsbibliotek tillhandahåller ett högnivågränssnitt för att skapa visuella resurser på DataFrames och matriser. Läs mer om Spark, SynapseMLoch 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
Ladda ner en datauppsättning och ladda upp till lakehouse
Den här koden laddar ner en offentligt tillgänglig version av datauppsättningen och lagrar sedan den datarersursen i en Fabric lakehouse.
Viktig
Se till att du Lägg till en lakehouse- i anteckningsboken innan du kör den. Om du inte gör det uppstår ett fel.
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.")
Börja registrera körtiden för denna notebook.
# Record the notebook running time
import time
ts = time.time()
Konfigurera MLflow-experimentspårning
För att utöka MLflow-loggningsfunktionerna samlar automatisk loggning automatiskt in värdena för indataparametrar och utdatamått för en maskininlärningsmodell under träningen. Den här informationen loggas sedan till arbetsytan, där MLflow-API:erna eller motsvarande experiment på arbetsytan kan komma åt och visualisera den. Besök den här resursen om du vill ha mer information om automatisk loggning.
# Set up the MLflow experiment
import mlflow
mlflow.set_experiment(EXPERIMENT_NAME)
mlflow.autolog(disable=True) # Disable MLflow autologging
Not
Om du vill inaktivera automatisk loggning av Microsoft Fabric i en notebook-session anropar du mlflow.autolog()
och anger disable=True
.
Läser data från lakehouse
Läs rådata från avsnittet lakehouse Files och lägg till fler kolumner för olika datumdelar. Samma information används för att skapa en partitionerad deltatabell.
raw_df = spark.read.csv(f"{DATA_FOLDER}/raw/{DATA_FILE}", header=True, inferSchema=True).cache()
Steg 2: Undersökande dataanalys
Använd kommandot display
för att visa statistik på hög nivå om datamängden. Du kan också visa diagramvyerna för att enkelt visualisera delmängder av datamängden.
display(raw_df.limit(20))
Granska procentandelen av de användare som besöker, procentandelen användare som konverterar och procentandelen besökare som konverterar.
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()
Analysen visar att 4,9% användare från behandlingsgruppen - användare som fick behandlingen eller reklam - besökte onlinebutiken. Endast 3,8% användare från kontrollgruppen - användare som aldrig fick behandlingen, eller aldrig erbjöds eller exponerades för reklam - gjorde detsamma. Dessutom 0,31% av alla användare från behandlingsgruppen konverterade eller gjorde ett köp - medan endast 0,19% av användare från kontrollgruppen gjorde det. Som ett resultat är konverteringsfrekvensen för besökare som gjorde ett köp, som också var medlemmar i behandlingsgruppen, 6,36%, jämfört med endast 5,07%** för användare av kontrollgruppen. Baserat på dessa resultat kan behandlingen potentiellt förbättra besöksfrekvensen med cirka 1%, och konverteringsgraden för besökare med cirka 1,3%. Behandlingen leder till en betydande förbättring.
Steg 3: Definiera modellen för träning
Förbereda träningen och testa datauppsättningarna
Här kan du anpassa en Featurize-transformator till raw_df
DataFrame för att extrahera funktioner från de angivna indatakolumnerna och mata ut dessa funktioner till en ny kolumn med namnet features
.
Resulterande DataFrame lagras i en ny dataram med namnet 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()
Förbereda datauppsättningarna för behandling och kontroll
När du har skapat tränings- och testdatauppsättningarna måste du också bilda datauppsättningarna för behandling och kontroll för att träna maskininlärningsmodellerna för att mäta upplyftningen.
# 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")
Nu när du har förberett dina data kan du fortsätta att träna en modell med LightGBM.
Upplyftningsmodellering: T-Learner med LightGBM
Metainlärare är en uppsättning algoritmer som bygger på maskininlärningsalgoritmer som LightGBM, Xgboost osv. De hjälper till att beräkna den villkorsstyrda genomsnittliga behandlingseffekten eller CATE-. T-learner är en metainlärare som inte använder en enda modell. I stället använder T-learner en modell per behandlingsvariabel. Därför utvecklas två modeller och vi refererar till meta-learner som T-learner. T-learner använder flera maskininlärningsmodeller för att lösa problemet med att helt ta bort behandlingen, genom att tvinga eleven att först dela på den.
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
Använda testdatauppsättningen för en förutsägelse
Här använder du treatment_model
och control_model
, som båda definierades tidigare, för att transformera test_df
testdatauppsättningen. Sedan beräknar du den förväntade upplyftningen. Du definierar den förutsagda upplyftningen som skillnaden mellan det förväntade behandlingsresultatet och det förutsagda kontrollresultatet. Ju större denna förutsagda ökningsskillnad är, desto större blir behandlingens effektivitet (till exempel reklam) på en individ eller en undergrupp.
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))
Utföra modellutvärdering
Eftersom faktisk upplyftning inte kan observeras för varje individ måste du mäta upplyftningen över en grupp individer. Du använder en upplyftningskurva som ritar den verkliga, kumulativa upplyftningen i hela populationen.
X-axeln representerar förhållandet mellan den population som valts för behandlingen. Ett värde på 0 tyder på att ingen behandlingsgrupp - ingen exponeras för, eller erbjuds, behandlingen. Ett värde på 1 tyder på en fullständig behandlingsgrupp - alla utsätts för, eller erbjuds, behandlingen. Y-axeln visar upplyftningsmåttet. Syftet är att hitta storleken på behandlingsgruppen, eller den procentandel av befolkningen som skulle erbjudas eller exponeras för behandlingen (till exempel reklam). Den här metoden optimerar målvalet för att optimera resultatet.
Rangordna först dataramens testordning efter den förutsagda upplyftningen. Den förutsagda upplyftningen är skillnaden mellan det förväntade behandlingsresultatet och det förväntade kontrollresultatet.
# 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))
Beräkna sedan den kumulativa procentandelen besök i både behandlings- och kontrollgrupperna.
# 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))
Vid varje procent beräknar du slutligen upplyftningen av gruppen som skillnaden mellan den kumulativa procentandelen besök mellan behandlings- och kontrollgrupperna.
test_ranked_df = test_ranked_df.withColumn("group_uplift", F.col("treatment_cumsum") - F.col("control_cumsum")).cache()
display(test_ranked_df.limit(20))
Rita nu upplyftkurvan för testdatasuppsättningens förutsägelse. Du måste konvertera PySpark DataFrame till en Pandas DataFrame innan du ritar.
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")
X-axeln representerar förhållandet mellan den population som valts för behandlingen. Ett värde på 0 tyder på att ingen behandlingsgrupp - ingen exponeras för, eller erbjuds, behandlingen. Ett värde på 1 tyder på en fullständig behandlingsgrupp - alla utsätts för, eller erbjuds, behandlingen. Y-axeln visar upplyftningsmåttet. Syftet är att hitta storleken på behandlingsgruppen, eller den procentandel av befolkningen som skulle erbjudas eller exponeras för behandlingen (till exempel reklam). Den här metoden optimerar målvalet för att optimera resultatet.
Rangordna först dataramens testordning efter den förutsagda upplyftningen. Den förutsagda upplyftningen är skillnaden mellan det förväntade behandlingsresultatet och det förväntade kontrollresultatet.
# 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))
Beräkna sedan den kumulativa procentandelen besök i både behandlings- och kontrollgrupperna.
# 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))
Vid varje procent beräknar du slutligen upplyftningen av gruppen som skillnaden mellan den kumulativa procentandelen besök mellan behandlings- och kontrollgrupperna.
test_ranked_df = test_ranked_df.withColumn("group_uplift", F.col("treatment_cumsum") - F.col("control_cumsum")).cache()
display(test_ranked_df.limit(20))
Rita nu upplyftningskurvan för förutsägelsen av testdatauppsättningen. Du måste konvertera PySpark DataFrame till en Pandas DataFrame innan du ritar.
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")
Analysen och upplyftningskurvan visar båda att de 20 bästa% populationen, enligt förutsägelsen, skulle ha en stor vinst om de fick behandlingen. Det innebär att de 20 främsta% i populationen representerar gruppen övertalningsbara objekt. Därför kan du sedan ange brytpoängen för den önskade storleken på behandlingsgruppen till 20%, för att identifiera målvalskunderna för den största effekten.
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}
)
Steg 4: Registrera den slutliga ML-modellen
Du använder MLflow för att spåra och logga alla experiment för både behandlings- och kontrollgrupper. Den här spårningen och loggningen omfattar motsvarande parametrar, mått och modeller. Den här informationen loggas under experimentnamnet på arbetsytan för senare användning.
# 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()
Så här visar du dina experiment:
- Välj din arbetsyta i den vänstra panelen.
- Leta upp och välj experimentnamnet, i det här fallet aisample-upliftmodelling.
Steg 5: Spara förutsägelseresultatet
Microsoft Fabric erbjuder PREDICT – en skalbar funktion som stöder batchbedömning i alla beräkningsmotorer. Det gör det möjligt för kunder att operationalisera maskininlärningsmodeller. Användare kan skapa batchförutsägelser direkt från en notebook-fil eller objektsidan för en specifik modell. Besök den här resursen om du vill veta mer om PREDICT och lära dig hur du använder PREDICT i 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.")