pltobing commited on
Commit
d4e8429
·
1 Parent(s): 6e309ad

fix: local talker & talker input structure

Browse files

- Now, local talker follows the backbone talker
to use attention_mask and cos/sin RoPE as input.
- This reduces the amount of CPU fallback ops
from mask and cos/sin creations.
- Both still use scatter with indexing from cache_pos
for KV-cache update. This needs to be modified to
avoid CPU fallback.
- Also separate the .onnx file as _prefill and _step
as handling the KV-cache without scatter in the
next revision will need baked static flow.
- This is within the process of optimizing Ops from
CPU fallback.

qwen3-tts_onnx/{talker_local_model.onnx → talker_local_model_prefill.onnx} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:8be4f6ddc1af78b7933ea9ae7c6ca787aa912d6a9aae54819d6bb5882d97b8f3
3
- size 314888365
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a64b5cc7bd8345a6a40c3cb0882d6ac699152817bd19e6f3a203214642c843c5
3
+ size 314880101
qwen3-tts_onnx/{talker_model.onnx → talker_local_model_step.onnx} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:8a3ad9936260c693af2eca5e8b22fb4a73fe1ca2d297a568e0f05ba520bd6033
3
- size 1775928943
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:76e7a751f3d8038c5adf5e81c0b1f4f56a480b43be643928ff1e4f748ff4ce43
3
+ size 314880101
qwen3-tts_onnx/talker_model_prefill.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:144ff25e52308068829932309acf2e8444965963a192b0649d07126b89983b4d
3
+ size 1775928908
qwen3-tts_onnx/talker_model_step.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2e0a94a3c6afafa67373fb841deef090bc432bdcf8c201bf86754db735da3932
3
+ size 1775928908
src/inference/qwen3_tts_inferencer_onnx.py CHANGED
@@ -349,10 +349,14 @@ class Qwen3TTSInferencerONNX:
349
 
350
  Parameters
351
  ----------
352
- talker_model_path : str
353
- Path to ``talker_model.onnx``.
354
- talker_local_model_path : str
355
- Path to ``talker_local_model.onnx``.
 
 
 
 
356
  talker_local_lm_head_model_path : str
357
  Path to ``talker_local_lm_head.onnx`` (batched lm_head for all 15 codebook groups).
358
  codec_decoder_model_path : str
@@ -380,8 +384,10 @@ class Qwen3TTSInferencerONNX:
380
 
381
  def __init__(
382
  self,
383
- talker_model_path: str,
384
- talker_local_model_path: str,
 
 
385
  talker_local_lm_head_model_path: str,
386
  codec_decoder_model_path: str,
387
  speaker_encoder_model_path: str,
@@ -421,18 +427,18 @@ class Qwen3TTSInferencerONNX:
421
  log.info("Loading ONNX sessions...")
422
 
423
  # Prefill sessions: IOBinding + CUDA graph (q_len is always fixed)
424
- log.info(f" talker prefill <- {talker_model_path}")
425
- _talker_prefill_sess = _sess(talker_model_path, graph=self._enable_cuda_graph)
426
- log.info(f" local prefill <- {talker_local_model_path}")
427
- _local_prefill_sess = _sess(talker_local_model_path, graph=self._enable_cuda_graph)
428
 
429
  # Step sessions: IOBinding + CUDA graph
430
- log.info(f" talker step <- {talker_model_path}")
431
- _talker_step_sess = _sess(talker_model_path, graph=self._enable_cuda_graph)
432
 
433
  # Unified Local Talker backbone step (shared for steps 2..15)
434
- log.info(f" local step <- {talker_local_model_path}")
435
- _local_step_sess = _sess(talker_local_model_path, graph=self._enable_cuda_graph)
436
 
437
  # Batched lm_head (all 15 codebook groups in one session)
438
  log.info(f" local lm_head <- {talker_local_lm_head_model_path}")
@@ -492,13 +498,14 @@ class Qwen3TTSInferencerONNX:
492
  self._num_hidden_layers = self._talker_config.num_hidden_layers # 28
493
  self._num_key_value_heads = self._talker_config.num_key_value_heads # 8
494
  self._text_hidden_size = self._talker_config.text_hidden_size # 2048
495
- self._text_vocab_size = self._talker_config.text_vocab_size
496
  self._vocab_size = self._talker_config.vocab_size # 3072
497
 
498
  # Local Talker dims
499
  self._local_head_dim = self._code_predictor_config.head_dim # 128
500
  self._local_hidden_size = self._code_predictor_config.hidden_size # 1024
501
  self._local_num_hidden_layers = self._code_predictor_config.num_hidden_layers # 5
 
502
  self._local_num_key_value_heads = self._code_predictor_config.num_key_value_heads # 8
503
  self._local_vocab_size = self._code_predictor_config.vocab_size # 2048
504
 
@@ -608,11 +615,49 @@ class Qwen3TTSInferencerONNX:
608
  (1, 1, self._hidden_size), np.float32, self._device, cuda_device_id
609
  )
610
 
 
 
 
 
 
 
 
 
 
 
 
611
  # Pre-allocate input OrtValues for Local Talker prefill
612
  self._local_prefill_inputs_embeds_ov = _make_device_ortvalue(
613
  (1, 2, self._hidden_size), np.float32, self._device, cuda_device_id
614
  )
615
  self._local_prefill_cache_position_ov = _make_device_ortvalue((2), np.int64, self._device, cuda_device_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616
 
617
  # Pre-allocate output OrtValues for Local Talker prefill
618
  # Backbone outputs last_hidden [1, cp_hidden_size]; logits come from lm_head
@@ -625,6 +670,33 @@ class Qwen3TTSInferencerONNX:
625
  (1, 1, self._hidden_size), np.float32, self._device, cuda_device_id
626
  )
