Determinare un punteggio di tendenza utilizzando un modello predittivo generato dall’apprendimento automatico

Query Service consente di sfruttare modelli predittivi, come i punteggi di propensione, basati sulla piattaforma di apprendimento automatico per analizzare i dati di Experience Platform.

Questa guida spiega come utilizzare Query Service per inviare dati alla piattaforma di machine learning per addestrare un modello in un blocco appunti computazionale. Il modello addestrato può essere applicato ai dati utilizzando SQL per prevedere la propensione di un cliente ad acquistare per ogni visita.

Introduzione

Poiché parte di questo processo richiede la formazione di un modello di apprendimento automatico, il presente documento presuppone una conoscenza operativa di uno o più ambienti di apprendimento automatico.

In questo esempio viene utilizzato Jupyter Notebook come ambiente di sviluppo. Sebbene siano disponibili molte opzioni, Jupyter Notebook è consigliato in quanto si tratta di un'applicazione Web open-source con requisiti di calcolo ridotti. Può essere scaricato dal sito ufficiale.

Se non lo hai già fatto, prima di continuare con questa guida segui i passaggi per connettersi Jupyter Notebook con Adobe Experience Platform Query Service.

Le librerie utilizzate in questo esempio includono:

python=3.6.7
psycopg2
sklearn
pandas
matplotlib
numpy
tqdm

Importa tabelle di analisi da Platform in Jupyter Notebook import-analytics-tables

Per generare un modello di punteggio tendenza, è necessario importare in Jupyter Notebook una proiezione dei dati di analisi memorizzati in Platform. Da un Python 3 Jupyter Notebook connesso a Query Service, i seguenti comandi importano un set di dati di comportamento del cliente da Luma, un archivio di abbigliamento fittizio. Poiché i dati di Platform vengono memorizzati utilizzando il formato Experience Data Model (XDM), è necessario generare un oggetto JSON di esempio conforme alla struttura dello schema. Per istruzioni su come generare l'oggetto JSON di esempio, vedere la documentazione.

Dashboard Jupyter Notebook con diversi comandi evidenziati.

L’output mostra una vista in forma di tabella di tutte le colonne del set di dati comportamentali di Luma all’interno del dashboard Jupyter Notebook.

L’output in forma di tabella del set di dati del comportamento cliente importato di Luma in Jupyter Notebook.

Prepara i dati per l’apprendimento automatico prepare-data-for-machine-learning

Per addestrare un modello di apprendimento automatico è necessario identificare una colonna target. Poiché la propensione all’acquisto è l’obiettivo per questo caso d’uso, la colonna analytic_action viene scelta come colonna di destinazione dai risultati Luma. Il valore productPurchase è l'indicatore di un acquisto del cliente. Anche le colonne purchase_value e purchase_num vengono rimosse in quanto sono direttamente correlate all'azione di acquisto del prodotto.

I comandi per eseguire queste azioni sono i seguenti:

#define the target label for prediction
df['target'] = (df['analytic_action'] == 'productPurchase').astype(int)
#remove columns that are dependent on the label
df.drop(['analytic_action','purchase_value'],axis=1,inplace=True)

Successivamente, i dati del set di dati Luma devono essere trasformati in rappresentazioni appropriate. Sono necessari due passaggi:

  1. Trasforma le colonne che rappresentano i numeri in colonne numeriche. Per eseguire questa operazione, convertire esplicitamente il tipo di dati in dataframe.
  2. Trasforma anche le colonne categoriche in colonne numeriche.
#convert columns that represent numbers
num_cols = ['purchase_num', 'value_cart', 'value_lifetime']
df[num_cols] = df[num_cols].apply(pd.to_numeric, errors='coerce')

Una tecnica denominata one hot encoding viene utilizzata per convertire le variabili di dati categorici da utilizzare con algoritmi di machine learning e deep learning. Ciò a sua volta migliora le previsioni e la precisione di classificazione di un modello. Utilizzare la libreria Sklearn per rappresentare ogni valore di categoria in una colonna separata.

from sklearn.preprocessing import OneHotEncoder

#get the categorical columns
cat_columns = list(set(df.columns) - set(num_cols + ['target']))

#get the dataframe with categorical columns only
df_cat = df.loc[:,cat_columns]

