NTaylor commited on
Commit
e1527f1
1 Parent(s): 057098a

Initial commit - uploading app and requirements

Browse files
Files changed (2) hide show
  1. app.py +367 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,367 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sklearn.pipeline import make_pipeline
2
+ from sklearn.preprocessing import PolynomialFeatures, StandardScaler
3
+ import numpy as np
4
+ from sklearn.datasets import make_regression
5
+ import pandas as pd
6
+ from sklearn.linear_model import ARDRegression, LinearRegression, BayesianRidge
7
+ import matplotlib.pyplot as plt
8
+ import seaborn as sns
9
+ from matplotlib.colors import SymLogNorm
10
+ import gradio as gr
11
+
12
+
13
+ # def make_regression_data(n_samples=100,
14
+ # n_features=100,
15
+ # n_informative=10,
16
+ # noise=8,
17
+ # coef=True,
18
+ # random_state=42,):
19
+ # X, y, true_weights = make_regression(
20
+ # n_samples=n_samples,
21
+ # n_features=n_features,
22
+ # n_informative=n_informative,
23
+ # noise=noise,
24
+ # coef=coef,
25
+ # random_state=random_state,
26
+ # )
27
+ # return X, y, true_weights
28
+
29
+ X, y, true_weights = make_regression(
30
+ n_samples=100,
31
+ n_features=100,
32
+ n_informative=10,
33
+ noise=8,
34
+ coef=True,
35
+ random_state=42,
36
+ )
37
+
38
+ # Fit the regressors
39
+ # ------------------
40
+ #
41
+ # We now fit both Bayesian models and the OLS to later compare the models'
42
+ # coefficients.
43
+
44
+ # olr = LinearRegression().fit(X, y)
45
+ # brr = BayesianRidge(compute_score=True, n_iter=30).fit(X, y)
46
+ # ard = ARDRegression(compute_score=True, n_iter=30).fit(X, y)
47
+ # df = pd.DataFrame(
48
+ # {
49
+ # "Weights of true generative process": true_weights,
50
+ # "ARDRegression": ard.coef_,
51
+ # "BayesianRidge": brr.coef_,
52
+ # "LinearRegression": olr.coef_,
53
+ # }
54
+ # )
55
+
56
+ def fit_regression_models(n_iter=30, X=X, y=y, true_weights=true_weights):
57
+ olr = LinearRegression().fit(X, y)
58
+ print(f"inside fit_regression n_iter={n_iter}")
59
+ brr = BayesianRidge(compute_score=True, n_iter=n_iter).fit(X, y)
60
+ ard = ARDRegression(compute_score=True, n_iter=n_iter).fit(X, y)
61
+ df = pd.DataFrame(
62
+ {
63
+ "Weights of true generative process": true_weights,
64
+ "ARDRegression": ard.coef_,
65
+ "BayesianRidge": brr.coef_,
66
+ "LinearRegression": olr.coef_,
67
+ }
68
+ )
69
+ return df, olr, brr, ard
70
+
71
+
72
+
73
+ # %%
74
+ # Plot the true and estimated coefficients
75
+ # ----------------------------------------
76
+ #
77
+ # Now we compare the coefficients of each model with the weights of
78
+ # the true generative model.
79
+
80
+
81
+ # plt.figure(figsize=(10, 6))
82
+ # ax = sns.heatmap(
83
+ # df.T,
84
+ # norm=SymLogNorm(linthresh=10e-4, vmin=-80, vmax=80),
85
+ # cbar_kws={"label": "coefficients' values"},
86
+ # cmap="seismic_r",
87
+ # )
88
+ # plt.ylabel("linear model")
89
+ # plt.xlabel("coefficients")
90
+ # plt.tight_layout(rect=(0, 0, 1, 0.95))
91
+ # _ = plt.title("Models' coefficients")
92
+
93
+ def visualize_coefficients(df=None):
94
+ fig = plt.figure(figsize=(10, 6))
95
+ ax = sns.heatmap(
96
+ df.T,
97
+ norm=SymLogNorm(linthresh=10e-4, vmin=-80, vmax=80),
98
+ cbar_kws={"label": "coefficients' values"},
99
+ cmap="seismic_r",
100
+ )
101
+ plt.ylabel("linear model")
102
+ plt.xlabel("coefficients")
103
+ plt.tight_layout(rect=(0, 0, 1, 0.95))
104
+ _ = plt.title("Models' coefficients")
105
+
106
+ return fig
107
+
108
+ # %%
109
+ # Due to the added noise, none of the models recover the true weights. Indeed,
110
+ # all models always have more than 10 non-zero coefficients. Compared to the OLS
111
+ # estimator, the coefficients using a Bayesian Ridge regression are slightly
112
+ # shifted toward zero, which stabilises them. The ARD regression provides a
113
+ # sparser solution: some of the non-informative coefficients are set exactly to
114
+ # zero, while shifting others closer to zero. Some non-informative coefficients
115
+ # are still present and retain large values.
116
+
117
+ # %%
118
+ # Plot the marginal log-likelihood
119
+ # --------------------------------
120
+
121
+
122
+ # ard_scores = -np.array(ard.scores_)
123
+ # brr_scores = -np.array(brr.scores_)
124
+ # plt.plot(ard_scores, color="navy", label="ARD")
125
+ # plt.plot(brr_scores, color="red", label="BayesianRidge")
126
+ # plt.ylabel("Log-likelihood")
127
+ # plt.xlabel("Iterations")
128
+ # plt.xlim(1, 30)
129
+ # plt.legend()
130
+ # _ = plt.title("Models log-likelihood")
131
+
132
+ def plot_marginal_log_likelihood(ard=None, brr=None, n_iter=30):
133
+
134
+ fig = plt.figure(figsize=(10, 6))
135
+ ard_scores = -np.array(ard.scores_)
136
+ brr_scores = -np.array(brr.scores_)
137
+ # print(f"ard_scores = {ard_scores}")
138
+ # print(f"brr_scores = {brr_scores}")
139
+ plt.plot(ard_scores, color="navy", label="ARD")
140
+ plt.plot(brr_scores, color="red", label="BayesianRidge")
141
+ plt.ylabel("Log-likelihood")
142
+ plt.xlabel("Iterations")
143
+ plt.xlim(1, n_iter)
144
+ plt.legend()
145
+ _ = plt.title("Models log-likelihood")
146
+
147
+ print("fig inside plot marginal = ", fig)
148
+ return fig
149
+
150
+ def make_regression_comparison_plot(n_iter=30):
151
+
152
+ # print(f"n_iter = {n_iter}")
153
+ # fit models
154
+ df, olr, brr, ard = fit_regression_models(n_iter=n_iter, X=X, y=y, true_weights=true_weights)
155
+ # print(f"df = {df}")
156
+ # get figure
157
+ fig = visualize_coefficients(df=df)
158
+
159
+ return fig
160
+
161
+ def make_log_likelihood_plot(n_iter=30):
162
+
163
+ # print(f"n_iter = {n_iter}")
164
+ # fit models
165
+ df, olr, brr, ard = fit_regression_models(n_iter=n_iter, X=X, y=y, true_weights=true_weights)
166
+ # print(f"df = {df}")
167
+ # get figure
168
+ fig = plot_marginal_log_likelihood(ard=ard, brr=brr, n_iter=n_iter)
169
+
170
+ print(f"fig = {fig}")
171
+
172
+ return fig
173
+
174
+ # visualize coefficients
175
+
176
+ # # %%
177
+ # # Indeed, both models minimize the log-likelihood up to an arbitrary cutoff
178
+ # # defined by the `n_iter` parameter.
179
+ # #
180
+ # # Bayesian regressions with polynomial feature expansion
181
+ # # ======================================================
182
+ # Generate synthetic dataset
183
+ # --------------------------
184
+ # We create a target that is a non-linear function of the input feature.
185
+ # Noise following a standard uniform distribution is added.
186
+
187
+
188
+
189
+ rng = np.random.RandomState(0)
190
+ n_samples = 110
191
+
192
+ # sort the data to make plotting easier later
193
+ g_X = np.sort(-10 * rng.rand(n_samples) + 10)
194
+ noise = rng.normal(0, 1, n_samples) * 1.35
195
+ g_y = np.sqrt(g_X) * np.sin(g_X) + noise
196
+ full_data = pd.DataFrame({"input_feature": g_X, "target": g_y})
197
+ g_X = g_X.reshape((-1, 1))
198
+
199
+ # extrapolation
200
+ X_plot = np.linspace(10, 10.4, 10)
201
+ y_plot = np.sqrt(X_plot) * np.sin(X_plot)
202
+ X_plot = np.concatenate((g_X, X_plot.reshape((-1, 1))))
203
+ y_plot = np.concatenate((g_y - noise, y_plot))
204
+
205
+ # %%
206
+ # Fit the regressors
207
+ # ------------------
208
+ #
209
+ # Here we try a degree 10 polynomial to potentially overfit, though the bayesian
210
+ # linear models regularize the size of the polynomial coefficients. As
211
+ # `fit_intercept=True` by default for
212
+ # :class:`~sklearn.linear_model.ARDRegression` and
213
+ # :class:`~sklearn.linear_model.BayesianRidge`, then
214
+ # :class:`~sklearn.preprocessing.PolynomialFeatures` should not introduce an
215
+ # additional bias feature. By setting `return_std=True`, the bayesian regressors
216
+ # return the standard deviation of the posterior distribution for the model
217
+ # parameters.
218
+
219
+ #TODO - make this function that can be adapted with the gr.slider
220
+
221
+ def generate_polynomial_dataset(degree = 10):
222
+
223
+ ard_poly = make_pipeline(
224
+ PolynomialFeatures(degree=degree, include_bias=False),
225
+ StandardScaler(),
226
+ ARDRegression(),
227
+ ).fit(g_X, g_y)
228
+ brr_poly = make_pipeline(
229
+ PolynomialFeatures(degree=degree, include_bias=False),
230
+ StandardScaler(),
231
+ BayesianRidge(),
232
+ ).fit(g_X, g_y)
233
+
234
+ y_ard, y_ard_std = ard_poly.predict(X_plot, return_std=True)
235
+ y_brr, y_brr_std = brr_poly.predict(X_plot, return_std=True)
236
+
237
+ return y_ard, y_ard_std, y_brr, y_brr_std
238
+
239
+ # %%
240
+ # Plotting polynomial regressions with std errors of the scores
241
+ # -------------------------------------------------------------
242
+
243
+ # ax = sns.scatterplot(
244
+ # data=full_data, x="input_feature", y="target", color="black", alpha=0.75
245
+ # )
246
+ # ax.plot(X_plot, y_plot, color="black", label="Ground Truth")
247
+ # ax.plot(X_plot, y_brr, color="red", label="BayesianRidge with polynomial features")
248
+ # ax.plot(X_plot, y_ard, color="navy", label="ARD with polynomial features")
249
+ # ax.fill_between(
250
+ # X_plot.ravel(),
251
+ # y_ard - y_ard_std,
252
+ # y_ard + y_ard_std,
253
+ # color="navy",
254
+ # alpha=0.3,
255
+ # )
256
+ # ax.fill_between(
257
+ # X_plot.ravel(),
258
+ # y_brr - y_brr_std,
259
+ # y_brr + y_brr_std,
260
+ # color="red",
261
+ # alpha=0.3,
262
+ # )
263
+ # ax.legend()
264
+ # _ = ax.set_title("Polynomial fit of a non-linear feature")
265
+
266
+
267
+ def visualize_bayes_regressions_polynomial_features(degree = 10):
268
+
269
+ #TODO - get data dynamically from the gr.slider
270
+ y_ard, y_ard_std, y_brr, y_brr_std = generate_polynomial_dataset(degree)
271
+
272
+ fig = plt.figure(figsize=(10, 6))
273
+ ax = sns.scatterplot(
274
+ data=full_data, x="input_feature", y="target", color="black", alpha=0.75)
275
+ ax.plot(X_plot, y_plot, color="black", label="Ground Truth")
276
+ ax.plot(X_plot, y_brr, color="red", label="BayesianRidge with polynomial features")
277
+ ax.plot(X_plot, y_ard, color="navy", label="ARD with polynomial features")
278
+ ax.fill_between(
279
+ X_plot.ravel(),
280
+ y_ard - y_ard_std,
281
+ y_ard + y_ard_std,
282
+ color="navy",
283
+ alpha=0.3,
284
+ )
285
+ ax.fill_between(
286
+ X_plot.ravel(),
287
+ y_brr - y_brr_std,
288
+ y_brr + y_brr_std,
289
+ color="red",
290
+ alpha=0.3,
291
+ )
292
+ ax.legend()
293
+ _ = ax.set_title("Polynomial fit of a non-linear feature")
294
+ # print(f"ax = {ax}")
295
+ return fig
296
+
297
+
298
+ # def make_polynomial_comparison_plot():
299
+
300
+
301
+
302
+ # return fig
303
+
304
+
305
+
306
+
307
+
308
+ title = " Illustration of Comparing Linear Bayesian Regressors with synthetic data"
309
+ with gr.Blocks(title=title) as demo:
310
+ gr.Markdown(f"# {title}")
311
+ gr.Markdown(""" This example shows a comparison of two different bayesian regressors:
312
+ Automatic Relevance Determination - ARD see [sklearn-docs](https://scikit-learn.org/stable/modules/linear_model.html#automatic-relevance-determination)
313
+ Bayesian Ridge Regression - see [sklearn-docs](https://scikit-learn.org/stable/modules/linear_model.html#bayesian-ridge-regression)
314
+ The tutorial is split into sections, with the first comparing model coeffecients produced by Ordinary Least Squares (OLS), Bayesian Ridge Regression, and ARD with the known true coefficients. For this
315
+ We generated a dataset where X and y are linearly linked: 10 of the features of X will be used to generate y. The other features are not useful at predicting y.
316
+ n addition, we generate a dataset where n_samples == n_features. Such a setting is challenging for an OLS model and leads potentially to arbitrary large weights.
317
+ Having a prior on the weights and a penalty alleviates the problem. Finally, gaussian noise is added.
318
+
319
+ For the final tab, we investigate bayesian regressors with polynomial features and generate an additional dataset where the target is a non-linear function of the input feature, with
320
+ added noise following a standard uniform distribution.
321
+
322
+ For further details please see the sklearn docs:
323
+ """)
324
+
325
+ gr.Markdown(" **[Demo is based on sklearn docs found here](https://scikit-learn.org/stable/auto_examples/linear_model/plot_ard.html#sphx-glr-auto-examples-linear-model-plot-ard-py)** <br>")
326
+
327
+
328
+ with gr.Tab("# Plot true and estimated coefficients"):
329
+
330
+ with gr.Row():
331
+ n_iter = gr.Slider(value=5, minimum=5, maximum=50, step=1, label="n_iterations")
332
+ btn = gr.Button(value="Plot true and estimated coefficients")
333
+ btn.click(make_regression_comparison_plot, inputs = [n_iter], outputs= gr.Plot(label='Plot true and estimated coefficients') )
334
+ gr.Markdown(
335
+ """
336
+ # Details
337
+
338
+ One can observe that with the added noise, none of the models can perfectly recover the coefficients of the original model. All models have more thab 10 non-zero coefficients,
339
+ where only 10 are useful. The Bayesian Ridge Regression manages to recover most of the coefficients, while the ARD is more conservative.
340
+ """)
341
+ with gr.Tab("# Plot marginal log likelihoods"):
342
+ with gr.Row():
343
+ n_iter = gr.Slider(value=5, minimum=5, maximum=50, step=1, label="n_iterations")
344
+ btn = gr.Button(value="Plot marginal log likelihoods")
345
+ btn.click(make_log_likelihood_plot, inputs = [n_iter], outputs= gr.Plot(label='Plot marginal log likelihoods') )
346
+ gr.Markdown(
347
+ """
348
+ # Confirm with marginal log likelihoods
349
+ Both ARD and Bayesian Ridge minimized the log-likelihood upto an arbitrary cuttoff defined the the n_iter parameter.
350
+ """
351
+ )
352
+ with gr.Tab("# Plot bayesian regression with polynomial features"):
353
+ with gr.Row():
354
+ degree = gr.Slider(value=5, minimum=5, maximum=50, step=1, label="n_degrees")
355
+ btn = gr.Button(value="Plot bayesian regression with polynomial features")
356
+ btn.click(visualize_bayes_regressions_polynomial_features, inputs = [degree], outputs= gr.Plot(label='Plot bayesian regression with polynomial features') )
357
+ gr.Markdown(
358
+ """
359
+ # Details
360
+ Here we try a degree 10 polynomial to potentially overfit, though the bayesian linear models regularize the size of the polynomial coefficients.
361
+ As fit_intercept=True by default for ARDRegression and BayesianRidge, then PolynomialFeatures should not introduce an additional bias feature. By setting return_std=True,
362
+ the bayesian regressors return the standard deviation of the posterior distribution for the model parameters.
363
+
364
+ """)
365
+
366
+
367
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ scikit-learn==1.2.2
2
+ matplotlib==3.5.1
3
+ numpy==1.21.6