bhardwajsatyam commited on
Commit
ff977f3
·
1 Parent(s): 2cc34a0

Added files

Browse files
Files changed (4) hide show
  1. README.md +4 -4
  2. app.py +322 -0
  3. description.md +14 -0
  4. requirements.txt +8 -0
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: Blr Demo
3
- emoji: 👀
4
- colorFrom: red
5
- colorTo: green
6
  sdk: streamlit
7
  sdk_version: 1.28.2
8
  app_file: app.py
 
1
  ---
2
+ title: Blr Numpyro
3
+ emoji: 🌍
4
+ colorFrom: gray
5
+ colorTo: yellow
6
  sdk: streamlit
7
  sdk_version: 1.28.2
8
  app_file: app.py
app.py ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import numpyro
3
+ from numpyro.distributions import Normal, StudentT, Laplace, Uniform
4
+ from numpyro.infer import MCMC, NUTS, Predictive
5
+ from jax import random
6
+ import arviz as az
7
+ import streamlit as st
8
+ import matplotlib.pyplot as plt
9
+
10
+ import pickle
11
+
12
+ plt.style.use("seaborn-v0_8")
13
+
14
+
15
+ def phi(X, degree=2):
16
+ return np.concatenate([X**i for i in range(1, degree + 1)], axis=1)
17
+
18
+
19
+ st.set_page_config(layout="wide")
20
+ st.title("Bayesian Linear Regression")
21
+ st.markdown(
22
+ "This app shows the effect of changing the prior and the likelihood distributions used in Bayesian Linear Regression. Since they do not always form a conjugate pair, we use ``` numpyro ``` to numerically sample from the posterior distribution using MCMC with the No-U-Turn Sampler (NUTS) algorithm."
23
+ )
24
+
25
+ left, right = st.columns([0.3, 0.7])
26
+
27
+ with left:
28
+ st.write("---")
29
+ d = st.select_slider(
30
+ label="Degree of polynomial features", options=np.arange(1, 11), value=1
31
+ )
32
+ weight_prior_type = st.selectbox(
33
+ r"##### Weight prior $p(\theta)$",
34
+ ["Normal", "Laplace", "Uniform"],
35
+ )
36
+
37
+ if weight_prior_type == "Normal":
38
+ ll, rr = st.columns(2)
39
+ with ll:
40
+ mu = st.select_slider(
41
+ label=r"$\mu$", options=np.arange(-5.0, 6.0), value=0.0
42
+ )
43
+
44
+ with rr:
45
+ sigma = st.slider(
46
+ label=r"$\sigma$",
47
+ min_value=0.1,
48
+ max_value=10.0,
49
+ value=1.0,
50
+ step=0.1,
51
+ )
52
+ weight_prior = Normal(mu, sigma)
53
+ elif weight_prior_type == "Laplace":
54
+ ll, rr = st.columns(2)
55
+ with ll:
56
+ mu = st.slider(
57
+ label=r"$\mu$", min_value=-5.0, max_value=5.0, value=0.0, step=0.1
58
+ )
59
+ with rr:
60
+ bw = st.slider(
61
+ label=r"$b$", min_value=0.1, max_value=10.0, value=1.0, step=0.1
62
+ )
63
+ weight_prior = Laplace(mu, bw)
64
+ elif weight_prior_type == "Uniform":
65
+ ll, rr = st.columns(2)
66
+ with ll:
67
+ a = st.slider(
68
+ label=r"$a$", min_value=-6.0, max_value=5.0, value=-6.0, step=0.1
69
+ )
70
+ with rr:
71
+ b = st.slider(
72
+ label=r"$b$",
73
+ min_value=a + 1e-3,
74
+ max_value=6.0 + 1e-3,
75
+ value=6.0,
76
+ step=0.1,
77
+ )
78
+ if a >= b:
79
+ st.error("Lower bound must be less than upper bound")
80
+ weight_prior = Uniform(a, b)
81
+
82
+ same_bias_prior = st.checkbox(r"Same prior on bias $\theta_0$", value=True)
83
+
84
+ if not same_bias_prior:
85
+ bias_prior_type = st.selectbox(
86
+ r"##### Bias prior $p(\mathcal{b})$",
87
+ ["Normal", "Laplace", "Uniform"],
88
+ )
89
+
90
+ if bias_prior_type == "Normal":
91
+ ll, rr = st.columns(2)
92
+ with ll:
93
+ mu = st.slider(
94
+ label=r"$\mu$",
95
+ min_value=-5.0,
96
+ max_value=5.0,
97
+ value=0.0,
98
+ step=0.1,
99
+ key="bias_mu",
100
+ )
101
+ with rr:
102
+ sigma = st.slider(
103
+ label=r"$\sigma$",
104
+ min_value=0.1,
105
+ max_value=10.0,
106
+ value=1.0,
107
+ step=0.1,
108
+ key="bias_sigma",
109
+ )
110
+ bias_prior = Normal(mu, sigma)
111
+ elif bias_prior_type == "Laplace":
112
+ ll, rr = st.columns(2)
113
+ with ll:
114
+ mu = st.slider(
115
+ label=r"$\mu$",
116
+ min_value=-5.0,
117
+ max_value=5.0,
118
+ value=0.0,
119
+ step=0.1,
120
+ key="bias_mu",
121
+ )
122
+ with rr:
123
+ bw = st.slider(
124
+ label=r"$b$",
125
+ min_value=0.1,
126
+ max_value=10.0,
127
+ value=1.0,
128
+ step=0.1,
129
+ key="bias_bw",
130
+ )
131
+ bias_prior = Laplace(mu, bw)
132
+ elif bias_prior_type == "Uniform":
133
+ ll, rr = st.columns(2)
134
+ with ll:
135
+ a = st.slider(
136
+ label="Lower bound",
137
+ min_value=-6.0,
138
+ max_value=5.0,
139
+ value=-6.0,
140
+ step=0.1,
141
+ key="bias_a",
142
+ )
143
+ with rr:
144
+ b = st.slider(
145
+ label="Upper bound",
146
+ min_value=a + 1e-3,
147
+ max_value=6.0 + 1e-3,
148
+ value=6.0,
149
+ step=0.1,
150
+ key="bias_b",
151
+ )
152
+ if a >= b:
153
+ st.error("Lower bound must be less than upper bound")
154
+ bias_prior = Uniform(a, b)
155
+
156
+ else:
157
+ bias_prior = weight_prior
158
+
159
+ st.write("---")
160
+
161
+ ll, rr = st.columns(2)
162
+ with ll:
163
+ likelihood_type = st.selectbox(
164
+ r"##### Likelihood $p(\mathcal{D} | \theta)$",
165
+ ["Normal", "StudentT", "Laplace"],
166
+ )
167
+ with rr:
168
+ noise_sigma = st.slider(
169
+ label="Aleatoric noise $\sigma$",
170
+ min_value=0.1,
171
+ max_value=2.0,
172
+ value=0.5,
173
+ step=0.1,
174
+ )
175
+
176
+ if likelihood_type == "StudentT":
177
+ likelihood_df = st.select_slider(
178
+ label=r"Degrees of freedom $\nu$",
179
+ options=list(range(1, 21)),
180
+ value=3,
181
+ key="likelihood_df",
182
+ )
183
+
184
+ st.write("---")
185
+ st.write("##### Sampling parameters")
186
+
187
+ ll, rr = st.columns(2)
188
+ with ll:
189
+ num_samples = st.slider(
190
+ label="Number of samples",
191
+ min_value=500,
192
+ max_value=10000,
193
+ value=2000,
194
+ step=500,
195
+ )
196
+ with rr:
197
+ num_warmup = st.slider(
198
+ label="Number of warmup steps",
199
+ min_value=100,
200
+ max_value=1000,
201
+ value=500,
202
+ step=100,
203
+ )
204
+
205
+ st.write("---")
206
+ st.write("##### Dataset parameters")
207
+
208
+ ll, rr = st.columns(2)
209
+ with ll:
210
+ dataset_type = st.selectbox("Select Dataset", ["Sin", "Log", "Exp"])
211
+ with rr:
212
+ dataset_noise_sigma = st.slider(
213
+ label="Dataset noise $\sigma$",
214
+ min_value=0.1,
215
+ max_value=2.0,
216
+ value=1.0,
217
+ step=0.1,
218
+ )
219
+
220
+
221
+ with right:
222
+ np.random.seed(42)
223
+
224
+ if dataset_type == "Sin":
225
+ X = np.sort(2 * np.random.rand(100)).reshape(-1, 1)
226
+ X_lin = np.linspace(0, 2, 100).reshape(-1, 1)
227
+ y = X * np.sin(2 * np.pi * X) + dataset_noise_sigma * np.random.randn(
228
+ 100
229
+ ).reshape(-1, 1)
230
+ elif dataset_type == "Log":
231
+ X = np.sort(2 * np.random.rand(100)).reshape(-1, 1)
232
+ X_lin = np.linspace(0, 2, 100).reshape(-1, 1)
233
+ y = np.log(X) + dataset_noise_sigma * np.random.randn(100).reshape(-1, 1)
234
+ elif dataset_type == "Exp":
235
+ X = np.sort(2 * np.random.rand(100)).reshape(-1, 1)
236
+ X_lin = np.linspace(0, 2, 100).reshape(-1, 1)
237
+ y = np.exp(X) + dataset_noise_sigma * np.random.randn(100).reshape(-1, 1)
238
+
239
+ X = phi(X, d)
240
+ X_lin = phi(X_lin, d)
241
+
242
+ def model(X=None, y=None):
243
+ w = numpyro.sample("w", weight_prior.expand([X.shape[1], 1]))
244
+ b = numpyro.sample("b", bias_prior.expand([1, 1]))
245
+
246
+ y_hat = X @ w + b
247
+
248
+ if likelihood_type == "Normal":
249
+ return numpyro.sample(
250
+ "y_pred",
251
+ Normal(y_hat, noise_sigma),
252
+ obs=y,
253
+ )
254
+ elif likelihood_type == "StudentT":
255
+ return numpyro.sample(
256
+ "y_pred",
257
+ StudentT(likelihood_df, y_hat, noise_sigma),
258
+ obs=y,
259
+ )
260
+ elif likelihood_type == "Laplace":
261
+ return numpyro.sample(
262
+ "y_pred",
263
+ Laplace(y_hat, noise_sigma),
264
+ obs=y,
265
+ )
266
+
267
+ kernel = NUTS(model)
268
+ mcmc = MCMC(
269
+ kernel,
270
+ num_samples=num_samples,
271
+ num_warmup=num_warmup,
272
+ )
273
+
274
+ rng_key = random.PRNGKey(0)
275
+ mcmc.run(rng_key, X=X, y=y)
276
+
277
+ posterior_samples = mcmc.get_samples()
278
+
279
+ rng_key = random.PRNGKey(1)
280
+ posterior_predictive = Predictive(model, posterior_samples)
281
+ y_pred = posterior_predictive(rng_key, X=X_lin, y=None)["y_pred"]
282
+
283
+ mean = y_pred.mean(0)
284
+ std = y_pred.std(0)
285
+
286
+ fig, ax = plt.subplots(figsize=(6, 4), dpi=300)
287
+ for i in range(1, 21):
288
+ ax.fill_between(
289
+ X_lin[:, 0],
290
+ (mean - (3 * i / 20) * std).reshape(-1),
291
+ (mean + (3 * i / 20) * std).reshape(-1),
292
+ color="C0",
293
+ alpha=0.05,
294
+ edgecolor=None,
295
+ )
296
+ ax.plot(X_lin[:, 0], mean, "r", label="Mean", linewidth=1)
297
+ ax.scatter(
298
+ X[:, 0],
299
+ y.ravel(),
300
+ c="k",
301
+ label="Datapoints",
302
+ marker="x",
303
+ s=8,
304
+ linewidth=0.5,
305
+ )
306
+ ax.set_xlabel("x", fontsize=7)
307
+ ax.set_ylabel("y", fontsize=7)
308
+ ax.set_title("Posterior Predictive", fontsize=8)
309
+ ax.tick_params(labelsize=6)
310
+ ax.legend(fontsize=7)
311
+ plt.tight_layout()
312
+ st.pyplot(fig)
313
+
314
+ axes = az.plot_trace(mcmc, compact=True)
315
+ fig = axes.ravel()[0].figure
316
+
317
+ plt.tight_layout()
318
+ st.pyplot(fig)
319
+
320
+
321
+ file = open("description.md", "r")
322
+ st.markdown(file.read())
description.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ In Bayesian linear regression, we typically consider the model
2
+
3
+ | Prior | Likelihood | Posterior | Posterior Predictive |
4
+ |:-----------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|
5
+ | $p(\boldsymbol{\theta}) = \mathcal{N}(\mathbf{m}_0, \mathbf{S}_0)$ | $p(y \| x, \boldsymbol{\theta}) = \mathcal{N}(\boldsymbol{\theta}^T x, \sigma^2)$ | $p(\boldsymbol{\theta} \mid \mathcal{X}, \mathcal{Y})=\mathcal{N}\left(\boldsymbol{\theta} \mid \boldsymbol{m}_N, \boldsymbol{S}_N\right)$ | $p\left(y_* \mid \mathcal{X}, \mathcal{Y}, \boldsymbol{x}_*\right)=\mathcal{N}\left(y_* \mid \boldsymbol{\phi}^{\top}\left(\boldsymbol{x}_*\right) \boldsymbol{m}_N, \boldsymbol{\phi}^{\top}\left(\boldsymbol{x}_*\right) \boldsymbol{S}_N \boldsymbol{\phi}\left(\boldsymbol{x}_*\right)+\sigma^2\right)$ |
6
+
7
+
8
+ where $\mathbf{m}_0$ and $\mathbf{S}_0$ are the mean and covariance of the prior distribution, respectively. $\boldsymbol{m}_N=\boldsymbol{S}_N\left(\boldsymbol{S}_0^{-1} \boldsymbol{m}_0+\sigma^{-2} \boldsymbol{\Phi}^{\top} \boldsymbol{y}\right)$ and $\boldsymbol{S}_N=\left(\boldsymbol{S}_0^{-1}+\sigma^{-2} \boldsymbol{\Phi}^{\top} \boldsymbol{\Phi}\right)^{-1}$ are the mean and covariance of the posterior distribution, respectively.
9
+
10
+ In this app, we allow the user to change the prior distribution from a Gaussian to a Laplace distribution or Uniform distribution. Likewise for the Likelihood function. The user can also select from 3 different datasets. Since the distributions don't always form a conjugate pair, we rely on ```numpyro``` library for it's fast implementation of MCMC using the NUTS algorithm.
11
+
12
+ References:
13
+ 1. Bayesian regression using numpyro—Numpyro documentation. (n.d.). Retrieved November 15, 2023, from https://num.pyro.ai/en/stable/tutorials/bayesian_regression.html
14
+ 2. Mathematics for machine learning. (n.d.). Mathematics for Machine Learning. Retrieved November 15, 2023, from https://mml-book.com/
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ numpy
2
+ matplotlib
3
+ numpyro
4
+ jax
5
+ jaxlib
6
+ arviz
7
+ tqdm
8
+ stqdm