Behind the Secrets of LLMslab

Step 2 · do this once

Get set up

Four steps take you from your own terminal to a working Python environment with PyTorch on the cluster. After that, every job script switches the environment on by itself.

Before you start#

  • You have a ZIH login and you joined the course project p_lv_llmsecrets.
  • You are on the university network or the VPN.
  • You have a terminal: Terminal on macOS and Linux, PowerShell or Windows Terminal on Windows. All of them know ssh.

Not sure what a login node or a workspace is? Read Meet the machines first.

Type it once and every command on this site is ready to copy. It stays in this browser and is not sent anywhere. Your password is never needed on this site.

Marie and Martin. Martin installs PyTorch by hand on the login node. Marie runs the setup script with bash and nothing is switched on afterwards. See what happened

The four steps#

  1. Log in

    Pick a GPU cluster. Alpha has A100 GPUs, Capella has H100 GPUs. Both work for everything in this lab.

    on your computershell
    ssh <zih-login>@login1.alpha.hpc.tu-dresden.de      # GPU cluster, 8 A100 per node
    ssh <zih-login>@login1.capella.hpc.tu-dresden.de    # GPU cluster, 4 H100 per node
    ssh <zih-login>@login1.barnard.hpc.tu-dresden.de    # CPU cluster

    The first time, ssh asks if you trust the machine. Compare what it shows with the key fingerprints in the Compendium and answer yes. Then type your ZIH password. Nothing appears while you type, that is normal.

    Optional: log in without typing the password every time

    Create a key pair on your own computer and copy the public half to the cluster. Give the key a passphrase when you are asked.

    on your computer (macOS, Linux)shell
    ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
    ssh-copy-id -i ~/.ssh/id_ed25519.pub <zih-login>@login1.alpha.hpc.tu-dresden.de

    Windows has no ssh-copy-id. Copy the file over and add it by hand:

    on Windows, in PowerShellshell
    ssh-keygen -t ed25519
    scp $env:USERPROFILE\.ssh\id_ed25519.pub <zih-login>@login1.alpha.hpc.tu-dresden.de:
    ssh <zih-login>@login1.alpha.hpc.tu-dresden.de
    then, on the login nodeshell
    mkdir -p ~/.ssh && cat id_ed25519.pub >> ~/.ssh/authorized_keys && rm id_ed25519.pub

    More about keys and ssh settings is in the Compendium.

  2. Get the lab kit

    You are now on a login node. Clone the repository into your home folder. It is small.

    on the login nodeshell
    git clone https://github.com/himanshubeniwal/behind-llms-lab.git
    cd behind-llms-lab/hpc_lab
  3. Build the Python environment

    Installing PyTorch is real work, so it does not belong on the login node. Ask Slurm for a small interactive job, run the setup script there, and give the machine back.

    Two lines every job needs on Alpha. --nodes=1, because ZIH wants the number of nodes in every job. And --gres=gpu:1, because Alpha only accepts jobs that ask for a GPU, even an installation that does not use one. Without them you get QOSMinGRES or a reminder about -N. On Barnard, leave out --gres.

    on the login nodeshell
    srun --account=p_lv_llmsecrets --nodes=1 --gres=gpu:1 \
         --ntasks=1 --cpus-per-task=4 --mem-per-cpu=2G \
         --time=00:45:00 --pty bash -l          # a small interactive job
    cd ~/behind-llms-lab/hpc_lab
    source 00_setup/setup_env.sh                # workspace, venv, training text
    exit                                        # give the machine back

    The first run takes 5 to 10 minutes. At the end you should see something like this:

    what the setup script prints
    workspace : /data/horse/ws/<zih-login>-llm-lab
    creating the venv /data/horse/ws/<zih-login>-llm-lab/venv-llm (only the first time, this takes a few minutes)
    python    : /data/horse/ws/<zih-login>-llm-lab/venv-llm/bin/python (Python 3.12.3)
    downloaded tiny Shakespeare to /data/horse/ws/<zih-login>-llm-lab/data/input.txt
    environment ready

    Use source, not bash. Only then does the environment stay switched on in your shell. From now on the same line takes a second, and the job scripts run it themselves.

  4. Check that it works

    Send the first job of the lab. Its work runs on the CPU and takes a few seconds once it starts. It still asks for a GPU, because Alpha takes nothing else.

    on the login nodeshell
    cd ~/behind-llms-lab/hpc_lab/01_hello
    mkdir -p logs && sbatch hello.sbatch
    squeue --me                                 # PD means waiting, R means running
    cat logs/hello-hpc-*.out                    # when the job is gone from the queue

    If the log shows the name of a compute node and an estimate of pi, you are ready for the HPC lab.

