helboukkouri's picture
initial commit
0a25afe
import gradio as gr
import numpy as np
import sympy as sp
import seaborn as sns
from matplotlib import pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
sns.set_style(style="darkgrid")
sns.set_context(context="notebook", font_scale=0.7)
MAX_NOISE = 20
DEFAULT_NOISE = 6
SLIDE_NOISE_STEP = 2
MAX_POINTS = 100
DEFAULT_POINTS = 20
SLIDE_POINTS_STEP = 5
def generate_equation(process_params):
process_params = process_params.astype(float).values.tolist()
# Define symbols
x = sp.symbols('x')
coefficients = sp.symbols('a b c d e')
# Create the polynomial expression
polynomial_expression = None
for i, coef in enumerate(reversed(coefficients)):
polynomial_expression = polynomial_expression + coef * x**i if polynomial_expression else coef * x**i
# Parameter mapping
parameters = {coef: value for coef, value in zip(coefficients, process_params[0])}
# Substitute parameter values into the expression
polynomial_with_values = polynomial_expression.subs(parameters)
latex_representation = sp.latex(polynomial_with_values)
return fr"$${latex_representation}$$"
def true_process(x, process_params):
"""The true process we want to model."""
process_params = process_params.astype(float).values.tolist()
return (
process_params[0][0] * (x ** 4)
+ process_params[0][1] * (x ** 3)
+ process_params[0][2] * (x ** 2)
+ process_params[0][3] * x
+ process_params[0][4]
)
def generate_data(num_points, noise_level, process_params):
# x is the list of input values
input_values = np.linspace(-5, 2, num_points)
input_values_dense = np.linspace(-5, 2, MAX_POINTS)
# y = f(x) is the underlying process we want to model
y = [true_process(x, process_params) for x in input_values]
y_dense = [true_process(x, process_params) for x in input_values_dense]
# however, we can only observe a noisy version of f(x)
noise = np.random.normal(0, noise_level, len(input_values))
y_noisy = y + noise
return input_values, input_values_dense, y, y_dense, y_noisy
def make_plot(
num_points, noise_level, process_params,
show_true_process, show_original_points,
show_noisy_points, show_added_noise,
show_learned_process, show_predicted_points,
show_prediction_error,
polynomial_degree=None
):
x, x_dense, y, y_dense, y_noisy = generate_data(num_points, noise_level, process_params)
fig = plt.figure(dpi=400)
if show_true_process:
plt.plot(
x_dense, y_dense, "-", color="#363A4F",
label="True Process",
lw=1.5,
)
if show_added_noise:
plt.vlines(
x, y, y_noisy, color="#556D9A",
linestyles="dashed",
alpha=0.75,
lw=1,
label="Added Noise",
)
if show_original_points:
plt.plot(
x, y, "-o", color="none",
ms=6,
markerfacecolor="white",
markeredgecolor="#556D9A",
markeredgewidth=1.2,
label="Original Points",
)
if show_noisy_points and not polynomial_degree:
plt.plot(
x, y_noisy, "-o", color="none",
ms=6.5,
markerfacecolor="#556D9A",
markeredgecolor="none",
markeredgewidth=1.5,
alpha=1,
label="Noisy Points",
)
# Fit the selected regression model
if polynomial_degree:
degree = polynomial_degree
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
model.fit(x.reshape(-1, 1), y_noisy)
# Plot the fitted regression model
y_pred_dense = model.predict(x_dense.reshape(-1, 1))
y_pred = model.predict(x.reshape(-1, 1))
if show_learned_process:
plt.plot(
x_dense, y_pred_dense, "-", color="#327747",
label="Learned Process",
lw=1.5,
alpha=0.75,
)
if show_prediction_error:
plt.vlines(
x, y_pred, y_noisy, color="#43A461",
linestyles="dashed",
alpha=0.75,
lw=1,
label="Prediction Error",
)
if show_noisy_points:
plt.plot(
x, y_noisy, "-o", color="none",
ms=6.5,
markerfacecolor="#556D9A",
markeredgecolor="none",
markeredgewidth=1.5,
alpha=1,
label="Training Points",
)
if show_predicted_points:
plt.plot(
x, y_pred, "-o", color="none",
ms=6.5,
markerfacecolor="#43A461",
markeredgecolor="none",
markeredgewidth=1.5,
label="Predicted Points",
alpha=1,
)
plt.xlabel("x")
plt.ylabel("y")
plt.legend(fontsize=7.5)
plt.tight_layout()
return fig
# Custom CSS
css = """
.train-button {
font-size: 1.2em;
width: 20%!important;
margin: 0;
}
.model-section {
font-size: 1em;
width: 100%!important;
margin: 0 0 1em 0;
}
.gradio-container {
width: 40%!important;
min-width: 800px;
}
"""
with gr.Blocks(css=css) as demo:
with gr.Row():
with gr.Column():
gr.Markdown("## Underlying Process")
with gr.Row():
process_params = gr.DataFrame(
value=[[0.5, 2, -0.5, -2, 1]],
label="Polynomial Coefficients",
type="pandas",
column_widths=("2", "1", "1", "1", "1w"),
headers=["x ** 4", "x ** 3", "x ** 2", "x", "1"],
interactive=True
)
equation = gr.Markdown()
gr.Markdown("## Data Generation")
with gr.Row():
num_points = gr.Slider(
minimum=5,
maximum=MAX_POINTS,
value=DEFAULT_POINTS,
step=SLIDE_POINTS_STEP,
label="Number of Points"
)
noise_level = gr.Slider(
minimum=0,
maximum=MAX_NOISE,
value=DEFAULT_NOISE,
step=SLIDE_NOISE_STEP,
label="Noise Level"
)
show_params = []
with gr.Row():
with gr.Column():
show_params.append(gr.Checkbox(label="Underlying Process", value=True))
show_params.append(gr.Checkbox(label="Original Points", value=True))
show_params.append(gr.Checkbox(label="Noisy Points", value=True))
show_params.append(gr.Checkbox(label="Added Noise", value=True))
with gr.Column():
show_params.append(gr.Checkbox(label="Learned Process", value=True))
show_params.append(gr.Checkbox(label="Predicted Points", value=True))
show_params.append(gr.Checkbox(label="Prediction Error", value=True))
# Add model choice dropdown and training trigger button
gr.Markdown("## Modelisation")
with gr.Row(elem_classes=["model-section"]):
polynomial_degree = gr.Number(label="Choose the degree of your regression model", value=1, minimum=1, maximum=15, step=1, scale=2)
train_button = gr.Button(value="Train Model", elem_classes=["train-button"], scale=1)
scatter_plot = gr.Plot(elem_classes=["main-plot"])
num_points.change(fn=make_plot, inputs=[num_points, noise_level, process_params, *show_params], outputs=scatter_plot)
noise_level.change(fn=make_plot, inputs=[num_points, noise_level, process_params, *show_params], outputs=scatter_plot)
process_params.change(fn=make_plot, inputs=[num_points, noise_level, process_params, *show_params], outputs=scatter_plot)
process_params.change(fn=generate_equation, inputs=[process_params], outputs=equation)
train_button.click(make_plot, inputs=[num_points, noise_level, process_params, *show_params, polynomial_degree], outputs=scatter_plot)
for component in show_params:
component.change(fn=make_plot, inputs=[num_points, noise_level, process_params, *show_params], outputs=scatter_plot)
demo.load(fn=make_plot, inputs=[num_points, noise_level, process_params, *show_params], outputs=scatter_plot)
demo.load(fn=generate_equation, inputs=[process_params], outputs=equation)
if __name__ == "__main__":
demo.launch()