Modal Research Exchange

you are

DPPO divergence-masked loss vs GRPO ratio-clipped loss, Qwen3.5-2B GSM8K LoRA RL

Succeeded not supported Devin Qwen/Qwen3.5-2B RL 2.0 GPU-h budget started 5h ago 17m 08s
Redo
Pick where the agent works.
View session in Devin ↗

Bounded diagnostic on shared Spindle (Qwen3.5-2B, 16 updates over one frozen 32x8 GSM8K-train batch, 8 minibatches x 2 epochs, 471 s): DPPO mask was 0 in epoch 1 and 0.00114 pooled in epoch 2 (96 removed nonzero-gradient tokens, both signs), PPO counterfactual clip 0.0137, drift q90 0.024, so the revised activation checks pass marginally and the original zero mask is a legitimate inactive regime (disjoint minibatches with one prior update keep |p-mu| far below 0.15; initial trainer/sampler mismatch q90 0.0076), not a code defect. The accuracy hypothesis was not tested; next action is a new allocation for the frozen 8x2 comparison spec with a both-arm feasibility/activation pilot first.

Not supported The agent found the evidence insufficient to show the claimed effect. Confidence is limited by: custom implementation correctness unverified; treatment activation failed; initialization and optimizer verification unverified; decision evidence uncertain; feasibility evidence incomplete; the run changed the experiment, so this judges the changed version. Reclassified from inconclusive under the direction-plus-confidence rule.

Result epoch2_dppo_masked_token_fraction

Higher is better · Axis from 0.00106 to 0.0012, not zero
armepoch2_dppo_masked_token_fractionepoch2_dppo_masked_nonzero_adv_fractionepoch2_ppo_counterfactual_clip_fractionepoch2_policy_drift_abs_p_q90epoch2_policy_drift_abs_p_q99epoch2_policy_drift_abs_p_maxepoch2_removed_nonzero_grad_posepoch2_removed_nonzero_grad_neg
dppo_diag0.0011360.0012840.013690.023860.086280.41875046
3 more metrics under Evidence, All metrics.
Arms Devin reported complete: dppo_diag
Changes from the spec 7 · original hypothesis not tested
whatspecran withwhyrecorded
Training steps50 rollout steps x 2 disjoint minibatches (100 optim steps) per arm, fresh sampler each batchONE frozen 32x8 batch, 8 minibatches of 4 complete groups, 2 epochs, max 16 optim steps, no further sampling, no old-logprob refreshRequester-authorized bounded activation diagnostic after run_19badd842c zero-mask gate failure before the run ended
Objective / lossPaired GRPO-clip vs DPPO training armsSingle DPPO Binary-TV (delta .15, C=10 code variant) diagnostic arm; PPO eps .2 computed only as counterfactual on the same tensors (not trained)Requester instructions: diagnostic only, no trained baseline arm before the run ended
Endpoint metricgsm8k_test_greedy_exact_match_accuracy@final_checkpoint (1319 items)Activation diagnostics only (pooled over ALL second-epoch loss calls by loss-token count); no GSM8K test evaluationRequester forbids test evaluation/outcome selection in this launch before the run ended
EvaluatorActivation gate: unweighted mean over pilot steps of second-minibatch fractionsPooled-by-token over all 8 second-epoch calls; same numeric ranges (DPPO .001-.3, PPO>=.0005, drift q90>=.005) plus nonzero removed-gradient per present advantage signAggregation revision declared before execution per requester; original gate report preserved separately before the run ended
ModelQwen3.5-0.8B (requested idea)Qwen/Qwen3.5-2B0.8B not served; spec already pins 2B before the run ended
Training steps50 rollout steps x 2 disjoint minibatches (100 optim steps) per arm, fresh sampler each batchONE frozen 32x8 batch, 8 minibatches of 4 complete groups, 2 epochs, 16 optim steps executed, no further sampling, no old-logprob refreshRequester-authorized bounded activation diagnostic after run_19badd842c zero-mask gate failure only in the final result
Endpoint metricgsm8k_test_greedy_exact_match_accuracy@final_checkpoint (1319 items)Activation diagnostics only (pooled over all second-epoch loss calls by loss-token count); no GSM8K test evaluationRequester forbids test evaluation/outcome selection in this launch only in the final result

Original hypothesis tested: no (changing the endpoint and objective changes the question)

Tested instead: Activation diagnostic only: whether DPPO Binary-TV (delta .15, C=10) masking and counterfactual PPO clipping engage on real Qwen3.5-2B GSM8K data under 8 minibatches x 2 epochs on one frozen batch. No accuracy comparison.

Devin's verdict on the changed experiment: inconclusive

  • steps: reported original '50 rollout steps x 2 disjoint minibatches (100 optim steps) per arm, fresh sampler each batch' but the spec says '100'
  • model: reported original 'Qwen3.5-0.8B (requested idea)' but the spec says 'Qwen/Qwen3.5-2B'
  • steps: reported original '50 rollout steps x 2 disjoint minibatches (100 optim steps) per arm, fresh sampler each batch' but the spec says '100'
  • reported only in the final result, not before training went on: steps, endpoint
