Commit Graph

7 Commits

Author SHA1 Message Date
Richard Loyer 19ac123afa TU sampler tuning : top_p 1.0->0.95 + rep_penalty 1.05->1.10 nouveaux défauts
Validation à l'écoute (panel A/B 4 combos sur 'Bonsoir, comment tu te sens
ce soir ?'). Verdict utilisateur : combinaison TOPP_0.95_REPP_1.10 sonne le
moins robotique.

Analyse complémentaire TU.2 (dump codes via KZTTS_DUMP_CODES=path) sur 4
phrases panel a confirmé :
  - code 690 CB0 = attracteur silence (5.9% du panel, doublé systématiquement
    en début de phrase, jusqu'à 4 répétitions consécutives mesurées)
  - rep_penalty 1.05 trop doux pour casser ces 690 690
  - rep_penalty 1.10 plus nucleus 0.95 -> top_p coupe la queue improbable
    + rep défavorise les codes récents -> moins de pause robotique

Nouveaux défauts (tts_engine.h TtsSynthesizeCfg + tts_pipeline env defaults
+ TtsEngine.kt TtsSampling) :
  cp_top_p     = 0.95f  (était 1.0)
  cp_rep_penalty = 1.10f (était 1.05)
  talker_top_p = 0.95f  (était 1.0)
  talker_rep_penalty = 1.10f (était 1.05)
  cp_temp, top_k, talker_temp/top_k inchangés (0.9, 50).

Mesure panel 4 phrases avec nouveaux défauts + decoder chraac subproc :
  'Bonjour Kazeia'                            : N=29 RTF 2.19
  'Bonsoir, comment tu te sens ce soir ?'    : N=48 RTF 2.21
  'Je rumine la nuit.'                       : N=34 RTF 2.21
  'Comment puis-je t'aider aujourd'hui ?'   : N=29 RTF 2.27

RTF stable ~2.2. Compression légère sur phrases courtes (29 vs 33 baseline),
expansion sur phrases interrogatives ('Bonsoir' N=48 vs 41). Cohérent
avec top_p resserré qui choisit des codes plus pertinents.

KZTTS_DUMP_CODES=path ajouté dans tts_engine pour analyse future
(int32 binaire [N,16] time-major).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 11:18:25 +02:00
Richard Loyer c332943181 Decoder chraac en sous-process : -20% decoder, -9% pipeline, talker intact
Architecture pour exploiter le gain chraac sans casser le talker :
  - kazeia_decoder_chraac : binaire static contre libqwen3tts-decoder.a chraac
    + ggml chraac statiques (9.8 MB autonome, pas de dép .so chraac).
    Args : <decoder.gguf> <codes.bin> <T> <out.wav>. Lit codes int32 time-major,
    transpose codebook-major, forward, écrit WAV.
  - tts_engine : si KZTTS_DECODER_SUBPROC=path + KZTTS_DECODER_GGUF=path posés,
    fork+execl au lieu de dec.forward intégré. Codes via /data/local/tmp tmpfile,
    WAV via rename. Le talker+CP continuent sur ql/ggml (qualcomm) qui marche.

Mesures Pad3 KZTTS_THREADS=6 GGML_NUM_THREADS=8 seed=42 :

  Phrase 'Bonjour Kazeia' (33 frames) :
    A baseline qualcomm : decoder 3.29s, total 6.61s, RTF 2.40
    B chraac subproc    : decoder 2.63s, total 6.06s, RTF 2.20
    -> decoder -20%, total -8.3%, audio RMS 1743 identique

  Phrase 'Bonsoir, comment tu te sens ce soir ?' (41 frames) :
    A baseline : decoder 3.95s, total 8.16s, RTF 2.39
    B subproc  : decoder 3.15s, total 7.38s, RTF 2.16
    -> decoder -20%, total -9.6%

Audio cohérent au baseline (range, RMS quasi identiques), imperceptible
à l'oreille (cf cas 'Bonsoir' envoyé).

Le gain chraac historique sur decoder seul est désormais accessible SANS
casser le talker.

Code committé opt-in (KZTTS_DECODER_SUBPROC absent par défaut = baseline intact).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 18:14:09 +02:00
Richard Loyer d6acf378d5 POC streaming TTFB ÷4-6 (KZTTS_STREAM_CHUNK=K)
Concept validé : decoder appelé incrementalement sur codes accumulés tous
les K frames. Premier appel donne TTFB rapide.

TtsSynthesizeResult ajoute ttfb_s + n_chunks. tts_engine_synthesize
détecte KZTTS_STREAM_CHUNK > 0 et appelle decoder.forward(codes_so_far)
tous les K codes, accumule la portion nouvelle dans wav_stream.

Mesure Pad3 phrase 'Bonjour Kazeia' (33 frames) :
  baseline           : TTFB 6.80s, RTF 2.47
  KZTTS_STREAM_CHUNK=8 : TTFB 1.90s (-72%), 5 chunks, total RTF 8.67 (×3.5 cost)
  KZTTS_STREAM_CHUNK=4 : TTFB 1.13s (-83%), 9 chunks, total RTF 13.82 (×5.6 cost)

Limitations connues du POC :
1. Total time explose : recompute decoder sur N_so_far à chaque chunk =
   somme des coûts O(N_chunks × N_avg) au lieu de O(N_total).
2. WAV pas bit-exact vs baseline : decoder.forward(K) ne match pas le
   prefix de decoder.forward(N). Probable conv_transpose_1d qui regarde
   au-delà du strict causal kernel/stride. À comparer à l'oreille.

POC opt-in (KZTTS_STREAM_CHUNK=0 par défaut = path baseline intact).

Pour rendre utilisable en prod (sessions suivantes) :
- Refactor decoder avec state KV cache (pre_transformer en KV cache style
  comme cp_forward_cached_step + BigVGAN avec context buffer ~5 frames input)
- Threading 2-thread : generator (talker+CP) en parallèle de decoder
- Cible : TTFB ~500ms, total time ≈ baseline + overhead minimal

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 22:56:14 +02:00
Richard Loyer 27d893c73f Vulkan Adreno 830 testé : neutre au mieux, régresse au pire (commit aussi le code opt-in)
Re-analyse globale plateforme : le GPU n'avait JAMAIS été testé. Build
ggml-vulkan pour Android arm64 (NDK r27d + SPIRV-Headers + vulkan.hpp
téléchargés depuis github, libomp.so poussé sur tablette).

Adreno 830 détecté : uma=1 fp16=1 matrix cores=NONE (compute shaders seuls).

3 patterns testés :
  (1) Decoder weights Vulkan buft + CPU compute : NEUTRE
      RTF 2.45 -> 2.40-2.47 (bruit), WAV bit-exact
      Cause : UMA = même DRAM, pas de gain BW
  (2) Decoder + sched (GPU compute attendu) : tout CPU
      sched route UMA buffer -> CPU (logique is_host)
      Cause : sched ne déclenche pas l'offload pour buffers UMA
  (3) Talker offload 29/29 layers via llama.cpp : REGRESSION +128%
      Prefill 87->775 ms, talker 32->45 ms, decoder 3.3->6.6s
      Cause : 0.6B + batch=1 + bus contention GPU/CPU sur DRAM partagée

Verdict cumulé HTP + Vulkan : sur SM8750 ce TTS spécifique, ni HTP ni
GPU n'apportent gain net. NEON CPU optimisé reste la voie unique
réaliste sur ce hardware.

Conso énergie : Vulkan talker = pipeline ×2.3 plus long, conso ~×2.
NEON CPU = meilleure efficience.
Verdict : RESTE SUR NEON CPU pour la prod.

Code committé opt-in (KZTTS_VULKAN_LIB, KZTTS_DECODER_VULKAN,
KZTTS_TALKER_VULKAN), désactivé par défaut. Path principal RTF 2.45 intact.

Détails complets dans dist/PERF_VULKAN_TESTS.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 22:28:16 +02:00
Richard Loyer 48c66c03e1 chantier F (CP HMX) : path sched livré, gain absent (overhead per-call)
cp_inference.h/.cpp : nouveau cp_load_with_backends + sched path dans
cp_forward_cached_step. KV cache aussi alloué sur backend buffer en mode sched
(no_alloc=true + ggml_backend_alloc_ctx_tensors_from_buft). Inputs (x, pos_ids,
mask) marqués ggml_set_input ; output ggml_set_output. Compute via sched_reset
+ alloc + tensor_set + sched_compute + tensor_get. Path legacy CPU pur (sans
sched) reste intact via détection s.sched.

tts_engine.cpp : KZTTS_CP_HTP=1 + KZTTS_CP_SCHED=1 active le path sched HMX.
KZTTS_CP_HTP=1 seul = poids HTP, compute CPU pur via opt_hostbuf=1 (baseline).

Mesures Pad3 (KZTTS_CP_CACHE=1, seed=42, 'Bonjour Kazeia') :
  CPU baseline                  : CP 111 ms/frame, RTF 2.95, N=33
  CP poids HTP, compute CPU     : CP 108 ms/frame, RTF 2.93, N=33  (~baseline)
  CP sched HMX (HTP-routé)      : CP 181 ms/frame, RTF 3.87, N=64  REGRESSION

GGML_SCHED_DEBUG=2 confirme que ~95% des MUL_MAT CP tombent bien sur HTP0
(blk.0.attn_q.weight : 360 HTP / 21 CPU). Donc HMX est techniquement actif.

Cause de la régression :
  - Overhead par sub-forward sched_reset + sched_alloc_graph + tensor_set/get
    × 15 sub-forwards par frame. ~5 ms/appel × 15 = 75 ms ajoutés par frame
    (cohérent avec 111 -> 181 ms).
  - N=64 vs N=33 = trajectoire talker diverge à cause des codes CP qui
    diffèrent (perte précision HMX f16 tile vs CPU NEON f32).

Pour livrer un gain CP HMX réel, il faudrait :
  - (a) ctx persistant entre sub-forwards (~refactor architectural)
  - (b) batcher les 15 sub-forwards en 1 graph autoregressif (~lourd)

Le path sched CP est laissé en opt-in (KZTTS_CP_SCHED=1, désactivé par défaut)
pour itérer dessus ultérieurement. KZTTS_CP_HTP=1 seul est neutre (~baseline).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 16:03:41 +02:00
Richard Loyer 9de6b76838 chantier BigVGAN HMX Phase 0+2a: audit + chaîne load HTP validée
PHASE 0 audit (PERF_ANALYSIS.md + cette commit):
  - BigVGAN = 95% du decoder (3.24s sur 3.36s), confirmé via KZTTS_DECODER_PROFILE=1
  - 26 conv1d (63.7 Gops) + 4 conv_transpose_1d (51.6 Gops) + 30 snake (0.4 Gops)
  - channels par block : 1024->1536->768->384->192->96->1
  - T_cur évolution : 124->992->4960->19840->59520
  - poids decoder TOUS en F16 déjà (110 MB F16 conv + 215 MB F32 snake α/β)
  - ggml_conv_1d = im2col + mul_mat en interne, le graphe expose déjà MUL_MAT
  - ggml-hexagon ql/ggml supporte : MUL_MAT (HMX fp16), UNARY (exp/silu/etc),
    SOFT_MAX, ROPE, ADD, MUL, NORM, PAD, etc.
    PAS supporté : IM2COL, CONV_1D, CONV_TRANSPOSE_1D, SIN
  - opt_hostbuf=1 default -> HTP buffer CPU-mappable via ION
  - budget RAM HTP session: 2 GB sur 3.5 GB plafond, ~1.5 GB marge

PHASE 2a (chaîne load HTP, sans gain HMX):
  - Refactor decoder.h + gguf_loader.cpp : load_with_backends(path, devs)
    crée le sched optionnellement (gated KZTTS_DECODER_SCHED).
  - tts_engine.cpp : KZTTS_DECODER_HTP=1 -> initialise backend HTP +
    backend CPU (sched), passe les deux à load_with_backends.
  - Mode KZTTS_DECODER_HTP=1 sans SCHED : poids 325 MB sur HTP, compute
    CPU pur lit via opt_hostbuf=1. WAV produit correct, ~baseline perf.
  - Mode SCHED=1 : sched créé mais sched_alloc_graph plante (buffer_id>=0)
    car les stages utilisent ggml_init(no_alloc=false) + memset, incompatible
    avec sched. Refactor stages requis pour activer.

Snapshots des modifs decoder dans dist/decoder_patches/ (le repo decoder
est externe /opt/Kazeia, fichiers untracked).

PHASE 2b à faire prochaine session (~1 session):
  - stage_bigvgan en pattern sched-friendly:
    * no_alloc=true + ggml_set_input/output
    * memset zeros -> ggml_pad_ext
    * precompute_snake host -> ggml_exp + ggml_div in-graph
    * sched_reset + alloc + tensor_set + compute + tensor_get
  - Cible: decoder 3.2s -> 0.5-0.8s (×4-6 sur MUL_MAT HMX)
  - Snake sin reste CPU (fallback sched), borné à 0.4 Gops/115 total

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 11:51:08 +02:00
Richard Loyer 930f3c8c1f chantier B TTS #11: libkazeia_tts.so + Kotlin wrapper, JNI validé bout-en-bout
Refactor: tts_engine.{h,cpp} = API publique de l'engine TTS (load une fois,
synthesize N fois, free). État load-once dans struct TtsEngine (talker
model+ctx, CPState, Decoder, KzTextTokenizer, fixtures + embeds spéciaux
+ role_proj pré-calculé). KV cache talker reset via llama_memory_clear
au début de chaque synthesize -> appels indépendants.

