Skip to content

【Task.31】feat(sft): add reward modeling and preference evaluation - #258

Open
A-Words wants to merge 31 commits into
redai-studio:mainfrom
A-Words:feat/task-31-reward-modeling
Open

【Task.31】feat(sft): add reward modeling and preference evaluation#258
A-Words wants to merge 31 commits into
redai-studio:mainfrom
A-Words:feat/task-31-reward-modeling

Conversation

@A-Words

@A-Words A-Words commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What

本堆叠 PR 增加 Reward Modeling,以及 Task 31 最终可复现的偏好评测链路。

  • 增加 RM 专用 scalar output head,不复用 critic/value-head checkpoint 语义。
  • 在 packed THD 输入下 pool completion 的末端 token,同时保留从 prompt context 传播的 causal gradient。
  • 增加 pairwise logistic RM loss,以及 chosen/rejected score、margin、accuracy 和 tie 指标。
  • 增加严格的 RM checkpoint metadata、双向 cross-load 拒绝、Megatron checkpoint format 处理、resume 支持和 HF export 拒绝。
  • 增加 DPO/RM 共用的固定 512-pair held-out evaluator,包括稳定 pair identity、batch-plan 校验、逐 pair 产物和确定性 FP64 bootstrap lower bound。
  • 增加公开的 Qwen3-0.6B/UltraFeedback RM recipe、文档,以及聚焦 loss/checkpoint/evaluation 测试。
  • 交付 RFC 【Task.31】DPO / Reward Modeling - RFC #208 要求的 DPO/RM 200-step 曲线、DP=1/DP=2 结果、环境清单和复现产物。

Why

Task 31 同时要求离线 DPO 和基于公开 chosen/rejected 数据训练 Reward Model。PR1 建立共享偏好数据与 DPO contract;本 PR 在不增加第二套 trainer 的前提下,补充 RM 专用模型/checkpoint lifecycle 和最终 held-out 验收证据。

实现遵循 RFC #208Task 31 要求

Relates to #208.

Depends on #257.

本 PR 堆叠在 #257(PR1)之上。请优先审阅 PR2-only range:0e8c334..b57a181。PR1 合并后,本 PR 将重放到最新 main,使最终 diff 仅保留 Reward Modeling 和共享偏好评测部分。

How

Actor 在 --sft-objective reward_model 下安装专用 scalar output layer。通过 packed THD metadata 选择每个 branch 最后一个有效 completion 位置,并产生一个 scalar score。Pairwise logistic objective 按全局 pair 数归一化,评测过程保留显式 pair ID 和 chosen/rejected identity。

Checkpoint metadata 区分 causal-LM、DPO、Reward Model 和 critic/value-head 格式。加载过程双向拒绝不兼容 objective,支持 Megatron distributed checkpoint layout,在 resume 时恢复 RM head,并拒绝当前不受支持的 HF export。

共享 evaluator 在 step 0 和 final evaluation 固定使用 manifest 中相同的 512 个 pair。写出逐 pair JSONL 和 10,000-replicate FP64 PCG64 paired-bootstrap summary 前,会校验预处理 identity、pair 顺序、截断结果和 DP/micro-batch plan。

Testing

  • pre-commit run --all-files passes
  • Tests pass (pytest tests/)
  • New tests added (if applicable)
  • Documentation updated (if applicable)

b57a1818f2f80d28d0ad650b314eb4d1c82df3aa 的标准容器中执行:

pytest tests/

873 passed, 7 skipped, 51 warnings in 193.75s (0:03:13)
pytest tests/engine/sft/ \
  tests/backends/megatron/test_sft_train_data_fields.py \
  tests/utils/training/test_preference_utils.py \
  tests/backends/megatron/test_preference_batching.py \
  tests/backends/megatron/test_dpo_loss.py \
  tests/backends/megatron/test_reward_model_loss.py \
  tests/backends/megatron/test_reward_model_checkpoint.py

179 passed
pre-commit run --all-files
All hooks passed

Reward Model 在固定 512-pair held-out 集合上的结果:

运行 Steps Step-0 loss Final loss Chosen Rejected Margin Tie-aware accuracy Bootstrap lower 95%
RM DP=1 200 0.857369 0.631391 1.202856 -0.315390 1.518246 0.705078 0.671875
RM DP=2 200 0.857623 0.643885 1.070853 -0.429695 1.500548 0.690430 0.654297
  • 两个运行均满足 final loss < step-0 loss、chosen > rejected、margin > 0、accuracy > 0.55 和 bootstrap lower-95 > 0.50。
  • RM DP=1 resume 从 iteration 199 恢复,完成下一 step,并保持稳定的 held-out 表现:accuracy=0.708984lower-95=0.675781
  • DP=2 日志明确记录 SeqlenBalancedSampler with dp_size=2,两张 A100 均有实际活动。
  • 最终报告同时包含 RFC 交付计划要求的 DPO DP=1/DP=2 200-step 结果。

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • CI/CD or build changes

