Almost every machine learning workflow you'll read about, from a Kaggle notebook to a research paper's published code, starts life in Jupyter. It's become the default environment for prototyping models, exploring data, and documenting experiments, largely because it collapses code, output, and explanation into a single document you can run one piece at a time.
What Jupyter Notebook actually is
Jupyter Notebook is a browser-based interface for running code interactively. Cells can hold code in Python, R, Julia, or any other language with a Jupyter kernel, alongside rendered output, plots, and formatted text, all in the same scrollable document.
Under the hood, a notebook is just a JSON file with an .ipynb extension, storing each cell's type (code, markdown, or raw text), its contents, its output, and metadata about the run. A running notebook connects to a kernel that holds the session's live state: variables and objects defined in one cell stay available to every other cell until the kernel restarts, which is what makes the whole "run pieces out of order and inspect results" workflow possible in the first place.
Jupyter itself grew out of the earlier IPython project, and "Jupyter" (a reference to the three core languages it originally targeted: Julia, Python, and R) now supports dozens of language kernels beyond those three. JupyterLab is the newer, more full-featured interface built around the same underlying notebook format as the original Jupyter Notebook, and most cloud notebook providers today default to JupyterLab while remaining fully compatible with plain .ipynb files either way.
For machine learning specifically, that turns a notebook into a live, inspectable experiment log. Data loading, feature engineering, training, and result visualization all happen in one place, and because each cell keeps its own output attached, re-running the notebook top to bottom can reproduce the entire experiment from scratch.
Key features that make Jupyter useful for ML
Cell-by-cell execution
Code runs one cell at a time, in whatever order you choose, and because the kernel holds state between cells, you don't have to re-run an entire script just to test a small change. That makes iterating on something like a data augmentation step fast: tweak the cell, re-run it, see the effect immediately, without touching anything upstream.
Output and visuals rendered inline
Results, whether text, tables, or plots, render directly below the cell that produced them. Catching a broken assumption in your data early, before it burns an expensive training run, is a lot easier when you can see the distribution or the loss curve immediately rather than digging through log files after the fact.
Markdown for real documentation
Notebooks aren't just code. Markdown cells let you write formatted text and math right alongside the code that produced a result, so a notebook can double as both an executable script and a readable writeup of what you were testing and why. For a research or ML team, that turns individual experiments into something a teammate (or your future self) can actually follow months later.
Native integration with visualization libraries
matplotlib, seaborn, and plotly all render straight into the notebook, whether as static images or interactive widgets. That means checking feature distributions, tracking a training curve, or visualizing embeddings never requires leaving the notebook for a separate tool.
What people actually use Jupyter for
Exploratory data analysis
Loading, cleaning, and immediately inspecting a dataset, cell by cell, is one of the most common notebook workflows there is. Pulling in a library like SciPy or statsmodels for a quick statistical test or regression fits naturally into the same flow, with results rendered right where you can see them.
Prototyping and debugging ML models
Within a single notebook you can load a dataset, stand up a baseline model, tweak hyperparameters, and track validation metrics, all without re-running the full pipeline every time you change one piece. That tight loop is why notebooks remain the default for early-stage model development, even on teams that move everything to production code once an approach proves out.
Teaching and reproducible research
Notebooks merge code and narrative into one document, which is exactly what both teaching and published research need: an instructor can walk through an algorithm with live code and commentary side by side, and a researcher can share a full, runnable record of an experiment rather than a static write-up someone has to take on faith. That reproducibility is part of why some scientific collaborations publish their full analysis notebooks alongside a paper, letting anyone rerun the same steps and check the result independently.
Why Jupyter stuck as the default for AI research
Every major ML framework, PyTorch, TensorFlow, scikit-learn, Hugging Face's libraries, has first-class notebook support, and Hugging Face in particular ships an extensive library of official notebooks walking through fine-tuning and evaluation for specific model families. That ecosystem fit is a big part of why Jupyter became the default rather than staying one option among several.
Fast iteration on ideas
Cell-based execution lets you rerun just the piece you changed, tune a hyperparameter, and immediately see the effect, without restarting a full pipeline. That makes a single notebook a reasonable place to compare several model variants or regularization strategies side by side before committing to one.
GPU acceleration without extra setup, when it's configured right
Training and fine-tuning inside a notebook benefit directly from GPU access, and cloud notebook environments that come with CUDA and drivers preconfigured remove a real source of friction: a lot of wasted setup time on a local machine goes into getting the CUDA toolkit, driver version, and framework version to actually agree with each other. Renting an L40S for lighter experimentation or an H100 for heavier training directly through a notebook session skips that entirely.
Reproducibility and collaboration
A notebook preserves not just code but the order it ran in and the outputs it produced, which means a colleague can open it, rerun it, and get comparable results. Notebooks can be versioned in Git, exported to HTML or PDF for sharing outside the team, and increasingly wired into CI pipelines that execute a notebook automatically to check it still produces valid output.
Running Jupyter: local versus cloud
Local setup
Locally, Jupyter runs through an environment you manage yourself, whether that's venv, Conda, or Docker, using JupyterLab, the classic Notebook interface, or an editor's built-in Jupyter support. You're responsible for your own CUDA runtime, drivers, and library versions, and you're capped by whatever's physically in the machine: scaling to a bigger GPU or a multi-GPU cluster means external setup a local environment doesn't give you. It's a fine setup for small models and quick experiments, but the moment a dataset or model outgrows a laptop's memory, or a fine-tuning run needs a GPU stronger than whatever's built in, the local setup becomes the bottleneck rather than the code.
Cloud-hosted notebooks
A cloud notebook environment runs the kernel on a remote server with direct GPU access, typically with CUDA, drivers, and common ML libraries already configured, and with storage that persists independently of any single session. That removes the local-environment management overhead entirely and gives you access to far more compute than most individual machines have on hand. On Aquanode, a notebook session backed by an L40S or H100 comes GPU-ready without you touching a driver install, and because the environment persists rather than resetting between sessions, you don't lose your installed packages or in-progress work when you pause it.
A typical workflow, start to finish
A notebook-driven project usually starts with loading data from wherever it lives (object storage, a database, or a repository), then moves into preprocessing: cleaning, normalizing, and sometimes generating new features, with pandas handling smaller datasets and Dask or PySpark stepping in once volume grows. From there, model setup differs by task: scikit-learn or XGBoost for classical ML, PyTorch or TensorFlow for deep learning, both of which scale across multiple GPUs when the workload calls for it. Evaluation follows, using whatever metric fits the task (accuracy, F1, perplexity for language models, and so on), and the last step is documentation: recording hyperparameters and library versions, saving the notebook to Git, and exporting a shareable report so the work stays reproducible even as the underlying data or code changes later.
Where Jupyter runs into real limits
Jupyter is exceptional for exploration, but it was never designed to be a production system, and stretching it into one tends to surface the same problems repeatedly.
Not built for production deployment
Notebook code tends to accumulate as loosely organized cells rather than tested, modular functions, and moving a working notebook into production usually means rewriting it into proper packages with real test coverage and CI/CD integration. Production also demands strict dependency pinning and monitoring that notebooks don't provide out of the box.
Versioning is genuinely painful
Because a notebook's .ipynb file is JSON and execution order affects results, the same notebook can behave differently for two different people, and Git diffs on notebook files are notoriously hard to read compared to diffing plain code. Left unchecked, notebooks also tend to accumulate redefined variables, duplicated functions, and abandoned experiments left in place, which erodes code quality over time.
Live collaboration barely exists
Unlike a shared document, Jupyter isn't built for multiple people to work in the same notebook at the same time. Notebooks can be forked and shared, similar to a code repository, but there's no built-in way to merge those forks back together the way you'd merge a pull request, which makes real-time collaborative editing a persistent gap rather than a solved problem.
Distributed training needs extra plumbing
A notebook runs within a single kernel by default, with no native support for distributed computing. Scaling training across many GPUs means integrating external systems like Slurm, Kubernetes, PyTorch's distributed data-parallel tooling, or Horovod, none of which Jupyter provides out of the box. Skip that integration and a notebook simply won't coordinate a multi-node training run on its own.
| Criterion | Jupyter Notebook | Production tooling (Kubernetes, Slurm, MLflow, Airflow) |
|---|---|---|
| Code structure | Loosely organized cells, prone to duplication | Packaged code with tests and CI/CD |
| Version control | JSON diffs, execution-order-dependent results | Deterministic, versioned pipelines |
| Reproducibility | Manual "Restart & Run All," session state varies | Locked dependencies, reproducible by default |
| Scaling | Single kernel; distributed runs need external tools | Native multi-node, multi-GPU orchestration |
| Collaboration | Shared as files, no live multi-user editing | Granular access controls across teams |
Despite these limits, notebooks do occasionally make it into production pipelines directly, most famously at Netflix, where engineering teams have published on using parameterized, scheduled notebooks as an actual step in production data workflows rather than treating them purely as a prototyping tool. That's the exception rather than the norm, but it's a reminder that the "notebooks are only for prototyping" rule has real edge cases.
Where notebooks fit in the bigger picture
Jupyter earns its place in nearly every ML workflow as the tool for experimentation, analysis, and documentation, not as a replacement for the production infrastructure a model eventually needs once it ships. The realistic path is exploration in a notebook first, then migration into tested, modular code once an approach is proven out.
Running that exploration against a cloud GPU instead of a local machine removes most of the setup friction, and skips the moment where a promising local prototype turns out to need more memory than a laptop GPU has. Spin up a notebook on an L40S or H100 through Aquanode's marketplace, and if you're testing tokenizer behavior or a fine-tuning run interactively, our pieces on how tokenizers work and what fine-tuning actually involves are natural next reads.