Skip to content

Commit 52dcc08

Browse files
committed
Add on-policy GSM8K data loop
1 parent b3ffe40 commit 52dcc08

5 files changed

Lines changed: 453 additions & 0 deletions

File tree

bash/20260529_gsm8k_mc_sampled.sh

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Current implementation:
5+
# on-policy loop = live GSM8K candidate generation -> generated parquet -> VERL/GRPO train
6+
# -> roll out the saved HF actor checkpoint -> repeat.
7+
#
8+
# This is intentionally different from the older static sampled-parquet launcher:
9+
# bash/20260327_gsm8k_mc_sampled.sh
10+
11+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
12+
BUILDER_DIR="$REPO_ROOT/reliable-gsm8k-builder"
13+
14+
DATE_TAG=${DATE_TAG:-20260529}
15+
GPU_IDS=${GPU_IDS:-}
16+
17+
BASE_MODEL=${BASE_MODEL:-Qwen/Qwen2.5-3B-Instruct}
18+
GENERATOR_PROFILE=${GENERATOR_PROFILE:-qwen25_3b}
19+
INFERENCE_PROFILE=${INFERENCE_PROFILE:-sample_balanced}
20+
TRAIN_DATASET=${TRAIN_DATASET:-mc_onecorrect} # mc_onecorrect | mc_allwrong | oe
21+
22+
ITERATIONS=${ITERATIONS:-3}
23+
NUM_SAMPLES=${NUM_SAMPLES:-1000}
24+
SPLIT=${SPLIT:-train}
25+
VAL_FILE=${VAL_FILE:-$HOME/data/gsm8k/test.parquet}
26+
27+
PROJECT_NAME=${PROJECT_NAME:-multiple_choice_question_study}
28+
IMPLEMENTATION_TAG=${IMPLEMENTATION_TAG:-gsm8k_mc_sampled_onpolicy_livegen_parseronly_grpo}
29+
RUN_PREFIX=${RUN_PREFIX:-${DATE_TAG}_${IMPLEMENTATION_TAG}_${GENERATOR_PROFILE}_${TRAIN_DATASET}_n${NUM_SAMPLES}_it${ITERATIONS}}
30+
31+
OUTPUT_ROOT=${OUTPUT_ROOT:-$BUILDER_DIR/runs_on_policy}
32+
CHECKPOINT_ROOT=${CHECKPOINT_ROOT:-$REPO_ROOT/checkpoints/on_policy}
33+
LOG_DIR=${LOG_DIR:-$REPO_ROOT/logs}
34+
LOG_FILE=${LOG_FILE:-$LOG_DIR/${RUN_PREFIX}.log}
35+
36+
TOTAL_EPOCHS=${TOTAL_EPOCHS:-1}
37+
SAVE_FREQ=${SAVE_FREQ:-999999} # positive; VERL still saves on final step
38+
TEST_FREQ=${TEST_FREQ:-1}
39+
TRAIN_BATCH_SIZE=${TRAIN_BATCH_SIZE:-1024}
40+
PPO_MINI_BATCH_SIZE=${PPO_MINI_BATCH_SIZE:-1024}
41+
ACTOR_MICRO_BATCH_SIZE=${ACTOR_MICRO_BATCH_SIZE:-8}
42+
ROLLOUT_N=${ROLLOUT_N:-8}
43+
LEARNING_RATE=${LEARNING_RATE:-1e-6}
44+
45+
USE_JUDGE=${USE_JUDGE:-0}
46+
JUDGE_PROFILE=${JUDGE_PROFILE:-judgelm_7b}
47+
DRY_RUN=${DRY_RUN:-0}
48+
49+
mkdir -p "$LOG_DIR"
50+
51+
CMD=(
52+
python "$BUILDER_DIR/run_on_policy_loop.py"
53+
--run-prefix "$RUN_PREFIX"
54+
--iterations "$ITERATIONS"
55+
--num-samples "$NUM_SAMPLES"
56+
--split "$SPLIT"
57+
--base-model "$BASE_MODEL"
58+
--generator-profile "$GENERATOR_PROFILE"
59+
--inference-profile "$INFERENCE_PROFILE"
60+
--train-dataset "$TRAIN_DATASET"
61+
--output-root "$OUTPUT_ROOT"
62+
--checkpoint-root "$CHECKPOINT_ROOT"
63+
--val-file "$VAL_FILE"
64+
--project-name "$PROJECT_NAME"
65+
--total-epochs "$TOTAL_EPOCHS"
66+
--save-freq "$SAVE_FREQ"
67+
--test-freq "$TEST_FREQ"
68+
--train-batch-size "$TRAIN_BATCH_SIZE"
69+
--ppo-mini-batch-size "$PPO_MINI_BATCH_SIZE"
70+
--actor-micro-batch-size "$ACTOR_MICRO_BATCH_SIZE"
71+
--rollout-n "$ROLLOUT_N"
72+
--learning-rate "$LEARNING_RATE"
73+
)
74+
75+
if [[ -n "$GPU_IDS" ]]; then
76+
export CUDA_VISIBLE_DEVICES="$GPU_IDS"
77+
CMD+=(--gpu-ids "$GPU_IDS")
78+
fi
79+
80+
if [[ "$USE_JUDGE" == "1" ]]; then
81+
CMD+=(--use-judge --judge-profile "$JUDGE_PROFILE")
82+
fi
83+
84+
if [[ "$DRY_RUN" == "1" ]]; then
85+
CMD+=(--dry-run)
86+
fi
87+
88+
echo "[20260529_gsm8k_mc_sampled] implementation=$IMPLEMENTATION_TAG"
89+
echo "[20260529_gsm8k_mc_sampled] run_prefix=$RUN_PREFIX"
90+
echo "[20260529_gsm8k_mc_sampled] base_model=$BASE_MODEL"
91+
echo "[20260529_gsm8k_mc_sampled] generator_profile=$GENERATOR_PROFILE inference_profile=$INFERENCE_PROFILE"
92+
echo "[20260529_gsm8k_mc_sampled] train_dataset=$TRAIN_DATASET iterations=$ITERATIONS num_samples=$NUM_SAMPLES"
93+
echo "[20260529_gsm8k_mc_sampled] output_root=$OUTPUT_ROOT"
94+
echo "[20260529_gsm8k_mc_sampled] checkpoint_root=$CHECKPOINT_ROOT"
95+
echo "[20260529_gsm8k_mc_sampled] CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES:-<all>}"
96+
echo "[20260529_gsm8k_mc_sampled] log_file=$LOG_FILE"
97+
98+
PYTHONUNBUFFERED=1 VLLM_USE_FLASHINFER_SAMPLER=1 "${CMD[@]}" 2>&1 | tee "$LOG_FILE"