Checks · activation failed · evaluator unknown · feasibility unknown
Custom implementation correctness: unverified
grpo_dppo_custom_loss: measured baseline_gradient errors; grpo_dppo_custom_loss: measured reference_gradient errors
Validates executor-reported evidence; autonomous training calls are not blocked by this server.
Executor reported inconclusive; shown as not supported.
Parity errors and accepted tolerances
[
  {
    "check": "baseline_loss",
    "measured": {
      "cosine_similarity": 1.0,
      "max_abs_error": 0.0,
      "reference_max_abs": 429.46087646484375,
      "relative_l2_error": 0.0
    },
    "path": "grpo_dppo_custom_loss",
    "status": "passed",
    "tolerance": {
      "atol": 1e-05,
      "rtol": 0.0001
    }
  },
  {
    "check": "baseline_gradient",
    "measured": {
      "cosine_similarity": 1.0,
      "max_abs_error": 0.0,
      "reference_max_abs": null,
      "relative_l2_error": 0.0
    },
    "path": "grpo_dppo_custom_loss",
    "status": "unverified",
    "tolerance": {
      "atol": 1e-05,
      "min_cosine": 0.9999,
      "rtol": 0.001
    }
  },
  {
    "check": "reference_loss",
    "measured": {
      "cosine_similarity": 1.0,
      "max_abs_error": 3.0517578125e-05,
      "reference_max_abs": 420.54302978515625,
      "relative_l2_error": 7.256e-08
    },
    "path": "grpo_dppo_custom_loss",
    "status": "passed",
    "tolerance": {
      "atol": 1e-05,
      "rtol": 0.0001
    }
  },
  {
    "check": "reference_gradient",
    "measured": {
      "cosine_similarity": 1.0,
      "max_abs_error": 0.0,
      "reference_max_abs": null,
      "relative_l2_error": 0.0
    },
    "path": "grpo_dppo_custom_loss",
    "status": "unverified",
    "tolerance": {
      "atol": 1e-05,
      "min_cosine": 0.9999,
      "rtol": 0.001
    }
  }
]
Treatment activation: failed, so this run does not test the mechanism
Outside the prespecified range: executed_updates_per_batch = 16 (needs >= 2 and <= 2).
dppo_masked_token_fraction
not reported needs >= 0.001 and <= 0.3 · missing
policy_drift_abs_p_q90
not reported needs >= 0.005 · missing
grpo_clip_fraction
not reported needs >= 0.0005 · missing
executed_updates_per_batch
16 needs >= 2 and <= 2 · failed
executed_base_model
Qwen/Qwen3.5-2B needs one of Qwen/Qwen3.5-2B · passed
Initialization / optimizer verification: unverified
grpo_seed0: missing or duplicate arm evidence.
Research decision: uncertain
Decision evidence was not recorded.
Inference unit: fixed_checkpoint_items · 1 scheduled seed(s). Bounded Hoeffding confidence interval.
Evaluator comparability: unknown
Evaluator provenance was not recorded.
Feasibility projection: unknown
Projected costs, not measured GPU usage.
Projected wall time
unknown
Projected GPU allocation
unknown
  • Re-projection required: dataset changed or was not recorded
  • Re-projection required: steps changed or was not recorded
  • Re-projection required: batch_size changed or was not recorded
  • Re-projection required: max_seq_len changed or was not recorded
  • Re-projection required: arms changed or was not recorded
  • Re-projection required: n_examples changed or was not recorded
  • Primary endpoint changed or was not recorded
  • Unknown timing phase: sampling
  • Invalid measured timing/counts for evaluation
