|
### IMPORTS |
|
import numpy as np |
|
import scipy.stats as stats |
|
import matplotlib.pyplot as plt |
|
|
|
### CONSTANTS |
|
# Between what options we choose. |
|
TITLE_OPTION_ONE = 'Title Option One' |
|
TITLE_OPTION_TWO = 'Title Option Two' |
|
|
|
# "Real" CTRs that are not known for the bandit. |
|
# We need them to simulate viewers' behavior. |
|
OPTION_ONE_POPULATION_CLICK_THROUGH_RATE = 0.05 |
|
OPTION_TWO_POPULATION_CLICK_THROUGH_RATE = 0.07 |
|
|
|
# Number of visitors to whom we will show titles. |
|
VISITORS_CNT = 1000 |
|
|
|
# Our prior belief about CTRs. |
|
# Using a=1 and b=1, we state that there are no beliefs; any CTR is equally probable. |
|
PRIOR_A = 1 |
|
PRIOR_B = 1 |
|
|
|
### SIMULATION |
|
# To replicate results |
|
np.random.seed(20231119) |
|
|
|
# Variables to track how many viewers have seen and clicked on a title. |
|
title_one_seen = 0 |
|
title_one_clicked = 0 |
|
|
|
title_two_seen = 0 |
|
title_two_clicked = 0 |
|
|
|
# These arrays are needed to evaluate the performance of algorithms later on |
|
bandit_visitors_seen_highest_ctr_title = [] |
|
random_visitors_seen_highest_ctr_title = [] |
|
|
|
# For each visitor... |
|
for _ in range(VISITORS_CNT): |
|
# ... generate a posterior using Beta distribution adjusted based on observed data ... |
|
option_one_beta_distribution = stats.beta( |
|
a=PRIOR_A + title_one_clicked, # Add a number of "clicks" |
|
b=PRIOR_B + (title_one_seen - title_one_clicked) # Add a number of "skips" |
|
) |
|
|
|
option_two_beta_distribution = stats.beta( |
|
a=PRIOR_A + title_two_clicked, # Add a number of "clicks" |
|
b=PRIOR_B + (title_two_seen - title_two_clicked) # Add a number of "skips" |
|
) |
|
|
|
# ... sample CTRs from these distributions ... |
|
title_one_sample_ctr = option_one_beta_distribution.rvs() |
|
title_two_sample_ctr = option_two_beta_distribution.rvs() |
|
|
|
# ... choose what title to show using Thompson sampling. |
|
if title_one_sample_ctr >= title_two_sample_ctr: |
|
# Show title 1 |
|
title_one_seen += 1 |
|
# We know that Title One has a lower CTR. Hence, the algorithm made a mistake. |
|
bandit_visitors_seen_highest_ctr_title.append(0) |
|
|
|
# Determine if the visitor has clicked on the title. |
|
if np.random.uniform(0.0, 1.0) <= OPTION_ONE_POPULATION_CLICK_THROUGH_RATE: |
|
title_one_clicked += 1 |
|
|
|
else: |
|
# Show title 2 |
|
title_two_seen += 1 |
|
# We know that Title Two has a higher CTR. Hence, the algorithm made a correct decision. |
|
bandit_visitors_seen_highest_ctr_title.append(1) |
|
|
|
# Determine if the visitor has clicked |
|
if np.random.uniform(0.0, 1.0) <= OPTION_TWO_POPULATION_CLICK_THROUGH_RATE: |
|
title_two_clicked += 1 |
|
|
|
# Our alternative to a Bayesian Multi-armed Bandit is a random split between both options |
|
# Split is 50/50, which is why, with a probability of 0.5, random split will assign the higher CTR title. |
|
if np.random.uniform(0.0, 1.0) <= 0.5: |
|
random_visitors_seen_highest_ctr_title.append(1) |
|
else: |
|
random_visitors_seen_highest_ctr_title.append(0) |
|
|
|
### SHOW RESULTS |
|
print('RESULTS OF THE SIMULATION') |
|
print('Viewers seen Title One', title_one_seen) |
|
print('Viewers clicked on Title One', title_one_clicked) |
|
print('Observed CTR of Title One', title_one_clicked / title_one_seen) |
|
|
|
print() |
|
|
|
print('RESULTS OF THE SIMULATION') |
|
print('Viewers seen Title Two', title_two_seen) |
|
print('Viewers clicked on Title Two', title_two_clicked) |
|
print('Observed CTR of Title Two', title_two_clicked / title_two_seen) |
|
|
|
print() |
|
|
|
print('% of viewers whom Multi-armed Bandit showed the highest CTR title', np.mean(bandit_visitors_seen_highest_ctr_title)) |
|
print('% of viewers whom Random Split showed the highest CTR title', np.mean(random_visitors_seen_highest_ctr_title)) |
|
|
|
### RESULTS |
|
# >>> RESULTS OF THE SIMULATION |
|
# >>> Viewers seen Title One 227 |
|
# >>> Viewers clicked on Title One 10 |
|
# >>> Observed CTR of Title One 0.04405286343612335 |
|
|
|
# >>> RESULTS OF THE SIMULATION |
|
# >>> Viewers seen Title Two 773 |
|
# >>> Viewers clicked on Title Two 54 |
|
# >>> Observed CTR of Title Two 0.06985769728331177 |
|
|
|
# >>> % of viewers whom Multi-armed Bandit showed the highest CTR title 0.773 |
|
# >>> % of viewers whom Random Split showed the highest CTR title 0.502 |