diff --git a/examples/deepseekv3.1-671b-torchtitan/convert/config.py b/examples/deepseekv3.1-671b-torchtitan/convert/config.py new file mode 100644 index 0000000..8e5c8e3 --- /dev/null +++ b/examples/deepseekv3.1-671b-torchtitan/convert/config.py @@ -0,0 +1,49 @@ +from truss.base import truss_config +from truss_train import definitions + +BASE_IMAGE = "ghcr.io/pytorch/pytorch-nightly:71739c4-cu12.9.1" +PROJECT_NAME = "DeepSeek-V3.1-LoRA" + +ACCELERATOR = truss_config.Accelerator.H200 +NGPU = 8 +NODE_COUNT = 1 + +training_runtime = definitions.Runtime( + start_commands=[ + "chmod +x ./run.sh && ./run.sh", + ], + environment_variables={ + # "HF_TOKEN": definitions.SecretReference(name="hf_token"), + "HF_HUB_ENABLE_HF_TRANSFER": "true", + "WANDB_API_KEY": definitions.SecretReference(name="wandb_api_key"), + "NGPU": str(NGPU), + "NODE_COUNT": str(NODE_COUNT), + "LOG_RANK": "0", + "HF_TOKEN": definitions.SecretReference(name="hf_access_token"), + "GITHUB_TOKEN": definitions.SecretReference(name="github_token"), + }, + cache_config=definitions.CacheConfig( + enabled=True, + require_cache_affinity=True, + ), + checkpointing_config=definitions.CheckpointingConfig( + enabled=True, + ) +) + +training_compute = definitions.Compute( + accelerator=truss_config.AcceleratorSpec( + accelerator=ACCELERATOR, + count=NGPU, + ), + node_count=NODE_COUNT, +) + +training_job = definitions.TrainingJob( + image=definitions.Image(base_image=BASE_IMAGE), + compute=training_compute, + runtime=training_runtime, + name="ConvertDistributedCheckpointsIntoHF" +) + +training_project = definitions.TrainingProject(name=PROJECT_NAME, job=training_job) \ No newline at end of file diff --git a/examples/deepseekv3.1-671b-torchtitan/convert/run.sh b/examples/deepseekv3.1-671b-torchtitan/convert/run.sh new file mode 100755 index 0000000..ca3a7f1 --- /dev/null +++ b/examples/deepseekv3.1-671b-torchtitan/convert/run.sh @@ -0,0 +1,168 @@ +#!/bin/bash +set -euo pipefail + +# ============================================================================= +# DeepSeek V3 Checkpoint Conversion Script +# ============================================================================= +# Converts DCP checkpoints to HuggingFace format and copies to BT_CHECKPOINT_DIR +# +# This script should be run on the leader node after training completes. +# It was extracted from run2.sh to allow manual execution when the conversion +# doesn't complete due to other nodes finishing first. +# +# Required environment variables: +# BT_TEAM_CACHE_DIR - Shared cache directory for model weights +# BT_CHECKPOINT_DIR - Directory to copy converted checkpoints to +# +# Usage: +# ./convert.sh +# ============================================================================= + +# ============================================================================= +# Setup torchtitan environment +# ============================================================================= +apt update +apt install -y curl git git-lfs + +# Configure git credentials if GITHUB_TOKEN is available +if [ -n "${GITHUB_TOKEN:-}" ]; then + echo "Configuring git credentials..." + git config --global credential.helper store + echo "https://aghilann:${GITHUB_TOKEN}@github.com" > ~/.git-credentials + chmod 600 ~/.git-credentials + git config --global url."https://aghilann:${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" +fi + +curl -LsSf https://astral.sh/uv/install.sh | sh +source $HOME/.local/bin/env + +mkdir -p /workspace/aghilan-workspace +cd /workspace/aghilan-workspace + +if [[ ! -d "torchtitan" ]]; then + echo "Cloning torchtitan..." + git clone https://github.com/aghilann/torchtitan +fi + +cd torchtitan +git checkout lora-stuff + +if [[ ! -d ".venv" ]]; then + echo "Creating virtual environment..." + uv venv +fi + +echo "Activating virtual environment..." +source .venv/bin/activate +uv sync +uv pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/cu128 +uv pip install wandb safetensors + +# ============================================================================= +# Configuration +# ============================================================================= + +# Required environment variables +BT_TEAM_CACHE_DIR="${BT_TEAM_CACHE_DIR:?ERROR: BT_TEAM_CACHE_DIR must be set}" +BT_CHECKPOINT_DIR="${BT_CHECKPOINT_DIR:?ERROR: BT_CHECKPOINT_DIR must be set}" + +# Paths +HF_ASSETS_PATH="${HF_ASSETS_PATH:-${BT_TEAM_CACHE_DIR}/DeepSeek-V3.1-Base}" +CHECKPOINT_DIR="${CHECKPOINT_DIR:-${BT_TEAM_CACHE_DIR}/hf_checkpoints/dsv3-671b-lora}" + +echo "==============================================" +echo "DeepSeek V3 Checkpoint Conversion" +echo "==============================================" +echo "" +echo "Configuration:" +echo " BT_TEAM_CACHE_DIR: ${BT_TEAM_CACHE_DIR}" +echo " BT_CHECKPOINT_DIR: ${BT_CHECKPOINT_DIR}" +echo " HF_ASSETS_PATH: ${HF_ASSETS_PATH}" +echo " CHECKPOINT_DIR: ${CHECKPOINT_DIR}" +echo "==============================================" +echo "" + +# ============================================================================= +# Convert Checkpoints to HuggingFace Format +# ============================================================================= +echo "==============================================" +echo "Converting Checkpoints to HuggingFace Format" +echo "==============================================" + +CHECKPOINT_SUBDIR="${CHECKPOINT_DIR}/checkpoint" +OUTPUT_BASE="${BT_TEAM_CACHE_DIR}/converted_hf_checkpoint" + +if [[ -d "${CHECKPOINT_SUBDIR}" ]]; then + echo "Found checkpoint directories:" + for dir in "${CHECKPOINT_SUBDIR}"/*/; do + if [[ -d "$dir" ]]; then + dir_name=$(basename "$dir") + echo " - $dir_name" + fi + done + + echo "" + echo "Starting conversions..." + echo "" + + for dir in "${CHECKPOINT_SUBDIR}"/*/; do + if [[ -d "$dir" ]]; then + dir_name=$(basename "$dir") + input_path="${CHECKPOINT_SUBDIR}/${dir_name}" + output_path="${OUTPUT_BASE}_${dir_name}" + + echo "==========================================" + echo "Converting: $dir_name" + echo " Input: $input_path" + echo " Output: $output_path" + echo "==========================================" + + python scripts/checkpoint_conversion/convert_to_hf.py \ + "$input_path" \ + "$output_path" \ + --model_name deepseek_v3 \ + --model_flavor 671B_lora \ + --hf_assets_path "$HF_ASSETS_PATH" \ + --export_dtype bfloat16 \ + --adapters-only \ + --base_model_name_or_path "deepseek-ai/DeepSeek-V3" + + if [ $? -eq 0 ]; then + echo "✓ Successfully converted $dir_name" + + # Copy converted checkpoint to BT_CHECKPOINT_DIR + dest_path="${BT_CHECKPOINT_DIR}/${dir_name}" + echo "Copying converted checkpoint to: $dest_path" + mkdir -p "${BT_CHECKPOINT_DIR}" + cp -r "$output_path" "$dest_path" + if [ $? -eq 0 ]; then + echo "✓ Successfully copied to $dest_path" + else + echo "✗ Failed to copy to $dest_path" + fi + else + echo "✗ Failed to convert $dir_name" + fi + + fi + done + + echo "==============================================" + echo "All checkpoint conversions complete!" + echo "==============================================" + echo "" + echo "Converted checkpoints available at:" + ls -d "${OUTPUT_BASE}"_* 2>/dev/null || echo "(none found)" + echo "" + echo "Checkpoints copied to BT_CHECKPOINT_DIR:" + ls -la "${BT_CHECKPOINT_DIR}" 2>/dev/null || echo "(none found)" +else + echo "WARNING: No checkpoint subdirectory found at ${CHECKPOINT_SUBDIR}" + echo "Skipping conversion." + exit 1 +fi + +echo "" +echo "==============================================" +echo "Conversion complete!" +echo "==============================================" diff --git a/examples/deepseekv3.1-671b-torchtitan/load/config.py b/examples/deepseekv3.1-671b-torchtitan/load/config.py new file mode 100644 index 0000000..1766921 --- /dev/null +++ b/examples/deepseekv3.1-671b-torchtitan/load/config.py @@ -0,0 +1,40 @@ +from truss.base import truss_config +from truss_train import definitions + +BASE_IMAGE = "ghcr.io/pytorch/pytorch-nightly:71739c4-cu12.9.1" +PROJECT_NAME = "DeepSeek-V3.1-LoRA" + +ACCELERATOR = truss_config.Accelerator.H200 +NGPU = 1 +NODE_COUNT = 1 + +training_runtime = definitions.Runtime( + start_commands=[ + "chmod +x ./run.sh && ./run.sh", + ], + environment_variables={ + "HF_HUB_ENABLE_HF_TRANSFER": "true", + "HF_TOKEN": definitions.SecretReference(name="hf_access_token"), + }, + cache_config=definitions.CacheConfig( + enabled=True, + require_cache_affinity=True, + ), +) + +training_compute = definitions.Compute( + accelerator=truss_config.AcceleratorSpec( + accelerator=ACCELERATOR, + count=NGPU, + ), + node_count=NODE_COUNT, +) + +training_job = definitions.TrainingJob( + image=definitions.Image(base_image=BASE_IMAGE), + compute=training_compute, + runtime=training_runtime, + name="LoadDataIntoVolume" +) + +training_project = definitions.TrainingProject(name=PROJECT_NAME, job=training_job) diff --git a/examples/deepseekv3.1-671b-torchtitan/load/run.sh b/examples/deepseekv3.1-671b-torchtitan/load/run.sh new file mode 100644 index 0000000..a350e46 --- /dev/null +++ b/examples/deepseekv3.1-671b-torchtitan/load/run.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -euo pipefail + +# ============================================================================= +# DeepSeek V3.1 Model Loader +# ============================================================================= +# Downloads DeepSeek-V3.1-Base model to BT_TEAM_CACHE_DIR +# ============================================================================= + +# Install dependencies +pip install huggingface_hub hf_transfer + +# Clone torchtitan for the download script +git clone https://github.com/aghilann/torchtitan +cd torchtitan + +# Download the model assets +python scripts/download_hf_assets.py \ + --repo_id deepseek-ai/DeepSeek-V3.1-Base \ + --assets safetensors config tokenizer \ + --local_dir $BT_TEAM_CACHE_DIR + +echo "" +echo "==============================================" +echo "Model downloaded to: $BT_TEAM_CACHE_DIR" +echo "==============================================" +echo "" +echo "Contents:" +ls -lah $BT_TEAM_CACHE_DIR diff --git a/examples/deepseekv3.1-671b-torchtitan/train/config.py b/examples/deepseekv3.1-671b-torchtitan/train/config.py new file mode 100644 index 0000000..decbefd --- /dev/null +++ b/examples/deepseekv3.1-671b-torchtitan/train/config.py @@ -0,0 +1,49 @@ +from truss.base import truss_config +from truss_train import definitions + +BASE_IMAGE = "ghcr.io/pytorch/pytorch-nightly:71739c4-cu12.9.1" +PROJECT_NAME = "DeepSeek-V3.1-LoRA" + + +ACCELERATOR = truss_config.Accelerator.H200 +NGPU = 8 +NODE_COUNT = 4 + +training_runtime = definitions.Runtime( + start_commands=[ + "chmod +x ./run.sh && ./run.sh", + ], + environment_variables={ + "HF_HUB_ENABLE_HF_TRANSFER": "true", + "WANDB_API_KEY": definitions.SecretReference(name="wandb_api_key"), + "NGPU": str(NGPU), + "NODE_COUNT": str(NODE_COUNT), + "LOG_RANK": "0", + "HF_TOKEN": definitions.SecretReference(name="hf_access_token"), + "GITHUB_TOKEN": definitions.SecretReference(name="github_token"), + }, + cache_config=definitions.CacheConfig( + enabled=True, + require_cache_affinity=True, + ), + checkpointing_config=definitions.CheckpointingConfig( + enabled=True, + ) +) + +training_compute = definitions.Compute( + accelerator=truss_config.AcceleratorSpec( + accelerator=ACCELERATOR, + count=NGPU, + ), + node_count=NODE_COUNT, +) + +training_job = definitions.TrainingJob( + image=definitions.Image(base_image=BASE_IMAGE), + compute=training_compute, + runtime=training_runtime, + name="Training" +) + +training_project = definitions.TrainingProject(name=PROJECT_NAME, job=training_job) \ No newline at end of file diff --git a/examples/deepseekv3.1-671b-torchtitan/train/run.sh b/examples/deepseekv3.1-671b-torchtitan/train/run.sh new file mode 100644 index 0000000..3cae939 --- /dev/null +++ b/examples/deepseekv3.1-671b-torchtitan/train/run.sh @@ -0,0 +1,177 @@ +#!/bin/bash +set -euo pipefail +# Misc utils +apt update +apt install -y curl fzf ripgrep git git-lfs tmux htop lsof gh neovim +apt-get update && apt-get install -y build-essential +apt-get install nvtop + +export TUNNEL_NAME="bt-${BT_TRAINING_JOB_ID}-${BT_NODE_RANK}" + +# Configure git credentials if GITHUB_TOKEN is available +if [ -n "${GITHUB_TOKEN:-}" ]; then + echo "Configuring git credentials..." + git config --global credential.helper store + echo "https://aghilann:${GITHUB_TOKEN}@github.com" > ~/.git-credentials + chmod 600 ~/.git-credentials + git config --global url."https://aghilann:${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" +fi + +curl -LsSf https://astral.sh/uv/install.sh | sh + +mkdir aghilan-workspace + +cd aghilan-workspace +echo "Cloning torchtitan" +git clone https://github.com/basetenlabs/kingkong.git +cd kingkong +uv venv +echo "Creating virtual environment" +. .venv/bin/activate +uv sync +uv pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/cu128 +uv pip install wandb safetensors +chmod +x run_train.sh + +# ============================================================================= +# DeepSeek V3 671B LoRA Fine-Tuning - Multi-Node Training Script +# ============================================================================= +# Designed for Baseten multi-node training infrastructure. +# +# Required Baseten Environment Variables: +# BT_GROUP_SIZE - Number of nodes in the training group +# BT_NUM_GPUS - Number of GPUs per node +# BT_NODE_RANK - This node's rank (0 to BT_GROUP_SIZE-1) +# BT_LEADER_ADDR - IP address of the leader node (rank 0) +# BT_TEAM_CACHE_DIR - Shared cache directory for model weights +# BT_TRAINING_PROJECT_NAME - WandB project name +# BT_TRAINING_JOB_NAME - WandB run/experiment name +# +# Usage: +# ./scripts/train_deepseek_v3_lora_multinode.sh +# ============================================================================= + +# rm -rf /root/.cache/team_artifacts/hf_checkpoints + +DEBUG="false" # Set to 'true' for debug (2-layer model), 'false' for production (671B) + +# Configuration - Uses Baseten environment variables +NNODES="${BT_GROUP_SIZE:?ERROR: BT_GROUP_SIZE must be set}" +NPROC_PER_NODE="${BT_NUM_GPUS:?ERROR: BT_NUM_GPUS must be set}" +NODE_RANK="${BT_NODE_RANK:?ERROR: BT_NODE_RANK must be set}" +MASTER_ADDR="${BT_LEADER_ADDR:?ERROR: BT_LEADER_ADDR must be set}" +MASTER_PORT="${MASTER_PORT:-29500}" + +export NGPU="${BT_NUM_GPUS:?ERROR: BT_NUM_GPUS must be set}" + +# Config, assets, and checkpoint paths based on DEBUG flag +if [[ "${DEBUG}" == "true" ]]; then + CONFIG_FILE="${CONFIG_FILE:-./torchtitan/models/deepseek_v3/train_configs/deepseek_aghilora.toml}" + HF_ASSETS_PATH="${HF_ASSETS_PATH:-${BT_TEAM_CACHE_DIR}/DeepSeek-v3.1-Base-DEBUG}" + CHECKPOINT_DIR="${CHECKPOINT_DIR:-${BT_TEAM_CACHE_DIR}/hf_checkpoints/dsv3-debug-lora}" +else + CONFIG_FILE="${CONFIG_FILE:-./torchtitan/models/deepseek_v3/train_configs/deepseek_v3_671b_lora.toml}" + HF_ASSETS_PATH="${HF_ASSETS_PATH:-${BT_TEAM_CACHE_DIR}/DeepSeek-V3.1-Base}" + CHECKPOINT_DIR="${CHECKPOINT_DIR:-${BT_TEAM_CACHE_DIR}/hf_checkpoints/dsv3-671b-lora}" +fi + +# WandB settings (from Baseten env) +WANDB_PROJECT="${BT_TRAINING_PROJECT_NAME:?ERROR: BT_TRAINING_PROJECT_NAME must be set}" +WANDB_RUN_NAME="${BT_TRAINING_JOB_NAME:?ERROR: BT_TRAINING_JOB_NAME must be set}" + +# Environment Setup +export PYTORCH_CUDA_ALLOC_CONF="expandable_segments:True" +export NCCL_DEBUG="${NCCL_DEBUG:-WARN}" +export NCCL_IB_DISABLE="${NCCL_IB_DISABLE:-0}" +export NCCL_NET_GDR_LEVEL="${NCCL_NET_GDR_LEVEL:-5}" +export NCCL_TIMEOUT=3600000 # 1 hour +export WANDB_PROJECT="${WANDB_PROJECT}" +export WANDB_RUN_NAME="${WANDB_RUN_NAME}" + +# Print Configuration +echo "==============================================" +echo "DeepSeek V3 LoRA Multi-Node Training" +echo "==============================================" +if [[ "${DEBUG}" == "true" ]]; then + echo "Mode: DEBUG (2-layer model)" +else + echo "Mode: PRODUCTION (671B)" +fi +echo "" +echo "Baseten Environment:" +echo " BT_GROUP_SIZE (nodes): ${NNODES}" +echo " BT_NUM_GPUS (per node): ${NPROC_PER_NODE}" +echo " BT_NODE_RANK: ${NODE_RANK}" +echo " BT_LEADER_ADDR: ${MASTER_ADDR}" +echo " MASTER_PORT: ${MASTER_PORT}" +echo "" +echo "Paths:" +echo " CONFIG_FILE: ${CONFIG_FILE}" +echo " HF_ASSETS_PATH: ${HF_ASSETS_PATH}" +echo " CHECKPOINT_DIR: ${CHECKPOINT_DIR}" +echo "" +echo "WandB:" +echo " BT_TRAINING_PROJECT_NAME: ${WANDB_PROJECT}" +echo " BT_TRAINING_JOB_NAME: ${WANDB_RUN_NAME}" +echo "==============================================" + +# Validate Environment +if [[ ! -f "${CONFIG_FILE}" ]]; then + echo "ERROR: Config file not found: ${CONFIG_FILE}" + exit 1 +fi + +if [[ ! -d "${HF_ASSETS_PATH}" ]]; then + echo "ERROR: HF assets not found: ${HF_ASSETS_PATH}" + echo "Please download with: python scripts/download_hf_assets.py --repo_id deepseek-ai/DeepSeek-V3.1-Base --local_dir \$BT_TEAM_CACHE_DIR --all" + exit 1 +fi + +if [[ "${NODE_RANK}" -lt 0 ]] || [[ "${NODE_RANK}" -ge "${NNODES}" ]]; then + echo "ERROR: NODE_RANK must be between 0 and $((NNODES - 1))" + exit 1 +fi + +# Launch Training +echo "" +echo "Starting training on node ${NODE_RANK}..." +echo "" + +echo "Checkpoint directory BEFORE training:" +ls -lah "${CHECKPOINT_DIR}" 2>/dev/null || echo "(directory does not exist yet)" +echo "" + +torchrun \ + --nnodes="${NNODES}" \ + --nproc_per_node="${NPROC_PER_NODE}" \ + --node_rank="${NODE_RANK}" \ + --rdzv_backend=c10d \ + --rdzv_endpoint="${MASTER_ADDR}:${MASTER_PORT}" \ + --local-ranks-filter 0 \ + --role rank \ + --tee 3 \ + -m torchtitan.train \ + --job.config_file "${CONFIG_FILE}" \ + --job.dump_folder "${CHECKPOINT_DIR}" \ + --model.hf_assets_path "${HF_ASSETS_PATH}" \ + --parallelism.expert_parallel_degree 16 \ + --parallelism.fsdp_reshard_after_forward always \ + --checkpoint.export_dtype bfloat16 \ + --activation_checkpoint.mode full \ + --training.local_batch_size 2 \ + --training.steps 1000 \ + --checkpoint.interval 500 \ + --optimizer.lr 1.5e-4 \ + --lr_scheduler.warmup_steps 0 \ + --lr_scheduler.min_lr_factor 1.0 + + + +echo "" +echo "Checkpoint directory AFTER training:" +ls -lah "${CHECKPOINT_DIR}" 2>/dev/null || echo "(directory does not exist)" +echo "" + +echo "==============================================" +echo "Training completed on node ${NODE_RANK}" +echo "==============================================" \ No newline at end of file