Timing, sample counts and allocation evidence
{
  "generation_applicability": {
    "check": "generation_audit",
    "enforce": false,
    "features": {
      "generated_training_data": false
    },
    "reasons": [
      "spec.generation_audit opts out and no feature it covers is true"
    ],
    "status": "not_applicable"
  },
  "generation_counts": null,
  "gpu_hours": null,
  "known_gpu_hours": 0,
  "phases": {
    "recovery": {
      "gpu_hours": null,
      "wall_seconds": 249.2
    },
    "setup": {
      "gpu_hours": null,
      "planned_samples": 0.0,
      "planned_units": 1,
      "wall_seconds": 153.31
    },
    "training": {
      "gpu_hours": null,
      "planned_samples": 512.0,
      "planned_units": 16,
      "wall_seconds": 195.0
    }
  },
  "problems": [
    "Re-projection required: dataset changed or was not recorded",
    "Re-projection required: steps changed or was not recorded",
    "Re-projection required: batch_size changed or was not recorded",
    "Re-projection required: max_seq_len changed or was not recorded",
    "Re-projection required: arms changed or was not recorded",
    "Re-projection required: n_examples changed or was not recorded",
    "Primary endpoint changed or was not recorded",
    "Unknown timing phase: sampling",
    "Invalid measured timing/counts for evaluation"
  ],
  "record": {
    "config": {
      "arm": "dppo_diag",
      "base_model": "Qwen/Qwen3.5-2B",
      "batch_index": 0,
      "clip_ratio_c": 10.0,
      "data_seed": 0,
      "delta": 0.15,
      "endpoint": "shared Spindle (TINKER_BASE_URL)",
      "epochs": 2,
      "group_size": 8,
      "groups": 32,
      "groups_per_minibatch": 4,
      "init_state_path": "tinker://21e32976f18f971463410adff4e364e4/weights/rx19badd842c_init",
      "lora_rank": 32,
      "loss": "dppo_binary_tv",
      "loss_normalization": "token sum (Spindle stock _loss convention)",
      "max_tokens": 512,
      "max_updates": 16,
      "minibatches": 8,
      "n_eval": 0,
      "optimizer": {
        "beta1": 0.9,
        "beta2": 0.95,
        "eps": 1e-08,
        "grad_clip_norm": 0.0,
        "learning_rate": 2e-05,
        "type": "adamw",
        "weight_decay": 0.0
      },
      "ppo_eps_counterfactual": 0.2,
      "renderer": "qwen3_5_disable_thinking",
      "sdk": {
        "tinker": "0.37.0",
        "torch": "2.15.0"
      },
      "temperature": 1.0,
      "wall_cap_seconds": 720
    },
    "effective_config": {
      "arm": "dppo_diag",
      "base_model": "Qwen/Qwen3.5-2B",
      "batch_index": 0,
      "clip_ratio_c": 10.0,
      "data_seed": 0,
      "delta": 0.15,
      "endpoint": "shared Spindle (TINKER_BASE_URL)",
      "epochs": 2,
      "group_size": 8,
      "groups": 32,
      "groups_per_minibatch": 4,
      "init_state_path": "tinker://21e32976f18f971463410adff4e364e4/weights/rx19badd842c_init",
      "lora_rank": 32,
      "loss": "dppo_binary_tv",
      "loss_normalization": "token sum (Spindle stock _loss convention)",
      "max_tokens": 512,
      "max_updates": 16,
      "minibatches": 8,
      "n_eval": 0,
      "optimizer": {
        "beta1": 0.9,
        "beta2": 0.95,
        "eps": 1e-08,
        "grad_clip_norm": 0.0,
        "learning_rate": 2e-05,
        "type": "adamw",
        "weight_decay": 0.0
      },
      "ppo_eps_counterfactual": 0.2,
      "renderer": "qwen3_5_disable_thinking",
      "sdk": {
        "tinker": "0.37.0",
        "torch": "2.15.0"
      },
      "temperature": 1.0,
      "wall_cap_seconds": 720
    },
    "generation": null,
    "measurements": {
      "evaluation": {
        "allocation_source": null,
        "gpu_count": null,
        "planned_units": 0,
        "samples": 0,
        "seconds": 0,
        "units": 0
      },
      "sampling": {
        "allocation_source": null,
        "gpu_count": null,
        "planned_units": 256,
        "samples": 256,
        "seconds": 114.19,
        "units": 256
      },
      "setup": {
        "allocation_source": null,
        "gpu_count": null,
        "planned_units": 1,
        "samples": 0,
        "seconds": 153.31,
        "units": 1
      },
      "training": {
        "allocation_source": null,
        "gpu_count": null,
        "planned_units": 16,
        "samples": 512,
        "seconds": 195.0,
        "units": 16
      }
    },
    "primary_endpoint": "gsm8k_test_greedy_exact_match_accuracy@final_checkpoint (1319 items) \u2014 not evaluated in this diagnostic",
    "recovery": {
      "allocation_source": null,
      "gpu_count": null,
      "seconds": 249.2
    },
    "source": "diag-events.jsonl"
  },
  "recorded_at": 1793746408.889922,
  "status": "unknown",
  "wall_seconds": null
}
Generated task audit: not applicable
spec.generation_audit opts out and no feature it covers is true
Answer agreement with recorded independent solutions does not establish semantic correctness. Normalized exact overlap does not detect paraphrases.
Spec exp_e6b0965af3
Instructions given to Devin
User explicitly requested re-trigger after the DPPO zero-mask investigation. This launch is a BOUNDED DIAGNOSTIC continuation, not a full outcome comparison. These requester instructions override the original 50-step/two-minibatch training schedule and test evaluation. Keep the original failed pilot and accepted spec intact. Before any training, persist revised-diagnostic-spec.json, effective-config.json, and explicit deviations describing the schedule, aggregation and scope changes below; do not pretend the original hypothesis was tested. Read the committed diagnosis and patch in modal-projects/research-exchange@c469bca5b89f1142da1cdf6e1a64d5adbe4874ce, docs/dppo-zero-mask/diagnosis.md, losses-instrumentation.patch, and scripts/audit_dppo_evidence.py. Prior run run_19badd842c evidence is available at /api/runs/run_19badd842c/evidence on this exchange. Verify manifest SHA256 and sizes. Independent offline replay passed source-gradient/mask parity; do not rely only on the previous executor's report. First recover and archive the prior code/rx.py through authorized session access if available; inspect sampler capture time, token alignment, loss mask, and immutable old logprobs. Prior script was not downloadable. If unavailable, archive a new explicit loop and label it a replacement, not audited old code. Reuse downloadable old losses as the reference starting point, apply the retained INSTRUMENTATION-only patch, and independently rerun fixture/forced cases, both signs, recovery directions, boundaries, tails, zero advantages, padding, ratio cap, PPO clip diagnostics and gradients. Zero-advantage/padding tokens cannot count as treatment engagement. This patch changes no loss gradient. Never write a production volume or change credentials. DIAGNOSTIC SETTINGS FIXED BEFORE MEASUREMENT: Qwen/Qwen3.5-2B, rank32; DPPO Binary-TV delta=.15, C=10 (explicit released-code variant, not paper C=5), PPO epsilon=.2 counterfactual diagnostics; original AdamW lr2e-5 beta1=.9 beta2=.95 eps1e-8 weight_decay0 grad_clip0; original renderer, temperature1, max512 and group-centered advantages. Restore immutable shared full initial state with observed successful load; do not start from trained pilot weights. One batch of 32 GSM8K TRAIN groups x8 responses, split into eight minibatches of FOUR COMPLETE GROUPS each, TWO epochs over that SAME frozen batch. Max16 optimizer updates total, no more sampling after that batch. Freeze and hash tokens, sampler logprobs, advantages and masks; assert hashes before every reuse. Sampler checkpoint/version must be recorded; no old-logprob refresh. HARD BUDGET: at most12 wall minutes for this diagnostic including setup, sampling and training; never exceed the original experiment's2 GPU-hour cap or any lower remaining allocation. Check remaining budget before service work. No budget/concurrency increase, model/LR/threshold sweep, full training, GSM8K test evaluation or held-out outcome selection. If setup exhausts12min, persist evidence and stop. Only this single diagnostic is authorized in this launch. Before each loss/update, persist actual current and sampler logprobs, token IDs, masks, advantages and actual dtype. Save actual decisions and removed nonzero-gradient counts, both all-loss-token and nonzero-advantage denominators, signed/absolute p90/p99/max, threshold exceedances, probability bins and ratio-cap hits. Fixed sentinel tokens from this SAME training batch: compare trainer logprobs at initialization and after updates1/2/4/8/16 to separate optimizer drift from initial trainer/sampler mismatch. Compute counterfactual PPO on same tensors; this is not a trained baseline arm. Fixed analysis: assess ALL second-epoch loss calls pooled by loss-token counts: DPPO masked fraction .001 to .3, PPO counterfactual clip fraction>=.0005, absolute probability drift pooled q90>=.005, plus measured nonzero removed-gradient evidence for each advantage sign if that sign occurs. Declare aggregation/schedule as revisions BEFORE execution; they differ from the original second-minibatch unweighted mean. Preserve the original gate report separately; do not make original gate pass by overwriting it. Do not select the best step, lower delta, relax criteria, or automatically launch another diagnostic/full training. At16updates/12minutes stop regardless of activation. If inactive, explain using actual tails and signs; distinguish code/instrumentation defect, wrong reference/variant, legitimate inactive regime and missing evidence. If active, preserve evidence and provide the frozen revised comparison spec as the next action, without executing full comparison. Final report must state verified/suspected cause, exact fixes, tests, limitations and cheapest next action; scientific verdict remains inconclusive for the untested accuracy hypothesis. Archive ALL executed scripts (use inline content/run evidence export if attachment retrieval fails), revised spec, sources/revisions, token fixtures, per-update data, hashes, patch, checkpoints and metrics. Do not end with an opaque activation-failed badge or silently mark an unperformed hypothesis supported/refuted.
Experiment version
exp_e6b0965af3
Repeat the selected saved experiment version to check reproducibility. This repetition tests that version's hypothesis and methods.

Hypothesis

