-
Notifications
You must be signed in to change notification settings - Fork 8
Qwen3-Coder-Next long-context LoRA fine-tuning example #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
b742ddc
a8192a4
ed733dc
be83e5c
30657e6
96d95be
ab80a18
a67c3b0
9e7f785
c37d8b3
d1368a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| ## Baseten Qwen 80b 2.5 Training | ||
|
|
||
| ### Prerequisites | ||
| https://github.com/basetenlabs/ml-cookbook/tree/main?tab=readme-ov-file#prerequisites | ||
|
|
||
| This example uses megatron training | ||
| - `run_megatron.sh` | ||
|
|
||
| Current files: | ||
| - `config.py`: Baseten training job config | ||
| - `run_megatron.sh`: Megatron SFT launcher. | ||
|
|
||
| ### Required Baseten secrets | ||
| - `hf_access_token` | ||
|
|
||
| ### Runtime knobs | ||
| Edit variables directly in the script you use: | ||
| - `run_megatron.sh`: | ||
| - `MODEL_ID`, `DATASET_ID`, `DATASET_SPLIT` | ||
| - `LORA_RANK`, `LORA_ALPHA` | ||
| - `TENSOR_PARALLEL_SIZE`, `PIPELINE_PARALLEL_SIZE`, `CONTEXT_PARALLEL_SIZE`, `EXPERT_PARALLEL_SIZE` | ||
| - `MICRO_BATCH_SIZE`, `GLOBAL_BATCH_SIZE`, `MAX_LENGTH` | ||
| - `LR_DECAY_STYLE`, `MIN_LR`, `SAVE_INTERVAL`, `LOG_INTERVAL`, `SAVE_FULL_MODEL` | ||
|
|
||
| ### Launch | ||
| `config.py` runs whichever script is in `training_runtime.start_commands`. | ||
|
|
||
| Run: | ||
| ```bash | ||
| truss train push config.py | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| from truss_train import definitions | ||
| from truss.base import truss_config | ||
|
|
||
| project_name = "qwen80b-instruct-megatron-lora" | ||
|
|
||
| # Prebuilt image with Megatron + ms-swift. | ||
| BASE_IMAGE = "baseten/megatron:py3.11.11-cuda12.8.1-torch2.8.0-fa2.8.1-megatron0.14.1-msswift3.10.3" | ||
|
|
||
| training_runtime = definitions.Runtime( | ||
| start_commands=[ | ||
| "/bin/bash -c 'chmod +x ./run_megatron.sh && ./run_megatron.sh'", | ||
| ], | ||
| environment_variables={ | ||
| "HF_TOKEN": definitions.SecretReference(name="hf_access_token"), | ||
| }, | ||
| cache_config=definitions.CacheConfig( | ||
| enabled=True, | ||
| enable_legacy_hf_mount=True, | ||
| ), | ||
| checkpointing_config=definitions.CheckpointingConfig(enabled=True), | ||
| ) | ||
|
|
||
| # Multi-node distributed job for large Qwen80B tuning. | ||
| training_compute = definitions.Compute( | ||
| node_count=1, | ||
| accelerator=truss_config.AcceleratorSpec( | ||
| accelerator=truss_config.Accelerator.H200, | ||
| count=8, | ||
| ), | ||
| ) | ||
|
||
|
|
||
| my_training_job = definitions.TrainingJob( | ||
| image=definitions.Image(base_image=BASE_IMAGE), | ||
| compute=training_compute, | ||
| runtime=training_runtime, | ||
| ) | ||
|
|
||
| first_project_with_job = definitions.TrainingProject(name=project_name, job=my_training_job) | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,111 @@ | ||||||||||||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Validate HF token | ||||||||||||||||||||||||||
| [[ -n "${HF_TOKEN:-}${HUGGING_FACE_HUB_TOKEN:-}${HUGGINGFACE_HUB_TOKEN:-}" ]] || { | ||||||||||||||||||||||||||
| echo "ERROR: HF token required for checkpoint upload"; exit 1 | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Environment | ||||||||||||||||||||||||||
| export HF_HOME="/tmp/huggingface" | ||||||||||||||||||||||||||
| export PYTORCH_CUDA_ALLOC_CONF="expandable_segments:True" | ||||||||||||||||||||||||||
| export TORCH_NCCL_ASYNC_ERROR_HANDLING="1" | ||||||||||||||||||||||||||
| export NCCL_SOCKET_IFNAME="^docker0,lo" | ||||||||||||||||||||||||||
| export MASTER_PORT="29500" | ||||||||||||||||||||||||||
| mkdir -p "$HF_HOME" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Install dependencies | ||||||||||||||||||||||||||
| pip install -q --upgrade pip | ||||||||||||||||||||||||||
| pip install -q "ms-swift[llm]==3.12.5" datasets huggingface_hub "transformers==4.57.1" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we want to install flash-linear-attention |
||||||||||||||||||||||||||
| # Checkpoint directory | ||||||||||||||||||||||||||
| checkpoint_dir="${BT_CHECKPOINT_DIR:-/mnt/ckpts}/qwen80b-instruct-megatron-lora" | ||||||||||||||||||||||||||
| mkdir -p "$checkpoint_dir" | ||||||||||||||||||||||||||
| printf '{}' > "$checkpoint_dir/args.json" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Workaround: sync args.json to timestamped subdirs created by ms-swift | ||||||||||||||||||||||||||
| (while true; do | ||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need this? |
||||||||||||||||||||||||||
| for d in "$checkpoint_dir"/v*-*; do | ||||||||||||||||||||||||||
| [[ -d "$d" && ! -f "$d/args.json" ]] && cp "$checkpoint_dir/args.json" "$d/args.json" 2>/dev/null || true | ||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||
| sleep 1 | ||||||||||||||||||||||||||
| done) & | ||||||||||||||||||||||||||
| trap "kill $! 2>/dev/null" EXIT | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
| trap "kill $! 2>/dev/null" EXIT | |
| args_sync_pid=$! | |
| trap 'kill "$args_sync_pid" 2>/dev/null' EXIT |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With set -u, referencing ${BT_GROUP_SIZE} / ${BT_NUM_GPUS} / ${BT_NODE_RANK} / ${BT_LEADER_ADDR} will immediately error if the script is run outside the Baseten runtime (or if any of these env vars are missing). Other example scripts in this repo use default fallbacks (e.g., ${BT_GROUP_SIZE:-1}, ${BT_NODE_RANK:-0}, ${BT_LEADER_ADDR:-localhost}) to avoid unbound-variable exits while still working on-platform.
| echo "Starting training: model=Qwen/Qwen3-Next-80B-A3B-Instruct nodes=${BT_GROUP_SIZE}x${BT_NUM_GPUS}gpu" | |
| train_exit=0 | |
| NPROC_PER_NODE="$BT_NUM_GPUS" \ | |
| NNODES="$BT_GROUP_SIZE" \ | |
| NODE_RANK="$BT_NODE_RANK" \ | |
| MASTER_ADDR="$BT_LEADER_ADDR" \ | |
| echo "Starting training: model=Qwen/Qwen3-Next-80B-A3B-Instruct nodes=${BT_GROUP_SIZE:-1}x${BT_NUM_GPUS:-1}gpu" | |
| train_exit=0 | |
| NPROC_PER_NODE="${BT_NUM_GPUS:-1}" \ | |
| NNODES="${BT_GROUP_SIZE:-1}" \ | |
| NODE_RANK="${BT_NODE_RANK:-0}" \ | |
| MASTER_ADDR="${BT_LEADER_ADDR:-localhost}" \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this the right template?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we set --recompute_num_layers 2 we can get FLA + 32k Seq Len
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we just have megatron write the checkpoint to our shared checkpoint dir, none of this is necessary
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| #!/usr/bin/env python3 | ||
| """Upload checkpoint to Hugging Face Hub.""" | ||
| import argparse | ||
| import datetime | ||
| from huggingface_hub import HfApi | ||
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser() | ||
| parser.add_argument("checkpoint_dir", help="Path to checkpoint directory") | ||
| parser.add_argument("repo_id", help="HF Hub repo ID (e.g., user/model-name)") | ||
| args = parser.parse_args() | ||
|
|
||
| api = HfApi() | ||
| api.create_repo(args.repo_id, repo_type="model", private=True, exist_ok=True) | ||
| api.upload_folder( | ||
| repo_id=args.repo_id, | ||
| folder_path=args.checkpoint_dir, | ||
| commit_message=f"Checkpoint {datetime.datetime.utcnow().isoformat()}Z", | ||
| ) | ||
|
||
| print(f"Uploaded to {args.repo_id}") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The README's title and "Runtime knobs" list appear copied from the MiniMax example and do not match this example: the script hard-codes the model/dataset/parallelism flags and does not define variables like
MODEL_ID,DATASET_SPLIT, etc. Update the README to reflect the actual knobs that can be edited (or refactorrun_megatron.shto use the documented variables).