Students put a 5-nearest-neighbour penguin model behind a Gradio app with sliders, show how sure it is with predict_proba, then add bill depth, answer "Not sure" below 80%, and build an app around a model of their own (sex or island).
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict and Run 10 min · Investigate 10 min · Modify 15 min · Make 10 min
Read the unit primer first. The model is trained on all 342 penguins (the method was tested fairly in Lesson 4; for use, a model should learn from all the data). The app's function turns the slider values into a one-row table with the training columns, then predict gives the species and predict_proba the share of the 5 neighbours voting for each species (the Predict works one out by hand). The bug task puts the numbers in the wrong order: the model reads by position, so it sees a 215 mm bill and answers wrongly without any error. In the Make, penguins.dropna() drops the penguins with no sex recorded. On a computer, pip install gradio scikit-learn pandas runs the same file; a teacher can host it as a Hugging Face Space (accounts need age 13+).
Keeping columns, parameters and sliders in the same order. Ask them to read the three lists side by side.
| What you will see | A question to ask | The fix |
|---|---|---|
| Predictions look wrong, with no error. | Are the mystery's numbers in the same order as columns? | [[bill, flipper]] for columns ["bill_length_mm", "flipper_length_mm"]. |
| ValueError: Input X contains NaN. | Are there penguins with missing values in what you train on? | penguins.dropna() before fitting. |
| TypeError: identify() takes 2 positional arguments but 3 were given. | Is there a parameter for every slider? | Add the parameter for the new slider, in the same order. |
One possible solution for each task. Other correct programs are fine.
import gradio as gr
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
penguins = pd.read_csv("penguins.csv")
columns = ["bill_length_mm", "flipper_length_mm", "bill_depth_mm"]
model = KNeighborsClassifier(n_neighbors=5)
model.fit(penguins[columns], penguins["species"])
def identify(bill, flipper, depth):
mystery = pd.DataFrame([[bill, flipper, depth]], columns=columns)
species = model.predict(mystery)[0]
sure = round(max(model.predict_proba(mystery)[0]) * 100)
return species, str(sure) + "% of the 5 nearest penguins are " + species
gr.Interface(
fn=identify,
inputs=[
gr.Slider(30, 60, value=45, step=0.5, label="Bill length (mm)"),
gr.Slider(170, 235, value=200, step=1, label="Flipper length (mm)"),
gr.Slider(13, 22, value=17, step=0.1, label="Bill depth (mm)"),
],
outputs=[gr.Textbox(label="Species"), gr.Textbox(label="How sure")],
title="Penguin identifier",
).launch()
import gradio as gr
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
penguins = pd.read_csv("penguins.csv")
columns = ["bill_length_mm", "flipper_length_mm"]
model = KNeighborsClassifier(n_neighbors=5)
model.fit(penguins[columns], penguins["species"])
def identify(bill, flipper):
mystery = pd.DataFrame([[bill, flipper]], columns=columns)
species = model.predict(mystery)[0]
sure = round(max(model.predict_proba(mystery)[0]) * 100)
if sure < 80:
return "Not sure", str(sure) + "% of the 5 nearest penguins are " + species
return species, str(sure) + "% of the 5 nearest penguins are " + species
gr.Interface(
fn=identify,
inputs=[
gr.Slider(30, 60, value=45, step=0.5, label="Bill length (mm)"),
gr.Slider(170, 235, value=200, step=1, label="Flipper length (mm)"),
],
outputs=[gr.Textbox(label="Species"), gr.Textbox(label="How sure")],
title="Penguin identifier",
).launch()
import gradio as gr
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
penguins = pd.read_csv("penguins.csv")
known = penguins.dropna()
columns = ["bill_depth_mm", "body_mass_g"]
model = KNeighborsClassifier(n_neighbors=5)
model.fit(known[columns], known["sex"])
def guess_sex(depth, mass):
mystery = pd.DataFrame([[depth, mass]], columns=columns)
return model.predict(mystery)[0]
gr.Interface(
fn=guess_sex,
inputs=[gr.Slider(13, 22, value=17, step=0.1, label="Bill depth (mm)"), gr.Slider(2700, 6300, value=4200, step=50, label="Body mass (g)")],
outputs=gr.Textbox(label="Male or female?"),
title="Penguin sex guesser",
).launch()
Stretch: Show the three species' vote shares as a bar chart (gr.BarPlot with a small DataFrame), or add a Dropdown to choose the model (neighbours or tree).
Support: Give students the third slider line, and ask them to add the column and the parameter.
Why does the app say 60% sure for some penguins?
Only 3 of the 5 most similar penguins in the data are that species, so the measurements sit between two species.
The optional last lesson looks at what models get wrong. The micro:bit and the computer course trains a model on movement with Teachable Machine.