Frontier-scale training
"> **Multi-node workspace required:** This is a multi-node example. To run it,\n> your Modal workspace must have multi-node enabled. Contact\n> [support@modal.com](mailto:support@modal.com) to enable multi-node.\n\n# Frontier-scale training\n\nWhen you decide you need a frontier-scale model for your workload, you want\nthe biggest hardware you can get, and runs that stay up. This tutorial trains\n[GLM-4.7](https://huggingface.co/zai-org/GLM-4.7) across 8 nodes with 8 H200s\neach (64 GPUs) using full-weight\n[Group Sequence Policy Optimization](https://arxiv.org/abs/2507.18071) (GSPO)\non\n[zhuzilin/dapo-math-17k](https://huggingface.co/datasets/zhuzilin/dapo-math-17k).\n\n```python\nfrom modal_training_gym import (\n Endpoint,\n GLM_4_7,\n HuggingFaceDataset,\n TrainConfig,\n)\nfrom modal_training_gym.train_recipes.slime_recipe import GLM_4_7_Recipe\n```\n\n## Set up\n\nModel and recipe classes carry the settings that make this run correct and\nfast. A few parameters are printed below so you can see the cluster shape.\n\n```python\nmodel = GLM_4_7()\n\n\nclass MathDataset(HuggingFaceDataset):\n hf_repo = \"zhuzilin/dapo-math-17k\"\n input_key = \"prompt\"\n label_key = \"label\"\n output_format = \"jsonl\"\n apply_chat_template = True\n always_prepare = True\n\n\ntrain_dataset = MathDataset(hf_split=\"train[:2000]\")\nrecipe = GLM_4_7_Recipe(\n eval_interval=None,\n rm_type=\"deepscaler\",\n)\n\nprint(f\"training and rollout gpus colocated: {recipe.colocate}\")\nprint(f\"training nodes: {recipe.actor_num_nodes}, gpus/node: {recipe.actor_num_gpus_per_node}\")\nprint(f\"rollout gpus: {recipe.rollout_num_gpus}, rollout gpus/engine: {recipe.rollout_num_gpus_per_engine}\")\nprint(f\"parallelism: tp={recipe.tensor_model_parallel_size}, pp={recipe.pipeline_model_parallel_size}, \"\n f\"cp={recipe.context_parallel_size}, ep={recipe.expert_model_parallel_size}\")\nprint(f\"optimizer cpu offload: {recipe.optimizer_cpu_offload}\")\n```\n\n## Kick off training\n\n```python\nconfig = TrainConfig(\n model=model,\n dataset=train_dataset,\n recipe=recipe,\n)\n\nrun = config.launch()\nprint(f\"run id: {run.training_run_id}\")\n```\n\n## Test out the trained model\n\nSpin up an [Endpoint](https://modal.com/docs/guide/endpoints) and try a prompt.\n\n```python\nresult = run.result()\ncheckpoint = result.checkpoints()[-1]\nprint(f\"checkpoint: {checkpoint.path}\")\n\ntrained_deployment = Endpoint.launch(\n model, checkpoint, unauthenticated=True, recreate_if_existing=True\n)\ntrained_deployment.wait_until_ready(timeout=45 * 60)\nprint(f\"checkpoint deployed to {trained_deployment.url}\")\n\nmsg = trained_deployment.chat(\n [\n {\n \"role\": \"user\",\n \"content\": (\n \"Let $p$ be a prime number. Find the number of integers $n$ \"\n \"with $1 \\\\le n \\\\le p^2$ such that $n^{p-1} \\\\equiv 1 \\\\pmod{p^2}$.\"\n ),\n }\n ],\n)\nprint(msg.get(\"content\") or msg.get(\"reasoning_content\") or \"\")\n```\n"
Multi-node workspace required: This is a multi-node example. To run it, your Modal workspace must have multi-node enabled. Contact support@modal.com to enable multi-node.
When you decide you need a frontier-scale model for your workload, you want the biggest hardware you can get, and runs that stay up. This tutorial trains GLM-4.7 across 8 nodes with 8 H200s each (64 GPUs) using full-weight Group Sequence Policy Optimization (GSPO) on zhuzilin/dapo-math-17k.
from modal_training_gym import ( Endpoint, GLM_4_7, HuggingFaceDataset, TrainConfig,)from modal_training_gym.train_recipes.slime_recipe import GLM_4_7_RecipeSet up
Model and recipe classes carry the settings that make this run correct and fast. A few parameters are printed below so you can see the cluster shape.
model = GLM_4_7()
class MathDataset(HuggingFaceDataset): hf_repo = "zhuzilin/dapo-math-17k" input_key = "prompt" label_key = "label" output_format = "jsonl" apply_chat_template = True always_prepare = True
train_dataset = MathDataset(hf_split="train[:2000]")recipe = GLM_4_7_Recipe( eval_interval=None, rm_type="deepscaler",)
print(f"training and rollout gpus colocated: {recipe.colocate}")print(f"training nodes: {recipe.actor_num_nodes}, gpus/node: {recipe.actor_num_gpus_per_node}")print(f"rollout gpus: {recipe.rollout_num_gpus}, rollout gpus/engine: {recipe.rollout_num_gpus_per_engine}")print(f"parallelism: tp={recipe.tensor_model_parallel_size}, pp={recipe.pipeline_model_parallel_size}, " f"cp={recipe.context_parallel_size}, ep={recipe.expert_model_parallel_size}")print(f"optimizer cpu offload: {recipe.optimizer_cpu_offload}")Kick off training
config = TrainConfig( model=model, dataset=train_dataset, recipe=recipe,)
run = config.launch()print(f"run id: {run.training_run_id}")Test out the trained model
Spin up an Endpoint and try a prompt.
result = run.result()checkpoint = result.checkpoints()[-1]print(f"checkpoint: {checkpoint.path}")
trained_deployment = Endpoint.launch( model, checkpoint, unauthenticated=True, recreate_if_existing=True)trained_deployment.wait_until_ready(timeout=45 * 60)print(f"checkpoint deployed to {trained_deployment.url}")
msg = trained_deployment.chat( [ { "role": "user", "content": ( "Let $p$ be a prime number. Find the number of integers $n$ " "with $1 \\le n \\le p^2$ such that $n^{p-1} \\equiv 1 \\pmod{p^2}$." ), } ],)print(msg.get("content") or msg.get("reasoning_content") or "")