Come ottimizzare il tuo budget di marketing

'Optimizing your marketing budget'

È ora di raccogliere i frutti del tuo duro addestramento del modello di marketing mix!

Immagine creata con DALL-E.

I modelli di marketing mix sono uno strumento potente per comprendere l’impatto dei diversi canali di marketing sulle vendite. Costruendo un modello di marketing mix, i marketer possono quantificare il contributo di ciascun canale alle loro vendite complessive, e poi utilizzare queste informazioni per ottimizzare l’allocazione del budget.

Fino ad ora, ho scritto un’intera serie sull’costruzione di modelli di marketing mix, ma ti devo ancora un articolo su come utilizzare questi modelli per ottimizzare la spesa per i media. Oggi è il tuo giorno fortunato, perché in questo articolo ti mostrerò proprio questo!

Immagine dell'autore.

Se sei nuovo nel marketing mix modeling, puoi iniziare con il mio articolo introduttivo:

Introduzione al Marketing Mix Modeling in Python

Quali spese pubblicitarie stanno davvero guidando le tue vendite?

towardsdatascience.com

Prerequisiti

Prima di poter ottimizzare qualcosa, dobbiamo prima costruire un modello. Lo faremo molto velocemente, in modo da poter passare alla sezione principale di questo articolo il prima possibile.

I Dati

Prima di tutto, carichiamo alcuni dati. Utilizzerò lo stesso dataset dei miei vecchi articoli.

import pandas as pdfrom sklearn.model_selection import cross_val_score, TimeSeriesSplitdata = pd.read_csv(    'https://raw.githubusercontent.com/Garve/datasets/4576d323bf2b66c906d5130d686245ad205505cf/mmm.csv',    parse_dates=['Date'],    index_col='Date')X = data.drop(columns=['Sales'])y = data['Sales']

Il dataset ha questo aspetto:

Immagine dell'autore.

La logica dietro questa tabella è la seguente: immagina di lavorare in un’azienda che vende un prodotto. Puoi vedere le vendite settimanali di questo prodotto nella colonna Sales. Per aumentare queste vendite, spendi dei soldi in pubblicità, nel nostro esempio pubblicità in TV, Radio e Banner. Ora vogliamo modellare le vendite utilizzando la spesa pubblicitaria e altre variabili di controllo, come il giorno della settimana, il mese, il prezzo del prodotto, il tempo, …

Immagine dell'autore.

Il Modello

Costruire modelli complessi come XGBoost o reti neurali profonde è difficile da interpretare e ottimizzare. Ci rivolgiamo a un metodo provato che utilizza effetti di trasferimento e saturazione interpretabili per costruire invece un modello additivo generalizzato, come fatto qui:

Immagine dell'autore.

I blocchi di trasferimento e saturazione sono trasformazioni delle caratteristiche intuitive:

  • Il blocco di trasferimento modella il fatto che una spesa pubblicitaria al tempo t potrebbe ancora influenzare le vendite ai tempi t + 1, t + 2, …, oppure il contrario, che le vendite osservate al tempo t sono influenzate anche dalla spesa al tempo t – 1, t – 2, …
  • Il blocco di saturazione modella i tassi di ritorno decrescenti, ad esempio aumentando la spesa in un canale da 0 € a 100.000 € ha un grande impatto, ma cambiarla da 1.000.000.000 a 1.000.100.000 non lo ha più.

Nota: Nella grafica sono omesse le variabili di controllo. Questo è normale perché non ne abbiamo bisogno per l’ottimizzazione — in ogni caso non possiamo cambiarle come il denaro che investiamo nei nostri canali mediatici. L’unica variabile di controllo che possiamo cambiare è il prezzo, ma assumiamo che sia costante qui e che vogliamo ottimizzare solo le nostre spese pubblicitarie.

Quindi, il modello ha la forma

Immagine dell'autore.

per alcune funzioni ancora da definire sat uration e car ryover. Ad esempio, assumiamo che

Immagine dell'autore.

e

Immagine dell'autore.

β è il coefficiente di saturazione, λ la forza del carryover e ℓ la lunghezza del carryover.

Possiamo apprendere questi parametri trattandoli come iperparametri o utilizzando metodi bayesiani e trattandoli come normali parametri apprendibili. Abbiamo visto in dettaglio come ottenere questi parametri in tutti i miei ultimi articoli sul marketing mix modeling, quindi non approfondirò ulteriormente questo argomento.

Invece, assumiamo che ora abbiamo i numeri e vogliamo usarli per creare un piano di allocazione del budget pubblicitario ottimizzato.

Ottimizzazione del Budget Pubblicitario

Assumiamo che i nostri precedenti tentativi di marketing mix modeling ci abbiano lasciato con i seguenti parametri:

N = 200 # numero di osservazioni# il marketing mix modeling precedente ci ha dato questi parametritv_coef = 10000       # αtv_lags = 4           # ℓtv_carryover = 0.5    # λtv_saturation = 0.002 # βradio_coef = 8000radio_lags = 2radio_carryover = 0.2radio_saturation = 0.0001banners_coef = 14000banners_lags = 0banners_carryover = 0.2banners_saturation = 0.001

