Skip to content

Commit 876caa1

Browse files
committed
initial commit of project files
1 parent 8ebbe1a commit 876caa1

16 files changed

+2387
-0
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# variationalsparsebayes
2+
3+
This library provides a PyTorch implementation for learning sparse models with with half-cauchy priors using stochastic variational inference.
4+
5+
# Features
6+
7+
The main features of the library are methods for performing:
8+
9+
- [sparse polynomial regression](https://github.com/coursekevin/variationalsparsebayes/blob/main/examples/sparse_poly_regression.py)
10+
- sparse learning with [precomputed features](https://github.com/coursekevin/variationalsparsebayes/blob/main/examples/support_vectors.py)
11+
- sparse learning of [Bayesian neural networks](https://github.com/coursekevin/variationalsparsebayes/blob/main/examples/sparse_bnn_regression.py).
12+
13+
To implement your own custom features, you can inherit from the [SparseFeaturesLibrary](https://github.com/coursekevin/variationalsparsebayes/blob/main/variationalsparsebayes/sparse_glm.py) class.
14+
15+
More generally you can use the [SVIHalfCauchyPrior](https://github.com/coursekevin/variationalsparsebayes/blob/main/variationalsparsebayes/svi_half_cauchy.py) class to perform sparse regression with _any_ parameterized model. To do so you need to define a method which takes reparameterized sample weights and computes the expected log-likelihood of your data using these weights.

examples/sparse_bnn_regression.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import torch
2+
import matplotlib.pyplot as plt
3+
import torch.optim as optim
4+
from torch.optim import optimizer
5+
from variationalsparsebayes import *
6+
import numpy as np
7+
import math
8+
9+
plt.style.use("default")
10+
11+
torch.set_default_dtype(torch.float64)
12+
13+
plt.style.use("default")
14+
15+
n_data = 1000
16+
train_x = torch.linspace(0, 1, 1000)
17+
noise = 0.2
18+
train_y = torch.stack(
19+
[
20+
torch.sin(train_x * (2 * math.pi)) + torch.randn(train_x.size()) * noise,
21+
torch.cos(train_x * (2 * math.pi)) + torch.randn(train_x.size()) * noise,
22+
torch.sin(train_x * (2 * math.pi))
23+
+ 2 * torch.cos(train_x * (2 * math.pi))
24+
+ torch.randn(train_x.size()) * noise,
25+
-torch.cos(train_x * (2 * math.pi)) + torch.randn(train_x.size()) * noise,
26+
],
27+
-1,
28+
)
29+
n_tasks = train_y.size(-1)
30+
31+
32+
def sampler():
33+
idx = np.random.choice(n_data, size=(64,), replace=False)
34+
return (train_x[idx].unsqueeze(-1), train_y[idx, :])
35+
36+
37+
if __name__ == "__main__":
38+
d = 1
39+
model = SparseBNN(
40+
in_features=1, out_features=n_tasks, n_hidden=30, n_layers=2, n_reparam=16
41+
)
42+
num_epochs = 1000
43+
beta_warmup_iters = 250
44+
optimizer = torch.optim.Adam(model.parameters(), lr=1e-2)
45+
fig, axs = plt.subplots(1, n_tasks, figsize=(4 * n_tasks, 3))
46+
beta = 0.0
47+
for j in range(1, num_epochs + 1):
48+
optimizer.zero_grad()
49+
x_batch, y_batch = sampler()
50+
beta = min(1.0, (1.0 * j) / (beta_warmup_iters))
51+
model.reparam_sample()
52+
loss = -model.elbo(x_batch, y_batch, n_data, beta=beta)
53+
loss.backward()
54+
optimizer.step()
55+
56+
if j % 100 == 0:
57+
print(f"iter: {j:05d} | loss: {loss.item():.2f}")
58+
# plotting
59+
x_t = torch.linspace(0, 1, 300)
60+
y_pred = model(x_t.unsqueeze(-1))
61+
mu = y_pred.mean(0)
62+
var = y_pred.var(0) + model.sigma.pow(2)
63+
lb = mu - 2 * var.sqrt()
64+
ub = mu + 2 * var.sqrt()
65+
for j, ax in enumerate(axs):
66+
ax.cla()
67+
ax.plot(train_x, train_y[:, j], "C7o", alpha=0.3)
68+
with torch.no_grad():
69+
ax.plot(x_t, mu[:, j], "C0")
70+
ax.fill_between(x_t, lb[:, j], ub[:, j], color="C0", alpha=0.3)
71+
fig.tight_layout()
72+
plt.pause(1 / 60)
73+
plt.show()

examples/sparse_poly_regression.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import torch
2+
import matplotlib.pyplot as plt
3+
import torch.optim as optim
4+
from torch.optim import optimizer
5+
from variationalsparsebayes import *
6+
import numpy as np
7+
8+
torch.set_default_dtype(torch.float64)
9+
10+
plt.style.use("default")
11+
12+
n_data = 1000
13+
x = torch.linspace(-3, 3, n_data)
14+
noise = 1e-1
15+
y = -0.1 * x + 2.0 * x.pow(3)
16+
y_data = y + torch.randn_like(x) * noise
17+
18+
19+
def sampler():
20+
idx = np.random.choice(np.arange(n_data), 128, replace=False)
21+
return (x[idx].unsqueeze(-1), y_data[idx])
22+
23+
24+
if __name__ == "__main__":
25+
d = 1
26+
features = SparsePolynomialFeatures(d, 8, include_bias=True, input_labels=["x"])
27+
model = SparseGLMGaussianLikelihood(d, features, noise=noise, learn_noise=False)
28+
opt_summary = model.optimize(
29+
data_sampler=sampler, n_data_total=n_data, print_progress=True
30+
)
31+
# prune basis
32+
model.prune_basis()
33+
print(model.features)
34+
# plotting
35+
x_t = x # torch.linspace(-2, 2, 500)
36+
mu, cov = model(x_t.unsqueeze(-1))
37+
lb = mu - 2 * torch.sqrt(cov.diag())
38+
ub = mu + 2 * torch.sqrt(cov.diag())
39+
40+
plt.plot(x, y_data, "C7o", alpha=0.3)
41+
with torch.no_grad():
42+
plt.plot(x_t, mu, "C0")
43+
plt.fill_between(x_t, lb, ub, color="C0", alpha=0.3)
44+
plt.plot(x, y, "C7--", alpha=1.0)
45+
plt.show()

examples/support_vectors.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from variationalsparsebayes.sparse_glm import SparsePrecomputedFeatures
2+
3+
# import sparsebayes
4+
import matplotlib.pyplot as plt
5+
import torch
6+
import numpy as np
7+
from variationalsparsebayes import *
8+
import math
9+
10+
import time
11+
12+
torch.set_default_dtype(torch.float64)
13+
torch.manual_seed(2021)
14+
15+
16+
plt.style.use("default")
17+
18+
# n_data = 1000
19+
n_data = 100
20+
x = torch.linspace(-10, 10, n_data)
21+
22+
noise = 1e-1 # 0.2
23+
y = torch.sinc(x / math.pi)
24+
# y_data = y + (torch.rand(n_data) * (0.2 + 0.2) - 0.2)
25+
y_data = y + torch.randn(n_data) * noise
26+
27+
28+
def rbf(x_in):
29+
x_in = x_in.unsqueeze(-1)
30+
xn = x.unsqueeze(0)
31+
d = (x_in - xn).pow(2)
32+
l = 3.0
33+
return torch.exp(-d / (l**2))
34+
35+
36+
phi = rbf(x)
37+
38+
39+
def data_sampler():
40+
return (phi, y_data)
41+
42+
43+
if __name__ == "__main__":
44+
x_test = torch.linspace(-10, 10, n_data * 2)
45+
phi_test = rbf(x_test)
46+
features = SparsePrecomputedFeatures(n_data)
47+
start_time = time.time()
48+
model = SparseGLMGaussianLikelihood(
49+
n_data, features, noise=noise, learn_noise=False, tau=1.0
50+
)
51+
opt_summary = model.optimize(
52+
data_sampler=data_sampler,
53+
n_data_total=n_data,
54+
lr=1e-1,
55+
beta_warmup_iters=3000,
56+
max_iter=20000,
57+
n_reparams=20,
58+
print_progress=True,
59+
)
60+
print("Time elapsed: {}".format(time.time() - start_time))
61+
# prune basis
62+
model.prune_basis()
63+
axs = [0]
64+
f, axs[0] = plt.subplots(1, 1, sharey=True)
65+
mu, cov = model(phi_test)
66+
with torch.no_grad():
67+
axs[0].plot(x, y_data, "k.", alpha=0.3)
68+
axs[0].plot(
69+
x_test,
70+
mu,
71+
"--",
72+
label="half-cauchy, {} support vecs.".format(model.num_sparse_features),
73+
)
74+
lb = mu - 2 * torch.sqrt(cov.diag())
75+
ub = mu + 2 * torch.sqrt(cov.diag())
76+
axs[0].fill_between(x_test, lb, ub, alpha=0.3)
77+
axs[0].axis("off")
78+
axs[0].legend()
79+
axs[0].plot(
80+
x[model.sparse_index],
81+
y_data[model.sparse_index],
82+
"ro",
83+
fillstyle="none",
84+
linewidth=1.0,
85+
markersize=10,
86+
)
87+
axs[0].plot(x, y, "k-")
88+
plt.show()

examples/well-tuned-example.png

339 KB
Loading

0 commit comments

Comments
 (0)