├── README.md ├── multivariate_normal.py ├── SVGD.py └── 1D_Gaussian_mixture.py /README.md: -------------------------------------------------------------------------------- 1 | # Stein-Variational-Gradient-Descent 2 | 3 | This repo contains code accompaning the paper, [Stein Variational Gradient Descent A General Purpose Bayesian Inference Algorithm (Qiang Liu et al.)](https://arxiv.org/abs/1608.04471). It was coded by reference to https://github.com/DartML/Stein-Variational-Gradient-Descent. 4 | 5 | ### Dependencies 6 | This code requires the following: 7 | * python 2.\* or python 3.\* 8 | * pytorch v0.4.0 9 | * seaborn v4.4.10 10 | -------------------------------------------------------------------------------- /multivariate_normal.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from SVGD import SVGD_model 3 | 4 | class MVN(): 5 | 6 | def __init__(self, mean, cov): 7 | self.mean = mean 8 | self.cov = cov 9 | 10 | def dlnprob(self, x): 11 | return -1 * np.matmul((x - self.mean), np.linalg.inv(self.cov)) 12 | 13 | if __name__ == "__main__": 14 | 15 | # cov = np.array([[5.3838, -1.3120],[-1.3120, 1.7949]]) 16 | cov = np.array([[0.2260, 0.1652], [0.1652, 0.6779]]) 17 | mean = np.array([-0.6871,0.8010]) 18 | 19 | mvn_model = MVN(mean, cov) 20 | 21 | np.random.seed(0) 22 | 23 | x0 = np.random.normal(0, 1, [10, 2]); 24 | dlnprob = mvn_model.dlnprob 25 | 26 | svgd_model = SVGD_model() 27 | x = svgd_model.update(x0, dlnprob, n_iter=1000, stepsize=1e-2, bandwidth=-1, alpha=0.9, debug=True) 28 | 29 | print ("Mean ground truth: ", mean) 30 | print ("Mean obtained by svgd: ", np.mean(x, axis=0)) 31 | 32 | print("Cov ground truth: ", cov) 33 | print("Cov obtianed by svgd: ", np.cov(x.T)) 34 | -------------------------------------------------------------------------------- /SVGD.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.spatial.distance import pdist, squareform 3 | 4 | class SVGD_model(): 5 | 6 | def __init__(self): 7 | pass 8 | 9 | def SVGD_kernal(self, x, h=-1): 10 | init_dist = pdist(x) 11 | pairwise_dists = squareform(init_dist) 12 | if h < 0: # if h < 0, using median trick 13 | h = np.median(pairwise_dists) 14 | h = h ** 2 / np.log(x.shape[0] + 1) 15 | 16 | kernal_xj_xi = np.exp(- pairwise_dists ** 2 / h) 17 | d_kernal_xi = np.zeros(x.shape) 18 | for i_index in range(x.shape[0]): 19 | d_kernal_xi[i_index] = np.matmul(kernal_xj_xi[i_index], x[i_index] - x) * 2 / h 20 | 21 | return kernal_xj_xi, d_kernal_xi 22 | 23 | def update(self, x0, lnprob, n_iter=1000, stepsize=1e-3, bandwidth=-1, alpha=0.9, debug=False): 24 | # Check input 25 | if x0 is None or lnprob is None: 26 | raise ValueError('x0 or lnprob cannot be None!') 27 | 28 | x = np.copy(x0) 29 | 30 | # adagrad with momentum 31 | eps_factor = 1e-8 32 | historical_grad_square = 0 33 | for iter in range(n_iter): 34 | if debug and (iter + 1) % 1000 == 0: 35 | print('iter ' + str(iter + 1)) 36 | 37 | kernal_xj_xi, d_kernal_xi = self.SVGD_kernal(x, h=-1) 38 | current_grad = (np.matmul(kernal_xj_xi, lnprob(x)) + d_kernal_xi) / x.shape[0] 39 | if iter == 0: 40 | historical_grad_square += current_grad ** 2 41 | else: 42 | historical_grad_square = alpha * historical_grad_square + (1 - alpha) * (current_grad ** 2) 43 | adj_grad = current_grad / np.sqrt(historical_grad_square + eps_factor) 44 | x += stepsize * adj_grad 45 | 46 | return x 47 | 48 | -------------------------------------------------------------------------------- /1D_Gaussian_mixture.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import seaborn as sns 4 | from SVGD import SVGD_model 5 | 6 | sns.set_palette('deep', desat=.6) 7 | sns.set_context(rc={'figure.figsize': (8, 5) } ) 8 | 9 | class OneDimensionGM(): 10 | 11 | def __init__(self, omega, mean, var): 12 | self.omega = omega 13 | self.mean = mean 14 | self.var = var 15 | 16 | def dlnprob(self, x): 17 | rep_x = np.matlib.repmat(x, 1, self.omega.shape[0]) 18 | category_prob = np.exp(- (rep_x - self.mean) ** 2 / (2 * self.var)) / (np.sqrt(2 * np.pi * self.var)) * self.omega 19 | den = np.sum(category_prob, 1) 20 | num = ((- (rep_x - self.mean) / self.var) * category_prob).sum(1) 21 | return np.expand_dims((num / den), 1) 22 | 23 | def MGprob(self, x): 24 | rep_x = np.matlib.repmat(x, 1, self.omega.shape[0]) 25 | category_prob = np.exp(- (rep_x - self.mean) ** 2 / (2 * self.var)) / (np.sqrt(2 * np.pi * self.var)) * self.omega 26 | den = np.sum(category_prob, 1) 27 | return np.expand_dims(den, 1) 28 | 29 | if __name__ == "__main__": 30 | 31 | w = np.array([1/3, 2/3]) 32 | mean = np.array([-2, 2]) 33 | var = np.array([1, 1]) 34 | 35 | OneDimensionGM_model = OneDimensionGM(w, mean, var) 36 | 37 | np.random.seed(0) 38 | 39 | x0 = np.random.normal(-10, 1, [100, 1]); 40 | dlnprob = OneDimensionGM_model.dlnprob 41 | 42 | svgd_model = SVGD_model() 43 | n_iter = 500 44 | x = svgd_model.update(x0, dlnprob, n_iter=n_iter, stepsize=1e-1, bandwidth=-1, alpha=0.9, debug=True) 45 | 46 | 47 | #plot result 48 | sns.kdeplot(x.reshape((100,)), bw = .4, color = 'g') 49 | 50 | mg_prob = OneDimensionGM_model.MGprob 51 | x_lin = np.expand_dims(np.linspace(-15, 15, 100), 1) 52 | x_prob = mg_prob(x_lin) 53 | plt.plot(x_lin, x_prob, 'b--') 54 | plt.axis([-15, 15, 0, 0.4]) 55 | plt.title(str(n_iter) + '$ ^{th}$ iteration') 56 | plt.show() 57 | 58 | 59 | --------------------------------------------------------------------------------