Prevedere il Successo delle Campagne di Abbigliamento: Un Approccio di Machine Learning con Regressione Logistica

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors

Immagina di lavorare per un’azienda di vendita al dettaglio di abbigliamento. L’azienda vuole ottimizzare le proprie campagne di marketing, concentrandosi sui clienti con la maggiore probabilità di acquistare abbigliamento. Invece di inviare promozioni a tutti, vorrebbero un approccio più mirato e basato sui dati. È qui che entra in gioco il machine learning, in particolare la regressione logistica.

Il Problema: Come possiamo prevedere quali clienti sono più propensi ad acquistare abbigliamento, in modo da indirizzare al meglio le nostre campagne di marketing?

La Soluzione: Un Modello di Regressione Logistica

Utilizzeremo un modello di regressione logistica per prevedere la probabilità che un cliente acquisti abbigliamento, basandoci su alcune caratteristiche del cliente.

Perchè utilizzare un modello di regressione logistica?

Abbiamo usato la regressione logistica per diverse ragioni, che la rendono particolarmente adatta al problema di prevedere la probabilità di acquisto di abbigliamento:

  1. Natura del Problema: Il nostro problema è di classificazione binaria. Vogliamo prevedere se un cliente acquisterà o non acquisterà abbigliamento. La regressione logistica è specificamente progettata per questo tipo di problemi, dove la variabile target (in questo caso, buys_clothing) ha due possibili valori (0 o 1, vero o falso, sì o no).

  2. Output Probabilistico: La regressione logistica non produce una semplice classificazione (es. “acquisterà” o “non acquisterà”), ma una probabilità di acquisto. Questo è molto utile in un contesto di business, perché ci permette di:

    • Ordinare i clienti: Possiamo ordinare i clienti in base alla loro probabilità di acquisto, concentrando le nostre risorse di marketing su quelli con la probabilità più alta.
    • Definire soglie: Possiamo definire una soglia di probabilità (es. 60%) sopra la quale consideriamo un cliente “probabile acquirente”.
    • Comprendere l’incertezza: Invece di una risposta binaria secca, otteniamo una misura di quanto siamo sicuri della nostra previsione.
  3. Interpretabilità: I coefficienti della regressione logistica sono relativamente facili da interpretare. Ci dicono come ciascuna variabile influenza la probabilità di acquisto. Ad esempio, un coefficiente positivo per store_visits indica che all’aumentare delle visite al negozio, aumenta anche la probabilità di acquisto. Questa interpretabilità è importante per capire il perché il modello fa determinate previsioni e per ricavare informazioni utili per il business.

  4. Efficienza Computazionale: La regressione logistica è un algoritmo relativamente semplice ed efficiente dal punto di vista computazionale. Questo la rende adatta a dataset di dimensioni moderate e a situazioni in cui è richiesta una rapida elaborazione.

  5. Ben consolidata e ampiamente utilizzata: La regressione logistica è una tecnica statistica ben consolidata e ampiamente utilizzata in molti campi, tra cui il marketing, la medicina e la finanza. Esistono molte risorse e librerie (come scikit-learn in Python) che ne semplificano l’implementazione.

Perché non altri modelli?

  • Regressione Lineare: La regressione lineare non è adatta perché la variabile target è binaria. La regressione lineare assumerebbe una relazione lineare tra le variabili, il che non è appropriato per una variabile che può assumere solo due valori. Inoltre, la regressione lineare potrebbe produrre previsioni al di fuori dell’intervallo [0, 1], che non hanno senso per una probabilità.
  • Altri algoritmi di classificazione (es. Support Vector Machines, Random Forest): Questi algoritmi potrebbero fornire prestazioni predittive migliori in alcuni casi, ma spesso sono meno interpretabili della regressione logistica. In situazioni in cui l’interpretabilità è importante quanto la precisione predittiva, la regressione logistica è una scelta preferibile. Inoltre, per dataset di dimensioni non eccessive, la regressione logistica può essere sufficientemente accurata e molto più efficiente in termini computazionali.
Forse potrebbe interessarti anche:  Pandas Series vs NumPy Array: La Differenza Pratica che Ogni Sviluppatore Python Deve Conoscere

