indextts25-inference 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. indextts25/__init__.py +5 -0
  2. indextts25/accel/__init__.py +2 -0
  3. indextts25/accel/accel_engine.py +205 -0
  4. indextts25/accel/attention.py +148 -0
  5. indextts25/accel/gpt2_accel.py +131 -0
  6. indextts25/accel/kv_manager.py +62 -0
  7. indextts25/api.py +191 -0
  8. indextts25/codec/__init__.py +0 -0
  9. indextts25/codec/models.py +97 -0
  10. indextts25/gpt/__init__.py +0 -0
  11. indextts25/gpt/conformer/__init__.py +0 -0
  12. indextts25/gpt/conformer/attention.py +311 -0
  13. indextts25/gpt/conformer/embedding.py +162 -0
  14. indextts25/gpt/conformer/subsampling.py +85 -0
  15. indextts25/gpt/conformer_encoder.py +503 -0
  16. indextts25/gpt/model_v2.py +487 -0
  17. indextts25/gpt/perceiver.py +180 -0
  18. indextts25/infer_v2_5.py +565 -0
  19. indextts25/s2mel/modules/audio.py +51 -0
  20. indextts25/s2mel/modules/bigvgan/activations.py +120 -0
  21. indextts25/s2mel/modules/bigvgan/alias_free_activation/torch/__init__.py +2 -0
  22. indextts25/s2mel/modules/bigvgan/alias_free_activation/torch/act.py +31 -0
  23. indextts25/s2mel/modules/bigvgan/alias_free_activation/torch/filter.py +102 -0
  24. indextts25/s2mel/modules/bigvgan/alias_free_activation/torch/resample.py +58 -0
  25. indextts25/s2mel/modules/bigvgan/bigvgan.py +119 -0
  26. indextts25/s2mel/modules/bigvgan/env.py +8 -0
  27. indextts25/s2mel/modules/bigvgan/utils.py +6 -0
  28. indextts25/s2mel/modules/campplus/DTDNN.py +122 -0
  29. indextts25/s2mel/modules/campplus/layers.py +251 -0
  30. indextts25/s2mel/modules/commons.py +40 -0
  31. indextts25/s2mel/modules/diffusion_transformer.py +237 -0
  32. indextts25/s2mel/modules/encodec.py +92 -0
  33. indextts25/s2mel/modules/flow_matching.py +55 -0
  34. indextts25/s2mel/modules/gpt_fast/model.py +225 -0
  35. indextts25/s2mel/modules/length_regulator.py +33 -0
  36. indextts25/s2mel/modules/wavenet.py +73 -0
  37. indextts25/utils/__init__.py +0 -0
  38. indextts25/utils/checkpoint.py +43 -0
  39. indextts25/utils/common.py +120 -0
  40. indextts25/utils/front.py +417 -0
  41. indextts25/utils/ja_g2p.py +102 -0
  42. indextts25/utils/model_download.py +30 -0
  43. indextts25/utils/nemo_tn.py +200 -0
  44. indextts25/utils/tokenizer.py +200 -0
  45. indextts25/utils/typical_sampling.py +31 -0
  46. indextts25_inference-0.1.1.dist-info/METADATA +320 -0
  47. indextts25_inference-0.1.1.dist-info/RECORD +49 -0
  48. indextts25_inference-0.1.1.dist-info/WHEEL +4 -0
  49. indextts25_inference-0.1.1.dist-info/licenses/LICENSE +57 -0
