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.
- indextts25/__init__.py +5 -0
- indextts25/accel/__init__.py +2 -0
- indextts25/accel/accel_engine.py +205 -0
- indextts25/accel/attention.py +148 -0
- indextts25/accel/gpt2_accel.py +131 -0
- indextts25/accel/kv_manager.py +62 -0
- indextts25/api.py +191 -0
- indextts25/codec/__init__.py +0 -0
- indextts25/codec/models.py +97 -0
- indextts25/gpt/__init__.py +0 -0
- indextts25/gpt/conformer/__init__.py +0 -0
- indextts25/gpt/conformer/attention.py +311 -0
- indextts25/gpt/conformer/embedding.py +162 -0
- indextts25/gpt/conformer/subsampling.py +85 -0
- indextts25/gpt/conformer_encoder.py +503 -0
- indextts25/gpt/model_v2.py +487 -0
- indextts25/gpt/perceiver.py +180 -0
- indextts25/infer_v2_5.py +565 -0
- indextts25/s2mel/modules/audio.py +51 -0
- indextts25/s2mel/modules/bigvgan/activations.py +120 -0
- indextts25/s2mel/modules/bigvgan/alias_free_activation/torch/__init__.py +2 -0
- indextts25/s2mel/modules/bigvgan/alias_free_activation/torch/act.py +31 -0
- indextts25/s2mel/modules/bigvgan/alias_free_activation/torch/filter.py +102 -0
- indextts25/s2mel/modules/bigvgan/alias_free_activation/torch/resample.py +58 -0
- indextts25/s2mel/modules/bigvgan/bigvgan.py +119 -0
- indextts25/s2mel/modules/bigvgan/env.py +8 -0
- indextts25/s2mel/modules/bigvgan/utils.py +6 -0
- indextts25/s2mel/modules/campplus/DTDNN.py +122 -0
- indextts25/s2mel/modules/campplus/layers.py +251 -0
- indextts25/s2mel/modules/commons.py +40 -0
- indextts25/s2mel/modules/diffusion_transformer.py +237 -0
- indextts25/s2mel/modules/encodec.py +92 -0
- indextts25/s2mel/modules/flow_matching.py +55 -0
- indextts25/s2mel/modules/gpt_fast/model.py +225 -0
- indextts25/s2mel/modules/length_regulator.py +33 -0
- indextts25/s2mel/modules/wavenet.py +73 -0
- indextts25/utils/__init__.py +0 -0
- indextts25/utils/checkpoint.py +43 -0
- indextts25/utils/common.py +120 -0
- indextts25/utils/front.py +417 -0
- indextts25/utils/ja_g2p.py +102 -0
- indextts25/utils/model_download.py +30 -0
- indextts25/utils/nemo_tn.py +200 -0
- indextts25/utils/tokenizer.py +200 -0
- indextts25/utils/typical_sampling.py +31 -0
- indextts25_inference-0.1.1.dist-info/METADATA +320 -0
- indextts25_inference-0.1.1.dist-info/RECORD +49 -0
- indextts25_inference-0.1.1.dist-info/WHEEL +4 -0
- indextts25_inference-0.1.1.dist-info/licenses/LICENSE +57 -0
indextts25/__init__.py
ADDED
|
@@ -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
|