In conclusione, la regressione logistica è stata scelta perché è un metodo adatto a problemi di classificazione binaria, fornisce output probabilistici interpretabili, è efficiente dal punto di vista computazionale e ben consolidato. Nel contesto specifico del nostro problema, dove l’interpretabilità e la comprensione dell’influenza delle variabili sono importanti, la regressione logistica rappresenta una buona scelta.

1. Raccolta e Generazione dei Dati (Simulazione)

Nel mondo reale, raccoglieremmo dati storici sui clienti, come:

  • Spesa in Prodotti per la Casa: Quanto un cliente spende in prodotti per la casa (potrebbe indicare il potere d’acquisto).
  • Visite al Negozio: Il numero di visite mensili al negozio (indica l’interesse verso il marchio).
  • Età: L’età del cliente.

Dato che non abbiamo dati reali a disposizione per questo esempio, li simuleremo usando Python e la libreria NumPy:

import numpy as np
import pandas as pd
np.random.seed(42) # Per garantire la riproducibilità

n_customers = 1000 # Numero di clienti

home_purchases = np.random.normal(500, 250, n_customers) # Spesa casa, distribuzione normale
store_visits = np.random.poisson(10, n_customers) # Visite, distribuzione di Poisson
age = np.random.randint(18, 70, n_customers) # Età, numeri interi casuali

Abbiamo anche bisogno di una variabile che indichi se un cliente ha effettivamente acquistato abbigliamento. Simuleremo anche questa, creando una relazione non lineare tra le caratteristiche e la probabilità di acquisto:

probability = 1 / (1 + np.exp(-(0.002 * home_purchases + 0.15 * store_visits + 0.01 * age - 3)))
clothing_purchases = np.random.binomial(1, probability) # 1 se acquista, 0 altrimenti

Infine, creiamo un DataFrame con Pandas:

data = pd.DataFrame({
    'home_purchases': home_purchases,
    'store_visits': store_visits,
    'age': age,
    'buys_clothing': clothing_purchases
})

Feature Engineering:

Creiamo una nuova feature che potrebbe essere utile: la spesa media per visita:

data['avg_purchase_per_visit'] = data['home_purchases'] / (data['store_visits'] + 1e-6) # Evita divisioni per zero

Preparazione dei Dati:

Dividiamo i dati in un set di training (per addestrare il modello) e un set di test (per valutarlo):

from sklearn.model_selection import train_test_split

X = data.drop('buys_clothing', axis=1) # Caratteristiche
y = data['buys_clothing']             # Variabile target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y) # Stratificazione

Addestramento del Modello:

Usiamo LogisticRegression da scikit-learn:

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV

param_grid = { # Ricerca iperparametri
    'C': [0.001, 0.01, 0.1, 1, 10, 100],
    'penalty': ['l1', 'l2'],
    'solver': ['liblinear', 'saga'] if 'saga' in LogisticRegression().get_params().keys() else ['liblinear']
}

model = LogisticRegression(max_iter=10000)
grid_search = GridSearchCV(model, param_grid, cv=5, scoring='roc_auc', n_jobs=-1, verbose=1)
grid_search.fit(X_train, y_train)

best_model = grid_search.best_estimator_
print("Migliori iperparametri:", grid_search.best_params_)

Valutazione del Modello:

Valutiamo le prestazioni del modello sul set di test:

from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

y_pred = best_model.predict(X_test)
y_prob = best_model.predict_proba(X_test)[:, 1]

print("\nReport di Classificazione:")
print(classification_report(y_test, y_pred))

print(f"AUC-ROC sul test set: {roc_auc_score(y_test, y_prob)}")

cm = confusion_matrix(y_test, y_pred) # Matrice di confusione
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title('Matrice di Confusione')
plt.xlabel('Predizioni')
plt.ylabel('Valori Reali')
plt.show()

# Visualizzazione importanza feature
coefficients = best_model.coef_[0]
feature_importance = pd.DataFrame({'Feature': X.columns, 'Coefficient': coefficients})
feature_importance['Absolute_Coefficient'] = abs(feature_importance['Coefficient'])
feature_importance = feature_importance.sort_values('Absolute_Coefficient', ascending=False)

