| import math |
| import gc |
| from collections import deque |
| from typing import Callable, List, Optional |
|
|
| import torch |
| from torch import nn |
| from tqdm import tqdm |
|
|
| from .attention import ( |
| ForwardContext, |
| get_forward_context, |
| reset_forward_context, |
| set_forward_context, |
| ) |
| from .kv_manager import KVCacheManager, Seq |
|
|
|
|
| class Sampler(nn.Module): |
| def __init__(self): |
| super().__init__() |
|
|
| |
| def forward(self, logits: torch.Tensor, temperatures: torch.Tensor): |
| temperatures = temperatures.to(logits.device).clamp(min=1e-8) |
| greedy_mask = temperatures < 1e-5 |
| temp_for_scaling = torch.where(greedy_mask, 1.0, temperatures) |
| scaled_logits = logits / temp_for_scaling.unsqueeze(-1) |
| probs = torch.softmax(scaled_logits, dim=-1, dtype=torch.float32) |
| q = torch.empty_like(probs) |
| q.exponential_() |
| sampled_tokens = probs.div_(q).argmax(dim=-1) |
| greedy_tokens = logits.argmax(dim=-1) |
| return torch.where(greedy_mask, greedy_tokens, sampled_tokens) |
|
|
|
|
| class AccelInferenceEngine: |
| def __init__( |
| self, |
| model, |
| lm_head, |
| num_layers: int, |
| num_heads: int, |
| head_dim: int, |
| block_size: int = 256, |
| num_blocks: int = 128, |
| use_cuda_graph: bool = True, |
| ): |
| """ |
| Args: |
| model: The GPT transformer model (should have accel attention) |
| lm_head: Language model head for generating logits |
| num_layers: Number of transformer layers |
| num_heads: Number of attention heads |
| head_dim: Dimension per head |
| block_size: KV cache block size |
| num_blocks: Total number of KV cache blocks |
| use_cuda_graph: Whether to use CUDA Graph for decode optimization |
| """ |
| self.model = model |
| self.lm_head = lm_head |
| self.block_size = block_size |
| self.num_layers = num_layers |
| self.num_heads = num_heads |
| self.head_dim = head_dim |
| self._default_num_blocks = max(1, int(num_blocks)) |
| self.num_blocks = 1 |
| self.use_cuda_graph = use_cuda_graph and torch.cuda.is_available() |
| self.hidden_size = ( |
| model.config.hidden_size |
| if hasattr(model, "config") |
| else head_dim * num_heads |
| ) |
| self.kv_manager = self._new_kv_manager(self.num_blocks, device=torch.device("cpu")) |
| self.kv_manager.wire_kv_cache_to_model(model) |
| self.sampler = Sampler() |
| self.current_sequences = [] |
| self.graphs = {} |
| self.graph_vars = None |
| self.graph_pool = None |
| self.graph_captured = False |
| self.graph_num_blocks = 0 |
| self.graph_signature = None |
|
|
| def _runtime_kv_device(self) -> torch.device: |
| first_param = next(self.model.parameters(), None) |
| if first_param is not None and first_param.device.type == "cuda": |
| return first_param.device |
| if torch.cuda.is_available(): |
| return torch.device(f"cuda:{torch.cuda.current_device()}") |
| if first_param is not None: |
| return first_param.device |
| return torch.device("cpu") |
|
|
| def _new_kv_manager(self, num_blocks: int, device: torch.device) -> KVCacheManager: |
| return KVCacheManager( |
| num_layers=self.num_layers, |
| num_heads=self.num_heads, |
| head_dim=self.head_dim, |
| block_size=self.block_size, |
| num_blocks=int(max(1, num_blocks)), |
| dtype=torch.float16, |
| device=device, |
| ) |
|
|
| @staticmethod |
| def _filter_logits(logits: torch.Tensor, top_k: int, top_p: float) -> torch.Tensor: |
| filtered = logits |
| vocab_size = int(filtered.size(-1)) |
| top_k = int(top_k) if top_k is not None else 0 |
| top_p = float(top_p) if top_p is not None else 1.0 |
| if top_k > 0 and top_k < vocab_size: |
| kth = torch.topk(filtered, k=top_k, dim=-1).values[..., -1, None] |
| filtered = filtered.masked_fill(filtered < kth, float("-inf")) |
| if top_p < 1.0: |
| sorted_logits, sorted_indices = torch.sort(filtered, descending=True, dim=-1) |
| sorted_probs = torch.softmax(sorted_logits.float(), dim=-1) |
| cumulative_probs = torch.cumsum(sorted_probs, dim=-1) |
| sorted_remove = cumulative_probs > top_p |
| sorted_remove[..., 0] = False |
| remove_mask = torch.zeros_like(sorted_remove, dtype=torch.bool) |
| remove_mask.scatter_(dim=-1, index=sorted_indices, src=sorted_remove) |
| filtered = filtered.masked_fill(remove_mask, float("-inf")) |
| return filtered |
|
|
| @staticmethod |
| def _tensor_sig(tensor: Optional[torch.Tensor]): |
| if tensor is None: |
| return None |
| return ( |
| int(tensor.data_ptr()), |
| str(tensor.dtype), |
| tuple(int(x) for x in tensor.shape), |
| int(tensor.device.index if tensor.device.index is not None else -1), |
| ) |
|
|
| def _module_first_param_sig(self, module: Optional[torch.nn.Module]): |
| if module is None: |
| return None |
| try: |
| first_param = next(module.parameters()) |
| except StopIteration: |
| return None |
| return self._tensor_sig(first_param) |
|
|
| def _make_capture_signature( |
| self, |
| tts_mel_embedding: Optional[torch.nn.Module] = None, |
| tts_text_pos_embedding: Optional[torch.nn.Module] = None, |
| ): |
| mel_weight = getattr(tts_mel_embedding, "weight", None) if tts_mel_embedding is not None else None |
| pos_emb = None |
| if tts_text_pos_embedding is not None: |
| pos_emb = getattr(tts_text_pos_embedding, "emb", tts_text_pos_embedding) |
| pos_weight = getattr(pos_emb, "weight", None) if pos_emb is not None else None |
| return ( |
| self._module_first_param_sig(self.model), |
| self._tensor_sig(self.kv_manager.kv_cache), |
| self._tensor_sig(mel_weight), |
| self._tensor_sig(pos_weight), |
| int(self.num_blocks), |
| int(self.block_size), |
| ) |
|
|
| def _compute_tts_embeds( |
| self, |
| input_ids: torch.Tensor, |
| positions: torch.Tensor, |
| tts_mel_embedding: Optional[torch.nn.Module] = None, |
| tts_text_pos_embedding: Optional[torch.nn.Module] = None, |
| ) -> torch.Tensor: |
| if tts_mel_embedding is None or tts_text_pos_embedding is None: |
| raise RuntimeError("TTS embedding modules are required for accel decode.") |
| pos_emb_module = getattr(tts_text_pos_embedding, "emb", tts_text_pos_embedding) |
| if not hasattr(pos_emb_module, "weight"): |
| raise RuntimeError("TTS positional embedding module is missing a '.weight' tensor.") |
| pos_clamped = torch.clamp(positions, min=0, max=pos_emb_module.weight.shape[0] - 1) |
| mel_emb = tts_mel_embedding(input_ids) |
| pos_emb = pos_emb_module(pos_clamped) |
| return mel_emb + pos_emb |
|
|
| def _required_blocks(self, total_tokens: int) -> int: |
| |
| return max(1, int(math.ceil(float(max(1, total_tokens)) / float(self.block_size))) + 1) |
|
|
| def _reset_decode_graph(self): |
| self.graphs = {} |
| self.graph_vars = None |
| self.graph_pool = None |
| self.graph_captured = False |
| self.graph_num_blocks = 0 |
| self.graph_signature = None |
|
|
| def _resize_kv_cache_if_needed(self, required_blocks: int): |
| target_blocks = int(max(1, required_blocks)) |
| target_device = self._runtime_kv_device() |
| current_device = self.kv_manager.kv_cache.device |
| same_device = ( |
| current_device.type == target_device.type |
| and (current_device.type != "cuda" or current_device.index == target_device.index) |
| ) |
| if target_blocks == int(self.num_blocks) and same_device: |
| return |
| self.num_blocks = target_blocks |
| self.kv_manager = self._new_kv_manager(self.num_blocks, target_device) |
| self.kv_manager.wire_kv_cache_to_model(self.model) |
| self._reset_decode_graph() |
|
|
| def release_runtime_cache(self): |
| self.current_sequences = [] |
| reset_forward_context() |
| self._reset_decode_graph() |
| old_kv_manager = self.kv_manager |
| self.num_blocks = 1 |
| self.kv_manager = self._new_kv_manager(self.num_blocks, torch.device("cpu")) |
| self.kv_manager.wire_kv_cache_to_model(self.model) |
| del old_kv_manager |
| if torch.cuda.is_available(): |
| torch.cuda.synchronize() |
| torch.cuda.empty_cache() |
| if hasattr(torch.cuda, "ipc_collect"): |
| torch.cuda.ipc_collect() |
| gc.collect() |
|
|
| def prepare_decode_graph( |
| self, |
| max_total_tokens: int, |
| tts_mel_embedding: Optional[torch.nn.Module] = None, |
| tts_text_pos_embedding: Optional[torch.nn.Module] = None, |
| ): |
| required_blocks = self._required_blocks(int(max_total_tokens)) |
| self._resize_kv_cache_if_needed(required_blocks) |
| if not self.use_cuda_graph: |
| return |
| signature = self._make_capture_signature( |
| tts_mel_embedding=tts_mel_embedding, |
| tts_text_pos_embedding=tts_text_pos_embedding, |
| ) |
| if ( |
| self.graph_captured |
| and int(self.graph_num_blocks) == int(self.num_blocks) |
| and self.graph_num_blocks >= required_blocks |
| and self.graph_signature == signature |
| ): |
| return |
| print( |
| f"[CAPTURE] use_cuda_graph={self.use_cuda_graph}, graph_captured={self.graph_captured}, " |
| f"graph_num_blocks={self.graph_num_blocks}, required_blocks={required_blocks}, cache_blocks={self.num_blocks}", |
| flush=True, |
| ) |
| self._reset_decode_graph() |
| self._capture_cuda_graphs( |
| tts_mel_embedding=tts_mel_embedding, |
| tts_text_pos_embedding=tts_text_pos_embedding, |
| max_num_blocks=self.num_blocks, |
| ) |
| self.graph_captured = True |
| self.graph_num_blocks = int(self.num_blocks) |
| self.graph_signature = signature |
| print(f"[CAPTURE] Completed! graphs={list(self.graphs.keys())}, num_blocks={self.graph_num_blocks}", flush=True) |
|
|
| def _prepare_prefill(self, requests: List[Seq]): |
| input_ids = [] |
| positions = [] |
| cu_seqlens_q = [0] |
| cu_seqlens_k = [0] |
| max_seqlen_q = 0 |
| max_seqlen_k = 0 |
| slot_mapping = [] |
|
|
| for req in requests: |
| seqlen = len(req) |
| input_ids.extend(req[req.num_cached_tokens :]) |
| positions.extend(list(range(req.num_cached_tokens, seqlen))) |
| seqlen_q = seqlen - req.num_cached_tokens |
| seqlen_k = seqlen |
| cu_seqlens_q.append(cu_seqlens_q[-1] + seqlen_q) |
| cu_seqlens_k.append(cu_seqlens_k[-1] + seqlen_k) |
| max_seqlen_q = max(seqlen_q, max_seqlen_q) |
| max_seqlen_k = max(seqlen_k, max_seqlen_k) |
|
|
| if req.block_table: |
| num_cached = req.num_cached_tokens |
| num_total = len(req) |
|
|
| for token_idx in range(num_cached, num_total): |
| block_idx = token_idx // self.block_size |
| block_offset = token_idx % self.block_size |
| block_id = req.block_table[block_idx] |
| slot_idx = block_id * self.block_size + block_offset |
| slot_mapping.append(slot_idx) |
|
|
| input_ids = torch.tensor(input_ids, dtype=torch.int64, pin_memory=True).cuda( |
| non_blocking=True |
| ) |
| positions = torch.tensor(positions, dtype=torch.int64, pin_memory=True).cuda( |
| non_blocking=True |
| ) |
| cu_seqlens_q = torch.tensor( |
| cu_seqlens_q, dtype=torch.int32, pin_memory=True |
| ).cuda(non_blocking=True) |
| cu_seqlens_k = torch.tensor( |
| cu_seqlens_k, dtype=torch.int32, pin_memory=True |
| ).cuda(non_blocking=True) |
| slot_mapping = torch.tensor( |
| slot_mapping, dtype=torch.int32, pin_memory=True |
| ).cuda(non_blocking=True) |
|
|
| block_tables = None |
| if cu_seqlens_k[-1] > cu_seqlens_q[-1]: |
| max_len = max(len(req.block_table) for req in requests) |
| block_tables_list = [] |
| for req in requests: |
| table = req.block_table + [-1] * (max_len - len(req.block_table)) |
| block_tables_list.append(table) |
| block_tables = torch.tensor( |
| block_tables_list, dtype=torch.int32, pin_memory=True |
| ).cuda(non_blocking=True) |
|
|
| set_forward_context( |
| True, |
| cu_seqlens_q, |
| cu_seqlens_k, |
| max_seqlen_q, |
| max_seqlen_k, |
| slot_mapping, |
| None, |
| block_tables, |
| ) |
|
|
| return input_ids, positions |
|
|
| def _reset_kv_allocator_state(self): |
| |
| |
| self.kv_manager.block_hash_to_id.clear() |
| self.kv_manager.free_block_ids = deque(range(self.num_blocks)) |
| self.kv_manager.used_block_ids.clear() |
| for block in self.kv_manager.blocks: |
| block.ref_cnt = 0 |
| block._block_hash = None |
| block.token_ids = [] |
|
|
| def _prepare_decode(self, requests: List[Seq]): |
| if not requests: |
| raise RuntimeError("FATAL: No requests provided to _prepare_decode!") |
|
|
| input_ids = [] |
| positions = [] |
| slot_mapping = [] |
| context_lens = [] |
|
|
| for req in requests: |
| input_ids.append(req.last_token) |
|
|
| pos = len(req) - 1 |
| if hasattr(self, "_tts_mode") and self._tts_mode: |
| pos = pos - (self._tts_prompt_len - 1) |
| positions.append(pos) |
|
|
| context_lens.append(len(req)) |
| slot_mapping.append( |
| req.block_table[-1] * self.block_size + req.last_block_num_tokens - 1 |
| ) |
|
|
| input_ids = torch.tensor(input_ids, dtype=torch.int64, pin_memory=True).cuda( |
| non_blocking=True |
| ) |
| positions = torch.tensor(positions, dtype=torch.int64, pin_memory=True).cuda( |
| non_blocking=True |
| ) |
| slot_mapping = torch.tensor( |
| slot_mapping, dtype=torch.int32, pin_memory=True |
| ).cuda(non_blocking=True) |
| context_lens = torch.tensor( |
| context_lens, dtype=torch.int32, pin_memory=True |
| ).cuda(non_blocking=True) |
|
|
| max_len = max(len(req.block_table) for req in requests) |
| block_tables_list = [] |
| for req in requests: |
| table = req.block_table + [-1] * (max_len - len(req.block_table)) |
| block_tables_list.append(table) |
| block_tables = torch.tensor( |
| block_tables_list, dtype=torch.int32, pin_memory=True |
| ).cuda(non_blocking=True) |
|
|
| assert block_tables.dim() == 2, ( |
| f"block_tables must be 2D, got shape {block_tables.shape}" |
| ) |
| assert block_tables.size(0) == len(requests), ( |
| f"block_tables batch size mismatch: {block_tables.size(0)} vs {len(requests)}" |
| ) |
|
|
| set_forward_context( |
| False, |
| slot_mapping=slot_mapping, |
| context_lens=context_lens, |
| block_tables=block_tables, |
| ) |
|
|
| return input_ids, positions |
|
|
| def _prepare_sample(self, requests: List[Seq], temperature: float): |
| temperatures = [temperature] * len(requests) |
| temperatures = torch.tensor( |
| temperatures, dtype=torch.float32, pin_memory=True |
| ).cuda(non_blocking=True) |
| return temperatures |
|
|
| def _capture_cuda_graphs( |
| self, |
| tts_mel_embedding=None, |
| tts_text_pos_embedding=None, |
| max_num_blocks: Optional[int] = None, |
| ): |
| print("Capturing CUDA graphs for decode optimization...") |
| max_bs = 8 |
| if max_num_blocks is None: |
| max_num_blocks = self.num_blocks |
| max_num_blocks = max(1, min(int(max_num_blocks), int(self.num_blocks))) |
| model_dtype = next(self.model.parameters()).dtype |
| input_ids = torch.ones(max_bs, dtype=torch.int64, device="cuda") |
| positions = torch.ones(max_bs, dtype=torch.int64, device="cuda") |
| slot_mapping = torch.zeros(max_bs, dtype=torch.int32, device="cuda") |
| context_lens = torch.zeros(max_bs, dtype=torch.int32, device="cuda") |
| block_tables = torch.zeros(max_bs, max_num_blocks, dtype=torch.int32, device="cuda") |
| outputs = torch.zeros(max_bs, self.hidden_size, dtype=model_dtype, device="cuda") |
| inputs_embeds_buffer = torch.zeros(max_bs, self.hidden_size, dtype=model_dtype, device="cuda") |
|
|
| self.graph_bs = [1, 2, 4, 8] |
|
|
| use_tts = tts_mel_embedding is not None and tts_text_pos_embedding is not None |
|
|
| for bs in reversed(self.graph_bs): |
| graph = torch.cuda.CUDAGraph() |
|
|
| slot_mapping[:bs].copy_(torch.arange(bs, dtype=torch.int32, device="cuda")) |
| context_lens[:bs].fill_(bs + 1) |
| block_tables[:bs, :].zero_() |
|
|
| set_forward_context( |
| False, |
| slot_mapping=slot_mapping[:bs], |
| context_lens=context_lens[:bs], |
| block_tables=block_tables[:bs], |
| ) |
|
|
| |
| if use_tts: |
| inputs_embeds_buffer[:bs].copy_( |
| self._compute_tts_embeds( |
| input_ids[:bs], |
| positions[:bs], |
| tts_mel_embedding=tts_mel_embedding, |
| tts_text_pos_embedding=tts_text_pos_embedding, |
| ) |
| ) |
| out = self.model( |
| inputs_embeds=inputs_embeds_buffer[:bs].unsqueeze(1), |
| return_dict=True, |
| ).last_hidden_state |
| else: |
| out = self.model( |
| input_ids=input_ids[:bs].unsqueeze(1), return_dict=True |
| ).last_hidden_state |
| outputs[:bs].copy_(out.squeeze(1) if out.dim() == 3 else out) |
|
|
| with torch.cuda.graph(graph, self.graph_pool, capture_error_mode="thread_local"): |
| if use_tts: |
| inputs_embeds_buffer[:bs].copy_( |
| self._compute_tts_embeds( |
| input_ids[:bs], |
| positions[:bs], |
| tts_mel_embedding=tts_mel_embedding, |
| tts_text_pos_embedding=tts_text_pos_embedding, |
| ) |
| ) |
| out = self.model( |
| inputs_embeds=inputs_embeds_buffer[:bs].unsqueeze(1), |
| return_dict=True, |
| ).last_hidden_state |
| else: |
| out = self.model( |
| input_ids=input_ids[:bs].unsqueeze(1), return_dict=True |
| ).last_hidden_state |
| outputs[:bs].copy_(out.squeeze(1) if out.dim() == 3 else out) |
|
|
| if self.graph_pool is None: |
| self.graph_pool = graph.pool() |
|
|
| self.graphs[bs] = graph |
| torch.cuda.synchronize() |
| reset_forward_context() |
|
|
| self.graph_vars = { |
| "input_ids": input_ids, |
| "positions": positions, |
| "slot_mapping": slot_mapping, |
| "context_lens": context_lens, |
| "block_tables": block_tables, |
| "outputs": outputs, |
| "inputs_embeds": inputs_embeds_buffer, |
| } |
| print(f"CUDA graphs captured for batch sizes: {self.graph_bs}") |
|
|
| def _run_decode_with_graph( |
| self, |
| input_ids: torch.Tensor, |
| positions: torch.Tensor, |
| context: ForwardContext, |
| tts_mel_embedding: Optional[torch.nn.Module] = None, |
| tts_text_pos_embedding: Optional[torch.nn.Module] = None, |
| ) -> torch.Tensor: |
| bs = input_ids.size(0) |
| use_tts_embedding = hasattr(self, "_tts_mode") and self._tts_mode |
|
|
| if not self.use_cuda_graph or not self.graphs: |
| if use_tts_embedding: |
| inputs_embeds = self._compute_tts_embeds( |
| input_ids, |
| positions, |
| tts_mel_embedding=tts_mel_embedding, |
| tts_text_pos_embedding=tts_text_pos_embedding, |
| ) |
| out = self.model( |
| inputs_embeds=inputs_embeds.unsqueeze(1), return_dict=True |
| ).last_hidden_state |
| else: |
| out = self.model( |
| input_ids=input_ids.unsqueeze(1), return_dict=True |
| ).last_hidden_state |
| return out.squeeze(1) if out.dim() == 3 else out |
|
|
| graph_bs = next((x for x in self.graph_bs if x >= bs), None) |
| if graph_bs is None: |
| if use_tts_embedding: |
| inputs_embeds = self._compute_tts_embeds( |
| input_ids, |
| positions, |
| tts_mel_embedding=tts_mel_embedding, |
| tts_text_pos_embedding=tts_text_pos_embedding, |
| ) |
| out = self.model( |
| inputs_embeds=inputs_embeds.unsqueeze(1), return_dict=True |
| ).last_hidden_state |
| else: |
| out = self.model( |
| input_ids=input_ids.unsqueeze(1), return_dict=True |
| ).last_hidden_state |
| return out.squeeze(1) if out.dim() == 3 else out |
|
|
| graph = self.graphs[graph_bs] |
| graph_vars = self.graph_vars |
|
|
| if graph_vars is None: |
| raise RuntimeError("Graph variables not initialized") |
|
|
| graph_vars["input_ids"][:bs].copy_(input_ids) |
| graph_vars["positions"][:bs].copy_(positions) |
| graph_vars["slot_mapping"].fill_(-1) |
| graph_vars["slot_mapping"][:bs].copy_(context.slot_mapping) |
| graph_vars["context_lens"].zero_() |
| graph_vars["context_lens"][:bs].copy_(context.context_lens) |
| graph_vars["block_tables"][:bs, :].fill_(-1) |
| graph_vars["block_tables"][:bs, : context.block_tables.size(1)].copy_(context.block_tables) |
| graph.replay() |
|
|
| return graph_vars["outputs"][:bs] |
|
|
| def generate( |
| self, |
| input_ids: torch.Tensor, |
| max_new_tokens: int = 100, |
| temperature: float = 1.0, |
| top_k: int = 50, |
| top_p: float = 1.0, |
| stop_tokens: Optional[List[int]] = None, |
| attention_mask: Optional[torch.Tensor] = None, |
| tts_embeddings: Optional[ |
| torch.Tensor |
| ] = None, |
| tts_mel_embedding: Optional[torch.nn.Module] = None, |
| tts_text_pos_embedding: Optional[ |
| torch.nn.Module |
| ] = None, |
| cg_max_total_tokens: Optional[int] = None, |
| stop_checker: Optional[Callable[[], bool]] = None, |
| ) -> torch.Tensor: |
| """ |
| Generate tokens. |
| |
| Args: |
| input_ids: Input token IDs [batch_size, seq_len] |
| max_new_tokens: Maximum number of tokens to generate |
| temperature: Sampling temperature |
| top_k: Top-k sampling |
| top_p: Nucleus sampling threshold |
| stop_tokens: List of token IDs that stop generation |
| |
| Returns: |
| Generated token IDs [batch_size, total_len] |
| """ |
| batch_size = input_ids.size(0) |
| device = input_ids.device |
|
|
| self._tts_mode = tts_embeddings is not None |
| self._tts_prompt_len = input_ids.size(1) if self._tts_mode else 0 |
| self._reset_kv_allocator_state() |
| prompt_tokens = (tts_embeddings.size(1) + 1) if tts_embeddings is not None else input_ids.size(1) |
| required_total_tokens = int(prompt_tokens + max(1, int(max_new_tokens))) |
| if cg_max_total_tokens is not None: |
| required_total_tokens = max(required_total_tokens, int(cg_max_total_tokens)) |
| self.prepare_decode_graph( |
| required_total_tokens, |
| tts_mel_embedding=tts_mel_embedding, |
| tts_text_pos_embedding=tts_text_pos_embedding, |
| ) |
|
|
| if tts_embeddings is not None: |
| actual_seq_len = tts_embeddings.size(1) + 1 |
| else: |
| actual_seq_len = input_ids.size(1) |
|
|
| is_varlen_batch = ( |
| tts_embeddings is not None |
| and attention_mask is not None |
| and batch_size > 1 |
| and (attention_mask.sum(dim=1) != attention_mask.size(1)).any() |
| ) |
|
|
| if is_varlen_batch: |
| seq_lens = [attention_mask[i].sum().item() for i in range(batch_size)] |
| else: |
| seq_lens = [actual_seq_len] * batch_size |
|
|
| sequences = [] |
| for i in range(batch_size): |
| seq_len = seq_lens[i] |
| token_ids = [1] * seq_len |
| if tts_embeddings is not None and seq_len > 0: |
| token_ids[-1] = input_ids[i, -1].item() if input_ids.size(1) > 0 else 1 |
| else: |
| token_ids = input_ids[i].tolist() |
| req = Seq(token_ids) |
| self.kv_manager.allocate(req) |
| sequences.append(req) |
|
|
| self.current_sequences = sequences |
|
|
| prefill_ids, prefill_pos = self._prepare_prefill(sequences) |
|
|
| if ( |
| tts_embeddings is not None |
| and tts_mel_embedding is not None |
| and tts_text_pos_embedding is not None |
| ): |
| start_token_id = input_ids[0, -1] if input_ids.size(1) > 0 else 8192 |
|
|
| start_emb = tts_mel_embedding( |
| torch.tensor([[start_token_id]], device="cuda") |
| ) |
|
|
| start_pos = torch.tensor( |
| [[tts_embeddings.size(1)]], device="cuda", dtype=torch.long |
| ) |
| pos_emb_module = getattr(tts_text_pos_embedding, "emb", tts_text_pos_embedding) |
| pos_emb = pos_emb_module(start_pos) |
| start_emb = start_emb + pos_emb |
| start_emb = start_emb.repeat(batch_size, 1, 1) |
|
|
| if is_varlen_batch: |
| valid_embeddings = [] |
| for i in range(batch_size): |
| emb_len = seq_lens[i] - 1 |
| padding_len = tts_embeddings.size(1) - emb_len |
| valid_emb = tts_embeddings[i, padding_len:].unsqueeze( |
| 0 |
| ) |
| valid_embeddings.append( |
| torch.cat([valid_emb, start_emb[i : i + 1]], dim=1) |
| ) |
| full_embeddings = torch.cat( |
| valid_embeddings, dim=1 |
| ) |
| else: |
| full_embeddings = torch.cat( |
| [tts_embeddings, start_emb], dim=1 |
| ) |
|
|
| model_dtype = next(self.model.parameters()).dtype |
| if full_embeddings.dtype != model_dtype: |
| full_embeddings = full_embeddings.to(model_dtype) |
|
|
| hidden_states = self.model( |
| inputs_embeds=full_embeddings, return_dict=True |
| ).last_hidden_state |
|
|
| else: |
| hidden_states = self.model( |
| input_ids=input_ids, attention_mask=attention_mask, return_dict=True |
| ).last_hidden_state |
|
|
| if is_varlen_batch: |
| context = get_forward_context() |
| cu_seqlens = context.cu_seqlens_q.cpu().tolist() |
| last_hidden = torch.stack( |
| [hidden_states[0, cu_seqlens[i + 1] - 1] for i in range(batch_size)] |
| ) |
| else: |
| last_hidden = hidden_states[:, -1, :] |
|
|
| reset_forward_context() |
|
|
| if self.lm_head is not None: |
| if last_hidden.dtype != next(self.lm_head.parameters()).dtype: |
| last_hidden = last_hidden.to(next(self.lm_head.parameters()).dtype) |
| logits = self.lm_head(last_hidden) |
| else: |
| logits = self.model.compute_logits(last_hidden) |
|
|
| temperatures = self._prepare_sample(sequences, temperature) |
| if temperature > 0: |
| sampling_logits = self._filter_logits(logits, top_k=top_k, top_p=top_p) |
| first_token = self.sampler(sampling_logits, temperatures) |
| else: |
| first_token = torch.argmax(logits, dim=-1) |
|
|
| first_token_list = first_token.tolist() |
|
|
| generated_tokens = [[] for _ in range(batch_size)] |
| is_finished = [False] * batch_size |
| token_progress = tqdm(total=int(max_new_tokens), desc="transformer_tokens", unit="tok", leave=True) |
| def _should_stop_early(): |
| if stop_checker is None: |
| return False |
| try: |
| return bool(stop_checker()) |
| except Exception: |
| return False |
|
|
| try: |
| for i, token_id in enumerate(first_token_list): |
| if stop_tokens and token_id in stop_tokens: |
| is_finished[i] = True |
| generated_tokens[i].append(token_id) |
| else: |
| generated_tokens[i].append(token_id) |
| sequences[i].append_token(token_id) |
| self.kv_manager.append_to_seq(sequences[i]) |
| token_progress.update(1) |
| stop_early = _should_stop_early() |
|
|
| if all(is_finished) and not stop_early: |
| for req in sequences: |
| self.kv_manager.remove_seq(req) |
| self.current_sequences = [] |
|
|
| output_ids = [] |
| for i in range(batch_size): |
| full_sequence = input_ids[i].tolist() + generated_tokens[i] |
| output_ids.append(full_sequence) |
|
|
| output = torch.tensor(output_ids, dtype=torch.long, device=device) |
| return output |
|
|
| remaining_tokens = 0 if stop_early else (max_new_tokens - 1) |
|
|
| for step in range(remaining_tokens): |
| if _should_stop_early(): |
| break |
| decode_ids, decode_pos = self._prepare_decode(sequences) |
|
|
| context = get_forward_context() |
| hidden_states = self._run_decode_with_graph( |
| decode_ids, |
| decode_pos, |
| context, |
| tts_mel_embedding=tts_mel_embedding, |
| tts_text_pos_embedding=tts_text_pos_embedding, |
| ) |
|
|
| |
| if self.lm_head is not None: |
| logits = self.lm_head(hidden_states) |
| else: |
| logits = self.model.compute_logits( |
| hidden_states |
| ) |
|
|
| reset_forward_context() |
|
|
| temperatures = self._prepare_sample(sequences, temperature) |
| if temperature > 0: |
| sampling_logits = self._filter_logits(logits, top_k=top_k, top_p=top_p) |
| next_token = self.sampler(sampling_logits, temperatures) |
| else: |
| next_token = torch.argmax(logits, dim=-1) |
| next_token_list = next_token.tolist() |
|
|
| for i, token_id in enumerate(next_token_list): |
| if is_finished[i]: |
| continue |
| elif stop_tokens and token_id in stop_tokens: |
| is_finished[i] = True |
| generated_tokens[i].append(token_id) |
| else: |
| sequences[i].append_token(token_id) |
| self.kv_manager.append_to_seq(sequences[i]) |
| generated_tokens[i].append(token_id) |
| token_progress.update(1) |
|
|
| if all(is_finished): |
| break |
|
|
| for req in sequences: |
| self.kv_manager.remove_seq(req) |
| self.current_sequences = [] |
|
|
| pad_token = stop_tokens[0] if stop_tokens else 0 |
|
|
| if is_varlen_batch: |
| max_prompt_len = attention_mask.size(1) |
| output_ids = [] |
|
|
| for i in range(batch_size): |
| padding_len = max_prompt_len - seq_lens[i] |
| initial_tokens = sequences[i].token_ids[ |
| : sequences[i].num_prompt_tokens |
| ] |
| padded_prompt = [pad_token] * padding_len + initial_tokens |
| full_sequence = padded_prompt + generated_tokens[i] |
| output_ids.append(full_sequence) |
| else: |
| output_ids = [ |
| sequences[i].token_ids[: sequences[i].num_prompt_tokens] |
| + generated_tokens[i] |
| for i in range(batch_size) |
| ] |
|
|
| max_length = max(len(seq) for seq in output_ids) |
| padded_output_ids = [ |
| seq + [pad_token] * (max_length - len(seq)) for seq in output_ids |
| ] |
|
|
| output = torch.tensor(padded_output_ids, dtype=torch.long, device=device) |
|
|
| assert output.size(0) == batch_size, ( |
| f"Output batch size mismatch: {output.size(0)} != {batch_size}" |
| ) |
|
|
| return output |
| finally: |
| token_progress.close() |
|
|
|
|
| class Sampler(nn.Module): |
| def __init__(self): |
| super().__init__() |
|
|
| |
| def forward(self, logits: torch.Tensor, temperatures: torch.Tensor): |
| logits = logits.float().div_(temperatures.unsqueeze(dim=1)) |
| probs = torch.softmax(logits, dim=-1) |
| sample_tokens = probs.div_( |
| torch.empty_like(probs).exponential_(1).clamp_min_(1e-10) |
| ).argmax(dim=-1) |
| return sample_tokens |
|
|