Home Machine Learning Optimising Non-Linear Therapy Results in Pricing and Promotions | by Ryan O’Sullivan | Could, 2024

Optimising Non-Linear Therapy Results in Pricing and Promotions | by Ryan O’Sullivan | Could, 2024

0
Optimising Non-Linear Therapy Results in Pricing and Promotions | by Ryan O’Sullivan | Could, 2024

[ad_1]

Causal AI, exploring the combination of causal reasoning into machine studying

Picture by Ernest Ojeh on Unsplash

Welcome to my sequence on Causal AI, the place we are going to discover the combination of causal reasoning into machine studying fashions. Anticipate to discover quite a few sensible purposes throughout totally different enterprise contexts.

Within the final article we lined utilizing Double Machine Studying and Linear Programming to optimise remedy methods. This time we are going to proceed with the theme of optimisation exploring optimising non-linear remedy results in Pricing & Promotions.

For those who missed the final article on Double Machine Studying and Linear Programming, test it out right here:

This text will showcase how we will optimise non-linear remedy results in pricing (however the concepts may also be utilized throughout advertising and different domains too).

On this article I’ll show you how to perceive:

  • Why is it frequent to have non-linear remedy results in pricing?
  • What instruments from our Causal AI toolbox are appropriate for estimating non-linear remedy results?
  • How can non-linear programming be used to optimise pricing?
  • A labored case examine in Python working via how we will mix our Causal AI toolbox and non-linear programming to optimise pricing budgets.

The total pocket book could be discovered right here:

Diminishing returns

Let’s take the instance of a retailer adjusting the worth of a product. Initially decreasing the worth would possibly result in a big improve in gross sales. Nonetheless, as they proceed to decrease the worth, the rise in gross sales might begin to plateau. We name this diminishing returns. As illustrated under, the impact of diminishing returns is usually non-linear.

Person generated picture

Diminishing returns could be noticed throughout varied fields past pricing. Some frequent examples are:

  • Advertising — Rising social media spend can improve buyer acquisition, however as time goes on it turns into tougher to focus on new, untapped audiences.
  • Farming — Including fertilizer to a discipline can improve crop yield considerably initially, however this impact will in a short time begin to diminish.
  • Manufacturing — Including extra employees to a manufacturing course of will enhance efficiencies, however every further employee might contribute much less to the general output.

This makes me begin to surprise, if diminishing returns are so frequent, then which strategies from our Causal AI toolbox are able to dealing with this?

Toolbox

There are two key questions which we are going to ask to assist us establish what strategies from our Causal AI toolbox are appropriate for our Pricing drawback:

  • Can it deal with steady therapies?
  • Can it seize non-linear remedy results?

Under we will see a abstract of how appropriate every technique is:

  • Propensity rating matching (PSM) — Therapy must be binary ❌
  • Inverse-propensity rating matching (IPSM) — Therapy must be binary ❌
  • T-Learner — Therapy must be binary ❌
  • Double Machine Studying (DML) — Therapy impact is linear ❌
  • Doubly-Strong Learner (DR) — Therapy must be binary ❌
  • S-Learner — Can deal with steady therapies and non-linear relationships between the remedy and end result if an applicable machine studying algorithm (e.g. gradient boosting) is used 💚

S-Learner

The “S” in S-Learner comes from it being a “single mannequin”. An arbitrary machine studying mannequin is used to foretell the result utilizing the remedy, confounders and different covariates as options. This mannequin is then used to estimate the distinction between the potential outcomes underneath totally different remedy situations (which provides us the remedy impact).

The are a number of advantages to the S-Learner:

  • It could possibly deal with each binary and steady therapies.
  • It could possibly use any machine studying algorithm, giving us the pliability to seize non-linear relationships for each the options and remedy.

One phrase of warning: regularisation bias! Fashionable machine studying algorithms use regularisation to stop overfitting — however this may be damaging to causal issues. Take the hyper-parameter max options from gradient boosting tree strategies — in quite a few bushes, it’s doubtless that the remedy gained’t be included within the mannequin. This can dampen the impact of the remedy.

When utilizing the S-Learner, I like to recommend pondering rigorously concerning the regularisation parameters e.g. set max options to 1.0 (successfully switching off the function regularisation).

Value optimisation

