Setting Python Environment
What core mathematical principle governs read the project before you run it?
How this lesson unfolds.
Begin with What core mathematical principle governs read the project before you run it?. Follow the visual diagrams to build an intuitive mental model of setting python environment.
Why must you inspect a machine learning project's architecture, dependencies, and entry points before executing code?
Think of firing up an unfamiliar deep learning repository like accepting an anonymous USB drive and plugging it into your main workstation. If you blindly type `python train.py` without inspecting what hardware it targets, what files it writes, or what PyTorch version it requires, you risk allocating 40 GB of VRAM on an 8 GB card, triggering CUDA out-of-memory crashes, or silently clobbering existing site-packages. Reading the blueprint first saves hours of cryptic debugging.
- Learning goalAudit repository documentation, system dependencies, hardware requirements, and execution entry points prior to initiating machine learning workflows.
- Helpful foundationsUnix shell navigation and file permissions · PEP 518 – Specifying Minimum Build System Requirements for Python Projects · Reproducible Research in Computational Science (Peng, 2011)
- Common trapAssuming that running `pip install` immediately on a new machine learning repo is safe. Many ML repos have compiled C++/CUDA dependencies (FlashAttention, bitsandbytes, torch-scatter) that will fail to compile or silently overwrite system-wide CUDA runtime libraries unless audited first.
- Next connectionAfter surveying the high-level project vision, the immediate next step is surveying the physical layout of the repository directory tree.
More context & mechanics
Auditing an ML repository follows a systematic inspection checklist:
- Root Configuration Inspection:
- `README.md`: Identifies hardware recommendations (GPU compute capability, VRAM size), software prerequisites, and baseline checkpoints.
- `pyproject.toml` or `setup.py`: Identifies project build-backends, package metadata, and entry points.
- `requirements.txt`: Maps exact package versions and potential dependency conflicts.
- Execution Vector Mapping:
- Determine whether the project is designed as an importable library (`import llms_from_scratch`) or a command-line script pipeline (`python train.py --config config.yaml`).
- Hardware & Resource Requirements:
- Compute footprint: CPU-only vs. NVIDIA CUDA vs. Apple Silicon MPS.
- Disk storage: Datasets (10–500 GB) and model checkpoints (GPT-2 124M weights require ≈ 500 MB; 7B weights require ≈ 14 GB).
In enterprise AI labs, executing untrusted code without repository audits violates security compliance. Engineers verify cryptographic commit signatures, inspect sub-dependencies for malicious supply-chain packages, and run sandboxed containerized evaluations before provisioning GPU cluster resources.
In the early days of deep learning (Caffe and Theano era, 2010–2014), setting up a project often required manual compiling of CUDA drivers and editing Makefiles for days. Modern Python packaging standards (PEP 517/518) streamlined builds, but the rapid evolution of PyTorch and CUDA toolkits makes methodical upfront inspection essential.
- Read before you run; inspect before you install.
- A five-minute audit of the README prevents a five-hour fight with CUDA drivers.
How does inspecting the repository directory structure reveal the data pipeline, model definitions, and execution flow?
Imagine entering a precision carpentry shop. Before touching a single power saw, you look around to see where raw lumber is unloaded, where the workbench is located, where safety goggles hang, and where finished cabinets are stored. In an LLM repository, you look for `data/` (raw corpora and cached datasets), `model.py` (tensor definitions and attention layers), `train.py` (gradient update loops and checkpointing), and `requirements.txt` (exact library pins). Knowing the floor plan prevents getting lost in code.
- Learning goalAnalyze a deep learning codebase structure to map data pipelines, model modules, checkpoint directories, and execution scripts.
- Helpful foundationsPython package directory structure · PEP 420 – Implicit Namespace Packages · Build a Large Language Model (From Scratch) - Sebastian Raschka Code Repository
- Common trapTreating a multi-file Python repository as a single script run from any folder. In Python, relative imports like `from .model import GPTModel` depend strictly on the current working directory and `PYTHONPATH`; executing scripts from the wrong directory triggers immediate `ModuleNotFoundError`.
- Next connectionUnderstanding directory organization leads directly to analyzing the three distinct software layers that make an ML project fully reproducible.
More context & mechanics
A standard educational and production LLM repository adheres to clear structural conventions:
```text
llms-from-scratch/
├── ch01/ # Overview and setup scripts
├── ch02/ # Data preprocessing, tokenization, DataLoader
├── ch03/ # Multi-head self-attention mechanisms
├── ch04/ # Complete GPT-2 model architecture
├── ch05/ # Pretraining loop, evaluation loss, generation
├── ch06/ # Text classification fine-tuning
├── ch07/ # Instruction fine-tuning (SFT)
├── requirements.txt # Pinned dependency specifications
└── README.md # Setup and execution guide
```
Tracing code dependencies flows sequentially:
ch02 (Data) →(Weights) ch05 (Pretraining)
Understanding this modular pipeline prevents importing code out of order.
Production codebases like Megatron-LM or Hugging Face Transformers separate dataset streaming, distributed communication primitives (`torch.distributed`), and model forward passes into separate modular subpackages. Navigating this structure quickly is a core skill for ML infrastructure engineers.
Sebastian Raschka structured the LLMs-from-scratch open-source repository as an organic pedagogical progression. Rather than dumping an opaque 5,000-line library, each chapter builds incrementally on the previous one, allowing students to verify each subsystem in isolation.
- Map the repository layout before firing the execution engine.
- Data feeds attention; attention feeds the transformer; the transformer feeds the training loop.
What are the three software layers governing a reproducible machine learning environment?
Think of your ML workstation as a three-story building. The ground foundation is the Hardware & System Layer: your physical GPU (NVIDIA A100 or Apple M-series silicon), Linux kernel, and CUDA runtime driver (e.g. CUDA 12.4). The middle floor is the Virtual Environment Layer: an isolated Python interpreter binary (Python 3.10) and its specific library wheel packages (PyTorch 2.3, tiktoken 0.7). The penthouse is the Project Code Layer: your actual scripts, configs, and weights. A crack in the middle floor can bring down the entire building even if the code upstairs is mathematically flawless.
- Learning goalDeconstruct an ML deployment stack into hardware/driver, virtual environment, and application code layers to ensure deterministic reproducibility.
- Helpful foundationsCUDA Driver vs CUDA Toolkit architecture · PEP 405 – Python Virtual Environments · PyTorch Ecosystem Hardware Requirements Guide
- Common trapBelieving that `pip install torch` installs physical GPU drivers. Pip installs PyTorch Python bindings and pre-compiled CUDA runtime libraries, but cannot install or repair the underlying NVIDIA kernel driver (`nvidia-smi`) on your operating system.
- Next connectionWith the three layers defined, we examine how to keep the educational guide code and your local virtual environment in lockstep.
More context & mechanics
Deterministic execution requires stability across three distinct layers:
- Layer 1: System & Hardware Layer
- Host Operating System (Linux x86_64, macOS ARM64).
- NVIDIA Display Driver & CUDA Kernel Driver (queried via `nvidia-smi`).
- Hardware Accelerators (GPU VRAM, Apple Unified Memory).
- Layer 2: Virtual Environment Layer
- CPython runtime binary (`bin/python` targeting e.g. 3.10.12).
- Pinned Python package wheels in `site-packages/` (`torch==2.3.0`, `tiktoken==0.7.0`).
- Isolated `PATH` and `PYTHONPATH` environment variables.
- Layer 3: Application Code & Data Layer
- Repository source files (`.py`, `.ipynb`).
- Hyperparameter configurations and random seeds (`torch.manual_seed(123)`).
- Tokenizer vocabularies and dataset binary caches.
Reproducibility = Layer 1 (Drivers) ∧ Layer 2 (Environment) ∧ Layer 3 (Code & Seeds)
In enterprise machine learning operations (MLOps), Docker containers and Kubernetes clusters freeze Layers 1 and 2 into immutable container images (`nvcr.io/nvidia/pytorch:24.03-py3`). This guarantees that training runs on an 8-GPU node reproduce identical loss curves on a 1,000-GPU cluster.
The 'it works on my machine' crisis plagued early data science. The introduction of virtual environments in PEP 405 (Python 3.3, 2011) and Docker containerization (2013) transformed experimental code into portable, industrial-grade software engineering pipelines.
- System drivers underneath; virtual environment in the middle; application code on top.
- If any layer shifts, reproducibility breaks.
How do version mismatches between deep learning dependencies lead to silent computational failures and breaking API shifts?
Imagine following a recipe written in 1920 that asks for 'two drams of spirit and a gill of milk'. If you interpret 'gill' using a different regional measurement, your cake will turn into soup. In PyTorch and Transformers, methods like `torch.nn.functional.scaled_dot_product_attention` or `tiktoken.get_encoding` introduce breaking parameter changes across minor versions. If your environment uses PyTorch 1.12 while the code relies on PyTorch 2.3 FlashAttention kernels, code crashes or silently falls back to 5x slower sequential math.
- Learning goalAudit and synchronize environment library versions with textbook reference specifications to prevent breaking API changes and numerical discrepancies.
- Helpful foundationsSemantic versioning (semver: major.minor.patch) · PyTorch migration release notes
- Common trapAssuming that newer package versions are always strictly backward-compatible. Minor version bumps in deep learning frameworks frequently deprecate arguments, alter default initialization seeds, or change tensor broadcasting behaviors.
- Next connectionTo keep our environment synchronized, we must commit to one deterministic environment manager rather than mixing tools.
More context & mechanics
Semantic versioning follows the convention `MAJOR.MINOR.PATCH`:
- MAJOR: Breaking API changes (e.g., PyTorch 1.x → 2.0 introduced `torch.compile`).
- MINOR: New functionality without breaking backward compatibility (e.g., PyTorch 2.2 → 2.3).
- PATCH: Backward-compatible bug fixes and performance patches.
In deep learning, however, even patch and minor releases can change:
- Default random weight initialization distributions.
- Tensor casting rules between BF16 and FP32.
- Underlying cuDNN convolution or attention algorithm selections.
To synchronize:
Specification: \texttttorch>=2.2.0, <2.4.0 ⟹ Pip Resolver selects highest compatible 2.3.x
Production teams maintain `requirements.lock` or `Pipfile.lock` containing exact sha256 wheel hashes. When a model trains for 3 weeks on 1024 H100 GPUs, an unexpected sub-dependency update in the middle of a node restart could corrupt the checkpoint resumption.
The transition from PyTorch 1.x to 2.0 in March 2023 introduced TorchDynamo and Inductor graph compilation. Codebases that didn't pin versions experienced immediate compilation errors or unexpected tensor graph capture issues.
- Lock your versions, or the versions will unlock chaos.
- Reproducibility begins with identical package versions.
Why must you strictly choose a single environment management toolchain (`venv`, `conda`, or `uv`) rather than mixing them?
Imagine hiring three different interior decorators at the same time to furnish one small studio apartment without letting them talk to each other. One moves the sofa, the second replaces the rug with tiles, and the third tears out the wall. Mixing `conda install` and `pip install` inside the same environment or swapping between `venv` and `brew` causes path collisions, dual C++ dynamic libraries (`libstdc++.so`), and corrupted binary wheels that crash with uninterpretable segmentation faults.
- Learning goalSelect and configure an isolated Python environment toolchain (`venv` or `conda`) while maintaining path determinism and preventing package manager cross-contamination.
- Helpful foundationsPython executable search order (`PATH`) · Package distribution formats (wheels vs conda binaries)
- Common trapBelieving you can freely mix `conda install` and `pip install` without consequence. Conda manages pre-compiled binary packages across all system libraries, whereas Pip manages Python wheels; mixing them frequently overwrites shared dynamic libraries, corrupting the environment.
- Next connectionOnce you have chosen your toolchain, you must verify exactly which Python executable your terminal session is invoking.
More context & mechanics
Comparison of standard Python environment pathways:
- Standard `venv` (Recommended for pure PyTorch):
- Built into Python standard library (`python3 -m venv venv`).
- Lightweight, zero system modifications.
- Relies directly on PyTorch's official pre-compiled wheels containing embedded CUDA runtime libraries.
- Conda / Mamba:
- Manages non-Python system dependencies (C++ compilers, BLAS, CUDA Toolkit).
- Higher disk footprint (2–5 GB baseline).
- Strict channel precedence rules (`conda-forge` vs `defaults` vs `pytorch`).
- `uv` (Modern Rust-based toolchain):
- Extremely fast resolver and environment manager (10–100× faster than pip).
- Fully compliant with standard Python wheel specifications.
Decision Rule:
Stick strictly to: \textttpython3 -m venv .venv ⟹ \textttsource .venv/bin/activate ⟹ \textttpip install
Machine learning teams standardize on a single organization-wide toolchain. A standard setup is Python's native `venv` orchestrated inside minimal Ubuntu Docker images, avoiding heavy Conda layers in production deployments.
Conda was created by Continuum Analytics in 2012 because pip could not manage compiled scientific C/Fortran libraries like NumPy and SciPy on Windows and macOS. With the arrival of the binary wheel format (PEP 427, 2012), pip became capable of installing complex pre-compiled binaries, reducing the need for Conda in modern PyTorch projects.
- Pick one environment path and walk it faithfully.
- Mixing package managers creates binary graveyards.
How do shell environment variables and `PATH` precedence determine the active Python interpreter?
When you type `python` in your terminal, your operating system does not possess magical psychic powers. It simply scans a list of folders listed in your `PATH` variable from left to right, executing the very first binary named `python` it encounters. If your system `/usr/bin/python3` comes before your virtual environment's `venv/bin/python`, your packages will be installed into an inaccessible system folder or blocked by permission errors.
- Learning goalTrace shell `PATH` resolution and inspect `sys.executable` and `which python3` to guarantee the active interpreter corresponds to the intended virtual environment.
- Helpful foundationsPOSIX shell environment variables (`PATH`, `PYTHONPATH`) · Process execution and binary discovery
- Common trapAssuming that typing `source venv/bin/activate` is permanent. Activation only modifies the current terminal shell's session variables; opening a new terminal window or tab resets `PATH` back to default system settings unless reactivated.
- Next connectionNow that we can verify the active binary, we explore why isolating this specific LLM environment is critical to workstation stability.
More context & mechanics
When a command is entered, the shell searches directories defined in `PATH`:
PATH = dir₁ : dir₂ : dir₃ : ṡ
When you activate a virtual environment (`source .venv/bin/activate`):
PATH arrow /path/to/project/.venv/bin : PATH
Verification commands:
```bash
which python3
# Correct output: /Users/username/.../project/.venv/bin/python3
python3 -c "import sys; print(sys.executable)"
# Output matches the active virtual environment binary
```
If `which python3` prints `/usr/bin/python3` or `/opt/homebrew/bin/python3`, your virtual environment is NOT active.
Automated CI/CD pipelines (GitHub Actions, GitLab CI) never rely on `source activate`. Instead, they explicitly invoke the virtual environment binary directly:
`/path/to/.venv/bin/python -m pytest tests/`
This eliminates any possibility of `PATH` ambiguity across execution agents.
The `PATH` environment variable was introduced in Unix Version 7 in 1979 by Stephen Bourne. Decades later, it remains the universal mechanism for process execution across macOS, Linux, and POSIX-compliant operating systems.
- Trust `which python3`, not your assumptions.
- The leftmost directory in `PATH` always wins the execution race.
Why do LLM development projects require strict environment isolation compared to standard software development?
Think of an LLM project like handling radioactive biological samples. LLM libraries demand massive compiled C++ and CUDA binaries, specific BLAS math libraries (MKL, OpenBLAS), and pinned versions of tokenizers. If you install these directly into your computer's global system Python, you risk breaking your operating system's internal utilities (like package managers or desktop display tools that rely on system Python). A virtual environment is a sealed containment glovebox: if an experiment goes wrong, you can delete the folder in 2 seconds with zero collateral damage.
- Learning goalAnalyze the architectural risks of system Python contamination and evaluate how isolated environments prevent dependency drift and ABI incompatibilities.
- Helpful foundationsOperating system package managers (apt, Homebrew) · C++ Application Binary Interface (ABI) compatibility
- Common trapThinking that virtual environments duplicate your operating system or waste hundreds of gigabytes like virtual machines. A Python virtual environment is lightweight (often a few hundred megabytes to gigabytes for PyTorch wheels) and simply maintains symlinks to the base interpreter alongside a local `site-packages` directory.
- Next connectionWithin this isolated glovebox, we must carefully read dependency constraints before triggering installation commands.
More context & mechanics
Operating systems (especially Ubuntu, Debian, macOS) utilize system Python for critical internal tasks:
- `apt` package manager integration (`python3-apt`).
- System daemon services, printing stacks, and networking scripts.
Executing `sudo pip install torch` overwrites shared C-libraries in `/usr/lib/python3/dist-packages/`, leading to:
- Dependency Deadlocks: System packages expecting `urllib3<2.0` breaking when an ML tool installs `urllib3==2.2`.
- C++ ABI Mismatches: Clashing versions of `glibc`, `libgomp`, or OpenMP runtime symbols.
- PEP 668 Protection: Modern Linux distros deliberately block global pip installs with `externally-managed-environment` errors to protect OS integrity.
An isolated environment decouples project dependencies entirely:
OS Utilities → System Python \bot LLM Code → Project .venv
Every serious AI research lab mandates containerization or isolated virtual environments. Engineers working on multiple models simultaneously (e.g. PyTorch 2.1 for an older LLaMA-2 repo, and PyTorch 2.4 for a new Llama-3 repo) switch between clean `.venv` folders without conflict.
PEP 668 ('Marking Python base environments as \"externally managed\"') was adopted in Python 3.11. It officially locked down system Python installations across Debian, Ubuntu, and Homebrew, forcing developers to adopt virtual environments as standard practice.
- Never touch system Python; always build inside a sandbox.
- Containment protects your OS; isolation protects your experiment.
How do dependency specification files (`requirements.txt`, `pyproject.toml`) encode version bounds, and how do pip resolvers handle them?
Reading a contract's fine print before signing is good practice. In `requirements.txt`, seeing `torch>=2.2.0,<2.4.0` is very different from seeing `torch`. An unpinned `torch` pulls down whatever bleeding-edge nightlies or newest releases exist—potentially breaking CUDA driver compatibility or changing default attention kernels. Reading constraints allows you to foresee compilation requirements (like requiring `ninja` or `cmake` for C++ extensions).
- Learning goalParse and interpret PEP 440 version specifiers in dependency files and predict solver resolution behaviors.
- Helpful foundationsPEP 440 – Version Identification and Specification · Backtracking dependency resolvers
- Common trapAssuming that running `pip install -r requirements.txt` will always produce the exact same environment on two different computers. Unless versions are pinned with exact equality (`==`) along with platform-specific wheel hashes, pip may resolve different sub-dependencies on macOS vs Linux.
- Next connectionAfter reading constraints, we proceed to executing the installation and running immediate programmatic verification tests.
More context & mechanics
Understanding PEP 440 comparison operators:
- `torch == 2.2.2`: Exact pin (deterministic).
- `torch >= 2.2.0, < 2.4.0`: Bounded range (allows non-breaking minor/patch updates).
- `torch ~= 2.2.0`: Compatible release clause (equivalent to `>= 2.2.0, == 2.2.*`).
- `torch`: Unbounded (unsafe; installs latest release regardless of compatibility).
The pip backtracking resolver constructs a dependency constraint graph:
G = (V, E), V = packages, E = version constraints
If Package A requires `numpy<1.24` and Package B requires `numpy>=1.26`, the resolver halts with an unsatisfiable conflict error (`ResolutionImpossible`).
Production LLM teams inspect dependencies for compiled binary wheel availability before initiating cluster jobs. If a dependency lacks a pre-compiled wheel for Python 3.12 on Linux ARM64, pip will attempt to build from source, triggering missing C++ header errors.
In 2020, pip 20.3 rolled out a brand-new backtracking dependency resolver. Previously, pip used a naive greedy first-match approach that frequently installed silently broken combinations of packages.
- Unpinned dependencies are bugs waiting to explode.
- Exact pins (`==`) yield reproducibility; loose pins (`>=`) invite drift.
What verification procedures prove that deep learning packages installed successfully and can access hardware acceleration?
A mechanic does not return a car after an engine rebuild without turning the ignition key and listening for odd noises. After running `pip install -r requirements.txt`, do not assume everything works just because the terminal stopped scrolling text. You must immediately run a one-line sanity check in Python: import `torch`, check `torch.__version__`, allocate a small tensor, and query `torch.cuda.is_available()` or `torch.backends.mps.is_available()`. If the tensor fails to allocate on your accelerator now, it will definitely crash during training at 2 AM.
- Learning goalExecute dependency installation via pip and run programmatic verification tests to validate PyTorch tensor operations and hardware accelerator connectivity (CUDA/MPS).
- Helpful foundationsPyTorch device management (`cuda`, `mps`, `cpu`) · Smoke testing principles
- Common trapAssuming `torch.cuda.is_available() == True` on an Apple Silicon Mac or CPU server. On Apple Silicon, PyTorch uses the Metal Performance Shaders (`torch.backends.mps.is_available()`); querying CUDA will correctly return `False`.
- Next connectionMany practitioners build and test models inside Jupyter notebooks, which introduces another layer of environment gotchas.
More context & mechanics
A robust verification script tests three fundamental capabilities:
```python
import torch, tiktoken
# 1. Verify library versions
print("PyTorch version:", torch.__version__)
print("Tiktoken version:", tiktoken.__version__)
# 2. Verify Hardware Acceleration
device = "cuda" if torch.cuda.is_available() else (
"mps" if torch.backends.mps.is_available() else "cpu"
)
print("Active compute device:", device)
# 3. Verify Tensor Arithmetic on Device
x = torch.tensor([1.0, 2.0, 3.0], device=device)
y = x * 2.0
assert y.sum().item() == 12.0
print("Hardware tensor operations verified successfully!")
```
This smoke test verifies:
- Dynamic library linkage (`libtorch.so`, `libc10.so`).
- GPU driver context creation.
- Accurate floating-point tensor execution on the targeted device.
Cluster orchestration engines (Slurm, Ray, Kubernetes) run this exact smoke test on every compute node as a pre-flight health check before dispatching multi-node distributed training jobs.
Hardware acceleration in Python evolved from manual CUDA C extensions in the late 2000s to PyTorch's unified `torch.device` abstraction introduced in PyTorch 0.4 (2018), allowing seamless device-agnostic tensor programming.
- Never trust a silent install; always run the smoke test.
- Allocate a tensor on device before allocating hours to training.
Why does running `!pip install` inside a Jupyter notebook often corrupt environments, and why is `%pip install` the correct magic command?
Imagine you are inside a submarine control room (your Jupyter notebook) and want to order supplies for your submarine. If you yell out the hatch using a megaphone (`!pip install`), you might trigger a delivery truck to deposit supplies at the general harbor dock (your system Python) rather than into the submarine's storage room. The `%pip install` magic command guarantees that pip is invoked using the exact same Python interpreter that is currently executing the notebook kernel.
- Learning goalDifferentiate shell escapes (`!pip`) from IPython magics (`%pip`) to ensure notebook package installations target the active kernel environment.
- Helpful foundationsIPython kernel execution model · Subprocess execution vs kernel introspection
- Common trapAssuming `!pip install <pkg>` installs packages into the notebook's active virtual environment. `!pip` runs in a separate subshell using the system `PATH`, which often points to a completely different Python interpreter than the kernel executing the notebook.
- Next connectionTo prevent notebook kernel confusion entirely, we must register and run Jupyter directly through the project's virtual environment.
More context & mechanics
The distinction between `!` and `%` in IPython:
- Shell Escape (`!pip install`):
- Spawns a temporary subshell: `/bin/sh -c "pip install ..."`.
- Uses the shell's default `PATH`.
- If the Jupyter server was launched from a different environment, `pip` resolves to the host or system Python, not the running kernel's environment.
- IPython Magic (`%pip install`):
- Directly queries the running kernel's executable:
\texttt\%pip install ... ≡ \textttsys.executable -m pip install ...
- Guarantees that packages land in the exact `site-packages` directory of the active kernel.
In Google Colab, Kaggle Kernels, and AWS SageMaker notebooks, running `%pip install` prevents silent environment corruption when multiple Python versions or conda environments coexist on the instance.
For years, the `!pip install` gotcha was one of the single most reported bugs across StackOverflow and GitHub for Jupyter users. In 2019, IPython 7.3 officially introduced the `%pip` and `%conda` magics specifically to eliminate this trap permanently.
- `%pip` reaches the kernel; `!pip` shouts at the shell.
- Always use percent (`%`) for package management inside notebooks.
How do you register a virtual environment as an isolated Jupyter kernel using `ipykernel`?
Think of Jupyter Lab as an airport terminal with multiple boarding gates. Each gate can board a flight to a different destination: one gate connects to Python 3.9 for legacy data science, another to Python 3.10 for your LLM project. By running `python -m ipykernel install --user --name=llm-scratch`, you hang a clear neon sign over the gate. When you open a notebook, you select that exact gate, ensuring your notebook code flies directly into your custom PyTorch environment.
- Learning goalRegister a Python virtual environment as a named IPython kernel specification using `ipykernel install` and verify kernel spec discovery.
- Helpful foundationsJupyter kernel specifications (`kernelspec`) · JSON configuration files in POSIX user directories
- Common trapBelieving that starting Jupyter from an activated virtual environment automatically forces all opened notebooks to run inside that environment. If the notebook's internal metadata specifies a different kernel, Jupyter will silently execute cells in the wrong environment.
- Next connectionFinally, we inspect the best practices for launching Jupyter directly from the activated project environment and verifying the execution path.
More context & mechanics
A Jupyter kernel specification (`kernel.json`) maps an interactive session to a concrete binary:
```json
"argv": [
"/path/to/project/.venv/bin/python",
"-m",
"ipykernel_launcher",
"-f",
"connection_file"
],
"display_name": "Python (llm-scratch)",
"language": "python"
```
Registration procedure:
- Activate virtual environment: `source .venv/bin/activate`
- Install ipykernel in environment: `pip install ipykernel`
- Register the kernel spec:
`python -m ipykernel install --user --name=llm-scratch --display-name "Python (llm-scratch)"`
- Verify discovery:
`jupyter kernelspec list`
Multi-tenant GPU servers in enterprise research labs host dozens of research projects. Shared JupyterLab servers rely on named kernelspecs so researchers can switch between distinct PyTorch and JAX environments with a single click in the UI.
Fernando Pérez created IPython in 2001, which split in 2014 into the language-agnostic Project Jupyter protocol (ZeroMQ messaging between client and kernel) and the IPython Python kernel (`ipykernel`).
- Register the kernel, name the spec, own your execution.
- A named kernel links Jupyter's frontend to your virtual environment's backend.
What is the complete deterministic workflow for activating the virtual environment, launching Jupyter, and verifying the runtime interpreter?
Think of an astronaut running through a pre-flight checklist. Before igniting the main engines, you verify fuel pressure, oxygen levels, and radio communications. When launching your LLM project: (1) open terminal in project directory, (2) run `source venv/bin/activate`, (3) run `jupyter lab`, and (4) in the very first notebook cell, execute `import sys; print(sys.executable)`. If the printed path points to `venv/bin/python`, you are cleared for launch.
- Learning goalExecute the end-to-end environment startup sequence and programmatically validate the active interpreter path and GPU acceleration devices in the first notebook cell.
- Helpful foundationsTerminal process management · Interactive notebook workflows
- Common trapSkipping interpreter verification in notebook cell 1. Many debugging headaches—such as missing imports or unexplained CUDA errors—are solved in 5 seconds simply by checking `sys.executable`.
- Next connectionWith our computing environment isolated, verified, and linked to Jupyter, we are ready to dive into the core architecture of Large Language Models in Lesson 2.
More context & mechanics
The canonical terminal startup protocol:
```bash
# Step 1: Navigate to repository root
cd /path/to/llms-from-scratch
# Step 2: Activate isolated virtual environment
source .venv/bin/activate
# Step 3: Launch JupyterLab directly from active environment
jupyter lab
```
In Notebook Cell 1 (Mandatory Verification Cell):
```python
import sys
import torch
print("Executable:", sys.executable)
assert ".venv" in sys.executable, "ERROR: Not running inside the project virtual environment!"
print("PyTorch Version:", torch.__version__)
print("CUDA Available:", torch.cuda.is_available())
if torch.cuda.is_available():
print("Device Name:", torch.cuda.get_device_name(0))
```
This guarantees that all subsequent cells run against the intended dependencies.
Senior deep learning engineers embed this verification cell as standard boilerplate at the top of every exploratory analysis and model training notebook to prevent silent execution bugs across team members.
The reproducibility crisis in computational science prompted scientific journals (such as Nature and Science) to demand explicit code execution environments. Starting every notebook with deterministic interpreter logging has become an industry gold standard.
- Verify in cell one before you train on step one.
- `sys.executable` never lies.