Screenshots / Logs

本 PR 附带 Reward Model DP=1/DP=2 训练曲线和脱敏后的可复现性证据包。证据包包含:

  • 200-step 训练日志、scalar CSV 和 GPU telemetry;
  • step 0/final 逐 pair 评测结果、冻结 batch plan 和 bootstrap summary;
  • DP=1 resume 日志和 Reward Model checkpoint metadata 验证结果;
  • 固定环境、模型/data revision、镜像身份和逐文件 SHA-256 清单。

公开附件不包含 checkpoint、optimizer state、模型权重、完整数据集、凭据或机器专属标识。DPO 的完整训练证据由 PR1 提供;本 PR 的最终报告保留 DPO/RM 汇总结果用于 Task 31 验收。

测量环境使用 ghcr.io/redai-infra/relaxrl:dev-20260723-8cc1e8fd;证据采集时从 GHCR 解析的 manifest digest 为 sha256:8dc39af377a570e6cd7ec88c8b7fcd44c1eb820111e9d2069f1c7c3024b2ea23。由于顶层 flash_attn_interface import 不可用,Transformer Engine 明确回退到 native attention。这些结果用于证明 correctness 和同环境 DP 行为,不用于声明 FlashAttention 性能。

task31-pr2-rm-evidence-public.tar.gz

task31-pr2-rm-dp1-curves task31-pr2-rm-dp2-curves

A-Words added 22 commits August 7, 2026 20:08
# 🐛 Bug Fix

## Reject --ref-load under preference objectives

- Standard DPO snapshots the frozen reference from the initialized
  policy and rebuilds it from --hf-checkpoint on resume; --ref-load
  was silently ignored on that path while also rerouting the bridge
  mode policy-init fallback, so validate_preference_args now fails
  fast when it is set (RFC redai-studio#208: --ref-load is not the v1 DPO
  reference source)
- Drop the misleading --ref-load from the DPO recipe; behavior is
  unchanged because the bridge fallback already resolves to the same
  HF checkpoint

---

# ♻️ Refactor

## Deduplicate the preference-mode predicate

- Route data.py get_data_iterator and loss.py loss_function through
  is_preference_mode() instead of inline loss_type/sft_objective
  checks, keeping objective dispatch on a single source of truth

---

# ✅ Tests

## Cover --ref-load rejection

- Parametrize standard and reference-free DPO rejection cases in
  test_preference_runtime.py
# ♻️ Refactor

## Remove unreachable DPO loss fallback

- Drop the dead `0 * logits.sum()` branch in dpo_loss_function:
  build_preference_pair_indices already raises on an empty micro-batch

## Attach stable reason codes at preference raise sites

- Introduce _PreferenceRowError carrying an explicit reason_code from
  every normalization/split/truncation rejection; message matching in
  _classify_preference_error remains only as a fallback for errors
  raised outside the module, so reworded messages can no longer
  silently degrade rejections to "schema"

---

# ⚡ Performance

## Merge DP pair-count collectives

- Reduce _get_preference_data_iterator from three all_reduces to two
  by riding MIN/MAX on a single MAX all_reduce over [count, -count]

## Skip redundant same-tag model restores

- _switch_model now returns early when the target tag is already
  active, eliminating the duplicate full-weight CPU->GPU restore after
  the ref-forward finally block; paths that deliberately dirty weights
  already clear the tag first (covered by the injected-loader-failure
  reference integrity test)

---

# 📝 Documentation

## Note probe bitwise-determinism prerequisite

- en/zh DPO guides now state that the reference probe digest assumes
  an identical GPU/driver/image/kernel stack on resume, and that a
  probe mismatch signals environment drift rather than data corruption

## Justify the checkpoint-save barrier

- Comment that the post-sidecar barrier is functional (peers must not
  pass before rank 0 persists the identity file), and use an explicit
  gloo group for the rank-0 check per distributed code rules
# 🐛 Bug Fix

## Preserve standard DPO likelihood semantics

- Reject non-unit or non-finite rollout temperatures before actor construction
- Replace CUDA-to-Python condition checks with device-side asynchronous assertions
- Consolidate finite-value validation on the final DPO logits

