jerryzhou196
Initial chord service
e319a7b unverified
Raw
History Blame Contribute Delete
3.38 kB
"""Sampling utilities for autoregressive generation."""
import torch
def length_to_mask(lengths: torch.Tensor, max_len: int | None = None) -> torch.Tensor:
"""Convert a tensor of sequence lengths to a boolean mask."""
assert len(lengths.shape) == 1
final_length = int(lengths.max().item()) if not max_len else max_len
final_length = max(final_length, 1)
return torch.arange(final_length, device=lengths.device)[None, :] < lengths[:, None]
def multinomial(
input: torch.Tensor, num_samples: int, replacement: bool = False, *, generator=None
) -> torch.Tensor:
"""torch.multinomial with arbitrary number of dimensions."""
input_ = input.reshape(-1, input.shape[-1])
output_ = torch.multinomial(
input_, num_samples=num_samples, replacement=replacement, generator=generator
)
output = output_.reshape(*list(input.shape[:-1]), -1)
return output
def sample_top_k(probs: torch.Tensor, k: int, num_samples: int = 1) -> torch.Tensor:
"""Sample from top-k probabilities."""
top_k_value, _ = torch.topk(probs, k, dim=-1)
min_value_top_k = top_k_value[..., [-1]]
probs = probs * (probs >= min_value_top_k).float()
probs = probs / probs.sum(dim=-1, keepdim=True)
return multinomial(probs, num_samples=num_samples)
def sample_top_p(probs: torch.Tensor, p: float, num_samples: int = 1) -> torch.Tensor:
"""Sample from nucleus (top-p) distribution."""
probs_sort, probs_idx = torch.sort(probs, dim=-1, descending=True)
probs_sum = torch.cumsum(probs_sort, dim=-1)
mask = probs_sum - probs_sort > p
probs_sort = probs_sort * (~mask).float()
probs_sort = probs_sort / probs_sort.sum(dim=-1, keepdim=True)
next_token = multinomial(probs_sort, num_samples=num_samples)
return torch.gather(probs_idx, -1, next_token)
def sample_from_probs(
probs: torch.Tensor, top_p: float = 0.0, top_k: int = 0
) -> torch.Tensor:
"""Sample one token from probs, optionally filtered by top-p or top-k."""
if top_p > 0.0:
return sample_top_p(probs, top_p)
if top_k > 0:
return sample_top_k(probs, top_k)
return multinomial(probs, num_samples=1)
def sample_stratified(
probs: torch.Tensor,
special_token: int,
first_temp: float,
second_temp: float = 1.0,
top_p: float = 0.0,
top_k: int = 0,
) -> torch.Tensor:
"""Stratified sampling: first decide special vs. non-special, then sample among non-special."""
eps = 1e-12
probs_special = probs[..., special_token : special_token + 1].clamp(
min=eps, max=1 - eps
)
logits_two = torch.cat(
[torch.log(probs_special), torch.log(1 - probs_special)], dim=-1
)
logits_two = logits_two / max(first_temp, eps)
probs_two = torch.softmax(logits_two, dim=-1)
probs_special_temp = probs_two[..., 0:1]
next_token_is_special = torch.rand_like(probs_special_temp).lt(probs_special_temp)
denom = (1 - probs_special).clamp(min=eps)
new_probs = probs.clone() / denom
new_probs[..., special_token] = 0.0
if second_temp > 0:
log_new = torch.log(new_probs.clamp(min=eps)) / second_temp
new_probs = torch.softmax(log_new, dim=-1)
next_token = sample_from_probs(new_probs, top_p=top_p, top_k=top_k)
return torch.where(
next_token_is_special, torch.full_like(next_token, special_token), next_token
)