plt.figure(figsize=(10, 6))
plt.bar(feature_importance['Feature'], feature_importance['Absolute_Coefficient'])
plt.xticks(rotation=45, ha='right')
plt.title('Importanza delle Feature')
plt.ylabel('Valore Assoluto del Coefficiente')
plt.tight_layout()
plt.show()

Utilizzo del Modello per Previsioni:

Forse potrebbe interessarti anche:  Una campagna email funziona davvero? Come misurare l'effetto causale con Python e DoWhy

Ora possiamo usare il modello per prevedere la probabilità di acquisto di un nuovo cliente:

def predict_clothing_purchase(home_spending, visits, customer_age):
    input_data = pd.DataFrame({
        'home_purchases': [home_spending],
        'store_visits': [visits],
        'age': [customer_age],
        'avg_purchase_per_visit': [home_spending / (visits + 1e-6)]
    })
    probability = best_model.predict_proba(input_data)[0][1]
    return probability

example_probability = predict_clothing_purchase(600, 12, 30)
print(f"\nProbabilità di acquisto abbigliamento per un cliente esempio: {example_probability:.2%}")

Il codice python completo 

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

# Seed per la riproducibilità
np.random.seed(42)

# Numero di clienti
n_customers = 1000

# Generazione dati simulati (con leggera modifica per una maggiore variabilità)
home_purchases = np.random.normal(500, 250, n_customers)  # Aumento la deviazione standard
store_visits = np.random.poisson(10, n_customers) # Distribuzione di Poisson per le visite
age = np.random.randint(18, 70, n_customers) # Età come interi

# Relazione non lineare per la probabilità di acquisto
probability = 1 / (1 + np.exp(-(0.002 * home_purchases + 0.15 * store_visits + 0.01 * age - 3)))
clothing_purchases = np.random.binomial(1, probability)

# DataFrame
data = pd.DataFrame({
    'home_purchases': home_purchases,
    'store_visits': store_visits,
    'age': age,
    'buys_clothing': clothing_purchases
})

# Feature Engineering (esempio: spesa media per visita)
data['avg_purchase_per_visit'] = data['home_purchases'] / (data['store_visits'] + 1e-6) # Evita divisioni per zero

# Definizione di X e y
X = data.drop('buys_clothing', axis=1)
y = data['buys_clothing']

# Divisione train/test con stratificazione (importante per classi sbilanciate)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

# Definizione del modello con regolarizzazione L2 e ricerca degli iperparametri
param_grid = {
    'C': [0.001, 0.01, 0.1, 1, 10, 100],  # Valori per il parametro di regolarizzazione
    'penalty': ['l1', 'l2'],           # Tipo di regolarizzazione
    'solver': ['liblinear', 'saga'] if 'saga' in LogisticRegression().get_params().keys() else ['liblinear'] # aggiunta di saga se disponibile
}

model = LogisticRegression(max_iter=10000) # aumento iterazioni per convergenza
grid_search = GridSearchCV(model, param_grid, cv=5, scoring='roc_auc', n_jobs=-1, verbose=1)
grid_search.fit(X_train, y_train)

best_model = grid_search.best_estimator_
print("Migliori iperparametri:", grid_search.best_params_)

# Valutazione del modello
y_pred = best_model.predict(X_test)
y_prob = best_model.predict_proba(X_test)[:, 1]

print("\nReport di Classificazione:")
print(classification_report(y_test, y_pred))

print(f"AUC-ROC sul test set: {roc_auc_score(y_test, y_prob)}")

# Matrice di Confusione
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title('Matrice di Confusione')
plt.xlabel('Predizioni')
plt.ylabel('Valori Reali')
plt.show()

# Importanza delle feature
coefficients = best_model.coef_[0]
feature_importance = pd.DataFrame({'Feature': X.columns, 'Coefficient': coefficients})
feature_importance['Absolute_Coefficient'] = abs(feature_importance['Coefficient'])
feature_importance = feature_importance.sort_values('Absolute_Coefficient', ascending=False)

plt.figure(figsize=(10, 6))
plt.bar(feature_importance['Feature'], feature_importance['Absolute_Coefficient'])
plt.xticks(rotation=45, ha='right')
plt.title('Importanza delle Feature')
plt.ylabel('Valore Assoluto del Coefficiente')
plt.tight_layout()
plt.show()

