Kılavuz: Uplift modeli oluşturma, eğitme ve değerlendirme

Bu eğitim, Microsoft Fabric'te uçtan uca bir Veri Bilimi iş akışı örneği sunar. Uplift modellerini oluşturmayı, eğitmeyi ve değerlendirmeyi ve uplift modelleme tekniklerini uygulamayı öğrenirsiniz.

Önkoşullar

Bir defterde takip et

Not defterindeki adımları iki yoldan biriyle izleyebilirsiniz:

  • Yerleşik not defterini açın ve çalıştırın.
  • GitHub'dan not defterinizi yükleyin.

Yerleşik not defterini açma

Bu öğreticiye eşlik eden örnek Uplift modelleri not defteridir.

  1. Bu öğreticinin örnek not defterini açmak için Sisteminizi veri bilimi öğreticilerine hazırlamabaşlığındaki yönergeleri izleyin.

  2. Kodu çalıştırmaya başlamadan önce not defterine bir göl evi eklediğinizden emin olun.

Not defterini GitHub'dan içeri aktarma

AIsample - Uplift Modelling.ipynb notebook bu eğitime eşlik eder.

Bu öğreticinin eşlik eden not defterini açmak için, Not defterini çalışma alanınıza aktarmak üzeresisteminizi veri bilimi öğreticilerine hazırlama başlığındaki yönergeleri izleyin.

Kodu bu sayfadan kopyalayıp yapıştırmak isterseniz yeni bir not defteri oluşturabilirsiniz.

Kod çalıştırmaya başlamadan önce not defteri bir göl evi eklemeyi unutmayın.

1. Adım: Verileri yükleme

Dataset

Criteo AI Lab veri kümesini oluşturmuştur. Bu veri kümesinin 13M satırı vardır. Her satır bir kullanıcıyı temsil eder. Her satırda 12 özellik, bir işlem göstergesi ve ziyaret ve dönüştürme içeren iki ikili etiket vardır.

Criteo AI Lab veri kümesi yapısını gösteren ekran görüntüsü.

  • f0 - f11: özellik değerleri (yoğun, kayan değerler)
  • işlem: Bir kullanıcının işlem için rastgele hedeflenip hedeflenmediği (örneğin reklam) (1 = işlem, 0 = kontrol)
  • dönüşüm: bir kullanıcı için dönüşüm yapılıp yapılmadığı (örneğin, satın alma) (ikili, etiketli)
  • ziyaret: kullanıcının çevrimiçi mağazayı ziyaret edip etmediği (ikili, etiket)

Alıntı

Bu not defteri için kullanılan veri kümesi için şu BibTex alıntısı gerekir:

@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}
}

Bahşiş

Aşağıdaki parametreleri tanımlayarak bu not defterini farklı veri kümelerine kolayca uygulayabilirsiniz.

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

Kitaplıkları içeri aktarma

İşlemden önce, gerekli Spark ve SynapseML kütüphanelerini içe aktarın. Ayrıca Seaborn gibi bir veri görselleştirme kütüphanesi de içe aktarmalısınız; örneğin Python veri görselleştirme kütüphanesi. Veri görselleştirme kitaplığı, DataFrame'ler ve diziler üzerinde görsel kaynaklar oluşturmak için üst düzey bir arabirim sağlar. Spark, SynapseMLve Seabornhakkında daha fazla bilgi edinin.

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

Veri kümesini indirme ve lakehouse'a yükleme

Bu kod, veri kümesinin genel kullanıma açık bir sürümünü indirir ve ardından bu veri kaynağını bir Fabric lakehouse'ta depolar.

Önemli

Çalıştırmadan önce not defterine bir lakehouse'u () eklediğinizden () emin olun. Aksi takdirde bir hata oluşur.

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.")

Bu not defterinin çalışma zamanını kaydetmeye başlayın.

# Record the notebook running time
import time

ts = time.time()

MLflow deneme izlemesini ayarlama

MLflow'un günlükleme yeteneklerini genişletmek için otomatik günlükleme, eğitim sırasında bir makine öğrenmesi modelinin giriş parametrelerinin ve çıkış metriklerinin değerlerini otomatik olarak kaydeder. Bu bilgileri çalışma alanına kaydedebilirsiniz; çalışma alanındaki MLflow API'leri veya ilgili deney bu bilgilere erişebilir ve bunları görselleştirebilir. Daha fazla bilgi için Microsoft Fabric'te Apache Spark MLflow otomatik logingine bakınız.