The setup script#

00_setup/setup_env.sh does five things, and it is safe to run as often as you like:

  1. It creates the workspace llm-lab, or reuses it. This is the big, fast storage for the environment, the data and the models.
  2. It loads a Python module of the cluster.
  3. It creates a virtual environment with PyTorch and transformers inside the workspace, or switches it on if it is already there.
  4. It points the pip, Hugging Face and temporary caches into the workspace, away from the small home folder.
  5. It downloads the tiny Shakespeare text that exercise 4 trains on.

It also sets $WS_DIR, the path of your workspace. The job scripts use it for data and results.

setup_env.sh87 lines
hpc_lab/00_setup/setup_env.shshellGitHub
#!/bin/bash
# Behind the Secrets of LLMs, HPC lab
# 00_setup/setup_env.sh: sets up your Python environment once, and switches it on later.
#
# The first time it installs packages, which takes 5 to 10 minutes. Do that inside
# a small interactive job, not on the login node:
#
#     srun --account=p_lv_llmsecrets --ntasks=1 --cpus-per-task=4 --mem-per-cpu=2G \
#          --time=00:45:00 --pty bash -l
#     source 00_setup/setup_env.sh
#
# After that, in every new shell and in every batch job, it only takes a second:
#
#     source 00_setup/setup_env.sh
#
# Use "source", not "bash", so that the venv stays active in your shell.
#
# What it does:
#   1. creates (or reuses) a workspace, which is big and fast storage for venv, data and models
#   2. loads a Python module
#   3. creates (or reuses) a venv with PyTorch and transformers inside the workspace
#   4. points the pip, Hugging Face and tmp caches into the workspace instead of $HOME
#   5. downloads the tiny Shakespeare text for exercise 4

# ---- settings you may change ------------------------------------------------
WS_NAME="${WS_NAME:-llm-lab}"     # workspace name
WS_FS="${WS_FS:-horse}"           # file system, horse works on all clusters
WS_DAYS="${WS_DAYS:-30}"          # lifetime in days, extend it later with ws_extend
VENV_NAME="${VENV_NAME:-venv-llm}"

# Python module. If it does not exist on your cluster, look with: module spider Python
PY_MODULES="release/24.10 GCCcore/13.3.0 Python/3.12.3"

# PyTorch built for CUDA 12.x runs with the drivers on Alpha (A100) and Capella (H100).
# A plain "pip install torch" may pick a CUDA 13 build that needs a newer driver.
# If nvidia-smi shows CUDA 13.0 or newer you can use .../whl/cu130 instead.
TORCH_INDEX="https://download.pytorch.org/whl/cu128"
# -----------------------------------------------------------------------------

# 1) workspace. If this shell (or the shell that submitted the job) already has it, reuse it.
if [ -z "$WS_DIR" ] || [ ! -d "$WS_DIR" ]; then
    # ws_allocate prints the path. For a workspace that already exists it just reuses it.
    WS_DIR="$(ws_allocate -F "$WS_FS" "$WS_NAME" "$WS_DAYS" 2>/dev/null)"
fi
if [ -z "$WS_DIR" ] || [ ! -d "$WS_DIR" ]; then
    echo "Could not create the workspace '$WS_NAME' on '$WS_FS'. Check with: ws_list -l" >&2
    return 1 2>/dev/null || exit 1
fi
export WS_DIR
echo "workspace : $WS_DIR"

# 2) modules
module purge > /dev/null 2>&1
if ! module load $PY_MODULES; then
    echo "'module load $PY_MODULES' did not work. Run 'module spider Python' and change PY_MODULES." >&2
    return 1 2>/dev/null || exit 1
fi

