├── BOCD_Algorithms.py ├── BOCD_modules.py ├── README.md └── demoOnlineDetection.py /BOCD_Algorithms.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | import numpy as np 3 | from BOCD_modules import * 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | """ 8 | -------------------------------------------------------------------------------------------------------------------------------------- 9 | 10 | Bayesian Online Change-point Detection original version 11 | 12 | Inputs: 13 | -- environment: numpy array of piece-wise Bernoulli distributions 14 | 15 | Outputs: 16 | -- ChangePointEstimation: numpy array of change-point estimations 17 | 18 | -------------------------------------------------------------------------------------------------------------------------------------- 19 | """ 20 | 21 | def BOCD(environment): 22 | #--------------- Initialization --------------------- 23 | Horizon = environment.size 24 | gamma = 1/Horizon # Switching Rate 25 | alphas = np.array([1]) 26 | betas = np.array([1]) 27 | ForecasterDistribution = np.array([1]) 28 | ChangePointEstimation = np.array([]) 29 | #----------------------------------------------------- 30 | #Interation with the environment ... 31 | print('Launching BOCD ...') 32 | for t in range(Horizon): 33 | EstimatedBestExpert = np.argmax(ForecasterDistribution) #Change-point estimation 34 | ChangePointEstimation = np.append(ChangePointEstimation,EstimatedBestExpert+1) 35 | reward = int ((np.random.uniform() < environment[t]) == True) #Get the observation from the environment 36 | ForecasterDistribution = updateForecasterDistribution(ForecasterDistribution, alphas, betas, reward, gamma) 37 | (alphas, betas) = updateLaplacePrediction(alphas, betas, reward) #Update the laplace predictor 38 | return ChangePointEstimation 39 | 40 | 41 | """ 42 | -------------------------------------------------------------------------------------------------------------------------------------- 43 | 44 | Bayesian Online Change-point Detection with original prior and restart 45 | 46 | Inputs: 47 | -- environment: numpy array of piece-wise Bernoulli distributions 48 | 49 | Outputs: 50 | -- ChangePointEstimation: numpy array of change-point estimations 51 | 52 | -------------------------------------------------------------------------------------------------------------------------------------- 53 | """ 54 | 55 | 56 | def BOCD_restart(environment): 57 | #--------------- Initialization --------------------- 58 | Horizon = environment.size 59 | gamma = 1/Horizon # Switching Rate 60 | alphas = np.array([1]) 61 | betas = np.array([1]) 62 | ForecasterDistribution = np.array([1]) 63 | ChangePointEstimation = np.array([]) 64 | Restart = 1 # Position of last restart 65 | #----------------------------------------------------- 66 | #Interation with the environment ... 67 | print('Launching BOCD with restart ... ') 68 | for t in range(Horizon): 69 | EstimatedBestExpert = np.argmax(ForecasterDistribution) 70 | # Restart precedure 71 | if not(EstimatedBestExpert == 0): 72 | # Reinitialization 73 | alphas = np.array([1]) 74 | betas = np.array([1]) 75 | ForecasterDistribution = np.array([1]) 76 | Restart = t+1 77 | ChangePointEstimation = np.append(ChangePointEstimation,Restart+1)#Change-point estimation 78 | reward = int ((np.random.uniform() < environment[t]) == True) #Get the observation from the environment 79 | ForecasterDistribution = updateForecasterDistribution(ForecasterDistribution, alphas, betas, reward, gamma) 80 | (alphas, betas) = updateLaplacePrediction(alphas, betas, reward) #Update the laplace predictor 81 | return ChangePointEstimation 82 | 83 | 84 | 85 | """ 86 | -------------------------------------------------------------------------------------------------------------------------------------- 87 | 88 | Bayesian Online Change-point Detection modified without restart and simple prior 89 | 90 | Inputs: 91 | -- environment: numpy array of piece-wise Bernoulli distributions 92 | 93 | Outputs: 94 | -- ChangePointEstimation: numpy array of change-point estimations 95 | 96 | -------------------------------------------------------------------------------------------------------------------------------------- 97 | """ 98 | 99 | def BOCDm(environment): 100 | #--------------- Initialization --------------------- 101 | Horizon = environment.size 102 | gamma = 1/Horizon # Switching Rate 103 | alphas = np.array([1]) 104 | betas = np.array([1]) 105 | ForecasterDistribution = np.array([1]) 106 | PseudoDist = np.array([1]) 107 | ChangePointEstimation = np.array([]) 108 | like1 = 1 109 | #----------------------------------------------------- 110 | #Interation with the environment ... 111 | print('Launching BOCD modified ... ') 112 | for t in range(Horizon): 113 | EstimatedBestExpert = np.argmax(ForecasterDistribution) 114 | ChangePointEstimation = np.append(ChangePointEstimation,EstimatedBestExpert+1) #Change-point estimation 115 | reward = int ((np.random.uniform() < environment[t]) == True) #Get the observation from the environment 116 | (ForecasterDistribution, PseudoDist, like1) = updateForecasterDistribution_m(ForecasterDistribution,PseudoDist, alphas, betas, reward, gamma, like1) 117 | (alphas, betas) = updateLaplacePrediction(alphas, betas, reward) #Update the laplace predictor 118 | return ChangePointEstimation 119 | 120 | 121 | 122 | """ 123 | -------------------------------------------------------------------------------------------------------------------------------------- 124 | 125 | Bayesian Online Change-point Detection modified without restart and simple prior 126 | 127 | Inputs: 128 | -- environment: numpy array of piece-wise Bernoulli distributions 129 | 130 | Outputs: 131 | -- ChangePointEstimation: numpy array of change-point estimations 132 | 133 | -------------------------------------------------------------------------------------------------------------------------------------- 134 | """ 135 | 136 | 137 | def BOCDm_restart(environment): 138 | #--------------- Initialization --------------------- 139 | Horizon = environment.size 140 | gamma = 1/Horizon # Switching Rate 141 | alphas = np.array([1]) 142 | betas = np.array([1]) 143 | ForecasterDistribution = np.array([1]) 144 | PseudoDist = np.array([1]) 145 | ChangePointEstimation = np.array([]) 146 | like1 = 1 147 | Restart = 1 # Position of last restart 148 | #------------------------------------------------------ 149 | #Interation with the environment ... 150 | print('Launching BOCD modified with restart ...') 151 | for t in range(Horizon): 152 | EstimatedBestExpert = np.argmax(ForecasterDistribution) 153 | # Restart precedure 154 | if not(EstimatedBestExpert == 0): 155 | # Reinitialization 156 | alphas = np.array([1]) 157 | betas = np.array([1]) 158 | ForecasterDistribution = np.array([1]) 159 | Restart = t+1 160 | like1 = 1 161 | ChangePointEstimation = np.append(ChangePointEstimation,Restart+1) #Change-point estimation 162 | reward = int ((np.random.uniform() < environment[t]) == True) #Get the observation from the environment 163 | (ForecasterDistribution, PseudoDist, like1) = updateForecasterDistribution_m(ForecasterDistribution,PseudoDist, alphas, betas, reward, gamma, like1) 164 | (alphas, betas) = updateLaplacePrediction(alphas, betas, reward) #Update the laplace predictor 165 | return ChangePointEstimation 166 | 167 | 168 | -------------------------------------------------------------------------------- /BOCD_modules.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | import numpy as np 3 | 4 | import sys 5 | 6 | 7 | #------------------------------------------------------------------------------------------------------------------- 8 | # Building the piece-wise stationary Bernoulli distributions 9 | #------------------------------------------------------------------------------------------------------------------- 10 | def constructEnvironment(environment, Period): 11 | vect = np.array([]) 12 | for periode in range(environment.size): 13 | vect = np.append(vect, environment[periode]*np.ones((Period))) 14 | return vect 15 | 16 | 17 | #------------------------------------------------------------------------------------------------------------------- 18 | # Updating the forecaster distribution using the message passing algorithm 19 | #------------------------------------------------------------------------------------------------------------------- 20 | 21 | def updateForecasterDistribution(ForecasterDistribution, alphas, betas, reward, gamma): 22 | if reward == 1: 23 | likelihood = np.divide(alphas, alphas + betas) 24 | else: 25 | likelihood = np.divide(betas, alphas + betas) 26 | ForecasterDistribution0 = gamma*np.dot(likelihood, np.transpose(ForecasterDistribution)) # Creating new Forecaster 27 | ForecasterDistribution = (1-gamma)*likelihood*ForecasterDistribution # update the previous forecasters 28 | ForecasterDistribution = np.append(ForecasterDistribution,ForecasterDistribution0) # Including the new forecaseter into the previons ones 29 | ForecasterDistribution = ForecasterDistribution/np.sum(ForecasterDistribution) # Normalization for numerical purposes 30 | return ForecasterDistribution 31 | 32 | 33 | #------------------------------------------------------------------------------------------------------------------- 34 | # Updating the forecaster distribution using the message passing algorithm with a modified prior (q) 35 | #------------------------------------------------------------------------------------------------------------------- 36 | 37 | def updateForecasterDistribution_m(ForecasterDistribution, PseudoDist, alphas, betas, reward, gamma, like1): 38 | if reward == 1: 39 | likelihood = np.divide(alphas, alphas + betas) 40 | else: 41 | likelihood = np.divide(betas, alphas + betas) 42 | Pseudo_w0 = gamma*like1*np.sum(PseudoDist) # Using the simple prior 43 | PseudoDist = like1*PseudoDist 44 | ForecasterDistribution0 = Pseudo_w0 # Creating new Forecaster 45 | ForecasterDistribution = (1-gamma)*likelihood*ForecasterDistribution # update the previous forecasters 46 | ForecasterDistribution = np.append(ForecasterDistribution,ForecasterDistribution0) # Including the new forecaseter into the previons ones 47 | ForecasterDistribution = ForecasterDistribution/np.sum(ForecasterDistribution) # Normalization for numerical purposes 48 | PseudoDist = np.append(PseudoDist,Pseudo_w0) 49 | PseudoDist = PseudoDist/np.sum(PseudoDist) # Normalization for numerical purposes 50 | return ForecasterDistribution, PseudoDist, like1 51 | 52 | 53 | #------------------------------------------------------------------------------------------------------------------- 54 | # Updating the laplace prediction for each forecasters 55 | #------------------------------------------------------------------------------------------------------------------- 56 | 57 | def updateLaplacePrediction(alphas, betas, x): 58 | alphas[:] += x 59 | betas[:] += (1-x) 60 | alphas = np.append(alphas,1) # Creating new Forecaster 61 | betas = np.append(betas,1) # Creating new Forecaster 62 | return alphas, betas 63 | 64 | 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bayesian Online Change-point Detection 2 | 3 | This folder contains: 4 | 5 | --- the python codes of the algorithms BOCD and BOCDm 6 | 7 | 8 | ============================ Python codes ======================================= 9 | 10 | -1- BOCD_Algorithms.py contains the four versions of the Bayesian Online Change-point Detection 11 | -2- demoOnlineDetection.py is a demonstration of the four algorithms (BOCD, BOCDm, BOCD_restart, BOCDm_restart) 12 | in a piece-wise Bernoulli environment 13 | 14 | ----> For a demon please launch the following script: demoOnlineDetection.py 15 | 16 | The original version of the Bayesian Online Change-point Detector can be found here: https://arxiv.org/abs/0710.3742 17 | 18 | -------------------------------------------------------------------------------- /demoOnlineDetection.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | import numpy as np 3 | from BOCD_Algorithms import * 4 | from BOCD_modules import * 5 | import matplotlib.pyplot as plt 6 | 7 | 8 | """ 9 | --------------------------------------------------------------------------------------------------------------------------- 10 | Define the environment 11 | --------------------------------------------------------------------------------------------------------------------------- 12 | """ 13 | 14 | environment = np.array([0.9,0.1,0.8,0.2]) # Bernoulli distributions 15 | Period = 300 # Length of each stationary period 16 | environment = constructEnvironment(environment, Period) # Building the piece-wise stationary Bernoulli distributions 17 | 18 | 19 | """ 20 | ---------------------------------------------------------------------------------------------------- 21 | Launch the change-point detection 22 | ---------------------------------------------------------------------------------------------------- 23 | """ 24 | 25 | # Bayesian Online Change-point detector without restart 26 | Original = BOCD(environment) 27 | Modified = BOCDm(environment) 28 | 29 | # Bayesian Online Change-point detector with restart 30 | Original_restart = BOCD_restart(environment) 31 | Modified_restart = BOCDm_restart(environment) 32 | #print(environment) 33 | #print(Original) 34 | 35 | 36 | """ 37 | ---------------------------------------------------------------------------------------------------------------------------- 38 | Plotting the results 39 | ---------------------------------------------------------------------------------------------------------------------------- 40 | """ 41 | 42 | plt.plot(range(environment.size), Modified_restart.tolist(), color='red', marker='o', label = "BOCDm") 43 | plt.hold(True) 44 | plt.grid(True) 45 | plt.plot(range(environment.size), Original_restart.tolist(), color='blue', marker='.', label = "BOCD") 46 | plt.legend(loc='upper left') 47 | plt.xlabel('Time step') 48 | plt.ylabel('Change-point Estimation') 49 | plt.title('with restart') 50 | plt.show() 51 | plt.hold(False) 52 | 53 | plt.plot(range(environment.size), Modified.tolist(), color='red', marker='o', label = "BOCDm") 54 | plt.hold(True) 55 | plt.grid(True) 56 | plt.plot(range(environment.size), Original.tolist(), color='blue', marker='.', label = "BOCD") 57 | plt.legend(loc='upper left') 58 | plt.xlabel('Time step') 59 | plt.ylabel('Change-point Estimation') 60 | plt.title('without restart') 61 | plt.show() 62 | 63 | --------------------------------------------------------------------------------