{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "---\n",
    "execute:\n",
    "  enabled: false\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=interactive-development-md-1 fw=all -->\n",
    "\n",
    "# Notebooks\n",
    "\n",
    "Every section of this book is an executable notebook: prose, mathematics,\n",
    "code, and the code's output live in a single document. That is a deliberate\n",
    "choice, not a formatting convenience. Deep learning is an empirical subject.\n",
    "The fastest way to understand a model is to train it, change something — the\n",
    "learning rate, the width of a layer, the amount of data — and watch what\n",
    "happens. A notebook makes that loop immediate: the explanation sits next to\n",
    "the experiment, and the experiment is one keystroke from running. This is\n",
    "also how most practitioners work day to day, from quick data inspection to\n",
    "research prototypes, which is why the Jupyter notebook has become the shared\n",
    "medium of machine learning.\n",
    "\n",
    "Reading alone will not build the same understanding. We wrote each notebook\n",
    "expecting you to *edit* it: break the code, fix it, try a variant the text\n",
    "only mentions. This section explains how to run the book on your own machine\n",
    "and how to work in notebooks effectively — including the one habit, *restart\n",
    "and run all*, that separates reproducible notebooks from lucky ones. If you\n",
    "would rather not install anything, that section shows how\n",
    "to run the same notebooks free of charge on Colab or Kaggle; if you need more\n",
    "hardware than your laptop offers, see that section.\n",
    "\n",
    "## Why Notebooks?\n",
    "\n",
    "A plain Python script re-runs from the top every time, so a slow step — say,\n",
    "loading and preprocessing a dataset — repeats on every experiment. A notebook\n",
    "keeps a live Python process (the *kernel*) between executions: load the data\n",
    "once, then iterate on the model as often as you like while the data stays in\n",
    "memory. Combined with inline plots and the ability to interleave explanation\n",
    "with computation, this is what makes notebooks such an effective medium for\n",
    "teaching and for research alike.\n",
    "\n",
    "The price of that convenience is *state*. Understanding where state lives is\n",
    "the key to using notebooks well.\n",
    "\n",
    "### The Document and the Kernel\n",
    "\n",
    "A notebook is really two things. The *document* stores cells and saved\n",
    "outputs — it is what you read, share, and commit. The *kernel* holds the live\n",
    "state: imported modules, variables, random-number generators, open files,\n",
    "compiled programs, and accelerator memory. The document orders cells top to\n",
    "bottom; the kernel only knows the order in which you *executed* them.\n",
    "Confusing these two orders is the root cause of most notebook bugs.\n",
    "\n",
    "![The document orders cells top to bottom; the kernel accumulates state in execution order. Restart and run all replays document order — the only order a reader can reproduce — and exposes the mismatch.](../img/tools-kernel-state.svg)\n",
    "\n",
    "the figure shows the classic failure. During an\n",
    "editing session you executed a data-loading cell first, then wrote a\n",
    "model-definition cell *above* it, and everything worked because the kernel\n",
    "already held `data`. The document now tells a story that never happened. A\n",
    "reader who runs the cells in the order shown — or you, tomorrow, after a\n",
    "restart — hits a `NameError`.\n",
    "\n",
    "The execution counters (`In [3]`, `In [1]`, …) are historical clues, not a\n",
    "dependency graph. The following pair of cells demonstrates benign,\n",
    "document-ordered state: the second cell works only because the first ran."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "interactive-development-hidden-state-a"
   },
   "source": [
    "import numpy as np\n",
    "\n",
    "rng = np.random.default_rng(7)\n",
    "samples = rng.normal(size=5)"
   ],
   "execution_count": 1,
   "outputs": []
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "interactive-development-hidden-state-b"
   },
   "source": [
    "float(samples.mean())"
   ],
   "execution_count": 2,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "-0.2638849576850524"
     },
     "metadata": {},
     "execution_count": 2
    }
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=interactive-development-md-2 fw=all -->\n",
    "\n",
    "That dependency is fine, because it follows the reading order. A cell that\n",
    "depends on an assignment *below* it, or on a cell you have since deleted, is\n",
    "not. Keep cells small, keep function definitions separate from experiments,\n",
    "and pass data explicitly rather than mutating globals across many cells.\n",
    "\n",
    "### Restart and Run All\n",
    "\n",
    "**Restart kernel and run all cells** discards the kernel's accumulated state\n",
    "and replays the document from a blank slate, in reading order. It is the\n",
    "single most important reproducibility check a notebook offers: it catches\n",
    "hidden state, missing setup, and order dependencies before another reader\n",
    "does. Make it a reflex before saving, sharing, or committing a notebook.\n",
    "Every notebook in this book is built and tested exactly this way — the\n",
    "outputs you see on the website are produced by a clean top-to-bottom run.\n",
    "\n",
    "## Running the Book Locally\n",
    "\n",
    "### Setting Up\n",
    "\n",
    "Download the notebook archive described in that section, or\n",
    "clone the current sources from [GitHub](https://github.com/smolix/d2l-neu).\n",
    "The archive includes CPU and GPU `uv` environment files. From the extracted\n",
    "directory, create the environment once and launch JupyterLab through it:\n",
    "\n",
    "```bash\n",
    "uv sync --locked\n",
    "uv run jupyter lab\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=interactive-development-md-3 fw=all -->\n",
    "\n",
    "Use the GPU lock file on a compatible NVIDIA system when the examples require\n",
    "it. One subtlety matters more than any other: the *kernel* selected inside a\n",
    "notebook must belong to the environment where the packages were installed. A\n",
    "Jupyter server can see many kernels; its own Python process does not\n",
    "determine which one executes your notebook.\n",
    "\n",
    "### A Quick Sanity Check\n",
    "\n",
    "Before changing code, record a compact identity check:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "interactive-development-identity"
   },
   "source": [
    "import os\n",
    "import platform\n",
    "import sys\n",
    "\n",
    "{\n",
    "    \"python\": sys.executable,\n",
    "    \"version\": platform.python_version(),\n",
    "    \"working_directory\": os.getcwd(),\n",
    "}"
   ],
   "execution_count": 3,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "{'python': '/home/smola/d2l-neu/.venv-pytorch/bin/python',\n 'version': '3.12.3',\n 'working_directory': '/home/smola/d2l-neu/_notebooks/pytorch/chapter_appendix-tools-for-deep-learning'}"
     },
     "metadata": {},
     "execution_count": 3
    }
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=interactive-development-md-4 fw=all -->\n",
    "\n",
    "This catches the two most common setup mistakes in one cell: a kernel from\n",
    "the wrong environment (`sys.executable` points somewhere unexpected) and a\n",
    "notebook opened from a directory where relative data paths no longer resolve.\n",
    "\n",
    "## Working in an Editor\n",
    "\n",
    "### JupyterLab\n",
    "\n",
    "[JupyterLab](https://jupyterlab.readthedocs.io/) combines a file browser,\n",
    "notebook editor, terminals, a text editor, debugger support, and a view of\n",
    "running kernels. The essentials are stable even as the interface evolves:\n",
    "\n",
    "* Run a cell with `Shift+Enter`; use a terminal tab for `uv`, Git, and\n",
    "  inspecting files.\n",
    "* Select the kernel by environment name, then verify `sys.executable`.\n",
    "* Interrupt a long computation before restarting. Restarting releases Python\n",
    "  state and, normally, the accelerator memory owned by that process.\n",
    "* Use **Restart Kernel and Run All Cells** before saving a result for others.\n",
    "* Inspect the **Running** panel and stop kernels you no longer use — closing\n",
    "  a browser tab does not necessarily stop its kernel, and an orphaned kernel\n",
    "  can hold gigabytes of GPU memory.\n",
    "\n",
    "### VS Code\n",
    "\n",
    "[Visual Studio Code](https://code.visualstudio.com/docs/datascience/jupyter-notebooks)\n",
    "edits and runs `.ipynb` files with kernel selection, a variable inspector,\n",
    "cell-level debugging, and notebook-aware diffs. Open the repository as a\n",
    "folder, choose the interpreter created by `uv`, and select that interpreter\n",
    "as the notebook kernel.\n",
    "\n",
    "VS Code shines when a notebook grows into a project: the same editor\n",
    "navigates definitions, runs tests, formats code, and reviews Git diffs. A\n",
    "good division of labor is to keep logic that deserves tests in `.py` modules\n",
    "and use the notebook as the explanatory path and experiment record. This\n",
    "repository also ships a VS Code extension that understands the book's\n",
    "authoring workflow — switching framework views, previewing slides, syncing\n",
    "edits back to the source — described in that section.\n",
    "\n",
    "### Debugging and Timing\n",
    "\n",
    "Use the notebook debugger when the kernel supports it, or move a small\n",
    "failing call into a module and debug it in a terminal. Resist broad\n",
    "`try/except` blocks that print \"failed\": they discard the exception type and\n",
    "location that diagnosis needs. For quick measurements, IPython's magics are\n",
    "built in:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "interactive-development-timing"
   },
   "source": [
    "values = np.arange(100_000, dtype=np.float64)\n",
    "%timeit values @ values"
   ],
   "execution_count": 4,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "9.43 μs ± 473 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=interactive-development-md-5 fw=all -->\n",
    "\n",
    "One caveat when timing accelerators: GPU operations are asynchronous, so a\n",
    "host-side timer may measure only the dispatch of work, not its completion.\n",
    "Synchronize explicitly (or use the framework's benchmarking utilities), warm\n",
    "up compiled kernels first, and record the shapes, precision, device, and\n",
    "library versions alongside the number.\n",
    "\n",
    "## Remote Machines\n",
    "\n",
    "Your editor, the notebook server, the kernel, and the accelerator need not\n",
    "be on the same machine. A common setup runs JupyterLab next to the GPU while\n",
    "you edit from a laptop.\n",
    "\n",
    "![A local editor reaches a remote Jupyter server and kernel through an SSH tunnel; the accelerator stays attached to the remote kernel.](../img/tools-remote-layers.svg)\n",
    "\n",
    "Bind the remote server to loopback and forward it over SSH:\n",
    "\n",
    "```bash\n",
    "# Remote machine\n",
    "uv run jupyter lab --no-browser --ip 127.0.0.1 --port 8888\n",
    "```\n",
    "\n",
    "\n",
    "```bash\n",
    "# Local machine\n",
    "ssh -N -L 8888:127.0.0.1:8888 myserver\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=interactive-development-md-6 fw=all -->\n",
    "\n",
    "Then open the tokenized `http://127.0.0.1:8888/...` URL locally. Never bind\n",
    "an unauthenticated Jupyter server to all network interfaces: anyone who can\n",
    "reach the port can execute code as you. The SSH tunnel provides encryption\n",
    "and access control; Jupyter's token remains a useful second boundary.\n",
    "\n",
    "VS Code's Remote SSH goes one step further: it installs a small server-side\n",
    "component so the editor UI runs locally while terminals, extensions, files,\n",
    "and kernels all live on the remote machine. Confirm the status bar shows\n",
    "**SSH** before selecting the Python environment — a local kernel cannot use\n",
    "the remote GPU. Either way, treat remote compute as disposable and its\n",
    "storage as precious: commit code and copy checkpoints off the machine before\n",
    "it is rebooted, preempted, or deleted (that section).\n",
    "\n",
    "Before sharing or committing any notebook, a final hygiene pass pays off:\n",
    "restart and run all; check that setup does not rely on personal paths or\n",
    "unrecorded downloads; remove secrets and private data from outputs; keep the\n",
    "outputs that teach or verify something and delete noisy progress logs; and\n",
    "note the environment and hardware needed to interpret any measurement.\n",
    "\n",
    "## Summary\n",
    "\n",
    "* Notebooks interleave prose, code, and results, and keep expensive state\n",
    "  alive between experiments — that is why this book is written as notebooks,\n",
    "  and why you should run and edit them rather than only read them.\n",
    "* The document and the kernel hold different state in different orders;\n",
    "  restart and run all is the test that they agree.\n",
    "* Launch JupyterLab from the book's `uv` environment, and verify the kernel\n",
    "  with `sys.executable` before trusting any result.\n",
    "* JupyterLab integrates notebooks, terminals, and kernels; VS Code adds\n",
    "  modules, tests, diffs, and first-class remote development.\n",
    "* Use SSH tunneling or VS Code Remote SSH instead of exposing a notebook\n",
    "  server to the network.\n",
    "\n",
    "## Exercises\n",
    "\n",
    "1. Create an intentional out-of-order dependency like the one in\n",
    "   the figure, confirm that it works interactively,\n",
    "   and then detect it with restart and run all.\n",
    "1. Compare `sys.executable` in a terminal, a JupyterLab kernel, and a VS\n",
    "   Code kernel on your machine. Explain any difference.\n",
    "1. Time a matrix product with `%timeit` on CPU and, if available, on a GPU\n",
    "   with and without synchronization. Explain the discrepancy.\n",
    "1. Connect to a remote machine through an SSH tunnel and identify where the\n",
    "   editor, server, kernel, and file system each run."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "d2l-pytorch",
   "display_name": "d2l (PyTorch)",
   "language": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