## Validate preference data preparation inputs

- Validate message objects, roles, and string content on both preference branches
- Classify non-object source rows as schema rejections

---

# ✅ Tests

## Cover validation regressions

- Test finite and non-unit temperature failures
- Verify CUDA conditions avoid Python boolean conversion
- Cover malformed chosen and rejected message schemas
# 🐛 Bug Fix

## Pin standard DPO frozen reference provenance

- Resolve the reference from the declared repository and revision in the configured HF checkpoint directory
- Require Hugging Face local metadata to verify the pinned snapshot without downloading during actor startup
- Rebuild the frozen reference from that verified directory for both fresh starts and resumes
- Fail clearly when the configured checkpoint is unavailable, unverified, or resolves elsewhere

---

# 📝 Documentation

## Document pinned local model preparation

- Show the fixed-revision hf download command used by the public DPO recipe

---

# ✅ Tests

## Cover local reference resolution

- Verify repository, revision, local directory, and local-only cache resolution
- Verify missing metadata and mismatched resolved directories fail before model loading
# 🐛 Bug Fix

## Require pinned local snapshot metadata

- Require an immutable 40-character commit SHA for standard DPO references
- Verify Hugging Face local-dir tree metadata before accepting the configured checkpoint
- Reject copied or incomplete local snapshots that lack pinned provenance metadata

---

# ✅ Tests

## Cover metadata validation

- Exercise the pinned tree lookup and missing-metadata rejection paths
- Document the full commit-SHA requirement for the DPO recipe
# 🐛 Bug Fix

## Validate pinned Hugging Face local snapshots

- Parse the per-file metadata format used by pinned huggingface_hub 1.7.2
- Require every reference file to match the configured commit revision
- Verify regular Git files with Git blob SHA-1 and LFS files with SHA-256
- Stream large-file hashing to keep reference validation memory bounded

---

# ✅ Tests

## Cover reference provenance and integrity

- Exercise Git and LFS ETag validation
- Reject missing or mismatched local metadata
- Reject replaced weights even when their original mtime is restored
# 🐛 Bug Fix

## Enforce local reference completeness

- Require a supported single-file weight or standard Transformers weight index
- Validate non-empty index weight maps, safe shard paths, and format-specific suffixes
- Reject indexes that reference missing model shards

---

# ✅ Tests

## Cover missing reference weights

- Accept a complete multi-shard safetensors index
- Reject snapshots without a supported weight entry point
- Reject a deleted shard even when its local metadata is also removed
# 🐛 Bug Fix

## Move preference scheduling to the CPU control plane

- Replace CUDA count reductions and scalar readbacks with one DP-Gloo control gather
- Preserve pair-row, global-denominator, and micro-batch-count agreement across ranks
- Require Gloo process groups for preference objectives and enable them in the DPO recipe

---

# ⚡ Performance

## Remove hot-path GPU synchronization

- Eliminate all item calls and control tensors from the preference iterator
- Avoid GPU-to-CPU synchronization for train, reference, and evaluation iterators

---

# ✅ Tests

## Guard preference iterator contracts

- Cover unequal DP pair-row rejection through the Gloo control path
- Assert the iterator contains no item or all-reduce scalar readback path
# ♻️ Refactor

## Keep one reference identity path

- Remove the unused helper that reads the already-backed-up ref weights
- Preserve rebuild validation and backup ordering without duplicate digest work

---

# ✅ Tests

- Pass the full pre-commit suite
- Compile the Megatron actor module
# 🐛 Bug Fix

## Enforce reward-model runtime contracts

- Avoid CUDA synchronization in the margin finite check
- Validate terminal masks, tokens, packed segments, and pair identity
- Restore step-zero and unconditional final preference evaluation

## Harden Megatron checkpoint resume

- Persist and validate reward-model objective, role, and scalar-head metadata
- Reject incompatible actor, SFT, critic, and partial-state resumes
- Preserve restored optimizer, scheduler, RNG, and next-step behavior

---

# ⭐ Feature

## Produce reproducible preference-evaluation evidence

- Freeze the 512-pair probe and canonical batch-plan hashes
- Write per-pair JSONL and PCG64 paired-bootstrap summaries
- Align reward-model metrics and recipe batch size with RFC values

---

# ✅ Tests

## Cover reward-model loss, checkpoint, and evaluation behavior

- Add focused checkpoint and loss contract suites
- Test bootstrap digests, artifacts, pair reordering, and eval scheduling

---

# 📝 Documentation

## Document reward-model recipes and acceptance artifacts

- Describe checkpoint compatibility and required evidence outputs
# 🐛 Bug Fix