# Esempio di predizione
def predict_clothing_purchase(home_spending, visits, customer_age):
    input_data = pd.DataFrame({
        'home_purchases': [home_spending],
        'store_visits': [visits],
        'age': [customer_age],
        'avg_purchase_per_visit': [home_spending / (visits + 1e-6)]
    })
    probability = best_model.predict_proba(input_data)[0][1]
    return probability

example_probability = predict_clothing_purchase(600, 12, 30)
print(f"\nProbabilità di acquisto abbigliamento per un cliente esempio: {example_probability:.2%}")

Output:

risultati esercizio di machine learning

AUC-ROC sul Test Set:

AUC-ROC = 0.6597

L’Area Under the Receiver Operating Characteristic curve (AUC-ROC) misura la capacità del modello di distinguere tra le due classi. Un AUC-ROC di 0.5 indica una performance equivalente a una predizione casuale, mentre un AUC-ROC di 1 indica una performance perfetta. Un valore di 0.6597 indica una performance leggermente migliore del caso, ma non particolarmente elevata.

matrice di confusione

Matrice di Confusione:

La matrice di confusione è una tabella che riassume le prestazioni di un modello di classificazione. Le righe rappresentano le classi reali (valori veri), mentre le colonne rappresentano le classi predette dal modello. Le celle della matrice contengono il numero di istanze che appartengono a una determinata combinazione di classe reale e classe predetta.

Forse potrebbe interessarti anche:  Guida Pratica a Regex in Python: 3 Esercizi Reali su DNA, Sentiment e Sicurezza

Nel nostro caso:

        Predetto 0   Predetto 1
Reale 0    71          36
Reale 1    43          50
  • 1: Veri Negativi (VN). Il modello ha predetto correttamente la classe 0 per 71 istanze che appartenevano effettivamente alla classe 0.
  • 36: Falsi Positivi (FP). Il modello ha predetto erroneamente la classe 1 per 36 istanze che appartenevano alla classe 0.
  • 43: Falsi Negativi (FN). Il modello ha predetto erroneamente la classe 0 per 43 istanze che appartenevano alla classe 1.
  • 50: Veri Positivi (VP). Il modello ha predetto correttamente la classe 1 per 50 istanze che appartenevano effettivamente alla classe 1.

In sintesi:

I risultati mostrano che il modello ha una performance moderata. L’accuratezza e l’AUC-ROC sono solo leggermente superiori al caso, e la matrice di confusione rivela un numero significativo di falsi positivi e falsi negativi. Questo suggerisce che il modello non è molto efficace nel distinguere tra le due classi.

Importanza delle Feature

Importanza delle Feature:

Il grafico “Importanza delle Feature” mostra l’impatto relativo di ciascuna variabile sulla previsione del modello. L’altezza delle barre rappresenta il valore assoluto del coefficiente di regressione logistica per quella feature. Un coefficiente più alto (in valore assoluto) indica una maggiore influenza sulla probabilità di acquisto.

Nel nostro caso:

  • store_visits (Visite al negozio): È di gran lunga la feature più importante. Questo suggerisce che il numero di visite al negozio ha un forte impatto sulla probabilità che un cliente acquisti abbigliamento. Più visite, maggiore è la probabilità.
  • age (Età): Ha un’importanza moderata, significativamente inferiore a store_visits, ma comunque rilevante. Il segno del coefficiente (non mostrato nel grafico, ma ottenibile dal codice) indicherebbe se l’età influenza positivamente o negativamente la probabilità di acquisto.
  • avg_purchase_per_visit (Spesa media per visita): Ha un impatto relativamente basso. Questo potrebbe indicare che, una volta considerato il numero totale di visite, la spesa media per singola visita non aggiunge molte informazioni predittive.
  • home_purchases (Spesa in prodotti per la casa): È la feature meno importante. Questo suggerisce che la spesa in prodotti per la casa ha un’influenza minima sulla probabilità di acquisto di abbigliamento, almeno nel contesto di questo modello e con i dati simulati utilizzati.

 

Pubblicità