# 3) caches go into the workspace, because the $HOME quota is small
export PIP_CACHE_DIR="$WS_DIR/.cache/pip"
export HF_HOME="$WS_DIR/.cache/huggingface"
export TMPDIR="$WS_DIR/tmp"
mkdir -p "$PIP_CACHE_DIR" "$HF_HOME" "$TMPDIR" "$WS_DIR/data" "$WS_DIR/runs"

# 4) venv
if [ ! -f "$WS_DIR/$VENV_NAME/bin/activate" ]; then
    echo "creating the venv $WS_DIR/$VENV_NAME (only the first time, this takes a few minutes)"
    python -m venv "$WS_DIR/$VENV_NAME"
    source "$WS_DIR/$VENV_NAME/bin/activate"
    pip install --quiet --upgrade pip
    pip install --quiet torch --index-url "$TORCH_INDEX"
    pip install --quiet numpy matplotlib tqdm transformers accelerate ipykernel
    # the same environment as a kernel in JupyterHub
    python -m ipykernel install --user --name "$VENV_NAME" --display-name "LLM Lab ($VENV_NAME)"
else
    source "$WS_DIR/$VENV_NAME/bin/activate"
fi
echo "python    : $(which python) ($(python --version 2>&1))"

# 5) the training text for exercise 4
if [ ! -s "$WS_DIR/data/input.txt" ]; then
    curl -sSL -o "$WS_DIR/data/input.txt" \
        https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt \
        && echo "downloaded tiny Shakespeare to $WS_DIR/data/input.txt"
fi

echo "environment ready"

Settings you may change

SettingDefaultWhat it is
WS_NAMEllm-labThe name of the workspace.
WS_FShorseThe file system. horse works on all clusters.
WS_DAYS30The lifetime in days. Extend it later with ws_extend.
VENV_NAMEvenv-llmThe name of the virtual environment.
PY_MODULESrelease/24.10 GCCcore/13.3.0 Python/3.12.3The Python module. Module versions change about twice a year. If loading fails, run module spider Python and put the new names here.
TORCH_INDEXhttps://download.pytorch.org/whl/cu128Which PyTorch build to install. This one fits the GPU drivers on Alpha and Capella.

Prefer conda?#

The venv above is what the lab uses and what ZIH recommends. If you are used to conda, the folder 00_setup/conda builds a similar environment with conda instead: PyTorch, transformers, pandas and evaluate, in a workspace of its own called llm-conda. It does not touch the lab venv, and the exercises keep using the venv.

Anaconda has a licence. Anaconda's terms now cover universities of TU Dresden's size, and TU Dresden has no central licence. So the script installs only from conda-forge. If in doubt, use the venv.

on an Alpha login nodeshell
cd ~/behind-llms-lab/hpc_lab/00_setup/conda
mkdir -p logs && sbatch create_env.sbatch   # once, about 10 to 15 minutes
cat logs/create-env-*.out                   # the last line should say  OK | CUDA: True
sbatch job.sbatch                           # runs check_env.py in the environment
cat logs/conda-job-*.out                    # ends with  ALL GOOD

Three things work differently from conda on your laptop:

  • No conda init. ZIH asks you not to use it. Load the module and source conda.sh instead, in every shell and in every job, as job.sbatch does.
  • The environment lives in a workspace, given with --prefix, not in ~/.conda. Workspaces expire: check with ws_list and extend with ws_extend -F horse llm-conda 30.
  • pip needs PIP_REQUIRE_VIRTUALENV=false inside a conda environment, because pip on ZIH systems refuses to run outside a venv.