tts_pipeline.cpp devient un thin CLI dessus (refonte sans changement de
sortie : WAV md5 identique au pré-refactor sur 'Bonjour je m'appelle Kazeia').

JNI bridge: kazeia_tts_jni.cpp expose 3 fonctions :
  Java_com_kazeia_tts_TtsJni_nativeLoad / Synthesize / Free
Signatures alignées avec TtsEngine.kt (companion loadLibrary 'kazeia_tts').
Build: build_kazeia_tts.sh -> b-jni/libkazeia_tts.so (~900 KB).

Test_engine_2calls (sans JNI) : 3 synth sur même instance, KV reset OK,
2 appels identiques -> WAV md5 identique, appel 3 différent -> codes
différents.

Test_jni_tts (harness JNI sans VM Java) : dlopen libkazeia_tts.so, JNIEnv
mock minimal (GetStringUTFChars/Release + NewIntArray/SetIntArrayRegion),
appels load + 2 synth + free. WAV md5 identiques aux runs directs. exit=0.

Empreinte tablette mesurée: ~3.5 GB par instance (talker f32 1.6 GB + CP
f16 mixte 250 MB + decoder 325 MB + vocab Qwen3 vocab_only 50 MB + fixtures
text_embed/tp_* 1.2 GB). Une instance par process.

RTF stable autour de 3.0 (CPU 6t), inchangé vs avant refonte.

Reste sur la liste initiale : tuning sampling (itératif à l'oreille).

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