627
  self._local_step_cache_position_ov = _make_device_ortvalue((1), np.int64, self._device, cuda_device_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
 
629
  # Pre-allocate output OrtValue for the unified Local Talker backbone step
630
  self._local_step_hidden_ov = _make_device_ortvalue(
@@ -812,6 +884,12 @@ class Qwen3TTSInferencerONNX:
812
  b = self._local_prefill_bound
813
  b.bind_input_device("inputs_embeds", self._local_prefill_inputs_embeds_ov)
814
  b.bind_input_device("cache_position", self._local_prefill_cache_position_ov)
 
 
 
 
 
 
815
  for i in range(self._local_num_hidden_layers):
816
  b.bind_input_device(f"past_key_{i}", self._kv_local_ov[2 * i])
817
  b.bind_input_device(f"past_value_{i}", self._kv_local_ov[2 * i + 1])
@@ -854,6 +932,12 @@ class Qwen3TTSInferencerONNX:
854
  b = self._local_step_bound
855
  b.bind_input_device("inputs_embeds", self._local_step_inputs_embeds_ov)
856
  b.bind_input_device("cache_position", self._local_step_cache_position_ov)
 
 
 
 
 
 
857
  for i in range(self._local_num_hidden_layers):
858
  b.bind_input_device(f"past_key_{i}", self._kv_local_ov[2 * i])
859
  b.bind_input_device(f"past_value_{i}", self._kv_local_ov[2 * i + 1])
@@ -936,6 +1020,7 @@ class Qwen3TTSInferencerONNX:
936
  return langs
937
 
938
  # ── Helper to build RoPE cos and sin tables for the talker ────────────────
 
939
  def _build_rope_tables_numpy(self):
940
  """
941
  Build NumPy RoPE cosine and sine lookup tables with interleaved multimodal layout.
@@ -1039,9 +1124,97 @@ class Qwen3TTSInferencerONNX:
1039
  # Add singleton dimension to match expected broadcast shape.
1040
  cos = np.expand_dims(cos, axis=unsqueeze_dim)
1041
  sin = np.expand_dims(sin, axis=unsqueeze_dim)
1042
- # each cos and sin with shape (bs, 1, positions, rope_dim*2)
1043
 
1044
- return cos, sin
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1045
 
1046
  # ── CPU sampling ──────────────────────────────────────────────────────────
1047
 
@@ -1200,8 +1373,8 @@ class Qwen3TTSInferencerONNX:
1200
  cache_position = np.arange(_TALKER_PREFILL_LEN, dtype=np.int64)
1201
  causal_rows = self._causal_tril[:, :, cache_position, :] # [1,1,q_len,MAX]
1202
  attn_mask = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1203
- cos_rope = self._cos_rope[:, :, cache_position]
1204
- sin_rope = self._sin_rope[:, :, cache_position]
1205
  self._run_talker_prefill(inputs_embeds, cache_position, attn_mask, cos_rope, sin_rope)
1206
  self._talker_seq_len = _TALKER_PREFILL_LEN
1207
  # Snapshot the post-prefill KV state for turn resets
@@ -1262,7 +1435,11 @@ class Qwen3TTSInferencerONNX:
1262
  self,
1263
  inputs_embeds: np.ndarray, # [1, 2, talker_hidden_size]
1264
  cache_position: np.ndarray, # [2]
 
 
 
1265
  ) -> np.ndarray:
 
1266
  """Execute the Local Talker prefill backbone via IOBinding (CUDA graph).
1267
 
1268
  The local KV-cache device OrtValues are updated in-place. Returns
@@ -1272,6 +1449,10 @@ class Qwen3TTSInferencerONNX:
1272
  b = self._local_prefill_bound
1273
  _copy_numpy_to_ortvalue(inputs_embeds, self._local_prefill_inputs_embeds_ov)
1274
  _copy_numpy_to_ortvalue(cache_position, self._local_prefill_cache_position_ov)
 
 
 
 
1275
  b.run()
1276
  return self._local_prefill_hidden_ov.numpy() # [1, cp_hidden_size]
1277
 
@@ -1281,7 +1462,11 @@ class Qwen3TTSInferencerONNX:
1281
  self,
1282
  inputs_embeds: np.ndarray, # [1, 1, talker_hidden_size]
1283
  cache_position: np.ndarray, # [1]
 
 
 
1284
  ) -> np.ndarray:
 
1285
  """Execute one unified Local Talker backbone step via IOBinding.
1286
 
1287
  Returns the last-token hidden state [1, cp_hidden_size] as NumPy.
@@ -1290,6 +1475,10 @@ class Qwen3TTSInferencerONNX:
1290
  b = self._local_step_bound
1291
  _copy_numpy_to_ortvalue(inputs_embeds, self._local_step_inputs_embeds_ov)
1292
  _copy_numpy_to_ortvalue(cache_position, self._local_step_cache_position_ov)
 
 
 
 
1293
  b.run()
1294
  return self._local_step_hidden_ov.numpy() # [1, cp_hidden_size]
1295
 
@@ -1356,8 +1545,8 @@ class Qwen3TTSInferencerONNX:
1356
  cache_position = np.array([self._talker_seq_len], dtype=np.int64)
1357
  causal_rows = self._causal_tril[:, :, cache_position, :] # [1,1,q_len,MAX]
1358
  attn_mask = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1359
- cos_rope = self._cos_rope[:, :, cache_position]
1360
- sin_rope = self._sin_rope[:, :, cache_position]
1361
  logits = self._run_talker_step(inputs_embeds, cache_position, attn_mask, cos_rope, sin_rope) # [1, vocab]
1362
  log.info(f"step-idx-{self._step_idx} logits {logits} {logits.shape} {logits.dtype}")
1363
 
@@ -1418,8 +1607,21 @@ class Qwen3TTSInferencerONNX:
1418
  ) # [1, 2, talker_hidden_size]
1419
  cache_position_prefill = np.array([0, 1], dtype=np.int64)
1420
 
 
 
 
 
 
 
1421
  # Backbone prefill → last_hidden [1, cp_hidden_size]
1422
- hidden = self._run_local_prefill(inputs_embeds_prefill, cache_position_prefill)
 
 
 
 
 
 
 
1423
  # Batched lm_head → [15, local_vocab_size]; select group 0 (codebook group 1)
1424
  all_logits = self._run_local_lm_head(hidden) # [15, local_vocab_size]
1425
  logits_g1 = all_logits[0:1, :] # [1, local_vocab_size]
@@ -1443,11 +1645,21 @@ class Qwen3TTSInferencerONNX:
1443
  head_idx = step_i - 1 # 1..14 (0-indexed into the 15-row logits tensor)
1444
  cache_position_step = np.array([step_i], dtype=np.int64)
1445
 
 
 
 
 
 
 
1446
  # Unified backbone step → last_hidden [1, cp_hidden_size]
1447
  hidden = self._run_local_step(
1448
  prev_embed.astype(np.float32),
1449
  cache_position_step,
 
 
 
1450
  )
 
1451
  # Batched lm_head → [15, local_vocab_size]; select the current group
1452
  all_logits = self._run_local_lm_head(hidden)
1453
  logits_gi = all_logits[head_idx : head_idx + 1, :] # [1, local_vocab_size]
@@ -1524,8 +1736,8 @@ class Qwen3TTSInferencerONNX:
1524
  causal_rows = self._causal_tril[:, :, cache_pos, :] # [1,1,q_len,MAX]
1525
  attn_mask = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1526
  for _ in range(n_iter):
1527
- cos_rope = self._cos_rope[:, :, cache_pos]
1528
- sin_rope = self._sin_rope[:, :, cache_pos]
1529
  t0 = time.perf_counter()
1530
  self._run_talker_prefill(dummy_embeds, cache_pos, attn_mask, cos_rope, sin_rope)
1531
  times["talker_prefill"].append((time.perf_counter() - t0) * 1000)
@@ -1540,8 +1752,8 @@ class Qwen3TTSInferencerONNX:
1540
  cache_pos_step = np.array([_TALKER_PREFILL_LEN + n], dtype=np.int64)
1541
  causal_rows = self._causal_tril[:, :, cache_pos_step, :] # [1,1,q_len,MAX]
1542
  attn_mask = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1543
- cos_rope = self._cos_rope[:, :, cache_pos_step]
1544
- sin_rope = self._sin_rope[:, :, cache_pos_step]
1545
  t0 = time.perf_counter()
1546
  self._run_talker_step(dummy_step_embeds, cache_pos_step, attn_mask, cos_rope, sin_rope)
1547
  times["talker_step"].append((time.perf_counter() - t0) * 1000)
@@ -1553,9 +1765,15 @@ class Qwen3TTSInferencerONNX:
1553
  log.info("Warming-up local talker prefill...")
1554
  dummy_lp_embeds = np.random.randn(1, _LOCAL_PREFILL_LEN, self._local_hidden_size).astype(np.float32)
1555
  cache_pos_lp = np.array([0, 1], dtype=np.int64)
 
 
 
 
 
1556
  for _ in range(n_iter):
1557
  t0 = time.perf_counter()
1558
- self._run_local_prefill(dummy_lp_embeds, cache_pos_lp)
 
1559
  times["local_prefill"].append((time.perf_counter() - t0) * 1000)
1560
  exec_time_tot = sum(times["local_prefill"]) / 1000
1561
  avg_exec_time = exec_time_tot / len(times["local_prefill"]) * 1000
@@ -1568,9 +1786,15 @@ class Qwen3TTSInferencerONNX:
1568
  log.info("Warming-up local talker step...")
1569
  dummy_ls_embeds = np.random.randn(1, 1, self._hidden_size).astype(np.float32)
1570
  cache_pos_ls = np.array([2], dtype=np.int64)
 
 
 
 
 
1571
  for _ in range(n_iter):
1572
  t0 = time.perf_counter()
1573
- self._run_local_step(dummy_ls_embeds, cache_pos_ls)
 
1574
  times["local_step"].append((time.perf_counter() - t0) * 1000)
1575
  exec_time_tot = sum(times["local_step"]) / 1000
1576
  avg_exec_time = exec_time_tot / len(times["local_step"]) * 1000
 
349
 
350
  Parameters
351
  ----------
352
+ talker_model_prefill_path : str
353
+ Path to ``talker_model_prefill.onnx``.
354
+ talker_model_step_path : str
355
+ Path to ``talker_model_step.onnx``.
356
+ talker_local_model_prefill_path : str
357
+ Path to ``talker_local_model_prefill.onnx``.
358
+ talker_local_model_step_path : str
359
+ Path to ``talker_local_model_step.onnx``.
360
  talker_local_lm_head_model_path : str
361
  Path to ``talker_local_lm_head.onnx`` (batched lm_head for all 15 codebook groups).
362
  codec_decoder_model_path : str
 
384
 
385
  def __init__(
386
  self,
387
+ talker_model_prefill_path: str,
388
+ talker_model_step_path: str,
389
+ talker_local_model_prefill_path: str,
390
+ talker_local_model_step_path: str,
391
  talker_local_lm_head_model_path: str,
392
  codec_decoder_model_path: str,
393
  speaker_encoder_model_path: str,
 
427
  log.info("Loading ONNX sessions...")
428
 
429
  # Prefill sessions: IOBinding + CUDA graph (q_len is always fixed)
430
+ log.info(f" talker prefill <- {talker_model_prefill_path}")
431
+ _talker_prefill_sess = _sess(talker_model_prefill_path, graph=self._enable_cuda_graph)
432
+ log.info(f" local prefill <- {talker_local_model_prefill_path}")
433
+ _local_prefill_sess = _sess(talker_local_model_prefill_path, graph=self._enable_cuda_graph)
434
 
435
  # Step sessions: IOBinding + CUDA graph
436
+ log.info(f" talker step <- {talker_model_step_path}")
437
+ _talker_step_sess = _sess(talker_model_step_path, graph=self._enable_cuda_graph)
438
 
439
  # Unified Local Talker backbone step (shared for steps 2..15)
440
+ log.info(f" local step <- {talker_local_model_step_path}")
441
+ _local_step_sess = _sess(talker_local_model_step_path, graph=self._enable_cuda_graph)
442
 
443
  # Batched lm_head (all 15 codebook groups in one session)
444
  log.info(f" local lm_head <- {talker_local_lm_head_model_path}")
 
498
  self._num_hidden_layers = self._talker_config.num_hidden_layers # 28
499
  self._num_key_value_heads = self._talker_config.num_key_value_heads # 8
500
  self._text_hidden_size = self._talker_config.text_hidden_size # 2048
501
+ self._text_vocab_size = self._talker_config.text_vocab_size # 151936
502
  self._vocab_size = self._talker_config.vocab_size # 3072
503
 
504
  # Local Talker dims
505
  self._local_head_dim = self._code_predictor_config.head_dim # 128
506
  self._local_hidden_size = self._code_predictor_config.hidden_size # 1024
507
  self._local_num_hidden_layers = self._code_predictor_config.num_hidden_layers # 5
508
+ self._local_num_attention_heads = self._code_predictor_config.num_attention_heads # 16
509
  self._local_num_key_value_heads = self._code_predictor_config.num_key_value_heads # 8
510
  self._local_vocab_size = self._code_predictor_config.vocab_size # 2048
511
 
 
615
  (1, 1, self._hidden_size), np.float32, self._device, cuda_device_id
616
  )
617
 
618
+ # Define causal_tril for attention_mask Local prefill and step
619
+ _causal_tril_local = np.tril(np.ones((self._num_code_groups, self._num_code_groups), dtype=bool))
620
+ self._causal_tril_local = _causal_tril_local.reshape(1, 1, self._num_code_groups, self._num_code_groups)
621
+
622
+ # Define cache_write mask for each position in the Local prefill and steps, used to update KV-cache
623
+ # self._local_cache_write_mask_list = self._make_local_cache_write_mask(batch_size=1) # 15 cache write masks
624
+ # For each, [B, H, 16, D], with varying 1/0 depending on the position
625
+
626
+ # Define cos and sin table for Local RoPE embedding
627
+ self._cos_rope_local, self._sin_rope_local = self._build_local_rope_tables_numpy()
628
+
629
  # Pre-allocate input OrtValues for Local Talker prefill
630
  self._local_prefill_inputs_embeds_ov = _make_device_ortvalue(
631
  (1, 2, self._hidden_size), np.float32, self._device, cuda_device_id
632
  )
633
  self._local_prefill_cache_position_ov = _make_device_ortvalue((2), np.int64, self._device, cuda_device_id)
634
+ self._local_prefill_attention_mask_ov = _make_device_ortvalue(
635
+ (1, 1, 2, self._num_code_groups), np.float32, self._device, cuda_device_id
636
+ )
637
+ self._local_prefill_cos_rope_ov = _make_device_ortvalue(
638
+ (1, 2, self._head_dim), np.float32, self._device, cuda_device_id
639
+ )
640
+ self._local_prefill_sin_rope_ov = _make_device_ortvalue(
641
+ (1, 2, self._head_dim), np.float32, self._device, cuda_device_id
642
+ )
643
+ # self._local_prefill_cache_write_mask_ov = _make_device_ortvalue(
644
+ # (1, self._local_num_key_value_heads, self._num_code_groups, self._local_head_dim),
645
+ # np.bool,
646
+ # self._device,
647
+ # cuda_device_id,
648
+ # )
649
+ # self._local_prefill_key_gqa_expanded_ov = _make_device_ortvalue(
650
+ # (1, self._local_num_attention_heads, self._num_code_groups, self._local_head_dim),
651
+ # np.float32,
652
+ # self._device,
653
+ # cuda_device_id,
654
+ # )
655
+ # self._local_prefill_value_gqa_expanded_ov = _make_device_ortvalue(
656
+ # (1, self._local_num_attention_heads, self._num_code_groups, self._local_head_dim),
657
+ # np.float32,
658
+ # self._device,
659
+ # cuda_device_id,
660
+ # )
661
 
662
  # Pre-allocate output OrtValues for Local Talker prefill
663
  # Backbone outputs last_hidden [1, cp_hidden_size]; logits come from lm_head
 
670
  (1, 1, self._hidden_size), np.float32, self._device, cuda_device_id
671
  )
672
  self._local_step_cache_position_ov = _make_device_ortvalue((1), np.int64, self._device, cuda_device_id)
673
+ self._local_step_attention_mask_ov = _make_device_ortvalue(
674
+ (1, 1, 1, self._num_code_groups), np.float32, self._device, cuda_device_id
675
+ )
676
+ self._local_step_cos_rope_ov = _make_device_ortvalue(
677
+ (1, 1, self._head_dim), np.float32, self._device, cuda_device_id
678
+ )
679
+ self._local_step_sin_rope_ov = _make_device_ortvalue(
680
+ (1, 1, self._head_dim), np.float32, self._device, cuda_device_id
681
+ )
682
+ # self._local_step_cache_write_mask_ov = _make_device_ortvalue(
683
+ # (1, self._local_num_key_value_heads, self._num_code_groups, self._local_head_dim),
684
+ # np.bool,
685
+ # self._device,
686
+ # cuda_device_id,
687
+ # )
688
+ # self._local_step_key_gqa_expanded_ov = _make_device_ortvalue(
689
+ # (1, self._local_num_attention_heads, self._num_code_groups, self._local_head_dim),
690
+ # np.float32,
691
+ # self._device,
692
+ # cuda_device_id,
693
+ # )
694
+ # self._local_step_value_gqa_expanded_ov = _make_device_ortvalue(
695
+ # (1, self._local_num_attention_heads, self._num_code_groups, self._local_head_dim),
696
+ # np.float32,
697
+ # self._device,
698
+ # cuda_device_id,
699
+ # )
700
 
701
  # Pre-allocate output OrtValue for the unified Local Talker backbone step
702
  self._local_step_hidden_ov = _make_device_ortvalue(
 
884
  b = self._local_prefill_bound
885
  b.bind_input_device("inputs_embeds", self._local_prefill_inputs_embeds_ov)
886
  b.bind_input_device("cache_position", self._local_prefill_cache_position_ov)
887
+ b.bind_input_device("attention_mask", self._local_prefill_attention_mask_ov)
888
+ b.bind_input_device("cos_rope", self._local_prefill_cos_rope_ov)
889
+ b.bind_input_device("sin_rope", self._local_prefill_sin_rope_ov)
890
+ # b.bind_input_device("cache_write_mask", self._local_prefill_cache_write_mask_ov)
891
+ # b.bind_input_device("key_gqa_expanded", self._local_prefill_key_gqa_expanded_ov)
892
+ # b.bind_input_device("value_gqa_expanded", self._local_prefill_value_gqa_expanded_ov)
893
  for i in range(self._local_num_hidden_layers):
894
  b.bind_input_device(f"past_key_{i}", self._kv_local_ov[2 * i])
895
  b.bind_input_device(f"past_value_{i}", self._kv_local_ov[2 * i + 1])
 
932
  b = self._local_step_bound
933
  b.bind_input_device("inputs_embeds", self._local_step_inputs_embeds_ov)
934
  b.bind_input_device("cache_position", self._local_step_cache_position_ov)
935
+ b.bind_input_device("attention_mask", self._local_step_attention_mask_ov)
936
+ b.bind_input_device("cos_rope", self._local_step_cos_rope_ov)
937
+ b.bind_input_device("sin_rope", self._local_step_sin_rope_ov)
938
+ # b.bind_input_device("cache_write_mask", self._local_step_cache_write_mask_ov)
939
+ # b.bind_input_device("key_gqa_expanded", self._local_step_key_gqa_expanded_ov)
940
+ # b.bind_input_device("value_gqa_expanded", self._local_step_value_gqa_expanded_ov)
941
  for i in range(self._local_num_hidden_layers):
942
  b.bind_input_device(f"past_key_{i}", self._kv_local_ov[2 * i])
943
  b.bind_input_device(f"past_value_{i}", self._kv_local_ov[2 * i + 1])
 
1020
  return langs
1021
 
1022
  # ── Helper to build RoPE cos and sin tables for the talker ────────────────
1023
+
1024
  def _build_rope_tables_numpy(self):
1025
  """
1026
  Build NumPy RoPE cosine and sine lookup tables with interleaved multimodal layout.
 
1124
  # Add singleton dimension to match expected broadcast shape.
1125
  cos = np.expand_dims(cos, axis=unsqueeze_dim)
1126
  sin = np.expand_dims(sin, axis=unsqueeze_dim)
1127
+ # each cos and sin with shape (bs, 1, positions, rope_dim)
1128
 
1129
+ return cos.astype(np.float32), sin.astype(np.float32)
1130
+
1131
+ def _build_local_rope_tables_numpy(self):
1132
+ """
1133
+ Build NumPy RoPE cosine and sine lookup tables for the local talker.
1134
+
1135
+ This function reproduces the logic of the provided Torch code in NumPy:
1136
+ 1. Compute inverse RoPE frequencies from `rope_theta`.
1137
+ 2. Build position-angle frequencies for `max_steps`.
1138
+ 3. Form full rotary embeddings by duplicating the frequency half.
1139
+ 4. Return `cos` and `sin` tables with an added singleton axis.
1140
+
1141
+ Parameters obtained from self.
1142
+ ----------
1143
+ _hidden_size : int
1144
+ Total hidden size of the model.
1145
+ _num_attention_heads : int
1146
+ Number of attention heads.
1147
+ _max_steps : int
1148
+ Maximum number of positions to precompute.
1149
+ base (rope_theta) : float
1150
+ RoPE base frequency parameter.
1151
+ mrope_section : sequence of int
1152
+ Section boundaries used to interleave multimodal rotary dimensions.
1153
+
1154
+ Returns
1155
+ -------
1156
+ cos : np.ndarray
1157
+ Cosine table with shape matching the Torch version, including the
1158
+ extra singleton dimension inserted at axis 1.
1159
+ sin : np.ndarray
1160
+ Sine table with shape matching the Torch version, including the
1161
+ extra singleton dimension inserted at axis 1.
1162
+ """
1163
+ rope_dim = self._code_predictor_config.head_dim
1164
+ base = self._code_predictor_config.rope_theta
1165
+
1166
+ # Build inverse frequencies for RoPE.
1167
+ inv_idx = np.arange(0, rope_dim, 2, dtype=np.float32)
1168
+
1169
+ # Compute position-dependent phase angles.
1170
+ inv_freq = (1.0 / (base ** (inv_idx / rope_dim))).astype(np.float32)
1171
+ # Match the original broadcasted matmul behavior.
1172
+ inv_freq_expanded = np.broadcast_to(
1173
+ inv_freq[None, :, None], (1, len(inv_idx), 1)
1174
+ ) # shape (bs, 1, rope_dim, 1)
1175
+ position_ids = np.arange(self._num_code_groups, dtype=np.float32)
1176
+ position_ids_expanded = np.broadcast_to(
1177
+ position_ids[None, None, :], (1, 1, len(position_ids))
1178
+ ) # shape (bs, 1, positions)
1179
+ freqs = np.matmul(inv_freq_expanded, position_ids_expanded).transpose(
1180
+ 0, 2, 1
1181
+ ) # shape (bs, positions, rope_dim)
1182
+
1183
+ # Duplicate the half-dimension to form full rotary embeddings.
1184
+ emb = np.concatenate([freqs, freqs], axis=-1)
1185
+ cos = np.cos(emb)
1186
+ sin = np.sin(emb)
1187
+ # each cos and sin with shape (bs, positions, rope_dim)
1188
+
1189
+ return cos.astype(np.float32), sin.astype(np.float32)
1190
+
1191
+ # ── Other helper functions ────────────────────────────────────────────────
1192
+
1193
+ # def _make_local_cache_write_mask(self, batch_size=1):
1194
+ # t = np.arange(self._num_code_groups, dtype=np.int64).reshape(batch_size, 1, 1, self._num_code_groups)
1195
+
1196
+ # cache_position = np.asarray([0, 1], dtype=np.int64).reshape(batch_size, 1, -1, 1)
1197
+ # mask = (t == cache_position).any(axis=2) # shape [1, 1, max_seq_len]
1198
+ # mask = mask[..., None] # shape [1, 1, max_seq_len, 1]
1199
+ # mask_list = [
1200
+ # np.broadcast_to(
1201
+ # mask, (batch_size, self._local_num_key_value_heads, self._num_code_groups, self._local_head_dim)
1202
+ # )
1203
+ # ]
1204
+
1205
+ # # cache write mask for each codebook from 1 to 15 (0 is generated by talker)
1206
+ # for pos in range(2, self._num_code_groups):
1207
+ # cache_position = np.asarray([pos], dtype=np.int64).reshape(1, 1, -1, 1)
1208
+ # mask = (t == cache_position).any(axis=2) # shape [1, 1, max_seq_len]
1209
+ # mask = mask[..., None] # shape [1, 1, max_seq_len, 1]
1210
+ # mask_list.append(
1211
+ # np.broadcast_to(
1212
+ # mask, (batch_size, self._local_num_key_value_heads, self._num_code_groups, self._local_head_dim)
1213
+ # )
1214
+ # )
1215
+ # # this is used for torch.where inside attention function to update KV-cache buffer in-place
1216
+
1217
+ # return mask_list
1218
 
1219
  # ── CPU sampling ──────────────────────────────────────────────────────────
1220
 
 
1373
  cache_position = np.arange(_TALKER_PREFILL_LEN, dtype=np.int64)
1374
  causal_rows = self._causal_tril[:, :, cache_position, :] # [1,1,q_len,MAX]
1375
  attn_mask = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1376
+ cos_rope = self._cos_rope[:, :, cache_position].copy()
1377
+ sin_rope = self._sin_rope[:, :, cache_position].copy()
1378
  self._run_talker_prefill(inputs_embeds, cache_position, attn_mask, cos_rope, sin_rope)
1379
  self._talker_seq_len = _TALKER_PREFILL_LEN
1380
  # Snapshot the post-prefill KV state for turn resets
 
1435
  self,
1436
  inputs_embeds: np.ndarray, # [1, 2, talker_hidden_size]
1437
  cache_position: np.ndarray, # [2]
1438
+ attention_mask: np.ndarray, # [1, 1, 2, max_seq_len]
1439
+ cos_rope: np.ndarray, # [1, 1, 2, 128]
1440
+ sin_rope: np.ndarray, # [1, 1, 2, 128]
1441
  ) -> np.ndarray:
1442
+ # cache_write_mask: np.ndarray, # [1, 8, 16, D]
1443
  """Execute the Local Talker prefill backbone via IOBinding (CUDA graph).
1444
 
1445
  The local KV-cache device OrtValues are updated in-place. Returns
 
1449
  b = self._local_prefill_bound
1450
  _copy_numpy_to_ortvalue(inputs_embeds, self._local_prefill_inputs_embeds_ov)
1451
  _copy_numpy_to_ortvalue(cache_position, self._local_prefill_cache_position_ov)
1452
+ _copy_numpy_to_ortvalue(attention_mask, self._local_prefill_attention_mask_ov)
1453
+ _copy_numpy_to_ortvalue(cos_rope, self._local_prefill_cos_rope_ov)
1454
+ _copy_numpy_to_ortvalue(sin_rope, self._local_prefill_sin_rope_ov)
1455
+ # _copy_numpy_to_ortvalue(cache_write_mask, self._local_prefill_cache_write_mask_ov)
1456
  b.run()
1457
  return self._local_prefill_hidden_ov.numpy() # [1, cp_hidden_size]
1458
 
 
1462
  self,
1463
  inputs_embeds: np.ndarray, # [1, 1, talker_hidden_size]
1464
  cache_position: np.ndarray, # [1]
1465
+ attention_mask: np.ndarray, # [1, 1, 1, max_seq_len]
1466
+ cos_rope: np.ndarray, # [1, 1, 1, 128]
1467
+ sin_rope: np.ndarray, # [1, 1, 1, 128]
1468
  ) -> np.ndarray:
1469
+ # cache_write_mask: np.ndarray, # [1, 8, 16, 128]
1470
  """Execute one unified Local Talker backbone step via IOBinding.