add a package to the conda environmentshell
srun --account=p_lv_llmsecrets --nodes=1 --ntasks=1 --cpus-per-task=4 --mem-per-cpu=2G --gres=gpu:1 --time=00:30:00 --pty bash -l
module load Anaconda3 && source $EBROOTANACONDA3/etc/profile.d/conda.sh
conda activate $(ws_find -F horse llm-conda)/env
PIP_REQUIRE_VIRTUALENV=false pip install --no-cache-dir datasets     # or whatever you need
exit
create_env.sbatch46 lines
hpc_lab/00_setup/conda/create_env.sbatchslurm job scriptGitHub
#!/bin/bash
# The conda route, an alternative to setup_env.sh for people who prefer conda.
# Builds a conda environment with PyTorch, transformers, pandas and evaluate in its
# own workspace. Run it once, from this folder, on an Alpha login node:
#
#   cd ~/behind-llms-lab/hpc_lab/00_setup/conda
#   mkdir -p logs && sbatch create_env.sbatch
#   cat logs/create-env-<jobid>.out          # the last line should say OK
#
# Running it again builds the environment from scratch.
#
# ZIH recommends the venv from setup_env.sh. Anaconda's licence now covers universities
# of TU Dresden's size and TU Dresden has no central licence, so this script installs
# only from conda-forge. If in doubt, use the venv.
#
#SBATCH --job-name=create-env
#SBATCH --account=p_lv_llmsecrets
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=6
#SBATCH --gres=gpu:1                  # Alpha takes only GPU jobs, and the last line tests CUDA
#SBATCH --mem=32G
#SBATCH --time=01:00:00
#SBATCH --output=logs/%x-%j.out
#SBATCH --error=logs/%x-%j.err

WS_NAME=llm-conda

module purge
module load Anaconda3                                 # if this fails: module spider Anaconda3
source $EBROOTANACONDA3/etc/profile.d/conda.sh        # ZIH asks for this instead of "conda init"

# The environment lives in its own workspace, not in the small $HOME
ws_allocate -F horse $WS_NAME 30 > /dev/null
ENV=$(ws_find -F horse $WS_NAME)/env

conda create -y --prefix $ENV --override-channels -c conda-forge python=3.11 pip
conda activate $ENV

# pip on ZIH systems refuses to run outside a venv, and a conda env does not count as one
export PIP_REQUIRE_VIRTUALENV=false
# PyTorch for CUDA 12.x fits the drivers on Alpha and Capella (see setup_env.sh)
pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cu128
pip install --no-cache-dir transformers pandas evaluate

python -c "import torch, transformers, pandas, evaluate; print('OK | CUDA:', torch.cuda.is_available())"
job.sbatch30 lines
hpc_lab/00_setup/conda/job.sbatchslurm job scriptGitHub
#!/bin/bash
# The conda route: a job that runs a Python script inside the conda environment.
# Run create_env.sbatch once first. Then, from this folder:
#
#   mkdir -p logs && sbatch job.sbatch
#   cat logs/conda-job-<jobid>.out           # ends with ALL GOOD
#
# For your own code, copy this file and change the last line.
#
#SBATCH --job-name=conda-job
#SBATCH --account=p_lv_llmsecrets
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=6
#SBATCH --gres=gpu:1
#SBATCH --mem=32G
#SBATCH --time=00:30:00
#SBATCH --output=logs/%x-%j.out
#SBATCH --error=logs/%x-%j.err

WS_NAME=llm-conda

module purge
module load Anaconda3
source $EBROOTANACONDA3/etc/profile.d/conda.sh
conda activate $(ws_find -F horse $WS_NAME)/env
export HF_HOME=$(ws_find -F horse $WS_NAME)/hf-cache   # downloaded models go to the workspace, not $HOME

export SRUN_CPUS_PER_TASK=$SLURM_CPUS_PER_TASK
srun python -u check_env.py
check_env.py57 lines
hpc_lab/00_setup/conda/check_env.pypythonGitHub
"""The conda route: check that the environment works on a GPU node.

Run it with job.sbatch. It tests the GPU, transformers, pandas and evaluate,
and ends with ALL GOOD. The first run downloads a small model (about 250 MB).
"""
import time

import evaluate
import pandas as pd
import torch
import transformers
from transformers import pipeline

# 1. Versions
print(f"torch {torch.__version__} | transformers {transformers.__version__} | "
      f"pandas {pd.__version__} | evaluate {evaluate.__version__}")

# 2. GPU
assert torch.cuda.is_available(), "No GPU visible. Is '#SBATCH --gres=gpu:1' in the job script?"
print("GPU:", torch.cuda.get_device_name(0))
x = torch.randn(4096, 4096, device="cuda")
x @ x  # warm-up
torch.cuda.synchronize()
t = time.time()
x @ x
torch.cuda.synchronize()
print(f"4096x4096 matmul: {(time.time() - t) * 1000:.1f} ms")

