← Teacher guide

A model behind an app

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

Machine learning with Gradio · Real tools · about 45 minutes · Open the student lesson

Classroom resources

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.

Students will be able to

Timing

Predict and Run 10 min · Investigate 10 min · Modify 15 min · Make 10 min

Before class

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+).

Running the lesson

  1. Run: have students find the most and least certain measurements, and compare.
  2. Investigate: ask what a 60% answer should tell the user.
  3. Make: insist on a fair test (Lesson 4) before the app, and the accuracy in the title.

The hard part (on purpose)

Keeping columns, parameters and sliders in the same order. Ask them to read the three lists side by side.

When students get stuck

What you will seeA question to askThe 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.

Answers to the checks

  1. Predict: What does it print?
    0.6. Three of the five neighbours are Gentoo, and 3 / 5 is 0.6. predict_proba gives these shares for each species.
  2. Trained on everything: Why is this model trained on all 342 penguins, with no test set?
    Testing was done before; for real use, the model should learn from every example there is
    Test fairly first to know how good the method is, then train the model you use on all the data, so it has the most to learn from.
  3. Sixty per cent: The app says 60% for some measurements. What does that mean?
    3 of the 5 nearest penguins are that species: the measurements are between two species
    It is the share of the vote. Measurements between two species get split votes, which is useful information for the user.
  4. Spot the bug: A penguin with bill 47 and flipper 215 (very like a Gentoo) is identified as Chinstrap. Click the line that is wrong.
    line 12
    The numbers are given in the wrong order: [flipper, bill] under the names bill and flipper. The model reads by position, so it sees a 215 mm bill. It must be [bill, flipper], as in training.
  5. Where this leads: Your app is 94% accurate. A user tries a penguin species that is not in the data. What happens?
    It still names one of the three species it knows, possibly 100% sure
    A model can only choose between the answers it was trained on. The optional last lesson looks at this and other limits.

Solutions

One possible solution for each task. Other correct programs are fine.

Modify › Bill depth

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

Modify › Honest answers

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

a model app of your own

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 and support

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.

Exit ticket

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.

What each step is for

Where it leads

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.