Marcel0123's picture
Upload 2 files
f8f46b8 verified
raw
history blame
9.78 kB
import gradio as gr
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.utils import shuffle
import csv, os, tempfile
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))
state_for_download = None
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}"
)
# Bewaar state voor download (laatste epoch)
state_for_download = {
"x_test": x_te,
"y_test": y_te,
"y_pred": y_te_pred,
"w": w,
"b": b,
"mse_train": mse_tr,
"mse_test": mse_te,
"r2_test": r2_te,
}
yield fig_main, fig_loss, summary, state_for_download
def prepare_download(state):
if not state:
return None
# Schrijf CSV met test set en voorspellingen
fd, path = tempfile.mkstemp(suffix="_diabetes_bmi_results.csv")
os.close(fd)
with open(path, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["bmi_normalized", "y_true", "y_pred", "residual"])
for x, yt, yp in zip(state["x_test"], state["y_test"], state["y_pred"]):
writer.writerow([float(x), float(yt), float(yp), float(yt-yp)])
# Voeg onderaan een lege regel + metrics toe
writer.writerow([])
writer.writerow(["w", state["w"]])
writer.writerow(["b", state["b"]])
writer.writerow(["mse_train", state["mse_train"]])
writer.writerow(["mse_test", state["mse_test"]])
writer.writerow(["r2_test", state["r2_test"]])
return path
with gr.Blocks(title="Diabetes: BMI → Progressiescore (Live Regressie)") as demo:
# Custom CSS to color the buttons (using elem_id selectors)
gr.HTML("""
<style>
#train-btn button { background:#2563eb; color:white; border:none; }
#train-btn button:hover { filter: brightness(0.95); }
#download-btn button { background:#059669; color:white; border:none; }
#download-btn button:hover { filter: brightness(0.95); }
</style>
""")
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", elem_id="train-btn", variant="primary")
download_btn = gr.DownloadButton(
label="Download resultaten (CSV)", elem_id="download-btn", file_name="diabetes_bmi_results.csv"
)
# Story direct onder de knoppen
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()
results_state = gr.State()
# Training starten via knop (streaming)
train_btn.click(
fn=sgd_train_generator,
inputs=[lr, epochs, batch, seed, split_seed],
outputs=[plot_main, plot_loss, results, results_state]
)
# 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, results_state]
)
# Download-button: maak CSV vanuit state
download_btn.click(fn=prepare_download, inputs=[results_state], outputs=download_btn)
if __name__ == "__main__":
demo.launch()