{ "cells": [ { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", "metadata": {}, "source": [ "# Training pipeline with callbacks\n", "\n", "- `VQCClassifier` trained with `EarlyStopping` and `ModelCheckpoint`.\n", "- Both run on both backends, unlike a Lightning callback.\n", "- Inspect what each callback recorded, resume the run from its last checkpoint, then reload the fitted preprocessing for new rows.\n", "- Pennylane backend throughout." ] }, { "cell_type": "markdown", "id": "1b64d729", "metadata": {}, "source": [ "### 1. Data\n", "\n", "- `make_moons`, 200 samples.\n", "- `normalize=\"minmax\"`, fit on train only.\n", "- `AngleEmbedding` prescaling maps it to [0, pi] in `setup()`." ] }, { "cell_type": "code", "id": "13433722", "metadata": { "ExecuteTime": { "end_time": "2026-09-19T10:45:44.483984752Z", "start_time": "2026-09-19T10:45:41.504380992Z" } }, "source": [ "from sklearn.datasets import make_moons\n", "\n", "import pyqit\n", "from pyqit import DataModule, Trainer\n", "from pyqit.ansatzes import SELAnsatz\n", "from pyqit.core import AngleEmbedding, EarlyStopping, ModelCheckpoint\n", "from pyqit.models import VQCClassifier\n", "\n", "pyqit.set_seed(42)\n", "\n", "X, y = make_moons(n_samples=200, noise=0.1, random_state=0)\n", "dm = DataModule(X, y, normalize=\"minmax\", batch_size=16, seed=42)" ], "outputs": [], "execution_count": 1 }, { "cell_type": "markdown", "id": "b936bced", "metadata": {}, "source": [ "### 2. Trainer with both callbacks\n", "\n", "- `EarlyStopping` on `val_loss`, patience 5. Stops once it stops improving.\n", "- `ModelCheckpoint` saves the best and last epoch, restores best weights after training.\n", "- Kept as named variables, not inlined, to read their state after `fit`.\n", "- `loss_fn=\"cross_entropy\"`." ] }, { "cell_type": "code", "id": "0dc28b7b", "metadata": { "ExecuteTime": { "end_time": "2026-09-19T10:45:51.552221241Z", "start_time": "2026-09-19T10:45:44.492386434Z" } }, "source": [ "model = VQCClassifier(n_qubits=4, n_layers=3, ansatz=SELAnsatz, encoder=AngleEmbedding)\n", "\n", "early_stop = EarlyStopping(monitor=\"val_loss\", patience=5)\n", "checkpoint = ModelCheckpoint(dirpath=\"ckpts\", save_best=True, save_last=True)\n", "\n", "trainer = Trainer(\n", " max_epochs=60,\n", " loss_fn=\"cross_entropy\",\n", " callbacks=[early_stop, checkpoint],\n", " verbose=1,\n", ")\n", "history = trainer.fit(model, dm)" ], "outputs": [ { "data": { "text/plain": [ "\u001B[1;36m[\u001B[0m\u001B[1;36mTrainer\u001B[0m\u001B[1;36m]\u001B[0m Starting \u001B[32mpennylane\u001B[0m backend | \u001B[1;36m60\u001B[0m epochs | \u001B[33mlr\u001B[0m=\u001B[1;36m0\u001B[0m\u001B[1;36m.01\u001B[0m\n", "\n" ], "text/html": [ "
[Trainer] Starting pennylane backend | 60 epochs | lr=0.01\n",
       "\n",
       "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "Output()" ], "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, "model_id": "ba89de78f1b844f4a3999bce3fc3c7d5" } }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\u001B[1;33m[\u001B[0m\u001B[1;33mEarlyStopping\u001B[0m\u001B[1;33m]\u001B[0m Stopped at epoch \u001B[1;36m39\u001B[0m -- val_loss did not improve for \u001B[1;36m5\u001B[0m \u001B[1;35mepoch\u001B[0m\u001B[1m(\u001B[0ms\u001B[1m)\u001B[0m \u001B[1m(\u001B[0mbest val_loss: \u001B[1;36m0.3256\u001B[0m\u001B[1m)\u001B[0m\n" ], "text/html": [ "
[EarlyStopping] Stopped at epoch 39 -- val_loss did not improve for 5 epoch(s) (best val_loss: 0.3256)\n",
       "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [], "text/html": [ "
\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "\u001B[1;32m[\u001B[0m\u001B[1;32mCheckpoint\u001B[0m\u001B[1;32m]\u001B[0m Last epoch -> ckpts/last.npz\n"
      ],
      "text/html": [
       "
[Checkpoint] Last epoch -> ckpts/last.npz\n",
       "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\u001B[1;32m[\u001B[0m\u001B[1;32mCheckpoint\u001B[0m\u001B[1;32m]\u001B[0m Restored best weights from epoch \u001B[1;36m34\u001B[0m \u001B[1m(\u001B[0mval_loss: \u001B[1;36m0.3256\u001B[0m\u001B[1m)\u001B[0m\n" ], "text/html": [ "
[Checkpoint] Restored best weights from epoch 34 (val_loss: 0.3256)\n",
       "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\u001B[1;32m[\u001B[0m\u001B[1;32mTrainer\u001B[0m\u001B[1;32m]\u001B[0m Training complete.\n" ], "text/html": [ "
[Trainer] Training complete.\n",
       "
\n" ] }, "metadata": {}, "output_type": "display_data" } ], "execution_count": 2 }, { "cell_type": "markdown", "id": "fd565fbe", "metadata": {}, "source": [ "### 3. What each callback recorded\n", "\n", "- `early_stop.stopped_epoch` and `early_stop.stopping_reason`, set once training stops early.\n", "- `checkpoint.best_epoch`, `checkpoint.best_path`, `checkpoint.last_path`, the epoch and files `ModelCheckpoint` wrote.\n", "- `history.best_epoch`, `history.best_score`, `history.best_metric`, the same best epoch from the run's own record." ] }, { "cell_type": "code", "id": "5630cfc2", "metadata": { "ExecuteTime": { "end_time": "2026-09-19T10:45:51.569949072Z", "start_time": "2026-09-19T10:45:51.555130093Z" } }, "source": [ "print(\"stopped at:\", early_stop.stopped_epoch, \"|\", early_stop.stopping_reason)\n", "print(\"best checkpoint:\", checkpoint.best_epoch, \"->\", checkpoint.best_path)\n", "print(\"last checkpoint:\", checkpoint.last_path)\n", "print(\"history best:\", history.best_epoch, history.best_score, history.best_metric)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "stopped at: 39 | val_loss did not improve for 5 epoch(s) (best val_loss: 0.3256)\n", "best checkpoint: 34 -> ckpts/best.npz\n", "last checkpoint: ckpts/last.npz\n", "history best: 34 0.32560367617711666 val_loss\n" ] } ], "execution_count": 3 }, { "cell_type": "markdown", "id": "5cebe8c4", "metadata": {}, "source": [ "### 4. Loss curve" ] }, { "cell_type": "code", "id": "cf8a90fd", "metadata": { "ExecuteTime": { "end_time": "2026-09-19T10:45:51.674453514Z", "start_time": "2026-09-19T10:45:51.571820426Z" } }, "source": [ "import matplotlib.pyplot as plt\n", "\n", "plt.plot(history.train_loss, label=\"train_loss\")\n", "plt.plot(history.val_loss, label=\"val_loss\")\n", "plt.xlabel(\"epoch\")\n", "plt.ylabel(\"loss\")\n", "plt.legend()\n", "plt.show()" ], "outputs": [ { "data": { "text/plain": [ "
" ], "image/png": "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h8MBQFV13W6lqspKoqPrv07pcbtZvXo1SUlJDBk8mDFjxvDGm29RXb3rf8jLL7uUa6+9pnEKFxERafVCde7iMmx8ShMxG/XC24JIXFx8ne3x8fFUVdU/MHjzli28+trrPPLoYxw98Vj69+/HBRf83273n/TU0/Tq3TfyGDK0/p4hERERab0MCzebN2+mqqqKnj171Nnes2dPVq5ctZujdlZVVcWaNWvJzu622328Xi9VVVWRx+56eERERKT1MyzchEIhvvzyK0499ZTIJar+/fsxfPgwPv/ii8h+J55wApdffhkANpuNUaNG1TlP165dGThwAAsX/mkWSxEREWmXDJ3E79777uejD9/nf998xZIlSxk/fjzvv/8B338/JbLP6NEHMnToUCZNeopQKMRll17CrbfezKqVq4iLi2PcuIP47rvveeONNw18JyIiItJSGDrPDYTvZBo/bhwpqSksX76cuXPn1Wk/8MADSEtN49PPPots69u3DwMGDMDtdrNkyVLWr1+/V6+peW5ERERanxY/z802Xq+X777/frftP//8y07bli1bzrJly5uyLBEREWmlDBtzIyIiItIUFG5ERESkTVG4ERERkTZF4UZERETaFIUbERERaVMUbkRERKRNUbgRERGRNsXweW6M5HK5jC5BRERE9tCefm63y3Cz7R9n3tw5BlciIiIie8vlctU7Q7Hhyy8YJT09vdFXB3e5XMybO4chQ4e36ZXH9T7blvbwPtvDewS9z7ZG73P3++fn59e7T7vsuQH+8h9mX1RXV7eLNav0PtuW9vA+28N7BL3Ptkbvs6492UcDikVERKRNUbgRERGRNkXhphF5vV4eeeRRvF6v0aU0Kb3PtqU9vM/28B5B77Ot0ftsuHY7oFhERETaJvXciIiISJuicCMiIiJtisKNiIiItCntdp6bptCjRw8cUVEsX7ECv99vdDmNbuSIETttW7d+PYWFhQZU07iysrLo2KEDCxYupLa2dpf7dOnShbi4OFavWoWnpqaZK2wcvXr1IjY2hj/+mLtT23777UeU3V5n2+YtW9i8eXNzldcoEhISyMrMZNPmzZSVle1yH7vdTu9evXB73KxZs7Z5C2wkGRkZJCUlsn79Btxud522uLg4+vTuvdMx9f1+t1QZGRkkJyeRk7OJioqKXe6TkpJCZmYmORs3UlJa2swVNo6uXbsSFRXFxg0bdvr70r17NslJyXW2VVRWsnz58uYssdFkZmaS2bEjK1aupLy8fKf2nj17YrfbWL58BYFAoEGvoQHFjSAzM5PXXnuFjK2zHjscDi6+5FJ++WW20aU1GovFQs7G9SxbtpzKyu1/YJ599nm+/e47AyvbN6NHH8glF1/MfvsNIikpiQNHj2X9+vV19omPj+fll15k4MABFBQWkpqSwg033sTnn39hTNENcNJJJ3LB//0fnTt3xm630bNXn532mTPnNzweD8VFRZFtH338CW+++VZzltpg/fr15bZbb2XQfoPYsnkz2dnZfPfd91x77XV1PiwOPng8k558goqKSuLj49m4cSPnnnc+BQUFhtW+N8aPH8+tt9xEUlIyJaUldOvalZdffoX7H3iwzj7vvP0mv/32W51j/3XxpeTl5TV3yQ0ycsQI/v3vO0lOTqKiooLs7O58+NGH3HzzrQSDwch+9937H04//TQ2bNhAly5deOmll7nv/gcMrHzvnHD88dxww/X4fF5MJhPp6ek88cSTPPPsc5F9nn3maQ44YFSdv01Lly3nlltuNaDifZOUmMi3335DZmYm55x7HlOm/BBp69q1K6++8hLJycnU1NRgtdq46KJ/8fucvV8qST03jeCpSU9SUlzMUUdNxO/3c8ftt/HiC88z6oDRbW5Wydtuv71NhbY+vXvz0ssvU1lZyeeffbrLfe655y4SExMZNnwkVVVVnH/euTzx+GPMnTuPTZs2NW/BDdSjRw9uvuVWBg4cwB2337bb/SZNeor33/+gGStrPN26deO5555n+owZAHTokMEXX3zODTfewF133Q2E/7A+9+wzPPvc8zz++BNERUXxwQeTeeThhzj7nHONLH+PdenSmSuuvIply8L/1T506BA+/OB9Vq1ezYcffhTZz+/3c8KJJxtV5j7LzMrkiiuvYvXq1UD4v+b/983XLJi/gHfefQ+As876OyeffBJHHjWRlStXMnjwYD75+EMWLV7MF198aWT5eywhIYETTjwpEq6POupIXn7pRWbO+pmFCxdG9psy5Qeuu/4Go8psNI89/iiffPIpl1126U5tTz81iZxNmzh0wuEEg0Hu/c89vPDCcxx44Ji97i3XmJt91K1bV/bffySTnno6cilq0lNPERsby+GHH2ZwdY0vIz2DgQMHkpCQYHQpjeLlV17lxx9/qvNfgjtyOp0ce8wxvPTSS5Gg+vobb1JVVcXJJ5/UnKXuk//+9yHmz5//l/slJSYxaNAgUlJSmr6oRvbVV19Hgg1Abm4e338/pc7l1GOOPQaLxcLzzz0PQG1tLc899zwHHzyetLS0Zq64YV5//Y1IsAGYO3cei5cs2eVl4x7du9O3bx8cDkdzltgoPvnk00iwAVi1ahVFRYVkZGREtp1+2ml8/fU3rFy5EoD58+czffp0Tj/ttGavt6Fee/31Or2GP//8CwAddnifANHR0QwaNIisrKxmra8xXXjhBURHR/Pc8y/s1NanTx+GDBnMpCefivw9fuLJSaSkpHDIoYfs9Wsp3OyjAQMGALBw4aLIttLSMjZu3Bhpa0vuuecuHnv0EebNncOLLzxPYmKC0SU1qV69ehEVFcXCRdt/vsFgkEWLFjOgf38DK2saV199JY88/F9m/zKLDz94n06dOhld0j4ZNHBgna78Af37s2rV6jr/FTh//nzMZjP9+/czoMJ953K56J7dfafLqVarlTfeeJ0XX3yBpUsWcf311xlT4D5wOByMHDGCcePG8cAD9+H3B3j3vfci7f369a3TuwEwb/4CBgxoXf/fTEpMZOSIERx22ASemvQkv/wymx9/+qnOPkcffRSPPPIQ//vmK36eNZMDDhhlTLENNHDgQC7+10VceeVVENp5NMy2n9mOf2sLCgrIzc1t0GepLkvto4SEBPx+/04D3UpLS0lsI70bAKFQiGuuuY73Jk8GwuOM3nvvHf774INccOFFBlfXdLb1UJX+aZBiaWkpaemt47/099TDDz3CBx9+SCAQICkxkVdffZnnnn2GY449jtAu/hi1dP+66CL69evLDTfeGNmWkJiw88+yJPy8tfZGPnD/fdTW1tb50C8oyOeYY49j7tx5AIwZM5o333id/Px83njjTaNK3WvJSUnccstNxMbG0blzJx5//Alyc8NjhpxOJw6Hg9LSsjrHlJaWtrqfZb/+/bju2mtISU0lLjaW226/o85svV99/TU33XwL5eXlWK1W7rnnbl5+6UXGH3xoqxgr5nK5ePbZp7n9jjvJzc0jKTFxp30SEhJwu907DXhv6M9TPTf7yO/zYbVasdlsdbY7HA68vrYzZXYwGIwEG4DNmzczadJTHHHE4URFRRlYWdPy+30AO71Hh8OBz+szoqQm897k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" }, "metadata": {}, "output_type": "display_data" } ], "execution_count": 4 }, { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", "metadata": {}, "source": [ "### 5. Resuming from a checkpoint\n", "\n", "- A checkpoint holds the weights, the optimizer state and the history to that epoch, `.npz` on pennylane and `.ckpt` on torch.\n", "- `ModelCheckpoint(resume_from=...)` loads all three before the first epoch, so a fresh model continues where `last.npz` stopped.\n", "- `max_epochs` counts from zero: the resumed run trains the epochs the file does not already hold." ] }, { "cell_type": "code", "id": "9a63283cbaf04dbcab1f6479b197f3a8", "metadata": { "ExecuteTime": { "end_time": "2026-09-19T10:45:52.301345728Z", "start_time": "2026-09-19T10:45:51.676930902Z" } }, "source": [ "n_done = len(history.train_loss)\n", "\n", "fresh_model = VQCClassifier(\n", " n_qubits=4, n_layers=3, ansatz=SELAnsatz, encoder=AngleEmbedding\n", ")\n", "resume = ModelCheckpoint(\n", " dirpath=\"ckpts\", save_best=False, resume_from=checkpoint.last_path\n", ")\n", "\n", "resumed = Trainer(\n", " max_epochs=n_done + 5, loss_fn=\"cross_entropy\", callbacks=[resume], verbose=1\n", ").fit(fresh_model, dm)\n", "\n", "assert resumed.train_loss[:n_done] == history.train_loss\n", "print(f\"{n_done} epochs from the file, {len(resumed.train_loss) - n_done} trained now\")" ], "outputs": [ { "data": { "text/plain": [ "\u001B[1;36m[\u001B[0m\u001B[1;36mTrainer\u001B[0m\u001B[1;36m]\u001B[0m Starting \u001B[32mpennylane\u001B[0m backend | \u001B[1;36m45\u001B[0m epochs | \u001B[33mlr\u001B[0m=\u001B[1;36m0\u001B[0m\u001B[1;36m.01\u001B[0m\n", "\n" ], "text/html": [ "
[Trainer] Starting pennylane backend | 45 epochs | lr=0.01\n",
       "\n",
       "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\u001B[1;32m[\u001B[0m\u001B[1;32mCheckpoint\u001B[0m\u001B[1;32m]\u001B[0m Resumed from ckpts/last.npz at epoch \u001B[1;36m40\u001B[0m\n" ], "text/html": [ "
[Checkpoint] Resumed from ckpts/last.npz at epoch 40\n",
       "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "Output()" ], "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, "model_id": "3bef087e6cde47f1a48d61e291d6130b" } }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [], "text/html": [ "
\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "\u001B[1;32m[\u001B[0m\u001B[1;32mTrainer\u001B[0m\u001B[1;32m]\u001B[0m Training complete.\n"
      ],
      "text/html": [
       "
[Trainer] Training complete.\n",
       "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "40 epochs from the file, 5 trained now\n" ] } ], "execution_count": 5 }, { "cell_type": "markdown", "id": "8dd0d8092fe74a7c96281538738b07e2", "metadata": {}, "source": [ "### 6. Saving the DataModule\n", "\n", "- The checkpoint holds the model. The fitted normalizer is the DataModule's, saved on request with `dm.save`.\n", "- `DataModule.load(path, X_new)` attaches new rows to the saved settings, like `dm.for_prediction`, so predicting needs neither the training data nor a refit." ] }, { "cell_type": "code", "id": "72eea5119410473aa328ad9291626812", "metadata": { "ExecuteTime": { "end_time": "2026-09-19T10:45:52.325905550Z", "start_time": "2026-09-19T10:45:52.303215238Z" } }, "source": [ "X_new, _ = make_moons(n_samples=5, noise=0.1, random_state=1)\n", "\n", "dm.save(\"ckpts/datamodule.pkl\")\n", "dm_new = DataModule.load(\"ckpts/datamodule.pkl\", X_new)\n", "\n", "print(trainer.predict(fresh_model, dm_new))" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 0 1 1 1]\n" ] } ], "execution_count": 6 } ], "metadata": { "kernelspec": { "display_name": ".venv (3.12.3)", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }