Marcel0123's picture
Upload 2 files
460491a verified
import gradio as gr
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.utils import shuffle
EXPLAIN_MD = """
### Wat testen we hier?
We bekijken of er een **lineair verband** is tussen **BMI** en de **diabetes-progressiescore** in een bekende (openbare) dataset.
Dat doen we met *supervised learning*: het model ziet voorbeelden `(BMI → score)` en leert een lijn \(y = w x + b\) die dit verband benadert.
**Hoe meten we of dat gelukt is?**
- We splitsen de data in **train (80%)** en **test (20%)**.
- We **trainen** het model alleen op de **trainset**.
- We **toetsen** het resultaat op de **testset** die het model niet gezien heeft.
- We rapporteren **MSE** (gemiddelde kwadratische fout) en **R²** (uitlegvariantie) op de testset.
> Let op: in deze sklearn-dataset is BMI **genormaliseerd** (geschaald). De helling `w` geeft wel de **richting en sterkte** aan (positief = hogere BMI hangt samen met hogere score).
"""
STORY_MD = r"""
### Waarom kijken we naar BMI en diabetes?
Stel je voor dat je arts wilt begrijpen of **het gewicht van mensen** (uitgedrukt als *Body Mass Index*, BMI)
iets zegt over hun **gezondheid**. Een van de dingen die onderzocht wordt is het verband tussen BMI en de
**ernst van diabetes**.
We gebruiken hier **echte gegevens** uit een medische dataset (dus **geen foto’s**, maar gemeten waarden van mensen
die in een onderzoek hebben meegedaan). Elke deelnemer heeft:
- een **BMI-waarde** (hoe zwaar of licht iemand is ten opzichte van zijn lengte),
- en een **score** die aangeeft hoe ernstig de diabetes bij die persoon verloopt.
Met lineaire regressie testen we: *kunnen we een lijn tekenen die laat zien of een hogere BMI vaak samenvalt met een
hogere (of juist lagere) score?*
**Waarom is dat belangrijk?**
- Als er wél een duidelijk verband is, kan dit helpen om **risico’s eerder te signaleren**.
- Als er géén verband is, leren we dat BMI misschien niet de juiste voorspeller is en moet er verder gekeken worden
naar andere factoren.
Kortom: dit experiment laat je zien hoe data ons kan helpen om **patronen in gezondheid** te ontdekken — en dat doen
we hier stap voor stap, live op je scherm.
"""
CONCLUSION_MD = r"""
# **Conclusie**
Mensen met een hogere **BMI** hebben in dit onderzoek gemiddeld vaker een ernstiger verloop van **diabetes**.
Maar **BMI is niet de enige factor** — leeftijd, erfelijkheid, leefstijl en andere medische waarden spelen ook mee.
"""
def load_bmi_diabetes():
d = datasets.load_diabetes()
X = d.data[:, 2] # BMI feature (genormaliseerd)
y = d.target # Progressiescore
return X.astype(np.float64), y.astype(np.float64), "Diabetes: BMI vs. score"
def train_test_split_1d(x, y, test_size=0.2, seed=42):
rng = np.random.RandomState(seed)
idx = np.arange(x.shape[0])
rng.shuffle(idx)
n_test = int(len(idx) * test_size)
test_idx = idx[:n_test]
train_idx = idx[n_test:]
return x[train_idx], y[train_idx], x[test_idx], y[test_idx]
def sgd_train_generator(lr, epochs, batch_size, seed, split_seed):
# Data & split
x, y, label = load_bmi_diabetes()
x_tr, y_tr, x_te, y_te = train_test_split_1d(x, y, test_size=0.2, seed=int(split_seed))
n = x_tr.shape[0]
w, b = 0.0, 0.0
x_min, x_max = float(np.min(x)), float(np.max(x))
train_losses, test_losses = [], []
rng = np.random.RandomState(int(seed))
for epoch in range(1, int(epochs) + 1):
# shuffle train set
x_tr, y_tr = shuffle(x_tr, y_tr, random_state=rng)
# SGD over mini-batches
for start in range(0, n, int(batch_size)):
end = min(start + int(batch_size), n)
xb, yb = x_tr[start:end], y_tr[start:end]
yhat = w * xb + b
err = yb - yhat
dw = -(2.0 / xb.size) * np.sum(xb * err)
db = -(2.0 / xb.size) * np.sum(err)
w -= lr * dw
b -= lr * db
# Metrics on train and test
y_tr_pred = w * x_tr + b
y_te_pred = w * x_te + b
mse_tr = float(np.mean((y_tr - y_tr_pred)**2))
mse_te = float(np.mean((y_te - y_te_pred)**2))
# R^2 on test
ss_res = float(np.sum((y_te - y_te_pred)**2))
ss_tot = float(np.sum((y_te - np.mean(y_te))**2))
r2_te = 1.0 - ss_res / ss_tot if ss_tot > 0 else float("nan")
train_losses.append(mse_tr)
test_losses.append(mse_te)
# Plot 1: data (train vs test) + regressielijn
fig_main = plt.figure(figsize=(7, 4))
ax1 = fig_main.add_subplot(111)
ax1.scatter(x_tr, y_tr, alpha=0.6, s=18, label="train")
ax1.scatter(x_te, y_te, alpha=0.8, s=22, marker="x", label="test")
xs = np.linspace(x_min, x_max, 200)
ax1.plot(xs, w * xs + b, linewidth=2, label="model")
ax1.set_title(f"{label} — Epoch {epoch}/{epochs}")
ax1.set_xlabel("BMI (genormaliseerd)")
ax1.set_ylabel("Progressiescore")
ax1.legend()
ax1.grid(True, linestyle=":", linewidth=0.6)
plt.tight_layout()
# Plot 2: loss-curve (train & test)
fig_loss = plt.figure(figsize=(7, 3.5))
ax2 = fig_loss.add_subplot(111)
ax2.plot(range(1, len(train_losses)+1), train_losses, marker="o", label="Train MSE")
ax2.plot(range(1, len(test_losses)+1), test_losses, marker="o", linestyle="--", label="Test MSE")
ax2.set_title("Loss-curve (MSE per epoch) — lager is beter")
ax2.set_xlabel("Epoch")
ax2.set_ylabel("MSE")
ax2.legend()
ax2.grid(True, linestyle=":", linewidth=0.6)
plt.tight_layout()
# Resultaten + opvallende conclusie
verdict = "positief" if w >= 0 else "negatief"
summary = (
f"**Wat levert dit op?**\n"
f"- Huidige regressielijn: `y = {w:.4f} * x + {b:.4f}`\n"
f"- Train MSE: `{mse_tr:.2f}` — Test MSE: `{mse_te:.2f}` — Test R²: `{r2_te:.3f}`\n"
f"- Interpretatie: het verband tussen BMI en progressiescore is **{verdict}** in deze dataset "
f"(hogere BMI hangt samen met hogere score als `w > 0`).\n\n"
f"{CONCLUSION_MD}"
)
yield fig_main, fig_loss, summary
with gr.Blocks(title="Diabetes: BMI → Progressiescore (Live Regressie)") as demo:
gr.Markdown("# Diabetes: BMI → Progressiescore (Live Lineaire Regressie)")
gr.Markdown(EXPLAIN_MD)
with gr.Row():
with gr.Column(scale=1):
lr = gr.Slider(1e-4, 1e-0, value=5e-3, step=1e-4, label="Learning rate")
epochs = gr.Slider(5, 200, value=60, step=1, label="Epochs")
batch = gr.Slider(8, 256, value=64, step=1, label="Batchgrootte")
seed = gr.Slider(0, 9999, value=42, step=1, label="Training seed")
split_seed = gr.Slider(0, 9999, value=7, step=1, label="Train/test split seed")
train_btn = gr.Button("Train live")
# Story direct onder de knop
gr.Markdown(STORY_MD)
with gr.Column(scale=2):
plot_main = gr.Plot(label="Data (train/test) & regressielijn (live)")
plot_loss = gr.Plot(label="Loss-curve (MSE per epoch) — train vs test")
results = gr.Markdown()
# Training starten via knop
train_btn.click(
fn=sgd_train_generator,
inputs=[lr, epochs, batch, seed, split_seed],
outputs=[plot_main, plot_loss, results]
)
# Auto-train bij laden met default-waarden
demo.load(
fn=sgd_train_generator,
inputs=[lr, epochs, batch, seed, split_seed],
outputs=[plot_main, plot_loss, results]
)
if __name__ == "__main__":
demo.launch()