# 3. transformers: a small sentiment model on the GPU
clf = pipeline(
    "sentiment-analysis",
    model="distilbert/distilbert-base-uncased-finetuned-sst-2-english",
    device=0,
)
texts = [
    "The cluster finally runs my jobs.",
    "Waiting in the queue all day is awful.",
    "This environment setup was painless.",
    "The error messages were confusing.",
]
labels = [1, 0, 1, 0]  # 1 = positive, 0 = negative
preds = clf(texts)

# 4. pandas
df = pd.DataFrame({
    "text": texts,
    "pred": [p["label"] for p in preds],
    "score": [round(p["score"], 3) for p in preds],
})
print(df.to_string(index=False))

# 5. evaluate
accuracy = evaluate.load("accuracy")
pred_ids = [1 if p["label"] == "POSITIVE" else 0 for p in preds]
print("accuracy:", accuracy.compute(predictions=pred_ids, references=labels)["accuracy"])

print("ALL GOOD")

Where to run the notebooks#

the easy way

Google Colab

Nothing to install. Use the buttons on the Notebooks page. For notebooks 5 and 6, choose a T4 GPU under Runtime > Change runtime type.

on the cluster

JupyterHub

Open jupyterhub.hpc.tu-dresden.de from the university network, start a server, and pick the kernel LLM Lab (venv-llm). The setup script installed it for you.

offline

Your laptop

Clone the repository, then pip install torch transformers matplotlib jupyter in a virtual environment. The notebooks pick small settings when there is no GPU.

Your own experiments#

The lab kit is meant to be taken apart. Once the seven exercises work, the same pieces run your own code.

  1. Code goes into your home folder, everything big goes into the workspace. After source 00_setup/setup_env.sh the workspace is $WS_DIR. The lab uses $WS_DIR/data for data and $WS_DIR/runs for results.
  2. Need another package? Start an interactive job as in step 3, switch the environment on and install it. It lands in the environment in your workspace and every later job has it.
  3. Write a job script. Copy the one from the exercise that is closest to what you want and change the last line, or use the generator below.
  4. Send it and watch it. sbatch, squeue --me, tail -f logs/…. The cheat sheet has the rest.
add a package to the lab environmentshell
srun --account=p_lv_llmsecrets --nodes=1 --ntasks=1 --cpus-per-task=4 --mem-per-cpu=2G --gres=gpu:1 --time=00:30:00 --pty bash -l
source ~/behind-llms-lab/hpc_lab/00_setup/setup_env.sh
pip install datasets                        # or whatever you need
exit

This job script generator needs JavaScript. Without it, copy hpc_lab/04_tinygpt/train.sbatch and change the last line.

Habits worth copying from the lab scripts

  • Create logs/ before you submit. If the folder for the log file is missing, the job ends at once and leaves no trace.
  • Print with flush=True or run python -u. Otherwise the log stays empty until the buffer is full.
  • Save a checkpoint when Slurm says stop. Slurm sends SIGTERM when you cancel a job or when the time is over. train_tinygpt.py catches it and saves, and --signal=TERM@60 makes the signal come 60 seconds early.
  • Ask for what you need, not more. A job that asks for less time and fewer GPUs starts sooner.
  • Download models on the login node first. Then set HF_HUB_OFFLINE=1 in the job, as exercise 6 does, so the job uses the copy in your workspace.
  • Sweeps are job arrays. One script, one line --array=0-3, and $SLURM_ARRAY_TASK_ID picks the setting. See exercise 5.

A separate workspace for another project

The setup script takes its settings from the environment. To keep a project apart from the lab, give it its own workspace. Jobs you send from the same shell use it too.

on the login node, inside an interactive job the first timeshell
unset WS_DIR                                # forget the lab workspace in this shell
export WS_NAME=my-project
source ~/behind-llms-lab/hpc_lab/00_setup/setup_env.sh

The course project is for the course. Jobs with --account=p_lv_llmsecrets are booked on the course. For your thesis or your own research, ask your supervisor which HPC project to use and put its name there.