#initialize sklearn's OneHotEncoder
enc = OneHotEncoder(handle_unknown='ignore')

#fit the data into the encoder
enc.fit(df_cat)

#define OneHotEncoder's columns names
ohc_columns = [[c+'='+c_ for c_ in cat] for c,cat in zip(cat_columns,enc.categories_)]
ohc_columns = [item for sublist in ohc_columns for item in sublist]

#finalize the data input to the ML models
X = pd.DataFrame( np.concatenate((enc.transform(df_cat).toarray(),df[num_cols]),axis=1),
                 columns =  ohc_columns + num_cols)

#define target column
y = df['target']

I dati definiti come X sono tabulati e vengono visualizzati come segue:

Loutput tabulato di X in Jupyter Notebook.

Ora che i dati necessari per l'apprendimento automatico sono disponibili, è possibile adattarli ai modelli di apprendimento automatico preconfigurati nella libreria sklearn di Python. Logistics Regression viene utilizzato per addestrare il modello di propensione e consente di visualizzare la precisione dei dati di test. In questo caso, è di circa l'85%.

L'algoritmo Logistic Regression e il metodo di suddivisione dei test del treno, utilizzati per stimare le prestazioni degli algoritmi di machine learning, vengono importati nel blocco di codice seguente:

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.33, random_state=42)

clf = LogisticRegression(max_iter=2000, random_state=0).fit(X_train, y_train)

print("Test data accuracy: {}".format(clf.score(X_test, y_test)))

La precisione dei dati di prova è 0,8518518518518519.

Utilizzando la regressione logistica, puoi visualizzare i motivi di un acquisto e ordinare le funzioni che determinano la propensione in base alla loro importanza classificata in ordine decrescente. Le prime colonne denotano una causalità più elevata che determina il comportamento di acquisto. Queste ultime colonne indicano fattori che non determinano un comportamento di acquisto.

Il codice per visualizzare i risultati come due grafici a barre è il seguente:

from matplotlib import pyplot as plt

#get feature importance as a sorted list of columns
feature_importance = np.argsort(-clf.coef_[0])
top_10_features_purchase_names = X.columns[feature_importance[:10]]
top_10_features_purchase_values = clf.coef_[0][feature_importance[:10]]
top_10_features_not_purchase_names = X.columns[feature_importance[-10:]]
top_10_features_not_purchase_values = clf.coef_[0][feature_importance[-10:]]

#plot the figures
fig, (ax1, ax2) = plt.subplots(1, 2,figsize=(10,5))

ax1.bar(np.arange(10),top_10_features_purchase_values)
ax1.set_xticks(np.arange(10))
ax1.set_xticklabels(top_10_features_purchase_names,rotation = 90)
ax1.set_ylim([np.min(clf.coef_[0])-0.1,np.max(clf.coef_[0])+0.1])
ax1.set_title("Top 10 features to define \n a propensity to purchase")

ax2.bar(np.arange(10),top_10_features_not_purchase_values, color='#E15750')
ax2.set_xticks(np.arange(10))
ax2.set_xticklabels(top_10_features_not_purchase_names,rotation = 90)
ax2.set_ylim([np.min(clf.coef_[0])-0.1,np.max(clf.coef_[0])+0.1])
ax2.set_title("Top 10 features to define \n a propensity to NOT purchase")

plt.show()

Di seguito è riportata una visualizzazione con grafico a barre verticale dei risultati:

Visualizzazione delle 10 funzionalità principali che definiscono la propensione allacquisto.

Dal grafico a barre è possibile individuare diversi pattern. Gli argomenti POS (Point of Sale) e Call del canale come rimborso sono i fattori più importanti che determinano un comportamento di acquisto. Mentre gli argomenti della chiamata come reclami e fatture sono ruoli importanti per definire il comportamento di non acquisto. Si tratta di informazioni quantificabili e actionable che gli esperti di marketing possono sfruttare per condurre campagne di marketing volte a gestire la propensione all’acquisto di questi clienti.

Utilizzare Query Service per applicare il modello addestrato use-query-service-to-apply-trained-model