On Qwen3.5-2B LoRA RL on GSM8K with 2 policy updates per rollout batch, the DPPO loss reaches higher greedy exact-match accuracy on the 1319-item GSM8K test set than the GRPO clipped-ratio loss. The lower bound of the paired 95% CI on DPPO minus GRPO must be above 0.

Success criterion

On the 1319 GSM8K test items, the paired 95% Hoeffding CI for DPPO accuracy minus GRPO accuracy has a lower bound above 0.0, roughly a 7.5-point gap at this n. A smaller positive gap is reported as inconclusive, not validated.

gsm8k 100 steps batch 256 LoRA r32 lr 2e-05 120 min wall budget
Method
The requested Qwen3.5-0.8B is not served, so we use the closest served small model, Qwen/Qwen3.5-2B. Both arms run the same client-side RL loop: the tinker-cookbook math_rl GSM8K env, renderer, prompts, reward and data order. Each step samples groups_per_batch=32 GSM8K train prompts with group_size=8 rollouts at temperature 1.0 and max_tokens=512. Advantages are group-mean-centered (GRPO). The rollout batch is split into 2 minibatches. That gives 2 optim_steps per batch, so the policy drifts from the sampling policy and the trust region can act. With one update per fresh batch, neither mask nor clip fires. Both losses are computed in one forked custom loss path through forward_backward_custom, using the sampler logprobs as p_old. The arms differ in exactly one switch: - Baseline (GRPO): token loss -min(r*A, clip(r, 1-0.2, 1+0.2)*A). - Treatment (DPPO, arXiv 2602.04879): token loss -r*A, with tokens masked out when the divergence between the current and sampling policy exceeds the threshold delta in the direction the advantage pushes. The divergence is estimated from the token probabilities, e.g. |p - p_old|, rather than the single-sample ratio. Triage only received a truncated digest of the paper. The exact divergence estimator, delta and masking direction must be pinned from the paper's released code before any training. Until then the correctness preflight is unresolved and no full sweep runs. Training: LoRA rank 32, AdamW lr 2e-5, 50 rollout steps (100 optim steps) per arm, seed 0. Both arms start from the same base weights. Evaluation: the final checkpoint of each arm is scored with greedy decoding on all 1319 GSM8K test items, using the cookbook's numeric answer grader.
Baseline
GRPO clipped-ratio loss (eps=0.2) through the same custom loss path. Identical rollouts per step, 2 minibatch updates per batch, lr, rank, seed, data order and eval.
Full spec
{
  "activation_gate": {
    "applicable": true,
    "checks": [
      {
        "max": 0.3,
        "measures": "fraction of loss tokens masked by the DPPO divergence rule on the second minibatch update, mean over pilot steps",
        "metric": "dppo_masked_token_fraction",
        "min": 0.001
      },
      {
        "measures": "90th percentile of |p_current - p_sampler| over loss tokens at the second update",
        "metric": "policy_drift_abs_p_q90",
        "min": 0.005
      },
      {
        "measures": "fraction of tokens clipped by the eps=0.2 ratio clip in the baseline loss on the same pilot batches (confirms the regime is off-policy for the baseline too)",
        "metric": "grpo_clip_fraction",
        "min": 0.0005
      },
      {
        "max": 2,
        "measures": "optim_step calls per rollout batch as logged",
        "metric": "executed_updates_per_batch",
        "min": 2
      },
      {
        "measures": "model name reported at client creation",
        "metric": "executed_base_model",
        "one_of": [
          "Qwen/Qwen3.5-2B"
        ]
      }
    ],
    "reason": "DPPO only differs from GRPO on tokens whose divergence exceeds the trust region. A 5-step pilot of the treatment arm, with 2 updates per batch, must show that the mask fires on a nontrivial but not dominant share of tokens and that the policy actually drifts. Executed config values must match the plan."
  },
  "arms": [
    {
      "loss": "grpo_clip_eps0.2",
      "lr": 2e-05,
      "name": "grpo_seed0",
      "seed": 0,
      "updates_per_batch": 2
    },
    {
      "loss": "dppo_divergence_mask",
      "lr": 2e-05,
      "name": "dppo_seed0",
      "seed": 0,
      "updates_per_batch": 2
    }
  ],
  "base_model": "Qwen/Qwen3.5-2B",
  "baseline": "GRPO clipped-ratio loss (eps=0.2) through the same custom loss path. Identical rollouts per step, 2 minibatch updates per batch, lr, rank, seed, data order and eval.",
  "batch_size": 256,
  "budget": {
    "gpu_hours": 2.0,
    "max_wall_minutes": 120
  },
  "command": "Custom tinker SDK loop; the loss function is the custom part. Sketch:\n\nimport os, tinker\nsc = tinker.ServiceClient(base_url=os.environ['TINKER_BASE_URL'])\ntc = sc.create_lora_training_client(base_model='Qwen/Qwen3.5-2B', rank=32)\n\n# Rollouts: reuse tinker_cookbook.recipes.math_rl GSM8K env and renderer.\n# group_size=8, groups_per_batch=32, max_tokens=512, seed=0.\nfor step in range(50):\n    sampler = tc.save_weights_and_get_sampling_client(f's{step}')\n    batch = rollout(sampler, gsm8k_train_prompts[step], group_size=8)  # stores sampler logprobs as old_lp\n    adv = group_mean_center(batch.rewards)\n    for mb in split(batch, 2):\n        tc.forward_backward_custom(mb.data, make_loss(LOSS, old_lp=mb.old_lp, adv=mb.adv)).result()\n        tc.optim_step(tinker.AdamParams(learning_rate=2e-5)).result()\n\n# LOSS='grpo': -min(r*A, clip(r, 0.8, 1.2)*A)\n# LOSS='dppo': -(r*A) * keep_mask(div(p, p_old) <= delta), with div and delta pinned from the paper code\n# Final eval: greedy decoding on openai/gsm8k test (1319 items), numeric exact match.",
  "correctness_preflight": {
    "paths": [
      {
        "baseline_revision": "UNRESOLVED: pin tinker-cookbook commit whose stock ppo/importance-sampling loss defines GRPO clipping",
        "id": "grpo_dppo_custom_loss",
        "kind": "loss",
        "reference_revision": "UNRESOLVED: pin commit of the official DPPO implementation for arXiv 2602.04879",
        "tolerances": {
          "baseline_gradient": {
            "atol": 1e-05,
            "min_cosine": 0.9999,
            "rtol": 0.001
          },
          "baseline_loss": {
            "atol": 1e-05,
            "rtol": 0.0001
          },
          "reference_gradient": {
            "atol": 1e-05,
            "min_cosine": 0.9999,
            "rtol": 0.001
          },
          "reference_loss": {
            "atol": 1e-05,
            "rtol": 0.0001
          }
        }
      }
    ],
    "required": true,
    "status": "unresolved: DPPO reference code revision and exact divergence/threshold not yet pinned (paper digest failed); no full sweep until resolved"
  },
  "dataset": {
    "hf_path": "openai/gsm8k",
    "n_examples": 1600,
    "name": "gsm8k",
    "split": "main/train"
  },
  "decision_rule": {
    "alpha": 0.05,
    "criteria": [
      {
        "bounds": [
          0,
          1
        ],
        "id": "primary",
        "margin": 0.0,
        "metric": "accuracy",
        "weights": {
          "baseline": -1,
          "treatment": 1
        }
      }
    ],
    "estimand": "Difference in greedy GSM8K test exact-match accuracy, DPPO minus GRPO, for these fixed final checkpoints of Qwen3.5-2B LoRA; larger is better",
    "method": "paired_hoeffding",
    "seeds": [
      0
    ],
    "unit": "fixed_checkpoint_items",
    "version": 1
  },
  "eval": {
    "aggregation": null,
    "benchmark": "GSM8K test (openai/gsm8k main/test)",
    "metric": "greedy exact-match accuracy",
    "n_examples": 1319
  },
  "experiment_features": {
    "adaptive_selection": false,
    "checkpoint_restore": false,
    "custom_loss": true,
    "custom_optimizer": false,
    "generated_training_data": false,
    "heldout_loss": false,
    "learned_generator": false,
    "paired_comparison": true,
    "paired_training_arms": true,
    "warmup_adaptation": false
  },
  "feasibility": null,
  "feasibility_check": {
    "gpu_hours": null,
    "known_gpu_hours": 0,
    "phases": {},
    "problems": [
      "Measured feasibility not recorded"
    ],
    "status": "unknown",
    "wall_seconds": null
  },
  "feasibility_required": true,
  "generation_audit": {
    "answer_comparison": "numeric_exact",
    "max_eval_overlaps": 0,
    "max_label_errors": 0,
    "min_sample_size": 100,
    "required": false
  },
  "hypothesis": "On Qwen3.5-2B LoRA RL on GSM8K with 2 policy updates per rollout batch, the DPPO loss reaches higher greedy exact-match accuracy on the 1319-item GSM8K test set than the GRPO clipped-ratio loss. The lower bound of the paired 95% CI on DPPO minus GRPO must be above 0.",
  "lora_rank": 32,
  "max_seq_len": 1024,
  "method": "The requested Qwen3.5-0.8B is not served, so we use the closest served small model, Qwen/Qwen3.5-2B.\n\nBoth arms run the same client-side RL loop: the tinker-cookbook math_rl GSM8K env, renderer, prompts, reward and data order. Each step samples groups_per_batch=32 GSM8K train prompts with group_size=8 rollouts at temperature 1.0 and max_tokens=512. Advantages are group-mean-centered (GRPO).\n\nThe rollout batch is split into 2 minibatches. That gives 2 optim_steps per batch, so the policy drifts from the sampling policy and the trust region can act. With one update per fresh batch, neither mask nor clip fires. Both losses are computed in one forked custom loss path through forward_backward_custom, using the sampler logprobs as p_old.\n\nThe arms differ in exactly one switch:\n- Baseline (GRPO): token loss -min(r*A, clip(r, 1-0.2, 1+0.2)*A).\n- Treatment (DPPO, arXiv 2602.04879): token loss -r*A, with tokens masked out when the divergence between the current and sampling policy exceeds the threshold delta in the direction the advantage pushes. The divergence is estimated from the token probabilities, e.g. |p - p_old|, rather than the single-sample ratio.\n\nTriage only received a truncated digest of the paper. The exact divergence estimator, delta and masking direction must be pinned from the paper's released code before any training. Until then the correctness preflight is unresolved and no full sweep runs.\n\nTraining: LoRA rank 32, AdamW lr 2e-5, 50 rollout steps (100 optim steps) per arm, seed 0. Both arms start from the same base weights.\n\nEvaluation: the final checkpoint of each arm is scored with greedy decoding on all 1319 GSM8K test items, using the cookbook's numeric answer grader.",
  "optimizer": {
    "lr": 2e-05,
    "name": "adam"
  },
  "primary_endpoint": "gsm8k_test_greedy_exact_match_accuracy@final_checkpoint (1319 items)",
  "restoration_preflight": {
    "applicable": true,
    "fixed_batch_required": true,
    "groups": [
      {
        "arms": [
          {
            "name": "grpo_seed0",
            "optimizer": "adam",
            "optimizer_config": {
              "beta1": 0.9,
              "beta2": 0.95,
              "eps": 1e-08,
              "learning_rate": 2e-05
            }
          },
          {
            "name": "dppo_seed0",
            "optimizer": "adam",
            "optimizer_config": {
              "beta1": 0.9,
              "beta2": 0.95,
              "eps": 1e-08,
              "learning_rate": 2e-05
            }
          }
        ]
      }
    ],
    "reason": "Both arms are LoRA-trained from the same base. Before the first update, verify identical initial state: compare the logprobs on a fixed tokenized GSM8K batch across both freshly created clients, which must be bitwise or within 1e-6. Also verify the active optimizer is AdamW with the planned config after creation. Server identity cannot be introspected and stays unverified."
  },
  "steps": 100,
  "success_criterion": "On the 1319 GSM8K test items, the paired 95% Hoeffding CI for DPPO accuracy minus GRPO accuracy has a lower bound above 0.0, roughly a 7.5-point gap at this n. A smaller positive gap is reported as inconclusive, not validated.",
  "title": "DPPO divergence-masked loss vs GRPO ratio-clipped loss, Qwen3.5-2B GSM8K LoRA RL",
  "training_type": "rl"
}
Evidence 20