reliable-gsm8k-builder/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ python run_build.py \
9393
--generator-profile qwen25_3b
9494
```
9595

96+
Use a local checkpoint/model path as the generator:
97+
98+
```bash
99+
python run_build.py \
100+
--run-id from-checkpoint \
101+
--split train \
102+
--num-samples 100 \
103+
--generator-profile qwen25_3b \
104+
--generator-model-path /path/to/actor/huggingface
105+
```
106+
96107
## Multi-GPU
97108

98109
If multiple GPUs are visible and either the generator or the optional judge uses a local Transformers model,
@@ -160,6 +171,7 @@ Each run writes to `runs/<run_id>/`:
160171
- `datasets/gsm8k_oe_<generator>/<split>.jsonl`
161172
- `datasets/gsm8k_mc_onecorrect_<generator>/<split>.jsonl`
162173
- `datasets/gsm8k_mc_allwrong_<generator>/<split>.jsonl`
174+
- matching `.parquet` files for `items` and every generated dataset
163175
- `manifest.json`
164176

165177
Every item and dataset row includes both:
@@ -198,3 +210,45 @@ FINAL_ANSWER: ...
198210
- `gsm8k_mc_allwrong` is a real all-wrong dataset:
199211
- the prompt tells the model to output `NONE` if no option is correct
200212
- the ground truth is always `NONE`
213+
214+
## On-Policy Loop
215+
216+
`run_on_policy_loop.py` automates:
217+
218+
1. generate `N` fresh data samples from the current model
219+
2. train with VERL/GRPO on the generated parquet
220+
3. find the saved HF actor checkpoint
221+
4. use that checkpoint as the generator for the next iteration
222+
223+
Example dry run:
224+
225+
```bash
226+
python run_on_policy_loop.py \
227+
--run-prefix onpol-smoke \
228+
--iterations 2 \
229+
--num-samples 100 \
230+
--base-model Qwen/Qwen2.5-3B-Instruct \
231+
--generator-profile qwen25_3b \
232+
--inference-profile sample_balanced \
233+
--train-dataset mc_onecorrect \
234+
--gpu-ids 0,1,2 \
235+
--dry-run
236+
```
237+
238+
Real run:
239+
240+
```bash
241+
python run_on_policy_loop.py \
242+
--run-prefix onpol-qwen \
243+
--iterations 3 \
244+
--num-samples 1000 \
245+
--base-model Qwen/Qwen2.5-3B-Instruct \
246+
--generator-profile qwen25_3b \
247+
--inference-profile sample_balanced \
248+
--train-dataset mc_onecorrect \
249+
--val-file ~/data/gsm8k/test.parquet \
250+
--gpu-ids 0,1,2
251+
```
252+
253+
The loop stores generated data under `runs_on_policy/<run>-iterXX-data/` and checkpoints under
254+
`../checkpoints/on_policy/<run>-iterXX-train/` by default.

reliable-gsm8k-builder/run_build.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def _spawn_multi_gpu_workers(args: argparse.Namespace, *, script_path: Path, gpu
5353
str(gpu_id),
5454
"--disable-multi-gpu",
5555
]
56+
if args.generator_model_path is not None:
57+
cmd.extend(["--generator-model-path", args.generator_model_path])
5658
if args.judge_profile is not None:
5759
cmd.extend(["--judge-profile", args.judge_profile])
5860
if args.use_judge:
@@ -86,6 +88,11 @@ def main() -> None:
8688
parser.add_argument("--num-samples", type=int, default=None, help="Alias for --max-items when you want to run only a small sample.")
8789
parser.add_argument("--output-root", default=str(SCRIPT_DIR / "runs"), help="Directory that will contain run outputs.")
8890
parser.add_argument("--generator-profile", default="qwen25_3b", help="Built-in generator profile name.")
91+
parser.add_argument(
92+
"--generator-model-path",
93+
default=None,
94+
help="Optional model/checkpoint path overriding the selected generator profile's model/tokenizer.",
95+
)
8996
parser.add_argument("--inference-profile", default="greedy", help="Built-in inference profile name.")
9097
parser.add_argument("--judge-profile", default="judgelm_7b", help="Built-in judge profile name if --use-judge is enabled.")
9198
parser.add_argument("--use-judge", action="store_true", help="Use a judge model to verify generated numeric answers. Disabled by default.")
@@ -162,6 +169,7 @@ def main() -> None:
162169
max_items=args.effective_max_items,
163170
output_root=output_root,
164171
generator_profile_name=args.generator_profile,
172+
generator_model_path=args.generator_model_path,
165173
inference_profile_name=args.inference_profile,
166174
judge_profile_name=args.judge_profile,
167175
judge_max_tokens=args.judge_max_tokens,

0 commit comments

Comments
 (0)