Train a network one pass at a time
Run training one epoch at a time with partial_fit, measure the network after every pass, and save a record of the run.
Work here, beside the explanation
In Module 2, one call to fit did all of the training at once: up to 100 passes through the training images, with no chance to look in between. In this module you run the passes yourself, one at a time, so you can measure the network after each one and see it learn. This lesson sets up the three pieces the rest of the module uses: training for exactly one pass, a loop that repeats it, and a file that records what happened.
Each numbered stage below shows a complete program. Try this stage copies it into the editor beside the article, including every line it needs from earlier stages, so it works even after you reload the page. Read the program first, predict what it will print, then press Run. Loading a stage replaces what is in the editor; Undo brings your own version back.
Build with me · 1
1. Record the versions you are using
Before an experiment, it helps to write down what it ran on. sys.version is the version of Python, and .split()[0] keeps just its first word, the number. sklearn.__version__ is the version of the machine learning library. A different version can give very slightly different numbers or rename a setting, so a record with versions lets someone else explain a small difference later. The last line confirms the same 1,797 images of 64 pixels as before.
from sklearn.datasets import load_digitsimport numpy as np digits = load_digits()X = digits.data.astype("float64") / 16.0y = digits.targetimport sklearnimport sys print("Python:", sys.version.split()[0])print("scikit-learn:", sklearn.__version__)print("Dataset:", X.shape, y.shape)Run and write the two version numbers at the top of your notes.
What to look for
Two version numbers print, then the dataset shapes (1797, 64) and (1797,).
Make it yours
Add a line that prints the brightness range of X, so your notes also record the scale.
Build with me · 2
2. Train for exactly one pass
The setup lines create the same network as in Module 2, with one difference: there is no max_iter, because the program, not the library, will decide how many passes to run. They also create history, a dictionary of four empty lists that later stages fill in. model.partial_fit(...) runs exactly one pass through the training images, one epoch, and keeps the network's weights afterwards, so a second call carries on from where the first stopped. classes=np.arange(10) tells the network on its first call that the possible answers are the digits 0 to 9. fit works that out from the labels; partial_fit is built to train on a few examples at a time, so it has to be told all the answers up front.
from sklearn.datasets import load_digitsimport numpy as np digits = load_digits()X = digits.data.astype("float64") / 16.0y = digits.targetfrom sklearn.model_selection import train_test_split # Reserve the final test before trying model settings.X_pool, X_final, y_pool, y_final = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y)X_train, X_dev, y_train, y_dev = train_test_split( X_pool, y_pool, test_size=0.25, random_state=42, stratify=y_pool)from sklearn.neural_network import MLPClassifierfrom sklearn.metrics import log_loss model = MLPClassifier( hidden_layer_sizes=(32,), activation="relu", solver="adam", learning_rate_init=0.003, batch_size=64, random_state=42,)history = {"epoch": [], "train_loss": [], "dev_loss": [], "dev_accuracy": []}model.partial_fit(X_train, y_train, classes=np.arange(10))print("One epoch completed.")print("Development accuracy:", round(model.score(X_dev, y_dev), 3))Run and write down the development accuracy after one pass.
What to look for
One epoch completes. In our run development accuracy after one pass is about 0.556: better than guessing, far from finished.
Make it yours
Run the whole program again. The accuracy is the same, because running the program from the top creates a fresh network each time.
Build with me · 3
3. Repeat passes with a loop
A for loop runs five passes in a row. range(1, 6) counts 1 to 5, so the printed epoch numbers start at 1 like a person would count. The network is created once, above the loop, so each pass continues training the same network. If the line that creates the model were inside the loop, every pass would start again from random weights and the network would never get past one pass. After each pass the program measures development accuracy, which never trains the network: it only checks it.
from sklearn.datasets import load_digitsimport numpy as np digits = load_digits()X = digits.data.astype("float64") / 16.0y = digits.targetfrom sklearn.model_selection import train_test_split # Reserve the final test before trying model settings.X_pool, X_final, y_pool, y_final = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y)X_train, X_dev, y_train, y_dev = train_test_split( X_pool, y_pool, test_size=0.25, random_state=42, stratify=y_pool)from sklearn.neural_network import MLPClassifierfrom sklearn.metrics import log_loss model = MLPClassifier( hidden_layer_sizes=(32,), activation="relu", solver="adam", learning_rate_init=0.003, batch_size=64, random_state=42,)history = {"epoch": [], "train_loss": [], "dev_loss": [], "dev_accuracy": []}for epoch in range(1, 6): model.partial_fit(X_train, y_train, classes=np.arange(10)) print("Epoch", epoch, "development accuracy", round(model.score(X_dev, y_dev), 3))Run and read the five lines from top to bottom.
What to look for
In our run development accuracy climbs 0.556, 0.739, 0.85, 0.875, 0.883.
Make it yours
Change the loop to 10 passes and predict whether the accuracy keeps rising, then run and see.
Build with me · 4
4. Save a record of the run
Now the loop also stores each measurement. history["epoch"].append(epoch) adds the epoch number to the list stored under the key "epoch", and the next line does the same for development accuracy, so position 0 of both lists describes epoch 1. float(...) turns NumPy's number into an ordinary one that JSON can save. The record dictionary gathers everything someone would need to understand the run: versions, data, seed, counts, network size, and the measured history. json.dump writes it to browser-run.json, as in the pairs lesson. The record describes the run; it does not contain the trained weights, so it cannot make predictions. Saving the model itself comes in the last module.
from sklearn.datasets import load_digitsimport numpy as np digits = load_digits()X = digits.data.astype("float64") / 16.0y = digits.targetfrom sklearn.model_selection import train_test_split # Reserve the final test before trying model settings.X_pool, X_final, y_pool, y_final = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y)X_train, X_dev, y_train, y_dev = train_test_split( X_pool, y_pool, test_size=0.25, random_state=42, stratify=y_pool)from sklearn.neural_network import MLPClassifierfrom sklearn.metrics import log_loss model = MLPClassifier( hidden_layer_sizes=(32,), activation="relu", solver="adam", learning_rate_init=0.003, batch_size=64, random_state=42,)history = {"epoch": [], "train_loss": [], "dev_loss": [], "dev_accuracy": []}import jsonimport sklearn for epoch in range(1, 6): model.partial_fit(X_train, y_train, classes=np.arange(10)) history["epoch"].append(epoch) history["dev_accuracy"].append(float(model.score(X_dev, y_dev)))record = { "environment": "browser Python", "sklearn_version": sklearn.__version__, "dataset": "sklearn digits 8x8", "seed": 42, "train_count": len(y_train), "development_count": len(y_dev), "hidden_units": 32, "epochs": 5, "history": history,}with open("browser-run.json", "w") as handle: json.dump(record, handle, indent=2)print(json.dumps(record, indent=2))Run and download browser-run.json. Find the five accuracies inside it.
What to look for
The printed record contains five development accuracies from this run, and browser-run.json is offered for download.
Make it yours
Add a "purpose" entry to the record with one sentence about why you ran this experiment.
What runs in this page
Everything here, including training, runs inside your browser. The first Run of a visit loads Python and its libraries, which can take a little while; wait for the loading message to finish before deciding something is wrong. Training speed depends on your device. Closing the page stops an unfinished run, so download any file you want to keep.
The complete reference is folded away below. Compare it with your work after trying the steps; changing a personal choice such as a name, a colour, or a display threshold can produce a different valid program.
Full reference solution
This is the final complete program built in the walkthrough. All its setup is included. Personal choices may differ in your own version; model scores are measured when you run, not promises about a future dataset.
from sklearn.datasets import load_digitsimport numpy as np digits = load_digits()X = digits.data.astype("float64") / 16.0y = digits.targetfrom sklearn.model_selection import train_test_split # Reserve the final test before trying model settings.X_pool, X_final, y_pool, y_final = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y)X_train, X_dev, y_train, y_dev = train_test_split( X_pool, y_pool, test_size=0.25, random_state=42, stratify=y_pool)from sklearn.neural_network import MLPClassifierfrom sklearn.metrics import log_loss model = MLPClassifier( hidden_layer_sizes=(32,), activation="relu", solver="adam", learning_rate_init=0.003, batch_size=64, random_state=42,)history = {"epoch": [], "train_loss": [], "dev_loss": [], "dev_accuracy": []}import jsonimport sklearn for epoch in range(1, 6): model.partial_fit(X_train, y_train, classes=np.arange(10)) history["epoch"].append(epoch) history["dev_accuracy"].append(float(model.score(X_dev, y_dev)))record = { "environment": "browser Python", "sklearn_version": sklearn.__version__, "dataset": "sklearn digits 8x8", "seed": 42, "train_count": len(y_train), "development_count": len(y_dev), "hidden_units": 32, "epochs": 5, "history": history,}with open("browser-run.json", "w") as handle: json.dump(record, handle, indent=2)print(json.dumps(record, indent=2))Compare this with your version. Different names and personal choices are fine when the program follows the same logic.
Keep your progress
Sign in and every reading, quiz, and exercise you finish is saved.