Alignment¶
Functional, mechanism-agnostic primitives for DP-safe preference learning
and supervised fine-tuning: per-example SFT and DPO losses, per-sequence
log-probability helpers, batch collators, dataset transforms,
reference-model handling, and reward metrics. Every public symbol is a
pure function or a factory returning a callable, so each composes under
vmap(grad(...)) — the per-example differentiation DP-SGD and DP-FTRL
clipping is built on. Import everything from the opake.alignment.*
façades; the mechanism and optimizer are chosen at the call site, never
inside this package. For end-to-end walkthroughs see the
SFT and DPO guides.
Overview¶
Losses¶
Per-example loss functions. SFT losses return the per-example mean over an
example's own non-ignored tokens (a DP-safe divisor, not a batch
aggregate). DPO per-pair heads take (chosen_logratio, rejected_logratio,
*, beta, ...) and return a per-example scalar; a DPO loss is
head(sequence_logp(...) - ref_logp, ...). Eager losses take logits;
the fused_* twins take hidden_states + the lm_head weight and never
materialize (T, V) logits (CUDA + half-precision fused kernel with
opake-alignment[patches], eager fallback otherwise).
SFT:
nll_loss()(opake.alignment.sft.loss) — per-example causal-LM cross-entropy with a per-example token-count divisor.dft_loss()(opake.alignment.sft.loss) — Dynamic Fine-Tuning: NLL weighted by the detached softmax probability of each target token.fused_nll_loss()(opake.alignment.sft.loss) — memory-efficientnll_lossover hidden states +lm_headweight.fused_dft_loss()(opake.alignment.sft.loss) — memory-efficientdft_lossover hidden states +lm_headweight.
DPO per-pair heads (opake.alignment.dpo.loss):
sigmoid_loss()— standard DPO sigmoid (logistic) loss; the default.hinge_loss()— DPO hinge loss.robust_loss()— robust (label-smoothed) DPO loss.ipo_loss()— IPO loss (Azar 2024).discopop_loss()— DiscoPOP loss.chosen_nll_loss()— chosen-completion NLL regularizer for MPO/RPO blends.apo_zero_loss()/apo_down_loss()— APO-zero / APO-down (arXiv:2408.06266).exo_loss()— EXO pairwise loss (Towards Efficient Exact Optimization of Language Model Alignment).nca_loss()— NCA pairwise loss.bco_loss()— BCO pairwise loss.sppo_loss()— SPPO hard-label loss.
Reference-free heads (opake.alignment.dpo.loss) — no
reference model; score the policy log-prob directly:
simpo_loss()— SimPO: length-normalized sigmoid with a target margin γ (Meng 2024).odds_ratio_loss()— ORPO odds-ratio loss on length-normalized log-probs (Hong 2024); pair withchosen_nll_lossviampo_combine.
DPO log-ratio combinators (opake.alignment.dpo.loss)
for composite objectives:
f_divergence_remap()— remap a per-example log-ratio under the chosen f-divergence.f_divergence_logits()— form the remapped preference logits under an f-divergence.mpo_combine()— weighted sum of per-example loss tensors (MPO / TRLloss_type=list).wpo_weights()— per-example WPO weight (arXiv:2406.11827).ld_dpo_split()— length-desensitized sequence-logp split (LD-DPO, arXiv:2409.06411).
Primary papers for the implemented alignment objectives:
- DPO — Rafailov et al., Direct Preference Optimization: Your Language Model is Secretly a Reward Model.
- IPO — Azar et al., A General Theoretical Paradigm to Understand Learning from Human Preferences.
- DiscoPOP — Lu et al., Discovering Preference Optimization Algorithms with and for Large Language Models.
- SimPO — Meng et al., SimPO: Simple Preference Optimization with a Reference-Free Reward.
- ORPO — Hong et al., ORPO: Monolithic Preference Optimization without Reference Model.
- WPO — Zhou et al., WPO: Enhancing RLHF with Weighted Preference Optimization.
- LD-DPO — Liu et al., Length Desensitization in Direct Preference Optimization.
- APO — D'Oosterlinck et al., Anchored Preference Optimization and Contrastive Revisions: Addressing Underspecification in Alignment.
- NCA — Chen et al., Noise Contrastive Alignment of Language Models with Explicit Rewards.
- BCO — Jhunjhunwala et al., Binary Classifier Optimization for Large Language Model Alignment.
- SPPO — Wu et al., Self-Play Preference Optimization for Language Model Alignment.
Log-probabilities¶
sequence_logp()(opake.alignment.dpo.loss) — sum of completion-token log-probabilities per sequence; applies the causal-LM shift, masks to the completion span, and sums. Works per-example or on a batch axis. Passlength_normalized=Truefor the per-token mean rewardlog π(y)/|y|(SimPO / ORPO), orld_alphafor the LD-DPO length-desensitized split. Its compatibility argumentshared_prefix_lenis the paper's public length (the shorter completion length), not a token-identical prefix.fused_sequence_logp()(opake.alignment.dpo.loss) — memory-efficientsequence_logpover hidden states +lm_headweight (per-example only; samelength_normalizedoption).
Collators¶
Factory functions returning a collate(examples) callable.
language_modeling_collator()(opake.alignment.sft.collator) — SFT/causal-LM collation:input_ids/attention_mask/labels(B, L)with pad and (optionally) prompt tokens masked to-100, optionalcompletion_mask, keep-start truncation tomax_length. Output schemaLMBatch(opake.alignment.sft.collator.types).preference_collator()(opake.alignment.dpo.collator) — DPO collation in a(B, ...)layout: the chosen and rejectedinput_ids/attention_mask/completion_masktrios, plus optional precomputedref_chosen_logps/ref_rejected_logps(B,). Chosen and rejected stay separate (not TRL's concatenated(2B, L)) so one pair maps to one clipped gradient.extract_prompt()(opake.alignment.dpo.data) — split an implicit shared prompt out of a preference example before tokenizing.
Data¶
Shared chat-template data prep (opake.alignment.data).
Install a training chat template, then tokenize chat turns into input_ids +
a completion_mask for completion-only loss.
clone_chat_template()— copy a chat template (and its special tokens) from a source tokenizer onto a destination tokenizer and resize the model's embedding matrix to match. Returns the 3-tuple(model, tokenizer, added_token_ids)— the (possibly resized) model, the updated tokenizer, and the ids of any newly added special tokens (mark these trainable when the rest of the model is frozen). Call it beforemake_functional(the embedding resize must be captured in the functional snapshot).get_training_chat_template()— return a chat-template string carrying validated assistant-turn{% generation %}markers so the assistant-token mask is recoverable at tokenization time. Unsupported templates raise instead of falling back to a potentially incorrect mask.apply_chat_template_with_mask()— tokenize a chat conversation intoinput_ids+completion_mask(1on assistant tokens), the mask consumed bylanguage_modeling_collator(..., completion_only_loss=True).
Reference handling¶
Reference-model helpers for DPO. These run outside the per-example
vmap(grad(...)) region — a separate forward pass, or PEFT adapter
toggles.
compute_ref_logprobs_for_dataset()(opake.alignment.dpo.reference) — run the reference once over a dataset, attach per-example logp columns, and cache to a content-addressed.safetensorsfile keyed by dataset identity,cache_identity, andoutput_columns(a cache hit skips the forward). Cache directories and archives are owner-only; callers remove selected cache directories when their private per-example values are no longer needed. Setuse_cache=Falsefor non-reusable one-shot values; TR-DPO uses this mode for its seed columns. Under a live process group each rank scores its own shard and the columns are gathered back into dataset order, so the result matches a single-process run — see Across ranks.null_ref_context()(opake.alignment.dpo.reference) — context manager that turns a model into its own reference, dispatching over the separate-model /"ref"-adapter / disabled-adapter / no-op configurations.with_disabled_adapter()(opake.alignment.dpo.reference) — context manager that disables a PEFT adapter so the base model serves as the reference.ema_update_reference()(opake.alignment.dpo.reference) — TR-DPO: leafwise EMA step(1 - alpha) * ref + alpha * policyto move the reference toward the policy.
Metrics¶
These metrics are un-noised and outside Opake's DP accounting. See
Telemetry outside the guarantee.
Shared token metrics
(opake.alignment.metric):
mean_token_accuracy()— mean next-token argmax accuracy over the supervised (non-ignored) positions.entropy_from_logits()— mean per-token predictive entropy.
Preference reward telemetry (opake.alignment.dpo.metric):
reward_metrics()—rewards/chosen,rewards/rejected,rewards/accuracies,rewards/marginsfrom per-example log-ratios.
See also: Alignment overview, SFT end-to-end, DPO end-to-end.
API Documentation¶
loss
¶
opake.alignment.sft.loss façade — re-exports the SFT loss functions.
nll_loss / dft_loss (on logits) and their memory-efficient fused twins
fused_nll_loss / fused_dft_loss (on hidden states + lm_head weight).
dft_loss(logits, labels)
¶
Per-example DFT loss with detached softmax weighting and DP-safe divisor.
Shifts logits[..., :-1, :] against labels[..., 1:], computes
the per-token DFT loss -detach(softmax_prob) * logp, masks positions
where the shifted label is -100, and returns the per-example mean
over non-ignored tokens using a per-example divisor (DP-safe).
The weighting probability p is detached (stop-gradient), so the
gradient of the loss w.r.t. logits equals that of -p.detach() *
logp. This matches the TRL dft formula in spirit but uses the
DP-corrected per-example divisor instead of TRL's batch-level
num_items_in_batch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
Float tensor of shape |
required |
labels
|
Tensor
|
Integer tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Float tensor of shape |
Tensor
|
for a batch). Each entry is |
Tensor
|
the mean per-token loss over non-ignored shifted labels. |
Source code in packages/opake-alignment/src/opake/api/alignment/sft/loss/_dft.py
fused_dft_loss(hidden_states, lm_head_weight, labels)
¶
Memory-efficient :func:dft_loss over hidden states (fused, per-example).
Mathematically dft_loss(hidden_states @ lm_head_weight.T, labels), but on
CUDA + half precision with opake-alignment[patches] installed it computes
the detached-confidence-weighted per-token CE against the projection via the
patches fused linear-CE kernel in its use_token_scaling mode — no (T,
V) logits are materialised and the LSE is recomputed in the backward.
Otherwise it falls back to the eager form.
Per-example. Pass one example (T, H) and drive with
vmap(grad(...)) (the clipped_grad DP-SGD path); see
:func:~opake.api.alignment.sft.loss.fused_nll_loss for the per-example
contract.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_states
|
Tensor
|
Last-layer hidden states |
required |
lm_head_weight
|
Tensor
|
LM-head weight |
required |
labels
|
Tensor
|
Token-id targets |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar per-example DFT loss (matches :func: |
Source code in packages/opake-alignment/src/opake/api/alignment/sft/loss/_dft.py
fused_nll_loss(hidden_states, lm_head_weight, labels)
¶
Memory-efficient :func:nll_loss over hidden states (fused, per-example).
Mathematically nll_loss(hidden_states @ lm_head_weight.T, labels), but on
CUDA + half precision with opake-alignment[patches] installed it computes
the per-token CE against the projection via the patches fused linear-CE
kernel — no (T, V) logits are materialised and the LSE is recomputed in
the backward. Otherwise it falls back to the eager form.
Per-example. Pass one example (T, H) and drive with
vmap(grad(...)) (the clipped_grad DP-SGD path); the kernel's merged
vmap rules then make the whole microbatch one forward + one backward kernel.
Unlike :func:nll_loss, this is not meant to be called directly on a batch
axis (the fused path would collapse it to a single scalar).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_states
|
Tensor
|
Last-layer hidden states |
required |
lm_head_weight
|
Tensor
|
LM-head weight |
required |
labels
|
Tensor
|
Token-id targets |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar per-example mean NLL (matches :func: |
Source code in packages/opake-alignment/src/opake/api/alignment/sft/loss/_nll.py
nll_loss(logits, labels)
¶
Per-example causal-LM NLL loss with per-example DP-safe divisor.
Shifts logits[..., :-1, :] against labels[..., 1:] (standard
causal-LM convention), masks positions where the shifted label is
-100, computes selective_log_softmax at the (clamped) shifted
labels, and returns the per-example mean of the negative per-token
log-probabilities over non-ignored tokens.
The exact shifted-label token count is computed per example from this example's data only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
Float tensor of shape |
required |
labels
|
Tensor
|
Integer tensor of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Float tensor of shape |
Tensor
|
|
Tensor
|
non-ignored shifted tokens. |
Source code in packages/opake-alignment/src/opake/api/alignment/sft/loss/_nll.py
collator
¶
opake.alignment.sft.collator façade — the language-modeling collator.
Output schema (:class:LMBatch) lives in
:mod:opake.alignment.sft.collator.types.
language_modeling_collator(pad_token_id, max_length, *, completion_only_loss=False, pad_to_multiple_of=None)
¶
Return a callable that collates per-example dicts into a padded :class:LMBatch.
This is a factory function: it returns a plain callable, not a user-instantiated class. The returned callable is stateless and deterministic — the same input list always produces identical output tensors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pad_token_id
|
int
|
Token id used to right-pad sequences to the batch length. |
required |
max_length
|
int
|
Maximum sequence length. Examples longer than this are truncated from the right (keep-start) before padding. |
required |
completion_only_loss
|
bool
|
When |
False
|
pad_to_multiple_of
|
int | None
|
When set, the padded length |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[list[LMExample]], LMBatch]
|
A |
Callable[[list[LMExample]], LMBatch]
|
callable. See :class: |
Callable[[list[LMExample]], LMBatch]
|
for the output schema. |
Example::
collate = language_modeling_collator(pad_token_id=0, max_length=16)
batch = collate([{"input_ids": [1, 2, 3]}, {"input_ids": [4, 5]}])
# batch["input_ids"].shape == (2, 3)
Source code in packages/opake-alignment/src/opake/api/alignment/sft/collator/_language_modeling.py
types
¶
opake.alignment.sft.collator types façade — re-exports the output schema.
LMBatch
¶
Bases: TypedDict
Output schema for :func:language_modeling_collator.
All present fields are :class:torch.Tensor instances with dtype=torch.long.
Required keys (always present):
input_ids
Token ids, shape (B, L). Right-padded to L with the
pad_token_id supplied to the factory; each example is first
truncated (keep-start) to max_length before padding.
L = min(max_length, max_example_length_in_batch), rounded up to
the nearest multiple of pad_to_multiple_of when that argument is
set.
attention_mask
Binary mask, shape (B, L). 1 on real tokens, 0 on pad
positions.
labels
Token ids with pad positions replaced by -100 (the
ignore_index recognised by :class:torch.nn.CrossEntropyLoss),
shape (B, L). When the collator was created with
completion_only_loss=True the non-completion positions (where the
completion_mask field of the raw example is 0) are also set to
-100.
Optional keys (present only when applicable):
completion_mask
Binary mask, shape (B, L). Included when at least one input
example in the batch carries a "completion_mask" field. 1 on
completion tokens, 0 elsewhere (including pad positions). If an
example in such a mixed batch has no mask, all its real tokens are
marked as completion tokens to match its fully supervised labels.
Source code in packages/opake-alignment/src/opake/api/alignment/sft/collator/types.py
LMExample
¶
Bases: TypedDict
One tokenized causal-language-model example accepted by the collator.
Source code in packages/opake-alignment/src/opake/api/alignment/sft/collator/types.py
data
¶
opake.alignment.data — shared chat-template data-prep (method namespace).
Method-agnostic data prep for chat datasets: install a training chat template,
then tokenize chat turns into input_ids + a completion_mask for
completion-only loss.
clone_chat_template— copy a chat template (and its special tokens) from a source tokenizer onto a destination tokenizer and resize the model's embedding matrix to match.get_training_chat_template— return a chat-template string carrying assistant-turn{% generation %}markers so the assistant-token mask can be recovered at tokenization time.apply_chat_template_with_mask— tokenize a chat conversation intoinput_ids+completion_mask(1on assistant tokens), the mask consumed bysft.collator.language_modeling_collator(..., completion_only_loss=True).
apply_chat_template_with_mask(tokenizer, conversation, **kwargs)
¶
Tokenize a chat conversation and return the assistant-completion mask.
Wraps tokenizer.apply_chat_template(..., tokenize=True,
return_assistant_tokens_mask=True, return_dict=True, **kwargs) and renames
Hugging Face's assistant_masks to completion_mask (1 on
assistant-turn tokens, 0 elsewhere) so the result drops straight into
:func:opake.alignment.sft.collator.language_modeling_collator with
completion_only_loss=True.
The active template (including a selected named template) must carry a
Transformers-recognized generation tag around assistant content; install
canonical markers with
:func:opake.api.alignment.data._chat_template.get_training_chat_template.
Without the marker HF returns no assistant mask and this function raises a
:class:ConfigurationError.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tokenizer
|
PreTrainedTokenizerBase
|
A tokenizer whose active |
required |
conversation
|
list[dict[str, Any]]
|
A list of chat-message dicts (each with |
required |
**kwargs
|
Any
|
Forwarded verbatim to
|
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dict with |
dict[str, Any]
|
|
dict[str, Any]
|
length. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the chat template does not carry |
Source code in packages/opake-alignment/src/opake/api/alignment/data/_completion_mask.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
clone_chat_template(model, tokenizer, source_tokenizer_or_path)
¶
Copy a chat template and special tokens from source onto tokenizer.
The function:
- Loads the source tokenizer (if a path/name string is given, it is
loaded via
AutoTokenizer.from_pretrainedwithtrust_remote_code=False). - Copies
source.chat_templateontotokenizer.chat_template. - Collects every token that is marked
special=Truein the source tokenizer'sadded_tokens_decoderand adds any that are missing from tokenizer viatokenizer.add_special_tokens. - Calls
model.resize_token_embeddings(len(tokenizer))to extend (or confirm the size of) the model's input-embedding matrix.
Ordering invariant: this mutates the model's embedding matrix and must be
called before any make_functional / torch.func.functional_call
snapshot, otherwise the snapshot keeps the old (smaller) matrix and the
functional forward errors at runtime (embedding index-out-of-range) when a
newly-added token id is first seen.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
PreTrainedModel
|
The :class: |
required |
tokenizer
|
PreTrainedTokenizerBase
|
The destination :class: |
required |
source_tokenizer_or_path
|
PreTrainedTokenizerBase | str
|
Either a loaded
:class: |
required |
Returns:
| Type | Description |
|---|---|
PreTrainedModel
|
A |
PreTrainedTokenizerBase
|
|
list[int]
|
chaining. |
tuple[PreTrainedModel, PreTrainedTokenizerBase, list[int]]
|
special tokens newly added to tokenizer (empty when none were added). |
tuple[PreTrainedModel, PreTrainedTokenizerBase, list[int]]
|
Callers fine-tuning with PEFT use it to mark the new embedding rows |
tuple[PreTrainedModel, PreTrainedTokenizerBase, list[int]]
|
trainable, since a frozen base would never learn an embedding for a |
tuple[PreTrainedModel, PreTrainedTokenizerBase, list[int]]
|
token that did not exist at pre-training time. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If source_tokenizer_or_path does not have a
|
TypeError
|
If source_tokenizer_or_path is neither a string nor a
:class: |
Source code in packages/opake-alignment/src/opake/api/alignment/data/_chat_template.py
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
get_training_chat_template(tokenizer)
¶
Return a chat-template string with assistant-turn generation markers.
Wraps the assistant-turn content of tokenizer's chat template with
{% generation %} / {% endgeneration %} Jinja2 markers so that
tokenizer.apply_chat_template(..., return_assistant_tokens_mask=True)
returns a per-token boolean mask suitable for assistant_only_loss.
The function is idempotent: a template that already contains a Transformers-recognized generation tag is returned unchanged after its rendered spans are validated as assistant-only.
Strategy (close to TRL chat_template_utils.py):
- If the tokenizer's current template already contains a generation marker (including Jinja whitespace-control variants), validate and return it.
- Otherwise, locate the assistant content expression in an explicit
assistant branch, a shared Llama/Qwen-style role expression, or a
Gemma-style shared render expression that maps the
assistantrole tomodel. - Render each candidate with distinct system, user, and assistant probe text. Accept it only when the generation spans contain assistant content and exclude non-assistant content.
- Raise :class:
ValueErrorwhen the template cannot be transformed without guessing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tokenizer
|
PreTrainedTokenizerBase
|
A tokenizer whose |
required |
Returns:
| Type | Description |
|---|---|
str
|
A Jinja2 template string guaranteed to contain |
str
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in packages/opake-alignment/src/opake/api/alignment/data/_chat_template.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | |
loss
¶
opake.alignment.dpo.loss — the DPO loss-construction toolkit.
Everything needed to assemble a per-example DPO loss, exposed as direct functions (mapping a config string to one of them is the caller's concern):
- Per-sequence log-probabilities:
sequence_logpand its memory-efficient fused drop-infused_sequence_logp(project hidden states through thelm_headwithout materialising logits); passlength_normalized=Truefor the per-token mean reward used by SimPO / ORPO. - Per-pair heads on log-ratios:
sigmoid_loss,hinge_loss, …. Used reference-free by passing the policy logp itself as the log-ratio (CPO ismpo_combineof a reference-freesigmoid_lossandchosen_nll_loss). - Reference-free heads on (length-normalized) log-probs:
simpo_loss(length-normalized sigmoid with a target margin) andodds_ratio_loss(ORPO; takes log-probs directly, not log-ratios). - Log-ratio combinators for composite objectives:
f_divergence_remap/f_divergence_logits,mpo_combine,wpo_weights,ld_dpo_split.
A DPO per_example_loss is head(sequence_logp(...) - ref_logp, …); see
examples/train_dpo.py.
Naming is <method>[_<variant>]_loss: the preference/pairwise context is
implied by this namespace, and a _<variant> qualifier appears only to
disambiguate co-existing variants of one method (apo_zero / apo_down).
chosen_nll_loss is the chosen-completion NLL regulariser used in MPO/RPO/CPO
blends — not the SFT method's loss (that lives in opake.alignment.sft.loss).
apo_down_loss(chosen_logratio, rejected_logratio, *, beta)
¶
APO-down loss (arXiv:2408.06266).
Pulls the chosen log-ratio down (σ(β·chosen)) while simultaneously enforcing that chosen exceeds rejected by a relative margin (1 − σ(β·Δ)):
loss = σ(β · chosen_lr) + (1 − σ(β · (chosen_lr − rejected_lr)))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example scalar |
required |
rejected_logratio
|
Tensor
|
Per-example scalar |
required |
beta
|
float
|
KL-regularisation temperature (DPO-style). |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss tensor with the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_apo.py
apo_zero_loss(chosen_logratio, rejected_logratio, *, beta)
¶
APO-zero loss (arXiv:2408.06266).
Pushes the chosen log-ratio up and the rejected log-ratio down independently of each other. Mathematically:
loss = (1 − σ(β · chosen_lr)) + σ(β · rejected_lr)
where σ is the sigmoid function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example scalar |
required |
rejected_logratio
|
Tensor
|
Per-example scalar |
required |
beta
|
float
|
KL-regularisation temperature (DPO-style). |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss tensor with the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_apo.py
bco_loss(chosen_logratio, rejected_logratio, *, beta, delta=0.0)
¶
BCO pairwise loss.
The reward baseline delta corresponds to TRL's running-mean reward
estimate. The default 0.0 gives the zero-baseline variant. A nonzero
value must be public, separately DP, or derived from prior DP outputs;
detaching a raw cross-batch statistic is not sufficient.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example scalar |
required |
rejected_logratio
|
Tensor
|
Per-example scalar |
required |
beta
|
float
|
KL-regularisation temperature (DPO-style). |
required |
delta
|
float
|
Public or DP-safe reward baseline. Default |
0.0
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss tensor with the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_bco.py
chosen_nll_loss(chosen_logp, /, *_args, **_kwargs)
¶
SFT regulariser: negative log-likelihood of the chosen completion.
Returns -chosen_logp, i.e. the NLL of the chosen sequence under the
current policy. Extra positional and keyword arguments are silently
ignored so this function can be dispatched alongside log-ratio–based
variants in the MPO blend loop.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logp
|
Tensor
|
Per-example scalar log-probability
|
required |
*_args
|
object
|
Ignored. Present so the function can be called with the
|
()
|
**_kwargs
|
object
|
Ignored. |
{}
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example NLL scalar (same shape as chosen_logp). |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_chosen_nll.py
discopop_loss(chosen_logratio, rejected_logratio, *, beta, discopop_tau=0.05)
¶
DiscoPOP per-example loss (Lu et al., 2024, equations 4-5).
Blends a logistic component -log σ(β·Δ) and an exponential component
exp(-β·Δ) using a soft gate parameterised by temperature τ::
logits = (chosen_logratio - rejected_logratio) * beta
gate = σ(logits / τ) # → 1 for large positive logits
logistic = -log σ(logits)
exp_comp = exp(-logits) # clamped to avoid overflow
L = logistic * (1 - gate) + exp_comp * gate
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example scalar log-ratio for the chosen
completion. May be 0-dim or |
required |
rejected_logratio
|
Tensor
|
Per-example scalar log-ratio for the rejected completion. Same shape as chosen_logratio. |
required |
beta
|
float
|
KL-regularisation temperature (DPO β). |
required |
discopop_tau
|
float
|
Modulation temperature τ (default |
0.05
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss tensor with the same shape as the inputs. |
Note
The exponential component overflows half precision when logits ≪ 0.
To prevent NaN/Inf while preserving gradient locality, the logits passed
to torch.exp are clamped from below at a dtype-specific bound:
11 for float16 and 80 otherwise. This keeps the
exponential finite in the active compute dtype while leaving the
representable region unchanged.
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_discopop.py
exo_loss(chosen_logratio, rejected_logratio, *, beta, label_smoothing=0.001)
¶
EXO pairwise loss (arXiv:2402.00856, simplified pairwise variant).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example scalar |
required |
rejected_logratio
|
Tensor
|
Per-example scalar |
required |
beta
|
float
|
KL-regularisation temperature (DPO-style). |
required |
label_smoothing
|
float
|
Label-smoothing coefficient ε ∈ (0, 0.5).
When 0 or negative, silently clamped to |
0.001
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss tensor with the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_exo.py
f_divergence_logits(chosen_logratio, rejected_logratio, *, f_divergence_type='reverse_kl', alpha=1.0)
¶
Form the remapped preference logits delta under an f-divergence.
Computes::
delta = f_divergence_remap(chosen_logratio) - f_divergence_remap(rejected_logratio)
where both sides are remapped under the same f_divergence_type /
alpha. The resulting delta is the generalised drop-in replacement for
the plain chosen_logratio - rejected_logratio term consumed by the DPO
variants. Under reverse_kl this reduces exactly to that plain difference.
The function is elementwise and works on batched (B,) inputs as well as
0-dim scalars.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example log-ratio for the chosen response. Shape
|
required |
rejected_logratio
|
Tensor
|
Per-example log-ratio for the rejected response. Same
shape as |
required |
f_divergence_type
|
FDivergence
|
Which f-divergence remap to apply to both sides.
Defaults to |
'reverse_kl'
|
alpha
|
float
|
The α parameter for |
1.0
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Remapped preference-logit tensor of the same shape as the inputs. |
Raises:
| Type | Description |
|---|---|
ValueError
|
Propagated from :func: |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_f_divergence.py
f_divergence_remap(logratio, *, f_divergence_type='reverse_kl', alpha=1.0)
¶
Remap a per-example log-ratio under the chosen f-divergence.
Applies the per-side remap g for the requested f-divergence:
reverse_kl:g(x) = x(identity — grad-transparent).forward_kl:g(x) = 1 - _cap_exp(-x).js_divergence:g(x) = log(2) + logsigmoid(x).alpha_divergence:g(x) = (_cap_exp((alpha - 1) * x) - 1) / (alpha - 1); nearalpha == 1this uses the identity (reverse_kl).
The function is elementwise and works on batched (B,) inputs as well as
0-dim scalars.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logratio
|
Tensor
|
Per-example log-ratio |
required |
f_divergence_type
|
FDivergence
|
Which f-divergence remap to apply. One of
|
'reverse_kl'
|
alpha
|
float
|
The α parameter for |
1.0
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Remapped log-ratio tensor of the same shape as |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_f_divergence.py
hinge_loss(chosen_logratio, rejected_logratio, *, beta)
¶
Per-example DPO hinge loss.
Implements the hinge variant of the DPO objective from Liu et al. 2023.
The function is elementwise and works on both batched (B,) inputs and
0-dim scalars.
The hinge loss is zero when the margin β·Δ ≥ 1 (chosen is clearly preferred over rejected by the temperature-scaled margin). It is linear in the deficit below 1 otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example log-ratio for the chosen response,
|
required |
rejected_logratio
|
Tensor
|
Per-example log-ratio for the rejected response,
|
required |
beta
|
float
|
DPO temperature (reference-deviation strength). Positive float; typical values in [0.01, 0.5]. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example loss tensor of the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_hinge.py
ipo_loss(chosen_logratio, rejected_logratio, *, beta)
¶
IPO per-example loss (Azar 2024).
Computes the squared deviation between the log-ratio difference and the inverse of twice the KL-regularisation temperature::
L = (Δ - 1/(2β))² where Δ = chosen_logratio - rejected_logratio
When the caller passes length-normalised log-ratios (i.e. each log-ratio divided by the corresponding completion token count), this recovers the original IPO objective from the paper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example scalar |
required |
rejected_logratio
|
Tensor
|
Per-example scalar |
required |
beta
|
float
|
KL-regularisation temperature (DPO β). Controls the strength of the preference signal; larger β → the model must diverge more from the reference to achieve the same reward margin. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss tensor with the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_ipo.py
ld_dpo_split(per_token_logps, completion_mask, shared_prefix_len, alpha)
¶
Apply the LD-DPO per-sequence weighting for a supplied public length.
Weights completion tokens within the public length by 1.0 and tokens
beyond it by alpha, then masked-sums over the sequence
axis::
pos = completion_mask.cumsum(-1)
weight = where((pos >= 1) & (pos <= shared_prefix_len), 1.0, alpha)
return (per_token_logps * completion_mask * weight).sum(-1)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
per_token_logps
|
Tensor
|
Per-token log-probabilities of the completion. Shape
|
required |
completion_mask
|
Tensor
|
Tensor of shape |
required |
shared_prefix_len
|
Tensor | int
|
The paper's public length (the shorter completion
length), retained under this compatibility name. A Python |
required |
alpha
|
float
|
Weight on the length-desensitised tail in |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Float tensor of shape |
Tensor
|
sequence). |
Note
This low-level, single-sequence primitive cannot derive the public length from a chosen/rejected pair. Callers claiming the LD-DPO objective must pass the minimum of the paired completion lengths.
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_ld_dpo.py
mpo_combine(losses, weights)
¶
Weighted sum of per-example loss tensors (MPO / TRL loss_type=list).
Computes sum_k weights[k] * losses[k] over the keys of weights.
Only the terms named in weights contribute, so a caller may pass a
superset of available losses in losses and select a subset via
weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
losses
|
dict[str, Tensor]
|
Mapping from loss name to its per-example loss tensor. All selected tensors must broadcast against one another. |
required |
weights
|
dict[str, float]
|
Mapping from loss name to its scalar blend weight. Its keys
must be a subset of |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The per-example blended loss tensor, with shape equal to the |
Tensor
|
broadcast of the selected loss tensors. |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If weights names a key that is absent from losses. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_mpo.py
nca_loss(chosen_logratio, rejected_logratio, *, beta)
¶
NCA pairwise loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example scalar |
required |
rejected_logratio
|
Tensor
|
Per-example scalar |
required |
beta
|
float
|
KL-regularisation temperature (DPO-style). |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss tensor with the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_nca.py
odds_ratio_loss(chosen_logp, rejected_logp)
¶
Per-example ORPO odds-ratio loss.
Computes -log σ(log odds(y_w) - log odds(y_r)) with
log odds(y) = log p(y) - log(1 - p(y)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logp
|
Tensor
|
Length-normalized log-probability |
required |
rejected_logp
|
Tensor
|
Same for the rejected completion. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar odds-ratio loss, same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_orpo.py
robust_loss(chosen_logratio, rejected_logratio, *, beta, label_smoothing=0.0)
¶
Per-example DPO robust (label-smoothed) loss.
Implements the robust label-smoothed DPO objective. The function is
elementwise and works on both batched (B,) inputs and 0-dim scalars.
Unlike the sigmoid variant's label smoothing (which simply blends the
reversed loss in), the robust variant normalises the blended loss by
(1 − 2·label_smoothing) so that the gradient scale is preserved
across smoothing levels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example log-ratio for the chosen response,
|
required |
rejected_logratio
|
Tensor
|
Per-example log-ratio for the rejected response,
|
required |
beta
|
float
|
DPO temperature (reference-deviation strength). Positive float; typical values in [0.01, 0.5]. |
required |
label_smoothing
|
float
|
Label-smoothing coefficient ε ∈ [0, 0.5). The
denominator is |
0.0
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example loss tensor of the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_robust.py
sigmoid_loss(chosen_logratio, rejected_logratio, *, beta, label_smoothing=0.0)
¶
Per-example DPO sigmoid (logistic) loss.
Implements the original DPO objective from Rafailov et al. 2023 with
optional label smoothing. The function is elementwise and works on both
batched (B,) inputs and 0-dim scalars.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example log-ratio for the chosen response,
|
required |
rejected_logratio
|
Tensor
|
Per-example log-ratio for the rejected response,
|
required |
beta
|
float
|
DPO temperature (reference-deviation strength). Positive float; typical values in [0.01, 0.5]. |
required |
label_smoothing
|
float
|
Label-smoothing coefficient ε ∈ [0, 1). At ε = 0 this
reduces to the unsmoothed DPO loss. Defaults to |
0.0
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example loss tensor of the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_sigmoid.py
simpo_loss(chosen_logratio, rejected_logratio, *, beta, gamma=0.0, label_smoothing=0.0)
¶
Per-example SimPO loss.
Computes L = -log σ(m)·(1 - ε) - log σ(-m)·ε with
m = β·(chosen_logratio - rejected_logratio) - γ. At gamma=0 and
label_smoothing=0 this is the length-normalized DPO sigmoid loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example length-normalized reward for the chosen
completion — the policy log-prob divided by the completion length.
Reference-free, so no reference is subtracted. Shape |
required |
rejected_logratio
|
Tensor
|
Same for the rejected completion. |
required |
beta
|
float
|
Reward scale (SimPO β). |
required |
gamma
|
float
|
Target reward margin subtracted inside the sigmoid; |
0.0
|
label_smoothing
|
float
|
Conservative label-smoothing coefficient ε ∈ [0, 0.5). |
0.0
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss, same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_simpo.py
sppo_loss(chosen_logratio, rejected_logratio, *, beta)
¶
SPPO hard-label loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example scalar |
required |
rejected_logratio
|
Tensor
|
Per-example scalar |
required |
beta
|
float
|
KL-regularisation temperature (DPO-style). The Nash-equilibrium
target in log-ratio space is |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Per-example scalar loss tensor with the same shape as the inputs. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_sppo.py
wpo_weights(per_token_logps, completion_mask, logits=None)
¶
Per-example WPO weight (arXiv:2406.11827).
When logits are provided, computes the weight-aligned policy probability
from Equation 2 of WPO and returns its detached geometric mean::
log_denom = logsumexp(2 * logits, -1) - 2 * logsumexp(logits, -1)
aligned_logp = per_token_logps - log_denom
return exp(sum(masked aligned_logp) / completion_token_count)
Omitting logits retains the unaligned geometric-mean weighting for
backward compatibility. The result is detached: it contributes no
gradient and serves only as a per-example multiplicative reweighting of the
downstream loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
per_token_logps
|
Tensor
|
Per-token log-probabilities of the completion under
the current policy. Shape |
required |
completion_mask
|
Tensor
|
Tensor of shape |
required |
logits
|
Tensor | None
|
Optional policy logits of shape |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Detached per-example weight tensor of shape |
Tensor
|
sequence). All-zero mask rows are protected by |
Tensor
|
there is no division by zero. |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/loss/_wpo.py
fused_sequence_logp(hidden_states, lm_head_weight, input_ids, completion_mask, *, length_normalized=False)
¶
Memory-efficient :func:sequence_logp over hidden states (fused, per-example).
Mathematically sequence_logp(hidden_states @ lm_head_weight.T, input_ids,
completion_mask), but on CUDA + half precision with
opake-alignment[patches] installed it computes the completion log-prob
via the patches fused linear-CE kernel — no (T, V) logits are
materialised and the LSE is recomputed in the backward. Otherwise it falls
back to the eager form.
Since sequence_logp = Σ_completion log p = −Σ_completion CE, the kernel is
the preference-logp kernel: encode the completion span as the kept labels
(everything else → -100), take the kernel's CE sum, and negate.
Per-example. Pass one sequence (T, H) and drive with
vmap(grad(...)) (how train_dpo already computes logp inside
clipped_grad); the kernel's merged vmap rules then make the chosen /
rejected microbatch one forward + one backward kernel. Unlike
:func:sequence_logp, it is not meant to be called directly on a batch
axis. The LD-DPO ld_alpha split is not supported on the fused path; use
the eager :func:sequence_logp for that.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_states
|
Tensor
|
Last-layer hidden states |
required |
lm_head_weight
|
Tensor
|
LM-head weight |
required |
input_ids
|
Tensor
|
Token ids |
required |
completion_mask
|
Tensor
|
|
required |
length_normalized
|
bool
|
Divide by the completion-token count for the per-token
mean reward (SimPO / ORPO); matches |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
Tensor
|
Scalar completion log-prob (per-token mean when |
|
matches |
Tensor
|
func: |
Source code in packages/opake-alignment/src/opake/api/alignment/logprob/_sequence.py
sequence_logp(logits, input_ids, completion_mask, *, ld_alpha=None, shared_prefix_len=None, length_normalized=False)
¶
Sum (or per-token mean) of completion-token log-probabilities per sequence.
Applies the causal-LM shift, computes per-token log-probs, multiplies by the (shifted) completion mask, and sums over the sequence (last remaining) axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
Float tensor of shape |
required |
input_ids
|
Tensor
|
Integer tensor of shape |
required |
completion_mask
|
Tensor
|
Tensor of shape |
required |
ld_alpha
|
float | None
|
LD-DPO (arXiv:2409.06411) length-desensitisation coefficient
in |
None
|
shared_prefix_len
|
int | Tensor | None
|
LD-DPO public length (the shorter completion length),
retained under this compatibility name. An |
None
|
length_normalized
|
bool
|
Divide the per-sequence logp by its completion-token
count, giving the per-token mean reward |
False
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Float tensor of shape |
Tensor
|
when |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in packages/opake-alignment/src/opake/api/alignment/logprob/_sequence.py
collator
¶
opake.alignment.dpo.collator façade — the preference (DPO) collator.
The language-modeling (SFT) collator lives under
:mod:opake.alignment.sft.collator.
preference_collator(pad_token_id, max_length, *, pad_to_multiple_of=None)
¶
DPO preference collator — factory returning a callable.
.. note:: Layout choice
TRL uses a (2B, L) concatenated layout; opake-alignment uses a
(B, ...) layout because per-example DP-SGD clips per preference
pair — the chosen/rejected forward passes share one per-example
gradient.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pad_token_id
|
int
|
Token id used for right-padding. |
required |
max_length
|
int
|
Maximum sequence length. Sequences longer than this are truncated from the right (keep-start). |
required |
pad_to_multiple_of
|
int | None
|
When given, each side's padded sequence length is independently rounded up to the nearest multiple. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
A callable |
Callable[[list[PreferenceExample]], PreferenceBatch]
|
that accepts a list of example dicts and returns: |
Callable[[list[PreferenceExample]], PreferenceBatch]
|
Mandatory keys (always present): |
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
other) after truncation, rounded up to |
Callable[[list[PreferenceExample]], PreferenceBatch]
|
Optional keys (present iff all examples in the batch carry them): |
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Callable[[list[PreferenceExample]], PreferenceBatch]
|
|
Each input example dict must have:
chosen_input_ids:list[int]rejected_input_ids:list[int]chosen_completion_mask:list[int]rejected_completion_mask:list[int]
And may optionally carry ref_chosen_logps: float and
ref_rejected_logps: float.
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/collator/_preference.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | |
data
¶
opake.alignment.dpo.data façade — preference prompt extraction.
extract_prompt(example)
¶
Extract the shared prompt prefix from a preference example.
Given a single dataset row, returns a new dict with the same keys plus
a "prompt" key holding the longest common prefix of "chosen" and
"rejected". The values for "chosen" and "rejected" are
replaced by their respective suffixes (the parts after the common prefix).
If the example already contains a "prompt" key the dict is returned
unchanged (idempotent). Unpaired examples ("prompt" +
"completion", no chosen/rejected) are also returned unchanged.
All other keys in example are preserved without modification.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
example
|
dict
|
A single dataset record — a plain :class: |
required |
Returns:
| Type | Description |
|---|---|
dict
|
A new :class: |
dict
|
examples without a pre-existing |
dict
|
|
dict
|
All other cases return the original dict unchanged. |
Examples:
Conversational preference pair::
>>> example = {
... "chosen": [{"role": "user", "content": "hi"}, {"role": "assistant", "content": "good"}],
... "rejected": [{"role": "user", "content": "hi"}, {"role": "assistant", "content": "bad"}],
... }
>>> extract_prompt(example)
{
"prompt": [{"role": "user", "content": "hi"}],
"chosen": [{"role": "assistant", "content": "good"}],
"rejected": [{"role": "assistant", "content": "bad"}],
}
Idempotent when "prompt" is already present::
>>> extract_prompt({"prompt": [...], "chosen": [...], "rejected": [...]})
# returned unchanged
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/data/_prompt.py
reference
¶
opake.alignment.dpo.reference — reference-model handling for DPO.
DPO scores each response against a frozen reference policy π_ref. These
helpers produce π_ref's log-probabilities and keep that work outside the
per-example vmap(grad(...)) region (it runs a separate forward and/or mutates
nn.Module adapter state):
compute_ref_logprobs_for_dataset(dataset, ref, ...)— the common path: run the reference once over the dataset (outer loop), caching per-example chosen/rejected sequence logps to a content-addressed.safetensorscache keyed by dataset identity, a structured model/tokenizer/preprocessingcache_identity, andoutput_columns. Feed the cachedref_chosen_logps/ref_rejected_logpsinto the per-pair head as the log-ratio baselinepolicy_logp - ref_logp.null_ref_context(model)/with_disabled_adapter(model)— when the policy is a PEFT/LoRA adapter, use the base model as the reference: enter the context (which disables the adapter), run the reference forward, no second model needed.ema_update_reference(...)— TR-DPO: periodically move the reference toward the policy by an EMA step between training steps.
End-to-end wiring is in examples/train_dpo.py.
compute_ref_logprobs_for_dataset(dataset, ref, collator, output_columns, *, cache_identity, batch_size=8, cache_dir=None, use_cache=True, shard=None)
¶
Compute per-example reference logprobs, optionally using a cache.
Runs a single pass over dataset calling ref(batch) for each
collated batch, collecting one (B,) tensor per name in
output_columns, and attaches the concatenated per-example results to
dataset as new columns. The forward runs under torch.no_grad().
When use_cache is true, results are cached to a content-addressed
.safetensors file keyed on (dataset._fingerprint, cache_identity,
output_columns). The cache directory and archive are owner-only because
they contain private per-example values. The caller is responsible for
removing selected cache directories when their contents are no longer
needed.
Under a live process group each rank runs the reference over its own shard and the results are gathered back into dataset order, so the returned dataset is the same on every rank and matches a single-process run. Pass the whole dataset on every rank; shard for training afterwards.
Outside vmap only — see the module docstring.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset
|
Any
|
A :class: |
required |
ref
|
Callable[[dict[str, Tensor]], dict[str, Tensor]]
|
A callable mapping a collated batch |
required |
collator
|
Callable[[list[dict]], dict[str, Tensor]]
|
|
required |
output_columns
|
Sequence[str]
|
The keys |
required |
cache_identity
|
Any
|
Deterministic JSON-like identity for |
required |
batch_size
|
int
|
|
8
|
cache_dir
|
str | None
|
Directory for the |
None
|
use_cache
|
bool
|
Whether to read or write the persistent cache. Set to
|
True
|
shard
|
bool | None
|
Whether each rank scores only its own slice of |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
|
Any
|
length |
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/reference/_precompute.py
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 | |
ema_update_reference(ref_params, policy_params, alpha)
¶
Functional EMA update of the reference parameters toward the policy.
Implements the TR-DPO core update (arXiv:2404.09656):
ref ← (1 − alpha) · ref + alpha · policy
Applied leafwise over a parameter pytree. Returns a new pytree of the same structure and does not mutate either input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ref_params
|
PyTree
|
Current reference-model parameters as a pytree of
:class: |
required |
policy_params
|
PyTree
|
Current policy-model parameters as a pytree with the
same structure as |
required |
alpha
|
float
|
EMA interpolation coefficient in
|
required |
Returns:
| Type | Description |
|---|---|
PyTree
|
A new pytree of tensors with the same structure as |
PyTree
|
where each leaf |
PyTree
|
|
PyTree
|
|
Note
This function is pure (no in-place mutation) and is intended to
be called outside the per-example gradient loop. It is safe to
call under torch.no_grad() to avoid accumulating an autograd graph
over the reference parameters.
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/reference/_sync.py
null_ref_context(model, ref_model=None)
¶
Context manager that turns model into its own reference inside the block.
Dispatches per the ref-model design table:
======================== ================================================
Configuration Behaviour
======================== ================================================
Separate model ref_model is not None → no-op
(caller uses ref_model directly; no policy
adapter to toggle).
LoRA with "ref" is_peft_model(model) and "ref" in
adapter clone model.peft_config → call
model.set_adapter("ref") on enter; restore
the previously-active adapter(s) on exit via a
finally block.
LoRA without separate is_peft_model(model) and ref_model is
ref None → call model.disable_adapter()
(base model serves as reference); re-enable on
exit via finally.
Explicit callable / model is not a PEFT model and ref_model is
non-PEFT no-ref None → no-op (caller-supplied callable
or frozen base model, nothing to toggle).
======================== ================================================
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Any
|
The policy model. May be a PEFT :class: |
required |
ref_model
|
Any | None
|
Optional separate reference model. When not |
None
|
.. warning:: Outside vmap only.
This helper mutates ``nn.Module`` adapter-selection state. Call it in
the outer loop; vmap the pure forward *inside* the activated context.
Note
Modelled on TRL utils.use_adapter (trl/trainer/utils.py).
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/reference/_adapter.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
with_disabled_adapter(model)
¶
Thin context manager that disables adapters on model for the block.
When model is a PEFT model, enters model.disable_adapter() so the
base model weights serve as the forward pass. The adapter is re-enabled on
exit (finally). When model is not a PEFT model, this is a no-op.
Used internally by the LoRA-disable reference path in
:func:null_ref_context and exposed publicly for callers
that want to forward through the base model directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Any
|
The model whose adapters should be disabled. May be a PEFT
:class: |
required |
.. warning:: Outside vmap only.
This helper mutates ``nn.Module`` adapter-selection state. Call it in
the outer loop; vmap the pure forward *inside* the activated context.
Note
Modelled on TRL utils.use_adapter (trl/trainer/utils.py).
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/reference/_adapter.py
metric
¶
opake.alignment.dpo.metric façade — preference reward telemetry.
General token-level metrics live in the shared impl
:mod:opake.api.alignment.metric.
reward_metrics(chosen_logratio, rejected_logratio, *, beta)
¶
Compute rewards/* telemetry from per-example preference log-ratios.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chosen_logratio
|
Tensor
|
Per-example log-ratio for the chosen response,
|
required |
rejected_logratio
|
Tensor
|
Per-example log-ratio for the rejected response,
|
required |
beta
|
float
|
DPO temperature; scales the log-ratios into implicit rewards. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Tensor]
|
A dict of detached scalar tensors: |
dict[str, Tensor]
|
|
dict[str, Tensor]
|
|
dict[str, Tensor]
|
|
dict[str, Tensor]
|
|
Source code in packages/opake-alignment/src/opake/api/alignment/dpo/metric/_reward.py
metric
¶
opake.alignment.metric — shared token-level metrics.
Detached, un-noised token telemetry; release is outside Opake's DP accounting.
Preference metrics live in :mod:opake.alignment.dpo.metric.
entropy_from_logits(logits, mask)
¶
Mean per-position Shannon entropy over masked (shifted) positions.
Logits and mask are shifted so that position t scores the predictive
distribution over the token at t + 1 (standard causal-LM next-token
alignment), matching :func:mean_token_accuracy. Callers therefore pass the
full-length logits and mask; the shift happens here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
Logits of shape |
required |
mask
|
Tensor
|
Boolean/float mask of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A detached scalar tensor: the entropy averaged over positions where the |
Tensor
|
shifted |
Source code in packages/opake-alignment/src/opake/api/alignment/metric/_token.py
mean_token_accuracy(logits, labels, mask)
¶
Mean next-token prediction accuracy over masked (shifted) positions.
Logits, labels, and mask are shifted so that position t predicts the
token at t + 1 (standard causal-LM next-token alignment).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
Logits of shape |
required |
labels
|
Tensor
|
Target token ids of shape |
required |
mask
|
Tensor
|
Boolean/float mask of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A detached scalar tensor: the fraction of unmasked positions whose |
Tensor
|
argmax prediction matches the label ( |