When to use a custom loss function
Write a custom loss function when no built-in loss function matches your objective, for example:- Preference objectives: DPO, or another objective over chosen and rejected pairs. For a complete worked example, see Direct preference optimization.
- Regularized SFT: Cross-entropy plus an extra term, such as a penalty for drifting from the starting model.
- New RL objectives: A policy-gradient variant that the built-in loss functions don’t cover.
- Research prototypes: Any objective you want to try without a change to the service.
- Built-in loss function: The service ships cross-entropy for SFT and several policy-gradient loss functions for RL, listed in Built-in loss functions. The policy-gradient ones already cover clipped objectives such as PPO and CISPO in a single
forward_backwardcall, and the cookbook’s GRPO loop can switch to any of them. Use one if it fits your objective. With a custom loss function, each step runs the model’s forward pass twice, once inforwardand again incustom_forward_backward, so it takes longer than it would with a built-in loss function on the same batch. - Reward function: If your objective is a score per completion rather than a function of the model’s log-probabilities, use GRPO with a reward function instead. It’s simpler and runs on-policy.
How a custom loss function runs
Each step of a custom loss function run splits the work between your process and the model resource:forwardscores the batch on the model resource’s trainer without accumulating gradients and returns the model’s log-probability of each target token.- Your local process runs your loss function on those log-probabilities in PyTorch, then calls
.backward()on the loss to get its gradient with respect to each log-probability. custom_forward_backwardsends those gradients to the model resource’s trainer, which runs the forward pass again and backpropagates them through the model to accumulate parameter gradients.optim_stepapplies the accumulated gradients, as in every other loop.
forward and custom_forward_backward, so the parameter gradients match what the trainer would compute if it ran your loss function itself.
Use forward for the first pass, not forward_backward with return_loss_fn_outputs=True. Even with that flag, forward_backward runs a backward pass through its built-in loss function, so those gradients add to yours before optim_step applies them.
Write a loss function
The cookbook’spost_training_cookbook.custom_loss module runs steps 1 through 3 for you, so you write only the loss function. Your function receives a list of float32 tensors, one per sample in batch order, each holding that sample’s per-position log-probabilities. It must return a scalar tensor.
Build each sample the same way as for SFT, with target_tokens and a weights row that marks the positions your loss function reads. Keep any other values it needs, such as reference log-probabilities or advantages, in your own code, and capture them in the function, as the example does with ref.
This loss function is regularized SFT: cross-entropy plus a penalty for moving the model’s log-probabilities away from a snapshot taken before training:
compute_logprobs runs only the forward pass, so use it to snapshot a frozen reference before training. compute_custom_forward_backward runs steps 1 through 3 and returns the loss your function computed as result.loss. The service doesn’t compute or report a loss for a custom step, so result.loss is the only record of it. Log it yourself to track training.
Get these details right:
- Reduce over the trained positions only:
forwardreturns a real log-probability only where the sample’sweightsentry is non-zero, and0.0at every other position. The service ignores gradients at those zero-weight positions, but the zeros still count in your loss if you average over the whole row. Divide by the mask’s sum, as the example does withm.sum(). - Return a scalar tensor:
compute_custom_forward_backwardcalls.backward()on what your function returns and raises aValueErrorfor anything other than a 0-dimensional tensor. - Snapshot a frozen reference once: Call
compute_logprobsfor it before training starts. Recomputing the reference every step repeats the same forward passes. - Chunk large forward passes:
forwardruns the whole batch at once. If it runs out of memory or returns no log-probabilities, callcompute_logprobson smaller slices of the batch. The DPO recipe does this withprecompute_ref_batch_size. - Skip generators when the loop never samples: Run a loop that never calls
sample, such as SFT or DPO, on a trainer-only model resource withnum_generator_replicas=0, which costs less.
Use a custom loss function in an RL loop
In an RL loop, a custom loss function replaces only theforward_backward call. Sample completions and score them as usual, then:
- Build one sample per completion with the cookbook’s
build_cross_entropy_sample. Pass the prompt and completion as one token sequence, with a loss mask of the same length that’s0over the prompt and1over the completion. Keep each completion’s advantage in your own code, not in the sample. - Run
compute_custom_forward_backwardwith a loss function that reads the advantages, then calloptim_step. - Call
weights_syncso the nextsamplecall uses the updated policy.
Call the SDK without the cookbook
The cookbook helpers wrap two SDK calls. To run them yourself, read the log-probabilities withforward, then send one list of gradients per sample, in batch order, to custom_forward_backward. This example reuses regularized_sft_loss from Write a loss function:
LossConfig on forward only selects which positions return a log-probability, so every sample needs target_tokens and weights. Each gradient list must have one value per target position, the same length as the sample’s target_tokens.
Next steps
Direct preference optimization
Follow a complete recipe that uses a custom loss function.
Built-in loss functions
Check whether a built-in loss function already fits your objective.
Reinforcement learning with GRPO
Run the RL loop that a custom policy-gradient loss function plugs into.
Training methods
Review how each training method uses a loss function.