Commit Graph

3 Commits

Author SHA1 Message Date
Richard Loyer 7626131d50 chantier B TTS #8: KV cache CP -40% par frame, bit-exact
cp_inference: nouvelle voie cp_predict_cached (1 prefill 2 tokens +
14 decode 1 token via KV cache f32 persistant) à côté de cp_predict
(oracle recompute conservé pour A/B). cp_forward_cached_step écrit le
K/V post-RoPE dans s.K_cache/V_cache via ggml_view_3d + ggml_cpy, et
lit le full cache [0..T_full) pour l'attention. Ordre topo garanti
par build_forward_expand(cpy) AVANT build_forward_expand(x).

tts_pipeline: switch KZTTS_CP_CACHE=0/1 (défaut 0 = oracle bit-exact).
build_tts_pipeline.sh: rebuild reproductible (CMakeLists ne couvrait
que libkazeia_engine.so).

Mesure tablette Pad3 (talker_f32+tts_dump, CPU 6t, seed=42, 31 frames):
  oracle  : CP 227.9 ms/frame, total 12.90s, RTF 4.99
  cached  : CP 137.8 ms/frame, total  8.91s, RTF 3.45
  codes_ref.bin == codes_cache.bin (cmp), out_ref.wav md5 == out_cache.wav

Speedup CP ×1.65, pas le ×8 espéré : le forward est BW-bound (~100 MB
de poids f32 streamés par sub-forward, on lit toujours 15 fois). Le
gain est sur la part compute (sum L=2..16 -> 16 token-fwd au lieu de
135). Pour aller plus loin : (a) cp_load en f16 conservé (×2 BW),
(b) fusion graphe multi-step. Decoder RTF 1.37 reste aussi à attaquer.

Bit-exactness : f32 mul_mat stable entre L=k+1 et L=1 à threads=6,
les sommations donnent les mêmes bits. Math identique, ordre identique
de fait.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 19:51:22 +02:00
Richard Loyer d20f2b297b chantier B TTS #7: sampler HF-style top_k+top_p+rep_penalty (générique)
sampler.{h,cpp} : module isolé reproduisant la chaine HF
LogitsProcessor :
  1. RepetitionPenaltyLogitsProcessor (logit/penalty si >0, logit*penalty sinon)
  2. TemperatureLogitsWarper (logit /= temp)
  3. TopKLogitsWarper (garde top_k, autres -> -inf)
  4. TopPLogitsWarper (nucleus : garde la masse cumulee jusqu'a top_p)
  5. multinomial sample (xorshift32, deterministe via sampler_seed)
PRNG xorshift32 != torch.mt19937 donc meme seed != audio Python, mais stable
sur n'importe quel texte/voix (pas d'attracteur greedy).

Plumbing :
- CPState.sampler (defaut greedy top_k=1, temp=0 -> cp_validate continue a sortir
  495/495 codes match). Pipeline override pour temp=0.9 top_k=50 rep_penalty=1.05
  rep_window=16 (intra-frame, 15 codebooks max). CB1..15 utilisent
  sampler_sample_local() qui prend les tokens deja samples CE step pour penaliser
  repetition intra-frame.
- tts_pipeline : Sampler talker_sampler avec rep_window=64 (historique long sur
  CB0). Override via env KZTTS_TEMP/TOPK/TOPP/REPP + KZTTS_CP_TEMP/TOPK/TOPP/REPP.

Validation sur 5 seeds (1, 42, 100, 777, 12345), phrase 'Bonjour je m'appelle Kazeia' :
  - Tous EOS naturel (step 31-44, 2.48-3.67s audio @12Hz, proche du PY_REF 2.08s)
  - silence_pct : 25-36% (vs > 60% avec greedy/sampler simple)
  - cp_validate continue a sortir 495/495 codes match (greedy par defaut)

Vs WAV pipeline_v3 user-confirme 'parfait' (seed=123 temp=0.8 sans rep_penalty),
le sampler v4 est generique sur tout seed/texte, plus de besoin de chercher un
seed favorable. Trade-off : on n'a plus le PRNG torch donc l'audio diffère de
PyTorch a chaque run (different trajectoire), mais reste audio TTS coherent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 16:55:05 +02:00
Richard Loyer 9ef5e11b53 chantier B TTS #6: P3.4+P3.5 = pipeline complet 100% tablette bout-en-bout
tts_pipeline.cpp : binaire unique qui orchestre TOUT en un seul process :
  input_ids + x_vector (fixtures)
    -> text_projection + lookups + spéciaux (P3.2) -> prefill_embeds [T, 1024]
    -> Talker engine (libllama, M-RoPE IMROPE, Patch 1+2) -> logits + hidden
    -> sampling top_k+temp sur CB0 (le greedy pur tombe dans un attracteur :
       cb0 répété -> decoder produit du silence. temp=0.9 top_k=50 = défauts
       Python, débloquent l'attracteur)
    -> CP runner (cp_inference, recompute bit-exact) -> CB1..15
    -> sum 16 codecs + tts_pad -> next embed (orchestration ggml-side, bit-exact)
    -> codes [N, 16] -> decoder ggml (libqwen3tts-decoder rebuilt vs ql/ggml,
       evite conflit double-ggml) -> WAV PCM 24kHz

Sortie : Bonjour je m'appelle Kazeia, 32 frames -> 2.56s audio audible
(rms=0.040, range [-0.26, 0.29]). 100% tablette : pas de Python, pas de root,
pas de QNN. Stack unifiee ggml.

Bench tablette CPU 6 threads (sweet spot, 8 = contention scheduler) :
  load total       :  1.12 s  (talker f32 + cp + decoder + fixtures)
  Talker prefill   :  0.10 s
  Loop (N=32)      :  8.57 s   talker_decode=1.01 cp=7.55 (talker 31ms/step, cp 236ms/step)
  Decoder          :  5.32 s   (RTF dec 2.00)
  TOTAL            : 14.00 s pour 2.67s audio -> RTF 5.25

Gros leviers perf restants pour RTF<1 (P3.5 itératif, dehors session) :
- KV cache CP : 15 passes/step recompute O(L^2) -> incremental O(L). Estimation
  gain x5-8 sur CP. Tentative cette session = abandonnee (risque casser bit-exact).
- Decoder ggml : histo chraac 0.96 prouve realisable, faut tuner.
- Threads big cores via taskset (Pad3 SD8Elite 8 cores big, scheduler ne route
  pas tjrs au mieux).

Q8_0 CP teste -> perd la precision (76% codes match vs 100% f16) ; sampling et
CP en f16 reste necessaire (confirme observation rapport TTS avril : modeles RVQ
incompatibles avec quantization).

Decoder ggml rebuilte au passage avec ggml engine : kazeia-tts-decoder-ggml/
build-android-engine/ -> libqwen3tts-decoder.a sans conflit symboles. Patch
mineur build-time, pas de modif source decoder.

P3.4 (libkazeia_tts.so JNI assemblage Kotlin) : reporte en session future, le
code de tts_pipeline.cpp est l'implementation reference a wrapper en JNI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 15:25:00 +02:00