| ### 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 |