Dopo la creazione del modello addestrato, questo deve essere applicato ai dati contenuti in Experience Platform. A questo scopo, la logica della pipeline di machine learning deve essere convertita in SQL. I due componenti chiave di questa transizione sono i seguenti:

  • Innanzitutto, SQL deve sostituire il modulo Logistics Regression per ottenere la probabilità di un'etichetta di previsione. Il modello creato dalla regressione logistica ha prodotto il modello di regressione y = wX + c, dove i pesi w e l'intercetta c sono l'output del modello. Le funzionalità SQL possono essere utilizzate per moltiplicare i pesi per ottenere una probabilità.

  • In secondo luogo, è necessario incorporare in SQL anche il processo tecnico ottenuto in Python con una codifica a caldo. Nel database originale, ad esempio, è presente la colonna geo_county per memorizzare la provincia, ma la colonna viene convertita in geo_county=Bexar, geo_county=Dallas, geo_county=DeKalb. L'istruzione SQL seguente esegue la stessa trasformazione, dove w1, w2 e w3 possono essere sostituiti con i pesi appresi dal modello in Python:

SELECT  CASE WHEN geo_state = 'Bexar' THEN FLOAT(w1) ELSE 0 END AS f1,
        CASE WHEN geo_state = 'Dallas' THEN FLOAT(w2) ELSE 0 END AS f2,
        CASE WHEN geo_state = 'Bexar' THEN FLOAT(w3) ELSE 0 END AS f3,

Per le funzionalità numeriche, è possibile moltiplicare direttamente le colonne con i pesi, come illustrato nell'istruzione SQL riportata di seguito.

SELECT FLOAT(purchase_num) * FLOAT(w4) AS f4,

Una volta ottenuti i numeri, possono essere portati a una funzione sigmoidea in cui l'algoritmo di regressione logistica produce le previsioni finali. Nell'istruzione seguente, intercept è il numero dell'intercetta nella regressione.

SELECT CASE WHEN 1 / (1 + EXP(- (f1 + f2 + f3 + f4 + FLOAT(intercept)))) > 0.5 THEN 1 ELSE 0 END AS Prediction;

Un esempio end-to-end

In una situazione in cui sono presenti due colonne (c1 e c2), se c1 ha due categorie, l'algoritmo Logistic Regression viene addestrato con la seguente funzione:

y = 0.1 * "c1=category 1"+ 0.2 * "c1=category 2" +0.3 * c2+0.4

L'equivalente in SQL è il seguente:

SELECT
  CASE WHEN 1 / (1 + EXP(- (f1 + f2 + f3 + FLOAT(0.4)))) > 0.5 THEN 1 ELSE 0 END AS Prediction
FROM
  (
    SELECT
      CASE WHEN c1 = 'Cateogry 1' THEN FLOAT(0.1) ELSE 0 END AS f1,
      CASE WHEN c1 = 'Cateogry 2' THEN FLOAT(0.2) ELSE 0 END AS f2,
      FLOAT(c2) * FLOAT(0.3) AS f3
    FROM TABLE
  )

Il codice Python per automatizzare il processo di traduzione è il seguente:

def generate_lr_inference_sql(ohc_columns, num_cols, clf, db):
    features_sql = []
    category_sql_text = "case when {col} = '{val}' then float({coef}) else 0 end as f{name}"
    numerical_sql_text = "float({col}) * float({coef}) as f{name}"
    for i, (column, coef) in enumerate(zip(ohc_columns+num_cols, clf.coef_[0])):
        if i < len(ohc_columns):
            col,val = column.split('=')
            val = val.replace("'","%''%")
            sql = category_sql_text.format(col=col,val=val,coef=coef,name=i+1)
        else:
            sql = numerical_sql_text.format(col=column,coef=coef,name=i+1)
        features_sql.append(sql)
    features_sum = '+'.join(['f{}'.format(i) for i in range(1,len(features_sql)+1)])
    final_sql = '''
    select case when 1/(1 + EXP(-({features} + float({intercept})))) > 0.5 then 1 else 0 end as Prediction
    from
        (select {cols}
        from {db})
    '''.format(features=features_sum,cols=",".join(features_sql),intercept=clf.intercept_[0],db=db)
    return final_sql

Quando SQL viene utilizzato per dedurre il database, l'output è il seguente:

sql = generate_lr_inference_sql(ohc_columns, num_cols, clf, "fdu_luma_raw")
cur.execute(sql)
samples = [r for r in cur]
colnames = [desc[0] for desc in cur.description]
pd.DataFrame(samples,columns=colnames)

