# Jupyter Notebooks

### What is a Jupyter Notebook?

A Jupyter Notebook is an open-source web application for creating and sharing documents containing:

* Live code
* Equations
* Visualizations
* Narrative text

It is a standard tool for data science, machine learning, and AI research.

***

### Getting Started with Your Notebook

When you open your rented Jupyter environment, you'll see the Jupyter file browser. From here, you can:

* **Create a new notebook:**
  * Click `New → Python 3 Kernel` to start a fresh notebook.
* **Upload files:**
  * Use the `Upload` button to bring in datasets or existing notebooks.

***

### Using GPU in Your Notebook

#### NVIDIA CUDA Environment

Your CUDA environment comes with PyTorch pre-installed.

* **Verify GPU access:**

  ```python
  import torch
  print(f"CUDA available: {torch.cuda.is_available()}")
  print(f"GPU: {torch.cuda.get_device_name(0)}")
  print(f"VRAM: {torch.cuda.get_device_properties(0).total_mem / 1e9:.1f} GB")
  ```
* **Load and run a model on GPU:**

  ```python
  from transformers import AutoModelForCausalLM, AutoTokenizer

  model_name = "meta-llama/Llama-3.2-1B"
  tokenizer = AutoTokenizer.from_pretrained(model_name)

  model = AutoModelForCausalLM.from_pretrained(
      model_name,
      torch_dtype=torch.float16,
      device_map="auto"
  )

  inputs = tokenizer("Hello, world!", return_tensors="pt").to("cuda")
  outputs = model.generate(**inputs, max_new_tokens=50)

  print(tokenizer.decode(outputs[0]))
  ```

***

#### Apple MLX Environment

Your MLX environment comes with Apple's ML framework pre-installed.

* **Check the MLX backend:**

  ```python
  import mlx.core as mx
  print(f"MLX backend: {mx.default_device()}")
  ```
* **Create arrays on the GPU:**

  ```python
  a = mx.random.normal((1000, 1000))
  b = mx.random.normal((1000, 1000))
  c = a @ b  # Matrix multiplication on Metal GPU
  mx.eval(c)
  print(f"Result shape: {c.shape}")
  ```
* **Run language models with mlx-lm:**

  ```python
  from mlx_lm import load, generate

  model, tokenizer = load("mlx-community/Llama-3.2-1B-Instruct-4bit")
  response = generate(
      model,
      tokenizer,
      prompt="Explain quantum computing in simple terms:",
      max_tokens=200
  )

  print(response)
  ```

***

### Installing Additional Packages

* Use pip directly in a notebook cell, e.g.

  ```python
  !pip install datasets scikit-learn matplotlib seaborn
  ```

Remember: Installed packages are ephemeral and do not persist after your session ends. Include your pip install commands at the top of your notebook for future sessions.

Learn more about Jupyter Notebooks at the official docs: <https://docs.jupyter.org/en/latest>

### Tips for Productive Sessions

* Save your work frequently. Download notebooks and outputs before your session expires.
* Monitor VRAM usage. Use `!nvidia-smi` (CUDA) or check memory in your code to avoid out-of-memory errors.
* Use efficient data types. Load models in `float16` or `int4` to maximize VRAM usage.
* Plan for ephemeral storage. Upload datasets at the start of each session and download results before ending.
* Extend if needed. If your training job requires more time, extend your rental before it expires to avoid interruptions.

### Common Workflows

| Workflow                     | Recommended GPU                        | Duration        |
| ---------------------------- | -------------------------------------- | --------------- |
| Prototyping & Testing        | Any available GPU                      | 30 min – 1 hour |
| Fine-tuning small models     | RTX 4070+ (12+ GB VRAM)                | 2 – 4 hours     |
| Fine-tuning large models     | RTX 4090 / Multi-GPU (24+ GB VRAM)     | 4 – 8 hours     |
| Inference & Evaluation       | Any GPU matching model requirements    | 30 min – 1 hour |
| Data Processing              | Any GPU with sufficient VRAM           | 1 – 2 hours     |
| MLX Fine-tuning or Inference | Apple M-series (16+ GB unified memory) | 1 – 2 hours     |
