Download upstreams/candi/algo.py from jyliuAI/diffusion_baseline: direct link, hf CLI and curl.
- Browser
- Download file 49.1 kB
-
https://huggingface.co/jyliuAI/diffusion_baseline/resolve/main/upstreams/candi/algo.py
- Command line
-
hf download hf://jyliuAI/diffusion_baseline/upstreams/candi/algo.py
-
curl -L -o algo.py https://huggingface.co/jyliuAI/diffusion_baseline/resolve/main/upstreams/candi/algo.py
49.1 kB
import models
import os
import time
import pdb
import collections
import copy
import pickle
import fsspec
import numpy as np
import torch
import torch.nn.functional as F
from torch.distributions import Normal
import trainer_base
import utils
import torch
from omegaconf import DictConfig
# torch.serialization.add_safe_globals([DictConfig])
class AR(trainer_base.TrainerBase):
def __init__(self, config, tokenizer):
vocab_size = tokenizer.vocab_size
if not hasattr(tokenizer, "mask_token") or tokenizer.mask_token is None:
self.mask_index = vocab_size
vocab_size += 1
else:
self.mask_index = tokenizer.mask_token_id
super().__init__(config, tokenizer, vocab_size=vocab_size)
self.save_hyperparameters()
self._validate_configuration()
self.temp = config.algo.get("temp", 1.0)
def _validate_configuration(self):
super()._validate_configuration()
assert not self.config.algo.time_conditioning
assert self.config.prior.type == "none"
def _process_model_input(self, x0, valid_tokens):
input_tokens = x0[:, :-1]
output_tokens = x0[:, 1:]
valid_tokens = valid_tokens[:, 1:]
return input_tokens, output_tokens, valid_tokens
def nll(self, input_tokens, output_tokens, current_accumulation_step, *args):
del current_accumulation_step
output = self.backbone(input_tokens, None)
output[:, :, self.mask_index] = self.neg_infinity
output = output.log_softmax(-1)
return -output.gather(-1, output_tokens[:, :, None])[:, :, 0]
def generate_samples(self, num_samples, prompt_mask=None, prompt_tokens=None, **kwargs):
# precompute token buffer
num_pred_tokens = self.num_tokens - 1
x = torch.zeros(
(num_samples, num_pred_tokens + 1), dtype=torch.long, device=self.device
)
if prompt_mask is not None and prompt_tokens is not None:
x = (prompt_mask * prompt_tokens + (1 - prompt_mask) * x).long()
start = prompt_mask.sum(1).max().long().item() - 1
else:
start = 0
x[:, 0] = self.tokenizer.bos_token_id
# precompute noise
noise = (
torch.distributions.Gumbel(0, 1)
.sample((num_samples, num_pred_tokens, self.vocab_size))
.to(self.device)
)
if self.config.sampling.use_float64:
noise = noise.to(torch.float64)
for i in range(start, num_pred_tokens):
output = self.backbone(x[:, : i + 1], None)
output[:, :, self.mask_index] = self.neg_infinity
output = output / self.temp
output = output.log_softmax(-1)
y = (output[:, -1, :] + noise[:, i, :]).argmax(-1)
x[:, i + 1] = y
return x
def _process_sigma(self, sigma):
del sigma
return None
class MDLM(trainer_base.AbsorbingState):
def __init__(self, config, tokenizer):
super().__init__(config, tokenizer)
self._validate_configuration()
def _validate_configuration(self):
# ancestral sampling isn't desirable because it's slow
assert self.sampler == "ancestral_cache" or self.sampler == "posterior"
def _process_model_output(self, model_output, xt, sigma):
del sigma
model_output = model_output / self.temp
model_output[:, :, self.mask_index] += self.neg_infinity
# Normalize the model_output such that x.exp() is
# a probability distribution over vocab_size.
model_output = model_output - torch.logsumexp(
model_output, dim=-1, keepdim=True
)
# Apply updates directly in the logits matrix.
# For the logits of the unmasked tokens, set all values
# to -infinity except for the indices corresponding to
# the unmasked tokens.
unmasked_indices = xt != self.mask_index
model_output[unmasked_indices] = self.neg_infinity
model_output[unmasked_indices, xt[unmasked_indices]] = 0
return model_output
def nll_per_token(self, log_x_theta, xt, x0, alpha_t, dalpha_t, low_var=False):
del xt
log_p_theta = torch.gather(
input=log_x_theta, dim=-1, index=x0[:, :, None]
).squeeze(-1)
return log_p_theta * dalpha_t / (1 - alpha_t)
def _get_score(self, x, sigma):
model_output = self.forward(x, sigma)
# score(x, t) = p_t(y) / p_t(x)
# => log score(x, t) = log p_t(y) - log p_t(x)
# case 1: x = masked
# (i) y = unmasked
# log score(x, t) = log p_\theta(x)|_y + log k
# where k = exp(- sigma) / (1 - exp(- sigma))
# (ii) y = masked
# log score(x, t) = 0
# case 2: x = unmasked
# (i) y != masked, y != x
# log score(x_i, t) = - inf
# (ii) y = x
# log score(x_i, t) = 0
# (iii) y = masked token
# log score(x_i, t) = - log k
# where k = exp(- sigma) / (1 - exp(- sigma))
log_k = -torch.log(torch.expm1(sigma)).squeeze(-1)
assert log_k.ndim == 1
masked_score = model_output + log_k[:, None, None]
masked_score[:, :, self.mask_index] = 0
unmasked_score = self.neg_infinity * torch.ones_like(model_output)
unmasked_score = torch.scatter(
unmasked_score, -1, x[..., None], torch.zeros_like(unmasked_score[..., :1])
)
unmasked_score[:, :, self.mask_index] = -(log_k[:, None] * torch.ones_like(x))
masked_indices = (x == self.mask_index).to(model_output.dtype)[:, :, None]
model_output = masked_score * masked_indices + unmasked_score * (
1 - masked_indices
)
return model_output.exp()
class D3PMAbsorb(trainer_base.AbsorbingState):
def __init__(self, config, tokenizer):
super().__init__(config, tokenizer)
self._validate_configuration()
def _validate_configuration(self):
super()._validate_configuration()
assert self.noise.type == "log-linear"
assert self.parameterization == "mean"
def _process_model_output(self, model_output, xt, sigma):
del xt
del sigma
if self.subs_masking:
model_output[:, :, self.mask_index] += self.neg_infinity
return model_output.log_softmax(dim=-1)
def nll_per_token(self, log_x_theta, xt, x0, alpha_t, dalpha_t, low_var=False):
del dalpha_t
assert not low_var
dt = 1 / self.T
t = 1 - alpha_t # Only valid for log-linear schedule.
t = t.clamp(0.0, 1.0 - 1e-4)
alpha_t = alpha_t + torch.zeros_like(xt)
alpha_s = t - dt + torch.zeros_like(xt)
assert alpha_s.shape == xt.shape
assert alpha_t.shape == xt.shape
log_x_theta_at_x0 = torch.gather(log_x_theta, -1, x0[:, :, None]).squeeze(-1)
log_x_theta_at_m = log_x_theta[:, :, self.mask_index]
x_theta_at_m = log_x_theta_at_m.exp()
term_1_coef = dt / t
term_1_log_nr = torch.log(alpha_t * x_theta_at_m / t + 1)
term_1_log_dr = log_x_theta_at_x0
term_2_coef = 1 - dt / t
term_2_log_nr = term_1_log_nr
term_2_log_dr = torch.log(alpha_s * x_theta_at_m / (t - dt) + 1)
L_vb_masked = term_1_coef * (term_1_log_nr - term_1_log_dr) + term_2_coef * (
term_2_log_nr - term_2_log_dr
)
diffusion_loss = self.T * L_vb_masked * (xt == self.mask_index)
return self._reconstruction_loss(x0) + diffusion_loss
class SEDDAbsorb(trainer_base.AbsorbingState):
def __init__(self, config, tokenizer):
super().__init__(config, tokenizer)
self._validate_configuration()
def _validate_configuration(self):
super()._validate_configuration()
assert self.config.sampling.predictor == "analytic"
def _get_score(self, x, sigma):
return self.forward(x, sigma).exp()
def _process_model_output(self, model_output, xt, sigma):
esigm1_log = (
torch.where(sigma < 0.5, torch.expm1(sigma), sigma.exp() - 1)
.log()
.to(model_output.dtype)
)
# logits shape
# (batch_size, context_length, vocab_size)
model_output = (
model_output
- esigm1_log[:, None, None]
- np.log(model_output.shape[-1] - 1)
)
# The below scatter operation sets the log score
# for the input word to 0.
model_output = torch.scatter(
model_output, -1, xt[..., None], torch.zeros_like(model_output[..., :1])
)
return model_output
def nll_per_token(self, log_x_theta, xt, x0, alpha_t, dalpha_t, low_var=False):
"""Computes the SEDD loss for the Absorbing State Diffusion.
Args:
log_x_theta: float torch.Tensor with shape (batch_size,
context_length, vocab_size),
log score, output of the denoising network.
xt: int torch.Tensor with shape (batch_size,
context_length), input.
x0: int torch.Tensor with shape (batch_size,
context_length), input.
alpha_t: float torch.Tensor with shape (batch_size, 1),
signal level.
alpha_t: float torch.Tensor with shape (batch_size, 1),
signal level.
dalpha_t: float or float torch.Tensor with shape (batch_size, 1),
time derivative of signal level.
low_var: bool, low variance loss during training.
Returns:
loss with shape (batch_size, context_length).
"""
assert not low_var
masked_indices = xt == self.mask_index
sigma = self._sigma_from_alphat(alpha_t)
dsigma = -dalpha_t / alpha_t
expsig_minus_1 = torch.expm1(sigma).expand_as(xt)
q_ratio = 1 / expsig_minus_1[masked_indices]
words_that_were_masked = x0[masked_indices]
neg_term = q_ratio * torch.gather(
log_x_theta[masked_indices], -1, words_that_were_masked[..., None]
).squeeze(-1)
score = log_x_theta[masked_indices].exp()
if self.mask_index == self.vocab_size - 1:
pos_term = score[:, :-1].sum(dim=-1)
else:
pos_term = score[:, : self.mask_index].sum(dim=-1) + score[
:, self.mask_index + 1 :
].sum(dim=-1)
const = q_ratio * (q_ratio.log() - 1)
entropy = torch.zeros(*xt.shape, device=xt.device)
entropy[masked_indices] += pos_term - neg_term + const
return dsigma * entropy
class DUO_BASE(trainer_base.UniformState):
def __init__(self, config, tokenizer):
self.temp = config.algo.get("temp", 1.0)
super().__init__(config, tokenizer)
self._validate_configuration()
def on_save_checkpoint(self, checkpoint):
checkpoint["state_dict"] = collections.OrderedDict(
(k, v)
for k, v in checkpoint["state_dict"].items()
if not k.startswith("teacher")
)
super().on_save_checkpoint(checkpoint)
def on_load_checkpoint(self, checkpoint):
checkpoint["state_dict"] = collections.OrderedDict(
(k, v)
for k, v in checkpoint["state_dict"].items()
if not k.startswith("teacher")
)
super().on_load_checkpoint(checkpoint)
def _process_model_output(self, model_output, xt, sigma):
del xt, sigma
return (model_output / self.temp).log_softmax(dim=-1)
def _compute_posterior(self, x, xt, alpha_s, alpha_t):
"""Computes the posterior / approximate posterior.
Args:
x: Either clean input `x0` (one-hot),
or model's predicted `x_theta` of shape (B, L, V).
xt: The noisy latent (as indices) of shape (B, L).
alpha_s: Noise level at s of shape (B, [L | 1], 1).
alpha_t: Noise level at t of shape (B, [L | 1], 1).
Returns:
Posterior / approximate posterior of shape (B, L, V).
"""
if self.config.sampling.use_float64:
x = x.to(torch.float64)
if alpha_s.ndim == 2:
alpha_s = alpha_s.unsqueeze(-1)
if alpha_t.ndim == 2:
alpha_t = alpha_t.unsqueeze(-1)
alpha_ts = alpha_t / alpha_s
d_alpha = alpha_s - alpha_t
xt_one_hot = F.one_hot(xt, self.vocab_size).to(self.dtype).to(self.device)
return (
alpha_t * self.vocab_size * x * xt_one_hot
+ (alpha_ts - alpha_t) * xt_one_hot
+ d_alpha * x
+ (1 - alpha_ts) * (1 - alpha_s) / self.vocab_size
) / (
alpha_t * self.vocab_size * torch.gather(x, -1, xt[..., None])
+ (1 - alpha_t)
)
def nll_per_token(self, log_x_theta, xt, x0, alpha_t, dalpha_t, low_var=False):
assert alpha_t.ndim == 2
assert x0.ndim == 2
assert xt.ndim == 2
assert not torch.is_tensor(dalpha_t) or dalpha_t.ndim == 2
x_reconst = log_x_theta.exp()
x_bar_theta = (
self.vocab_size * alpha_t[:, :, None] * x_reconst + 1 - alpha_t[:, :, None]
)
coeff = dalpha_t / (self.vocab_size * alpha_t)
x_eq_xt = (x0 == xt).float()
x_neq_xt = 1 - x_eq_xt
xbar_xt = (1 - alpha_t) + self.vocab_size * alpha_t * x_eq_xt
xbar_theta_xt = torch.gather(x_bar_theta, -1, xt.unsqueeze(-1)).squeeze(-1)
xbar_theta_x = torch.gather(x_bar_theta, -1, x0.unsqueeze(-1)).squeeze(-1)
term1 = self.vocab_size * (1 / xbar_xt - 1 / xbar_theta_xt)
const = (1 - alpha_t) / (self.vocab_size * alpha_t + 1 - alpha_t)
term2_coefs = x_eq_xt * const + x_neq_xt
term2_offset = (
(self.vocab_size - 1) * const * x_eq_xt - (1 / const) * x_neq_xt
) * const.log()
term2_theta = -term2_coefs * (
x_bar_theta.log().sum(-1) - self.vocab_size * xbar_theta_xt.log()
)
term2_theta = (
term2_theta
- self.vocab_size
* alpha_t
/ (1 - alpha_t)
* (xbar_theta_x.log() - xbar_theta_xt.log())
* x_neq_xt
)
term2 = term2_theta + term2_offset
diffusion_loss = coeff * (term1 - term2)
assert diffusion_loss.ndim == 2
return diffusion_loss
def _ancestral_update(self, x, t, dt, p_x0=None, noise_removal_step=False, prompt_mask=None):
del p_x0
_, alpha_t = self.noise(t)
if noise_removal_step:
alpha_s = torch.ones_like(alpha_t)
else:
_, alpha_s = self.noise(t - dt)
sigma_t = self._sigma_from_alphat(alpha_t)
assert alpha_t.ndim == 2
cur_logits = (self.forward(x, sigma_t)).double()
xtheta_to_use = cur_logits.exp()
q_xs = self._compute_posterior(
x=xtheta_to_use, xt=x, alpha_s=alpha_s, alpha_t=alpha_t
)
if self.p_nucleus < 1:
q_xs = utils.top_k_top_p_filtering(q_xs.log(), top_p=self.p_nucleus)
xs = trainer_base.sample_categorical(q_xs)
# to handle conditional generation for inputs / infilling tasks
if prompt_mask is not None:
# expects the original x to have the prompt tokens where prompt_mask = 1
xs = (xs * (1 - prompt_mask) + x * prompt_mask).long()
return None, xs
class Integral(torch.autograd.Function):
"""
torch module calculating UDLM's p_t
"""
@staticmethod
def forward(ctx, gamma_t, data):
gamma_max = data["gamma_max"]
gamma_min = data["gamma_min"]
if (gamma_t.max() > gamma_max) or (gamma_t.min() < gamma_min):
print("max:{} {}".format(gamma_t.max(), gamma_max))
print("min:{} {}".format(gamma_t.min(), gamma_min))
gamma_t = torch.clip(gamma_t, gamma_min, gamma_max)
indices = torch.round(
(data["num_points"] - 1) * (gamma_t - gamma_min) / (gamma_max - gamma_min)
).long()
grad_pt = data["grad_pt"]
ctx.grad_pt = grad_pt[indices]
pt = data["pt"][indices]
assert pt.shape == gamma_t.shape
return pt
@staticmethod
def backward(ctx, grad_output):
return ctx.grad_pt * grad_output, None
class DUO(DUO_BASE):
def __init__(self, config, tokenizer):
super().__init__(config, tokenizer)
with fsspec.open(self.config.algo.integral_cache_path, "rb") as f:
self.integral_cache = pickle.load(f)
self.integral_cache["pt"] = torch.from_numpy(self.integral_cache["pt"])
self.integral_cache["grad_pt"] = torch.from_numpy(
self.integral_cache["grad_pt"]
)
self.gamma_min = self.config.algo.gamma_min
self.gamma_max = self.config.algo.gamma_max
self.gumbel_tau_log10_start = self.config.algo.gumbel_tau_log10_start
self.gumbel_tau_log10_end = self.config.algo.gumbel_tau_log10_end
self.curriculum_start = self.config.algo.curriculum_start
self.curriculum_end = self.config.algo.curriculum_end
self.loss_type = self.config.algo.loss_type
self.temp = config.algo.temp
self._validate_configuration()
def to(self, *args, **kwargs):
self = super().to(*args, **kwargs)
self.integral_cache["pt"] = self.integral_cache["pt"].to(*args, **kwargs)
self.integral_cache["grad_pt"] = self.integral_cache["grad_pt"].to(
*args, **kwargs
)
return self
def _compute_gumbel_tau_inverse(self):
start = self.gumbel_tau_log10_start
end = self.gumbel_tau_log10_end
delta = end - start
if self.global_step < self.curriculum_start:
tau = start
elif self.global_step < self.curriculum_end:
frac = (self.global_step - self.curriculum_start) / (
self.curriculum_end - self.curriculum_start
)
tau = start + frac * delta
else:
tau = -10
return 10 ** (-tau)
def training_step(self, batch, batch_idx):
self.log(
name="gumbel_tau_log10",
value=1 / self._compute_gumbel_tau_inverse(),
on_step=True,
on_epoch=False,
sync_dist=True,
)
return super().training_step(batch, batch_idx)
def _gamma_to_alphat(self, gamma_t):
integral = Integral.apply(gamma_t, self.integral_cache)
return (self.vocab_size * integral - 1) / (self.vocab_size - 1)
def _prior_loss(self):
alpha_1 = self._gamma_to_alphat(torch.tensor(self.gamma_max))
loss = (alpha_1 + (1 - alpha_1) / self.vocab_size) * torch.log(
(self.vocab_size - 1) * alpha_1 + 1
) + (1 - 1 / self.vocab_size) * (1 - alpha_1) * torch.log(1 - alpha_1)
return loss.item()
def _q_xt_gaussian(self, x, gamma_t):
"""Computes the noisy sample xt."""
assert gamma_t.ndim == 1
assert x.ndim == 3
gamma_t = gamma_t.unsqueeze(-1).unsqueeze(-1)
alpha_t = torch.sigmoid(-gamma_t).sqrt()
sigma_t = torch.sigmoid(gamma_t).sqrt()
epsilon = torch.randn(x.shape, dtype=torch.float32, device=self.device)
return alpha_t * x + sigma_t * epsilon
def nll(self, x0, output_tokens, current_accumulation_step=None, train_mode=False):
use_true_nll = self.global_step > self.curriculum_end or not train_mode
if use_true_nll:
return super().nll(x0, output_tokens, current_accumulation_step)
del output_tokens
t = self._sample_t(x0.shape[0], current_accumulation_step)
gamma_t = self.gamma_min + t * (self.gamma_max - self.gamma_min)
gamma_t_prime = self.gamma_max - self.gamma_min
usdm_alpha_t = self._gamma_to_alphat(gamma_t)
T = 1000
usdm_dalpha_t = (
gamma_t_prime * T * (self._gamma_to_alphat(gamma_t + 1 / T) - usdm_alpha_t)
)
usdm_alpha_t = usdm_alpha_t.unsqueeze(-1)
usdm_dalpha_t = usdm_dalpha_t.unsqueeze(-1)
assert usdm_alpha_t.ndim == 2
sigma = self._sigma_from_alphat(usdm_alpha_t)
x0_one_hot = F.one_hot(x0, self.vocab_size)
xt = self._q_xt_gaussian(x0_one_hot, gamma_t)
xt = xt * self._compute_gumbel_tau_inverse()
xt_usdm = xt.argmax(-1)
log_x_theta = self.forward(xt, sigma=sigma)
return self.nll_per_token(
log_x_theta=log_x_theta,
xt=xt_usdm,
x0=x0,
alpha_t=usdm_alpha_t,
dalpha_t=usdm_dalpha_t,
low_var=False,
)
class Distillation(DUO):
def __init__(self, config, tokenizer):
super().__init__(config, tokenizer)
self.update_teacher_every = config.algo.update_teacher_every
self.save_hyperparameters()
self.teacher = None
self.teacher_ema = config.algo.teacher_ema
self.linear_growth_dt = config.algo.linear_growth_dt
self.linear_growth_min = config.algo.linear_growth_min
self.linear_growth_max = config.algo.linear_growth_max
def _validate_configuration(self):
assert os.path.exists(self.config.algo.integral_cache_path), (
"The integral cache (Eq. 10 in the paper) for "
f"the {self.config.data.tokenizer_name_or_path} "
" tokenizer doesnt exist at "
f"{self.config.algo.integral_cache_path}. "
"Please generate it by running the utils.py script, "
"and ensure the correct path is specified using the "
"algo.integral_cache_path flag."
)
assert self.loss_type in {"kl-fwd", "kl-bwd", "posterior", "kl-posterior"}
def _maybe_update_teacher_weights(self):
if self.global_step % self.update_teacher_every != 0:
return
if self.teacher_ema:
self.ema.copy_to(self.teacher.parameters())
else:
for better_param, current_param in zip(
self.backbone.parameters(), self.teacher.parameters()
):
if current_param.requires_grad:
current_param.data.copy_(better_param.data)
@torch.no_grad()
def _teacher_logits(self, xt, sigma):
if self.teacher is None:
self.teacher = copy.deepcopy(self.backbone)
self._maybe_update_teacher_weights()
sigma = self._process_sigma(sigma)
with torch.cuda.amp.autocast(dtype=torch.float32):
model_output = self.teacher(xt, sigma)
logits = self._process_model_output(
model_output=model_output, xt=xt, sigma=sigma
)
return logits.detach()
def _sample_trajectory(self, x0, gamma_t, gamma_s):
"""Computes the noisy sample xt."""
assert gamma_t.ndim == 1
assert gamma_s.ndim == 1
assert x0.ndim == 2
x0 = F.one_hot(x0, self.vocab_size).to(self.dtype).to(self.device)
gamma_t = gamma_t.unsqueeze(-1).unsqueeze(-1)
alpha_t = torch.sigmoid(-gamma_t).sqrt()
sigma_t = torch.sigmoid(gamma_t).sqrt()
gamma_s = gamma_s.unsqueeze(-1).unsqueeze(-1)
alpha_s = torch.sigmoid(-gamma_s).sqrt()
sigma_s = torch.sigmoid(gamma_s).sqrt()
epsilon = torch.randn(x0.shape, dtype=torch.float32, device=self.device)
xt = alpha_t * x0 + sigma_t * epsilon
xs = alpha_s * x0 + sigma_s * epsilon
return xt, xs
def _compute_dt(self):
if self.linear_growth_dt:
scale = self.global_step / self.trainer.max_steps
return self.linear_growth_min + scale * (
self.linear_growth_max - self.linear_growth_min
)
n = self.global_step // self.update_teacher_every
return 2**n / self.T
def nll(self, x0, output_tokens, current_accumulation_step=None, train_mode=None):
del output_tokens, train_mode
t = self._sample_t(x0.shape[0], current_accumulation_step)
dt = self._compute_dt()
t = torch.clip(t + dt, 0, 1)
gamma_t = self.gamma_min + t * (self.gamma_max - self.gamma_min)
gamma_s = self.gamma_min + (t - dt) * (self.gamma_max - self.gamma_min)
usdm_alpha_t = self._gamma_to_alphat(gamma_t)
usdm_alpha_t = usdm_alpha_t.unsqueeze(-1)
assert usdm_alpha_t.ndim == 2
usdm_alpha_s = self._gamma_to_alphat(gamma_s)
usdm_alpha_s = usdm_alpha_s.unsqueeze(-1)
assert usdm_alpha_s.ndim == 2
xt, xs = self._sample_trajectory(x0, gamma_t, gamma_s)
xt_discrete = xt.argmax(-1)
xs_discrete = xs.argmax(-1)
log_x_theta_student = self.forward(
xt_discrete, sigma=self._sigma_from_alphat(usdm_alpha_t)
)
log_x_theta_teacher = self._teacher_logits(
xs_discrete, sigma=self._sigma_from_alphat(usdm_alpha_s)
)
if self.config.training.loss_precision == "float64":
log_x_theta_student = log_x_theta_student.to(torch.float64)
log_x_theta_teacher = log_x_theta_teacher.to(torch.float64)
if self.loss_type == "kl-fwd":
return (
log_x_theta_teacher.exp() * (log_x_theta_teacher - log_x_theta_student)
).sum(-1)
elif self.loss_type == "kl-bwd":
return (
log_x_theta_student.exp() * (log_x_theta_student - log_x_theta_teacher)
).sum(-1)
def training_step(self, batch, batch_idx):
self.log(
name="dt",
value=self._compute_dt(),
on_step=True,
on_epoch=False,
sync_dist=True,
)
return super().training_step(batch, batch_idx)
class CANDI(trainer_base.Diffusion):
def __init__(self, config, tokenizer):
self.mask_index = len(tokenizer)
vocab_size = len(tokenizer) + 1
super().__init__(config, tokenizer, vocab_size=vocab_size)
self._validate_configuration()
self.save_hyperparameters()
self.pure_continuous = config.algo.pure_continuous
self.continuous_noise =torch.linspace(.2, 4.0, 1000)
self.discrete_noise = expected_rank(d=self.vocab_size-1, a=torch.tensor(1).to(self.device), sigma=self.continuous_noise)
self.continuous_noise = torch.tensor(self.continuous_noise)
self.discrete_noise = torch.tensor(self.discrete_noise)
self.step_size = config.algo.step_size
self.min_percentile = config.algo.min_percentile
self.max_percentile = config.algo.max_percentile
self.sigma_min = config.algo.sigma_min
self.sigma_max = config.algo.sigma_max
self.temp = config.algo.temp
self.sampler = config.algo.sampler
self.is_embed = config.algo.is_embed
self.use_percentile_scheduling = config.algo.use_percentile_scheduling
def ve_sde_noise_sched(self, t, sigma_min=0.01, sigma_max=10.0):
return sigma_min * (sigma_max / sigma_min) ** t
def sample_t(self, n, accum_step):
return super()._sample_t(n, accum_step)
def get_continuous_from_discrete_noise(self, discrete_noise):
if self.is_embed:
sigmas = training_sigma_ve(discrete_noise, sigma_min=self.sigma_min, sigma_max=self.sigma_max)
return sigmas
else:
target_percentile = discrete_noise * (self.max_percentile - self.min_percentile) + self.min_percentile
return sigma_from_time_vectorized(target_percentile, self.continuous_noise, self.discrete_noise)
# inference schedule
# while the embedding diffusion should use the VE schedule, this produced better results
# at least on text8 -- both schedules for embedding diffusion collapsed on OWT
def get_continuous_noise_sched_pure_cont(self, timesteps):
target_percentile = timesteps * (self.max_percentile - self.min_percentile) + self.min_percentile
return sigma_from_time_vectorized(target_percentile, self.continuous_noise, self.discrete_noise)
def to(self, *args, **kwargs):
super().to(*args, **kwargs)
self.continuous_noise = self.continuous_noise.to(*args, **kwargs)
self.discrete_noise = self.discrete_noise.to(*args, **kwargs)
return self
def _validate_configuration(self):
return
def _process_model_output(self, xt, model_output, reveal_mask):
if xt.ndim == 2:
xt_tokens = xt
else:
xt_tokens = xt.argmax(dim=-1)
model_output = model_output / self.temp
model_output = model_output - torch.logsumexp(
model_output, dim=-1, keepdim=True
)
reveal_mask = reveal_mask.bool()
# for pure continuous diffusion, reveal mask is all false -- so this doesnt have any impact
model_output[reveal_mask] = self.neg_infinity
model_output[reveal_mask, xt_tokens[reveal_mask]] = 0
return model_output
def nll_per_token(self,
log_x_theta,
x0_tokens,
alpha_t,
dalpha_t,
reveal_mask,
**kwargs):
log_p_theta = torch.gather(
input=log_x_theta, dim=-1, index=x0_tokens[:, :, None]
).squeeze(-1)
if self.pure_continuous:
nll = -1 * log_p_theta
else:
nll = log_p_theta * dalpha_t / (1 - alpha_t)
return nll
def discrete_noising(self, x, alpha_t):
"""Computes the noisy sample xt.
Args:
x: int torch.Tensor with shape (batch_size,
diffusion_model_input_length), input.
move_chance: float torch.Tensor with shape
(batch_size, 1).
"""
move_indices = torch.rand(*x.shape, device=x.device) < 1 - alpha_t
uniform_tensor = torch.randint(0, self.vocab_size, x.shape, device=x.device)
xt = torch.where(move_indices, uniform_tensor, x)
if self.ignore_bos:
xt[:, 0] = x[:, 0]
return xt
# gets noisy sample xt
def q_xt(self, x, alpha_t):
disc_xt = self.discrete_noising(x, alpha_t)
reveal_mask = (disc_xt == x).float()
discrete_noise = (1 - alpha_t).squeeze()
continuous_noise = self.get_continuous_from_discrete_noise(discrete_noise)
# accounting for the mask token
onehot = F.one_hot(x, num_classes=self.vocab_size-1).float()
if self.pure_continuous:
if self.is_embed:
clean_embeddings = self.backbone.get_embedding(x)
t = discrete_noise
sigma = self.get_continuous_from_discrete_noise(t).squeeze()
noise = torch.randn_like(clean_embeddings, device=x.device) * sigma[:, None, None]
xt = clean_embeddings + noise
# in pure continuous with embedding diffusion, we dont use any reveal mask
reveal_mask = torch.zeros_like(reveal_mask)
return {
'xt': xt,
'reveal_mask': reveal_mask,
'discrete_noise': discrete_noise,
'continuous_noise': continuous_noise,
'is_embed': True
}
else:
if self.cont_noise_schedule == 'corruption':
if self.sigma_table.device != discrete_noise.device:
self.sigma_table = self.sigma_table.to(discrete_noise.device)
self.error_table = self.error_table.to(discrete_noise.device)
sigma = error_to_sigma(discrete_noise, self.sigma_table, self.error_table)
else:
sigma = continuous_noise
noise = torch.randn_like(onehot, device=x.device) * sigma[:, None, None]
xt = onehot + noise
reveal_mask = torch.zeros_like(reveal_mask)
return {
'xt': xt,
'reveal_mask': reveal_mask,
'discrete_noise': discrete_noise,
'continuous_noise': continuous_noise,
'is_embed': False
}
xt_cont = onehot + continuous_noise[:, None, None].to(x.device) * torch.randn_like(onehot, device=x.device)
xt = onehot * reveal_mask.unsqueeze(-1) + (1 - reveal_mask).unsqueeze(-1) * xt_cont
return {
'xt': xt,
'reveal_mask': reveal_mask,
'discrete_noise': discrete_noise,
'continuous_noise': continuous_noise
}
def prior_sample(self, *batch_dims):
sigma = self.get_continuous_from_discrete_noise(torch.tensor(.999).to(self.device))
if self.is_embed:
sigma_max = self.sigma_max
noise = torch.randn(
*batch_dims, self.backbone.config.hidden_size, dtype=torch.float32, device=self.device) * sigma_max
else:
noise = torch.randn(
*batch_dims, self.vocab_size-1, dtype=torch.float32, device=self.device
) * sigma
return noise
def forward(self, **kwargs):
with torch.cuda.amp.autocast(dtype=torch.float32):
model_output = self.backbone(**kwargs)
return self._process_model_output(model_output=model_output, xt=kwargs['xt'], reveal_mask=kwargs['reveal_mask'])
def nll(self, x0, output_tokens, current_accumulation_step=None, train_mode=False):
del output_tokens
t = self._sample_t(x0.shape[0], current_accumulation_step)
assert t.shape[0] == x0.shape[0]
dalpha_t, alpha_t = self.noise(t)
alpha_t = alpha_t.unsqueeze(-1)
assert alpha_t.ndim == 2
noisy_output = self.q_xt(x0, alpha_t)
log_x_theta = self.forward(**noisy_output)
utils.print_nans(log_x_theta, "model_output")
return self.nll_per_token(
log_x_theta=log_x_theta,
alpha_t=alpha_t,
dalpha_t=dalpha_t,
x0_tokens=x0,
low_var=train_mode,
**noisy_output,
)
def generate_sample_prompt(self, prompt_tokens, x, prompt_mask):
prompt_onehots = torch.nn.functional.one_hot(prompt_tokens, x.size(-1)).float().to(x.device)
x = prompt_onehots * prompt_mask.unsqueeze(-1) + (1 - prompt_mask).unsqueeze(-1) * x
return x, prompt_mask
def generate_samples(self, **kwargs):
if self.pure_continuous or self.is_embed:
return self.generate_samples_pure_cont(**kwargs)
else:
if self.sampler == 'cached':
return self.generate_samples_cache(**kwargs)
else:
return self.generate_samples_nocache(**kwargs)
@torch.no_grad()
def generate_samples_nocache(self, num_samples, num_steps=None, eps=1e-5, prompt_tokens=None, prompt_mask=None):
if num_steps is None:
num_steps = self.config.sampling.steps
x = self.prior_sample(num_samples, self.num_tokens)
clean_mask = torch.zeros((num_samples, self.num_tokens), device=x.device)
if prompt_tokens is not None and prompt_mask is not None:
x, clean_mask = self.generate_sample_prompt(prompt_tokens, x, prompt_mask)
timesteps = torch.linspace(.999, eps, num_steps + 1, device=self.device)
if self.use_percentile_scheduling:
continuous_noise = self.get_continuous_from_discrete_noise(timesteps)
else:
continuous_noise = inference_sigmas(num_steps+1, self.sigma_min, self.sigma_max)
dt = (1 - eps) / (num_steps)
self.max_sigma = continuous_noise.max().item()
self.prev_px0 = None
for i in range(num_steps):
t = timesteps[i]
s = timesteps[i+1]
sigma_s = continuous_noise[i]
sigma_t = continuous_noise[i+1]
x_cont, p_x0 = self._continuous_step(x, t, sigma_s=sigma_s, sigma_t=sigma_t, clean_mask=clean_mask, time_s=s)
x, clean_mask = self._discrete_step(x_cont,
p_x0, t, dt, prev_clean_mask=clean_mask)
final_tokens = x.argmax(dim=-1)
return final_tokens
def generate_samples_pure_cont(self, num_samples, num_steps=None, eps=1e-5, prompt_tokens=None, prompt_mask=None):
if num_steps is None:
num_steps = self.config.sampling.steps
x = self.prior_sample(num_samples, self.num_tokens)
clean_mask = torch.zeros((num_samples, self.num_tokens), device=x.device)
if prompt_tokens is not None and prompt_mask is not None:
x, clean_mask = self.generate_sample_prompt(prompt_tokens, x, prompt_mask)
timesteps = torch.linspace(.999, eps, num_steps + 1, device=self.device)
continuous_noise = self.get_continuous_noise_sched_pure_cont(timesteps)
self.max_sigma = continuous_noise.max().item()
for i in range(num_steps):
t = timesteps[i]
s = timesteps[i+1]
sigma_s = continuous_noise[i]
sigma_t = continuous_noise[i+1]
new_x, denoised = self._continuous_step(x, t, sigma_s=sigma_s, sigma_t=sigma_t, clean_mask=clean_mask, time_s=s, is_embed=self.is_embed)
x = new_x
if self.is_embed:
final_tokens = denoised.argmax(dim=-1)
else:
final_tokens = x.argmax(dim=-1)
return final_tokens
# optimized function with cached embeddings
@torch.no_grad()
def generate_samples_cache(self, num_samples, num_steps=None, eps=1e-5, prompt_tokens=None, prompt_mask=None):
x = self.prior_sample(num_samples, self.num_tokens)
if num_steps is None:
num_steps = self.config.sampling.steps
embedding_cache = self.backbone.get_embedding(x)
clean_mask = torch.zeros((num_samples, self.num_tokens), device=x.device, dtype=torch.bool)
if prompt_tokens is not None and prompt_mask is not None:
x, clean_mask = self.generate_sample_prompt(prompt_tokens, x, prompt_mask)
timesteps = torch.linspace(.999, eps, num_steps + 1, device=self.device)
if self.use_percentile_scheduling:
continuous_noise = self.get_continuous_from_discrete_noise(timesteps)
else:
continuous_noise = inference_sigmas(num_steps+1, self.sigma_min, self.sigma_max)
dt = (1 - eps) / (num_steps)
self.max_sigma = continuous_noise.max().item()
# convert to tokens
x = x.argmax(dim=-1)
for i in range(num_steps):
t = timesteps[i]
s = timesteps[i+1]
sigma_s = continuous_noise[i]
sigma_t = continuous_noise[i+1]
embedding_cache, x0_hat = self._continuous_step_cache(x, t,
sigma_s=sigma_s,
sigma_t=sigma_t,
clean_mask=clean_mask.float(),
time_s=s,
embedding_cache=embedding_cache,
is_first=(i==0))
x, clean_mask = self._discrete_step_optimized(x0_hat, x, t, dt,
prev_clean_mask=clean_mask,
noise_removal_step=False)
return x
def _discrete_step_optimized(self, x0_hat, xt, t, dt, prev_clean_mask, noise_removal_step=False):
if noise_removal_step:
s=0
else:
s = t - dt
# unmasking correspends to 1-alpha(s) / 1-alpha(t)
# this is just s / t under a log linear schedule
unmask = torch.rand(prev_clean_mask.shape, device=prev_clean_mask.device) < (t-s)/t
xt[~prev_clean_mask] = x0_hat[~prev_clean_mask]
new_clean_mask = prev_clean_mask | unmask
return xt, new_clean_mask
def _discrete_step(self, x_sigma, p_x0, t, dt, prev_clean_mask, noise_removal_step=False):
if noise_removal_step:
s = 0.0
else:
s = t - dt
t_vec = torch.ones(x_sigma.shape[0], device=x_sigma.device) * t.item()
s_vec = torch.ones(x_sigma.shape[0], device=x_sigma.device) * s.item()
mask_probs = torch.ones((x_sigma.shape[0], x_sigma.shape[1], 1), device=x_sigma.device) * (s)
unmasked_probs = p_x0 * (t_vec - s_vec)[:, None, None]
q_xs = torch.cat([unmasked_probs, mask_probs], dim=-1)
_x = sample_categorical(q_xs)
new_clean_mask = (prev_clean_mask.bool() | (_x != self.mask_index)).float()
# For tokens that got sampled to real values (not mask), use those
old_x_tokens=x_sigma.argmax(dim=-1)
# For tokens that got sampled to mask, keep old tokens but mark as not clean
sampled_real_tokens = torch.where(_x != self.mask_index, _x, old_x_tokens)
# Apply copy logic: keep old tokens where prev_clean_mask is True
updated_tokens = torch.where(prev_clean_mask.bool(), old_x_tokens, sampled_real_tokens)
updated_x = torch.nn.functional.one_hot(updated_tokens, num_classes=x_sigma.shape[-1]).float().to(x_sigma.device)
updated_x = updated_x * new_clean_mask.unsqueeze(-1) + (1 - new_clean_mask).unsqueeze(-1) * x_sigma
return updated_x, new_clean_mask
@torch.no_grad()
def _continuous_step(
self,
x: torch.Tensor,
time_t: torch.Tensor,
time_s: torch.Tensor,
sigma_s: torch.Tensor,
sigma_t: torch.Tensor,
clean_mask: torch.Tensor=None,
is_embed=False,
) -> torch.Tensor:
dt_cont_vec = torch.ones(x.shape[0], device=x.device) * (sigma_s - sigma_t).item()
time_t_vec = torch.ones(x.shape[0], device=x.device) * time_t.item()
sigma_t_vec = torch.ones(x.shape[0], device=x.device) * sigma_t.item()
if clean_mask is None:
clean_mask = torch.zeros(x.shape[:-1], device=x.device)
cond_denoised = self.forward(xt=x, discrete_noise=time_t_vec, reveal_mask=clean_mask, continuous_noise=sigma_t_vec, is_embed=is_embed).double()
denoised = cond_denoised.exp()
if self.is_embed:
x0_hat = self.backbone.get_embedding(denoised)
else:
x0_hat = denoised
d = (x - x0_hat) / (sigma_t_vec[:, None, None] ** 2)
x_cont = x - dt_cont_vec[:, None, None] * d
return x_cont, denoised
def _continuous_step_cache(
self,
x: torch.Tensor,
time_t: torch.Tensor,
time_s: torch.Tensor,
sigma_s: torch.Tensor,
sigma_t: torch.Tensor,
embedding_cache: torch.Tensor,
clean_mask: torch.Tensor=None,
is_first=False,
) -> torch.Tensor:
dt = sigma_s - sigma_t
time_t_vec = torch.ones(x.shape[0], device=x.device) * time_t.item()
sigma_t_vec = torch.ones(x.shape[0], device=x.device) * sigma_t.item()
if clean_mask is None:
clean_mask = torch.zeros(x.shape[:-1], device=x.device)
cond_denoised = self.forward(xt=x,
discrete_noise=time_t_vec,
reveal_mask=clean_mask,
continuous_noise=sigma_t_vec,
embedding=embedding_cache).double()
denoised = cond_denoised.exp()
x0_hat = sample_categorical(denoised)
embedding_hat = self.backbone.get_embedding(x0_hat)
d = (embedding_cache - embedding_hat) / (sigma_t ** 2)
new_embedding_cache = embedding_cache - dt* d * self.step_size
return new_embedding_cache, x0_hat
def sigma_from_time_vectorized(
t: torch.Tensor, sigmas: torch.Tensor, errors: torch.Tensor
) -> torch.Tensor:
"""
Vectorized version of sigma_from_time using linear interpolation.
Args:
t: tensor of target Bayes error rates (ε ∈ [0, 1])
sigmas: 1D tensor of σ values
errors: 1D tensor of corresponding ε(σ)
Returns:
Tensor of interpolated σ values
"""
t = t.clamp(min=0.0, max=1.0)
t = t.to(sigmas.device)
# Normalize ε ∈ [0, 1] to indices in [0, len(errors)-1]
indices = torch.searchsorted(errors, t, right=True).clamp(1, len(errors) - 1)
i0 = indices - 1
i1 = indices
e0 = errors[i0]
e1 = errors[i1]
s0 = sigmas[i0]
s1 = sigmas[i1]
interp_t = (t - e0) / (e1 - e0 + 1e-8)
return s0 + interp_t * (s1 - s0)
def sample_categorical(categorical_probs):
gumbel_norm = 1e-10 - (torch.rand_like(categorical_probs) + 1e-10).log()
return (categorical_probs / gumbel_norm).argmax(dim=-1)
def expected_rank(d: int, a: torch.Tensor, sigma: torch.Tensor) -> torch.Tensor:
"""
Vectorized expected rank of the true coordinate in a one-hot vector
corrupted by Gaussian noise.
Args:
d: dimension (number of coordinates)
a: amplitude(s) of the true coordinate, scalar or tensor
sigma: noise standard deviation(s), scalar or tensor
Returns:
Expected rank(s), same broadcasted shape as a and sigma
"""
# ensure tensors
a = torch.as_tensor(a, dtype=torch.float32)
sigma = torch.as_tensor(sigma, dtype=torch.float32)
# standard normal CDF in torch
Phi = torch.distributions.Normal(0.0, 1.0).cdf
# compute p = P(other beats true coordinate)
p = Phi(-a / (sigma * torch.sqrt(torch.tensor(2.0, dtype=torch.float32))))
return (d - 1) * p / d
# below is the noise schedule used for embedding diffusion -- follows the VE SDE schedule
def training_sigma_ve(t: torch.Tensor, sigma_min: float, sigma_max: float) -> torch.Tensor:
"""
Map training time t ∈ [0,1] to sigma via log-linear schedule.
σ(t) = σ_min * (σ_max / σ_min) ** t
Args:
t: tensor of shape [...] with values in [0,1]
sigma_min: minimum noise level
sigma_max: maximum noise level
Returns:
σ(t) with same shape as t
"""
t = torch.as_tensor(t, dtype=torch.float32)
log_ratio = torch.log(torch.tensor(sigma_max / sigma_min, dtype=torch.float32).to(t.device))
return sigma_min * torch.exp(t * log_ratio)
def inference_sigmas(n_steps: int, sigma_min: float, sigma_max: float) -> torch.Tensor:
"""
Log-linear schedule of sigmas for inference, decreasing from sigma_max to sigma_min.
σ_k = σ_max * (σ_min / σ_max) ** (k / (n_steps-1))
Args:
n_steps: number of inference steps
sigma_min: minimum noise level
sigma_max: maximum noise level
Returns:
Tensor of shape [n_steps], from σ_max down to σ_min
"""
ks = torch.linspace(0, 1, n_steps, dtype=torch.float32)
return sigma_max * (sigma_min / sigma_max) ** ks
def gaussian_to_disc_corruption(K: int, sigma: float) -> float:
normal = Normal(0., 1.)
s = torch.linspace(-4*sigma, 1+6*sigma, 1001)
phi = normal.log_prob((s-1)/sigma).exp() / sigma
cdf_power = normal.cdf(s/sigma).pow(K-1)
return 1.0 - torch.trapz(cdf_power * phi, s).item()
def build_error_to_sigma_schedule(
vocab_size: int,
sigma_range: tuple = (0.01, 1.0),
num_points: int = 500,
device: str = 'cpu'
) -> tuple[torch.Tensor, torch.Tensor]:
sigmas = torch.linspace(*sigma_range, steps=num_points, device=device)
errors = torch.tensor(
[gaussian_to_disc_corruption(vocab_size, s.item()) for s in sigmas],
device=device
)
return sigmas, errors
def error_to_sigma(
error_rates: torch.Tensor,
sigmas: torch.Tensor,
errors: torch.Tensor
) -> torch.Tensor:
error_rates = error_rates.clamp(min=0.0, max=1.0)
# Find bracketing indices
indices = torch.searchsorted(errors, error_rates, right=True).clamp(1, len(errors)-1)
i0 = indices - 1
i1 = indices
# Get bracketing values
e0 = errors[i0]
e1 = errors[i1]
s0 = sigmas[i0]
s1 = sigmas[i1]
# Linear interpolation
interp_t = (error_rates - e0) / (e1 - e0 + 1e-8)
return s0 + interp_t * (s1 - s0)
def sigma_to_error(
sigma_vals: torch.Tensor,
sigmas: torch.Tensor,
errors: torch.Tensor
) -> torch.Tensor:
sigma_vals = sigma_vals.clamp(min=sigmas[0], max=sigmas[-1])
# Find bracketing indices
indices = torch.searchsorted(sigmas, sigma_vals, right=True).clamp(1, len(sigmas)-1)
i0 = indices - 1
i1 = indices
# Get bracketing values
s0 = sigmas[i0]
s1 = sigmas[i1]
e0 = errors[i0]
e1 = errors[i1]
# Linear interpolation
interp_t = (sigma_vals - s0) / (s1 - s0 + 1e-8)
return e0 + interp_t * (e1 - e0)
import models
import os
import time
import pdb
import collections
import copy
import pickle
import fsspec
import numpy as np
import torch
import torch.nn.functional as F
from torch.distributions import Normal
import trainer_base
import utils
import torch
from omegaconf import DictConfig
# torch.serialization.add_safe_globals([DictConfig])
class AR(trainer_base.TrainerBase):
def __init__(self, config, tokenizer):
vocab_size = tokenizer.vocab_size
if not hasattr(tokenizer, "mask_token") or tokenizer.mask_token is None:
self.mask_index = vocab_size
vocab_size += 1
else:
self.mask_index = tokenizer.mask_token_id
super().__init__(config, tokenizer, vocab_size=vocab_size)
self.save_hyperparameters()
self._validate_configuration()
self.temp = config.algo.get("temp", 1.0)
def _validate_configuration(self):
super()._validate_configuration()
assert not self.config.algo.time_conditioning
assert self.config.prior.type == "none"
def _process_model_input(self, x0, valid_tokens):
input_tokens = x0[:, :-1]
output_tokens = x0[:, 1:]
valid_tokens = valid_tokens[:, 1:]
return input_tokens, output_tokens, valid_tokens
def nll(self, input_tokens, output_tokens, current_accumulation_step, *args):
del current_accumulation_step
output = self.backbone(input_tokens, None)
output[:, :, self.mask_index] = self.neg_infinity
output = output.log_softmax(-1)
return -output.gather(-1, output_tokens[:, :, None])[:, :, 0]
def generate_samples(self, num_samples, prompt_mask=None, prompt_tokens=None, **kwargs):
# precompute token buffer
num_pred_tokens = self.num_tokens - 1
x = torch.zeros(
(num_samples, num_pred_tokens + 1), dtype=torch.long, device=self.device
)
if prompt_mask is not None and prompt_tokens is not None:
x = (prompt_mask * prompt_tokens + (1 - prompt_mask) * x).long()
start = prompt_mask.sum(1).max().long().item() - 1
else:
start = 0
x[:, 0] = self.tokenizer.bos_token_id
# precompute noise
noise = (
torch.distributions.Gumbel(0, 1)
.sample((num_samples, num_pred_tokens, self.vocab_size))
.to(self.device)
)
if self.config.sampling.use_float64:
noise = noise.to(torch.float64)
for i in range(start, num_pred_tokens):
output = self.backbone(x[:, : i + 1], None)
output[:, :, self.mask_index] = self.neg_infinity
output = output / self.temp
output = output.log_softmax(-1)
y = (output[:, -1, :] + noise[:, i, :]).argmax(-1)
x[:, i + 1] = y
return x
def _process_sigma(self, sigma):
del sigma
return None
class MDLM(trainer_base.AbsorbingState):
def __init__(self, config, tokenizer):
super().__init__(config, tokenizer)
self._validate_configuration()
def _validate_configuration(self):
# ancestral sampling isn't desirable because it's slow
assert self.sampler == "ancestral_cache" or self.sampler == "posterior"
def _process_model_output(self, model_output, xt, sigma):
del sigma
model_output = model_output / self.temp
model_output[:, :, self.mask_index] += self.neg_infinity