I risultati tabulari mostrano la propensione all'acquisto per ogni sessione del cliente con 0 che significa nessuna propensione all'acquisto e 1 che significa una propensione confermata all'acquisto.

Risultati tabulari dellinferenza del database utilizzando SQL.

Lavori sui dati campionati: Bootstrapping working-on-sampled-data

Nel caso in cui le dimensioni dei dati siano troppo grandi per consentire al computer locale di memorizzare i dati per l’apprendimento dei modelli, puoi prelevare campioni invece dei dati completi da Query Service. Per sapere quanti dati sono necessari per campionare da Query Service, puoi applicare una tecnica denominata bootstrapping. A questo proposito, l'avvio comporta l'addestramento del modello più volte con vari campioni e l'ispezione della varianza delle accuratezze del modello tra i diversi campioni. Per regolare l’esempio del modello di propensione riportato sopra, in primo luogo, incapsula l’intero flusso di lavoro di apprendimento automatico in una funzione. Il codice è il seguente:

def end_to_end_pipeline(df):

    #define the target label for prediction
    df['target'] = (df['analytic_action'] == 'productPurchase').astype(int)
    #remove columns that are dependent on the label
    df.drop(['analytic_action','purchase_value'],axis=1,inplace=True)

    num_cols = ['purchase_num','value_cart','value_lifetime']
    df[num_cols] = df[num_cols].apply(pd.to_numeric, errors='coerce')

    #get the categorical columns
    cat_columns = list(set(df.columns) - set(num_cols + ['target']))

    #get the dataframe with categorical columns only
    df_cat = df.loc[:,cat_columns]

    #initialize sklearn's One Hot Encoder
    enc = OneHotEncoder(handle_unknown='ignore')

    #fit the data into the encoder
    enc.fit(df_cat)

    #define one hot encoder's columns names
    ohc_columns = [[c+'='+c_ for c_ in cat] for c,cat in zip(cat_columns,enc.categories_)]
    ohc_columns = [item for sublist in ohc_columns for item in sublist]

    #finalize the data input to the ML models
    X = pd.DataFrame( np.concatenate((enc.transform(df_cat).toarray(),df[num_cols]),axis=1),
                     columns =  ohc_columns + num_cols)

    #define target column
    y = df['target']

    X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.33, random_state=42)

    clf = LogisticRegression(max_iter=2000,random_state=0).fit(X_train, y_train)

    return clf.score(X_test, y_test)

Questa funzione può quindi essere eseguita più volte in un ciclo, ad esempio 10 volte. La differenza rispetto al codice precedente è che ora il campione non viene estratto dall'intera tabella, ma solo da un campione di righe. Ad esempio, il codice di esempio seguente accetta solo 1000 righe. È possibile memorizzare le accuratezze di ogni iterazione.

from tqdm import tqdm

bootstrap_accuracy = []
for i in tqdm(range(100)):

    #sample data from QS
    cur.execute('''SELECT *
    FROM fdu_luma_raw
    ORDER BY random()
    LIMIT 1000
    ''')
    samples = [r for r in cur]
    colnames = [desc[0] for desc in cur.description]
    df_samples = pd.DataFrame(samples,columns=colnames)
    df_samples.fillna(0,inplace=True)

    #train the propensity model with sampled data and output its accuracy
    bootstrap_accuracy.append(end_to_end_pipeline(df_samples))

bootstrap_accuracy = np.sort(bootstrap_accuracy)

Le precisioni del modello avviato vengono quindi ordinate. Dopo di che, il decimo e il novantesimo quantile della precisione del modello diventano un intervallo di affidabilità del 95% per la precisione del modello con la dimensione del campione specificata.

Comando di stampa per visualizzare lintervallo di attendibilità del punteggio tendenza.

La figura precedente indica che se si utilizzano solo 1000 righe per addestrare i modelli, è possibile prevedere che la precisione diminuirà tra circa l’84% e l’88%. È possibile modificare la clausola LIMIT nelle query di Query Service in base alle proprie esigenze per garantire le prestazioni dei modelli.

recommendation-more-help
ccf2b369-4031-483f-af63-a93b5ae5e3fb