import numpy as np import matplotlib.pyplot as plt np.random.seed(123123123) # Compute ELBO for the model described in simple_elbo.pdf def compute_elbo(alpha_tau, beta_tau, r1, r2, m2, beta2, alpha0, beta0, x): from scipy.special import psi, gammaln # digamma function, logarithm of gamma function # E[log p(tau)] term1 = (alpha0 - 1) * (psi(alpha_tau) + psi(beta_tau) - 2 * psi(alpha_tau + beta_tau)) # E[log p(theta)] term2 = # EXERCISE # E[log p(z|tau)] N2 = np.sum(r2); N1 = np.sum(r1); N = N1 + N2 term3 = N2 * psi(alpha_tau) + N1 * psi(beta_tau) - N * psi(alpha_tau + beta_tau) # E[log p(x|z,theta)] term4 = # EXERCISE # Negative entropy of q(z) term5 = np.sum(r1 * np.log(r1)) + np.sum(r2 * np.log(r2)) # Negative entropy of q(tau) term6 = (gammaln(alpha_tau + beta_tau) - gammaln(alpha_tau) - gammaln(beta_tau) + (alpha_tau - 1) * psi(alpha_tau) + (beta_tau - 1) * psi(beta_tau) - (alpha_tau + beta_tau - 2) * psi(alpha_tau + beta_tau)) # Negative entropy of q(theta) term7 = # EXERCISE elbo = term1 + term2 + term3 + term4 - term5 - term6 - term7 return elbo # Simulate data theta_true = 4 tau_true = 0.3 n_samples = 10000 z = (np.random.rand(n_samples) < tau_true) # True with probability tau_true x = np.random.randn(n_samples) + z * theta_true # Parameters of the prior distributions. alpha0 = 0.5 beta0 = 0.2 n_iter = 15 # The number of iterations elbo_array = np.zeros(n_iter) # To track the elbo # Some initial value for the things that will be updated E_log_tau = -0.7 # E(log(tau)) E_log_tau_c = -0.7 # E(log(1-tau)) E_log_var = 4 * np.ones(n_samples) # E((x_n-theta)^2) r2 = 0.5 * np.ones(n_samples) # Responsibilities of the second cluster. for i in range(n_iter): # Updated of responsibilites, factor q(z) log_rho1 = E_log_tau_c - 0.5 * np.log(2 * np.pi) - 0.5 * (x ** 2) log_rho2 = E_log_tau - 0.5 * np.log(2 * np.pi) - 0.5 * E_log_var max_log_rho = np.maximum(log_rho1, log_rho2) # Normalize to avoid numerical problems when exponentiating. rho1 = np.exp(log_rho1 - max_log_rho) rho2 = np.exp(log_rho2 - max_log_rho) r2 = rho2 / (rho1 + rho2) r1 = 1 - r2 N1 = np.sum(r1) N2 = np.sum(r2) # Update of factor q(tau) from scipy.special import psi # digamma function E_log_tau = psi(N2 + alpha0) - psi(N1 + N2 + 2*alpha0) E_log_tau_c = psi(N1 + alpha0) - psi(N1 + N2 + 2*alpha0) # Update of factor q(theta) x2_avg = 1 / N2 * np.sum(r2 * x) beta_2 = beta0 + N2 m2 = 1 / beta_2 * N2 * x2_avg E_log_var = (x - m2) ** 2 + 1 / beta_2 # Keep track of the current estimates tau_est = (N2 + alpha0) / (N1 + N2 + 2*alpha0) theta_est = m2 # Compute ELBO alpha_tau = N2 + alpha0 beta_tau = N1 + alpha0 elbo_array[i] = compute_elbo(alpha_tau, beta_tau, r1, r2, m2, beta_2, alpha0, beta0, x) # Plot ELBO as a function of iteration plt.plot(np.arange(n_iter) + 1, elbo_array) plt.xticks(np.arange(n_iter) + 1) plt.xlabel("iteration") plt.title("ELBO") plt.show()