lyhisme's picture
Add OraRL training and evaluation code
53c10a4 verified
Raw
History Blame Contribute Delete
4.95 kB
# Copyright 2026 The OraRL Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Typed configuration for the dependency-light algorithm helpers."""
from __future__ import annotations
import math
from dataclasses import dataclass, field
def _positive_finite(name: str, value: float) -> None:
if not math.isfinite(value) or value <= 0.0:
raise ValueError(f"{name} must be finite and positive, got {value}.")
def _nonnegative_finite(name: str, value: float) -> None:
if not math.isfinite(value) or value < 0.0:
raise ValueError(f"{name} must be finite and non-negative, got {value}.")
@dataclass(frozen=True, slots=True)
class GRPOConfig:
"""Configuration for the unmodified group-relative baseline."""
normalize: bool = True
eps: float = 1e-6
def __post_init__(self) -> None:
_positive_finite("eps", self.eps)
@dataclass(frozen=True, slots=True)
class DirectionalGainConfig:
"""Configuration for OraRL's positive-direction utility transform."""
gamma: float = 0.25
eps: float = 1e-6
def __post_init__(self) -> None:
_nonnegative_finite("gamma", self.gamma)
_positive_finite("eps", self.eps)
@dataclass(frozen=True, slots=True)
class DetachedOracleConfig:
"""Configuration for the detached annotation advantage."""
scale: float = 2.0
gap_beta: float = 2.0
match_best_ratio: float | None = 1.2
match_best_min: float = 0.05
match_best_max: float = 1.0
eps: float = 1e-6
def __post_init__(self) -> None:
_nonnegative_finite("scale", self.scale)
_positive_finite("gap_beta", self.gap_beta)
_positive_finite("eps", self.eps)
_nonnegative_finite("match_best_min", self.match_best_min)
_nonnegative_finite("match_best_max", self.match_best_max)
if self.match_best_min > self.match_best_max:
raise ValueError("match_best_min must not exceed match_best_max.")
if self.match_best_ratio is not None:
_positive_finite("match_best_ratio", self.match_best_ratio)
@dataclass(frozen=True, slots=True)
class SelectionConfig:
"""Strict sign-balanced selection settings.
The annotation row occupies one slot in ``keep_per_group``. The remaining
slots are assigned to the positive and negative policy quotas.
"""
keep_per_group: int = 4
positive_quota: int = 1
negative_quota: int = 2
world_size: int = 1
def __post_init__(self) -> None:
if self.keep_per_group < 2:
raise ValueError("keep_per_group must leave room for policy and oracle rows.")
if self.positive_quota < 0 or self.negative_quota < 0:
raise ValueError("selection quotas must be non-negative.")
if self.positive_quota + self.negative_quota + 1 != self.keep_per_group:
raise ValueError(
"positive_quota + negative_quota + one oracle slot must equal keep_per_group."
)
if self.world_size <= 0:
raise ValueError("world_size must be positive.")
@dataclass(frozen=True, slots=True)
class CorrectionConfig:
"""Settings for post-selection mean and RMS correction."""
rms_match: bool = True
rms_min_scale: float = 0.25
sigma_policy_floor: float = 1e-3
eps: float = 1e-6
def __post_init__(self) -> None:
if not 0.0 <= self.rms_min_scale <= 1.0:
raise ValueError("rms_min_scale must be in [0, 1].")
_nonnegative_finite("sigma_policy_floor", self.sigma_policy_floor)
_positive_finite("eps", self.eps)
@dataclass(frozen=True, slots=True)
class PostSelectionReference:
"""Pre-selection statistics for one rollout group."""
policy_rms: float
sigma_policy: float
policy_rows: int
def __post_init__(self) -> None:
_nonnegative_finite("policy_rms", self.policy_rms)
_nonnegative_finite("sigma_policy", self.sigma_policy)
if self.policy_rows < 1:
raise ValueError("policy_rows must be positive.")
@dataclass(frozen=True, slots=True)
class OraRLConfig:
"""Paper recipe defaults for the four numerical stages."""
directional_gain: DirectionalGainConfig = field(default_factory=DirectionalGainConfig)
detached_oracle: DetachedOracleConfig = field(default_factory=DetachedOracleConfig)
selection: SelectionConfig = field(default_factory=SelectionConfig)
correction: CorrectionConfig = field(default_factory=CorrectionConfig)