| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| import time |
| import warnings |
| import copy |
| from dataclasses import dataclass |
| from typing import Any, Dict, Optional, Tuple, Union |
|
|
| import torch |
| import torch.distributions as dists |
| from torch.nn import functional as F |
| from transformers import __version__ |
| from transformers.generation.configuration_utils import ( |
| GenerationConfig |
| ) |
| from transformers.utils import ( |
| ModelOutput, |
| is_torchdynamo_compiling, |
| logging, |
| ) |
|
|
| logger = logging.get_logger(__name__) |
|
|
|
|
| def top_p_logits(logits, top_p=None): |
| sorted_logits, sorted_indices = torch.sort(logits, descending=True) |
| cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1) |
| sorted_indices_to_remove = cumulative_probs > top_p |
| |
| sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone() |
| sorted_indices_to_remove[..., 0] = 0 |
|
|
| mask = torch.zeros_like(logits, dtype=torch.bool, device=logits.device) |
| mask = mask.scatter_(-1, sorted_indices, sorted_indices_to_remove) |
| logits = logits.masked_fill(mask, torch.finfo(logits.dtype).min) |
| return logits |
|
|
| def top_k_logits(logits, top_k=None): |
| top_k = min(top_k, logits.size(-1)) |
| |
| indices_to_remove = logits < torch.topk(logits, top_k)[0][..., -1, None] |
| logits = logits.masked_fill(indices_to_remove, torch.finfo(logits.dtype).min) |
| return logits |
|
|
|
|
| def sample_tokens(logits, temperature=0.0, top_p=None, top_k=None, margin_confidence=False, neg_entropy=False): |
|
|
| if temperature > 0: |
| logits = logits / temperature |
| if top_p is not None and top_p < 1: |
| logits = top_p_logits(logits, top_p) |
| if top_k is not None: |
| logits = top_k_logits(logits, top_k) |
| probs = torch.softmax(logits, dim=-1) |
|
|
| if temperature > 0: |
| try: |
| x0 = dists.Categorical(probs=probs).sample() |
| confidence = torch.gather(probs, -1, x0.unsqueeze(-1)).squeeze(-1) |
| except: |
| confidence, x0 = probs.max(dim=-1) |
| else: |
| confidence, x0 = probs.max(dim=-1) |
| |
| if margin_confidence: |
| sorted_probs, _ = torch.sort(probs, dim=-1, descending=True) |
| |
| top1_probs = sorted_probs[:, 0] |
| top2_probs = sorted_probs[:, 1] |
| |
| confidence = top1_probs - top2_probs |
| |
| if neg_entropy: |
| epsilon = 1e-10 |
| log_probs = torch.log(probs + epsilon) |
| confidence = torch.sum(probs * log_probs, dim=-1) |
| |
| return confidence, x0 |
|
|
|
|
| def sample_tokens_with_entropy(logits, temperature=1.0): |
| """Sample tokens and return their entropy values. |
| |
| Used by multi-block generation for entropy-based token selection. |
| Returns (entropy, samples) where lower entropy = higher confidence. |
| """ |
| original_probs = torch.softmax(logits, dim=-1) |
| log_probs = torch.log(original_probs + 1e-8) |
| entropy = -torch.sum(original_probs * log_probs, dim=-1) |
| |
| if temperature == 0: |
| samples = torch.argmax(logits, dim=-1) |
| else: |
| scaled_logits = logits / temperature |
| probs = torch.softmax(scaled_logits, dim=-1) |
| samples = torch.multinomial(probs, num_samples=1).squeeze(-1) |
| |
| return entropy, samples |
|
|
|
|
| @dataclass |
| class DreamModelOutput(ModelOutput): |
| sequences: torch.LongTensor = None |
| history: Optional[Tuple[torch.FloatTensor]] = None |
| |
| nfe: Optional[int] = None |
|
|
|
|
| class DreamGenerationConfig(GenerationConfig): |
| def __init__(self, **kwargs): |
| self.temperature: float = kwargs.pop("temperature", 0.0) |
| self.top_p: Optional[float] = kwargs.pop("top_p", None) |
| self.top_k: Optional[int] = kwargs.pop("top_k", None) |
| self.max_length = kwargs.pop("max_length", 20) |
| self.max_new_tokens = kwargs.pop("max_new_tokens", None) |
| |
| self.eps: float = kwargs.pop("eps", 1e-3) |
| self.steps: int = kwargs.pop("steps", 512) |
| self.alg: str = kwargs.pop("alg", 'origin') |
| self.alg_temp: Optional[float] = kwargs.pop("alg_temp", None) |
|
|
| |
| self.num_return_sequences: int = kwargs.pop("num_return_sequences", 1) |
| self.return_dict_in_generate: bool = kwargs.pop("return_dict_in_generate", False) |
| self.output_history: bool = kwargs.pop("output_history", False) |
|
|
| |
| self.mask_token_id = kwargs.pop("mask_token_id", None) |
| self.pad_token_id = kwargs.pop("pad_token_id", None) |
| self.bos_token_id = kwargs.pop("bos_token_id", None) |
| self.eos_token_id = kwargs.pop("eos_token_id", None) |
|
|
| |
| self.generation_kwargs = kwargs.pop("generation_kwargs", {}) |
|
|
| |
| |
| self._from_model_config = kwargs.pop("_from_model_config", False) |
| self._commit_hash = kwargs.pop("_commit_hash", None) |
| self.transformers_version = kwargs.pop("transformers_version", __version__) |
|
|
| |
| if not self._from_model_config: |
| |
| |
| for key, value in kwargs.items(): |
| try: |
| setattr(self, key, value) |
| except AttributeError as err: |
| logger.error(f"Can't set {key} with value {value} for {self}") |
| raise err |
|
|
| |
| self.validate(is_init=True) |
|
|
| def validate(self, is_init=False): |
| pass |
|
|
| class DreamGenerationMixin: |
| @staticmethod |
| def _expand_inputs_for_generation( |
| expand_size: int = 1, |
| input_ids: Optional[torch.LongTensor] = None, |
| attention_mask: Optional[torch.LongTensor] = None |
| ) -> Tuple[torch.LongTensor, Dict[str, Any]]: |
| """Expands tensors from [batch_size, ...] to [batch_size * expand_size, ...]""" |
| |
| |
| if expand_size == 1: |
| return input_ids, attention_mask |
| if input_ids is not None: |
| input_ids = input_ids.repeat_interleave(expand_size, dim=0) |
| if attention_mask is not None: |
| attention_mask = attention_mask.repeat_interleave(expand_size, dim=0) |
| return input_ids, attention_mask |
|
|
| def _validate_generated_length(self, generation_config, input_ids_length, has_default_max_length): |
| """Performs validation related to the resulting generated length""" |
|
|
| |
| if is_torchdynamo_compiling(): |
| return |
|
|
| |
| if has_default_max_length and generation_config.max_new_tokens is None and generation_config.max_length == 20: |
| |
| warnings.warn( |
| f"Using the model-agnostic default `max_length` (={generation_config.max_length}) to control the " |
| "generation length. We recommend setting `max_new_tokens` to control the maximum length of the " |
| "generation.", |
| UserWarning, |
| ) |
| if input_ids_length >= generation_config.max_length: |
| input_ids_string = "input_ids" |
| raise ValueError( |
| f"Input length of {input_ids_string} is {input_ids_length}, but `max_length` is set to" |
| f" {generation_config.max_length}. This can lead to unexpected behavior. You should consider" |
| " increasing `max_length` or, better yet, setting `max_new_tokens`." |
| ) |
|
|
| def _prepare_generated_length( |
| self, |
| generation_config, |
| has_default_max_length, |
| input_ids_length, |
| ): |
| """Prepared max and min length in generation configs to avoid clashes between similar attributes""" |
|
|
| if generation_config.max_new_tokens is not None: |
| if not has_default_max_length and generation_config.max_length is not None: |
| logger.warning( |
| f"Both `max_new_tokens` (={generation_config.max_new_tokens}) and `max_length`(=" |
| f"{generation_config.max_length}) seem to have been set. `max_new_tokens` will take precedence. " |
| "Please refer to the documentation for more information. " |
| "(https://huggingface.co/docs/transformers/main/en/main_classes/text_generation)" |
| ) |
| generation_config.max_length = generation_config.max_new_tokens + input_ids_length |
|
|
| elif has_default_max_length: |
| if generation_config.max_length == DreamGenerationConfig().max_length: |
| generation_config.max_length = generation_config.max_length + input_ids_length |
| max_position_embeddings = getattr(self.config, "max_position_embeddings", None) |
| if max_position_embeddings is not None: |
| generation_config.max_length = min(generation_config.max_length, max_position_embeddings) |
|
|
| return generation_config |
|
|
| def _prepare_generation_config( |
| self, generation_config: Optional[DreamGenerationConfig], **kwargs: Dict |
| ) -> DreamGenerationConfig: |
| """ |
| Prepares the base generation config, then applies any generation configuration options from kwargs. This |
| function handles retrocompatibility with respect to configuration files. |
| """ |
| |
| using_model_generation_config = False |
| if generation_config is None: |
| generation_config = DreamGenerationConfig.from_model_config(self.config) |
| using_model_generation_config = True |
|
|
| |
| |
| |
| if not is_torchdynamo_compiling(): |
| generation_config = copy.deepcopy(generation_config) |
| _kwargs = generation_config.update(**kwargs) |
| |
| if not using_model_generation_config: |
| if generation_config.bos_token_id is None: |
| generation_config.bos_token_id = self.generation_config.bos_token_id |
| if generation_config.eos_token_id is None: |
| generation_config.eos_token_id = self.generation_config.eos_token_id |
| if generation_config.pad_token_id is None: |
| generation_config.pad_token_id = self.generation_config.pad_token_id |
| if generation_config.mask_token_id is None: |
| generation_config.mask_token_id = self.generation_config.mask_token_id |
|
|
| return generation_config |
|
|
| def _prepare_special_tokens( |
| self, |
| generation_config: DreamGenerationConfig, |
| device: Optional[Union[torch.device, str]] = None, |
| ): |
| """ |
| Prepares the special tokens for generation, overwriting the generation config with their processed versions |
| converted to tensor. |
| Note that `generation_config` is changed in place and stops being serializable after this method is called. |
| That is no problem if called within `generate` (`generation_config` is a local copy that doesn't leave the |
| function). However, if called outside `generate`, consider creating a copy of `generation_config` first. |
| """ |
|
|
| |
| def _tensor_or_none(token, device=None): |
| if token is None: |
| return token |
|
|
| device = device if device is not None else self.device |
| if isinstance(token, torch.Tensor): |
| return token.to(device) |
| return torch.tensor(token, device=device, dtype=torch.long) |
|
|
| bos_token_tensor = _tensor_or_none(generation_config.bos_token_id, device=device) |
| eos_token_tensor = _tensor_or_none(generation_config.eos_token_id, device=device) |
| pad_token_tensor = _tensor_or_none(generation_config.pad_token_id, device=device) |
| mask_token_tensor = _tensor_or_none(generation_config.mask_token_id, device=device) |
|
|
| |
| if eos_token_tensor is not None and eos_token_tensor.ndim == 0: |
| eos_token_tensor = eos_token_tensor.unsqueeze(0) |
|
|
| |
| if pad_token_tensor is None and eos_token_tensor is not None: |
| pad_token_tensor = eos_token_tensor[0] |
| logger.warning(f"Setting `pad_token_id` to `eos_token_id`:{pad_token_tensor} for open-end generation.") |
|
|
| |
| |
| |
| |
| generation_config._bos_token_tensor = bos_token_tensor |
| generation_config._eos_token_tensor = eos_token_tensor |
| generation_config._pad_token_tensor = pad_token_tensor |
| generation_config._mask_token_tensor = mask_token_tensor |
|
|
| @torch.no_grad() |
| def diffusion_generate( |
| self, |
| inputs: Optional[torch.Tensor] = None, |
| generation_config: Optional[DreamGenerationConfig] = None, |
| **kwargs, |
| ) -> Union[DreamModelOutput, torch.LongTensor]: |
| |
| generation_config = self._prepare_generation_config(generation_config, **kwargs) |
| generation_tokens_hook_func = kwargs.pop("generation_tokens_hook_func", lambda step, x, logits: x) |
| generation_logits_hook_func = kwargs.pop("generation_logits_hook_func", lambda step, x, logits: logits) |
|
|
| |
| assert inputs is not None |
| input_ids = inputs |
| device = input_ids.device |
| attention_mask = kwargs.pop("attention_mask", None) |
| self._prepare_special_tokens(generation_config, device=device) |
|
|
| |
| input_ids_length = input_ids.shape[-1] |
| has_default_max_length = kwargs.get("max_length") is None and generation_config.max_length is not None |
| generation_config = self._prepare_generated_length( |
| generation_config=generation_config, |
| has_default_max_length=has_default_max_length, |
| input_ids_length=input_ids_length, |
| ) |
|
|
| self._validate_generated_length(generation_config, input_ids_length, has_default_max_length) |
| |
| |
| if not is_torchdynamo_compiling() and self.device.type != input_ids.device.type: |
| warnings.warn( |
| "You are calling .generate() with the `input_ids` being on a device type different" |
| f" than your model's device. `input_ids` is on {input_ids.device.type}, whereas the model" |
| f" is on {self.device.type}. You may experience unexpected behaviors or slower generation." |
| " Please make sure that you have put `input_ids` to the" |
| f" correct device by calling for example input_ids = input_ids.to('{self.device.type}') before" |
| " running `.generate()`.", |
| UserWarning, |
| ) |
| if ( |
| hasattr(generation_config, "pad_token_id") and |
| torch.any(input_ids == generation_config.pad_token_id) and |
| attention_mask is None |
| ): |
| warnings.warn( |
| "Padding was detected but no attention mask is passed here. For correct " |
| "generation results, please set `attention_mask` when batch-padding inputs.", |
| UserWarning, |
| ) |
|
|
| input_ids, attention_mask = self._expand_inputs_for_generation( |
| expand_size=generation_config.num_return_sequences, |
| input_ids=input_ids, |
| attention_mask=attention_mask |
| ) |
| threshold = kwargs.get("threshold", 0.9) |
|
|
| |
| block_length = kwargs.get("block_length", None) |
| early_stop = kwargs.get("early_stop", False) |
|
|
| result = self._sample( |
| input_ids, |
| attention_mask=attention_mask, |
| generation_config=generation_config, |
| generation_tokens_hook_func=generation_tokens_hook_func, |
| generation_logits_hook_func=generation_logits_hook_func, |
| threshold=threshold, |
| block_length=block_length, |
| early_stop=early_stop, |
| ) |
| return result |
|
|
| def _sample( |
| self, |
| input_ids: torch.LongTensor, |
| attention_mask: Optional[torch.LongTensor], |
| generation_config: DreamGenerationConfig, |
| generation_tokens_hook_func, |
| generation_logits_hook_func, |
| threshold: Optional[float] = 0.9, |
| block_length: Optional[int] = None, |
| early_stop: bool = False, |
| ) -> Union[DreamModelOutput, torch.LongTensor]: |
| |
| output_history = generation_config.output_history |
| return_dict_in_generate = generation_config.return_dict_in_generate |
| max_length = generation_config.max_length |
| mask_token_id = generation_config.mask_token_id |
| steps = generation_config.steps |
| eps = generation_config.eps |
| alg = generation_config.alg |
| alg_temp = generation_config.alg_temp |
| temperature = generation_config.temperature |
| top_p = generation_config.top_p |
| top_k = generation_config.top_k |
| eos_token_id = generation_config.eos_token_id if early_stop else None |
|
|
| histories = [] if (return_dict_in_generate and output_history) else None |
| start_time = time.time() |
| |
| x = F.pad(input_ids, (0, max_length - input_ids.shape[1]), value=mask_token_id) |
| gen_length = max_length - input_ids.shape[1] |
|
|
| |
| if attention_mask is not None and torch.any(attention_mask == 0.0): |
| attention_mask = F.pad(attention_mask, (0, max_length - attention_mask.shape[1]), value=1.0) |
| tok_idx = attention_mask.long().cumsum(-1) - 1 |
| tok_idx.masked_fill_(attention_mask == 0, 1) |
| attention_mask = torch.logical_and( |
| attention_mask.unsqueeze(1).unsqueeze(-2), |
| attention_mask.unsqueeze(1).unsqueeze(-1), |
| ) |
| else: |
| tok_idx = None |
| attention_mask = "full" |
|
|
| |
| if block_length is None: |
| block_length = gen_length if gen_length > 0 else 1 |
| assert gen_length % block_length == 0, f"gen_length ({gen_length}) must be divisible by block_length ({block_length})" |
| num_blocks = max(gen_length // block_length, 1) |
|
|
| assert steps % num_blocks == 0, f"steps ({steps}) must be divisible by num_blocks ({num_blocks})" |
| steps_per_block = steps // num_blocks |
|
|
| |
| timesteps_block = torch.linspace(1, eps, steps_per_block + 1, device=x.device) |
|
|
| |
| x = generation_tokens_hook_func(None, x, None) |
|
|
| |
| total_nfe = 0 |
| prompt_length = input_ids.shape[1] |
| has_eos = False |
| for num_block in range(num_blocks): |
| |
| if eos_token_id is not None and has_eos: |
| break |
| current_block_start = input_ids.shape[1] + num_block * block_length |
| current_block_end = current_block_start + block_length |
|
|
| i = 0 |
| while True: |
| |
| if (x[:, current_block_start:current_block_end] == mask_token_id).sum() == 0: |
| break |
|
|
| |
| logits = self(x, attention_mask, tok_idx).logits |
| logits = torch.cat([logits[:,:1], logits[:, :-1]], dim=1) |
| total_nfe += 1 |
|
|
| |
| logits = generation_logits_hook_func(i, x, logits) |
|
|
| |
| mask_index_full = (x == mask_token_id) |
| |
| mask_index_full[:, :current_block_start] = False |
| mask_index_full[:, current_block_end:] = False |
|
|
| mask_logits = logits[mask_index_full] |
|
|
| if alg == 'confidence_threshold': |
| confidence, x0 = sample_tokens(mask_logits, temperature=temperature, top_p=top_p, top_k=top_k) |
| x_block = torch.zeros_like(x, device=self.device, dtype=torch.long) + mask_token_id |
| x_block[mask_index_full] = x0.clone() |
| full_confidence = torch.full_like(x, -torch.inf, device=self.device, dtype=logits.dtype) |
| full_confidence[mask_index_full] = confidence |
| |
| full_confidence[:, :current_block_start] = -torch.inf |
| full_confidence[:, current_block_end:] = -torch.inf |
|
|
| current_transfer_tokens = (x[:, current_block_start:current_block_end] == mask_token_id).sum() |
| selected_confidence, select_index = torch.topk(full_confidence, current_transfer_tokens) |
| transfer_index = torch.zeros_like(x, device=x.device, dtype=torch.bool) |
| select_index = select_index.to(x.device) |
| transfer_index[0, select_index[0]] = True |
| for k in range(1, current_transfer_tokens): |
| if selected_confidence[0, k] < threshold: |
| transfer_index[0, select_index[0, k]] = False |
| x[transfer_index] = x_block[transfer_index].clone() |
| elif alg == 'entropy_threshold': |
| |
| |
| neg_entropy, x0 = sample_tokens(mask_logits, temperature=temperature, top_p=top_p, top_k=top_k, neg_entropy=True) |
| |
| entropy = -neg_entropy |
| |
| x_block = torch.zeros_like(x, device=self.device, dtype=torch.long) + mask_token_id |
| x_block[mask_index_full] = x0.clone() |
| full_entropy = torch.full_like(x, torch.inf, device=self.device, dtype=logits.dtype) |
| full_entropy[mask_index_full] = entropy |
| |
| full_entropy[:, :current_block_start] = torch.inf |
| full_entropy[:, current_block_end:] = torch.inf |
|
|
| current_transfer_tokens = (x[:, current_block_start:current_block_end] == mask_token_id).sum() |
| |
| selected_entropy, select_index = torch.topk(full_entropy, current_transfer_tokens, largest=False) |
| transfer_index = torch.zeros_like(x, device=x.device, dtype=torch.bool) |
| select_index = select_index.to(x.device) |
| transfer_index[0, select_index[0]] = True |
| for k in range(1, current_transfer_tokens): |
| if selected_entropy[0, k] > threshold: |
| |
| transfer_index[0, select_index[0, k]] = False |
| x[transfer_index] = x_block[transfer_index].clone() |
| else: |
| |
| if i == steps_per_block: |
| break |
| t = timesteps_block[i] |
| s = timesteps_block[i + 1] |
|
|
| |
| if alg == 'maskgit_plus': |
| confidence, x0 = sample_tokens(mask_logits, temperature=temperature, top_p=top_p, top_k=top_k) |
| elif alg == 'topk_margin': |
| confidence, x0 = sample_tokens(mask_logits, temperature=temperature, top_p=top_p, top_k=top_k, margin_confidence=True) |
| elif alg == 'entropy': |
| confidence, x0 = sample_tokens(mask_logits, temperature, top_p=top_p, top_k=top_k, neg_entropy=True) |
| elif alg == 'origin': |
| |
| confidence, x0 = sample_tokens(mask_logits, temperature=temperature, top_p=top_p, top_k=top_k) |
| else: |
| raise RuntimeError(f"Unknown alg: {alg}") |
|
|
| |
| full_confidence = torch.full_like(x, -torch.inf, device=self.device, dtype=logits.dtype) |
| full_confidence[mask_index_full] = confidence |
| full_confidence[:, :current_block_start] = -torch.inf |
| full_confidence[:, current_block_end:] = -torch.inf |
|
|
| |
| num_mask_token = mask_index_full.sum() / mask_index_full.shape[0] |
| number_transfer_tokens = int(num_mask_token * (1 - s / t)) if i < steps_per_block - 1 else int(num_mask_token) |
|
|
| if number_transfer_tokens > 0: |
| if alg_temp is None or alg_temp == 0: |
| _, transfer_index = torch.topk(full_confidence, number_transfer_tokens) |
| else: |
| full_confidence = full_confidence / alg_temp |
| full_confidence = F.softmax(full_confidence, dim=-1) |
| transfer_index = torch.multinomial(full_confidence, num_samples=number_transfer_tokens) |
| x_block = torch.zeros_like(x, device=self.device, dtype=torch.long) + mask_token_id |
| x_block[mask_index_full] = x0.clone() |
| row_indices = torch.arange(x.size(0), device=self.device).unsqueeze(1).expand_as(transfer_index) |
| x[row_indices, transfer_index] = x_block[row_indices, transfer_index] |
| i += 1 |
|
|
| |
| x = generation_tokens_hook_func(i, x, logits) |
| if histories is not None: |
| histories.append(x.clone()) |
|
|
| |
| if eos_token_id is not None: |
| gen_region = x[:, prompt_length:] |
| eos_found = (gen_region == eos_token_id) & (gen_region != mask_token_id) |
| if eos_found.any(): |
| pos = torch.arange(gen_region.shape[1], device=x.device).unsqueeze(0) |
| first_eos_rel = torch.where(eos_found, pos, gen_region.shape[1]).amin(dim=1) |
| first_eos_abs = prompt_length + first_eos_rel[0].item() |
| x[:, first_eos_abs + 1:] = eos_token_id |
| has_eos = True |
| break |
| |
| end_time = time.time() |
| if return_dict_in_generate: |
| return DreamModelOutput( |
| sequences=x, |
| history=histories, |
| nfe=total_nfe, |
| ) |
| else: |
| return x |
|
|
| @torch.no_grad() |
| def generate_multi_block( |
| self, |
| inputs: Optional[torch.Tensor] = None, |
| generation_config: Optional[DreamGenerationConfig] = None, |
| **kwargs, |
| ) -> Union[DreamModelOutput, torch.LongTensor]: |
| """ |
| Entry point for multi-block pipelined parallel decoding. |
| Mirrors diffusion_generate() but dispatches to _sample_multi_block(). |
| """ |
| |
| generation_config = self._prepare_generation_config(generation_config, **kwargs) |
|
|
| |
| assert inputs is not None |
| input_ids = inputs |
| device = input_ids.device |
| attention_mask = kwargs.pop("attention_mask", None) |
| self._prepare_special_tokens(generation_config, device=device) |
|
|
| |
| input_ids_length = input_ids.shape[-1] |
| has_default_max_length = kwargs.get("max_length") is None and generation_config.max_length is not None |
| generation_config = self._prepare_generated_length( |
| generation_config=generation_config, |
| has_default_max_length=has_default_max_length, |
| input_ids_length=input_ids_length, |
| ) |
| self._validate_generated_length(generation_config, input_ids_length, has_default_max_length) |
|
|
| |
| if not is_torchdynamo_compiling() and self.device.type != input_ids.device.type: |
| warnings.warn( |
| "You are calling .generate() with the `input_ids` being on a device type different" |
| f" than your model's device. `input_ids` is on {input_ids.device.type}, whereas the model" |
| f" is on {self.device.type}. You may experience unexpected behaviors or slower generation.", |
| UserWarning, |
| ) |
| if ( |
| hasattr(generation_config, "pad_token_id") and |
| torch.any(input_ids == generation_config.pad_token_id) and |
| attention_mask is None |
| ): |
| warnings.warn( |
| "Padding was detected but no attention mask is passed here. For correct " |
| "generation results, please set `attention_mask` when batch-padding inputs.", |
| UserWarning, |
| ) |
|
|
| input_ids, attention_mask = self._expand_inputs_for_generation( |
| expand_size=generation_config.num_return_sequences, |
| input_ids=input_ids, |
| attention_mask=attention_mask, |
| ) |
|
|
| |
| threshold = kwargs.get("threshold", 0.9) |
| block_size = kwargs.get("block_size", 32) |
| block_add_threshold = kwargs.get("block_add_threshold", 0.5) |
| decoded_token_threshold = kwargs.get("decoded_token_threshold", 0.5) |
| early_stop = kwargs.get("early_stop", False) |
|
|
| result = self._sample_multi_block( |
| input_ids, |
| attention_mask=attention_mask, |
| generation_config=generation_config, |
| threshold=threshold, |
| block_size=block_size, |
| block_add_threshold=block_add_threshold, |
| decoded_token_threshold=decoded_token_threshold, |
| early_stop=early_stop, |
| ) |
| return result |
|
|
| def _sample_multi_block( |
| self, |
| input_ids: torch.LongTensor, |
| attention_mask: Optional[torch.LongTensor], |
| generation_config: DreamGenerationConfig, |
| threshold: float = 0.9, |
| block_size: int = 32, |
| block_add_threshold: float = 0.5, |
| decoded_token_threshold: float = 0.5, |
| early_stop: bool = False, |
| ) -> Union[DreamModelOutput, torch.LongTensor]: |
| """ |
| Pipelined parallel decoding without cache. |
| |
| Args: |
| block_add_threshold: Add new block when last block progress >= this threshold. |
| Set to 1.0 for fully sequential processing. |
| decoded_token_threshold: Block becomes fully activated when previous block progress >= this threshold. |
| Set to 1.0 for fully sequential processing. |
| threshold: Entropy threshold for decoding (lower entropy = higher confidence). |
| |
| When block_add_threshold=1.0 and decoded_token_threshold=1.0, this method behaves |
| identically to standard generation with sequential block processing. |
| """ |
| return_dict_in_generate = generation_config.return_dict_in_generate |
| max_length = generation_config.max_length |
| mask_token_id = generation_config.mask_token_id |
| temperature = generation_config.temperature |
| alg = generation_config.alg |
| eos_token_id = generation_config.eos_token_id if early_stop else None |
|
|
| max_new_tokens = max_length - input_ids.shape[1] |
| prompt_length = input_ids.shape[1] |
| x = F.pad(input_ids, (0, max_new_tokens), value=mask_token_id) |
|
|
| |
| if attention_mask is not None and torch.any(attention_mask == 0.0): |
| attention_mask_padded = F.pad(attention_mask, (0, max_new_tokens), value=1.0) |
| tok_idx = attention_mask_padded.long().cumsum(-1) - 1 |
| tok_idx.masked_fill_(attention_mask_padded == 0, 1) |
| attn_mask_4d = torch.logical_and( |
| attention_mask_padded.unsqueeze(1).unsqueeze(-2), |
| attention_mask_padded.unsqueeze(1).unsqueeze(-1), |
| ) |
| else: |
| tok_idx = None |
| attn_mask_4d = "full" |
|
|
| |
| |
| block_states = { |
| 0: { |
| "start": 0, |
| "end": input_ids.shape[1], |
| "mask_count": 0, |
| "total_masks": input_ids.shape[1], |
| "is_complete": True, |
| } |
| } |
|
|
| |
| num_blocks = max_new_tokens // block_size |
| next_block_id = 1 |
| if next_block_id <= num_blocks: |
| block_start = input_ids.shape[1] + (next_block_id - 1) * block_size |
| block_end = min(block_start + block_size, input_ids.shape[1] + max_new_tokens) |
| should_activate = 1.0 >= decoded_token_threshold |
| block_states[next_block_id] = { |
| "start": block_start, |
| "end": block_end, |
| "mask_count": block_end - block_start, |
| "total_masks": block_end - block_start, |
| "is_complete": should_activate, |
| } |
| next_block_id += 1 |
|
|
| nfe = 0 |
|
|
| while True: |
| |
| mask_index = x == mask_token_id |
| total_masks = mask_index[:, prompt_length:].sum() |
|
|
| if total_masks == 0 and next_block_id > num_blocks: |
| break |
|
|
| nfe += 1 |
|
|
| |
| if eos_token_id is not None: |
| gen_region = x[:, prompt_length:] |
| eos_found = (gen_region == eos_token_id) & (gen_region != mask_token_id) |
| if eos_found.any(): |
| pos = torch.arange(gen_region.shape[1], device=x.device).unsqueeze(0) |
| first_eos_rel = torch.where(eos_found, pos, gen_region.shape[1]).amin(dim=1) |
| first_eos_abs = prompt_length + first_eos_rel[0].item() |
| x[:, first_eos_abs + 1:] = eos_token_id |
| |
| while next_block_id <= num_blocks: |
| bs = prompt_length + (next_block_id - 1) * block_size |
| be = min(bs + block_size, prompt_length + max_new_tokens) |
| if bs > first_eos_abs: |
| block_states[next_block_id] = { |
| "start": bs, "end": be, "mask_count": 0, |
| "total_masks": be - bs, "is_complete": True, |
| } |
| next_block_id += 1 |
| else: |
| break |
| if (x == mask_token_id)[:, prompt_length:].sum() == 0: |
| break |
|
|
| |
| def update_block_activation_states(): |
| """Update which blocks should be fully activated based on previous block progress.""" |
| for bid in sorted(block_states.keys()): |
| if bid > 0 and not block_states[bid]["is_complete"]: |
| prev_progress = ( |
| 1 - block_states[bid - 1]["mask_count"] / block_states[bid - 1]["total_masks"] |
| ) |
| if prev_progress >= decoded_token_threshold: |
| block_states[bid]["is_complete"] = True |
|
|
| update_block_activation_states() |
|
|
| |
| if next_block_id <= num_blocks: |
| last_bid = max(block_states.keys()) |
| if last_bid > 0: |
| last_progress = ( |
| 1 - block_states[last_bid]["mask_count"] / block_states[last_bid]["total_masks"] |
| ) |
| should_add_block = (last_progress >= block_add_threshold) or (block_states[last_bid]["mask_count"] == 0) |
|
|
| if should_add_block: |
| block_start = input_ids.shape[1] + (next_block_id - 1) * block_size |
| block_end = min(block_start + block_size, input_ids.shape[1] + max_new_tokens) |
| if block_end > block_start: |
| actual_mask_count = (x[:, block_start:block_end] == mask_token_id).sum().item() |
| prev_bid = next_block_id - 1 |
| prev_progress = ( |
| 1 - block_states[prev_bid]["mask_count"] / block_states[prev_bid]["total_masks"] |
| ) |
| should_activate = prev_progress >= decoded_token_threshold |
|
|
| block_states[next_block_id] = { |
| "start": block_start, |
| "end": block_end, |
| "mask_count": actual_mask_count, |
| "total_masks": block_end - block_start, |
| "is_complete": should_activate, |
| } |
| next_block_id += 1 |
|
|
| |
| rightmost_active_bid = 0 |
| for bid in sorted(block_states.keys()): |
| if block_states[bid]["is_complete"] or block_states[bid]["mask_count"] > 0: |
| rightmost_active_bid = bid |
|
|
| if rightmost_active_bid == 0: |
| break |
|
|
| active_end = block_states[rightmost_active_bid]["end"] |
|
|
| |
| model_output = self(x, attn_mask_4d, tok_idx) |
| logits = model_output.logits |
| logits = torch.cat([logits[:, :1], logits[:, :-1]], dim=1) |
|
|
| |
| mask_index_for_decode = mask_index.clone() |
| mask_index_for_decode[:, active_end:] = 0 |
|
|
| |
| if alg == 'entropy_threshold': |
| mask_logits = logits[mask_index_for_decode] |
|
|
| entropy, x0 = sample_tokens_with_entropy(mask_logits, temperature=temperature) |
|
|
| x_ = torch.zeros_like(x, device=self.device, dtype=torch.long) + mask_token_id |
| full_entropy = torch.full_like(x, torch.inf, device=self.device, dtype=logits.dtype) |
|
|
| x_[mask_index_for_decode] = x0.clone() |
| full_entropy[mask_index_for_decode] = entropy |
|
|
| current_transfer_tokens = mask_index_for_decode.sum() |
|
|
| selected_entropy, select_index = torch.topk(full_entropy, current_transfer_tokens, largest=False) |
| transfer_index = torch.zeros_like(x, device=x.device, dtype=torch.bool) |
|
|
| select_index = select_index.to(x.device) |
| transfer_index[0, select_index[0]] = True |
| for k in range(1, current_transfer_tokens): |
| if selected_entropy[0, k] < threshold: |
| transfer_index[0, select_index[0, k]] = True |
| else: |
| transfer_index[0, select_index[0, k]] = False |
|
|
| |
| first_fully_activated_bid = None |
| for bid in sorted(block_states.keys()): |
| if bid > 0 and block_states[bid]["is_complete"] and block_states[bid]["mask_count"] > 0: |
| first_fully_activated_bid = bid |
| break |
|
|
| if first_fully_activated_bid is not None: |
| start, end = block_states[first_fully_activated_bid]["start"], block_states[first_fully_activated_bid]["end"] |
| block_transfer = transfer_index[:, start:end] |
|
|
| if not block_transfer.any(): |
| |
| block_mask = mask_index_for_decode[:, start:end] |
| block_entropy = full_entropy[:, start:end] |
| block_entropy = torch.where(block_mask, block_entropy, torch.inf) |
| best_idx = block_entropy[0].argmin() |
| transfer_index[0, start + best_idx] = True |
|
|
| |
| x[transfer_index] = x_[transfer_index].clone() |
|
|
| |
| for bid in sorted(block_states.keys()): |
| if bid > 0 and block_states[bid]["mask_count"] > 0: |
| start, end = block_states[bid]["start"], block_states[bid]["end"] |
| block_decoded = transfer_index[:, start:end].sum().item() |
| if block_decoded > 0: |
| block_states[bid]["mask_count"] -= block_decoded |
|
|
| if nfe > 10000: |
| break |
|
|
| if return_dict_in_generate: |
| return DreamModelOutput(sequences=x, nfe=nfe), nfe |
| return x, nfe |