def sample_prior(self, proj_2_prob=False): """ Samples Probability matrix from the prior Parameters ---------- proj_2_prob: Bool: if TRUE returns the projection to the probability simplex Returns ------- x - Dict: keys: mu, Sigma, Psi, Pi (optional) mu: [MxK_prime] Normal sample from the prior Sigma: [MxM] Inverse Wishart sample from the prior Psi: [MxK_prime] Normal sample from the prior distribution Pi: [M x K] Probability matrix over K categories """ Psi = [] Sigma = invwishart.rvs(self.hyper['nu_0'], inv(self.hyper['W_0'])) mu = np.zeros((self.M, self.K_prime)) for k in range(self.K_prime): mu[:, k] = multivariate_normal(self.hyper['mu_0'][:, k], 1 / self.hyper['lambda_0'][k] * Sigma) Psi.append(multivariate_normal(mu[:, k], Sigma)) # Pi = stick_breaking(np.array(Psi)) if proj_2_prob: Pi = stick_breaking(np.array(Psi)) x = {'Psi': Psi, 'Pi': Pi, 'mu': mu, 'Sigma': Sigma} else: x = {'Psi': Psi, 'mu': mu, 'Sigma': Sigma} return x
def PGinfer(data, mu, Sigma, nonInformative, nSamples, nBurnin): """ Helper function for inference of correlated probabilities Parameters ---------- data - [M x K] - counts of K categories for a set of M histograms nSamples - Number of posterior samples mu - [MxK-1] Mean parameter for the prior variables in the normal sigmoid multinomial model Sigma- [MxM] Covariance parameter for the prior variables in the normal sigmoid multinomial model Returns ------- nSamples of the [MxK] probability matrices """ nDists, nCats = data.shape PgMultObj = PgMultNormal(M=nDists, K_prime=nCats - 1, mu=mu, Sigma=Sigma, nonInformative=nonInformative) samples = PgMultObj.sample_posterior(nSamples, data, disp=False, burnin=nBurnin) Psi = samples['Psi'] Pi_samples = np.array([stick_breaking(sample) for sample in Psi]) return Pi_samples
def sample_prior(self, proj_2_prob=False): """ Samples Probability matrix from the prior Parameters ---------- proj_2_prob: Bool: if TRUE returns the projection to the probability simplex Returns ------- x - Dict: keys: Psi, Pi (optional) Psi: [MxK_prime] Normal sample from the prior distribution Pi: [M x K] Probability matrix over K categories """ Psi = np.array([ multivariate_normal(self.hyper['mu'][:, k], self.hyper['Sigma']) for k in range(self.K_prime) ]).T if proj_2_prob: Pi = stick_breaking(Psi) x = {'Psi': Psi, 'Pi': Pi} else: x = {'Psi': Psi} return x
def mean(self): """ Returns ------- the stickbreaking transformation of the posterior mean of the transition model """ if not self.isFitted: self.fit() T = np.zeros_like(self.data) for action in range(self.nActions): Pi = stick_breaking(self.lambda_[action]) # np.array(stick_breaking(samples['Psi'][-1])) T[:, :, action] = Pi.T return T
def sample_var_posterior(self, N_samples, proj_2_prob=False): """ Creates N samples from the variational posterior Parameters ---------- N_samples - number of posterior samples proj_2_prob: Bool: if TRUE returns the projection to the probability simplex Returns ------- """ # container for the posterior samples if proj_2_prob: post_samples = {'Pi': [], 'Psi': []} else: post_samples = {'Psi': []} Psi_sam = np.zeros((N_samples, self.M, self.K_prime)) if self.hyper['nonInformative']: Psi_sam = self.var_param['V_diag'][None, :, :] * np.random.randn(N_samples, self.M, self.K_prime) + \ self.var_param['lambda_'][None, :, :] else: # Precompute cholesky if not already calculated if self.var_param['V_cho'] is None: V_cho = np.zeros_like(self.var_param['V']) for k in range(self.K_prime): V_cho[:, :, k] = cholesky(self.var_param['V'][:, :, k]) self.var_param['V_cho'] = V_cho # sample using cholesky for n in range(N_samples): for k in range(self.K_prime): Psi_sam[n, :, k] = cholesky_normal( self.var_param['lambda_'][:, k], U=self.var_param['V_cho'][:, :, k]) post_samples['Psi'] = [sam for sam in Psi_sam] if proj_2_prob: post_samples['Pi'] = [ stick_breaking(sam.copy()) for sam in post_samples['Psi'] ] return post_samples
def gibbs_step(self, X=None, Psi_init=None, proj_2_prob=False): """ calculates one gibbs step Parameters ---------- proj_2_prob: Bool: if TRUE returns the projection to the probability simplex X: [MxK] data matrix if None previously saved training data is used Psi_init: [MxK_prime] starting value of the Gibbs sampler for the normal distributed variables (if None last value is used) Returns ------- x - Dict: keys: Psi, omega Pi (optional) Psi: [MxK_prime] One normal sample from the posterior distribution omega: [MxK_prime] One PG sample from the posterior distribution Pi: [M x K] One sample from the probability matrix over K categories """ if X is not None: self.X_train = X if Psi_init is not None: self.Psi_init = Psi_init # --------------- update polya-gamma variables --------------- # # polya-gamma auxiliary variable omega_sam = poly_gamma_rand(self._N_mat, self.Psi_init) # --------------- update latent Gaussian variables --------------- # # sample new latent Gaussian variables Psi_sam = self.sample_Psi(self._kappa, omega_sam, self.hyper['mu'], self.hyper['Sigma'], Sigma_inv=self.hyper['Sigma_inv'], nonInformative=self.hyper['nonInformative']) self.Psi_init = Psi_sam if proj_2_prob: Pi = stick_breaking(Psi_sam) x = {'Psi': Psi_sam, 'omega': omega_sam, 'Pi': Pi} else: x = {'Psi': Psi_sam, 'omega': omega_sam} return x
def mean_variational_posterior(self, proj_2_prob=False): """ Returns the mean of the variational posterior Parameters ---------- proj_2_prob: Bool: if TRUE returns the projection to the probability simplex Returns ------- mean - dictonary containing the variational mean """ if proj_2_prob: mean = {'Pi': None, 'Psi': None} else: mean = {'Psi': None} mean['Psi'] = self.var_param['lambda_'] if proj_2_prob: mean['Pi'] = stick_breaking(self.var_param['lambda_']) return mean
def sample_posterior(self, N_samples, X, Psi_init=None, burnin=0, thinning=0, disp=True, proj_2_prob=False): """ Samples N_samples from the Posterior given data X Parameters ---------- N_samples: Number of samples to draw from the posterior X: [M x K] Count data used to do posterior inference Psi_init: initial value for the gibbs sampler burnin: int number of burnin samples thinning: number of discarded values in the chain between two samples disp: output steps of the MCMC sampler in the console proj_2_prob: Bool: if TRUE returns the projection to the probability simplex Returns ------- post_samples - Dict: keys: Psi, omega, mu, Sigma, Pi (optional) Psi: List of posterior samples for the Gaussians omega: List of posterior samples for the Poly Gamma variables Sigma: List of posterior samples for covariance matrix mu: List of posterior samples for the means Pi: List of posterior samples for the probability matrix over K categories """ # container for the posterior samples post_samples = { 'Pi': [], 'Psi': [], 'omega': [], 'mu': [], 'Sigma': [] } # initialize latent Gaussian random variables if Psi_init is not None: self.Psi_init = Psi_init self.X_train = X # Save input and compute sufficient stats # generate Gibbs samples enough_samples = False n = 0 while not enough_samples: n += 1 # Print results if disp: print(tabulate([[n]], headers=['MCMC sampling step'])) sam = self.gibbs_step() # add samples to list if n > burnin and np.mod(n, thinning + 1) == 0: if proj_2_prob: post_samples['Pi'].append(stick_breaking( sam['Psi'].copy())) post_samples['Psi'].append(sam['Psi'].copy()) post_samples['omega'].append(sam['omega'].copy()) post_samples['mu'].append(sam['mu'].copy()) post_samples['Sigma'].append(sam['Sigma'].copy()) if post_samples['Psi'].__len__() == N_samples: enough_samples = True return post_samples
def gibbs_step(self, X=None, Psi_init=None, proj_2_prob=False): """ calculates one gibbs step Parameters ---------- X: [MxK] data matrix if None previously saved training data is used Psi_init: [MxK_prime] starting value of the Gibbs sampler for the normal distributed variables (if None last value is used) proj_2_prob: Bool: if TRUE returns the projection to the probability simplex Returns ------- x - Dict: keys: Psi, omega, mu, Sigma, Pi (optional) Psi: [MxK_prime] One normal sample from the posterior distribution omega: [MxK_prime] One PG sample from the posterior distribution mu: [MxK_prime] One mean parameter sample from the posterior distribution Sigma: [MxM] One covariance parameter sample from the posterior distribution Pi: [M x K] One sample from the probability matrix over K categories """ if X is not None: self.X_train = X if Psi_init is not None: self.Psi_init = Psi_init # --------------- update Gaussian covariance matrix --------------- # # posterior covariance hyperparameter Psi_centered = self.Psi_init - self.hyper['mu_0'] W_bar = self.hyper['W_0'] + (self._lambda_ratio * Psi_centered) @ Psi_centered.T # sample new covariance matrix Sigma_sam = invwishart.rvs(self._nu_bar, inv(W_bar)) # --------------- update Gaussian means --------------- # # posterior mean hyperparameters mu_bar = (self.hyper['mu_0'] * self.hyper['lambda_0'] + self.Psi_init) / self._lambda_bar # sample new mean values mu_sam = np.zeros((self.M, self.K_prime)) U = cholesky(Sigma_sam) for k in range(self.K_prime): mu_sam[:, k] = cholesky_normal(mu_bar[:, k], U=1 / np.sqrt(self._lambda_bar[k]) * U) # --------------- update polya-gamma variables --------------- # # polya-gamma auxiliary variable omega_sam = poly_gamma_rand(self._N_mat, self.Psi_init) # --------------- update latent Gaussian variables --------------- # # sample new latent Gaussian variables Psi_sam = self.sample_Psi(self._kappa, omega_sam, mu_sam, Sigma_sam) self.Psi_init = Psi_sam if proj_2_prob: Pi = stick_breaking(Psi_sam) x = { 'Psi': Psi_sam, 'omega': omega_sam, 'Pi': Pi, 'mu': mu_sam, 'Sigma': Sigma_sam } else: x = { 'Psi': Psi_sam, 'omega': omega_sam, 'mu': mu_sam, 'Sigma': Sigma_sam } return x # Psi_sam, omega_sam, mu_sam, Sigma_sam