1471
 
1472
  Returns the last-token hidden state [1, cp_hidden_size] as NumPy.
 
1475
  b = self._local_step_bound
1476
  _copy_numpy_to_ortvalue(inputs_embeds, self._local_step_inputs_embeds_ov)
1477
  _copy_numpy_to_ortvalue(cache_position, self._local_step_cache_position_ov)
1478
+ _copy_numpy_to_ortvalue(attention_mask, self._local_step_attention_mask_ov)
1479
+ _copy_numpy_to_ortvalue(cos_rope, self._local_step_cos_rope_ov)
1480
+ _copy_numpy_to_ortvalue(sin_rope, self._local_step_sin_rope_ov)
1481
+ # _copy_numpy_to_ortvalue(cache_write_mask, self._local_step_cache_write_mask_ov)
1482
  b.run()
1483
  return self._local_step_hidden_ov.numpy() # [1, cp_hidden_size]
1484
 
 
1545
  cache_position = np.array([self._talker_seq_len], dtype=np.int64)
1546
  causal_rows = self._causal_tril[:, :, cache_position, :] # [1,1,q_len,MAX]
1547
  attn_mask = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1548
+ cos_rope = self._cos_rope[:, :, cache_position].copy()
1549
+ sin_rope = self._sin_rope[:, :, cache_position].copy()
1550
  logits = self._run_talker_step(inputs_embeds, cache_position, attn_mask, cos_rope, sin_rope) # [1, vocab]
