import numpy as np
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
# ------------------------
# Simulate data
# ------------------------
np.random.seed(42)
n = 100
height = np.random.normal(170, 10, n)
weight = height * 0.45 + np.random.normal(0, 5, n)
# ------------------------
# First Plot: Adjustable Function
# ------------------------
initial_slope = 0
initial_bias = 80
x_line1 = np.linspace(140, 200, 100)
# ------------------------
# Second Plot: Regression + Prediction
# ------------------------
regression_slope, regression_bias = np.polyfit(height, weight, 1)
x_line2 = np.linspace(140, 200, 100)
y_line2 = regression_slope * x_line2 + regression_bias
x_pred = 185
y_pred = regression_slope * x_pred + regression_bias
# ------------------------
# Third Plot: Loss function
# ------------------------
sel = np.random.choice(np.arange(n), 15)
s_height = height[sel]
s_weight = weight[sel]
x_pred2 = s_height[0]
y_real = s_weight[0]
x_line3 = np.linspace(140, 200, 100)
# Grid for loss surface
a_vals = np.linspace(-1, 1, 100)
b_vals = np.linspace(-20, 80, 100)
A, B = np.meshgrid(a_vals, b_vals)
def compute_loss(a, b, x, y):
y_pred = a * x[:, None, None] + b
return ((y_pred-y[:, None, None]) ** 2).mean(axis=0)
Z = compute_loss(A, B, s_height, s_weight)
# ------------------------
# Third-and-a-half Plot: Gradient descent
# ------------------------
def f_c(x):
return (
0.2*x**4
- 2.7*x**2
+ 0.5*x
#+ 2*np.exp(-(x+2)**2)
# + 1.5*np.exp(-(x-1.5)**2)
)
def df_c(x):
return (
0.8*x**3
- 2*2.7*x
+ 0.5
#-4*(x+2)*np.exp(-(x+2)**2)
# -3*(x-1.5)*np.exp(-(x-1.5)**2)
)
# -------- Gradient descent --------
def gradient_descent(x0, lr, steps=20):
xs = [x0]
x = x0
for _ in range(steps):
x = x - lr * df_c(x)
xs.append(x)
xs = np.array(xs)
ys = f_c(xs)
return xs, ys
# -------- Domini --------
x_c = np.linspace(-4,4,800)
# ------------------------
# Fourth Plot: classificacio
# ------------------------
n_d = 50
x1_d = np.random.normal(2, 0.5, n_d)
y1_d = np.random.normal(2, 0.5, n_d)
x2_d = np.random.normal(3.6, 0.5, n_d)
y2_d = np.random.normal(3.6, 0.5, n_d)
# Interval per la línia
x_line_d = np.linspace(0.5, 5, 100)
initial_slope_d = 0.0
initial_bias_d = 3.0
y_line_d = initial_slope_d * x_line_d + initial_bias_d
# Punt on dibuixarem el vector ortogonal
x0_d = 3.0
def compute_orthogonal_vector(slope_d, bias_d, length_d=4):
y0_d = slope_d * x0_d + bias_d
if slope_d == 0:
dx_d, dy_d = 0, length_d
else:
ortho_slope_d = -1 / slope_d
dx_d = length_d / np.sqrt(1 + ortho_slope_d ** 2)
dy_d = ortho_slope_d * dx_d
x_vals_d = [x0_d - dx_d / 2, x0_d + dx_d / 2]
y_vals_d = [y0_d - dy_d / 2, y0_d + dy_d / 2]
return x_vals_d, y_vals_d
def transform_to_line_coordinates(x_d, y_d, a_d, b_d):
x_d = np.array(x_d)
y_d = np.array(y_d)
norm_d = np.sqrt(1 + a_d ** 2)
d_d = np.array([1, a_d]) / norm_d
n_d = np.array([-a_d, 1]) / norm_d
p0_d = np.array([0, b_d])
points_d = np.vstack((x_d, y_d)).T
v_d = points_d - p0_d
u_coords_d = np.dot(v_d, d_d)
v_coords_d = np.dot(v_d, n_d)
return u_coords_d, v_coords_d
# ------------------------
# Fourth Plot B: classificacio
# ------------------------
n_d = 50
x1_d = np.random.normal(2, 0.5, n_d)
y1_d = np.random.normal(2, 0.5, n_d)
x2_d = np.random.normal(3.6, 0.5, n_d)
y2_d = np.random.normal(3.6, 0.5, n_d)
# Interval per la línia
xy_line_d = np.linspace(0.5, 5, 100)
initial_w1_d = 1.0
initial_w2_d = 0.0
initial_bias_d = 3.0
y_line_d = initial_w1_d * xy_line_d + initial_w2_d * xy_line_d + initial_bias_d
# Punt on dibuixarem el vector ortogonal
x0_d = 3.0
def compute_orthogonal_vector_b(w1_d, w2_d, bias_d, length_d=2):
# vector de pesos
w_d = np.array([w1_d, w2_d])
# norma al quadrat
norm_sq_d = w1_d**2 + w2_d**2
# punt de la recta més proper a l'origen
p0_d = bias_d * w_d / norm_sq_d
# normalitzar vector normal
n_d = w_d / np.sqrt(norm_sq_d)
dx_d = n_d[0] * length_d
dy_d = n_d[1] * length_d
x_vals_d = [p0_d[0], p0_d[0] + dx_d]
y_vals_d = [p0_d[1], p0_d[1] + dy_d]
return x_vals_d, y_vals_d
def transform_to_line_coordinates_b(x_d, y_d, w1_d, w2_d, b_d):
x_d = np.array(x_d)
y_d = np.array(y_d)
# norma del vector de pesos
norm_d = np.sqrt(w1_d**2 + w2_d**2)
# vector direcció de la recta
d_d = np.array([w2_d, -w1_d]) / norm_d
# vector normal
n_d = np.array([w1_d, w2_d]) / norm_d
# punts a transformar
points_d = np.vstack((x_d, y_d)).T
# projeccions (rotació)
u_coords_d = np.dot(points_d, d_d)
v_coords_d = np.dot(points_d, n_d) - b_d / norm_d
## punt de la recta (el més proper a l'origen)
#p0_d = b_d * np.array([w1_d, w2_d]) / (norm_d**2)
#v_d = points_d - p0_d
## coordenada al llarg de la recta
#u_coords_d = np.dot(v_d, d_d)
## coordenada perpendicular (distància signada a la recta)
#v_coords_d = np.dot(v_d, n_d)
return u_coords_d, v_coords_d
# ------------------------
# Code: Fifth plot
# ------------------------
#initial_slope_e = 0.0
initial_w1_e = 1.0
initial_w2_e = 0.0
initial_bias_e = 3.0
x0_e = 3.0
def step(v):
return (v > 0).astype(int)
# ------------------------
# Code: Sixth plot
# ------------------------
# Dades
initial_w1_f = 1.0
initial_w2_f = 0.0
initial_bias_f = 0.5
xy_line8_f = np.linspace(-0.1, 1.2, 100)
#y_line8_f = initial_slope_f * x_line8_f + initial_bias_f
# ------------------------
# Code: Seventh plot
# ------------------------
# Dades inicials
initial_w11_g = 1.0
initial_w12_g = 1.0
initial_w21_g = 1.0
initial_w22_g = 1.0
initial_w31_g = -1.0
initial_w32_g = 1.0
initial_bias_1_g = 1.5
initial_bias_2_g = 0.5
initial_bias_3_g = -0.5
x_line20_g = np.linspace(-0.1, 1.2, 100)
# ------------------------
# Code: Eight plot
# ------------------------
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def relu(z):
return np.maximum(0, z)
# Inputs
x_vals_h = np.linspace(-10, 10, 400)
# Initial parameters
initial_weight_h = 1.0
initial_bias_h = 0.0
# ------------------------
# Code: Nineth plot
# ------------------------
# Define the true function
def actual_func_i(x):
return x ** 3 + x ** 2 - x - 1
# Generate noisy samples
def generate_samples_i(n, margin=[-3,3], noise_std=2.0, seed=42):
np.random.seed(seed)
x = np.sort(np.random.uniform(margin[0], margin[1], n))
y = actual_func_i(x) + np.random.normal(0, noise_std, size=n)
return x, y
# ------------------------
# Code for plot GD for simple linear regression
# ------------------------
initial_w_j = 0.0
initial_b_j = 80.0
initial_alpha_j = 0.01
def gradient_descent_j(w0, b0, lr, x, y, steps=30):
w, b = w0, b0
path_w = [w]
path_b = [b]
path_loss = [np.mean((w*x + b - y)**2)/2]
n = len(x)
for _ in range(steps):
y_pred = w * x + b
error = y_pred - y
dw = (1/n) * np.sum(error * x)
db = (1/n) * np.sum(error)
w = w - lr * dw
b = b - lr * db
loss = np.mean(error**2)/2
path_w.append(w)
path_b.append(b)
path_loss.append(loss)
return np.array(path_w), np.array(path_b), np.array(path_loss)
# ------------------------
# Code for plot GD for logistic regression
# ------------------------
initial_w1_k = 1.0
initial_w2_k = 1.0
initial_b_k = 6
initial_alpha_k = 0.01
def sigmoid_k(z):
return 1 / (1 + np.exp(-z))
def binary_cross_entropy_k(y_true, y_pred):
eps = 1e-9
y_pred = np.clip(y_pred, eps, 1 - eps)
return -np.mean(y_true*np.log(y_pred) + (1-y_true)*np.log(1-y_pred))
def logistic_gd_k(w1_0, w2_0, b0, lr, x1, x2, y, steps=40):
w1, w2, b = w1_0, w2_0, b0
n = len(y)
path_w1, path_w2, path_b, path_loss = [], [], [], []
for _ in range(steps):
z = w1*x1 + w2*x2 + b
p = sigmoid_k(z)
loss = binary_cross_entropy_k(y, p)
dw1 = np.mean((p - y) * x1)
dw2 = np.mean((p - y) * x2)
db = np.mean(p - y)
w1 -= lr * dw1
w2 -= lr * dw2
b -= lr * db
path_w1.append(w1)
path_w2.append(w2)
path_b.append(b)
path_loss.append(loss)
return (np.array(path_w1),
np.array(path_w2),
np.array(path_b),
np.array(path_loss))
# ------------------------
# Dash App Layout
# ------------------------
external_stylesheets = [
# 'https://jhernandezgonzalez.github.io/presentation_style.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
html.Div([
html.H1("Regressió lineal simple:\n f(x) = w·x + b",
style={'whiteSpace': 'pre-line'}),
html.P("Només necessitem dos paràmetres per definir-la: pendent (w) i intersecció (biaix, b)"),
html.Br(),
html.Br(),
html.Br(),
dcc.Graph(id='plot-adjustable'),
html.Div([
html.Div('Pendent (w)', className="two columns", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='slope-slider',
min=-1,
max=1,
step=0.05,
value=initial_slope,
#marks={i: str(i) for i in np.arange(-1, 1.05, 0.25)},
marks={i:str(i) for i in range(-1, 2, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="four columns"),
html.Div('Intersecció (b)', className="two columns", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='bias-slider',
min=-20,
max=80,
step=1,
value=initial_bias,
marks={i: str(i) for i in range(-20, 81, 20)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="four columns"),
], className="row"),
], className="slide"),
html.Div([
html.H1("Què està aprenent?"),
html.P(
"Ajustar el millor possible la funció (a través del valor dels paràmetres) a les dades. "),
html.Ul([
html.Li("Per persona, sabem alçada i pes (x,y), i pes teòric y' = h(x) = w·x + b,"),
html.Li("i així podem calcular el seu error quadràtic, e(y,y') = (y - y')²=(y - w·x-b)²."),
html.Li("Per totes les persones: "
"[(y1 - w·x1-b)² +...+ (yn - w·xn-b)²]/n."),
html.Li("Busquem els paràmetres w i b amb menor error quadràtic mitjà.")
]),
html.Br(),
html.Div([
html.Div([dcc.Graph(id='loss-plot')], className="eight columns"),
html.Div([dcc.Graph(id='loss-surface')], className="four columns"),
], className="row"),
html.Div([
html.Div('Pendent (w)', className="two columns", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='slope-slider-2',
min=-1, max=1, step=0.05, value=initial_slope,
#marks={i: str(i) for i in np.arange(-1, 1.05, 0.25)},
marks={i:str(i) for i in range(-1, 2, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="four columns"),
html.Div('Intersecció (b)', className="two columns", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='bias-slider-2',
min=-20, max=80, step=1, value=initial_bias,
marks={i: str(i) for i in range(-20, 81, 20)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="four columns"),
], className="row"),
], className="slide"),
html.Div([
html.H1("Descens del gradient i pas (alfa)"),
dcc.Graph(id="gradient-plot"),
html.Div([
html.Div("Learning Rate", className="two columns", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
min=0.01,
max=1.0,
step=0.01,
value=0.1,
id="lr-slider"
),
], className="four columns"),
html.Div("Punt inicial w₀", className="two columns", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
min=-4,
max=4,
step=0.1,
value=3,
id="x0-slider"
),
], className="four columns"),
], className="row"),
], className="slide"),
# html.Div([
# html.H1('Una funció com a separador'),
# html.P('Un classificador necessita diferenciar casos (p.ex., malalt/sà).'),
# html.Div([
# html.Div([dcc.Graph(id='graph1_d')], className="six columns"),
# html.Div([dcc.Graph(id='graph2_d')], className="six columns"),
# ], className="row"),
# html.Div([
# html.Div('Pendent (a)', className="two columns", style={'text-align': 'right'}),
# html.Div([
# dcc.Slider(
# id='slope-slider_d', min=-2, max=2, step=0.1, value=initial_slope_d,
# marks={i: str(i) for i in range(-2, 3, 1)},
# tooltip={"placement": "bottom", "always_visible": True}
# )
# ], className="four columns"),
# html.Div('Intersecció (b)', className="two columns", style={'text-align': 'right'}),
# html.Div([
# dcc.Slider(
# id='bias-slider_d', min=-8, max=8, step=0.2, value=initial_bias_d,
# marks={i: str(i) for i in range(-8, 9, 4)},
# tooltip={"placement": "bottom", "always_visible": True}
# )
# ], className="four columns"),
# ], className="row"),
# ], className="slide"),
html.Div([
html.H1('Una funció com a separador'),
html.P('Un classificador necessita diferenciar casos (p.ex., malalt/sà).'),
html.Div([
html.Div([dcc.Graph(id='graph1_d2')], className="six columns"),
html.Div([dcc.Graph(id='graph2_d2')], className="six columns"),
], className="row"),
html.Div([
html.Div('w1', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w1-slider_d2', min=-2, max=2, step=0.1, value=initial_w1_d,
marks={i: str(i) for i in range(-2, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
html.Div('w2', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w2-slider_d2', min=-2, max=2, step=0.1, value=initial_w2_d,
marks={i: str(i) for i in range(-2, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
html.Div('b', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='bias-slider_d2', min=-8, max=8, step=0.2, value=initial_bias_d,
marks={i: str(i) for i in range(-8, 9, 4)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
], className="row"),
], className="slide"),
html.Div([
html.H1('Composició de funcions (de nou!)'),
html.P("Separem millor les dades si apliquem una funció a la sortida d'f(x) (funció lineal)."),
html.Ul([
html.Li("Per exemple, la funció esglaó: s(z) = {0 si z < 0, 1 si no}"),
html.Li("Si composem les dues funcions, s(f(x)), tenim:")
]),
html.Div([
html.Div([dcc.Graph(id='graph6_e')], className="six columns"),
html.Div([dcc.Graph(id='graph7_e')], className="six columns"),
], className="row"),
# html.Div([
# html.Div('Pendent (a)', className="two columns", style={'text-align': 'right'}),
# html.Div([
# dcc.Slider(
# id='slope-slider_e', min=-2, max=2, step=0.1, value=initial_slope_e,
# marks={i: str(i) for i in range(-2, 3, 1)},
# tooltip={"placement": "bottom", "always_visible": True}
# )
# ], className="four columns"),
# html.Div('Intersecció (b)', className="two columns", style={'text-align': 'right'}),
# html.Div([
# dcc.Slider(
# id='bias-slider_e', min=-8, max=8, step=0.2, value=initial_bias_e,
# marks={i: str(i) for i in range(-8, 9, 4)},
# tooltip={"placement": "bottom", "always_visible": True}
# )
# ], className="four columns"),
# ], className="row"),
html.Div([
html.Div('w1', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w1-slider_e', min=-2, max=2, step=0.1, value=initial_w1_e,
marks={i: str(i) for i in range(-2, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
html.Div('w2', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w2-slider_e', min=-2, max=2, step=0.1, value=initial_w2_e,
marks={i: str(i) for i in range(-2, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
html.Div('b', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='bias-slider_e', min=-8, max=8, step=0.2, value=initial_bias_e,
marks={i: str(i) for i in range(-8, 9, 4)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
], className="row"),
], className="slide"),
html.Div([
html.H1('Fem servir una neurona'),
html.P("Reproduim els operadors (funcions) lògiques OR, AND, and XOR."),
html.Ul([
html.Li("0: Fals"),
html.Li("1: Cert"),
]),
html.Div([
html.Div([dcc.Graph(id='graph2d_f')], className="six columns"),
html.Div([dcc.Graph(id='graph3d_f')], className="six columns"),
#dcc.Graph(id='graph2d_f', figure=fig8_f),
#dcc.Graph(id='graph3d_f', figure=fig9_f)
], className="row"),
# html.Div([
# html.Div('Pendent (a)', className="two columns", style={'text-align': 'right'}),
# html.Div([
# dcc.Slider(
# id='slope-slider_f', min=-2, max=2, step=0.1, value=initial_slope_f,
# marks={i: str(i) for i in range(-2, 3, 1)},
# tooltip={"placement": "bottom", "always_visible": True}
# )
# ], className="four columns"),
# html.Div('Intersecció (b)', className="two columns", style={'text-align': 'right'}),
# html.Div([
# dcc.Slider(
# id='bias-slider_f', min=-4, max=4, step=0.2, value=initial_bias_f,
# marks={i: str(i) for i in range(-4, 5, 2)},
# tooltip={"placement": "bottom", "always_visible": True}
# )
# ], className="four columns"),
# ], className="row"),
html.Div([
html.Div('w1', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w1-slider_f', min=-2, max=2, step=0.1, value=initial_w1_f,
marks={i: str(i) for i in range(-2, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
html.Div('w2', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w2-slider_f', min=-2, max=2, step=0.1, value=initial_w2_f,
marks={i: str(i) for i in range(-2, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
html.Div('b', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='bias-slider_f', min=-8, max=8, step=0.2, value=initial_bias_f,
marks={i: str(i) for i in range(-8, 9, 4)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
], className="row"),
], className="slide"),
html.Div([
html.H1('I què fem per aproximar XOR?'),
html.P(["Una xarxa neuronal de 3 neurones en dos capes!",
html.Br(),
"Composició: ",
html.Strong("n3( w1*n1(x) + w2*n2(x) + b )")]),
html.Div([
html.Div([dcc.Graph(id='fig1_g')], className="six columns"),
html.Div([dcc.Graph(id='fig2_g')], className="six columns"),
], className="row"),
html.Div([
# html.Div([
# html.Div("n1 (a):"),
# html.Div("n1 (b):",style={'margin-top': '10px'})
# ], className="one column", style={'text-align': 'right'}),
# html.Div([
# dcc.Slider(
# id='slope1_g', min=-2, max=2, step=0.1, value=initial_slope_1_g,
# marks={i: str('') for i in range(-2, 3, 1)},
# tooltip={"placement": "bottom", "always_visible": True}
# ),
# dcc.Slider(
# id='bias1_g', min=-2, max=2, step=0.1, value=initial_bias_1_g,
# marks={i: str(i) for i in range(-2, 3, 1)},
# tooltip={"placement": "bottom", "always_visible": True}
# )
# ], className="three columns"),
# html.Div([
# html.Div("n2 (a):"),
# html.Div("n2 (b):",style={'margin-top': '10px'})
# ], className="one column", style={'text-align': 'right'}),
# html.Div([
# dcc.Slider(
# id='slope2_g', min=-2, max=2, step=0.1, value=initial_slope_2_g,
# marks={i: str('') for i in range(-2, 3, 1)},
# tooltip={"placement": "bottom", "always_visible": True}
# ),
# dcc.Slider(
# id='bias2_g', min=-2, max=2, step=0.1, value=initial_bias_2_g,
# marks={i: str(i) for i in range(-2, 3, 1)},
# tooltip={"placement": "bottom", "always_visible": True}
# )
# ], className="three columns"),
html.Div([
html.Div("n1 (w1):"),
html.Div("n1 (w2):",style={'margin-top': '10px'}),
html.Div("n1 (b):",style={'margin-top': '10px'})
], className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w11_g', min=-2, max=2, step=0.1, value=initial_w11_g,
marks={float(i): str('') for i in range(-1, 3, 1)},
tooltip = {"placement": "bottom", "always_visible": True}
),
dcc.Slider(
id='w12_g', min=-2, max=2, step=0.1, value=initial_w12_g,
marks={float(i): str('') for i in range(-1, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
),
dcc.Slider(
id='bias1_g', min=-2, max=2, step=0.1, value=initial_bias_1_g,
marks={i: str(i) for i in range(-1, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
),
], className="three columns"),
html.Div([
html.Div("n2 (w1):"),
html.Div("n2 (w2):",style={'margin-top': '10px'}),
html.Div("n2 (b):",style={'margin-top': '10px'})
], className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w21_g', min=-2, max=2, step=0.1, value=initial_w21_g,
marks={float(i): str('') for i in range(-1, 3, 1)},
tooltip = {"placement": "bottom", "always_visible": True}
),
dcc.Slider(
id='w22_g', min=-2, max=2, step=0.1, value=initial_w22_g,
marks={float(i): str('') for i in range(-1, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
),
dcc.Slider(
id='bias2_g', min=-2, max=2, step=0.1, value=initial_bias_2_g,
marks={i: str(i) for i in range(-1, 3, 1)},
tooltip={"placement": "bottom", "always_visible": True}
),
], className="three columns"),
html.Div([
html.Div("n3 (w1):"),
html.Div("n3 (w2):",style={'margin-top': '10px'}),
html.Div("n3 (b):",style={'margin-top': '10px'})
], className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w31_g', min=-1, max=1, step=0.1, value=initial_w31_g,
marks={float(i): str('') for i in range(-1, 2, 1)},
tooltip = {"placement": "bottom", "always_visible": True}
),
dcc.Slider(
id='w32_g', min=-1, max=1, step=0.1, value=initial_w32_g,
marks={float(i): str('') for i in range(-1, 2, 1)},
tooltip={"placement": "bottom", "always_visible": True}
),
dcc.Slider(
id='bias3_g', min=-1, max=1, step=0.1, value=initial_bias_3_g,
marks={i: str(i) for i in range(-1, 2, 1)},
tooltip={"placement": "bottom", "always_visible": True}
),
], className="three columns"),
], className="row"),
], className="slide"),
html.Div([
html.H1("Composició de funcions (de nou!)"),
html.P(["Normalment s'apliquen diferents funcions a la sortida d'",
html.Strong("f(x) = w·x + b")]),
html.Ul([
html.Li(["Funció esglaó: ", html.Strong("step(z) = {0 si z<0, 1 si no}")]),
html.Li(["Funció sigmoide: ", html.Strong("sigmoid(z) = 1 / (1 + e^-z)")]),
html.Li(["Lineal rectificada: ", html.Strong("relu(z) = max(0, z)")])
]),
dcc.Graph(id='activation-plot-h'),
html.Div([
html.Div('Pes (w)', className="two columns", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='weight-slider-h', min=-5, max=5, step=0.1, value=initial_weight_h,
marks={i: str(i) for i in range(-5, 6, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="four columns"),
html.Div('Biaix (b)', className="two columns", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='bias-slider-h', min=-5, max=5, step=0.1, value=initial_bias_h,
marks={i: str(i) for i in range(-5, 6, 1)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="four columns"),
], className="row"),
], className="slide"),
html.Div([
html.H1("Descens del gradient en regressió lineal"),
html.P(
"Ajustar automàticament la funció a les dades. "),
html.Ul([
html.Li("Per persona, l'error quadràtic per persona és: e(y,y') = (y - y')²=(y - w·x-b)²."),
html.Li("Per totes les persones: "
"[(y1 - w·x1-b)² +...+ (yn - w·xn-b)²]/n."),
html.Li("Busquem els paràmetres w i b amb menor error quadràtic mitjà.")
]),
html.Br(),
html.Div([
html.Div([dcc.Graph(id='gd-scatter-j')], className="eight columns"),
html.Div([dcc.Graph(id='gd-loss-surface-j')], className="four columns"),
], className="row"),
html.Div([
html.Div('Alfa', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='lr-slider-j',
min=0.001, max=0.2, step=0.001, value=initial_alpha_j,
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
html.Div('w', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w-slider-j',
min=-1, max=1, step=0.1, value=initial_w_j,
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
html.Div('b', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='b-slider-j',
min=-20, max=80, step=1, value=initial_b_j,
#marks={i: str(i) for i in range(-20, 81, 20)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="three columns"),
], className="row"),
], className="slide"),
html.Div([
html.H1("Descens del gradient en regressió logística"),
html.P(
"Ajustar automàticament la funció a les dades. "),
#html.Ul([
# html.Li("Per persona, l'error quadràtic per persona és: e(y,y') = (y - y')²=(y - w·x-b)²."),
# html.Li("Per totes les persones: "
# "[(y1 - w·x1-b)² +...+ (yn - w·xn-b)²]/n."),
# html.Li("Busquem els paràmetres w i b amb menor error quadràtic mitjà.")
#]),
html.Br(),
html.Div([
html.Div([dcc.Graph(id='gd-scatter-k')], className="six columns"),
html.Div([dcc.Graph(id='gd-scatter3d-k')], className="three columns"),
html.Div([dcc.Graph(id='gd-loss-surface-k')], className="three columns"),
], className="row"),
html.Div([
html.Div('Alfa', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='lr-slider-k',
min=0.01, max=0.3, step=0.01, value=initial_alpha_k,
tooltip={"placement": "bottom", "always_visible": True}
)
], className="two columns"),
html.Div('w1', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w1-slider-k',
min=-2, max=2, step=0.1, value=initial_w1_k,
tooltip={"placement": "bottom", "always_visible": True}
)
], className="two columns"),
html.Div('w2', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='w2-slider-k',
min=-2, max=2, step=0.1, value=initial_w2_k,
tooltip={"placement": "bottom", "always_visible": True}
)
], className="two columns"),
html.Div('b', className="one column", style={'text-align': 'right'}),
html.Div([
dcc.Slider(
id='b-slider-k',
min=-10, max=10, step=0.1, value=initial_b_k,
#marks={i: str(i) for i in range(-20, 81, 20)},
tooltip={"placement": "bottom", "always_visible": True}
)
], className="two columns"),
], className="row"),
], className="slide"),
])
# ------------------------
# Callback for First Plot
# ------------------------
@app.callback(
Output('plot-adjustable', 'figure'),
Input('slope-slider', 'value'),
Input('bias-slider', 'value')
)
def update_adjustable_plot(slope, bias):
y_line = slope * x_line1 + bias
scatter = go.Scatter(x=height, y=weight, mode='markers',
name='Persones',
marker=dict(color='teal', opacity=0.6))
line = go.Scatter(x=x_line1, y=y_line, mode='lines',
name='Funció',
line=dict(color='darkorange'))
fig = go.Figure(data=[scatter, line])
fig.update_layout(
xaxis=dict(range=[140, 200], title='Alçada (cm)'),
yaxis=dict(range=[40, 120], title='Pes (kg)'),
width=1350,
height=400,
margin=dict(t=20)
)
return fig
# ------------------------
# Static Second Plot
# ------------------------
@app.callback(
Output('plot-regression', 'figure'),
Input('plot-adjustable', 'figure') # Dummy dependency to ensure rendering
)
def render_static_regression(_):
scatter = go.Scatter(x=height, y=weight, mode='markers', name='Dades',
marker=dict(color='teal', opacity=0.6))
regression_line = go.Scatter(x=x_line2, y=y_line2, mode='lines', name='Funció',
line=dict(color='darkorange', width=2))
predicted_point = go.Scatter(x=[x_pred], y=[y_pred], mode='markers+text', name='Avaluació',
marker=dict(color='red', size=5),
text=[f'({x_pred}, {y_pred:.1f})'],
textposition="bottom right")
given_point_x = go.Scatter(x=[x_pred], y=[40], mode='markers+text', name='Punt donat',
marker=dict(color='blue', size=10),
text=[f'{x_pred}'],
textposition="top right")
predicted_point_y = go.Scatter(x=[140], y=[y_pred], mode='markers+text', name='Predicció',
marker=dict(color='red', size=10),
text=[f'y=f(x)={y_pred:.1f}'],
textposition="top right")
v_line = go.Scatter(x=[x_pred, x_pred], y=[40, y_pred], mode='lines',
line=dict(color='gray', dash='dot'), showlegend=False)
h_line = go.Scatter(x=[140, x_pred], y=[y_pred, y_pred], mode='lines',
line=dict(color='gray', dash='dot'), showlegend=False)
fig = go.Figure(data=[
scatter, regression_line,
given_point_x, predicted_point, predicted_point_y,
v_line, h_line
])
fig.update_layout(
xaxis=dict(title='Alçada (cm)', range=[140, 200]),
yaxis=dict(title='Pes (kg)', range=[40, 120]),
width=1350,
height=400,
margin=dict(t=20)
)
return fig
# ------------------------
# Callback for Third Plot
# ------------------------
@app.callback(
Output('loss-plot', 'figure'),
Output('loss-surface', 'figure'),
Input('slope-slider-2', 'value'),
Input('bias-slider-2', 'value')
)
def update_graphs45(slope, bias):
# Line regression
y_line3 = slope * x_line3 + bias
y_pred = slope * x_pred2 + bias
v_line = go.Scatter(x=[x_pred2, x_pred2], y=[y_real, y_pred], mode='lines',
line=dict(color='red', dash='dot'), showlegend=False)
fig4 = go.Figure([
go.Scatter(x=s_height, y=s_weight, mode='markers', name='Dades',
marker=dict(color='teal', opacity=0.6)),
go.Scatter(x=x_line3, y=y_line3, mode='lines', name='Funció',
line=dict(color='darkorange', width=2)),
v_line
])
fig4.update_layout(
xaxis=dict(title='Alçada (cm)', range=[140, 200]),
yaxis=dict(title='Pes (kg)', range=[40, 120]),
width=800,
height=350,
margin=dict(l=20, r=20, b=20, t=40)
)
# Loss surface
loss = compute_loss(np.array([slope]), np.array([bias]), s_height, s_weight)[0, 0]
point = go.Scatter3d(x=[slope], y=[bias], z=[loss], mode='markers',
marker=dict(size=5, color='red'), name='Current (a,b)')
surface = go.Surface(x=A, y=B, z=Z, colorscale='Viridis', showscale=False, name='Loss Surface')
fig5 = go.Figure([surface, point])
fig5.update_layout(
scene=dict(
xaxis_title='a (pendent)',
yaxis_title='b (biaix)',
zaxis_title='Error'
),
width=350,
height=350,
margin=dict(l=20, r=20, b=20, t=40),
showlegend=False
)
return fig4, fig5
# ------------------------
# Callback for Third-and-a-half Plot
# ------------------------
@app.callback(
Output("gradient-plot","figure"),
Input("lr-slider","value"),
Input("x0-slider","value")
)
def update_plot(lr,x0):
xs, ys = gradient_descent(x0=x0, lr=lr)
fig = make_subplots(
rows=2, cols=1,
shared_xaxes=True,
subplot_titles=("Funció g(w)", "Derivada g'(w)"),
vertical_spacing = 0.05
)
# funció
fig.add_trace(
go.Scatter(
x=x_c,
y=f_c(x_c),
mode="lines",
name="g(w)"
),
row=1, col=1
)
# camí gradient descent
fig.add_trace(
go.Scatter(
x=xs,
y=ys,
mode="markers+lines",
name="Gradient descent"
),
row=1, col=1
)
# derivada
fig.add_trace(
go.Scatter(
x=x_c,
y=df_c(x_c),
mode="lines",
name="g'(w)"
),
row=2, col=1
)
# punts sobre la derivada
fig.add_trace(
go.Scatter(
x=xs,
y=df_c(xs),
mode="markers",
name="Gradient"
),
row=2, col=1
)
# --- BARRES DEL GRADIENT ---
for xi in xs:
gi = df_c(xi)
fig.add_shape(
type="line",
x0=xi,
x1=xi,
y0=0,
y1=gi,
xref="x",
yref="y2",
line=dict(width=3)
)
fig.update_layout(
height=700,
showlegend=False
)
return fig
# ------------------------
# Callback for Fourth Plot
# ------------------------
@app.callback(
[Output('graph1_d', 'figure'),
Output('graph2_d', 'figure')],
[Input('slope-slider_d', 'value'),
Input('bias-slider_d', 'value')]
)
def update_graphs_d(slope_d, bias_d):
y_line_d = slope_d * x_line_d + bias_d
x_ortho_d, y_ortho_d = compute_orthogonal_vector(slope_d, bias_d)
# First graph (original)
fig1_d = go.Figure()
fig1_d.add_trace(go.Scatter(x=x1_d, y=y1_d, mode='markers', name='Grup A', marker=dict(color='blue')))
fig1_d.add_trace(go.Scatter(x=x2_d, y=y2_d, mode='markers', name='Grup B', marker=dict(color='red')))
fig1_d.add_trace(go.Scatter(x=x_line_d, y=y_line_d, mode='lines', name='Funció', line=dict(color='green')))
fig1_d.add_trace(go.Scatter(x=x_ortho_d, y=y_ortho_d, mode='lines+markers', name='Vector ortogonal',
line=dict(color='purple', dash='dot')))
fig1_d.update_layout(width=675, height=500)
# Second graph (orthogonal coordinates)
x1_proj_d, y1_proj_d = transform_to_line_coordinates(x1_d, y1_d, slope_d, bias_d)
x2_proj_d, y2_proj_d = transform_to_line_coordinates(x2_d, y2_d, slope_d, bias_d)
fig2_d = go.Figure()
fig2_d.add_trace(go.Scatter(x=x1_proj_d, y=y1_proj_d, mode='markers', marker=dict(color='blue'), name='Grup A',
showlegend=False))
fig2_d.add_trace(
go.Scatter(x=x2_proj_d, y=y2_proj_d, mode='markers', marker=dict(color='red'), name='Grup B', showlegend=False))
fig2_d.update_layout(width=575, height=500)
return fig1_d, fig2_d
# ------------------------
# Callback for Fourth Plot B
# ------------------------
@app.callback(
[Output('graph1_d2', 'figure'),
Output('graph2_d2', 'figure')],
[Input('w1-slider_d2', 'value'),
Input('w2-slider_d2', 'value'),
Input('bias-slider_d2', 'value')]
)
def update_graphs_d2(w1_d, w2_d, bias_d):
#y_line_d = w1_d * xy_line_d + w2_d * xy_line_d + bias_d
#y_line_d = (-w1_d * xy_line_d - bias_d) / w2_d
if abs(w2_d) > 1e-6:
y_line_d = (-w1_d * xy_line_d + bias_d) / w2_d
x_line_d = xy_line_d
else:
x_line_d = np.full_like(xy_line_d, + bias_d / w1_d)
y_line_d = xy_line_d
x_ortho_d, y_ortho_d = compute_orthogonal_vector_b(w1_d, w2_d, bias_d)
# First graph (original)
fig1_d = go.Figure()
fig1_d.add_trace(go.Scatter(x=x1_d, y=y1_d, mode='markers', name='Grup A', marker=dict(color='blue')))
fig1_d.add_trace(go.Scatter(x=x2_d, y=y2_d, mode='markers', name='Grup B', marker=dict(color='red')))
fig1_d.add_trace(go.Scatter(x=x_line_d, y=y_line_d, mode='lines', name='Funció', line=dict(color='green')))
fig1_d.add_trace(go.Scatter(x=x_ortho_d, y=y_ortho_d, mode='lines+markers', name='Vector ortogonal',
line=dict(color='purple', dash='dot')))
fig1_d.update_layout(width=675, height=500)
# Second graph (orthogonal coordinates)
x1_proj_d, y1_proj_d = transform_to_line_coordinates_b(x1_d, y1_d, w1_d, w2_d, bias_d)
x2_proj_d, y2_proj_d = transform_to_line_coordinates_b(x2_d, y2_d, w1_d, w2_d, bias_d)
fig2_d = go.Figure()
fig2_d.add_trace(go.Scatter(x=x1_proj_d, y=y1_proj_d, mode='markers', marker=dict(color='blue'), name='Grup A',
showlegend=False))
fig2_d.add_trace(
go.Scatter(x=x2_proj_d, y=y2_proj_d, mode='markers', marker=dict(color='red'), name='Grup B', showlegend=False))
fig2_d.update_layout(width=575, height=500)
return fig1_d, fig2_d
# ------------------------
# Callback for Fifth Plot
# ------------------------
@app.callback(
[Output('graph6_e', 'figure'),
Output('graph7_e', 'figure')],
[Input('w1-slider_e', 'value'),
Input('w2-slider_e', 'value'),
Input('bias-slider_e', 'value')]
)
def update_figures_e(w1_e, w2_e, b_e):
#y_line_e = a_e * x_line_d + b_e
#x_ortho_e, y_ortho_e = compute_orthogonal_vector(a_e, b_e)
if abs(w2_e) > 1e-6:
y_line_e = (-w1_e * xy_line_d + b_e) / w2_e
x_line_e = xy_line_d
else:
x_line_e = np.full_like(xy_line_d, + b_e / w1_e)
y_line_e = xy_line_d
x_ortho_e, y_ortho_e = compute_orthogonal_vector_b(w1_e, w2_e, b_e)
# Graph 6
scatter_group_a_e = go.Scatter(x=x1_d, y=y1_d, mode='markers', name='Grup A', marker=dict(color='blue'))
scatter_group_b_e = go.Scatter(x=x2_d, y=y2_d, mode='markers', name='Grup B', marker=dict(color='red'))
line_e = go.Scatter(x=x_line_e, y=y_line_e, mode='lines', name='Funció', line=dict(color='green'))
orthogonal_e = go.Scatter(x=x_ortho_e, y=y_ortho_e, mode='lines+markers', name='vector ortogonal', line=dict(color='purple', dash='dot'))
fig6_e = go.Figure(data=[scatter_group_a_e, scatter_group_b_e, line_e, orthogonal_e])
fig6_e.update_layout(width=600, height=500)
# Graph 7
x_ortho_a_e, y_ortho_a_e = transform_to_line_coordinates_b(x1_d, y1_d, w1_e, w2_e, b_e)
x_ortho_b_e, y_ortho_b_e = transform_to_line_coordinates_b(x2_d, y2_d, w1_e, w2_e, b_e)
z_a_e = step(y_ortho_a_e)
z_b_e = step(y_ortho_b_e)
scatter3d_a_e = go.Scatter3d(x=x_ortho_a_e, y=y_ortho_a_e, z=z_a_e, mode='markers', marker=dict(size=4, color='blue'),
name='Grup A', showlegend=False)
scatter3d_b_e = go.Scatter3d(x=x_ortho_b_e, y=y_ortho_b_e, z=z_b_e, mode='markers', marker=dict(size=4, color='red'),
name='Grup B', showlegend=False)
fig7_e = go.Figure(data=[scatter3d_a_e, scatter3d_b_e])
fig7_e.update_layout(width=600, height=500, scene=dict(
xaxis_title="U",
yaxis_title="V",
zaxis_title="Step"
))
return fig6_e, fig7_e
# ------------------------
# Callback for Sixth Plot
# ------------------------
@app.callback(
[Output('graph2d_f', 'figure'),
Output('graph3d_f', 'figure')],
[Input('w1-slider_f', 'value'),
Input('w2-slider_f', 'value'),
Input('bias-slider_f', 'value')]
)
def update_graphs_f(w1_f, w2_f, b_f):
if abs(w2_f) > 1e-6:
y_line_f = (-w1_f * xy_line8_f + b_f) / w2_f
x_line_f = xy_line8_f
else:
x_line_f = np.full_like(xy_line8_f, + b_f / w1_f)
y_line_f = xy_line8_f
# Update 2D
#y_line_updated_f = a_f * x_line8_f + b_f
line_updated_f = go.Scatter(x=x_line_f, y=y_line_f, mode='lines', name='Funció', line=dict(color='red'))
scatter_group_ff_f = go.Scatter(x=[0], y=[0], mode='markers', name='FF', marker=dict(color='blue', size=20))
scatter_group_tf_f = go.Scatter(x=[0, 1], y=[1, 0], mode='markers', name='CF', marker=dict(color='green', size=20))
scatter_group_tt_f = go.Scatter(x=[1], y=[1], mode='markers', name='CC', marker=dict(color='yellow', size=20))
fig2d_f = go.Figure(data=[
scatter_group_ff_f, scatter_group_tf_f, scatter_group_tt_f, line_updated_f
])
fig2d_f.update_layout(width=600, height=500,
showlegend=True, scene=dict(
xaxis=dict(tickvals=[0, 1]),
yaxis=dict(tickvals=[0, 1])))
# Update 3D
u_ff_f, v_ff_f = transform_to_line_coordinates_b([0], [0], w1_f, w2_f, b_f)
u_tf_f, v_tf_f = transform_to_line_coordinates_b([0, 1], [1, 0], w1_f, w2_f, b_f)
u_tt_f, v_tt_f = transform_to_line_coordinates_b([1], [1], w1_f, w2_f, b_f)
scatter3d_ff_updated_f = go.Scatter3d(x=[0], y=[0], z=step(v_ff_f),
mode='markers', marker=dict(size=10, color='blue'), name='FF',
showlegend=False)
scatter3d_tf_updated_f = go.Scatter3d(x=[0, 1], y=[1, 0], z=step(v_tf_f),
mode='markers', marker=dict(size=10, color='green'), name='CF',
showlegend=False)
scatter3d_tt_updated_f = go.Scatter3d(x=[1], y=[1], z=step(v_tt_f),
mode='markers', marker=dict(size=10, color='yellow'), name='CC',
showlegend=False)
fig3d_f = go.Figure(data=[scatter3d_ff_updated_f, scatter3d_tf_updated_f, scatter3d_tt_updated_f])
fig3d_f.update_layout(width=600, height=500, scene=dict(
xaxis_title="U",
yaxis_title="V",
zaxis_title="Step",
xaxis=dict(tickvals=[0, 1]),
yaxis=dict(tickvals=[0, 1]),
zaxis=dict(tickvals=[0, 1])
))
return fig2d_f, fig3d_f
# ------------------------
# Callback for Seventh Plot
# ------------------------
@app.callback(
Output('fig1_g', 'figure'),
Output('fig2_g', 'figure'),
Input('w11_g', 'value'),
Input('w12_g', 'value'),
Input('bias1_g', 'value'),
Input('w21_g', 'value'),
Input('w22_g', 'value'),
Input('bias2_g', 'value'),
Input('w31_g', 'value'),
Input('w32_g', 'value'),
Input('bias3_g', 'value'),
)
def update_figures_g(w11, w12, b1, w21, w22, b2, w31, w32, b3):
# Línies
if abs(w12) > 1e-6:
y_line_g1 = (-w11 * x_line20_g + b1) / w12
x_line_g1 = x_line20_g
else:
x_line_g1 = np.full_like(x_line20_g, + b1 / w11)
y_line_g1 = x_line20_g
line21_g = go.Scatter(x=x_line_g1, y=y_line_g1, mode='lines', name='Neurona 1', line=dict(color='red'))
#line21_g = go.Scatter(x=x_line20_g, y=a1 * x_line20_g + b1, mode='lines', name='Neurona 1', line=dict(color='red'))
if abs(w22) > 1e-6:
y_line_g2 = (-w21 * x_line20_g + b2) / w22
x_line_g2 = x_line20_g
else:
x_line_g2 = np.full_like(x_line20_g, + b2 / w21)
y_line_g2 = x_line20_g
line22_g = go.Scatter(x=x_line_g2, y=y_line_g2, mode='lines', name='Neurona 2', line=dict(color='pink'))
#line22_g = go.Scatter(x=x_line20_g, y=a2 * x_line20_g + b2, mode='lines', name='Neurona 2', line=dict(color='pink'))
scatter_group_ff_g = go.Scatter(x=[0], y=[0], mode='markers', name='FF', marker=dict(color='blue', size=20))
scatter_group_tf_g = go.Scatter(x=[0, 1], y=[1, 0], mode='markers', name='CF', marker=dict(color='green', size=20))
scatter_group_tt_g = go.Scatter(x=[1], y=[1], mode='markers', name='CC', marker=dict(color='yellow', size=20))
# Projeccions i sortides
def process_inputs_g(xi, yi):
_, y1 = transform_to_line_coordinates_b(xi, yi, w11, w12, b1)
_, y2 = transform_to_line_coordinates_b(xi, yi, w21, w22, b2)
z1 = step(y1)
z2 = step(y2)
return step(z1 * w31 + z2 * w32 + b3)
z_ff = process_inputs_g([0], [0])
z_tf = process_inputs_g([0,1], [1,0])
z_tt = process_inputs_g([1], [1])
# Figures
fig20_g = go.Figure(data=[scatter_group_ff_g, scatter_group_tf_g, scatter_group_tt_g,
line21_g, line22_g])
fig20_g.update_layout(width=600, height=500, showlegend=True, scene=dict(
xaxis_title="X",
yaxis_title="Y",
xaxis=dict(tickvals=[0, 1]),
yaxis=dict(tickvals=[0, 1]),
))
fig23_g = go.Figure(data=[
go.Scatter3d(x=[0], y=[0], z=z_ff, mode='markers', marker=dict(size=10, color='blue'), name='FF',
showlegend=False),
go.Scatter3d(x=[0,1], y=[1,0], z=z_tf, mode='markers', marker=dict(size=10, color='green'), name='TF',
showlegend=False),
go.Scatter3d(x=[1], y=[1], z=z_tt, mode='markers', marker=dict(size=10, color='yellow'), name='TT',
showlegend=False),
])
fig23_g.update_layout(width=600, height=500, scene=dict(
xaxis_title="X",
yaxis_title="Y",
zaxis_title="XOR",
xaxis=dict(tickvals=[0, 1]),
yaxis=dict(tickvals=[0, 1]),
zaxis=dict(tickvals=[0, 1])
))
return fig20_g, fig23_g
# ------------------------
# Callback for Eighth Plot
# ------------------------
# Callback
@app.callback(
Output('activation-plot-h', 'figure'),
Input('weight-slider-h', 'value'),
Input('bias-slider-h', 'value')
)
def update_plot_h(w_h, b_h):
z_h = w_h * x_vals_h + b_h
line_step_h = go.Scatter(x=x_vals_h, y=step(z_h), mode='lines', name='Step', line=dict(color='blue'))
line_sigmoid_h = go.Scatter(x=x_vals_h, y=sigmoid(z_h), mode='lines', name='Sigmoid', line=dict(color='green'))
line_relu_h = go.Scatter(x=x_vals_h, y=relu(z_h), mode='lines', name='ReLU', line=dict(color='red'))
fig_h = go.Figure(data=[line_step_h, line_sigmoid_h, line_relu_h])
fig_h.update_layout(
width=1350,
height=400,
xaxis_title="z",
yaxis=dict(
title="Activació",
range=[-0.5, 4.5] # Adjust this to your desired y-axis range
),
)
return fig_h
# ------------------------
# Callback for Nineth Plot
# ------------------------
# Callback
@app.callback(
Output('sample-vs-complexity-graph', 'figure'),
Input('samplesize-slider', 'value'),
Input('polydegree-slider', 'value')
)
def update_sample_vs_complexity_figure(n_samples, degree):
x, y = generate_samples_i(n_samples, margin=[-2,1.5], noise_std=0.2)
x_true = np.linspace(-2, 2, 200)
y_true = actual_func_i(x_true)
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
model.fit(x.reshape(-1, 1), y)
y_pred = model.predict(x_true.reshape(-1, 1))
fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y, mode='markers', name='Dades', marker=dict(color='rgba(0, 155, 0, 1)')))
fig.add_trace(
go.Scatter(x=x_true, y=y_true, mode='lines', name='Funció original', line=dict(dash='dash', color='rgba(255, 100, 100, 0.5)')))
fig.add_trace(
go.Scatter(x=x_true, y=y_pred, mode='lines', name=f'Model polinòmic (grau={degree})', line=dict(color='rgba(100, 100, 255, 1)')))
fig.update_layout(
xaxis=dict(range=[-2, 2], title='x'),
yaxis=dict(range=[-3.2, 3.2], title='f'),
height=600,
width=900
)
return fig
@app.callback(
Output('gd-scatter-j', 'figure'),
Output('gd-loss-surface-j', 'figure'),
Input('lr-slider-j', 'value'),
Input('w-slider-j', 'value'),
Input('b-slider-j', 'value')
)
def update_gradient_descent_j(lr, w0, b0):
# --- Run gradient descent ---
mean_x = s_height.mean()
std_x = s_height.std()
s_height_std = (s_height - mean_x) / std_x
w0_std = w0 * std_x
b0_std = w0 * mean_x + b0
path_w_std, path_b_std, path_loss_std = gradient_descent_j(w0_std, b0_std, lr, s_height_std, s_weight)
path_w = path_w_std / std_x
path_b = path_b_std - path_w * mean_x
w_final = path_w[-1]
b_final = path_b[-1]
# Recalcular loss en escala real perquè coincideixi amb la superfície
path_loss = [
np.mean((w*s_height + b - s_weight)**2)
for w, b in zip(path_w, path_b)
]
path_loss = np.array(path_loss)
#a_vals = np.linspace(-1, 1, 100)*std_x
#b_vals = np.linspace(-20, 80, 100)+a_vals*mean_x
#A, B = np.meshgrid(a_vals, b_vals)
#Z = compute_loss(A, B, s_height_std, s_weight)
# -------------------------
# Scatter plot with init and final model
# -------------------------
y_init = w0 * x_line3 + b0
y_final = w_final * x_line3 + b_final
fig_scatter = go.Figure([
go.Scatter(
x=s_height,
y=s_weight,
mode='markers',
name='Dades',
marker=dict(color='teal', opacity=0.6)
),
go.Scatter(
x=x_line3,
y=y_init,
mode='lines',
name='Model Inicial',
line=dict(color='gray', dash='dash', width=2)
),
go.Scatter(
x=x_line3,
y=y_final,
mode='lines',
name=f'Final (w={w_final:.2f}, b={b_final:.1f})',
line=dict(color='darkorange', width=3)
)
])
fig_scatter.update_layout(
xaxis=dict(title='Alçada (cm)', range=[140, 200]),
yaxis=dict(title='Pes (kg)', range=[40, 120]),
width=800,
height=350,
margin=dict(l=20, r=20, b=20, t=40)
)
# -------------------------
# Loss surface with GD path
# -------------------------
surface = go.Surface(
x=A,
y=B,
z=Z,
colorscale='Viridis',
showscale=False
)
path = go.Scatter3d(
x=path_w,
y=path_b,
z=path_loss,
#x=path_w_std,
#y=path_b_std,
#z=path_loss_std,
mode='lines+markers',
line=dict(color='red', width=4),
marker=dict(size=4),
name='DG'
)
start = go.Scatter3d(
x=[path_w[0]],
y=[path_b[0]],
z=[path_loss[0]],
#x=[path_w_std[0]],
#y=[path_b_std[0]],
#z=[path_loss_std[0]],
mode='markers',
marker=dict(size=6, color='orange'),
name='Ini'
)
end = go.Scatter3d(
x=[path_w[-1]],
y=[path_b[-1]],
z=[path_loss[-1]],
#x=[path_w_std[-1]],
#y=[path_b_std[-1]],
#z=[path_loss_std[-1]],
mode='markers',
marker=dict(size=6, color='green'),
name='Fi'
)
fig_surface = go.Figure([surface, path, start, end])
fig_surface.update_layout(
scene=dict(
xaxis_title='w (pendent)',
yaxis_title='b (biaix)',
zaxis_title='Error'
),
width=350,
height=350,
margin=dict(l=20, r=20, b=20, t=40)
)
return fig_scatter, fig_surface
@app.callback(
Output('gd-scatter-k', 'figure'),
Output('gd-scatter3d-k', 'figure'),
Output('gd-loss-surface-k', 'figure'),
Input('lr-slider-k', 'value'),
Input('w1-slider-k', 'value'),
Input('w2-slider-k', 'value'),
Input('b-slider-k', 'value')
)
def update_logistic_gd_k(lr_k, w1_0_k, w2_0_k, b0_k):
# dataset
X1_k = np.concatenate([x1_d, x2_d])
X2_k = np.concatenate([y1_d, y2_d])
Y_k = np.concatenate([np.zeros(len(x1_d)), np.ones(len(x2_d))])
# Normalitzar característiques
mean1_k, std1_k = X1_k.mean(), X1_k.std()
mean2_k, std2_k = X2_k.mean(), X2_k.std()
X1_k_std = (X1_k - mean1_k) / std1_k
X2_k_std = (X2_k - mean2_k) / std2_k
w1_0_std_k = w1_0_k * std1_k
w2_0_std_k = w2_0_k * std2_k
b0_std_k = b0_k + w1_0_k * mean1_k + w2_0_k * mean2_k
# --- run gradient descent ---
path_w1_std_k, path_w2_std_k, path_b_std_k, path_loss_std_k = logistic_gd_k(
w1_0_std_k, w2_0_std_k, b0_std_k, lr_k, X1_k_std, X2_k_std, Y_k, steps=200
)
path_w1_k = path_w1_std_k / std1_k
path_w2_k = path_w2_std_k / std2_k
path_b_k = path_b_std_k - path_w1_k * mean1_k - path_w2_k * mean2_k
path_loss_k = [
binary_cross_entropy_k(Y_k, sigmoid_k(w*X1_k + v*X2_k + b))
for w, v, b in zip(path_w1_k, path_w2_k, path_b_k)
]
path_loss_k = np.array(path_loss_k)
# Pes final
w1_final_k = path_w1_k[-1]
w2_final_k = path_w2_k[-1]
b_final_k = path_b_k[-1]
# -------------------------
# Decision boundary
# -------------------------
xy_line_k = np.linspace(0, 5, 200)
if abs(w2_0_k) > 1e-6:
y_line_init_k = (-w1_0_k*xy_line_k - b0_k)/w2_0_k
x_line_init_k = xy_line_k
else:
x_line_init_k = np.full_like(xy_line_k, -b0_k/w1_0_k)
y_line_init_k = xy_line_k
if abs(w2_final_k) > 1e-6:
y_line_final_k = (-w1_final_k*xy_line_k - b_final_k)/w2_final_k
x_line_final_k = xy_line_k
else:
x_line_final_k = np.full_like(xy_line_k, -b_final_k/w1_final_k)
y_line_final_k = xy_line_k
fig_scatter_k = go.Figure()
fig_scatter_k.add_trace(
go.Scatter(x=x1_d, y=y1_d,
mode='markers',
name='Grup A',
marker=dict(color='blue'))
)
fig_scatter_k.add_trace(
go.Scatter(x=x2_d, y=y2_d,
mode='markers',
name='Grup B',
marker=dict(color='red'))
)
fig_scatter_k.add_trace(
go.Scatter(x=x_line_init_k,
y=y_line_init_k,
mode='lines',
name='Inicial',
line=dict(color='gray', dash='dash'))
)
fig_scatter_k.add_trace(
go.Scatter(x=x_line_final_k,
y=y_line_final_k,
mode='lines',
name=f'Final',
line=dict(color='darkorange', width=3))
)
fig_scatter_k.add_annotation(
x=2, # posició X dins del plot
y=6, # posició Y dins del plot
text=f'Params. model final: w1={w1_final_k:.2f}, w2={w2_final_k:.2f}, b={b_final_k:.2f}',
showarrow=False,
font=dict(size=12, color='darkorange'),
bgcolor='rgba(255,255,255,0.7)'
)
fig_scatter_k.update_layout(width=800, height=400)
z_a_k=sigmoid_k(w1_final_k*x1_d + w2_final_k*y1_d + b_final_k)
z_b_k=sigmoid_k(w1_final_k*x2_d + w2_final_k*y2_d + b_final_k)
scatter3d_a_k = go.Scatter3d(x=x1_d, y=y1_d, z=z_a_k, mode='markers', marker=dict(size=4, color='blue'),
name='Grup A', showlegend=False)
scatter3d_b_k = go.Scatter3d(x=x2_d, y=y2_d, z=z_b_k, mode='markers', marker=dict(size=4, color='red'),
name='Grup B', showlegend=False)
fig_scatter3d_k = go.Figure(data=[scatter3d_a_k, scatter3d_b_k])
fig_scatter3d_k.update_layout(width=400, height=400, scene=dict(
xaxis_title="X1",
yaxis_title="X2",
zaxis_title="p"
))
# -------------------------
# LOSS SURFACE
# -------------------------
min_w1 = min(path_w1_k.min()-1, path_w1_k.min()/2)
max_w1 = max(path_w1_k.max()+1, path_w1_k.max()*2)
min_w2 = min(path_w2_k.min()-1, path_w2_k.min()/2)
max_w2 = max(path_w2_k.max()+1, path_w2_k.max()*2)
w1_vals_k = np.linspace(min_w1, max_w1, 60)
w2_vals_k = np.linspace(min_w2, max_w2, 60)
A_k, B_k = np.meshgrid(w1_vals_k, w2_vals_k)
Z_k = np.zeros_like(A_k)
for i in range(A_k.shape[0]):
for j in range(A_k.shape[1]):
z = A_k[i,j]*X1_k + B_k[i,j]*X2_k + b_final_k
p = sigmoid_k(z)
Z_k[i,j] = binary_cross_entropy_k(Y_k, p)
surface_k = go.Surface(
x=A_k,
y=B_k,
z=Z_k,
colorscale='Viridis',
showscale=False
)
path_k = go.Scatter3d(
x=path_w1_k,
y=path_w2_k,
z=path_loss_k,
mode='lines+markers',
line=dict(color='red', width=1),
marker=dict(size=4),
name='GD'
)
start_k = go.Scatter3d(
x=[path_w1_k[0]],
y=[path_w2_k[0]],
z=[path_loss_k[0]],
mode='markers',
marker=dict(size=6, color='orange'),
name='I'
)
end_k = go.Scatter3d(
x=[path_w1_k[-1]],
y=[path_w2_k[-1]],
z=[path_loss_k[-1]],
mode='markers',
marker=dict(size=6, color='green'),
name='F'
)
fig_surface_k = go.Figure([surface_k, path_k, start_k, end_k])
fig_surface_k.update_layout(
scene=dict(
xaxis_title='w1',
yaxis_title='w2',
zaxis_title='Binary Cross Entropy'
),
width=400,
height=400
)
return fig_scatter_k, fig_scatter3d_k, fig_surface_k
# ------------------------
# Run Server
# ------------------------
if __name__ == '__main__':
app.run(debug=True)