Repository navigation
Feat(genai,#23415): tts-fishaudio -- route int4 reelle via l'outillage amont de l'image - #23417
Conversation
…l'outillage amont Quatre defauts de l'outillage officiel corriges par deux wrappers: - quantize_int4.py : empaquetage uint8 torch 2.8 (auto-controle round-trip erreur 0.0), construction GPU bfloat16 (201 nn.Linear, pic CPU hors RAM), retrait des shards bf16 du dossier de sortie (le chargeur les prefererait). - serve_int4.py : la branche int4 du chargeur exige un Path, l'API server passe un str (AttributeError) -- conversion avant l'appel. - docker-compose.yml : route int4 documentee (FISHAUDIO_SERVE_SCRIPT, FISHAUDIO_HALF_FLAG vide -- le noyau int4 exige scales_and_zeros bf16), wrapper monte en lecture seule. - docs/genai/tts-fishaudio-int4.md : rapport de mesure. Mesures (GPU 1, RTX 3090 24 Go, meme texte, seed 42): poids 9,12 -> 2,96 Go (35 %); VRAM totale 23 061 -> 17 429 MiB (attribuable ~18,0 -> ~12,4 Gio, -31 %); transcription whisper identique mot a mot; degradation prosodique (duree +22 %), declaree comme telle. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml). Detector: |
…numerotation 2.5 docs-index-guard: le nouveau doc etait inatteignable depuis docs/README.md (275/276). Entree ajoutee dans la section GenAI. Au passage : la section padding passe de 2.4 (doublon) a 2.5, et le renvoi de la section 3.1 vers le correctif de chargement designe desormais le 2.4. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Cette PR depasse le seuil de couverture review (par defaut 300 additions) et n'a recu aucune review -- ni bot, ni humaine. Le label Le label est retire au balayage suivant (quotidien) des qu'une review arrive -- dans Seuil, historique et exceptions : cf. |
Grain: DEEP/genai — lane myia-po-2023:CoursIA — prev: DEEP/research-code #23128
Le fait, et ce que cette PR change
docs/genai/genai-services.mdannonçait « BnB 4-bit NF4 · ~5 GB » pourtts-fishaudio, alors que le service charge le snapshot fp16 (--half), mesuré à 20,33 Go de VRAM au warm-up. La PR #23241 (ouverte) corrige cette annonce ; cette PR rend le 4 bits réel.Jusqu'ici, la seule voie 4 bits identifiée passait par un fork (
groxaxo/fish-speech-int4-patch, cheminbnb4) et un checkpoint téléchargé. L'image officielle embarque son propre chemin int4, sans fork :tools/llama/quantize.py+ la branche int4 du chargeur.Le mécanisme, mesuré dans la source de l'image
tools/llama/quantize.py— CLIclick:--checkpoint-path,--mode int8|int4,--groupsize,--timestampcheckpoints/fs-1.2-int4-g{groupsize}-{timestamp}/model.pthfish_speech/models/text2semantic/llama.py—if "int4" in str(Path(path))→WeightOnlyInt4QuantHandler; groupsize relu danspath.name.split("-")[-2]torch.ops.aten._convert_weight_to_int4pack— int4 weight-only affine par groupeLe nom produit par l'outil porte les deux informations que le chargeur relit (
int4et-g128-) : l'outillage amont produit, par construction, un dossier que son propre chargeur sait lire.Ce n'est pas du bitsandbytes NF4 : l'image ne contient ni
bitsandbytesniaccelerate, et aucun cheminbnb4— un checkpoint NF4 n'y est donc pas chargeable.Les quatre obstacles amont, et les quatre correctifs
_convert_weight_to_int4pack(codes_int32, ...)— écrit pour un torch où l'op acceptait des codes int32 ; sur le torch 2.8 de l'image, l'op exige un uint8 à deux codes 4 bits par octet, le code d'indice pair dans le nibble haut, et unscales_and_zerosen bfloat16 (en fp32 le noyau rend une constante ; en fp16 des valeurs aberrantes). Sans correctif :RuntimeError: Expected in.dtype() == at::kByte. → le wrapper réécritprepare_int4_weight_and_scales_and_zeros(re-empaquetage uint8 mesuré par round-trip) et s'auto-contrôle avant d'écrire : un round-trippack → mmsur des codes connus doit restituer les codes à l'identique (erreur max0.0mesurée) sinon il sort sans produire de checkpoint.quantize()charge sur CPU et la construction se fait en float32 (modèledual_ar: 201nn.Linear, dont 4,05 Md de poids quantifiés + 0,58 Md non quantifiés — mesuré sur le checkpoint produit → pic hors RAM disponible sur le siège). → le wrapper forcetorch.set_default_device("cuda")+bfloat16: construction et chargement sur GPU. Le CUDA était déjà requis de toute façon (prepare_int4_weight_and_scales_and_zerosfait.to("cuda")).model.safetensors.index.json; or le chargeur préfère l'index, puis le shard, et ne tombe surmodel.pthqu'en dernier → sans correction il chargerait les poids bf16 d'origine et ignorerait la quantification (succès apparent, effet nul). → le wrapper retire les shards et l'index.str(côté serveur). La branche int4 relit le groupsize viapath.name.split("-")(exige unpathlib.Path), mais l'API server passe le checkpoint en str →AttributeError: 'str' object has no attribute 'name'juste aprèsUsing int4 quantization!(mesuré). Le quantificateur amont ne le voit pas : click lui fournit unPath. →serve_int4.pyconvertit enPathavant l'appel, puis exécute l'API server avec les arguments inchangés — monté en lecture seule, activé parFISHAUDIO_SERVE_SCRIPT=serve_int4.pyuniquement sur la route int4.Cohérence du padding : quantificateur et runtime partagent le même prédicat
_check_linear_int4_k; avecgroupsize=128,dim=2560etintermediate=9728sont divisibles par 128 → aucun padding, des deux côtés.Pourquoi le compose change de forme. L'int4 exige le
scales_and_zerosen bfloat16, or--halffaitmodel.to(dtype=float16)sur tous les buffers flottants (mesuré : le noyau rend alors des valeurs aberrantes) et l'image n'expose pas de--no-half. Lecommand:passe donc en forme chaîne avec${FISHAUDIO_HALF_FLAG---half}—${VAR:-def}se replie aussi sur une variable vide, ce qui collerait--halfde force ; la forme sans deux-points ne se replie que si la variable est indéfinie. L'int4 se sert avecFISHAUDIO_HALF_FLAG= LLAMA_CHECKPOINT_PATH=checkpoints/fs-1.2-int4-g128-20261030.La commande exacte
--entrypoint /app/.venv/bin/pythonest obligatoire : l'entrypoint de l'image lancerun_webui.py, qui intercepte les arguments (unrecognized arguments). Etquantizede l'amont est un objetclick.Command— l'appeler directement lèveTypeError: 'PosixPath' object is not iterable; le wrapper appelle.callback.Les mesures
Seat
myia-po-2023, GPU 1 (RTX 3090 24 Go), même jour, même texte,seed=42des deux côtés.--half)model.pth, 35 %)All keys matchedUsing int4 quantization!puisAll keys matched— aucunsize mismatchwhisper-apifrVerdict qualité : sortie non muette, non bruitée, intelligible (transcription indépendante identique mot à mot) ; la dégradation observable est prosodique — débit +22 %, enveloppe décorrélée (0,32). Quantification : 364 s sur GPU 1, 201
nn.Linear(4,05 Md poids à 4 bits, 0,58 Md conservés bf16), contrôle d'empaquetage erreur max 0.0.Fichiers
docker-configurations/services/tts-fishaudio/quantize_int4.py— le wrapper de quantification (nouveau)docker-configurations/services/tts-fishaudio/serve_int4.py— le correctif de chargement (nouveau)docker-configurations/services/tts-fishaudio/docker-compose.yml— route int4 documentée + drapeau de précision supprimable + montage du wrapperdocs/genai/tts-fishaudio-int4.md— le rapport de mesure (nouveau)docs/genai/genai-services.mdetREADME.mddu service ne sont pas touchés : ils sont dans le périmètre de #23241 (ouverte). Le renvoi croisé y sera ajouté quand #23241 sera mergée.Closes #23415
🤖 Generated with Claude Code