1551
  log.info(f"step-idx-{self._step_idx} logits {logits} {logits.shape} {logits.dtype}")
1552
 
 
1607
  ) # [1, 2, talker_hidden_size]
1608
  cache_position_prefill = np.array([0, 1], dtype=np.int64)
1609
 
1610
+ causal_rows = self._causal_tril_local[:, :, cache_position_prefill, :] # [1,1,q_len,MAX]
1611
+ attn_mask_prefill = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1612
+ cos_rope_prefill = self._cos_rope_local[:, cache_position_prefill].copy()
1613
+ sin_rope_prefill = self._sin_rope_local[:, cache_position_prefill].copy()
1614
+ # cache_write_mask_prefill = self._local_cache_write_mask_list[0]
1615
+
1616
  # Backbone prefill → last_hidden [1, cp_hidden_size]
1617
+ hidden = self._run_local_prefill(
1618
+ inputs_embeds_prefill,
1619
+ cache_position_prefill,
1620
+ attn_mask_prefill,
1621
+ cos_rope_prefill,
1622
+ sin_rope_prefill,
1623
+ )
1624
+ # cache_write_mask_prefill,
1625
  # Batched lm_head → [15, local_vocab_size]; select group 0 (codebook group 1)
1626
  all_logits = self._run_local_lm_head(hidden) # [15, local_vocab_size]
