Skip to content

Commit e17a8ae

Browse files
Document how to change the training objective by subclassing a trainer (#6918)
Co-authored-by: Quentin Gallouédec <45557362+qgallouedec@users.noreply.github.com>
1 parent e56e323 commit e17a8ae

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

docs/source/customization.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,67 @@ training_args = DPOConfig(
111111
gradient_accumulation_steps=8,
112112
)
113113
```
114+
115+
## Change the training objective
116+
117+
Subclassing a trainer is the simplest way to customize training. The loss is defined in the `compute_loss` method, so to train with a different objective, subclass the trainer and override it. Data preparation, generation, logging, and checkpointing are inherited, so the subclass holds only what actually changes.
118+
119+
```python
120+
import torch
121+
122+
from trl import DPOTrainer
123+
from trl.trainer.utils import selective_log_softmax
124+
125+
126+
class MyDPOTrainer(DPOTrainer):
127+
def compute_loss(self, model, inputs, return_outputs=False, num_items_in_batch=None):
128+
# In this example: the hinge loss from https://huggingface.co/papers/2309.06657
129+
input_ids, attention_mask = inputs["input_ids"], inputs["attention_mask"]
130+
shift_labels, shift_completion_mask = input_ids[:, 1:], inputs["completion_mask"][:, 1:]
131+
logits = model(input_ids=input_ids, attention_mask=attention_mask).logits
132+
with torch.no_grad():
133+
ref_logits = self.ref_model(input_ids=input_ids, attention_mask=attention_mask).logits
134+
135+
# Sum the log-probs over the completion tokens, for the policy and for the reference model
136+
logps = (selective_log_softmax(logits[:, :-1], shift_labels) * shift_completion_mask).sum(dim=1)
137+
ref_logps = (selective_log_softmax(ref_logits[:, :-1], shift_labels) * shift_completion_mask).sum(dim=1)
138+
139+
chosen_logps, rejected_logps = logps.chunk(2, dim=0) # batch is [chosen, rejected]
140+
ref_chosen_logps, ref_rejected_logps = ref_logps.chunk(2, dim=0)
141+
delta = (chosen_logps - ref_chosen_logps) - (rejected_logps - ref_rejected_logps)
142+
return torch.relu(1 - self.beta * delta).mean()
143+
```
144+
145+
### Add parameters to the config
146+
147+
Subclass the config to declare the parameters your loss needs:
148+
149+
```python
150+
from dataclasses import dataclass, field
151+
152+
from trl import DPOConfig
153+
154+
155+
@dataclass
156+
class MyDPOConfig(DPOConfig):
157+
my_coef: float = field(default=0.1, metadata={"help": "Coefficient of the custom term."})
158+
```
159+
160+
### Change the batch format
161+
162+
When the loss needs inputs that the default collator doesn't produce, pass your own collator, and override `_prepare_dataset` to leave the dataset untouched when it is already in the expected format:
163+
164+
```python
165+
class MyDPOTrainer(DPOTrainer):
166+
def _prepare_dataset(self, dataset, *args, **kwargs):
167+
return dataset
168+
169+
170+
trainer = MyDPOTrainer(..., data_collator=my_collator)
171+
```
172+
173+
### Complete example
174+
175+
The block-diffusion SFT example [`examples/sft_diffusion_gemma/sft_diffusion_gemma.py`](https://github.com/huggingface/trl/blob/main/examples/sft_diffusion_gemma/sft_diffusion_gemma.py) combines the three: `DiffusionGemmaSFTConfig` extends [`SFTConfig`] with the canvas and corruption parameters, and `DiffusionGemmaSFTTrainer` extends [`SFTTrainer`] and replaces the autoregressive cross-entropy with a block-diffusion denoising objective. Neither requires a change to the library.
176+
177+
[Antidoom](https://github.com/Liquid4All/antidoom), an open-source tool from [Liquid AI](https://huggingface.co/LiquidAI), extends [`DPOTrainer`] with Final Token Preference Optimization ([Antislop](https://huggingface.co/papers/2510.15061)), a preference loss over a single token position that reduces repetition loops in reasoning models.

0 commit comments

Comments
 (0)