|
| 1 | +#!/bin/bash |
| 2 | +# SingleController + NeMo-Gym e2e smoke. Mirrors grpo_async_gym.sh but |
| 3 | +# routes everything through the SC path (TransferQueue data plane + |
| 4 | +# SingleControllerActor) instead of async_grpo_train. |
| 5 | + |
| 6 | +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) |
| 7 | +PROJECT_ROOT=$(realpath $SCRIPT_DIR/../..) |
| 8 | +# Mark the current repo as safe, since wandb fetches metadata about the repo |
| 9 | +git config --global --add safe.directory $PROJECT_ROOT |
| 10 | + |
| 11 | +set -eou pipefail |
| 12 | + |
| 13 | +EXP_NAME=$(basename $0 .sh) |
| 14 | +EXP_DIR=$SCRIPT_DIR/$EXP_NAME |
| 15 | +LOG_DIR=$EXP_DIR/logs |
| 16 | +JSON_METRICS=$EXP_DIR/metrics.json |
| 17 | +RUN_LOG=$EXP_DIR/run.log |
| 18 | +CHECKPOINT_DIR=$EXP_DIR/checkpoints |
| 19 | +DATA_DIR=$EXP_DIR/data |
| 20 | +export PYTHONPATH=${PROJECT_ROOT}:${PYTHONPATH:-} |
| 21 | + |
| 22 | +rm -rf $EXP_DIR $LOG_DIR |
| 23 | +mkdir -p $EXP_DIR $LOG_DIR $CHECKPOINT_DIR $DATA_DIR |
| 24 | + |
| 25 | +# clean up checkpoint directory on exit |
| 26 | +trap "rm -rf $CHECKPOINT_DIR" EXIT |
| 27 | + |
| 28 | +cd $PROJECT_ROOT |
| 29 | + |
| 30 | +# Follow nemo-gym instructions here to get this data: |
| 31 | +# https://docs.nvidia.com/nemo/gym/0.1.0/tutorials/nemo-rl-grpo/setup.html#training-nemo-rl-grpo-setup |
| 32 | +cd 3rdparty/Gym-workspace/Gym |
| 33 | + |
| 34 | +# We need HF_TOKEN to download the data from huggingface |
| 35 | +if [[ ! -f env.yaml ]]; then |
| 36 | + if [[ -z "${HF_TOKEN:-}" ]]; then |
| 37 | + echo "[ERROR] HF_TOKEN is not set" |
| 38 | + exit 1 |
| 39 | + fi |
| 40 | + echo "hf_token: $HF_TOKEN" >> env.yaml |
| 41 | +fi |
| 42 | + |
| 43 | +uv run ng_prepare_data "+config_paths=[resources_servers/workplace_assistant/configs/workplace_assistant.yaml]" \ |
| 44 | + +output_dirpath=data/workplace_assistant \ |
| 45 | + +mode=train_preparation \ |
| 46 | + +should_download=true \ |
| 47 | + +data_source=huggingface |
| 48 | +cd - |
| 49 | + |
| 50 | +# This trimming of the workplace assistant dataset is necessary b/c with all the tools the first prompt is >4000 tokens |
| 51 | +# which will cause vllm to return nothing on the first prompt and crash RL. Since we want to keep this test short to |
| 52 | +# smoke test, we trim all but the first tool |
| 53 | +TRAIN_PATH=$DATA_DIR/workplace_assistant_train.jsonl |
| 54 | +VALIDATION_PATH=$DATA_DIR/workplace_assistant_validation.jsonl |
| 55 | +jq -c '.responses_create_params.tools |= (.[0:1])' 3rdparty/Gym-workspace/Gym/data/workplace_assistant/train.jsonl > $TRAIN_PATH |
| 56 | +jq -c '.responses_create_params.tools |= (.[0:1])' 3rdparty/Gym-workspace/Gym/data/workplace_assistant/validation.jsonl > $VALIDATION_PATH |
| 57 | + |
| 58 | +uv run coverage run -a --data-file=$PROJECT_ROOT/tests/.coverage --source=$PROJECT_ROOT/nemo_rl \ |
| 59 | + $PROJECT_ROOT/examples/run_grpo_single_controller.py \ |
| 60 | + --config $PROJECT_ROOT/examples/nemo_gym/grpo_qwen3_30ba3b_instruct.yaml \ |
| 61 | + policy.model_name=Qwen/Qwen3-0.6B \ |
| 62 | + policy.dtensor_cfg.enabled=false \ |
| 63 | + policy.megatron_cfg.enabled=true \ |
| 64 | + policy.megatron_cfg.tensor_model_parallel_size=1 \ |
| 65 | + policy.megatron_cfg.pipeline_model_parallel_size=1 \ |
| 66 | + policy.megatron_cfg.expert_model_parallel_size=1 \ |
| 67 | + policy.megatron_cfg.context_parallel_size=1 \ |
| 68 | + policy.megatron_cfg.sequence_parallel=false \ |
| 69 | + policy.generation.vllm_cfg.tensor_parallel_size=1 \ |
| 70 | + policy.generation.vllm_cfg.async_engine=true \ |
| 71 | + policy.max_total_sequence_length=512 \ |
| 72 | + policy.generation.colocated.enabled=false \ |
| 73 | + policy.generation.colocated.resources.num_nodes=1 \ |
| 74 | + policy.generation.colocated.resources.gpus_per_node=1 \ |
| 75 | + grpo.num_prompts_per_step=4 \ |
| 76 | + grpo.num_generations_per_prompt=2 \ |
| 77 | + grpo.max_num_steps=10 \ |
| 78 | + grpo.val_period=-1 \ |
| 79 | + policy.train_global_batch_size=8 \ |
| 80 | + policy.train_micro_batch_size=1 \ |
| 81 | + cluster.gpus_per_node=2 \ |
| 82 | + loss_fn.reference_policy_kl_penalty=0.01 \ |
| 83 | + loss_fn.use_importance_sampling_correction=true \ |
| 84 | + logger.tensorboard_enabled=true \ |
| 85 | + logger.log_dir=$LOG_DIR \ |
| 86 | + logger.wandb_enabled=false \ |
| 87 | + logger.monitor_gpus=true \ |
| 88 | + checkpointing.enabled=false \ |
| 89 | + data.train.data_path=$TRAIN_PATH \ |
| 90 | + data.validation.data_path=$VALIDATION_PATH \ |
| 91 | + ++data_plane.enabled=true \ |
| 92 | + ++data_plane.impl=transfer_queue \ |
| 93 | + ++data_plane.backend=simple \ |
| 94 | + ++data_plane.storage_capacity=1000000 \ |
| 95 | + ++data_plane.num_storage_units=2 \ |
| 96 | + ++data_plane.claim_meta_poll_interval_s=0.5 \ |
| 97 | + ++data_plane.global_segment_size=549755813888 \ |
| 98 | + ++data_plane.local_buffer_size=68719476736 \ |
| 99 | + ++async_rl.batch_selection_strategy=strict_on_policy \ |
| 100 | + ++async_rl.max_weight_staleness_versions=0 \ |
| 101 | + ++async_rl.min_prompt_groups_per_batch=4 \ |
| 102 | + ++async_rl.max_inflight_prompts=4 \ |
| 103 | + ++async_rl.max_buffered_rollouts=4 \ |
| 104 | + $@ \ |
| 105 | + 2>&1 | tee $RUN_LOG |
| 106 | + |
| 107 | +uv run tests/json_dump_tb_logs.py $LOG_DIR --output_path $JSON_METRICS |
| 108 | + |
| 109 | +# Observed to be between 0.8-1.3 |
| 110 | +uv run tests/check_metrics.py $JSON_METRICS \ |
| 111 | + 'median(data["train/gen_kl_error"]) < 1.3' \ |
| 112 | + 'max(data["train/reward"]) > 0' |
0 commit comments