1627
  logits_g1 = all_logits[0:1, :] # [1, local_vocab_size]
 
1645
  head_idx = step_i - 1 # 1..14 (0-indexed into the 15-row logits tensor)
1646
  cache_position_step = np.array([step_i], dtype=np.int64)
1647
 
1648
+ causal_rows = self._causal_tril_local[:, :, cache_position_step, :] # [1,1,q_len,MAX]
1649
+ attn_mask_step = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1650
+ cos_rope_step = self._cos_rope_local[:, cache_position_step].copy()
1651
+ sin_rope_step = self._sin_rope_local[:, cache_position_step].copy()
1652
+ # cache_write_mask_step = self._local_cache_write_mask_list[step_i - 1]
1653
+
1654
  # Unified backbone step → last_hidden [1, cp_hidden_size]
1655
  hidden = self._run_local_step(
1656
  prev_embed.astype(np.float32),
1657
  cache_position_step,
1658
+ attn_mask_step,
1659
+ cos_rope_step,
1660
+ sin_rope_step,
1661
  )
1662
+ # cache_write_mask_step,
1663
  # Batched lm_head → [15, local_vocab_size]; select the current group
1664
  all_logits = self._run_local_lm_head(hidden)
1665
  logits_gi = all_logits[head_idx : head_idx + 1, :] # [1, local_vocab_size]
 