Let’s say now we have quite a few merchandise and we wish to optimise their worth given a set promotional finances. For every product we practice an S-Learner (utilizing gradient boosting) with the remedy set as low cost degree and the result set as complete variety of orders. Our S-Leaners output a posh mannequin that can be utilized to estimate the impact of various low cost ranges. However how can we optimise the low cost ranges for every product?

Response Curves

Optimisation strategies akin to linear (and even non-linear) programming depend on having a transparent practical type of the response. Machine studying strategies like random forests and gradient boosting don’t give us this (not like say linear regression). Nonetheless, a response curve can translate the outputs of an S-Learner right into a complete type, exhibiting how the result responds to the remedy.

For those who can’t fairly image how we will create a response curve but, don’t fear we are going to cowl this within the Python case examine!

Michaelis-Menton equation

There are a number of equations we might use to map the S-Learner to a response curve. One in every of them is the Micaelis-Menton equation.

The Micaelis-Menton equation is usually utilized in enzyme kinetics (the examine of the charges at which enzymes catalyse chemical reactions) to explain the speed of enzymatic reactions.

Person generated picture
  • v — is the response velocity (that is our reworked response, so complete variety of orders in our pricing instance)
  • Vmax — is the utmost response velocity (we are going to name this alpha, a parameter we have to be taught)
  • Km — is the substrate focus (we are going to name this lambda, a parameter we have to be taught)
  • S — is the Michaelis fixed (that is our remedy, so low cost degree in our pricing instance)

Its ideas may also be utilized to different fields, particularly when coping with methods the place growing enter doesn’t proportionally improve output attributable to saturation components. Under we visualise how totally different values of alpha and lamda impact the curve:

def michaelis_menten(x, alpha, lam):
return alpha * x / (lam + x)
Person generated picture

As soon as now we have our response curves, subsequent we will take into consideration optimisation. The Micaelis-Menton provides us a non-linear operate. Due to this fact non-linear programming is an applicable selection.

Non-linear programming

We lined linear programming within the my final article. Non-linear programing is analogous however the goal operate and/or constraints are non-linear in nature.

Sequential Least Squares Programming (SLSQP) is an algorithm used for fixing non-linear programming issues. It permits for each equality and inequality constraints making it a good choice for our use case.

  • Equality constraints e.g. Complete promotional finances is the same as £100k
  • Inequality constraints e.g. Low cost on every product between £1 and £10

SciPy have a straightforward to make use of implementation of SLSQP:

Subsequent we are going to illustrate how highly effective the mix of the S-Learner, Micaelis-Menton equation and non-linear programing could be!

Background

Traditionally the promotions groups have used their skilled judgement to set the low cost for his or her 3 high merchandise. Given the present financial situations, they’re being compelled to scale back their general promotional finances by 20%. They flip to the Knowledge Science crew to advise how they’ll do that while minimising the loss in orders being positioned.

Knowledge producing course of

We arrange an information producing course of with the next traits:

  • 4 options with a posh relationship with the variety of orders
  • A remedy impact which follows the Micaelis-Menton equation
def data_generator(n, tau_weight, alpha, lam):

# Set variety of options
p=4

# Create options
X = np.random.uniform(dimension=n * p).reshape((n, -1))

# Nuisance parameters
b = (
np.sin(np.pi * X[:, 0])
+ 2 * (X[:, 1] - 0.5) ** 2
+ X[:, 2] * X[:, 3]
)

# Create remedy and remedy impact
T = np.linspace(200, 10000, n)
T_mm = michaelis_menten(T, alpha, lam) * tau_weight
tau = T_mm / T

# Calculate end result
y = b + T * tau + np.random.regular(dimension=n) * 0.5

y_train = y
X_train = np.hstack((X, T.reshape(-1, 1)))

return y_train, X_train, T_mm, tau

The X options are confounding variables:

Person generated picture

We use the info generator to create samples for 3 merchandise, every with a distinct remedy impact:

np.random.seed(1234)

n=100000

y_train_1, X_train_1, T_mm_1, tau_1 = data_generator(n, 1.00, 2, 5000)
y_train_2, X_train_2, T_mm_2, tau_2 = data_generator(n, 0.25, 2, 5000)
y_train_3, X_train_3, T_mm_3, tau_3 = data_generator(n, 2.00, 2, 5000)

S-Learner

We will practice an S-Learner through the use of any machine studying algorithm and together with the remedy and covariates as options:

def train_slearner(X_train, y_train):

mannequin = LGBMRegressor(random_state=42)
mannequin.match(X_train, y_train)

yhat_train = mannequin.predict(X_train)

mse_train = mean_squared_error(y_train, yhat_train)
r2_train = r2_score(y_train, yhat_train)

print(f'MSE on practice set is {spherical(mse_train)}')
print(f'R2 on practice set is {spherical(r2_train, 2)}')

return mannequin, yhat_train

We practice an S-Learner for every product:

np.random.seed(1234)

model_1, yhat_train_1 = train_slearner(X_train_1, y_train_1)
model_2, yhat_train_2 = train_slearner(X_train_2, y_train_2)
model_3, yhat_train_3 = train_slearner(X_train_3, y_train_3)

In the mean time that is only a prediction mannequin — Under we visualise how effectively it does at this job:

Person generated picture

Extracting the remedy results

Subsequent we are going to use our S-learner to extract the remedy impact for the total vary of remedy values (low cost quantity) while holding different options to their imply worth.

We begin by extracting the anticipated end result (variety of orders) for the total vary of remedy values:

def extract_treated_effect(n, X_train, mannequin):

# Set options to imply worth
X_mean_mapping = {'X1': [X_train[:, 0].imply()] * n,
'X2': [X_train[:, 1].imply()] * n,
'X3': [X_train[:, 2].imply()] * n,
'X4': [X_train[:, 3].imply()] * n}

# Create DataFrame
df_scoring = pd.DataFrame(X_mean_mapping)

# Add full vary of remedy values
df_scoring['T'] = X_train[:, 4].reshape(-1, 1)

# Calculate end result prediction for handled
handled = mannequin.predict(df_scoring)

return handled, df_scoring

We do that for every product:

treated_1, df_scoring_1 = extract_treated_effect(n, X_train_1, model_1)
treated_2, df_scoring_2 = extract_treated_effect(n, X_train_2, model_2)
treated_3, df_scoring_3 = extract_treated_effect(n, X_train_3, model_3)

We then extract the anticipated end result (variety of orders) when the remedy is ready to 0:

def extract_untreated_effect(n, X_train, mannequin):

# Set options to imply worth
X_mean_mapping = {'X1': [X_train[:, 0].imply()] * n,
'X2': [X_train[:, 1].imply()] * n,
'X3': [X_train[:, 2].imply()] * n,
'X4': [X_train[:, 3].imply()] * n,
'T': [0] * n}

# Create DataFrame
df_scoring = pd.DataFrame(X_mean_mapping)

# Add full vary of remedy values
df_scoring

# Calculate end result prediction for handled
untreated = mannequin.predict(df_scoring)

return untreated

Once more, we do that for every product:

untreated_1 = extract_untreated_effect(n, X_train_1, model_1)
untreated_2 = extract_untreated_effect(n, X_train_2, model_2)
untreated_3 = extract_untreated_effect(n, X_train_3, model_3)

We will now calculate the remedy impact for the total vary of remedy values:

treatment_effect_1 = treated_1 - untreated_1
treatment_effect_2 = treated_2 - untreated_2
treatment_effect_3 = treated_3 - untreated_3

Once we examine this to the precise remedy impact which we saved from our data-generator, we will see the S-Learner may be very efficient at estimating the remedy results for the total vary of remedy values:

Person generated picture

Now now we have this remedy impact knowledge, we will use it to construct response curves for every product.

Michaelis-Menton

To construct the response curves, we’d like a curve becoming instrument. SciPy has an incredible implementation of 1 which we are going to use:

We begin by establishing the operate that we wish to be taught:

def michaelis_menten(x, alpha, lam):
return alpha * x / (lam + x)

We will then use curve_fit to be taught the alpha and lambda parameters:

def response_curves(treatment_effect, df_scoring):

maxfev = 100000
lam_initial_estimate = 0.001
alpha_initial_estimate = max(treatment_effect)
initial_guess = [alpha_initial_estimate, lam_initial_estimate]

popt, pcov = curve_fit(michaelis_menten, df_scoring['T'], treatment_effect, p0=initial_guess, maxfev=maxfev)

return popt, pcov

We do that for every product:

popt_1, pcov_1 = response_curves(treatment_effect_1, df_scoring_1)
popt_2, pcov_2 = response_curves(treatment_effect_2, df_scoring_2)
popt_3, pcov_3 = response_curves(treatment_effect_3, df_scoring_3)

We will now feed the learnt parameters into the michaelis menten operate to assist us visualise how effectively the curve becoming did:

treatment_effect_curve_1 = michaelis_menten(df_scoring_1['T'], popt_1[0], popt_1[1])
treatment_effect_curve_2 = michaelis_menten(df_scoring_2['T'], popt_2[0], popt_2[1])
treatment_effect_curve_3 = michaelis_menten(df_scoring_3['T'], popt_3[0], popt_3[1])

We will see that the curve becoming did an incredible job!

Person generated picture

Now now we have the alpha and lambda parameters for every product, we will begin eager about the non-linear optimisation…

Non-linear programming

We begin by setting collating all of the required data for the optimisation:

  • A listing of all of the merchandise
  • The full promotional finances
  • The finances ranges for every product
  • The parameters for every product from the Michaelis Menten response curves
# Listing of merchandise
merchandise = ["product_1", "product_2", "product_3"]

# Set complete finances to be the sum of the imply of every product decreased by 20%
total_budget = (df_scoring_1['T'].imply() + df_scoring_2['T'].imply() + df_scoring_3['T'].imply()) * 0.80

# Dictionary with min and max bounds for every product - set as +/-20% of max/min low cost
budget_ranges = {"product_1": [df_scoring_1['T'].min() * 0.80, df_scoring_1['T'].max() * 1.2],
"product_2": [df_scoring_2['T'].min() * 0.80, df_scoring_2['T'].max() * 1.2],
"product_3": [df_scoring_3['T'].min() * 0.80, df_scoring_3['T'].max() * 1.2]}

# Dictionary with response curve parameters
parameters = {"product_1": [popt_1[0], popt_1[1]],
"product_2": [popt_2[0], popt_2[1]],
"product_3": [popt_3[0], popt_3[1]]}

Subsequent we arrange the target operate — We wish to maximise orders however as we’re going to use a minimisation technique, we return the detrimental of the sum of orders anticipated.

def objective_function(x, merchandise, parameters):

sum_orders = 0.0

# Unpack parameters for every product and calculate anticipated orders
for product, finances in zip(merchandise, x, strict=False):
L, ok = parameters[product]
sum_orders += michaelis_menten(finances, L, ok)

return -1 * sum_orders

Lastly we will run our optimisation to find out the optimum finances to allocate to every product:

# Set preliminary guess by equally sharing out the entire finances
initial_guess = [total_budget // len(products)] * len(merchandise)

# Set the decrease and higher bounds for every product
bounds = [budget_ranges[product] for product in merchandise]

# Set the equality constraint - constraining the entire finances
constraints = {"kind": "eq", "enjoyable": lambda x: np.sum(x) - total_budget}

# Run optimisation
outcome = decrease(
lambda x: objective_function(x, merchandise, parameters),
initial_guess,
technique="SLSQP",
bounds=bounds,
constraints=constraints,
choices={'disp': True, 'maxiter': 1000, 'ftol': 1e-9},
)

# Extract outcomes
optimal_treatment = {product: finances for product, finances in zip(merchandise, outcome.x, strict=False)}
print(f'Optimum promo finances allocations: {optimal_treatment}')
print(f'Optimum orders: {spherical(outcome.enjoyable * -1, 2)}')

The output exhibits us what the optimum promotional finances is for every product:

Person generated picture

For those who carefully examine the response curves, you will notice that what the optimisation outcomes are intuitive:

  • Small lower within the finances for product 1
  • Lower the finances for product 2 considerably
  • Improve the finances for product 3 considerably

As we speak we lined the highly effective mixture of the S-Learner, Micaelis-Menton equation and non-linear programing! Listed below are some closing ideas:

  • As talked about earlier, when utilizing the S-Learner watch out for regularisation bias!
  • I selected to make use of the Micaelis-Menton equation to construct my response curves — Nonetheless, this will likely not suit your drawback and could be changed by different transformations that are extra appropriate.
  • Utilizing SLSQP to resolve nonlinear programming issues provides you the pliability to make use of each equality and inequality constraints.
  • I’ve chosen to deal with Pricing & Promotions, however this framework could be prolonged to Advertising budgets.

[ad_2]