Ricostruiremo ora il modello di marketing mix in Python utilizzando numpy.

Ma perché? Abbiamo già costruito un modello usando scikit-learn o PyMC! Non possiamo riutilizzarlo?

Buona domanda! Potremmo utilizzare il nostro modello pre-addestrato e fornirlo a un algoritmo di ottimizzazione generico che cerca di trovare input di spesa pubblicitaria che massimizzano le vendite. Tuttavia, questo viene chiamato ottimizzazione black-box e ha il problema di tendere a rimanere bloccato in ottimi locali anziché trovare un ottimo globale.

Un altro problema dell’ottimizzazione black-box è che gli algoritmi di solito hanno vari parametri con cui è necessario sperimentare per trovare una soluzione buona (ma forse non ottimale). Ecco perché alcune persone dicono che questo tipo di ottimizzazione è più un’arte che una scienza.

Convessità alla Riscossa

Se possiamo formulare il nostro problema come un problema di ottimizzazione convessa, possiamo risolverlo utilizzando librerie come cvxpy che sono garantite a trovare la migliore allocazione del budget pubblicitario. Ho già utilizzato questa libreria per risolvere un altro problema di ottimizzazione qui.

Per poter utilizzare un metodo di ottimizzazione convessa, il nostro modello deve essere convesso o concavo, il che significa che mettere un segno meno davanti al modello lo rende convesso.

Immagine dell'autore.

Ad esempio, se il nostro modello è y = x ², sarebbe una funzione concava che è facile da minimizzare. y = 100 – x ² sarebbe un modello concavo che è facile da massimizzare.

Non entrerò nei dettagli oltre; sappi solo che il nostro modello è infatti una funzione concava! In un modello di saturazione con trasferimento che abbiamo creato, è sufficiente che la seconda derivata della funzione di saturazione sia negativa, quindi il modello è concavo.

Immagine dell'autore.

Tuttavia, se utilizziamo altre funzioni di saturazione come Adbudg o altre funzioni a forma di S tipiche, potrebbero non essere né concave né convesse, il che rende più difficile ottimizzarle.

Immagine dell'autore.

Ok, basta con la teoria. Ricorda solo per ora che il nostro modello è concavo, il che è ottimo perché possiamo trovare un ottimo globale, ovvero una distribuzione del budget che produce le vendite massime.

Riimplementazione del nostro modello in Numpy

Prima di tutto, definiamo alcune matrici che si occupano dell’effetto di trasferimento.

import numpy as nptv_carryover_matrix = sum([np.diag(tv_carryover**i*np.ones(N-i), k=-i) for i in range(tv_lags)])radio_carryover_matrix = sum([np.diag(radio_carryover**i*np.ones(N-i), k=-i) for i in range(radio_lags)])banners_carryover_matrix = np.eye(N)

So che è difficile da comprendere, quindi diamo un’occhiata a una di queste matrici.

Immagine dell'autore.

Questo implementa un trasferimento con una forza del 0,2 e una lunghezza di 1. Puoi vederlo se moltiplichi questa matrice con un vettore di spesa.

Immagine dell'autore.

Con questo fatto, continuiamo con la saturazione. Si tratta solo di una semplice formula che coinvolge exp, quindi nessun problema.

Possiamo scrivere:

sales = (    tv_coef * np.sum(1 - np.exp(-tv_saturation * tv_carryover_matrix @ data["TV"]))     + radio_coef * np.sum(1 - np.exp(-radio_saturation * radio_carryover_matrix @ data["Radio"]))     + banners_coef * np.sum(1 - np.exp(-banners_saturation * banners_carryover_matrix @ data["Banners"])))

Questo ci dà la somma delle vendite che provengono dai nostri sforzi di marketing poiché qui ignoriamo le variabili di controllo. Il numero è di 3.584.648,73 € e vogliamo aumentarlo ora cambiando le nostre spese pubblicitarie! Spoiler: si scopre che possiamo aumentare questo numero di circa 1,5 milioni a 5.054.070,21 €. Wow! Niente male per solo giocare con alcuni numeri.

Immagine dell'autore.

Riimplementazione del nostro modello in CVXPY

Ok, ora siamo pronti per arrivare a quella soluzione ottimale utilizzando cvxpy. Prima di tutto, definiamo le variabili, nel nostro caso una per ogni canale e ogni intervallo di tempo, quindi 3 * N = 3 * 200 = 600 variabili in totale.

Senza nulla di più, l’ottimo sarebbe impostare tutte le variabili a infinito, quindi abbiamo bisogno di alcuni vincoli. Le variabili dovrebbero essere tutte

  1. non negative e
  2. vogliamo che la somma di tutte queste 600 variabili sia minore o uguale a quanto abbiamo speso storicamente.

Quindi, vogliamo ottimizzare il modello che abbiamo implementato utilizzando le funzioni numpy, ma utilizzando il loro equivalente in cvxpy, il che significa tipicamente scrivere cp invece di np. Possiamo persino riutilizzare la matrice di carryover di prima!

