Initial Commit
This commit is contained in:
commit
93dd342791
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Active Learning Demo
|
||||
|
||||
This is a basic script I used for showcasing an Active Learning approach using Gaussian Processes (GP) for a presentation on improved sampling strategies for computationally expensive functions.
|
||||
The actual Active Learning part is extremely simple, the next sampling point is selected based solely on the maximum standard deviation of the GP.
|
||||
In previous roles I have implemented more robust approaches that better balance exploration versus exploitation such as using maximum entropy or the Mismatch-first farthest-traversal heuristic to select the next sampling point.
|
||||
Included in this repo is a simple video I made using `imagemagick` and the outputs of the `active_learning.py` script.
|
BIN
active_learning.mp4
Normal file
BIN
active_learning.mp4
Normal file
Binary file not shown.
215
active_learning.py
Normal file
215
active_learning.py
Normal file
@ -0,0 +1,215 @@
|
||||
# This code is a demonstration of some python data science techniques
|
||||
|
||||
# %% import modules
|
||||
|
||||
import seaborn as sns
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import cm
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import sklearn
|
||||
from sklearn.gaussian_process import GaussianProcessRegressor
|
||||
from sklearn.gaussian_process.kernels import ConstantKernel, RBF, WhiteKernel
|
||||
import math
|
||||
from scipy.interpolate import griddata
|
||||
import itertools
|
||||
import random
|
||||
|
||||
|
||||
import scipy
|
||||
|
||||
|
||||
def lhs_sampler(var_ranges: list[tuple[float, float]], n_samples: int):
|
||||
"""Sample a number of variables using latin hypercube sampling
|
||||
Args:
|
||||
var_ranges: list of tuples where each tuple is (lower_bound, upper_bound)
|
||||
n_samples: Number of samples to draw
|
||||
"""
|
||||
# Initialize sampler
|
||||
dimensions = len(var_ranges)
|
||||
sampler = scipy.stats.qmc.LatinHypercube(dimensions)
|
||||
|
||||
# Take samples and transform them to correct ranges
|
||||
output = list()
|
||||
for sample in sampler.random(n_samples):
|
||||
output.append([x * (hi - lo) + lo for (lo, hi), x in zip(var_ranges, sample)])
|
||||
|
||||
return np.array(output)
|
||||
|
||||
|
||||
# %% Test sampler
|
||||
var_ranges = [(0.0, 1.0), (2.0, 4.0)]
|
||||
samples = lhs_sampler(var_ranges, 4)
|
||||
print(samples)
|
||||
|
||||
# %% Now compare full DOE and latin hypercube sampling
|
||||
|
||||
|
||||
# def function(a, b):
|
||||
# return math.sin(a) + math.sin(b)
|
||||
|
||||
|
||||
def function(x, y):
|
||||
return -x * x * x + 3 * x * x - 2 * x - y * y * y + 3 * y * y - 2 * y
|
||||
|
||||
|
||||
x_range = [0, 4]
|
||||
|
||||
doe_vals = np.linspace(x_range[0], x_range[1], 3)
|
||||
X_doe = np.array(list(itertools.product(*[doe_vals, doe_vals])))
|
||||
|
||||
y_doe = np.array([function(*point) for point in X_doe])
|
||||
|
||||
sampler = scipy.stats.qmc.LatinHypercube(2)
|
||||
X_lhs = np.array(
|
||||
[
|
||||
[coord * (x_range[1] - x_range[0]) + x_range[0] for coord in point]
|
||||
for point in sampler.random(9)
|
||||
]
|
||||
)
|
||||
y_lhs = np.array([[function(*point) for point in X_lhs]])
|
||||
|
||||
# %% Fit GP model to results
|
||||
|
||||
lhs_model = GaussianProcessRegressor().fit(X_lhs, y_lhs.reshape(-1, 1))
|
||||
num_bin = 50
|
||||
x_space = np.linspace(x_range[0], x_range[1], num_bin)
|
||||
X_pred = np.array(list(itertools.product(*[x_space for _ in range(2)])))
|
||||
y_lhs_pred = lhs_model.predict(X_pred)
|
||||
|
||||
doe_model = GaussianProcessRegressor().fit(X_doe, y_doe.reshape(-1, 1))
|
||||
y_doe_pred = doe_model.predict(X_pred)
|
||||
|
||||
# %% Now plot the performance of the full doe and latin hypercube sample
|
||||
y_real = np.array([function(*x) for x in X_pred])
|
||||
|
||||
# Interpolate here
|
||||
|
||||
# y_doe_pred = griddata(X_doe, y_doe, (X_pred[:, 0], X_pred[:, 1]), method="cubic")
|
||||
# y_lhs_pred = griddata(X_lhs, y_lhs, (X_pred[:, 0], X_pred[:, 1]), method="cubic")
|
||||
|
||||
|
||||
norm = cm.colors.Normalize(vmax=y_real.max(), vmin=y_real.min())
|
||||
levels = np.linspace(y_real.min(), y_real.max(), 10)
|
||||
cmap = cm.RdBu
|
||||
|
||||
fig, ax = plt.subplots(nrows=1, ncols=3)
|
||||
cset1 = ax[0].contourf(
|
||||
x_space,
|
||||
x_space,
|
||||
y_doe_pred.reshape(num_bin, num_bin),
|
||||
norm=norm,
|
||||
levels=levels,
|
||||
cmap=cmap,
|
||||
)
|
||||
|
||||
ax[0].plot(X_doe[:, 0], X_doe[:, 1], "ko")
|
||||
cset2 = ax[1].contourf(
|
||||
x_space,
|
||||
x_space,
|
||||
y_lhs_pred.reshape(num_bin, num_bin),
|
||||
norm=norm,
|
||||
levels=levels,
|
||||
cmap=cmap,
|
||||
)
|
||||
cset3 = ax[2].contourf(
|
||||
x_space,
|
||||
x_space,
|
||||
y_real.reshape(num_bin, num_bin),
|
||||
norm=norm,
|
||||
levels=levels,
|
||||
cmap=cmap,
|
||||
)
|
||||
ax[1].plot(X_lhs[:, 0], X_lhs[:, 1], "ko")
|
||||
cbar = fig.colorbar(cset2, ax=ax)
|
||||
cbar.set_ticks(ticks=[y_real.min(), y_real.max()])
|
||||
plt.show()
|
||||
# %%
|
||||
|
||||
# %% Active learning example
|
||||
|
||||
|
||||
def function(x):
|
||||
return np.sin(np.pi * x / 5) * np.cos(2 * np.pi * x / 3) + np.sqrt(
|
||||
1 - (np.sin(np.pi * x / 4)) ** 2
|
||||
)
|
||||
|
||||
|
||||
def function_with_noise(x):
|
||||
return function(x) + np.random.uniform(-0.2, 0.2)
|
||||
|
||||
|
||||
def gpPrediction(l, sigma_f, sigma_n, X_train, y_train, X_test):
|
||||
# GP model
|
||||
kernel = 1.0 * RBF(
|
||||
length_scale=1e-1, length_scale_bounds=(1e-2, 1e3)
|
||||
) + WhiteKernel(noise_level=1e-2, noise_level_bounds=(1e-10, 1e1))
|
||||
gp = GaussianProcessRegressor(
|
||||
kernel=kernel,
|
||||
alpha=0.0,
|
||||
n_restarts_optimizer=10,
|
||||
)
|
||||
# Fitting in the gp model
|
||||
gp.fit(X_train, y_train)
|
||||
# Make the prediction on test set.
|
||||
y_pred = gp.predict(X_test)
|
||||
return y_pred, gp
|
||||
|
||||
|
||||
def fit_gp(i, x, y):
|
||||
pos_x = np.linspace(0, 10, 50).reshape(-1, 1)
|
||||
l_init = 1
|
||||
sigma_f_init = 3
|
||||
sigma_n = 1
|
||||
|
||||
y_pred, gp = gpPrediction(l_init, sigma_f_init, sigma_n, x, y, pos_x)
|
||||
# Generate samples from posterior distribution.
|
||||
y_hat_samples = gp.sample_y(pos_x, n_samples=50)
|
||||
# Compute the mean of the sample.
|
||||
y_hat = np.apply_over_axes(func=np.mean, a=y_hat_samples, axes=1).squeeze()
|
||||
# Compute the standard deviation of the sample.
|
||||
y_hat_sd = np.apply_over_axes(func=np.std, a=y_hat_samples, axes=1).squeeze()
|
||||
|
||||
_, ax = plt.subplots(figsize=(15, 8))
|
||||
# Plotting the training data.
|
||||
sns.scatterplot(x=x.flatten(), y=y.flatten(), label="training data", ax=ax)
|
||||
# Plot the functional evaluation
|
||||
sns.lineplot(
|
||||
x=pos_x.flatten(),
|
||||
y=np.array([function(x) for x in pos_x]).flatten(),
|
||||
color="red",
|
||||
label="f(x)",
|
||||
ax=ax,
|
||||
)
|
||||
# Plot corridor.
|
||||
ax.fill_between(
|
||||
x=pos_x.flatten(),
|
||||
y1=(y_hat - 2 * y_hat_sd).flatten(),
|
||||
y2=(y_hat + 2 * y_hat_sd).flatten(),
|
||||
color="green",
|
||||
alpha=0.3,
|
||||
label="Credible Interval",
|
||||
)
|
||||
# Plot prediction.
|
||||
sns.lineplot(x=pos_x.flatten(), y=y_pred.flatten(), color="green", label="pred")
|
||||
|
||||
# Labeling axes
|
||||
ax.set(title="Gaussian Process Regression")
|
||||
ax.legend(loc="lower left")
|
||||
ax.set(xlabel="x", ylabel="")
|
||||
idx = list(y_hat_sd).index(max(y_hat_sd))
|
||||
add_x = pos_x[idx]
|
||||
new_y = function_with_noise(add_x[0])
|
||||
plt.plot(add_x, new_y, "ro")
|
||||
plt.savefig(f"{i}.png")
|
||||
return add_x[0], new_y
|
||||
|
||||
|
||||
x = [0.0, 5.0, 10.0]
|
||||
y = [function_with_noise(x) for x in x]
|
||||
for i in range(25):
|
||||
new_x, new_y = fit_gp(i, np.array(x).reshape(-1, 1), np.array(y).reshape(-1, 1))
|
||||
x.append(new_x)
|
||||
y.append(new_y)
|
||||
|
||||
# %%
|
Loading…
x
Reference in New Issue
Block a user