patdev commited on
Commit
14e1364
·
verified ·
1 Parent(s): 93e9593

verdict TRT-LLM : noyau MoE 4 bits reserve a sm90

Browse files
reports/2026-08-23-mxfp4-deblocage-trtllm-et-familles-moe.md CHANGED
@@ -1,166 +1,270 @@
1
- # MXFP4 débloque TensorRT-LLM, et le plancher des familles MoE (23/08/2026, nuit 2)
2
-
3
- Deux questions tranchées, toutes deux par de la lecture de code et de l'arithmétique vérifiable
4
- plutôt que par des essais successifs : **pourquoi TensorRT-LLM refusait nos modèles**, et
5
- **quelles variantes MoE sont réellement fabricables** à partir de Qwen3.8 et de Kimi K3.
6
-
7
- ## 1. Le mur TensorRT-LLM : trois formats, une seule issue
8
-
9
- Le mode de quantification `536`, longtemps opaque, se décode depuis `QuantMode` :
10
-
11
- ```
12
- 536 = FP8_ROWWISE (512) + PER_TOKEN (16) + PER_CHANNEL (8)
13
- ```
14
-
15
- Le FP8 officiel d'Ornith est donc du **FP8 dynamique** (poids par canal, activations par jeton).
16
- Or `CutlassFusedMoE._get_quant_method()` ne connaît que ces méthodes MoE :
17
-
18
- | condition | méthode |
19
- |---|---|
20
- | `has_fp8_qdq()` | `FP8QDQFusedMoEMethod` |
21
- | `has_fp8_block_scales()` | `DeepSeekFP8BlockScalesFusedMoEMethod` |
22
- | `has_nvfp4()` | `NVFP4CutlassFusedMoEMethod` |
23
- | `is_int4_weight_only_per_group()` | `WInt4AFP8FusedMoEMethod` (**chemin W4A8**) |
24
- | **`has_w4a16_mxfp4()`** | **`WFP4A16FusedMoEMethod`** |
25
- | `has_w4a8_mxfp4_*()`, `has_mxfp8()` | méthodes MXFP4 / MXFP8 |
26
- | sinon | `raise ValueError("Unsupported quantization mode")` |
27
-
28
- D'où les deux échecs constatés : l'INT4 groupwise tombe sur la méthode **W4A8**, qui déréférence
29
- un `input_scale` absent du checkpoint (`'NoneType' object has no attribute 'device'`), et le FP8
30
- rowwise ne correspond à aucune entrée (`mode [536]`).
31
-
32
- ### La sortie : MXFP4
33
-
34
- `ModelConfig.get_mxfp4_quant_algo()` décide selon la carte :
35
-
36
- ```python
37
- if get_sm_version() >= 100: # Blackwell
38
- return QuantAlgo.W4A8_MXFP4_MXFP8 # (ou _FP8 avec le backend TRITON)
39
- else: # Ada sm89 = notre cas
40
- return QuantAlgo.W4A16_MXFP4
41
- ```
42
-
43
- Et `load_hf_quant_config()` accepte un checkpoint HF déclarant `quant_method: "mxfp4"`. Donc
44
- **sur sm89, un checkpoint MXFP4 route vers `WFP4A16FusedMoEMethod`, qui est implémentée**.
45
- `compressed-tensors` fournit le préréglage `MXFP4A16`, ce qui rend la production du checkpoint
46
- immédiate avec `llmcompressor`.
47
-
48
- **Conséquence : le verdict « TRT-LLM ne peut pas servir nos MoE » était vrai des formats que nous
49
- avions, pas du moteur.** Il ne manquait pas un opérateur custom, il manquait le bon format d'entrée.
50
-
51
- ### Le dense charge, lui
52
-
53
- `Qwen/Qwen3.8-27B-FP8` est en `fp8` **block-scales** (`weight_block_size: [128,128]`), chemin
54
- supporté. Il a chargé sans erreur de quantification et n'a échoué que sur un `cudaMalloc out of
55
- memory` : 27 Go de poids sur une carte de 49 Go, cache KV non borné. Correction :
56
- `KvCacheConfig(free_gpu_memory_fraction=0.25)` et `max_seq_len=4096`.
57
-
58
- ## 2. ONNX Runtime + TensorRT : l'EP existait, la bibliothèque manquait
59
-
60
- Le rapport du 23/08 concluait « EP absent de la build ». C'était vrai de la roue
61
- **`onnxruntime-genai-cuda`**, et faux de **`onnxruntime-gpu` 1.29.0**, qui annonce :
62
-
63
- ```
64
- ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']
65
- ```
66
-
67
- L'EP échouait seulement à charger `libnvinfer.so.10`, TensorRT n'étant pas installé sur le pod.
68
- Piège de version : `tensorrt-cu13-libs` fournit `libnvinfer.so.**11**` ; ORT 1.29 exige la **10**,
69
- qui vient de `tensorrt-cu12-libs==10.*`.
70
-
71
- ## 3. ONNX GenAI sur Ada : 163,2 tok/s, devant vLLM
72
-
73
- Le graphe réparé (`model_fixed3.onnx`) mesuré sur la RTX 6000 Ada :
74
-
75
- | moteur, même carte, solo | débit |
76
- |---|---:|
77
- | vLLM (Ornith INT4) | 134,1 tok/s |
78
- | **ONNX Runtime GenAI (CUDA EP)** | **163,2 tok/s** |
79
- | ONNX GenAI sur L40S (pour mémoire) | 126,9 tok/s |
80
-
81
- Texte français correct et cohérent. **Réserve essentielle** : ORT GenAI n'a pas de traitement
82
- continu par lots — ces 163 tok/s sont un débit *mono-session*, sans équivalent agrégé
83
- multi-agents. La piste reste « edge / session unique », mais elle y bat vLLM.
84
-
85
- Piège : `libcufft.so.12` manquait au chemin alors qu'il est présent dans
86
- `site-packages/nvidia/cu13/lib` du venv — un simple `LD_LIBRARY_PATH`.
87
-
88
- ## 4. Le plancher des variantes MoE demandées
89
-
90
- La question « faire un A4B / A9B / A12B » se tranche avant tout essai, en calculant le **socle
91
- incompressible** : les paramètres actifs à chaque jeton quoi qu'on fasse aux experts.
92
-
93
- **Erreur de méthode commise puis corrigée** : une première estimation appliquait la formule de
94
- l'attention classique aux 64 couches, donnant un socle de 7,24 B. Qwen3.8 est **hybride** —
95
- 48 couches d'attention linéaire, 16 pleines, avec porte de sortie (`attn_output_gate: true`).
96
- Les tailles réelles, lues sur les formes des tenseurs :
97
-
98
- | composant | taille |
99
- |---|---:|
100
- | experts routés (16 × 1024) | 16,106 B |
101
- | **attention linéaire (48 couches)** | **5,562 B** |
102
- | embeddings (248 320 × 5 120, non liés) | 2,543 B |
103
- | attention pleine (16 couches) | 1,678 B |
104
- | expert partagé | 1,007 B |
105
- | routeur | 0,005 B |
106
- | **socle** | **9,78 B** |
107
-
108
- Avec l'expert partagé, le **plancher absolu** — zéro expert routé — est de **10,79 B actifs**.
109
-
110
- | granularité | top-k | actifs/jeton |
111
- |---|---|---:|
112
- | 64 × 256 (dépôt parent) | 2 | 11,29 B |
113
- | 64 × 256 | 5 | **12,05 B** (le plus proche de 12 B) |
114
- | 16 × 1024 | 2 | 12,80 B |
115
- | 16 × 1024 | 4 | 14,81 B |
116
-
117
- **A4B et A9B sont donc impossibles pour cette famille** : ils sont sous le plancher.
118
- Le seul levier serait d'amputer l'attention linéaire (5,56 B) ou les embeddings (2,54 B), ce qui
119
- demande une distillation et ne serait plus le même modèle.
120
-
121
- **Correction d'un chiffre que j'avais publié** : `patdev/Qwen3.8-27B-MoE-A12B-L4` fait
122
- **11,29 B actifs** (64 × 256, top-2), pas 8,75 B comme je l'avais d'abord calculé. Son README
123
- annonçant « ~12,2 B » était à peu près juste.
124
-
125
- ### Kimi K3
126
-
127
- | composant | valeur |
128
- |---|---:|
129
- | attention (96 têtes × 74, 93 couches) | 18,94 B |
130
- | embeddings | 2,35 B |
131
- | **socle** | **21,29 B** |
132
- | actif/jeton actuel (top-16 sur 320 experts) | 119,6 B |
133
- | poids de `lovedheart/Kimi-K3-Lite` | **1 561 Go** |
134
-
135
- **Les trois cibles Kimi sont impossibles, A12B comprise** : le socle fait 21,29 B avant qu'un seul
136
- expert ne s'active. Et matériellement, 1 561 Go de poids contre 200 Go de disque et 49 Go de VRAM
137
- interdisent jusqu'au chargement. Aucune quantité de calcul ne change ce constat.
138
-
139
- ## 5. Ce qui a été fabriqué
140
-
141
- | dépôt | actifs/jeton | total | nature |
142
- |---|---:|---:|---|
143
- | `patdev/Qwen3.8-MoE-27B-A12B` | **12,80 B** | 26,90 B | re-partition **exacte** : 16 experts × 1 024, top-2. Aucun poids modifié — fusion de 4 tranches contiguës du FFN parent. Total conservé. |
144
-
145
- Un squelette A4B a été construit puis **supprimé** : il visait 3,96 B, sous le plancher de
146
- 10,79 B, et son découpage de `q_proj` ignorait la porte de sortie (`attn_output_gate`), donc
147
- produisait des formes fausses. Double invalidité, aucun intérêt à le conserver.
148
-
149
- ## 6. Pièges opérationnels payés cette nuit
150
-
151
- - **`bash /run.sh` ne doit jamais être tué**, quel que soit son PID : `start_pod.sh` l'exécute en
152
- `exec` et sshd en est un enfant, donc sa mort redémarre le conteneur. Deux interruptions de
153
- service à cause de ça. Ne tuer que `vllm [s]erve`.
154
- - **Le superviseur du bootstrap ne relance pas un `vllm serve` tué** : la restauration doit être
155
- explicite.
156
- - **`exec $(cat cmd.txt)`** perd les guillemets : `--limit-mm-per-prompt '{"video":0}'` devient
157
- invalide et l'endpoint reste mort. Repasser par le bootstrap, qui reconstruit la commande
158
- depuis l'environnement.
159
- - **La garde `if __name__ == '__main__':` est obligatoire** pour TRT-LLM *et* pour `vllm.LLM()` :
160
- les deux lancent des processus par spawn qui ré-importent le script. Sans elle, `MPI_ABORT`
161
- d'un côté, « attempt to start a new process before bootstrapping » de l'autre. Deux échecs
162
- attribués à tort aux moteurs.
163
- - **Le jeton HF du pod est mort** depuis la rotation qui a suivi la fuite : les envois vers HF et
164
- la publication du cache de compilation v73 échouaient silencieusement.
165
- - **Convertir un modèle en place double l'occupation disque** : supprimer chaque shard source dès
166
- qu'il est converti divise le pic par deux et a rendu inutile l'agrandissement du disque.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TensorRT-LLM sur Ada : le fond du problème est un noyau Hopper-only (23/08/2026, nuit 2)
2
+
3
+ Deux questions tranchées, toutes deux par de la lecture de code et de l'arithmétique vérifiable
4
+ plutôt que par des essais successifs : **pourquoi TensorRT-LLM refusait nos modèles**, et
5
+ **quelles variantes MoE sont réellement fabricables** à partir de Qwen3.8 et de Kimi K3.
6
+
7
+ ## 1. Le mur TensorRT-LLM : trois formats, une seule issue
8
+
9
+ Le mode de quantification `536`, longtemps opaque, se décode depuis `QuantMode` :
10
+
11
+ ```
12
+ 536 = FP8_ROWWISE (512) + PER_TOKEN (16) + PER_CHANNEL (8)
13
+ ```
14
+
15
+ Le FP8 officiel d'Ornith est donc du **FP8 dynamique** (poids par canal, activations par jeton).
16
+ Or `CutlassFusedMoE._get_quant_method()` ne connaît que ces méthodes MoE :
17
+
18
+ | condition | méthode |
19
+ |---|---|
20
+ | `has_fp8_qdq()` | `FP8QDQFusedMoEMethod` |
21
+ | `has_fp8_block_scales()` | `DeepSeekFP8BlockScalesFusedMoEMethod` |
22
+ | `has_nvfp4()` | `NVFP4CutlassFusedMoEMethod` |
23
+ | `is_int4_weight_only_per_group()` | `WInt4AFP8FusedMoEMethod` (**chemin W4A8**) |
24
+ | **`has_w4a16_mxfp4()`** | **`WFP4A16FusedMoEMethod`** |
25
+ | `has_w4a8_mxfp4_*()`, `has_mxfp8()` | méthodes MXFP4 / MXFP8 |
26
+ | sinon | `raise ValueError("Unsupported quantization mode")` |
27
+
28
+ D'où les deux échecs constatés : l'INT4 groupwise tombe sur la méthode **W4A8**, qui déréférence
29
+ un `input_scale` absent du checkpoint (`'NoneType' object has no attribute 'device'`), et le FP8
30
+ rowwise ne correspond à aucune entrée (`mode [536]`).
31
+
32
+ ### La sortie : MXFP4
33
+
34
+ `ModelConfig.get_mxfp4_quant_algo()` décide selon la carte :
35
+
36
+ ```python
37
+ if get_sm_version() >= 100: # Blackwell
38
+ return QuantAlgo.W4A8_MXFP4_MXFP8 # (ou _FP8 avec le backend TRITON)
39
+ else: # Ada sm89 = notre cas
40
+ return QuantAlgo.W4A16_MXFP4
41
+ ```
42
+
43
+ Et `load_hf_quant_config()` accepte un checkpoint HF déclarant `quant_method: "mxfp4"`. Donc
44
+ **sur sm89, un checkpoint MXFP4 route vers `WFP4A16FusedMoEMethod`, qui est implémentée**.
45
+ `compressed-tensors` fournit le préréglage `MXFP4A16`, ce qui rend la production du checkpoint
46
+ immédiate avec `llmcompressor`.
47
+
48
+ **Conséquence : le verdict « TRT-LLM ne peut pas servir nos MoE » était vrai des formats que nous
49
+ avions, pas du moteur.** Il ne manquait pas un opérateur custom, il manquait le bon format d'entrée.
50
+
51
+ ### Le dense charge, lui
52
+
53
+ `Qwen/Qwen3.8-27B-FP8` est en `fp8` **block-scales** (`weight_block_size: [128,128]`), chemin
54
+ supporté. Il a chargé sans erreur de quantification et n'a échoué que sur un `cudaMalloc out of
55
+ memory` : 27 Go de poids sur une carte de 49 Go, cache KV non borné. Correction :
56
+ `KvCacheConfig(free_gpu_memory_fraction=0.25)` et `max_seq_len=4096`.
57
+
58
+ ## 2. ONNX Runtime + TensorRT : l'EP existait, la bibliothèque manquait
59
+
60
+ Le rapport du 23/08 concluait « EP absent de la build ». C'était vrai de la roue
61
+ **`onnxruntime-genai-cuda`**, et faux de **`onnxruntime-gpu` 1.29.0**, qui annonce :
62
+
63
+ ```
64
+ ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']
65
+ ```
66
+
67
+ L'EP échouait seulement à charger `libnvinfer.so.10`, TensorRT n'étant pas installé sur le pod.
68
+ Piège de version : `tensorrt-cu13-libs` fournit `libnvinfer.so.**11**` ; ORT 1.29 exige la **10**,
69
+ qui vient de `tensorrt-cu12-libs==10.*`.
70
+
71
+ ## 2 bis. Les deux verrous restants, et leurs correctifs
72
+
73
+ Ouvrir la voie MXFP4 ne suffisait pas : deux défauts de TRT-LLM 1.3.0rc22 se dressaient encore.
74
+ Tous deux sont du Python — aucun noyau CUDA n'a été nécessaire, contrairement à ce qu'annonçaient
75
+ les sessions précédentes. Publiés dans `patches_trtllm/`.
76
+
77
+ ### a. Le mappage compressed-tensors ignore le MXFP4
78
+
79
+ `llmcompressor` écrit `quant_method: "compressed-tensors"` avec `format: "mxfp4-pack-quantized"`,
80
+ et TRT-LLM **a** une branche pour ce `quant_method`. Mais
81
+ `update_quant_config_from_compressed_tensors()` ne couvre que trois cas — FP8 par canal, FP8 par
82
+ blocs, NVFP4 (`strategy: "tensor_group"`, groupe 16) — et pas le MXFP4A16 : `strategy: "group"`,
83
+ `group_size: 32`, `input_activations: null`.
84
+
85
+ Pire, elle exécute `inputs_quant_config["strategy"]` **sans tester la nullité**, donc elle lève un
86
+ `TypeError` avant d'atteindre le moindre test utile. Correctif : garde de nullité, plus une
87
+ branche `num_bits == 4 and type == "float" and strategy == "group" and input_activations is None`
88
+ → `QuantAlgo.W4A16_MXFP4`.
89
+
90
+ ### b. Le cache mRoPE mélange CPU et GPU
91
+
92
+ `_prepare_qwen_vl_mrope_config()` construit `deltas` par `torch.cat()` de tenseurs issus des
93
+ paramètres multimodaux — donc du CPU — puis les écrit dans `mrope_position_deltas_cache`, sur GPU :
94
+
95
+ ```
96
+ RuntimeError: Expected all tensors to be on the same device,
97
+ but got source is on cpu, different from other tensors on cuda:0
98
+ ```
99
+
100
+ Le symptôme remonté est trompeur : le modèle **charge entièrement**, l'exécuteur démarre, et
101
+ l'erreur ne se manifeste qu'à la première génération, sous la forme
102
+ `AssertionError: Sampling failed` dans la boucle d'événements. Correctif : un `.to(device, dtype)`
103
+ avant `index_copy_`.
104
+
105
+ ## 2 ter. L'EP TensorRT d'ONNX Runtime est bien actif
106
+
107
+ Après installation de `tensorrt-cu12-libs==10.*` :
108
+
109
+ ```
110
+ providers actifs: ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']
111
+ TRT_EP_ACTIF
112
+ ```
113
+
114
+ Le verdict « EP absent de la build », qui tenait depuis trois sessions, ne valait que pour la roue
115
+ `onnxruntime-genai-cuda`. `onnxruntime-gpu` 1.29.0 l'embarque. Piège de version à retenir :
116
+ `tensorrt-cu13-libs` fournit `libnvinfer.so.**11**`, qu'ORT 1.29 ne reconnaît pas — il exige la **10**.
117
+
118
+ ## 2 quater. Verdict final : le noyau MoE 4 bits est réservé à Hopper
119
+
120
+ Les deux correctifs ci-dessus ont fait leur travail — et c'est justement ce qui rend le résultat
121
+ concluant, puisque toutes les causes d'échec antérieures ont été éliminées une à une.
122
+
123
+ ### Le dense s'exécute, mais échoue sur la qualité et la vitesse
124
+
125
+ ```
126
+ TRTLLM_DENSE 89 jetons en 3.04s = 29.2 tok/s
127
+ TEXTE: 一串string string string string ... 串串串根__sontacs/串/他的__ivr
128
+ ```
129
+
130
+ Le correctif mRoPE débloque bien la génération de bout en bout. Mais la sortie est du **charabia**
131
+ — le chemin FP8 block-scales est numériquement faux pour cette architecture — et 29,2 tok/s
132
+ représentent **4,6× moins que les 134,1 de vLLM** sur la même carte. Doublement disqualifié.
133
+ Ceci reproduit indépendamment, cause de plantage éliminée, l'observation « charge mais sort du
134
+ charabia » d'une session antérieure.
135
+
136
+ ### Le MoE MXFP4 est refusé explicitement
137
+
138
+ Le correctif de mappage fonctionne : le checkpoint est reconnu, routé vers `W4A16_MXFP4`, puis
139
+ vers `WFP4A16FusedMoEMethod`. Et là :
140
+
141
+ ```
142
+ NotImplementedError: WFP4A16 MoE is unsupported on SM89.
143
+ ```
144
+
145
+ Dans `_torch/modules/fused_moe/quantization.py` :
146
+
147
+ ```python
148
+ if module.sm_version == 90: # Hopper, et rien d'autre
149
+ module.interleave = [128 // self.group_size for k_shape in (...)]
150
+ else:
151
+ raise NotImplementedError(f"WFP4A16 MoE is unsupported on SM{module.sm_version}.")
152
+ ```
153
+
154
+ **Le noyau MoE 4 bits de TRT-LLM n'existe que pour sm90.** `get_mxfp4_quant_algo()` renvoie
155
+ pourtant `W4A16_MXFP4` pour tout `sm < 100` : sur Ada, il désigne donc du code mort.
156
+
157
+ ### Tableau de synthèse : aucun chemin MoE quantifié sur Ada
158
+
159
+ | format | résultat sur sm89 |
160
+ |---|---|
161
+ | INT4 groupwise (AWQ) | routé vers `WInt4AFP8FusedMoEMethod` (W4A8) → `input_scale` absent |
162
+ | FP8 rowwise (Ornith officiel) | aucune méthode MoE — `Unsupported quantization mode [536]` |
163
+ | **MXFP4 / W4A16** | **noyau réservé à sm90** |
164
+ | FP8 block-scales (dense, pas MoE) | s'exécute, mais charabia et 29,2 tok/s contre 134,1 |
165
+
166
+ **Conclusion : TensorRT-LLM ne peut pas servir un MoE quantifié sur Ada.** Ce n'est plus une
167
+ question de format — c'est un noyau absent, refusé explicitement. Les deux seules issues sont
168
+ une carte **Hopper (H100/H200, sm90)**, où le chemin MXFP4 devrait fonctionner tel quel avec nos
169
+ deux correctifs, ou l'écriture d'un noyau CUTLASS sm89 avec sa disposition d'entrelacement —
170
+ du vrai CUDA cette fois, pas du Python.
171
+
172
+ Vu que vLLM délivre 134 tok/s sur cette carte et 1 380 j/s agrégés à 32 sessions, l'effort ne se
173
+ justifierait que si l'on migrait vers du Hopper pour d'autres raisons.
174
+
175
+ ## 3. ONNX GenAI sur Ada : 163,2 tok/s, devant vLLM
176
+
177
+ Le graphe réparé (`model_fixed3.onnx`) mesuré sur la RTX 6000 Ada :
178
+
179
+ | moteur, même carte, solo | débit |
180
+ |---|---:|
181
+ | vLLM (Ornith INT4) | 134,1 tok/s |
182
+ | **ONNX Runtime GenAI (CUDA EP)** | **163,2 tok/s** |
183
+ | ONNX GenAI sur L40S (pour mémoire) | 126,9 tok/s |
184
+
185
+ Texte français correct et cohérent. **Réserve essentielle** : ORT GenAI n'a pas de traitement
186
+ continu par lots — ces 163 tok/s sont un débit *mono-session*, sans équivalent agrégé
187
+ multi-agents. La piste reste « edge / session unique », mais elle y bat vLLM.
188
+
189
+ Piège : `libcufft.so.12` manquait au chemin alors qu'il est présent dans
190
+ `site-packages/nvidia/cu13/lib` du venv — un simple `LD_LIBRARY_PATH`.
191
+
192
+ ## 4. Le plancher des variantes MoE demandées
193
+
194
+ La question « faire un A4B / A9B / A12B » se tranche avant tout essai, en calculant le **socle
195
+ incompressible** : les paramètres actifs à chaque jeton quoi qu'on fasse aux experts.
196
+
197
+ **Erreur de méthode commise puis corrigée** : une première estimation appliquait la formule de
198
+ l'attention classique aux 64 couches, donnant un socle de 7,24 B. Qwen3.8 est **hybride** —
199
+ 48 couches d'attention linéaire, 16 pleines, avec porte de sortie (`attn_output_gate: true`).
200
+ Les tailles réelles, lues sur les formes des tenseurs :
201
+
202
+ | composant | taille |
203
+ |---|---:|
204
+ | experts routés (16 × 1024) | 16,106 B |
205
+ | **attention linéaire (48 couches)** | **5,562 B** |
206
+ | embeddings (248 320 × 5 120, non liés) | 2,543 B |
207
+ | attention pleine (16 couches) | 1,678 B |
208
+ | expert partagé | 1,007 B |
209
+ | routeur | 0,005 B |
210
+ | **socle** | **9,78 B** |
211
+
212
+ Avec l'expert partagé, le **plancher absolu** — zéro expert routé — est de **10,79 B actifs**.
213
+
214
+ | granularité | top-k | actifs/jeton |
215
+ |---|---|---:|
216
+ | 64 × 256 (dépôt parent) | 2 | 11,29 B |
217
+ | 64 × 256 | 5 | **12,05 B** (le plus proche de 12 B) |
218
+ | 16 × 1024 | 2 | 12,80 B |
219
+ | 16 × 1024 | 4 | 14,81 B |
220
+
221
+ **A4B et A9B sont donc impossibles pour cette famille** : ils sont sous le plancher.
222
+ Le seul levier serait d'amputer l'attention linéaire (5,56 B) ou les embeddings (2,54 B), ce qui
223
+ demande une distillation et ne serait plus le même modèle.
224
+
225
+ **Correction d'un chiffre que j'avais publié** : `patdev/Qwen3.8-27B-MoE-A12B-L4` fait
226
+ **11,29 B actifs** (64 × 256, top-2), pas 8,75 B comme je l'avais d'abord calculé. Son README
227
+ annonçant « ~12,2 B » était à peu près juste.
228
+
229
+ ### Kimi K3
230
+
231
+ | composant | valeur |
232
+ |---|---:|
233
+ | attention (96 têtes × 74, 93 couches) | 18,94 B |
234
+ | embeddings | 2,35 B |
235
+ | **socle** | **21,29 B** |
236
+ | actif/jeton actuel (top-16 sur 320 experts) | 119,6 B |
237
+ | poids de `lovedheart/Kimi-K3-Lite` | **1 561 Go** |
238
+
239
+ **Les trois cibles Kimi sont impossibles, A12B comprise** : le socle fait 21,29 B avant qu'un seul
240
+ expert ne s'active. Et matériellement, 1 561 Go de poids contre 200 Go de disque et 49 Go de VRAM
241
+ interdisent jusqu'au chargement. Aucune quantité de calcul ne change ce constat.
242
+
243
+ ## 5. Ce qui a été fabriqué
244
+
245
+ | dépôt | actifs/jeton | total | nature |
246
+ |---|---:|---:|---|
247
+ | `patdev/Qwen3.8-MoE-27B-A12B` | **12,80 B** | 26,90 B | re-partition **exacte** : 16 experts × 1 024, top-2. Aucun poids modifié — fusion de 4 tranches contiguës du FFN parent. Total conservé. |
248
+
249
+ Un squelette A4B a été construit puis **supprimé** : il visait 3,96 B, sous le plancher de
250
+ 10,79 B, et son découpage de `q_proj` ignorait la porte de sortie (`attn_output_gate`), donc
251
+ produisait des formes fausses. Double invalidité, aucun intérêt à le conserver.
252
+
253
+ ## 6. Pièges opérationnels payés cette nuit
254
+
255
+ - **`bash /run.sh` ne doit jamais être tué**, quel que soit son PID : `start_pod.sh` l'exécute en
256
+ `exec` et sshd en est un enfant, donc sa mort redémarre le conteneur. Deux interruptions de
257
+ service à cause de ça. Ne tuer que `vllm [s]erve`.
258
+ - **Le superviseur du bootstrap ne relance pas un `vllm serve` tué** : la restauration doit être
259
+ explicite.
260
+ - **`exec $(cat cmd.txt)`** perd les guillemets : `--limit-mm-per-prompt '{"video":0}'` devient
261
+ invalide et l'endpoint reste mort. Repasser par le bootstrap, qui reconstruit la commande
262
+ depuis l'environnement.
263
+ - **La garde `if __name__ == '__main__':` est obligatoire** pour TRT-LLM *et* pour `vllm.LLM()` :
264
+ les deux lancent des processus par spawn qui ré-importent le script. Sans elle, `MPI_ABORT`
265
+ d'un côté, « attempt to start a new process before bootstrapping » de l'autre. Deux échecs
266
+ attribués à tort aux moteurs.
267
+ - **Le jeton HF du pod est mort** depuis la rotation qui a suivi la fuite : les envois vers HF et
268
+ la publication du cache de compilation v73 échouaient silencieusement.
269
+ - **Convertir un modèle en place double l'occupation disque** : supprimer chaque shard source dès
270
+ qu'il est converti divise le pic par deux et a rendu inutile l'agrandissement du disque.
reports/README.md CHANGED
@@ -13,7 +13,7 @@ en a conclu, pour ne pas le refaire.
13
  | 2026-08-23 | `2026-08-23-pont-et-integration-claude-code.md` | Pont sous charge (fuite de connexions), compaction Claude Code (`[1m]`, réponse vide), compteur de jetons en direct, débit solo ressenti |
14
  | 2026-08-23 | `2026-08-23-l4-graphes-cuda-et-multi-modeles.md` | L4 : 12,4 → 75,7 tok/s en activant les graphes CUDA (×6) ; rendement par Go/s vs notre TP=2 ; Kimi-K3 / Qwen3.8 déjà servis par le bootstrap ; TRT-LLM impasse confirmée |
15
  | 2026-08-23 | `2026-08-23-tensorrt-et-onnx-mesures.md` | TRT-LLM **impasse fermée** (INT4 et FP8, sur L40S et sur Ada) ; graphe ONNX réparé et fonctionnel (126,9 tok/s L40S) ; baseline L4 corrigée 13→76 ; MTP à 29 % d’acceptation même en FP8 |
16
- | 2026-08-23 | `2026-08-23-mxfp4-deblocage-trtllm-et-familles-moe.md` | **MXFP4 debloque TRT-LLM** sur sm89 (`W4A16_MXFP4` -> `WFP4A16FusedMoEMethod`) ; l'EP TensorRT existe bien dans onnxruntime-gpu 1.29 ; **ONNX GenAI 163,2 tok/s sur Ada, devant vLLM** ; planchers reels Qwen3.8 (10,79 B) et Kimi K3 (21,29 B) : A4B et A9B impossibles |
17
 
18
  Convention : `AAAA-MM-JJ-sujet.md`, français, sources en URL, « non trouvé » explicite, et une
19
  section finale « ce que nous en avons fait » quand c'est appliqué.
 
13
  | 2026-08-23 | `2026-08-23-pont-et-integration-claude-code.md` | Pont sous charge (fuite de connexions), compaction Claude Code (`[1m]`, réponse vide), compteur de jetons en direct, débit solo ressenti |
14
  | 2026-08-23 | `2026-08-23-l4-graphes-cuda-et-multi-modeles.md` | L4 : 12,4 → 75,7 tok/s en activant les graphes CUDA (×6) ; rendement par Go/s vs notre TP=2 ; Kimi-K3 / Qwen3.8 déjà servis par le bootstrap ; TRT-LLM impasse confirmée |
15
  | 2026-08-23 | `2026-08-23-tensorrt-et-onnx-mesures.md` | TRT-LLM **impasse fermée** (INT4 et FP8, sur L40S et sur Ada) ; graphe ONNX réparé et fonctionnel (126,9 tok/s L40S) ; baseline L4 corrigée 13→76 ; MTP à 29 % d’acceptation même en FP8 |
16
+ | 2026-08-23 | `2026-08-23-mxfp4-deblocage-trtllm-et-familles-moe.md` | **TRT-LLM : le noyau MoE 4 bits est reserve a sm90** — aucun chemin MoE quantifie sur Ada (2 correctifs Python ecrits, valables sur Hopper) ; l'EP TensorRT existe bien dans onnxruntime-gpu 1.29 ; **ONNX GenAI 163,2 tok/s sur Ada, devant vLLM** ; planchers reels Qwen3.8 (10,79 B) et Kimi K3 (21,29 B) : A4B et A9B impossibles |
17
 
18
  Convention : `AAAA-MM-JJ-sujet.md`, français, sources en URL, « non trouvé » explicite, et une
19
  section finale « ce que nous en avons fait » quand c'est appliqué.