import cvxpy as cporiginal_total_spends = data[["TV", "Radio", "Banners"]].sum().sum()# dichiarazione delle variabili da ottimizzare, N=200 per canaletv = cp.Variable(N)radio = cp.Variable(N)banners = cp.Variable(N)# i vincoli, spese positive e un budget totale limitatoconstraints = [    tv >= 0,    radio >= 0,    banners >= 0,    cp.sum(tv + radio + banners) <= original_total_spends,]# formulazione cvxpy, il modello assomiglia alla versione numpyproblem = cp.Problem(    cp.Maximize(        tv_coef * cp.sum(1 - cp.exp(-tv_saturation * tv_carryover_matrix @ tv)) \        + radio_coef * cp.sum(1 - cp.exp(-radio_saturation * radio_carryover_matrix @ radio)) \        + banners_coef * cp.sum(1 - cp.exp(-banners_saturation * banners_carryover_matrix @ banners))    ), # come il modello numpy, somma di tutte le vendite    constraints)

Ora possiamo risolvere questo problema di massimizzazione in tempi molto brevi tramite

problem.solve()# Output:# 5054070.207463957

Bravo! Possiamo ottenere il budget ottimale tramite tv.value, radio.value, banners.value. Puoi vedere che le spese sono abbastanza costanti per ogni settimana in ogni canale, il che potrebbe non essere così interessante come ci si aspettava. Ma l’ottimale è ottimale, quindi lo accetteremo.

Potevamo ottenere 5 milioni invece di 3,6 milioni in passato. Anche se è bello saperlo, ora non ha valore e potrebbe solo preoccupare l’azienda. Tuttavia, possiamo utilizzare questa logica ora per ottimizzare anche le future spese di marketing, ovviamente!

Ulteriori Vincoli

Ecco fatto, ora hai uno strumento di ottimizzazione di base per il budget! E la cosa buona è che puoi modellare ancora più vincoli che potrebbero provenire dall’azienda. Come esempio, l’azienda potrebbe dire che le spese totali per la radio sono abbastanza alte:

sum(radio.value)# Output: # 524290.3686626207 (= 524.290,37 €)

L’azienda vuole che sia inferiore a 300.000 €, per motivi strategici che il modello non può conoscere. Va bene, nessun problema, aggiungiamolo all’insieme di vincoli!

constraints = [    tv >= 0,    radio >= 0,    banners >= 0,    cp.sum(tv + radio + banners) <= original_total_spends,    cp.sum(radio) <= 300000 # nuovo vincolo]

Semplice come quello. Possiamo far girare nuovamente l’ottimizzazione e otteniamo vendite ottimizzate leggermente ridotte di 4.990.178,80 €. Ma se controlliamo la somma delle spese per la radio adesso

sum(radio.value)# Output: # 299999.9992275703

possiamo vedere che il vincolo aziendale è stato rispettato. E possiamo aggiungere ancora più vincoli, come ad esempio

  • la somma di due canali dovrebbe essere minore o maggiore di un certo numero, o
  • in alcune settimane non consentiamo alcuna spesa per i media.

Devi solo modellarlo utilizzando alcune somme e uguaglianze o disuguaglianze.

Conclusioni

In questo articolo abbiamo prima ripassato le formule per i modelli di marketing mix. Questo era importante perché dovevamo reimplementare i modelli. Fortunatamente, dato che i nostri modelli sono semplici e interpretabili, questo non è stato affatto un problema.

Il nostro modello aveva effettivamente un’altra grande proprietà: è concavo! In questo caso, il valore massimo delle vendite è univocamente definito e potremmo raggiungerlo tramite un’ottimizzazione convessa. Ottimizzare funzioni non-convessi o non-concave è difficile in generale e richiede un’arte che coinvolge l’aggiustamento di molti iperparametri, ecco perché non abbiamo seguito questa strada.

Come gran finale, abbiamo ottimizzato il nostro budget pubblicitario! Era ora. Abbiamo persino visto come incorporare ulteriori vincoli nel modello, come ad esempio alcune assegnazioni di budget minime o massime per alcuni canali. Utilizzando questo approccio, puoi ora ottimizzare l’allocazione futura del tuo budget pubblicitario.

Un’altra ottimizzazione di cui non abbiamo parlato è la minimizzazione del tuo budget pubblicitario sotto il vincolo che desideri effettuare una certa quantità minima di vendite, ovvero spendere il meno possibile per raggiungere comunque il tuo obiettivo. Questo è qualcosa che puoi implementare facilmente anche tu! In contrasto, prima prendevamo tutti i soldi che avevamo e facevamo il maggior numero possibile di vendite.

Spero che oggi tu abbia imparato qualcosa di nuovo, interessante e prezioso. Grazie per aver letto!

Come ultimo punto, se

  1. vuoi supportarmi nella scrittura di più contenuti su machine learning e
  2. hai intenzione di sottoscrivere comunque un abbonamento a Nisoo,

perché non farlo tramite questo link ? Mi aiuteresti molto! 😊

Per essere trasparenti, il prezzo per te non cambia, ma circa la metà delle commissioni di abbonamento vanno direttamente a me.

Grazie mille se consideri di supportarmi!

Se hai delle domande, scrivimi su LinkedIn!