# Set up the MLflow experiment
import mlflow

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

Not

Not defteri oturumunda Microsoft Fabric otomatik kaydetmeyi devre dışı bırakmak için mlflow.autolog()'ı çağırın ve disable=Trueayarlayın.

Göl evinden veri okuma

Lakehouse Dosyalar bölümünden ham verileri okuyun ve farklı tarih bölümleri için daha fazla sütun ekleyin. Aynı bilgiyi kullanarak bölümlenmiş bir delta tablosu oluşturun.

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

2. Adım: Keşif veri analizi

Veri kümesiyle ilgili üst düzey istatistikleri görüntülemek için display komutunu kullanın. Veri kümesinin alt kümelerini kolayca görselleştirmek için Grafik görünümlerini de gösterebilirsiniz.

display(raw_df.limit(20))

Ziyaret eden kullanıcıların yüzdesini, dönüştüren kullanıcıların yüzdesini ve dönüştüren ziyaretçilerin yüzdesini inceleyin.

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()

Analiz, 4,9% işlem grubundan (tedaviyi alan veya reklam veren kullanıcılar) çevrimiçi mağazayı ziyaret ettiğini gösterir. Kontrol grubundaki kullanıcıların yalnızca 3,8% - hiç işlem almamış, teklif edilmemiş veya reklama maruz kalmamış kullanıcılar - aynısını yaptı. Buna ek olarak, işlem grubundaki tüm kullanıcıların %0,31% dönüştü veya satın alma yaptı; ancak denetim grubundaki kullanıcıların yalnızca %0,19% dönüştü veya satın alma yaptı. Sonuç olarak, satın alma yapan ve aynı zamanda tedavi grubuna da dahil olan ziyaretçilerin dönüşüm oranı 6,36%iken, kontrol grubundaki kullanıcılar için sadece 5,07% oranı var. Bu sonuçlara bağlı olarak, tedavi potansiyel olarak ziyaret oranını yaklaşık%artırabilir ve ziyaretçilerin dönüşüm oranını yaklaşık %ile 1,3 kat artırabilir. Tedavi önemli bir gelişmeye yol açar.

3. Adım: Eğitim için modeli tanımlama

Eğitimi hazırlama ve veri kümelerini test edin

Burada, belirtilen giriş sütunlarından özellikleri ayıklamak ve bu özellikleri raw_dfadlı yeni bir sütuna çıkarmak için features DataFrame'e bir Featurize transformer uygulayacaksınız.

Sonuçta elde edilen DataFrame, dfadlı yeni bir DataFrame'de depolanır.

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()

İşlem ve denetim veri kümelerini hazırlama

Eğitim ve test veri kümelerini oluşturduktan sonra, makine öğrenmesi modellerini yükseltmeyi ölçecek şekilde eğitmek için işleme ve denetim veri kümelerini de oluşturmanız gerekir.

# 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")

Verilerinizi hazırladığınıza göre, LightGBM ile bir modeli eğitmeye devam edebilirsiniz.

Uplift modelleme: LightGBM ile T-Learner

Meta öğrenenler, LightGBM, Xgboost vb. makine öğrenmesi algoritmalarının üzerine kurulmuş bir dizi algoritmadır. Koşullu ortalama işlem etkisini, veya CATE, tahmin etmeye yardımcı olurlar. T-learner, tek bir model kullanmayan bir meta öğrenicidir. Bunun yerine, T-learner işlem değişkeni başına bir model kullanır. Bu nedenle iki model geliştirilir ve meta-öğrenene T-learner olarak atıfta bulunuruz. T-learner, tedavi etkisini tamamen atma sorununun üstesinden gelmek için birden çok makine öğrenmesi modeli kullanır ve öğrenme sürecini önce bu etkiyi dikkate alacak şekilde bölmeye zorlar.

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
     

Tahmin için test veri kümesini kullanma