indextts25/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """IndexTTS-2.5: zero-shot speech synthesis with emotion and speed control."""
2
+ from .api import STREAM_SEGMENT_TOKENS, Chunk, IndexTTS25
3
+
4
+ __all__ = ["IndexTTS25", "Chunk", "STREAM_SEGMENT_TOKENS"]
5
+ __version__ = "0.1.1" # x-release-please-version
@@ -0,0 +1,2 @@
1
+ from .accel_engine import AccelInferenceEngine # noqa: F401
2
+ from .gpt2_accel import GPT2AccelModel # noqa: F401
@@ -0,0 +1,205 @@
1
+ """Accelerated decoding engine for the IndexTTS GPT (attn_backend="flash").
2
+
3
+ One sequence at a time (batch 1): paged fp16 KV cache, prefill with
4
+ flash_attn_varlen_func and every decode step captured in a CUDA graph. Samples like
5
+ transformers with num_beams=1 (repetition_penalty, temperature, top-k, top-p); no
6
+ beam search.
7
+ """
8
+ import torch
9
+ from torch import nn
10
+
11
+ from .attention import reset_forward_context, set_forward_context
12
+ from .kv_manager import KVCacheManager, Seq
13
+
14
+
15
+ class AccelInferenceEngine:
16
+ def __init__(
17
+ self,
18
+ model,
19
+ lm_head,
20
+ num_layers: int,
21
+ num_heads: int,
22
+ head_dim: int,
23
+ block_size: int = 256,
24
+ num_blocks: int = 16,
25
+ use_cuda_graph: bool = True,
26
+ ):
27
+ """
28
+ Args:
29
+ model: the GPT2 with accel attention (GPT2AccelModel).
30
+ lm_head: final_norm + mel_head.
31
+ block_size / num_blocks: geometry of the paged KV pool.
32
+ use_cuda_graph: capture the decode step in a CUDA graph.
33
+ """
34
+ self.model = model
35
+ self.lm_head = lm_head
36
+ self.block_size = block_size
37
+ self.use_cuda_graph = use_cuda_graph and torch.cuda.is_available()
38
+ self.hidden_size = model.config.hidden_size
39
+ self.kv_manager = KVCacheManager(num_layers, num_heads, head_dim, block_size, num_blocks)
40
+ self.kv_manager.wire_kv_cache_to_model(model)
41
+ self.graph = None
42
+ self.graph_vars = None
43
+
44
+ def _capture_cuda_graph(self, mel_embedding, mel_pos_embedding):
45
+ # The block-table width is baked into the graph, and flash_attn_with_kvcache uses
46
+ # it to decide how to split the work: changing it changes the output in the last
47
+ # bit. It covers 2048 tokens; a longer sequence falls back to the non-graph path.
48
+ max_num_blocks = (2048 + self.block_size - 1) // self.block_size
49
+ dtype = next(self.model.parameters()).dtype
50
+ gv = {
51
+ "input_ids": torch.ones(1, dtype=torch.int64, device="cuda"),
52
+ "positions": torch.ones(1, dtype=torch.int64, device="cuda"),
53
+ "slot_mapping": torch.zeros(1, dtype=torch.int32, device="cuda"),
54
+ "context_lens": torch.full((1,), 2, dtype=torch.int32, device="cuda"),
55
+ "block_tables": torch.zeros(1, max_num_blocks, dtype=torch.int32, device="cuda"),
56
+ "inputs_embeds": torch.zeros(1, self.hidden_size, dtype=dtype, device="cuda"),
57
+ "outputs": torch.zeros(1, self.hidden_size, dtype=dtype, device="cuda"),
58
+ }
59
+
60
+ def step():
61
+ emb = mel_embedding(gv["input_ids"])
62
+ pos_emb = mel_pos_embedding.emb(torch.clamp(gv["positions"], min=0))
63
+ gv["inputs_embeds"][:] = emb + pos_emb
64
+ out = self.model(inputs_embeds=gv["inputs_embeds"].unsqueeze(1))
65
+ gv["outputs"][:] = out.squeeze(1)
66
+
67
+ set_forward_context(False, slot_mapping=gv["slot_mapping"], context_lens=gv["context_lens"],
68
+ block_tables=gv["block_tables"])
69
+ step() # warmup
70
+ graph = torch.cuda.CUDAGraph()
71
+ with torch.cuda.graph(graph):
72
+ step()
73
+ torch.cuda.synchronize()
74
+ reset_forward_context()
75
+ self.graph, self.graph_vars = graph, gv
76
+
77
+ def _decode(self, seq: Seq, mel_embedding, mel_pos_embedding):
78
+ """One decode step for the last token of `seq`. Returns the hidden state [1, C]."""
79
+ pos = len(seq) - seq.num_prompt_tokens # mel position of the code
80
+ slot = seq.block_table[-1] * self.block_size + seq.last_block_num_tokens - 1
81
+ gv = self.graph_vars
82
+ if gv is not None and len(seq.block_table) <= gv["block_tables"].size(1):
83
+ gv["input_ids"].fill_(seq.last_token)
84
+ gv["positions"].fill_(pos)
85
+ gv["slot_mapping"].fill_(slot)
86
+ gv["context_lens"].fill_(len(seq))
87
+ if len(seq.block_table) > self._graph_blocks: # a new block was opened
88
+ self._graph_blocks = len(seq.block_table)
89
+ gv["block_tables"][0, self._graph_blocks - 1] = seq.block_table[-1]
90
+ self.graph.replay()
91
+ return gv["outputs"]
92
+
93
+ set_forward_context(
94
+ False,
95
+ slot_mapping=torch.tensor([slot], dtype=torch.int32, device="cuda"),
96
+ context_lens=torch.tensor([len(seq)], dtype=torch.int32, device="cuda"),
97
+ block_tables=torch.tensor([seq.block_table], dtype=torch.int32, device="cuda"),
98
+ )
99
+ emb = mel_embedding(torch.tensor([seq.last_token], device="cuda"))
100
+ emb = emb + mel_pos_embedding.emb(torch.tensor([pos], device="cuda"))
101
+ out = self.model(inputs_embeds=emb.to(next(self.model.parameters()).dtype).unsqueeze(1))
102
+ reset_forward_context()
103
+ return out.squeeze(1)
104
+
105
+ def _sample(self, logits, temperature, top_k, top_p, rp, seen):
106
+ """One token, in transformers' order: repetition penalty on the raw logits,
107
+ temperature, top-k, top-p and sampling (exponential race)."""
108
+ if rp != 1.0:
109
+ logits = torch.where(seen, torch.where(logits < 0, logits * rp, logits / rp), logits)
110
+ if temperature <= 0:
111
+ tok = torch.argmax(logits, dim=-1).tolist()[0]
112
+ else:
113
+ logits = logits.float() / temperature
114
+ if top_k > 0:
115
+ kth = torch.topk(logits, min(top_k, logits.size(-1))).values[..., -1:]
116
+ logits = logits.masked_fill(logits < kth, float("-inf"))
117
+ if top_p < 1.0:
118
+ sl, idx = torch.sort(logits, descending=False)
119
+ remove = sl.softmax(-1).cumsum(-1) <= (1 - top_p)
120
+ remove[..., -1:] = False
121
+ logits = logits.masked_fill(remove.scatter(1, idx, remove), float("-inf"))
122
+ probs = logits.softmax(-1)
123
+ tok = (probs / torch.empty_like(probs).exponential_(1).clamp_min_(1e-10)).argmax(-1).tolist()[0]
124
+ seen[0, tok] = True
125
+ return tok
126
+
127
+ def generate(
128
+ self,
129
+ input_ids: torch.Tensor,
130
+ max_new_tokens: int,
131
+ temperature: float,
132
+ stop_tokens,
133
+ tts_embeddings: torch.Tensor,
134
+ tts_mel_embedding: nn.Module,
135
+ tts_text_pos_embedding: nn.Module,
136
+ top_k: int = 0,
137
+ top_p: float = 1.0,
138
+ repetition_penalty: float = 1.0,
139
+ ) -> torch.Tensor:
140
+ """Generate codes for one sequence.
141
+
142
+ Args:
143
+ input_ids: [1, n] filler ids ([1]*(n-1) + [start_mel]); the real prompt is
144
+ `tts_embeddings` ([1, n-1, C]: [pad][cond][text]) plus the start_mel.
145
+ max_new_tokens: cap on the number of generated codes.
146
+ stop_tokens: codes that end the sequence (included in the output).
147
+ tts_mel_embedding / tts_text_pos_embedding: code and position embeddings.
148
+
149
+ Returns:
150
+ [1, n + generated], like transformers' generate().
151
+ """
152
+ if self.use_cuda_graph and self.graph is None:
153
+ self._capture_cuda_graph(tts_mel_embedding, tts_text_pos_embedding)
154
+
155
+ n = input_ids.size(1)
156
+ vocab = tts_mel_embedding.num_embeddings
157
+ seen = torch.zeros(1, vocab, dtype=torch.bool, device=input_ids.device)
158
+ seen[0, input_ids[0]] = True # like transformers: the prompt's filler ids count too
159
+ knobs = (top_k, top_p, repetition_penalty, seen)
160
+ seq = Seq(n, last_token=-1, block_size=self.block_size)
161
+ self.kv_manager.allocate(seq)
162
+ if self.graph_vars is not None:
163
+ self.graph_vars["block_tables"].fill_(-1)
164
+ first = seq.block_table[: self.graph_vars["block_tables"].size(1)]
165
+ self.graph_vars["block_tables"][0, : len(first)] = torch.tensor(first, dtype=torch.int32)
166
+ self._graph_blocks = len(first)
167
+ try:
168
+ # Prefill: [pad][cond][text] + start_mel at mel position 0, as in training
169
+ # (it used to go at n-1, the position of the last prompt token).
170
+ start_emb = tts_mel_embedding(input_ids[:, -1:])
171
+ start_pos = torch.zeros((1, 1), device="cuda", dtype=torch.long)
172
+ start_emb = start_emb + tts_text_pos_embedding.emb(start_pos)
173
+ full_embeddings = torch.cat([tts_embeddings, start_emb], dim=1)
174
+ model_dtype = next(self.model.parameters()).dtype
175
+ if full_embeddings.dtype != model_dtype:
176
+ full_embeddings = full_embeddings.to(model_dtype)
177
+
178
+ slots = [seq.block_table[t // self.block_size] * self.block_size + t % self.block_size
179
+ for t in range(n)]
180
+ cu_seqlens = torch.tensor([0, n], dtype=torch.int32, device="cuda")
181
+ set_forward_context(True, cu_seqlens, cu_seqlens, n, n,
182
+ torch.tensor(slots, dtype=torch.int32, device="cuda"))
183
+ hidden = self.model(inputs_embeds=full_embeddings)[:, -1, :]
184
+ reset_forward_context()
185
+ lm_dtype = next(self.lm_head.parameters()).dtype
186
+ if hidden.dtype != lm_dtype:
187
+ hidden = hidden.to(lm_dtype)
188
+
189
+ generated = []
190
+ token = self._sample(self.lm_head(hidden), temperature, *knobs)
191
+ for step in range(max_new_tokens):
192
+ generated.append(token)
193
+ # The stop token goes in the output, as in transformers: without it,
194
+ # infer_v2_5 warned about a max_mel_tokens cutoff on every call.
195
+ if token in stop_tokens or step == max_new_tokens - 1:
196
+ break
197
+ seq.append_token(token)
198
+ self.kv_manager.append_to_seq(seq)
199
+ hidden = self._decode(seq, tts_mel_embedding, tts_text_pos_embedding)
200
+ token = self._sample(self.lm_head(hidden), temperature, *knobs)
201
+ finally:
202
+ self.kv_manager.free(seq)
203
+ reset_forward_context()
204
+
205
+ return torch.cat([input_ids, torch.tensor([generated], dtype=input_ids.dtype, device=input_ids.device)], dim=1)
@@ -0,0 +1,148 @@
1
+ from dataclasses import dataclass
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+ from flash_attn import flash_attn_varlen_func, flash_attn_with_kvcache
7
+ from torch import nn
8
+
9
+
10
+ @dataclass
11
+ class ForwardContext:
12
+ is_prefill: bool = False
13
+ cu_seqlens_q: torch.Tensor | None = None
14
+ cu_seqlens_k: torch.Tensor | None = None
15
+ max_seqlen_q: int = 0
16
+ max_seqlen_k: int = 0
17
+ slot_mapping: torch.Tensor | None = None
18
+ context_lens: torch.Tensor | None = None
19
+ block_tables: torch.Tensor | None = None
20
+
21
+
22
+ _FORWARD_CONTEXT = ForwardContext()
23
+
24
+
25
+ def get_forward_context():
26
+ return _FORWARD_CONTEXT
27
+
28
+
29
+ def set_forward_context(
30
+ is_prefill,
31
+ cu_seqlens_q=None,
32
+ cu_seqlens_k=None,
33
+ max_seqlen_q=0,
34
+ max_seqlen_k=0,
35
+ slot_mapping=None,
36
+ context_lens=None,
37
+ block_tables=None,
38
+ ):
39
+ global _FORWARD_CONTEXT
40
+ _FORWARD_CONTEXT = ForwardContext(
41
+ is_prefill,
42
+ cu_seqlens_q,
43
+ cu_seqlens_k,
44
+ max_seqlen_q,
45
+ max_seqlen_k,
46
+ slot_mapping,
47
+ context_lens,
48
+ block_tables,
49
+ )
50
+
51
+
52
+ def reset_forward_context():
53
+ global _FORWARD_CONTEXT
54
+ _FORWARD_CONTEXT = ForwardContext()
55
+
56
+
57
+ @triton.jit
58
+ def store_kvcache_kernel(
59
+ key_ptr,
60
+ key_stride,
61
+ value_ptr,
62
+ value_stride,
63
+ k_cache_ptr,
64
+ v_cache_ptr,
65
+ slot_mapping_ptr,
66
+ D: tl.constexpr,
67
+ ):
68
+ BLOCK_SIZE: tl.constexpr = 2048
69
+ idx = tl.program_id(0)
70
+ slot = tl.load(slot_mapping_ptr + idx)
71
+ if slot == -1:
72
+ return
73
+ d_offset = 0
74
+ while d_offset < D:
75
+ cur_block_size = min(BLOCK_SIZE, D - d_offset)
76
+ key_offsets = idx * key_stride + d_offset + tl.arange(0, BLOCK_SIZE)
77
+ value_offsets = idx * value_stride + d_offset + tl.arange(0, BLOCK_SIZE)
78
+ cache_offsets = slot * D + d_offset + tl.arange(0, BLOCK_SIZE)
79
+
80
+ mask = tl.arange(0, BLOCK_SIZE) < cur_block_size
81
+ key = tl.load(key_ptr + key_offsets, mask=mask, other=0.0)
82
+ value = tl.load(value_ptr + value_offsets, mask=mask, other=0.0)
83
+ tl.store(k_cache_ptr + cache_offsets, key, mask=mask)
84
+ tl.store(v_cache_ptr + cache_offsets, value, mask=mask)
85
+
86
+ d_offset += BLOCK_SIZE
87
+
88
+
89
+ def store_kvcache(
90
+ key: torch.Tensor,
91
+ value: torch.Tensor,
92
+ k_cache: torch.Tensor,
93
+ v_cache: torch.Tensor,
94
+ slot_mapping: torch.Tensor,
95
+ ):
96
+ N, num_heads, head_dim = key.shape
97
+ D = num_heads * head_dim
98
+ assert key.stride(-1) == 1 and value.stride(-1) == 1
99
+ assert key.stride(1) == head_dim and value.stride(1) == head_dim
100
+ assert k_cache.stride(1) == D and v_cache.stride(1) == D
101
+ assert slot_mapping.numel() == N
102
+ store_kvcache_kernel[(N,)](
103
+ key, key.stride(0), value, value.stride(0), k_cache, v_cache, slot_mapping, D
104
+ )
105
+
106
+
107
+ class Attention(nn.Module):
108
+ """Attention over the paged KV cache: writes k/v into their slots and uses flash-attn
109
+ (varlen for prefill, with_kvcache for decode). The engine sets the context."""
110
+
111
+ def __init__(self, scale: float):
112
+ super().__init__()
113
+ self.scale = scale
114
+ self.k_cache = self.v_cache = torch.tensor([])
115
+
116
+ def forward(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor):
117
+ context = get_forward_context()
118
+ k_cache, v_cache = self.k_cache, self.v_cache
119
+
120
+ if k_cache.numel() and v_cache.numel() and context.slot_mapping is not None:
121
+ store_kvcache(k, v, k_cache, v_cache, context.slot_mapping)
122
+
123
+ if context.is_prefill:
124
+ if context.block_tables is not None:
125
+ k, v = k_cache, v_cache
126
+ o = flash_attn_varlen_func(
127
+ q,
128
+ k,
129
+ v,
130
+ max_seqlen_q=context.max_seqlen_q,
131
+ cu_seqlens_q=context.cu_seqlens_q,
132
+ max_seqlen_k=context.max_seqlen_k,
133
+ cu_seqlens_k=context.cu_seqlens_k,
134
+ softmax_scale=self.scale,
135
+ causal=True,
136
+ block_table=context.block_tables,
137
+ )
138
+ else:
139
+ o = flash_attn_with_kvcache(
140
+ q.unsqueeze(1),
141
+ k_cache,
142
+ v_cache,
143
+ cache_seqlens=context.context_lens,
144
+ block_table=context.block_tables,
145
+ softmax_scale=self.scale,
146
+ causal=True,
147
+ )
148
+ return o
@@ -0,0 +1,131 @@
1
+ import torch
2
+ import torch.nn as nn
3
+ from transformers.models.gpt2.modeling_gpt2 import Conv1D, GPT2Block, GPT2Model
4
+
5
+ from .attention import Attention
6
+
7
+
8
+ class GPT2AccelAttention(nn.Module):
9
+ def __init__(self, config, layer_idx=None):
10
+ super().__init__()
11
+ self.config = config
12
+ self.layer_idx = layer_idx
13
+
14
+ self.embed_dim = config.hidden_size
15
+ self.num_heads = config.num_attention_heads
16
+ self.head_dim = self.embed_dim // self.num_heads
17
+ self.split_size = self.embed_dim
18
+
19
+ if self.head_dim * self.num_heads != self.embed_dim:
20
+ raise ValueError(
21
+ f"`embed_dim` must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
22
+ f" {self.num_heads})."
23
+ )
24
+
25
+ self.scale_attn_weights = config.scale_attn_weights
26
+
27
+ self.c_attn = Conv1D(3 * self.embed_dim, self.embed_dim)
28
+ self.c_proj = Conv1D(self.embed_dim, self.embed_dim)
29
+
30
+ self.attn_dropout = nn.Dropout(config.attn_pdrop)
31
+ self.resid_dropout = nn.Dropout(config.resid_pdrop)
32
+
33
+ scale = (self.head_dim**-0.5) if self.scale_attn_weights else 1.0
34
+ self.accel_attn = Attention(scale)
35
+
36
+ def forward(
37
+ self,
38
+ hidden_states: torch.Tensor,
39
+ layer_past=None,
40
+ attention_mask=None,
41
+ head_mask=None,
42
+ encoder_hidden_states=None,
43
+ encoder_attention_mask=None,
44
+ use_cache=False,
45
+ output_attentions=False,
46
+ past_key_value=None,
47
+ **kwargs,
48
+ ):
49
+ if encoder_hidden_states is not None:
50
+ raise NotImplementedError("Cross attention not supported in accel mode")
51
+
52
+ qkv = self.c_attn(hidden_states)
53
+ query, key, value = qkv.split(self.split_size, dim=-1)
54
+
55
+ # [B, T, H*D] -> [B, H, T, D]
56
+ query = self._split_heads(query, self.num_heads, self.head_dim)
57
+ key = self._split_heads(key, self.num_heads, self.head_dim)
58
+ value = self._split_heads(value, self.num_heads, self.head_dim)
59
+
60
+ # flatten to [B*T, H, D]
61
+ bsz, num_heads, seq_len, head_dim = query.shape
62
+ q_flat = query.transpose(1, 2).contiguous().view(-1, num_heads, head_dim)
63
+ k_flat = key.transpose(1, 2).contiguous().view(-1, num_heads, head_dim)
64
+ v_flat = value.transpose(1, 2).contiguous().view(-1, num_heads, head_dim)
65
+
66
+ # ensure fp16
67
+ if q_flat.device.type == "cuda" and q_flat.dtype != torch.float16:
68
+ orig_dtype = q_flat.dtype
69
+ q_flat = q_flat.to(torch.float16)
70
+ k_flat = k_flat.to(torch.float16)
71
+ v_flat = v_flat.to(torch.float16)
72
+ else:
73
+ orig_dtype = q_flat.dtype
74
+
75
+ o_flat = self.accel_attn(q_flat, k_flat, v_flat) # [B*T, H, D]
76
+
77
+ if o_flat.dtype != orig_dtype:
78
+ o_flat = o_flat.to(orig_dtype)
79
+
80
+ # Reshape back: [B*T, H, D] -> [B, H, T, D]
81
+ attn_output = o_flat.view(bsz, seq_len, num_heads, head_dim).transpose(1, 2)
82
+
83
+ attn_output = self._merge_heads(attn_output, self.num_heads, self.head_dim)
84
+
85
+ attn_output = self.c_proj(attn_output)
86
+ attn_output = self.resid_dropout(attn_output)
87
+
88
+ outputs = (attn_output, None)
89
+ if output_attentions:
90
+ outputs += (None,)
91
+
92
+ return outputs
93
+
94
+ def _split_heads(self, tensor, num_heads, head_dim):
95
+ new_shape = tensor.size()[:-1] + (num_heads, head_dim)
96
+ tensor = tensor.view(new_shape)
97
+ return tensor.permute(0, 2, 1, 3) # (batch, head, seq_length, head_features)
98
+
99
+ def _merge_heads(self, tensor, num_heads, head_dim):
100
+ tensor = tensor.permute(0, 2, 1, 3).contiguous()
101
+ new_shape = tensor.size()[:-2] + (num_heads * head_dim,)
102
+ return tensor.view(new_shape)
103
+
104
+
105
+ class GPT2AccelBlock(GPT2Block):
106
+ def __init__(self, config, layer_idx=None):
107
+ super().__init__(config, layer_idx)
108
+ self.attn = GPT2AccelAttention(config, layer_idx)
109
+
110
+ def forward(self, hidden_states):
111
+ # Custom forward: GPT2Block's forward changed its contract across transformers
112
+ # versions (through 5.2 it returns a tuple; from 5.3 on, the tensor).
113
+ hidden_states = hidden_states + self.attn(self.ln_1(hidden_states))[0]
114
+ return hidden_states + self.mlp(self.ln_2(hidden_states))
115
+
116
+
117
+ class GPT2AccelModel(GPT2Model):
118
+ """GPT2 with the accel attention. Only used with inputs_embeds (the codes are
119
+ embedded outside) and returns the final hidden state, with ln_f already applied."""
120
+
121
+ def __init__(self, config):
122
+ super().__init__(config)
123
+ self.h = nn.ModuleList(
124
+ [GPT2AccelBlock(config, layer_idx=i) for i in range(config.num_hidden_layers)]
125
+ )
126
+
127
+ def forward(self, inputs_embeds):
128
+ hidden_states = inputs_embeds
129
+ for block in self.h:
130
+ hidden_states = block(hidden_states)
131
+ return self.ln_f(hidden_states)
@@ -0,0 +1,62 @@
1
+ from collections import deque
2
+
3
+ import torch
4
+
5
+
6
+ class Seq:
7
+ """A sequence in the paged pool: how many tokens it has and which blocks hold them."""
8
+
9
+ def __init__(self, num_prompt_tokens: int, last_token: int, block_size: int = 256):
10
+ self.num_tokens = num_prompt_tokens
11
+ self.num_prompt_tokens = num_prompt_tokens
12
+ self.last_token = last_token
13
+ self.block_size = block_size
14
+ self.block_table: list[int] = []
15
+
16
+ def __len__(self):
17
+ return self.num_tokens
18
+
19
+ @property
20
+ def num_blocks(self):
21
+ return (self.num_tokens + self.block_size - 1) // self.block_size
22
+
23
+ @property
24
+ def last_block_num_tokens(self):
25
+ return self.num_tokens - (self.num_blocks - 1) * self.block_size
26
+
27
+ def append_token(self, token_id: int):
28
+ self.last_token = token_id
29
+ self.num_tokens += 1
30
+
31
+
32
+ class KVCacheManager:
33
+ """fp16 KV pool, [2, layers, blocks, block_size, heads, head_dim], handed out
34
+ block by block. No prefix caching: in TTS the real prompt is embeddings and the ids
35
+ are filler, so two long prompts "matched" and overwrote each other."""
36
+
37
+ def __init__(self, num_layers: int, num_heads: int, head_dim: int, block_size: int, num_blocks: int):
38
+ self.block_size = block_size
39
+ self.free_block_ids: deque = deque(range(num_blocks))
40
+ self.kv_cache = torch.empty(
41
+ 2, num_layers, num_blocks, block_size, num_heads, head_dim, dtype=torch.float16, device="cuda")
42
+
43
+ def allocate(self, seq: Seq):
44
+ for _ in range(seq.num_blocks):
45
+ seq.block_table.append(self.free_block_ids.popleft())
46
+
47
+ def append_to_seq(self, seq: Seq):
48
+ # The just-appended token opens a new block.
49
+ if len(seq) % self.block_size == 1:
50
+ seq.block_table.append(self.free_block_ids.popleft())
51
+
52
+ def free(self, seq: Seq):
53
+ self.free_block_ids.extend(reversed(seq.block_table))
54
+ seq.block_table.clear()
55
+
56
+ def wire_kv_cache_to_model(self, model):
57
+ layer_id = 0
58
+ for module in model.modules():
59
+ if hasattr(module, "k_cache") and hasattr(module, "v_cache"):
60
+ module.k_cache = self.kv_cache[0, layer_id]
61
+ module.v_cache = self.kv_cache[1, layer_id]
62
+ layer_id += 1