Mmm.allocate_budget_to_maximize_response generating weird decimal places results

Hi there, I am trying to utilize the budget allocation function using pymc-marketing version 0.7.0…
I put the total budget as 5 (representing 5 Million) for 2 weeks… Below are my code… The result came out to weird decimal points and did not add up to 5… What could be happening here?
Thanks in advanced!

total_budget = 5
# Define your channels
channels = spend_cols
# The initial split per channel
budget_per_channel = total_budget / len(channels)
# Initial budget per channel as dictionary.
initial_budget_dict = {channel: budget_per_channel for channel in channels}
# bounds for each channel
budget_bounds = {'spend_youtube': [0.5, 2],
                'spend_linkedin': [0.5, 1],
                'spend_ctv': [0.01, 1],
                'spend_facebook': [0.01, 1],
                'spend_other_display_prospecting': [0.01, 1],
                'spend_nonbrand_bing_low': [0.01, 1],
                'spend_nonbrand_google_low': [0.01, 1],
                'spend_pinterest': [0.01, 1],
                'spend_radio': [0.01, 2],
                'spend_dm': [1, 4]
                 }


response = mmm.allocate_budget_to_maximize_response(
    budget=total_budget,
    num_days=2,
    time_granularity="weekly",
    budget_bounds=budget_bounds,
)


budgetalloc=mmm.optimal_allocation_dict
budgetalloc

The result looked like this…
Screenshot 2024-07-23 at 10.19.12 AM

Hi @JenniferSiwu ,
I am facing a similar issue as well and then I started to retest my model and question the rsq of my model. I would love help here as well.

Hi @JenniferSiwu

You need to rescale the values in your mmm.optimal_allocation_dict multiplying by mmm.channel_transformer["scaler"].scale_. See the example below:

allocate_spend = (
                  np.array(list(mmm.optimal_allocation_dict.values()))
                  * mmm.channel_transformer["scaler"].scale_
              )

Hope that helps!

2 Likes

Hi @Alfredo_Delgado
Thanks for the update - I tried this as I was also facing a similar issue but even after rescaling the the budget splits don’t add up to the total (500K).

Here’s the code and output that I am getting. Are you facing something similar @JenniferSiwu
total_budget = 500000

channels = [‘Google_Spend’,‘Bing_Spend’,‘ASA_Spend’,‘Facebook_Spend’,‘TikTok_Spend’,‘Taboola_Spend’]

budget_per_channel = total_budget / len(channels)

initial_budget_dict = {channel: budget_per_channel for channel in channels}
response = mmm.allocate_budget_to_maximize_response(
budget=total_budget,
num_days=4,
time_granularity=“weekly”

)

allocate_spend = (
np.array(list(mmm.optimal_allocation_dict.values()))
* mmm.channel_transformer[“scaler”].scale_
)

Output:
array([148084.16420463, 12016.41390591, 2857.62798331, 58788.11413278,
7317.07505723, 6778.25850656])
The sum is 235841.6537904233

Thank you so much! Appreciate your help!

Hi @YT19 That means you do not need to spend 500k in the following 4 weeks to maximize your response and only less than 250k is enough :slight_smile:

1 Like

thanks so much @Alfredo_Delgado ! this works :smiling_face:

1 Like

hey both @Alfredo_Delgado @YT19 , I’m running into another issue with the budget_bounds… it seems like the minimizer isn’t really taking into consideration the lower bound constraints… I put minimum constraint at 100K and the optimizer still outputted ~1K for some channels… Are you experiencing this as well?

Screenshot 2024-07-29 at 10.19.50 AM
Screenshot 2024-07-29 at 10.20.11 AM

As for the channel_contribution plot, is it based on the optimal spend level outputted by then optimizer?

@JenniferSiwu have you looked at the saturation curves? Maybe your bounds are too high and are above the inflection and saturation points.

Also, the response vs spent chart looks odd. Small spend gets a high contribution, and high spend gets small contribution so I suspect something is wrong in your model

Hi @Alfredo_Delgado
I write here the same message as in pymc-marketing github

My problem is the same but I am sure that it is not the answer :slight_smile: if there will be not point to spend more then if I increase the hole budget, the budget allocation should not increase. In fact - it does.
Second thing - example notebook suggest it should allocate hole budget as the next step is limiting the budget based on saturation curves to show that it almost doesn’t affect outcome.
Finally and the most important, in library code it is impossible to not allocate hole budget, from budget_allocation.py

constraints = {"type": "eq", "fun": lambda x: np.sum(x) - total_budget}

that means that default constraints ensure us that budget will be sum up to 1. So somewhere in between something is going on badly.

4 Likes

Hi All, I am facing the same issue. Even with lower bound constraints, I am getting less budget. Were you able to resolve this issue? @JenniferSiwu @Alfredo_Delgado

Hi there! Yeah i still have the same issue and i ended up downgrading pymc_marketing to version 0.6.0… :frowning:

We will be looking into this soon! Thanks for reporting this!

2 Likes

I just realised that the below will never be equal to the budget to allocate because the budget is scaled using the max channel transformer scaler self.channel_transformer["scaler"].scale_.max()

So if we want to get the re-scaled allocated budget from self.optimal_allocation_dict we need to use the same scaler. Note the extra .max() below

allocate_spend = (
                  np.array(list(mmm.optimal_allocation_dict.values()))
                  * mmm.channel_transformer["scaler"].scale_.max()
              )
2 Likes

Hey @Alfredo_Delgado is right.

The issue was address in the following PR → here

2 Likes

Hi Jennifer,

For 0.6.0, which function are you using for budget allocation. Can you provide snippet if possible to understand

Yes, so i used sigmoid params to compute the budget allocation because on 0.6.0, we are unable to use the mmm fitted output yet…

opt_params = mmm.compute_channel_curve_optimization_parameters_original_scale(method='sigmoid')
 response = mmm.optimize_channel_budget_for_maximum_contribution(
            method='sigmoid',
            total_budget=total_budget,
            budget_bounds=budget_bounds,
            parameters=opt_params
        )
1 Like

Got it, also to confirm how would the model understand the granularity. If the data is on weekly level and we would want to predict for 12 weeks, how would that be taken into consideration? Do you happen to have any idea on it?

Hi @cetagostini,

I modified the code to include max() at the end for optimized budget allocation. In the allocate_budget_to_maximize_response() function, I set the budget per unit (e.g., for a total budget of 50 over 10 weeks, I used 5 as the budget). However, after running the allocation, one channel gets the maximum budget while the rest are assigned the minimum values.

Could you clarify how the min and max bounds work and what values should be set to achieve a more balanced budget allocation?

Hello,

Indeed, the budget that goes into the optimizer and the bounds is per unit of time. If you set a budget of 5 dollars and you have 5 weeks, the total is 25 dollars for the 5 weeks.

If you have a budget bound the 10 for each channel, basically that channel should not spend more than 10*N weeks (days or months) based on your time unit.
@VT02