Burada, treatment_model test veri kümesini dönüştürmek için daha önce tanımlanan control_model ve test_dfkullanırsınız. Ardından tahmin edilen yukarı kaldırmayı hesaplarsınız. Tahmin edilen yukarı kaldırmayı, tahmin edilen tedavi sonucu ile tahmin edilen kontrol sonucu arasındaki fark olarak tanımlarsınız. Bu tahmin edilen yükseltme farkı ne kadar büyük olursa, bir birey veya alt grup üzerinde tedavinin (örneğin reklam) etkinliği de o kadar fazla olur.

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))

Model değerlendirmesi gerçekleştirme

Her birey için gerçek yükselmeyi gözlemleyemeyeceğiniz için, artışı bir grup kişi üzerinde ölçebilirsiniz. Nüfus genelinde gerçek, birikimli artışı gösteren bir Yükselme Eğrisi kullanın.

Normalleştirilmiş bir yukarı kaldırma modeli eğrisini ve rastgele işlemeyi gösteren grafiğin ekran görüntüsü.

x ekseni, işlem için seçilen popülasyonun oranını temsil eder. 0 değeri, hiçbir tedavi grubuna işaret etmez - kimsenin tedaviye maruz kaldığı veya tedavinin sunulduğu belirtilmez. 1 değeri, tam bir tedavi grubunu işaret eder - herkes tedaviye tabi tutuluyor veya tedavi sunuluyor. y ekseni, yukarı kaldırma ölçüsünü gösterir. Amaç, tedavi grubunun boyutunu veya tedavinin sunulacağı veya kullanıma sunulacağı popülasyonun yüzdesini bulmaktır (örneğin, reklam). Bu yaklaşım, sonucu iyileştirmek için hedef seçimi iyileştirir.

İlk olarak, test DataFrame sırasını tahmin edilen yukarı kaldırmaya göre derecelendirin. Tahmin edilen yukarı kaldırma, tahmin edilen tedavi sonucu ile tahmin edilen kontrol sonucu arasındaki farktır.

# 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))

Ardından, hem tedavi hem de kontrol gruplarındaki ziyaretlerin kümülatif yüzdesini hesaplayın.

# 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))

Son olarak, her yüzdede, tedavi ve kontrol grupları arasındaki ziyaretlerin kümülatif yüzdeleri arasındaki fark olarak grubun artışını hesaplayın.

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

Şimdi test veri kümesi tahmini için yükseltme eğrisini çizin. Çizim yapmadan önce PySpark DataFrame'i Pandas DataFrame'e dönüştürün.

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("Proportion 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")

Normalleştirilmiş bir yukarı kaldırma modeli eğrisini ve rastgele işlemeyi gösteren grafiğin ekran görüntüsü.

Hem analiz hem de yukarı kaldırma eğrisi, tahmine göre dereceli olarak ilk 20% popülasyonunun, tedaviyi alırsa büyük bir kazanç elde edeceğini göstermektedir. Bu bulgu, nüfusun en iyi 20% ikna edilebilir grubu temsil ettiğini ifade eder. İstenen tedavi grubu büyüklüğü için kesme puanını 20%olarak ayarlayın, böylece en büyük etkiyi elde eden hedef müşterileri belirleyin.

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}
)

Adım 4: Son ML modelini kaydedin

Tedavi ve kontrol grupları için tüm deneyleri takip etmek ve kaydetmek için MLflow kullanın. Bu takip ve kayıt süreci, ilgili parametreleri, metrikleri ve modelleri içerir. Bu bilgiyi çalışma alanında deney adı altında kaydederek daha sonra kullanın.

# 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()

Denemelerinizi görüntülemek için:

  1. Sol panelde çalışma alanınızı seçin.
  2. Bu durumda deney adını, yani aisample-upliftmodelling, bulun ve seçin.

Aisample uplift modelleme deneme sonuçlarını gösteren ekran görüntüsü.

5. Adım: Tahmin sonuçlarını kaydetme

Microsoft Fabric, herhangi bir hesaplama motorunda toplu puanlamayı destekleyen ölçeklenebilir bir fonksiyon olan PREDICT'i sunar. Müşterilerin makine öğrenmesi modellerini kullanıma hazır hale getirmesini sağlar. Kullanıcılar, belirli bir model için doğrudan bir not defterinden veya öğe sayfasından toplu tahminler oluşturabilir. PREDICT ve Microsoft Fabric'te nasıl kullanılacağı hakkında daha fazla bilgi edinmek için Makine öğrenimi modeli puanlama ile PREDICT bölümüne bakınız.

# 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.")