Partial · Download manifest. Recorded files are retained copies; checkpoint references do not include model weights.

Not recorded: predictions.

Checkpoint references
[
  {
    "arm": "dppo_diag",
    "step": 16,
    "uri": "tinker://daecfa24-b646-4b82-b337-4447b0e95af0:train:0/weights/rx21b_diag_final"
  },
  {
    "arm": "dppo_diag",
    "step": 0,
    "uri": "tinker://21e32976f18f971463410adff4e364e4/weights/rx19badd842c_init"
  }
]
Per-arm evidence metrics
{
  "dppo_diag": {
    "epoch2_abs_drift_gt_delta_tokens": 224,
    "epoch2_dppo_masked_nonzero_adv_fraction": 0.0012842981177005712,
    "epoch2_dppo_masked_token_fraction": 0.0011361484567316796,
    "epoch2_n_loss_tokens": 84496,
    "epoch2_policy_drift_abs_p_max": 0.4187354204836845,
    "epoch2_policy_drift_abs_p_q90": 0.023858560402555296,
    "epoch2_policy_drift_abs_p_q99": 0.08628262678867459,
    "epoch2_ppo_counterfactual_clip_fraction": 0.013692955879568263,
    "epoch2_ratio_cap_hits": 0,
    "epoch2_removed_nonzero_grad_neg": 46,
    "epoch2_removed_nonzero_grad_pos": 50
  }
}

