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()