1736
  causal_rows = self._causal_tril[:, :, cache_pos, :] # [1,1,q_len,MAX]
1737
  attn_mask = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1738
  for _ in range(n_iter):
1739
+ cos_rope = self._cos_rope[:, :, cache_pos].copy()
1740
+ sin_rope = self._sin_rope[:, :, cache_pos].copy()
1741
  t0 = time.perf_counter()
1742
  self._run_talker_prefill(dummy_embeds, cache_pos, attn_mask, cos_rope, sin_rope)
1743
  times["talker_prefill"].append((time.perf_counter() - t0) * 1000)
 
1752
  cache_pos_step = np.array([_TALKER_PREFILL_LEN + n], dtype=np.int64)
1753
  causal_rows = self._causal_tril[:, :, cache_pos_step, :] # [1,1,q_len,MAX]
1754
  attn_mask = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1755
+ cos_rope = self._cos_rope[:, :, cache_pos_step].copy()
1756
+ sin_rope = self._sin_rope[:, :, cache_pos_step].copy()
1757
  t0 = time.perf_counter()
1758
  self._run_talker_step(dummy_step_embeds, cache_pos_step, attn_mask, cos_rope, sin_rope)
1759
  times["talker_step"].append((time.perf_counter() - t0) * 1000)
 
1765
  log.info("Warming-up local talker prefill...")
1766
  dummy_lp_embeds = np.random.randn(1, _LOCAL_PREFILL_LEN, self._local_hidden_size).astype(np.float32)
1767
  cache_pos_lp = np.array([0, 1], dtype=np.int64)
1768
+ causal_rows = self._causal_tril_local[:, :, cache_pos_lp, :] # [1,1,q_len,MAX]
1769
+ attn_mask_lp = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1770
+ cos_rope_lp = self._cos_rope_local[:, cache_pos_lp].copy()
1771
+ sin_rope_lp = self._sin_rope_local[:, cache_pos_lp].copy()
1772
+ # cache_write_mask_lp = self._local_cache_write_mask_list[0]
1773
  for _ in range(n_iter):
1774
  t0 = time.perf_counter()
1775
+ self._run_local_prefill(dummy_lp_embeds, cache_pos_lp, attn_mask_lp, cos_rope_lp, sin_rope_lp)
1776
+ # cache_write_mask_lp
1777
  times["local_prefill"].append((time.perf_counter() - t0) * 1000)
1778
  exec_time_tot = sum(times["local_prefill"]) / 1000