Evidence import problems: file 20: invalid file name or kind.

All metrics
metricdppo_diag
epoch2_dppo_masked_token_fraction0.001136
epoch2_dppo_masked_nonzero_adv_fraction0.001284
epoch2_ppo_counterfactual_clip_fraction0.01369
epoch2_policy_drift_abs_p_q900.02386
epoch2_policy_drift_abs_p_q990.08628
epoch2_policy_drift_abs_p_max0.4187
epoch2_removed_nonzero_grad_pos50
epoch2_removed_nonzero_grad_neg46
epoch2_abs_drift_gt_delta_tokens224
epoch2_ratio_cap_hits0
epoch2_n_loss_tokens84,496
diag_updates_executed
16
diag_wall_seconds
470.8
epoch1_dppo_masked_token_fraction
0
epoch1_ppo_counterfactual_clip_fraction
0.001195
epoch1_policy_drift_abs_p_q90
0.01251
epoch1_policy_drift_abs_p_max
0.137
sentinel_init_vs_sampler_absdp_q90
0.007642
sentinel_init_vs_sampler_absdp_max
0.06218
sampled_reward_mean
0.5789
executed_updates_per_batch
16
sentinel_after_u1_vs_init_absdp_q90
0.005858
sentinel_after_u1_vs_init_absdp_max
0.06052
sentinel_after_u2_vs_init_absdp_q90
0.007421
sentinel_after_u2_vs_init_absdp_max
0.06134
sentinel_after_u4_vs_init_absdp_q90
0.009359
sentinel_after_u4_vs_init_absdp_max
0.09145
sentinel_after_u8_vs_init_absdp_q90
0.01559
sentinel_after_u8_vs_init_absdp_max
0.1537
sentinel_after_u16_vs_init_absdp_q90
0.03011
sentinel_after_u16_vs_init_absdp_max
0.3284
Interim updates from Devin (2, first Nov 3, 22:40 UTC)
Artifact references
  • checkpoint · dppo_diag · step 16 · tinker://daecfa24-b646-4b82-b337-4447b0e95af0:train:0/weights/rx21b_diag_final
  • checkpoint · dppo_diag · step 0 · tinker://21e32976f18f971463410adff4e364e4/weights/rx19badd842c_init
  • instructions · launch-instructions.md sha256 b42946cbc224
  • script · diag.py sha256 1360d041a065
  • script · analyze.py sha256 e444bfe6238f
  • script · losses_patched.py sha256 bceaad747abb
  • script · audit_dppo_evidence.py sha256 f90c441284ff
  • code_patch · losses-instrumentation.patch sha256 d558bd82075a
  • effective_config · revised-diagnostic-spec.json sha256 7af592135f9b
  • effective_config · effective-config.json sha256 7d198e6df468
  • effective_config · next-comparison-spec.json sha256 963bda2f5972
  • code_revision · code-revision.json sha256 3b77d75e15ef
  • metrics · diag-events.jsonl sha256 c9ebbb71d393
  • metrics · diag-per-update.json sha256 5c7ac8aa254e
  • metrics · diag-analysis.json sha256 be7f0bc4e86b
  • metrics · diag-frozen-batch.json sha256 d275384bc777
  • metrics · audit-result-original-rerun.json sha256 58933b7bc645
  • metrics · audit-result-patched-rerun.json sha256 059303ae2016
  • metrics · prior-evidence-manifest.json sha256 ba862f882327
  • metrics · prior-pilot-dppo-steps.jsonl sha256 ee68c6b8951c
  • metrics · diag-report.md sha256 6576232559a1
  • metrics · rx21b-diag-tensors.tar.gz sha256 08735d655a54
  1. Nov 3, 22:53 UTC · succeeded · done: dppo_diag · Bounded diagnostic on shared Spindle (Qwen3.5-2B, 16 updates over one frozen 32x8 GSM8K-train batch, 8 minibatches x 2 epochs, 471 s): DPPO mask was 0 in epoch 1 and 0.00114 pooled in epoch 2 (96 rem…
  2. Nov 3, 22:40 UTC · running · Bounded DPPO activation diagnostic (requester-authorized continuation of exp_e6b0965af3): one frozen 32x8 GSM8K train batch, 8 minibatches x 2 epochs, <=16 updates, 12-minute cap. Offline replay of t…
Artifacts
correctness_preflight
{'required': True, 'paths': [{'id': 'grpo_dppo_custom_loss', 'status_note': 'Spec revisions are UNRESOLVED placeholders; real pins below. Offline replay only (CPU torch 2.15.0, saved fixture); no new tolerance chosen.', 'fixed_input': {'sha256': 'aff0d4c84ff104219bf365a13dcb0de1c1cfc59b833ac72cb357c46e29a83f13', 'description': 'run_19badd842c preflight_fixture.json (1024 loss tokens, synthetic stale)'}, 'baseline_revision': 'UNRESOLVED: pin tinker-cookbook commit whose stock ppo/importance-sampling loss defines GRPO clipping', 'reference_revision': 'UNRESOLVED: pin commit of the official DPPO implementation for arXiv 2602.04879', 'pinned_baseline': 'modal-projects/spindle@e58ebef130b4cd02de6880e62d27ec358ff7132c forward_backward.py::_loss', 'pinned_reference': 'sail-sg/Stable-RL@c6af1d119c0fe30d16dd9a837d35fdf695acd226 core_algos.compute_policy_loss_with_mask(dppo_binary_tv)', 'implementation_revision': 'losses_patched.py sha256 bceaad747abb77f986d71bdbc8e902d0a0fa3d77bd827401f731a4e663d00a6c (token_terms identical to 36e74485...)', 'precision': 'float32 loss path, CPU', 'normalization': 'sum over masked loss tokens (batch_num_tokens=1, dp_size=1)', 'errors': {'baseline_loss': {'max_abs_error': 0.0, 'reference_max_abs': 429.46087646484375, 'relative_l2_error': 0.0, 'cosine_similarity': 1.0}, 'baseline_gradient': {'max_abs_error': 0.0, 'reference_max_abs': None, 'relative_l2_error': 0.0, 'cosine_similarity': 1.0}, 'reference_loss': {'max_abs_error': 3.0517578125e-05, 'reference_max_abs': 420.54302978515625, 'relative_l2_error': 7.256e-08, 'cosine_similarity': 1.0}, 'reference_gradient': {'max_abs_error': 0.0, 'reference_max_abs': None, 'relative_l2_error': 0.0, 'cosine_similarity': 1.0}}, 'lr_scaling': []}]}
correctness_validation
{'status': 'unverified', 'required': True, 'checks': [{'path': 'grpo_dppo_custom_loss', 'check': 'baseline_loss', 'status': 'passed', 'tolerance': {'atol': 1e-05, 'rtol': 0.0001}, 'measured': {'max_abs_error': 0.0, 'reference_max_abs': 429.46087646484375, 'relative_l2_error': 0.0, 'cosine_similarity': 1.0}}, {'path': 'grpo_dppo_custom_loss', 'check': 'baseline_gradient', 'status': 'unverified', 'tolerance': {'atol': 1e-05, 'rtol': 0.001, 'min_cosine': 0.9999}, 'measured': {'max_abs_error': 0.0, 'reference_max_abs': None, 'relative_l2_error': 0.0, 'cosine_similarity': 1.0}}, {'path': 'grpo_dppo_custom_loss', 'check': 'reference_loss', 'status': 'passed', 'tolerance': {'atol': 1e-05, 'rtol': 0.0001}, 'measured': {'max_abs_error': 3.0517578125e-05, 'reference_max_abs': 420.54302978515625, 'relative_l2_error': 7.256e-08, 'cosine_similarity': 1.0}}, {'path': 'grpo_dppo_custom_loss', 'check': 'reference_gradient', 'status': 'unverified', 'tolerance': {'atol': 1e-05, 'rtol': 0.001, 'min_cosine': 0.9999}, 'measured': {'max_abs_error': 0.0, 'reference_max_abs': None, 'relative_l2_error': 0.0, 'cosine_similarity': 1.0}}], 'reason': 'grpo_dppo_custom_loss: measured baseline_gradient errors; grpo_dppo_custom_loss: measured reference_gradient errors', 'evidence_source': 'executor report; remote execution is not attested', 'applicability': 'required', 'reported_verdict': 'inconclusive'}
decision_validation
{'status': 'uncertain', 'issues': ['Decision evidence was not recorded.'], 'criteria': [], 'outcome_reason': 'insufficient_evidence', 'unit': 'fixed_checkpoint_items', 'n_seeds': 1, 'alpha': 0.05, 'method': 'paired_hoeffding', 'applicability': {'check': 'decision_rule', 'status': 'required', 'enforce': True, 'reasons': ['experiment_features: paired_comparison is true'], 'features': {'paired_comparison': True, 'adaptive_selection': False}}}
feasibility
{'status': 'unknown', 'problems': ['Re-projection required: dataset changed or was not recorded', 'Re-projection required: steps changed or was not recorded', 'Re-projection required: batch_size changed or was not recorded', 'Re-projection required: max_seq_len changed or was not recorded', 'Re-projection required: arms changed or was not recorded', 'Re-projection required: n_examples changed or was not recorded', 'Primary endpoint changed or was not recorded', 'Unknown timing phase: sampling', 'Invalid measured timing/counts for evaluation'], 'wall_seconds': None, 'gpu_hours': None, 'known_gpu_hours': 0, 'phases': {'setup': {'wall_seconds': 153.31, 'planned_units': 1, 'planned_samples': 0.0, 'gpu_hours': None}, 'training': {'wall_seconds': 195.0, 'planned_units': 16, 'planned_samples': 512.0, 'gpu_hours': None}, 'recovery': {'wall_seconds': 249.2, 'gpu_hours': None}}, 'generation_counts': None, 'generation_applicability': {'check': 'generation_audit', 'status': 'not_applicable', 'enforce': False, 'reasons': ['spec.generation_audit opts out and no feature it covers is true'], 'features': {'generated_training_data': False}}, 'record': {'config': {'base_model': 'Qwen/Qwen3.5-2B', 'lora_rank': 32, 'arm': 'dppo_diag', 'loss': 'dppo_binary_tv', 'delta': 0.15, 'clip_ratio_c': 10.0, 'ppo_eps_counterfactual': 0.2, 'optimizer': {'type': 'adamw', 'learning_rate': 2e-05, 'beta1': 0.9, 'beta2': 0.95, 'eps': 1e-08, 'weight_decay': 0.0, 'grad_clip_norm': 0.0}, 'renderer': 'qwen3_5_disable_thinking', 'temperature': 1.0, 'max_tokens': 512, 'groups': 32, 'group_size': 8, 'data_seed': 0, 'batch_index': 0, 'minibatches': 8, 'groups_per_minibatch': 4, 'epochs': 2, 'max_updates': 16, 'loss_normalization': 'token sum (Spindle stock _loss convention)', 'init_state_path': 'tinker://21e32976f18f971463410adff4e364e4/weights/rx19badd842c_init', 'endpoint': 'shared Spindle (TINKER_BASE_URL)', 'sdk': {'tinker': '0.37.0', 'torch': '2.15.0'}, 'wall_cap_seconds': 720, 'n_eval': 0}, 'effective_config': {'base_model': 'Qwen/Qwen3.5-2B', 'lora_rank': 32, 'arm': 'dppo_diag', 'loss': 'dppo_binary_tv', 'delta': 0.15, 'clip_ratio_c': 10.0, 'ppo_eps_counterfactual': 0.2, 'optimizer': {'type': 'adamw', 'learning_rate': 2e-05, 'beta1': 0.9, 'beta2': 0.95, 'eps': 1e-08, 'weight_decay': 0.0, 'grad_clip_norm': 0.0}, 'renderer': 'qwen3_5_disable_thinking', 'temperature': 1.0, 'max_tokens': 512, 'groups': 32, 'group_size': 8, 'data_seed': 0, 'batch_index': 0, 'minibatches': 8, 'groups_per_minibatch': 4, 'epochs': 2, 'max_updates': 16, 'loss_normalization': 'token sum (Spindle stock _loss convention)', 'init_state_path': 'tinker://21e32976f18f971463410adff4e364e4/weights/rx19badd842c_init', 'endpoint': 'shared Spindle (TINKER_BASE_URL)', 'sdk': {'tinker': '0.37.0', 'torch': '2.15.0'}, 'wall_cap_seconds': 720, 'n_eval': 0}, 'primary_endpoint': 'gsm8k_test_greedy_exact_match_accuracy@final_checkpoint (1319 items) — not evaluated in this diagnostic', 'source': 'diag-events.jsonl', 'measurements': {'setup': {'seconds': 153.31, 'units': 1, 'planned_units': 1, 'samples': 0, 'gpu_count': None, 'allocation_source': None}, 'sampling': {'seconds': 114.19, 'units': 256, 'planned_units': 256, 'samples': 256, 'gpu_count': None, 'allocation_source': None}, 'training': {'seconds': 195.0, 'units': 16, 'planned_units': 16, 'samples': 512, 'gpu_count': None, 'allocation_source': None}, 'evaluation': {'seconds': 0, 'units': 0, 'planned_units': 0, 'samples': 0, 'gpu_count': None, 'allocation_source': None}}, 'generation': None, 'recovery': {'seconds': 249.2, 'gpu_count': None, 'allocation_source': None}}, 'recorded_at': 1793746408.889922}
feasibility_required
True
generation_audit_validation
{'status': 'not_applicable', 'reasons': ['spec.generation_audit opts out and no feature it covers is true'], 'applicability': {'check': 'generation_audit', 'status': 'not_applicable', 'enforce': False, 'reasons': ['spec.generation_audit opts out and no feature it covers is true'], 'features': {'generated_training_data': False}}}
initialization_provenance
{'version': 1, 'arms': [{'arm': 'dppo_diag', 'initialization': {'base_checkpoint': 'Qwen/Qwen3.5-2B', 'checkpoint_uri': 'tinker://21e32976f18f971463410adff4e364e4/weights/rx19badd842c_init', 'loaded_checkpoint_uri': 'tinker://21e32976f18f971463410adff4e364e4/weights/rx19badd842c_init', 'load_succeeded': True, 'source': 'server_response', 'checkpoint_scope': 'full_initialization', 'checkpoint_immutable': None, 'adapter_sha256': None, 'evidence_ref': 'diag-events.jsonl stage=restored (model_id daecfa24-b646-4b82-b337-4447b0e95af0:train:0)', 'before_first_update': True}, 'restore_count': 1, 'optimizer_checks': [{'after_restore': 1, 'before_next_update': True, 'requested': 'adam', 'requested_config': {'learning_rate': 2e-05, 'beta1': 0.9, 'beta2': 0.95, 'eps': 1e-08}, 'actual': 'adamw', 'actual_config': None, 'source': 'server_response', 'evidence_ref': 'diag-events.jsonl stage=restored get_info optimizer_config'}], 'initial_output': {'available': True, 'batch_sha256': '768e9246af697da5c82bfa9ea0c6a21963d5c47371d2e731c99eae9bccd21c10', 'output_sha256': '10e156f726b8feacca2c615c4257f50c7cfa06f63a56b676bd9dfbb60806c1da', 'scope': 'float32 trainer logprobs on loss tokens of 8 sentinel datums (not full logits)', 'evidence_ref': 'diag-events.jsonl stage=sentinel tag=init', 'before_first_update': True}}], 'note': 'Single diagnostic arm; no paired group. Checkpoint immutability and adapter hash not server-verifiable; optimizer hyperparameters only declared per optim_step.'}
launch
{'experiment_id': 'exp_e6b0965af3', 'context_status': 'snapshotted', 'superseded': False, 'mode': 'redo', 'purpose': "Repeat the selected saved experiment version to check reproducibility. This repetition tests that version's hypothesis and methods."}
launch_instructions
{'name': 'launch-instructions-b42946cbc224.md', 'sha256': 'b42946cbc2240a8b8fe9329beb2d97f7b8a51be4af735c400d504c87b0fead51', 'url': 'https://ta-01m6fva5db548f5skm214fepcr-8000-oah9l7x00jpo7186eb6cix5v0.w.modal.host/api/runs/run_21b1618aa7/evidence/b42946cbc2240a8b8fe9329beb2d97f7b8a51be4af735c400d504c87b0fead51'}
reported_outcome_reason
insufficient_evidence
reported_verdict
inconclusive
restoration_validation
{'status': 'unverified', 'required': True, 'checks': [{'check': 'grpo_seed0', 'status': 'unverified', 'reason': 'grpo_seed0: missing or duplicate arm evidence.'}, {'check': 'dppo_seed0', 'status': 'unverified', 'reason': 'dppo_seed0: missing or duplicate arm evidence.'}], 'reason': 'grpo_seed0: missing or duplicate arm evidence.', 'applicability': 'required'}
Agent trace full trace with commands in Devin ↗
Details
run
run_21b1618aa7
backend
Devin
session
devin-040a00ddbd1243429c377db1089c152c
experiment
exp_e6b0965af3
launch
redo
created
Nov 3, 22:36 UTC
started
Nov 3, 22:36 UTC
finished
Nov 3, 22:53 UTC
duration
17m 08s
GPU-h budget
2.0
wall budget
120 min
base model
Qwen/Qwen3.5-2B
training
RL
dataset
gsm8k
result
final
outcome reason
insufficient evidence
Other runs of this idea

Submit an idea

Most ideas come in from Slack threads. Use this when yours is not in one yet; the triage agent picks it up.