## Break the step-zero evaluation wait cycle

- Treat the first preference eval chunk as the colocate actor's initial input
- Preserve the baseline barrier before exposing the step-zero train partition
- Keep causal SFT, RL, and resumed steps gated on their normal train partitions

---

# ✅ Tests

## Cover colocate partition readiness

- Verify preference step zero accepts only the first baseline eval chunk
- Verify causal SFT and resumed preference steps retain existing readiness behavior
# 🐛 Bug Fix

## Keep preference outputs bound to original pairs

- Restore per-sample forward outputs whenever the iterator supplies a packed index schedule
- Validate packed schedules before reordering scores and log probabilities

## Preserve the complete fixed probe

- Consume all 512 preference pairs with capacity-bounded partial chunks
- Use actual per-rank and global batch sizes for final chunks
- Reject chunk plans that cannot divide cleanly across data-parallel ranks

## Correct causal SFT prediction cadence

- Evaluate prediction intervals against completed optimizer steps
- Prevent baseline and pre-boundary prediction triggers

---

# ✅ Tests

## Cover ordering, chunking, and scheduling regressions

- Test packed output restoration independently of the dynamic-batch flag
- Cover probe chunk plans across small, uneven, exact, and oversized batch sizes
- Verify completed-step prediction boundaries
# 🐛 Bug Fix

## Use completed steps for every SFT post-train evaluation

- Map zero-based SFT rollout IDs to completed optimizer-step values
- Apply the same mapping to sync, hybrid, and fully-async training paths
- Preserve zero-based rollout evaluation steps for RL and step zero for the preference baseline

---

# ✅ Tests

## Cover SFT and RL evaluation-step mapping

- Verify initial and interval-boundary rollout IDs for both training modes
# 🐛 Bug Fix

## Mirror Megatron checkpoint tracker semantics

- Resolve release metadata to the official release checkpoint directory
- Preserve iteration and checkpoint-step handling for non-release trackers
- Honor an explicitly requested checkpoint step of zero
- Reject invalid and negative checkpoint metadata with explicit errors

## Preserve legacy checkpoint compatibility

- Detect distributed checkpoints before loading common state metadata
- Delegate legacy SFT, DPO, and PPO checkpoints to the upstream Megatron loader
- Reject legacy and model-only release checkpoints for reward-model resumes

---

# ✅ Tests

## Cover checkpoint path and format contracts

- Test release, zero, iteration, explicit step zero, checkpoint-step, invalid, and negative tracker values
- Verify non-RM legacy checkpoints bypass distributed metadata and reach the upstream loader
- Verify RM legacy and release checkpoints fail before checkpoint state is loaded
# 🐛 Bug Fix

## Read pair identity after TransferQueue expansion

- Prefer pair-level IDs from expanded preference data during evaluation
- Keep raw pair-row compatibility for direct callers and test fixtures
- Reject missing pair-level identity instead of using duplicated branch IDs

---

# ✅ Tests

## Cover raw and expanded preference schemas

- Verify pair IDs are extracted once per pair from both data shapes
- Verify expanded data removes raw IDs and preserves branch-level identity separately
- Reject data that exposes only branch-level pair IDs
# 🐛 Bug Fix

## Reject unsupported reward-model exports

- Fail before Serve startup when reward modeling is combined with --save-hf
- Direct users to native Megatron checkpoints that preserve the scalar reward head
- Keep existing Hugging Face export behavior for DPO and causal SFT

---

# ✅ Tests

## Cover export configuration boundaries

- Verify reward modeling rejects configured HF export paths
- Verify reward modeling without HF export remains valid
- Verify DPO and causal SFT continue to accept HF export paths
# 🐛 Bug Fix

## Keep the PR2 recipe compatible with its PR1 base

- Enable Gloo process groups required by the preference iterator control plane
- Prevent reward-model training from failing preference argument validation after the PR1 rebase
# 🐛 Bug Fix

## Keep preference eval chunk plans consistent

- Validate the frozen 512-pair probe before optional artifact handling
- Reject nonconforming eval sets before any TransferQueue partition is pushed
- Verify preference packing preserves every expected probe pair

---

# ✅ Tests

## Cover evaluation without artifact output

- Reject non-512 probes when --save is unset or empty
- Allow the frozen 512-pair probe when artifact output is disabled
@A-Words A-Words changed the title feat(sft): add reward modeling and preference evaluation 【Task.31】feat(sft): add reward modeling and preference evaluation Aug 11, 2026
@A-Words
A-Words marked this pull request as ready for review August 11, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant