Teil 3 einer praktischen Anleitung, mit der Sie MMM in PYMC beherrschen können
Willkommen in Teil 3 meiner Serien über Advertising Combine Modeling (MMM), einem praktischen Leitfaden, mit dem Sie MMM beherrschen können. In dieser Serie werden wir wichtige Themen wie Modelltraining, Validierung, Kalibrierung und Budgetoptimierung behandeln, die alle mit dem leistungsfähigen Pymc-Advertising Python -Paket. Egal, ob Sie neu in MMM sind oder Ihre Fähigkeiten schärfen möchten, in dieser Serie werden Sie praktische Werkzeuge und Erkenntnisse zur Verbesserung Ihrer Marketingstrategien ausstatten.
Wenn Sie Teil 2 verpasst haben, schauen Sie es sich hier an:
In der dritten Folge der Serie werden wir abdecken, wie wir anfangen können, den Geschäftswert aus unseren Advertising -Combine -Modellen zu erhalten, indem wir die folgenden Bereiche abdecken:
- Warum möchten Unternehmen ihre Marketingbudgets optimieren?
- Wie können wir die Ausgaben unseres Advertising -Combine -Modells verwenden, um Budgets zu optimieren?
- Eine Python -Walkthrough, die zeigt, wie die Budgets mithilfe von Budgets optimiert werden können Pymc-Advertising.
Das vollständige Notizbuch finden Sie hier:
Dieses berühmte Zitat (von John Wanamaker, denke ich?!) Zeigt sowohl die Herausforderung als auch die Chancen im Advertising. Während moderne Analysen einen langen Weg zurückgelegt haben, bleibt die Herausforderung related: Verständnis, welche Teile Ihres Marketingbudgets Wert liefern.
Marketingkanäle können in Bezug auf ihre Leistung und ROI aufgrund mehrerer Faktoren erheblich variieren:
- Publikum Reichweite und Engagement – Einige Kanäle sind effektiver darin, bestimmte Aussichten zu erreichen, die an Ihre Zielgruppe ausgerichtet sind.
- Akquisitionskosten – Die Kosten für die Erreichung der Aussichten unterscheiden sich zwischen Kanälen.
- Kanalsättigung – Überbeanspruchung eines Marketingkanals kann zu sinkenden Renditen führen.
Diese Variabilität schafft die Möglichkeit, kritische Fragen zu stellen, die Ihre Marketingstrategie verändern können:
Eine effektive Budgetoptimierung ist ein kritischer Bestandteil moderner Marketingstrategien. Durch die Nutzung der Ergebnisse von MMM können Unternehmen fundierte Entscheidungen darüber treffen, wo ihre Ressourcen für maximale Auswirkungen zugewiesen werden können. MMM bietet Einblicke in die Artwork und Weise, wie verschiedene Kanäle zum Gesamtumsatz beitragen und es uns ermöglichen, Verbesserungs- und Optimierungsmöglichkeiten zu ermitteln. In den folgenden Abschnitten werden wir untersuchen, wie wir MMM -Ausgaben in umsetzbare Budgetzuweisungsstrategien umsetzen können.
2.1 Antwortkurven
Eine Antwortkurve kann die Ausgaben von MMM in eine umfassende Kind umsetzen und zeigen, wie der Verkauf auf die Ausgaben für jeden Marketingkanal reagiert.
Die Antwortkurven allein sind sehr mächtig und ermöglichen es uns, was-wenn-wenn-Szenarien auszuführen. Mit der obigen Antwortkurve als Beispiel könnten wir schätzen, wie der Vertriebsbeitrag aus sozialen Veränderungen mehr ausgibt. Wir können auch visuell sehen, wo die Rückgänge in Kraft treten. Aber was ist, wenn wir versuchen möchten, komplexere Was-wäre-wenn-Szenarien wie die Optimierung der Budgets der Kanalebene bei einem festen Gesamtbudget zu beantworten? Hier kommt die lineare Programmierung ins Spiel – lassen Sie uns dies im nächsten Abschnitt untersuchen!
2.2 Lineare Programmierung
Die lineare Programmierung ist eine Optimierungsmethode, mit der die optimale Lösung einer linearen Funktion bei einigen Einschränkungen ermittelt werden kann. Es ist ein sehr vielseitiges Software aus dem Operations -Forschungsbereich, erhält jedoch nicht oft die Anerkennung, die es verdient. Es wird verwendet, um Probleme mit den Planung, Transport- und Ressourcenzuweisung zu lösen. Wir werden untersuchen, wie wir es verwenden können, um Marketingbudgets zu optimieren.
Versuchen wir, die lineare Programmierung mit einem einfachen Budgetoptimierungsproblem zu verstehen:
- Entscheidungsvariablen (x): Dies sind die unbekannten Mengen, die wir für die optimalen Werte für die Marketingausgaben für jeden Kanal schätzen möchten.
- Objektive Funktion (z): Die lineare Gleichung, die wir versuchen, z.
- Einschränkungen: Einige Einschränkungen der Entscheidungsvariablen, die normalerweise durch lineare Ungleichheiten dargestellt werden, z.
Der Schnittpunkt aller Einschränkungen bildet eine realisierbare Area, die die Menge aller möglichen Lösungen ist, die die angegebenen Einschränkungen erfüllen. Das Ziel der linearen Programmierung ist es, den Punkt innerhalb des realisierbaren Bereichs zu finden, der die objektive Funktion optimiert.
Angesichts der Sättigungstransformation, die wir für jeden Marketingkanal anwenden, ist die Optimierung von Budgets der Kanalebene tatsächlich ein nichtlineares Programmierungsproblem. Die sequentielle Programmierung der kleinsten Quadrate (SLSQP) ist ein Algorithmus zur Lösung nichtlinearer Programmierprobleme. Es ermöglicht sowohl Gleichheit als auch Ungleichheit, dass es für unseren Anwendungsfall eine vernünftige Wahl macht.
- Gleichstellungsbeschränkungen zB Gesamtmarketingbudget beträgt 50 Mio. GBP
- Ungleichheitsbeschränkungen EG Channel Stage Budgets zwischen 5 Mio. GBP und 15 Mio. GBP
Scipy hat eine großartige Implementierung von SLSQP:
Das folgende Beispiel zeigt, wie wir es verwenden können:
from scipy.optimize import reduceconsequence = reduce(
enjoyable=objective_function, # Outline your ROI operate right here
x0=initial_guess, # Preliminary guesses for spends
bounds=bounds, # Channel-level finances constraints
constraints=constraints, # Equality and inequality constraints
technique='SLSQP'
)
print(consequence)
Das Schreiben von Budgetoptimierungscode von Grund auf ist eine komplexe, aber sehr lohnende Übung. Glücklicherweise die Pymc-Advertising Das Crew hat das starke Heben durchgeführt und bietet einen robusten Rahmen für die Ausführung von Budgetoptimierungsszenarien. Im nächsten Abschnitt werden wir untersuchen, wie ihr Paket den Budgetallokationsprozess optimieren und es für Analysten zugänglicher macht.
Jetzt verstehen wir, wie wir die Ausgabe von MMM verwenden können, um Budgets zu optimieren. Lassen Sie uns sehen, wie viel Wert wir mit unserem Modell aus dem letzten Artikel fahren können! In dieser Vorgehensweise werden wir abdecken:
- Daten simulieren
- Coaching des Modells
- Validierung des Modells
- Antwortkurven
- Budgetoptimierung
3.1 Simulation von Daten
Wir werden den Datengeneratprozess aus dem ersten Artikel wiederverwenden. Wenn Sie eine Erinnerung an den Datengeneratprozess wünschen, schauen Sie sich den ersten Artikel an, in dem wir eine detaillierte Vorgehensweise gemacht haben:
np.random.seed(10)# Set parameters for information generator
start_date = "2021-01-01"
durations = 52 * 3
channels = ("television", "social", "search")
adstock_alphas = (0.50, 0.25, 0.05)
saturation_lamdas = (1.5, 2.5, 3.5)
betas = (350, 150, 50)
spend_scalars = (10, 15, 20)
df = dg.data_generator(start_date, durations, channels, spend_scalars, adstock_alphas, saturation_lamdas, betas)
# Scale betas utilizing most gross sales worth - that is so it's akin to the fitted beta from pymc (pymc does characteristic and goal scaling utilizing MaxAbsScaler from sklearn)
betas_scaled = (
((df("tv_sales") / df("gross sales").max()) / df("tv_saturated")).imply(),
((df("social_sales") / df("gross sales").max()) / df("social_saturated")).imply(),
((df("search_sales") / df("gross sales").max()) / df("search_saturated")).imply()
)
# Calculate contributions
contributions = np.asarray((
spherical((df("tv_sales").sum() / df("gross sales").sum()), 2),
spherical((df("social_sales").sum() / df("gross sales").sum()), 2),
spherical((df("search_sales").sum() / df("gross sales").sum()), 2),
spherical((df("demand").sum() / df("gross sales").sum()), 2)
))
df(("date", "demand", "demand_proxy", "tv_spend_raw", "social_spend_raw", "search_spend_raw", "gross sales"))
3.2 Coaching des Modells
Wir werden das Modell jetzt aus dem ersten Artikel neu ausbauen. Wir werden die Trainingsdaten auf die gleiche Weise wie beim letzten Mal vorbereiten von:
- Daten in Funktionen und Ziel aufteilen.
- Erstellen von Indizes für Zug- und Auszeitscheiben.
Da der Schwerpunkt dieses Artikels jedoch nicht auf der Modellkalibrierung liegt, werden wir die Nachfrage eher als Kontrollvariable als Demand_proxy einbeziehen. Dies bedeutet, dass das Modell sehr intestine kalibriert ist – obwohl dies nicht sehr realistisch ist, werden wir einige gute Ergebnisse erzielen, um zu veranschaulichen, wie wir Budgets optimieren können.
# set date column
date_col = "date"# set consequence column
y_col = "gross sales"
# set advertising variables
channel_cols = ("tv_spend_raw",
"social_spend_raw",
"search_spend_raw")
# set management variables
control_cols = ("demand")
# create arrays
X = df((date_col) + channel_cols + control_cols)
y = df(y_col)
# set take a look at (out-of-sample) size
test_len = 8
# create practice and take a look at indexs
train_idx = slice(0, len(df) - test_len)
out_of_time_idx = slice(len(df) - test_len, len(df))
mmm_default = MMM(
adstock=GeometricAdstock(l_max=8),
saturation=LogisticSaturation(),
date_column=date_col,
channel_columns=channel_cols,
control_columns=control_cols,
)
fit_kwargs = {
"tune": 1_000,
"chains": 4,
"attracts": 1_000,
"target_accept": 0.9,
}
mmm_default.match(X(train_idx), y(train_idx), **fit_kwargs)
3.3 Validieren des Modells
Bevor wir uns mit der Optimierung befassen, überprüfen wir unser Modell intestine. Zuerst überprüfen wir die wahren Beiträge:
channels = np.array(("television", "social", "search", "demand"))true_contributions = pd.DataFrame({'Channels': channels, 'Contributions': contributions})
true_contributions= true_contributions.sort_values(by='Contributions', ascending=False).reset_index(drop=True)
true_contributions = true_contributions.model.bar(subset=('Contributions'), shade='lightblue')
true_contributions
Wie erwartet übereinstimmt unser Modell sehr eng auf die wahren Beiträge:
mmm_default.plot_waterfall_components_decomposition(figsize=(10,6));
3.4 Antwortkurven
Bevor wir uns mit der Budgetoptimierung befassen, schauen wir uns die Antwortkurven an. Es gibt zwei Möglichkeiten, die Antwortkurven in der zu betrachten Pymc-Advertising Paket:
- Direkte Antwortkurven
- Reaktionskurven für Kostenanteile
Beginnen wir mit den direkten Antwortkurven. In den direkten Antwortkurven erstellen wir einfach ein Streudiagramm von wöchentlichen Ausgaben gegen den wöchentlichen Beitrag für jeden Kanal.
Nachfolgend zeichnen wir die direkten Antwortkurven auf:
fig = mmm_default.plot_direct_contribution_curves(show_fit=True, xlim_max=1.2)
(ax.set(xlabel="spend") for ax in fig.axes);
Die Kosten für die Kosten für die Kosten für die Kosten für die Kosten sind eine various Möglichkeit, die Wirksamkeit von Kanälen zu vergleichen. Wenn δ = 1,0, bleibt der Kanalausgaben auf dem gleichen Niveau wie die Trainingsdaten. Wenn Δ = 1,2, wird der Kanalausgaben um 20percenterhöht.
Im Folgenden zeichnen wir die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten für die Kosten: Antwort:
mmm_default.plot_channel_contributions_grid(begin=0, cease=1.5, num=12, figsize=(15, 7));
Wir können auch die X-Achse ändern, um absolute Ausgabenwerte zu zeigen:
mmm_default.plot_channel_contributions_grid(begin=0, cease=1.5, num=12, absolute_xrange=True, figsize=(15, 7));
Die Antwortkurven sind großartige Werkzeuge, um die Planung zukünftiger Marketingbudgets auf Kanalebene zu erstellen. Lassen Sie sie als nächstes in die Handlung setzen und einige Budgetoptimierungsszenarien ausführen!
3.5 Budgetoptimierung
Lassen Sie uns zunächst ein paar Parameter einstellen:
- perc_change: Dies wird verwendet, um die Einschränkung um Min und Max für jeden Kanal zu setzen. Diese Einschränkung hilft uns, das Szenario realistisch zu halten, und bedeutet, dass wir die Reaktionskurven nicht zu weit außerhalb dessen extrapolieren, was das Modell im Coaching gesehen hat.
- Budget_len: Dies ist die Länge des Haushaltsszenarios in Wochen.
Wir werden zunächst die gewünschte Länge des Price range -Szenarios verwenden, um die jüngste Datenperiode auszuwählen.
perc_change = 0.20
budget_len = 12
budget_idx = slice(len(df) - test_len, len(df))
recent_period = X(budget_idx)(channel_cols)recent_period
Anschließend verwenden wir diesen letzten Zeitraum, um allgemeine Budgetbeschränkungen und Kanalbeschränkungen auf wöchentlicher Ebene festzulegen:
# set general finances constraint (to the closest £1k)
finances = spherical(recent_period.sum(axis=0).sum() / budget_len, -3)# file the present finances break up by channel
current_budget_split = spherical(recent_period.imply() / recent_period.imply().sum(), 2)
# set channel degree constraints
lower_bounds = spherical(recent_period.min(axis=0) * (1 - perc_change))
upper_bounds = spherical(recent_period.max(axis=0) * (1 + perc_change))
budget_bounds = {
channel: (lower_bounds(channel), upper_bounds(channel))
for channel in channel_cols
}
print(f'General finances constraint: {finances}')
print('Channel constraints:')
for channel, bounds in budget_bounds.objects():
print(f' {channel}: Decrease Certain = {bounds(0)}, Higher Certain = {bounds(1)}')
Jetzt ist es Zeit, unser Szenario zu betreiben! Wir füttern die relevanten Daten und Parameter und erhalten die optimalen Ausgaben zurück. Wir vergleichen es mit der Übernahme des Gesamtbudgets und der Aufteilung des aktuellen Price range -Cut up -Proportionen (die wir als tatsächliche Ausgaben bezeichnet haben).
model_granularity = "weekly"# run situation
allocation_strategy, optimization_result = mmm_default.optimize_budget(
finances=finances,
num_periods=budget_len,
budget_bounds=budget_bounds,
minimize_kwargs={
"technique": "SLSQP",
"choices": {"ftol": 1e-9, "maxiter": 5_000},
},
)
response = mmm_default.sample_response_distribution(
allocation_strategy=allocation_strategy,
time_granularity=model_granularity,
num_periods=budget_len,
noise_level=0.05,
)
# extract optimum spend
opt_spend = pd.Sequence(allocation_strategy, index=recent_period.imply().index).to_frame(identify="opt_spend")
opt_spend("avg_spend") = finances * current_budget_split
# plot precise vs optimum spend
fig, ax = plt.subplots(figsize=(9, 4))
opt_spend.plot(type='barh', ax=ax, shade=('blue', 'orange'))
plt.xlabel("Spend")
plt.ylabel("Channel")
plt.title("Precise vs Optimum Spend by Channel")
plt.legend(("Optimum Spend", "Precise Spend"))
plt.legend(("Optimum Spend", "Precise Spend"), loc='decrease proper', bbox_to_anchor=(1.5, 0.0))
plt.present()
Wir können sehen, dass der Vorschlag das Price range von digitalen Kanälen in das Fernsehen übertragen. Aber wie wirkt sich auf den Umsatz aus?
Um den Beitrag der optimalen Ausgaben zu berechnen, müssen wir den neuen Ausgabenwert professional Kanal sowie alle anderen Variablen im Modell einfügen. Wir haben nur eine Nachfrage, daher ernähren wir den Mittelwert aus der jüngsten Periode dafür. Wir werden auch den Beitrag der durchschnittlichen Ausgaben auf die gleiche Weise berechnen.
# create dataframe with optimum spend
last_date = mmm_default.X("date").max()
new_dates = pd.date_range(begin=last_date, durations=1 + budget_len, freq="W-MON")(1:)
budget_scenario_opt = pd.DataFrame({"date": new_dates,})
budget_scenario_opt("tv_spend_raw") = opt_spend("opt_spend")("tv_spend_raw")
budget_scenario_opt("social_spend_raw") = opt_spend("opt_spend")("social_spend_raw")
budget_scenario_opt("search_spend_raw") = opt_spend("opt_spend")("search_spend_raw")
budget_scenario_opt("demand") = X(budget_idx)(control_cols).imply()(0)# calculate general contribution
scenario_contrib_opt = mmm_default.sample_posterior_predictive(
X_pred=budget_scenario_opt, extend_idata=False
)
opt_contrib = scenario_contrib_opt.imply(dim="pattern").sum()("y").values
# create dataframe with avg spend
last_date = mmm_default.X("date").max()
new_dates = pd.date_range(begin=last_date, durations=1 + budget_len, freq="W-MON")(1:)
budget_scenario_avg = pd.DataFrame({"date": new_dates,})
budget_scenario_avg("tv_spend_raw") = opt_spend("avg_spend")("tv_spend_raw")
budget_scenario_avg("social_spend_raw") = opt_spend("avg_spend")("social_spend_raw")
budget_scenario_avg("search_spend_raw") = opt_spend("avg_spend")("search_spend_raw")
budget_scenario_avg("demand") = X(budget_idx)(control_cols).imply()(0)
# calculate general contribution
scenario_contrib_avg = mmm_default.sample_posterior_predictive(
X_pred=budget_scenario_avg , extend_idata=False
)
avg_contrib = scenario_contrib_avg.imply(dim="pattern").sum()("y").values
# calculate % improve in gross sales
print(f'% improve in gross sales: {spherical((opt_contrib / avg_contrib) - 1, 2)}')
Die optimalen Ausgaben verleihen uns einen Umsatzsteigerung von 6%! Das ist beeindruckend, vor allem, da wir das Gesamtbudget festgelegt haben!
Heute haben wir gesehen, wie leistungsstarke Budgetoptimierung sein kann. Es kann Organisationen mit monatlicher/vierteljährlicher/jährlicher Haushaltsplanung und Prognose helfen. Wie immer kommt der Schlüssel zu guten Empfehlungen zurück, um ein robustes, intestine kalibriertes Modell zu haben.