1779
  avg_exec_time = exec_time_tot / len(times["local_prefill"]) * 1000
 
1786
  log.info("Warming-up local talker step...")
1787
  dummy_ls_embeds = np.random.randn(1, 1, self._hidden_size).astype(np.float32)
1788
  cache_pos_ls = np.array([2], dtype=np.int64)
1789
+ causal_rows = self._causal_tril_local[:, :, cache_pos_ls, :] # [1,1,q_len,MAX]
1790
+ attn_mask_ls = np.where(causal_rows, 0.0, -np.inf).astype(np.float32) # [1, 1, q_len, MAX_SEQ_LEN]
1791
+ cos_rope_ls = self._cos_rope_local[:, cache_pos_ls].copy()
1792
+ sin_rope_ls = self._sin_rope_local[:, cache_pos_ls].copy()
1793
+ # cache_write_mask_ls = self._local_cache_write_mask_list[1]
1794
  for _ in range(n_iter):
1795
  t0 = time.perf_counter()
1796
+ self._run_local_step(dummy_ls_embeds, cache_pos_ls, attn_mask_ls, cos_rope_ls, sin_rope_ls)
1797
+ # cache_write_mask_ls
1798
  times["local_step"].append((time.perf_counter() - t0) * 1000)
1799
  exec_time_tot = sum(times["local_step"]) / 1000
1800
  avg_exec_time = exec_time_tot / len(times["local_step"]) * 1000
test_qwen3-tts-streaming_onnx.py CHANGED
@@ -24,12 +24,7 @@ This script demonstrates the full Qwen3-TTS-Streaming-ONNX pipeline by:
24
  5. Writing the concatenated audio to a WAV file.
25
  Usage:
26
  python test_qwen3-tts-streaming_onnx.py \
27
- --talker_model_path qwen3-tts_onnx/talker_model.onnx \
28
- --talker_local_model_path qwen3-tts_onnx/talker_local_model.onnx \
29
- --codec_decoder_model_path qwen3-tts_onnx/codec_decoder_model.onnx \
30
- --speaker_encoder_model_path qwen3-tts_onnx/speaker_encoder_model.onnx \
31
- --talker_codec_embed_model_path qwen3-tts_onnx/talker_codec_embed_model.onnx \
32
- --text_embed_proj_model_path qwen3-tts_onnx/text_embed_proj_model.onnx \
33
  --model_config_path configs/config.json \
34
  --codec_config_path configs/tokenizer_config.json \
35
  --preprocessor_config_dir configs/ \
@@ -281,8 +276,10 @@ def main() -> None:
281
  suffix = "_int8.onnx" if args.use_int8 else ".onnx"
282
 
283
  # Build model paths
284
- talker_path = _onnx_path(args.onnx_dir, f"talker_model{suffix}")
285
- talker_local_path = _onnx_path(args.onnx_dir, f"talker_local_model{suffix}")
 
 
286
  # Unified backbone step (shared for steps 2..15) and batched lm_head
287
  talker_local_lm_head_path = _onnx_path(args.onnx_dir, f"talker_local_lm_head{suffix}")
288
 
@@ -294,8 +291,10 @@ def main() -> None:
294
 
295
  # Validate required paths
296
  required = [
297
- talker_path,
298
- talker_local_path,
 
 
299
  talker_local_lm_head_path,
300
  codec_decoder_path,
301
  speaker_encoder_path,
@@ -312,8 +311,10 @@ def main() -> None:
312
 
313
  log.info("Building inferencer...")
314
  inferencer = Qwen3TTSInferencerONNX(
315
- talker_model_path=talker_path,
316
- talker_local_model_path=talker_local_path,
 
 
317
  talker_local_lm_head_model_path=talker_local_lm_head_path,
318
  codec_decoder_model_path=codec_decoder_path,
319
  speaker_encoder_model_path=speaker_encoder_path,
 
24
  5. Writing the concatenated audio to a WAV file.
25
  Usage:
26
  python test_qwen3-tts-streaming_onnx.py \
27
+ --onnx_dir qwen3-tts_onnx/ \
 
 
 
 
 
28
  --model_config_path configs/config.json \
29
  --codec_config_path configs/tokenizer_config.json \
30
  --preprocessor_config_dir configs/ \
 
276
  suffix = "_int8.onnx" if args.use_int8 else ".onnx"
277
 
278
  # Build model paths
279
+ talker_prefill_path = _onnx_path(args.onnx_dir, f"talker_model_prefill{suffix}")
280
+ talker_step_path = _onnx_path(args.onnx_dir, f"talker_model_step{suffix}")
281
+ talker_local_prefill_path = _onnx_path(args.onnx_dir, f"talker_local_model_prefill{suffix}")
282
+ talker_local_step_path = _onnx_path(args.onnx_dir, f"talker_local_model_step{suffix}")
283
  # Unified backbone step (shared for steps 2..15) and batched lm_head
284
  talker_local_lm_head_path = _onnx_path(args.onnx_dir, f"talker_local_lm_head{suffix}")
285
 
 
291
 
292
  # Validate required paths
293
  required = [
294
+ talker_prefill_path,
295
+ talker_step_path,
296
+ talker_local_prefill_path,
297
+ talker_local_step_path,
298
  talker_local_lm_head_path,
299
  codec_decoder_path,
300
  speaker_encoder_path,
 
311
 
312
  log.info("Building inferencer...")
313
  inferencer = Qwen3TTSInferencerONNX(
314
+ talker_model_prefill_path=talker_prefill_path,
315
+ talker_model_step_path=talker_step_path,
316
+ talker_local_model_prefill_path=talker_local_prefill_path,
317
+ talker_local_model_step_path=talker_local_step_path,
318
  talker_local_lm_head_model_path=talker_local_lm_head_path,
319
  codec_decoder_model_path=codec_decoder_path,
320
  speaker_encoder_model_path=speaker_encoder_path,