| import itertools |
| import os |
| import random |
| import inspect |
|
|
| from dataclasses import dataclass |
|
|
|
|
| from tqdm import tqdm |
| import hydra.utils |
| import lightning as L |
| import numpy as np |
| import torch |
| import torch.nn.functional as F |
| import transformers |
| import wandb |
| from torch.cuda.amp import autocast |
| import torch.distributed as dist |
| import dataloader |
| import metrics |
| import models |
| import utils |
| from omegaconf import ListConfig |
|
|
|
|
| @dataclass |
| class Loss: |
| loss: torch.FloatTensor |
| nlls: torch.FloatTensor |
| prior_loss: torch.FloatTensor |
| num_tokens: torch.FloatTensor |
|
|
|
|
| class LogLinear(torch.nn.Module): |
| def __init__(self): |
| super().__init__() |
| self.eps = 1e-3 |
|
|
| def forward(self, t): |
| t = (1 - self.eps) * t |
| alpha_t = 1 - t |
| dalpha_t = - (1 - self.eps) + t * 0 |
| assert alpha_t.shape == dalpha_t.shape |
| return dalpha_t, alpha_t |
|
|
| def sample_categorical(categorical_probs, temperature=1.0): |
| categorical_probs = categorical_probs.to(torch.float64) |
| if temperature != 1.0: |
| categorical_probs = categorical_probs.pow(1.0 / temperature) |
| gumbel_norm = ( |
| 1e-10 |
| - (torch.rand_like(categorical_probs) + 1e-10).log()) |
| return (categorical_probs / gumbel_norm.to(categorical_probs.dtype)).argmax(dim=-1) |
|
|
| def _unsqueeze(x, reference): |
| return x.view( |
| * x.shape, |
| * ((1,) * (len(reference.shape) - len(x.shape)))) |
|
|
|
|
| class TrainerBase(L.LightningModule): |
| def __init__( |
| self, |
| config, |
| tokenizer: transformers.PreTrainedTokenizer, |
| vocab_size=None): |
| super().__init__() |
| self.save_hyperparameters() |
| self.config = config |
| if hasattr(self.config.algo, 'ignore_bos'): |
| self.ignore_bos = config.algo.ignore_bos |
| else: |
| self.ignore_bos = False |
| if hasattr(self.config.algo, 'loss_type'): |
| self.loss_type = config.algo.loss_type |
| self.tokenizer = tokenizer |
| if vocab_size is None: |
| self.vocab_size = len(self.tokenizer) |
| else: |
| self.vocab_size = vocab_size |
| self.sampler = self.config.sampling.predictor |
| self.antithetic_sampling = self.config.training.antithetic_sampling |
| self.parameterization = self.config.algo.parameterization |
| if self.config.algo.backbone == 'dit': |
| self.backbone = models.dit.DIT( |
| self.config, vocab_size=self.vocab_size) |
| elif self.config.algo.backbone == 'dimamba': |
| self.backbone = models.dimamba.DiMamba( |
| self.config, |
| vocab_size=self.vocab_size, |
| pad_token_id=self.tokenizer.pad_token_id) |
| elif self.config.algo.backbone == 'hf_dit': |
| self.backbone = transformers.AutoModelForMaskedLM.from_pretrained( |
| config.eval.checkpoint_path, trust_remote_code=True) |
| |
| self._pending_ema_state = None |
| self.T = self.config.algo.T |
| self.num_tokens = self.config.model.length |
| self.softplus = torch.nn.Softplus() |
| self.p_nucleus = self.config.sampling.p_nucleus |
| |
| self.noise = LogLinear() |
|
|
| self.metrics = metrics.Metrics( |
| gen_ppl_eval_model_name_or_path=self.config.eval.gen_ppl_eval_model_name_or_path, |
| eval_ppl_batch_size=self.config.eval.perplexity_batch_size) |
|
|
| if self.config.training.ema > 0: |
| self.ema = models.ema.ExponentialMovingAverage( |
| self._get_parameters(), |
| decay=self.config.training.ema) |
| else: |
| self.ema = None |
|
|
|
|
| self.lr = self.config.optim.lr |
| self.sampling_eps = self.config.training.sampling_eps |
| self.time_conditioning = self.config.algo.time_conditioning |
| self.neg_infinity = -1000000.0 |
| self.fast_forward_epochs = None |
| self.fast_forward_batches = None |
| self.target_tokens = None |
|
|
|
|
| def _validate_configuration(self): |
| assert self.config.algo.backbone in {'dit', 'hf_dit'} |
| if self.config.algo.parameterization == 'ar': |
| assert not self.config.algo.time_conditioning |
| assert self.config.prior.type == 'none' |
|
|
| if self.parameterization in {'score', 'mean'}: |
| assert self.time_conditioning |
| if self.T > 0: |
| assert self.parameterization != 'score' |
|
|
| def to(self, *args, **kwargs): |
| self = super().to(*args, **kwargs) |
| self.metrics.to(*args, **kwargs) |
| return self |
|
|
| def q_xt(self, x, alpha_t): |
| raise NotImplementedError |
|
|
| def _get_parameters(self): |
| return itertools.chain(self.backbone.parameters(), |
| self.noise.parameters()) |
|
|
| def _eval_mode(self): |
| if self.ema and not self.config.eval.disable_ema: |
| print('Copying EMA parameters to model') |
| self.ema.store(self._get_parameters()) |
| self.ema.copy_to(self._get_parameters()) |
| else: |
| print('No EMA parameters') |
| self.backbone.eval() |
| self.noise.eval() |
|
|
| def _train_mode(self): |
| if self.ema: |
| self.ema.restore(self._get_parameters()) |
| self.backbone.train() |
| self.noise.train() |
|
|
| def load_state_dict(self, state_dict, strict=True): |
| if any('_orig_mod' in k for k in state_dict.keys()): |
| new_state_dict = {} |
| for k, v in state_dict.items(): |
| new_key = k.replace('._orig_mod.', '.') |
| new_state_dict[new_key] = v |
| state_dict = new_state_dict |
| |
| if hasattr(self, 'teacher_model') and self.teacher_model is not None: |
| filtered_state_dict = {} |
| for k, v in state_dict.items(): |
| if not k.startswith('teacher_model.'): |
| filtered_state_dict[k] = v |
| state_dict = filtered_state_dict |
| |
| ret = super().load_state_dict(state_dict, strict=strict) |
| |
| if self.ema: |
| ema_sd = getattr(self, "_pending_ema_state", None) |
| ema_loaded = False |
|
|
| if ema_sd is not None: |
| try: |
| self.ema.load_state_dict(ema_sd) |
| current_params = list(self._get_parameters()) |
|
|
| if len(self.ema.shadow_params) == len(current_params): |
| shapes_match = all( |
| s.shape == p.shape |
| for s, p in zip(self.ema.shadow_params, current_params) |
| ) |
| if shapes_match: |
| ema_loaded = True |
| else: |
| print("[WARNING] EMA shape mismatch - will reinitialize from loaded weights") |
| else: |
| print("[WARNING] EMA count mismatch - will reinitialize from loaded weights") |
|
|
| except Exception as e: |
| print(f"[WARNING] Failed to load EMA after weights load: {e}") |
|
|
| if not ema_loaded: |
| print("Initializing EMA from loaded model weights") |
| import models.ema |
| self.ema = models.ema.ExponentialMovingAverage( |
| list(self._get_parameters()), |
| decay=self.config.training.ema |
| ) |
|
|
| self._pending_ema_state = None |
|
|
| return ret |
|
|
| def on_load_checkpoint(self, checkpoint): |
| if self.ema: |
| self._pending_ema_state = checkpoint.get('ema', None) |
| |
| |
| self.fast_forward_epochs = checkpoint['loops'][ |
| 'fit_loop']['epoch_progress']['current']['completed'] |
| self.fast_forward_batches = checkpoint['loops'][ |
| 'fit_loop']['epoch_loop.batch_progress'][ |
| 'current']['completed'] |
|
|
| def on_save_checkpoint(self, checkpoint): |
| if self.ema: |
| checkpoint['ema'] = self.ema.state_dict() |
| |
| |
| |
| |
| checkpoint['loops']['fit_loop'][ |
| 'epoch_loop.batch_progress']['total'][ |
| 'completed'] = checkpoint['loops']['fit_loop'][ |
| 'epoch_loop.automatic_optimization.optim_progress'][ |
| 'optimizer']['step']['total'][ |
| 'completed'] * self.trainer.accumulate_grad_batches |
| checkpoint['loops']['fit_loop'][ |
| 'epoch_loop.batch_progress']['current'][ |
| 'completed'] = checkpoint['loops']['fit_loop'][ |
| 'epoch_loop.automatic_optimization.optim_progress'][ |
| 'optimizer']['step']['current'][ |
| 'completed'] * self.trainer.accumulate_grad_batches |
| |
| |
| |
| checkpoint['loops']['fit_loop'][ |
| 'epoch_loop.state_dict'][ |
| '_batches_that_stepped'] = checkpoint['loops']['fit_loop'][ |
| 'epoch_loop.automatic_optimization.optim_progress'][ |
| 'optimizer']['step']['total']['completed'] |
| if 'sampler' not in checkpoint.keys(): |
| checkpoint['sampler'] = {} |
| if hasattr(self.trainer.train_dataloader.sampler, |
| 'state_dict'): |
| sampler_state_dict = self.trainer.\ |
| train_dataloader.sampler.state_dict() |
| checkpoint['sampler'][ |
| 'random_state'] = sampler_state_dict.get( |
| 'random_state', None) |
| else: |
| checkpoint['sampler']['random_state'] = None |
|
|
| def on_train_start(self): |
| if self.ema: |
| self.ema.move_shadow_params_to_device(self.device) |
| |
| |
| distributed = ( |
| self.trainer._accelerator_connector.use_distributed_sampler |
| and self.trainer._accelerator_connector.is_distributed) |
| if distributed: |
| sampler_cls = dataloader.FaultTolerantDistributedSampler |
| else: |
| sampler_cls = dataloader.RandomFaultTolerantSampler |
| updated_dls = [] |
| for dl in self.trainer.fit_loop._combined_loader.flattened: |
| if hasattr(dl.sampler, 'shuffle'): |
| dl_sampler = sampler_cls(dl.dataset, shuffle=dl.sampler.shuffle) |
| else: |
| dl_sampler = sampler_cls(dl.dataset) |
| if (distributed |
| and self.fast_forward_epochs is not None |
| and self.fast_forward_batches is not None): |
| dl_sampler.load_state_dict({'epoch': self.fast_forward_epochs, 'counter': (self.fast_forward_batches * self.config.loader.batch_size)}) |
| updated_dls.append( |
| torch.utils.data.DataLoader( |
| dl.dataset, |
| batch_size=self.config.loader.batch_size, |
| num_workers=self.config.loader.num_workers, |
| pin_memory=self.config.loader.pin_memory, |
| sampler=dl_sampler, |
| shuffle=False, |
| persistent_workers=True)) |
| self.trainer.fit_loop._combined_loader.flattened = updated_dls |
|
|
| def optimizer_step(self, *args, **kwargs): |
| super().optimizer_step(*args, **kwargs) |
| if self.ema: |
| self.ema.update(self._get_parameters()) |
|
|
| def _process_sigma(self, sigma): |
| raise NotImplementedError |
|
|
| def _process_model_output(self, model_output, xt, sigma): |
| raise NotImplementedError |
|
|
| def forward(self, xt, sigma, sigma_prime=None, use_jvp_attn=False): |
|
|
| sigma = self._process_sigma(sigma) |
| if sigma_prime is not None: |
| sigma_prime = self._process_sigma(sigma_prime) |
| with torch.amp.autocast(device_type=self.device.type, dtype=torch.float32): |
| model_output = self.backbone(xt, sigma, sigma_prime, use_jvp_attn=use_jvp_attn) |
| |
| return self._process_model_output( |
| model_output=model_output, xt=xt, sigma=sigma) |
|
|
| def on_train_epoch_start(self): |
| self.metrics.reset() |
| assert self.metrics.train_nlls.nll.mean_value == 0 |
| assert self.metrics.train_nlls.nll.weight == 0 |
|
|
| def training_step(self, batch, batch_idx): |
| current_accumulation_step = ( |
| batch_idx % self.trainer.accumulate_grad_batches) |
|
|
| losses = self._loss(batch['input_ids'], |
| batch['attention_mask'], |
| current_accumulation_step, |
| train_mode=True, |
| xT=None if 'xT' not in batch else batch['xT'], |
| given_t=batch['given_t'] if 'given_t' in batch else None, |
| not_sampling_t=self.config.training.not_sampling_t |
| ) |
| self.metrics.update_train(losses.nlls, losses.prior_loss, |
| losses.num_tokens) |
| self.log(name='trainer/loss', |
| value=losses.loss.item(), |
| on_step=True, |
| on_epoch=False, |
| sync_dist=True) |
| return losses.loss |
|
|
| def on_train_epoch_end(self): |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| return |
|
|
| def on_validation_epoch_start(self): |
| self.metrics.reset() |
| self._eval_mode() |
| assert self.metrics.valid_nlls.nll.mean_value == 0 |
| assert self.metrics.valid_nlls.nll.weight == 0 |
|
|
| def validation_step(self, batch, batch_idx): |
| del batch_idx |
| losses = self._loss(batch['input_ids'], |
| batch['attention_mask'], |
| xT=None if 'xT' not in batch else batch['xT'] |
| ) |
| self.metrics.update_valid(losses.nlls, losses.prior_loss, |
| losses.num_tokens) |
| return losses.loss |
|
|
| def on_validation_epoch_end(self): |
|
|
| for k, v in self.metrics.valid_nlls.items(): |
| self.log(name=k, value=v.compute(), on_step=False, |
| on_epoch=True, sync_dist=True) |
| if ((self.config.eval.compute_perplexity_on_sanity |
| or not self.trainer.sanity_checking) |
| and self.config.eval.generate_samples): |
|
|
| step_list = self.config.sampling.steps |
| if isinstance(step_list, ListConfig): |
| step_list = list(step_list) |
| elif isinstance(step_list, int): |
| step_list = [step_list] |
|
|
| for num_steps in step_list: |
| if hasattr(self.metrics, 'gen_ppl'): |
| self.metrics.gen_ppl.reset() |
| if hasattr(self.metrics, 'sample_entropy'): |
| self.metrics.sample_entropy.reset() |
|
|
| current_text_samples = [] |
|
|
| for _ in range(self.config.sampling.num_sample_batches): |
| samples = self.generate_samples( |
| num_samples=self.config.loader.eval_batch_size, |
| num_steps=num_steps |
| ) |
|
|
| self.metrics.record_entropy(samples) |
|
|
| decoded_batch = self.tokenizer.batch_decode(samples) |
|
|
| if len(current_text_samples) < self.config.sampling.num_sample_log: |
| current_text_samples.extend(decoded_batch) |
|
|
| if self.config.eval.compute_generative_perplexity: |
| self.metrics.record_generative_perplexity( |
| decoded_batch, self.num_tokens, self.device) |
|
|
| if self.config.eval.compute_generative_perplexity: |
| self.log(f'val/gen_ppl_T{num_steps}', |
| self.metrics.gen_ppl.compute(), |
| on_epoch=True, |
| on_step=False, |
| sync_dist=True) |
| self.log(f'val/sample_entropy_T{num_steps}', |
| self.metrics.sample_entropy.compute(), |
| on_epoch=True, |
| on_step=False, |
| sync_dist=True) |
|
|
| if self.trainer.global_rank == 0 and hasattr(self.trainer.logger, 'log_table'): |
| log_samples = current_text_samples[:self.config.sampling.num_sample_log] |
|
|
| self.trainer.logger.log_table( |
| key=f'samples_T{num_steps}@global_step{self.global_step}', |
| columns=['Generated Samples'], |
| data=[[s] for s in log_samples] |
| ) |
|
|
| self._train_mode() |
|
|
| def on_test_epoch_start(self): |
| self._eval_mode() |
| self.xTx0s = [] |
|
|
| def test_step(self, batch, batch_idx): |
| xT = batch |
| x0 = self.generate_samples(xT.shape[0], xT=xT.detach().clone()) |
| pair = torch.stack([xT, x0], dim=0) |
| self.xTx0s.append(pair) |
| return 0. |
|
|
| def on_test_epoch_end(self): |
| |
| self.xTx0s = torch.cat(self.xTx0s, dim=1) |
| torch.distributed.barrier() |
|
|
| |
| if torch.distributed.is_initialized(): |
| data_xTx0s_all = [torch.empty_like(self.xTx0s) for _ in range( |
| torch.distributed.get_world_size())] if self.trainer.global_rank == 0 else None |
| torch.distributed.gather(self.xTx0s, |
| data_xTx0s_all, |
| dst=0) |
|
|
| if self.trainer.global_rank == 0: |
| xTx0s = torch.cat(data_xTx0s_all, dim=1).cpu()[ |
| :, :self.config.sampling.num_reflow_samples] |
| xTs, x0s = xTx0s[0], xTx0s[1] |
|
|
| save_path = self.config.data.cache_dir |
| if not os.path.exists(save_path): |
| os.makedirs(save_path) |
|
|
| xTs = xTs.cpu().numpy() |
| x0s = x0s.cpu().numpy() |
| xT_path = os.path.join(save_path, 'xT.npy') |
| x0_path = os.path.join(save_path, 'x0.npy') |
| np.save(xT_path, xTs) |
| np.save(x0_path, x0s) |
| print('xT shape:', xTs.shape) |
| print('x0 shape:', x0s.shape) |
| print('xT saved to:', xT_path) |
| print('x0 saved to:', x0_path) |
| return |
| def configure_optimizers(self): |
| optimizer = torch.optim.AdamW( |
| self._get_parameters(), |
| lr=self.config.optim.lr, |
| betas=(self.config.optim.beta1, |
| self.config.optim.beta2), |
| eps=self.config.optim.eps, |
| weight_decay=self.config.optim.weight_decay) |
|
|
| scheduler = hydra.utils.instantiate( |
| self.config.lr_scheduler, optimizer=optimizer) |
| scheduler_dict = {'scheduler': scheduler, |
| 'interval': 'step', |
| 'monitor': 'val/loss', |
| 'name': 'trainer/lr'} |
| return [optimizer], [scheduler_dict] |
|
|
| def generate_samples(self, num_samples, num_steps, eps, xT, given_t): |
| raise NotImplementedError |
|
|
| def restore_model_and_sample(self, num_steps, eps=1e-5): |
| """Generate samples from the model.""" |
| |
| self._eval_mode() |
|
|
| step_list = self.config.sampling.steps |
| if isinstance(step_list, ListConfig): |
| step_list = list(step_list) |
| elif isinstance(step_list, int): |
| step_list = [step_list] |
| all_samples = [] |
| for num_steps in step_list: |
| batch_samples = self.generate_samples( |
| num_samples=self.config.loader.eval_batch_size, |
| num_steps=num_steps, |
| eps=eps) |
| |
| |
| if isinstance(batch_samples, torch.Tensor): |
| batch_samples = [batch_samples[i] for i in range(batch_samples.shape[0])] |
| all_samples.extend(batch_samples) |
| self._train_mode() |
| return all_samples |
|
|
| def _process_model_input(self, x0, valid_tokens): |
| raise NotImplementedError |
|
|
| def nll(self, input_tokens, output_tokens, |
| current_accumulation_step=None, train_mode=False): |
| raise NotImplementedError |
|
|
| def _loss(self, x0, valid_tokens, |
| current_accumulation_step=None, |
| train_mode=False, |
| xT=None, given_t=None, not_sampling_t=False): |
| (input_tokens, output_tokens, |
| valid_tokens) = self._process_model_input( |
| x0, valid_tokens) |
| loss = self.nll(input_tokens, output_tokens, |
| current_accumulation_step, train_mode) |
| |
|
|
| assert loss.ndim == 2 |
| if self.ignore_bos: |
| loss[:, 1:] = loss[:, 1:] |
| valid_tokens[:, 1:] = valid_tokens[:, 1:] |
|
|
| nlls = (loss * valid_tokens).sum() |
| num_tokens = valid_tokens.sum() |
| token_nll = nlls / num_tokens |
|
|
| return Loss(loss=token_nll, |
| nlls=nlls, |
| prior_loss=0.0, |
| num_tokens=num_tokens) |
|
|
|
|
| class Diffusion(TrainerBase): |
| def _validate_configuration(self): |
| super()._validate_configuration() |
| assert self.config.sampling.noise_removal in { |
| 'none', 'ancestral', 'greedy', 'flow'} |
| assert self.config.training.loss_type in {'elbo', 'low_var', 'mse', 'adaptive_l2', 'flow'} |
| if self.config.sampling.noise_removal == 'greedy': |
| assert self.sampler != 'analytic' |
| assert self.parameterization in {'mean', 'subs'} |
|
|
| def _process_model_input(self, x0, valid_tokens): |
| return x0, None, valid_tokens |
|
|
| def _process_sigma(self, sigma): |
| assert sigma.ndim == 2 |
| sigma = sigma.mean(-1).squeeze() |
| if sigma.ndim == 0: |
| sigma = sigma.unsqueeze(0) |
| if not self.time_conditioning: |
| sigma = torch.zeros_like(sigma) |
| assert sigma.ndim == 1, sigma.shape |
| return sigma |
|
|
| def _sample_t(self, n, accum_step): |
| if accum_step is not None: |
| batch_dim = n |
| n = self.config.loader.global_batch_size |
| _eps_t = torch.rand(n, device=self.device) |
| if self.antithetic_sampling: |
| offset = torch.arange(n, device=self.device) / n |
| _eps_t = (_eps_t / n + offset) % 1 |
| t = (1 - self.sampling_eps) * _eps_t + self.sampling_eps |
| if accum_step is not None: |
| t = t.chunk(self.trainer.num_nodes)[self.trainer.node_rank] |
| t = t.chunk(self.trainer.num_devices)[self.trainer.local_rank] |
| t = t.chunk(self.trainer.accumulate_grad_batches)[ |
| accum_step] |
| t = t[:batch_dim] |
| return t |
|
|
| def _sigma_from_alphat(self, alpha_t): |
| return -torch.log(alpha_t) |
|
|
| def _reconstruction_loss(self, x0): |
| t0 = torch.zeros(1, x0.shape[0], dtype=self.dtype, |
| device=self.device) |
| sigma_t0 = self._sigma_from_alphat(self.noise(t0)[1]) |
| model_output_t0 = self.forward(x0, sigma_t0) |
| return - torch.gather(input=model_output_t0, |
| dim=-1, |
| index=x0[:, :, None]).squeeze(-1) |
|
|
| def nll_per_token(self, model_output, xt, x0, alpha_t, |
| dalpha_t, low_var): |
| raise NotImplementedError |
|
|
| 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] |
| if self.T > 0: |
| t = (t * self.T).to(torch.int) |
| t = t / self.T |
| |
| t += (1 / self.T) |
|
|
| dalpha_t, alpha_t = self.noise(t) |
| alpha_t = alpha_t.unsqueeze(-1) |
| assert alpha_t.ndim == 2 |
| sigma = self._sigma_from_alphat(alpha_t) |
|
|
| xt = self.q_xt(x0, alpha_t) |
| log_x_theta = self.forward(xt, sigma=sigma) |
| utils.print_nans(log_x_theta, 'model_output') |
| return self.nll_per_token( |
| log_x_theta=log_x_theta, |
| xt=xt, |
| x0=x0, |
| alpha_t=alpha_t, |
| dalpha_t=dalpha_t, |
| low_var=train_mode and self.loss_type == 'low_var') |
|
|
| def _get_score(self, **kwargs): |
| del kwargs |
| raise NotImplementedError |
|
|
| def _denoiser_update(self, x, t): |
| raise NotImplementedError |
|
|
| def _analytic_update(self, x, t, dt): |
| raise NotImplementedError |
|
|
| def _ancestral_update(self, x, t, dt, p_x0, noise_removal_step): |
| raise NotImplementedError |
| |
| @torch.no_grad() |
| def generate_samples(self, num_samples, num_steps=None, |
| eps=1e-5): |
| if num_steps is None: |
| num_steps = self.config.sampling.steps |
| x = self.prior_sample(num_samples, self.num_tokens) |
| timesteps = torch.linspace( |
| 1, eps, num_steps + 1, device=self.device) |
| dt = (1 - eps) / num_steps |
| p_x0_cache = None |
|
|
| for i in range(num_steps): |
| t = timesteps[i] * torch.ones( |
| x.shape[0], 1, device=self.device) |
| if self.sampler == 'ancestral': |
| _, x = self._ancestral_update( |
| x=x, t=t, dt=dt, p_x0=None) |
| elif self.sampler == 'ancestral_cache': |
| p_x0_cache, x_next = self._ancestral_update( |
| x=x, t=t, dt=dt, p_x0=p_x0_cache) |
| if (not torch.allclose(x_next, x) |
| or self.time_conditioning): |
| |
| p_x0_cache = None |
| x = x_next |
| else: |
| x = self._analytic_update(x=x,t=t, dt=dt) |
|
|
| t0 = timesteps[-1] * torch.ones(x.shape[0], 1, |
| device=self.device) |
| if self.config.sampling.noise_removal == 'ancestral': |
| if self.sampler == 'analytic': |
| x = self._denoiser_update(x=x, t=t0) |
| else: |
| _, x = self._ancestral_update(x=x, t=t0, dt=None, |
| p_x0=p_x0_cache, |
| noise_removal_step=True) |
| elif self.config.sampling.noise_removal == 'greedy': |
| sigma = self._sigma_from_alphat(self.noise(t0)[1]) |
| x = self.forward(xt=x, sigma=sigma).argmax(dim=-1) |
| return x |
|
|
| @torch.no_grad |
| def _semi_ar_sampler( |
| self, n_samples, stride_length, num_strides, dt=0.001): |
| |
| ones = torch.ones(n_samples, dtype=self.dtype, |
| device=self.device) |
|
|
| num_steps = int(1 / dt) |
| sampling_steps = 0 |
| intermediate_tokens = [] |
| target = None |
| for _ in range(num_strides + 1): |
| p_x0_cache = None |
| x = self.prior_sample(n_samples, self.num_tokens) |
| if target is not None: |
| x[:, : -stride_length] = target |
| for i in range(num_steps + 1): |
| p_x0_cache, x_next = self._ancestral_update( |
| x=x, t=(1 - i * dt) * ones, dt=dt, p_x0=p_x0_cache) |
| if (not torch.allclose(x_next, x) |
| or self.time_conditioning): |
| p_x0_cache = None |
| sampling_steps += 1 |
| x = x_next |
| x = self.forward(x, 0 * ones).argmax(dim=-1) |
| intermediate_tokens.append( |
| x[:, :stride_length].cpu().numpy()) |
| target = x[:, stride_length:] |
|
|
| intermediate_tokens.append(target.cpu().numpy()) |
| intermediate_text_samples = [] |
| sequence_lengths = (( |
| np.concatenate(intermediate_tokens, axis=1)[:, 1:] |
| == self.tokenizer.eos_token_id).cumsum(-1) == 0).sum(-1) |
| for i in range(2, len(intermediate_tokens) + 1): |
| intermediate_text_samples.append( |
| self.tokenizer.batch_decode( |
| np.concatenate(intermediate_tokens[:i], axis=1))) |
| return (sampling_steps, intermediate_text_samples, |
| sequence_lengths) |
|
|
| def restore_model_and_semi_ar_sample( |
| self, stride_length, num_strides, dt=0.001): |
| """Generate samples from the model.""" |
| |
| |
| self._eval_mode() |
| (sampling_steps, samples, |
| sequence_lengths) = self._semi_ar_sampler( |
| n_samples=self.config.loader.eval_batch_size, |
| stride_length=stride_length, |
| num_strides=num_strides, |
| dt=dt) |
| self._train_mode() |
| return sampling_steps, samples, sequence_lengths |
|
|
|
|
| class AbsorbingState(Diffusion): |
| 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 |
| self.subs_masking = config.algo.subs_masking |
| super().__init__(config, tokenizer, |
| vocab_size=vocab_size) |
| self.save_hyperparameters() |
|
|
| def _validate_configuration(self): |
| super()._validate_configuration() |
| if self.parameterization in {'score', 'mean'}: |
| assert self.time_conditioning |
| assert not (self.parameterization == 'mean' |
| and self.T == 0) |
| if self.T > 0: |
| assert self.parameterization in {'mean', 'subs'} |
| if self.subs_masking: |
| assert self.parameterization == 'mean' |
|
|
| def q_xt(self, x, alpha_t): |
| """Computes the noisy sample xt. |
| |
| Args: |
| x: int torch.Tensor with shape (batch_size, |
| diffusion_model_input_length), input. |
| alpha_t: float torch.Tensor with shape (batch_size, 1). |
| """ |
| move_indices = torch.rand( |
| * x.shape, device=x.device) < 1 - alpha_t |
| xt = torch.where(move_indices, self.mask_index, x) |
| if self.ignore_bos: |
| xt[:, 0] = x[:, 0] |
| return xt |
|
|
| def prior_sample(self, *batch_dims): |
| return self.mask_index * torch.ones( |
| * batch_dims, dtype=torch.int64, device=self.device) |
|
|
| def _ancestral_update(self, x, t, dt, p_x0=None, |
| noise_removal_step=False): |
| _, alpha_t = self.noise(t) |
| if noise_removal_step: |
| alpha_s = torch.ones_like(alpha_t) |
| else: |
| _, alpha_s = self.noise(t - dt) |
| assert alpha_t.ndim == 2 |
| if p_x0 is None: |
| p_x0 = self.forward( |
| x, self._sigma_from_alphat(alpha_t)).exp() |
|
|
| q_xs = p_x0 * (alpha_s - alpha_t)[:, :, None] |
| q_xs[:, :, self.mask_index] = 1 - alpha_s |
| _x = sample_categorical(q_xs) |
|
|
| copy_flag = (x != self.mask_index).to(x.dtype) |
| return p_x0, copy_flag * x + (1 - copy_flag) * _x |
|
|
| def _staggered_score(self, score, dsigma): |
| score = score.clone() |
| extra_const = (1 - dsigma.exp()) * score.sum(dim=-1) |
| score *= dsigma.exp()[:, None] |
| score[..., self.mask_index] += extra_const |
| return score |
|
|
| def _analytic_update(self, x, t, dt): |
| sigma_t = self._sigma_from_alphat(self.noise(t)[1]) |
| sigma_s = self._sigma_from_alphat(self.noise(t - dt)[1]) |
| dsigma = sigma_t - sigma_s |
| score = self._get_score(x, sigma_t) |
| if self.config.sampling.use_float64: |
| score = score.to(torch.float64) |
| stag_score = self._staggered_score(score, dsigma) |
| probs = stag_score * self._transp_transition(x, dsigma) |
| return sample_categorical(probs) |
|
|
| def _denoiser_update(self, x, t): |
| sigma = self._sigma_from_alphat(self.noise(t)[1]) |
| score = self._get_score(x, sigma) |
| if self.config.sampling.use_float64: |
| score = score.to(torch.float64) |
| stag_score = self._staggered_score(score, sigma) |
| probs = stag_score * self._transp_transition(x, sigma) |
| probs[..., self.mask_index] = 0 |
| samples = sample_categorical(probs) |
| return samples |
|
|
| def _transp_transition(self, i, sigma): |
| sigma = _unsqueeze(sigma, reference=i[..., None]) |
| edge = torch.exp(-sigma) * F.one_hot( |
| i, num_classes=self.vocab_size) |
| edge += torch.where(i == self.mask_index, |
| 1 - torch.exp(-sigma).squeeze(-1), |
| 0)[..., None] |
| return edge |
|
|
|
|
| class UniformState(Diffusion): |
| def _validate_configuration(self): |
| super()._validate_configuration() |
| assert self.time_conditioning |
| assert self.parameterization == 'mean' |
| if self.config.algo.name != 'distillation': |
| assert self.T == 0 |
|
|
| def q_xt(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 |
|
|
| def prior_sample(self, *batch_dims): |
| return torch.randint( |
| 0, self.vocab_size, batch_dims, dtype=torch.int64, |
| device=self.device) |