Spaces:
Running on Zero
Running on Zero
Download src/diffusion_lm/loss.py from Ruurd/llada-8b-instruct: direct link, hf CLI and curl.
- Browser
- Download file 8.33 kB
-
https://huggingface.co/spaces/Ruurd/llada-8b-instruct/resolve/main/src/diffusion_lm/loss.py
- Command line
-
hf download hf://spaces/Ruurd/llada-8b-instruct/src/diffusion_lm/loss.py
-
curl -L -o loss.py https://huggingface.co/spaces/Ruurd/llada-8b-instruct/resolve/main/src/diffusion_lm/loss.py
8.33 kB
| """Same-position denoising objectives. No autoregressive shift is used.""" | |
| from __future__ import annotations | |
| import torch | |
| import torch.nn.functional as F | |
| def _separate_answer_padding_loss( | |
| selected_ce, example_ids, counts, sampled_t, token_weights, | |
| selected_answer_mask, selected_padding_mask, answer_lengths, padding_lengths, | |
| answer_padding_weights, compute_unweighted_metric, | |
| ): | |
| """Normalize each region per example before applying fixed mixing weights. | |
| Region lengths count all eligible positions, not just this draw's masks. | |
| Empty regions/draws contribute zero; coefficients are never redistributed. | |
| Include eligible examples with zero masks in the batch average so masking | |
| fewer positions does not condition the objective on a nonempty draw. | |
| """ | |
| if sampled_t is None: | |
| raise ValueError("Separate answer/padding loss requires sampled_t") | |
| if any(value is None for value in ( | |
| selected_answer_mask, selected_padding_mask, answer_lengths, padding_lengths, | |
| )): | |
| raise ValueError("Separate answer/padding loss requires region masks and lengths") | |
| weighted_ce = selected_ce if token_weights is None else selected_ce * token_weights | |
| inverse_t_ce = weighted_ce / sampled_t[example_ids].to(weighted_ce.dtype).clamp_min(1e-8) | |
| component_losses = [] | |
| valid = (answer_lengths + padding_lengths) > 0 | |
| valid_count = valid.sum() | |
| for selected_mask, lengths in ( | |
| (selected_answer_mask, answer_lengths), (selected_padding_mask, padding_lengths), | |
| ): | |
| sums = torch.zeros(counts.shape[0], device=weighted_ce.device, dtype=weighted_ce.dtype).scatter_add( | |
| 0, example_ids, inverse_t_ce * selected_mask, | |
| ) | |
| per_example = sums / lengths.clamp_min(1) | |
| component_losses.append((per_example * valid).sum() / valid_count.clamp_min(1)) | |
| answer_loss, padding_loss = component_losses | |
| answer_weight, padding_weight = answer_padding_weights | |
| loss = answer_weight * answer_loss + padding_weight * padding_loss | |
| metrics = { | |
| "weighted_loss": loss.detach(), | |
| "answer_loss": answer_loss.detach(), | |
| "padding_loss": padding_loss.detach(), | |
| "valid_examples": valid_count.detach(), | |
| "supervised_tokens": counts.sum().detach(), | |
| } | |
| if compute_unweighted_metric: | |
| metrics["unweighted_masked_token_ce"] = (selected_ce.sum() / counts.sum().clamp_min(1)).detach() | |
| return loss, metrics | |
| def _finish_selected_loss( | |
| selected_ce_sums, | |
| selected_ce_total, | |
| counts, | |
| sampled_t, | |
| normalization_mask, | |
| compute_unweighted_metric, | |
| ): | |
| """Reduce already-selected token losses with the configured weighting.""" | |
| valid = counts > 0 | |
| per_example = selected_ce_sums / counts.clamp_min(1) | |
| if sampled_t is None: | |
| weighted = per_example | |
| else: | |
| if normalization_mask is None: | |
| raise ValueError("normalization_mask is required when sampled_t is provided") | |
| response_lengths = normalization_mask.sum(dim=1).clamp_min(1) | |
| weighted = selected_ce_sums / sampled_t.to(selected_ce_sums.device).clamp_min(1e-8) / response_lengths | |
| valid_count = valid.sum() | |
| loss = (weighted * valid).sum() / valid_count.clamp_min(1) | |
| metrics = { | |
| "weighted_loss": loss.detach(), | |
| "valid_examples": valid_count.detach(), | |
| "supervised_tokens": counts.sum().detach(), | |
| } | |
| if compute_unweighted_metric: | |
| metrics["unweighted_masked_token_ce"] = ( | |
| selected_ce_total / counts.sum().clamp_min(1) | |
| ).detach() | |
| return loss, metrics | |
| def selected_denoising_loss( | |
| selected_logits, | |
| selected_labels, | |
| example_ids, | |
| counts, | |
| sampled_t=None, | |
| normalization_mask=None, | |
| *, | |
| compute_unweighted_metric=True, | |
| token_weights=None, | |
| answer_padding_weights=None, | |
| selected_answer_mask=None, | |
| selected_padding_mask=None, | |
| answer_lengths=None, | |
| padding_lengths=None, | |
| ): | |
| """Compute the objective when the LM head emitted supervised positions only.""" | |
| selected_ce = F.cross_entropy(selected_logits, selected_labels, reduction="none") | |
| if answer_padding_weights is not None: | |
| return _separate_answer_padding_loss( | |
| selected_ce, example_ids, counts, sampled_t, token_weights, | |
| selected_answer_mask, selected_padding_mask, answer_lengths, padding_lengths, | |
| answer_padding_weights, compute_unweighted_metric, | |
| ) | |
| weighted_ce = selected_ce if token_weights is None else selected_ce * token_weights | |
| selected_ce_sums = torch.zeros( | |
| counts.shape[0], device=selected_logits.device, dtype=weighted_ce.dtype | |
| ).scatter_add(0, example_ids, weighted_ce) | |
| return _finish_selected_loss( | |
| selected_ce_sums, | |
| selected_ce.sum(), | |
| counts, | |
| sampled_t, | |
| normalization_mask, | |
| compute_unweighted_metric, | |
| ) | |
| def masked_denoising_loss( | |
| logits, | |
| labels, | |
| loss_mask, | |
| sampled_t=None, | |
| normalization_mask=None, | |
| *, | |
| compute_unweighted_metric=True, | |
| sparse_positions=True, | |
| token_weights=None, | |
| answer_padding_weights=None, | |
| answer_mask=None, | |
| padding_mask=None, | |
| ): | |
| """Compute masked CE with optional LLaDA-style inverse-t weighting. | |
| Returns a differentiable scalar and aggregate metrics. Examples without selected | |
| tokens are excluded rather than changing another example's denominator. When | |
| sampled_t is provided, normalization_mask must represent the complete eligible | |
| response, not only the positions selected for corruption. Optional token_weights | |
| correct frontier positions by t / p(position) before inverse-t reduction; | |
| unweighted_masked_token_ce always reports the original CE. | |
| """ | |
| counts = loss_mask.sum(dim=1) | |
| if answer_padding_weights is not None: | |
| if answer_mask is None or padding_mask is None: | |
| raise ValueError("Separate answer/padding loss requires answer_mask and padding_mask") | |
| answer_mask = answer_mask & ~padding_mask | |
| example_ids, token_ids = loss_mask.nonzero(as_tuple=True) | |
| return selected_denoising_loss( | |
| logits[example_ids, token_ids], labels[example_ids, token_ids], example_ids, | |
| counts, sampled_t, normalization_mask, | |
| compute_unweighted_metric=compute_unweighted_metric, | |
| token_weights=None if token_weights is None else token_weights[example_ids, token_ids], | |
| answer_padding_weights=answer_padding_weights, | |
| selected_answer_mask=answer_mask[example_ids, token_ids], | |
| selected_padding_mask=padding_mask[example_ids, token_ids], | |
| answer_lengths=answer_mask.sum(dim=1), padding_lengths=padding_mask.sum(dim=1), | |
| ) | |
| if sparse_positions: | |
| # Computing CE over [batch, sequence, vocabulary] wastes a large softmax | |
| # on positions excluded from the objective. Reuse one set of selected | |
| # indices for the logits, labels, and per-example reduction. | |
| example_ids, token_ids = loss_mask.nonzero(as_tuple=True) | |
| selected_ce = F.cross_entropy( | |
| logits[example_ids, token_ids], labels[example_ids, token_ids], reduction="none" | |
| ) | |
| weighted_ce = selected_ce if token_weights is None else selected_ce * token_weights[example_ids, token_ids] | |
| return _finish_selected_loss( | |
| torch.zeros( | |
| logits.shape[0], device=logits.device, dtype=weighted_ce.dtype | |
| ).scatter_add(0, example_ids, weighted_ce), | |
| selected_ce.sum(), | |
| counts, | |
| sampled_t, | |
| normalization_mask, | |
| compute_unweighted_metric, | |
| ) | |
| else: | |
| # Structured all-token training has nothing to compact; avoid copying | |
| # the entire logits tensor through advanced indexing in that mode. | |
| token_ce = F.cross_entropy(logits.transpose(1, 2), labels, reduction="none") | |
| selected_ce_sums = (token_ce * loss_mask).sum(dim=1) | |
| weighted_ce_sums = selected_ce_sums if token_weights is None else (token_ce * loss_mask * token_weights).sum(dim=1) | |
| return _finish_selected_loss( | |
| weighted_ce_sums, | |
| selected_ce_sums.sum(), | |
| counts, | |
| sampled_t, | |
| normalization_mask, | |
| compute_unweighted_metric, | |
| ) | |