File size: 16,976 Bytes
f40f952 89bd096 f40f952 a393524 f17dddf 37d8122 d9eec2b 6857ec0 827713a 886cf7c c40e77f 9b728b4 096854b efb8037 850e745 3a2cb1d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | # Journal des décisions (ADR léger)
Chaque décision structurante est enregistrée ici : **contexte → décision → conséquences**.
---
## ADR-000 — Initialisation du projet
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : construire une IA multimodale from scratch, sans aucun poids pré-entraîné.
- **Décision** : démarrer par une **Phase 0** de fondations (structure, config, docs,
tests TDD rouges) avant tout code de modèle.
- **Conséquences** : socle stable, vérifiable, avant d'écrire la moindre couche de neurones.
---
## ADR-001 — Interdiction stricte des modèles pré-entraînés
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : le cahier des charges interdit `transformers.AutoModel`, CLIP, BLIP,
LLaVA, Whisper, Wav2Vec, ViT pré-entraîné, Qwen, Llama, DeepSeek, Mistral, GPT.
- **Décision** : n'autoriser que PyTorch, numpy, torchaudio (I/O), torchvision (transforms),
tokenizers (entraînement de **notre** tokenizer), datasets, huggingface_hub, FastAPI,
Gradio, pytest, ruff, Docker.
- **Conséquences** : `transformers` n'est **pas** une dépendance. Un test automatique
(Phase 0) échoue si un import interdit apparaît dans `src/`.
---
## ADR-002 — Positional encoding : RoPE pour le texte, appris pour l'image/audio
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : le texte est une séquence de longueur variable (longue portée, besoin
d'extrapolation) ; l'image et l'audio ont une structure de grille/trames de taille fixe.
- **Décision** : **RoPE** (rotary) pour l'encodeur/décodeur texte ; **embedding
positionnel appris** pour le ViT et l'encodeur audio.
- **Conséquences** : meilleure extrapolation en longueur pour le texte ; simplicité et
suffisance pour des grilles de taille fixe. Détails en `architecture.md`.
---
## ADR-003 — Profile 50M honnête
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : le cahier des charges demande « ~50M params (n_layer=6, n_head=6,
n_embd=384, ctx=512) », mais cette combinaison avec vocab 32k donne ~22.9 M (lié) /
~35.2 M (non lié).
- **Décision** : conserver le profil demandé **et** ajouter `text_model_50m`
(`n_embd=512, n_head=8, n_layer=6`, embeddings non liés ≈ **51.6 M**). Documenter l'écart.
- **Conséquences** : transparence ; l'utilisateur choisit le profil. Pas de fausse
déclaration de taille.
---
## ADR-004 — Aucun secret en dur ; HF_TOKEN par variable d'environnement
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : publication sur un dépôt Hugging Face privé/public.
- **Décision** : le token est lu depuis `HF_TOKEN` (via `.env` gitignoré). Jamais dans le
code, la config, les notebooks ou les commits.
- **Conséquences** : `.env` et `.env.example` séparés ; `.gitignore` bloque `.env`.
---
## ADR-005 — Tests unitaires sur CPU, entraînement sur Colab
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : machine de développement = CPU seul (4 cœurs, 15 Go, pas de GPU).
- **Décision** : toutes les tailles de test dans `configs/model.yaml > test` sont
minuscules ; l'entraînement réel se fait via un notebook Colab par phase.
- **Conséquences** : `pytest` reste rapide ; aucune promesse d'entraînement local.
---
## ADR-006 — Une phase à la fois, validation explicite requise
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : règle 1 du cahier des charges.
- **Décision** : ne jamais passer à la phase suivante sans tests verts + commit/push Hub
+ validation explicite (« phase X validée »).
- **Conséquences** : progression lente mais contrôlée et vérifiable.
---
## ADR-007 — Licence Apache-2.0
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : la Phase 0 avait posé un placeholder MIT, explicitement à remplacer.
- **Décision** : adopter **Apache-2.0**. Le texte intégral est dans `LICENSE`, la model
card (`README.md`) porte `license: apache-2.0`, et `pyproject.toml` déclare
`license = { text = "Apache-2.0" }`.
- **Conséquences** :
- Licence permissive avec **clause de brevet explicite** (section 3), avantageuse pour
un projet modèle/IA réutilisé par des tiers.
- Obligation de **conserver les notices** et de signaler les fichiers modifiés en cas
de redistribution.
- Plus verbeuse que MIT ; aucune contrainte copyleft.
## ADR-008 — Nom du modèle : Cortex-0
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : le projet était désigné « OpenDevin Multimodal ». L'auteur fixe
l'identité publique du modèle.
- **Décision** : nommer le **modèle** `Cortex-0` (README, `configs/model.yaml > project.name`,
model card, package `pyproject.toml` → `cortex-0`). Le **dépôt Hugging Face conserve**
`Frankenstein-Labs/opendevin-multimodal` jusqu'à la Phase 12 (renommage prévu alors).
- **Conséquences** : identité claire du modèle ; une variable `PROJECT_NAME=Cortex-0` et
un futur renommage de dépôt clairement planifié. Aucune ambiguïté entre nom du modèle
et nom du dépôt.
## ADR-009 — Configuration finale verrouillée
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : l'auteur a figé les paramètres structurants à l'issue de la Phase 0.
- **Décision** :
| Paramètre | Valeur |
|---|---|
| Licence | Apache-2.0 |
| Nom du modèle | Cortex-0 |
| Organisation HF | Frankenstein-Labs |
| Dépôt cible | Frankenstein-Labs/opendevin-multimodal |
| MoE (Phase 10) | **20 experts, top-2** |
| Langue principale | français |
| Matériel | CPU (dev/tests) + Colab (entraînement) |
- **Conséquences** : `configs/model.yaml > moe.n_experts` passe de 8 à 20 ; le coût mémoire
du MoE augmente d'autant (tous les experts résident en mémoire), ce qui sera réévalué
lors de la Phase 10.
---
## ADR-010 — Tokenizer : BPE ByteLevel, corpus FR plafonné, 19 tokens spéciaux
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : la Phase 1 exige un BPE de 32 000 tokens sur corpus français public,
100 % from scratch, capable de gérer accents, emojis et caractères inconnus.
- **Décision** :
- **BPE** avec `pre_tokenizer=ByteLevel(add_prefix_space=False)`,
`byte_fallback=True` (sur le modèle), `min_frequency=2`.
- **`initial_alphabet=ByteLevel.alphabet()`** : indispensable pour un round-trip exact.
- **19 tokens spéciaux** réservés en premier (4 base + 6 raisonnement + 9 modalités),
ids 0..18 fixes → stables entre runs.
- **Corpus plafonné à ~150–200 Mo** de `wikimedia/wikipedia` (config `20231101.fr`),
en **streaming** (pas de téléchargement des ~6 Go).
- Corpus « propre » : pas d'en-tête de licence ni de boilerplate.
- **Conséquences** :
- Round-trip exact pour toute entrée, y compris hors corpus.
- `vocab_size` traité comme borne supérieure ; échec explicite si le corpus est
trop pauvre (pas de résultat trompeur).
- Mesuré : 32 000 tokens en 21,8 s en local (CPU) sur 150 Mo.
- Recherche d'un vocabulaire « 32000 exact » : atteint, donc **aucune réduction
de scope nécessaire**.
---
## ADR-011 — Transformer texte : RoPE, pre-norm, attention causale à la main
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 2 — coder un transformer decoder-only from scratch, sans
`transformers` ni `nn.MultiheadAttention`.
- **Décision** :
- **RoPE** (rotation des paires de dimensions de Q/K) plutôt qu'un embedding
positionnel absolu appris : position **relative** dans `Q·K`, zéro paramètre,
meilleure extrapolation, réutilisable pour toutes les modalités.
- **Pre-norm** (`x = x + F(LN(x))`) : gradient non multiplié par la norme sur le
chemin résiduel → entraînement plus stable.
- **Masque causal** triangulaire à `-inf` avant softmax.
- **FFN GELU** à deux couches, `ffn_mult=4`.
- **`lm_head`** optionnellement lié aux embeddings.
- Génération (`temperature`, `top-k`, `top-p`, glouton) écrite à la main, sans
`model.generate`.
- **Conséquences** :
- Causalité et invariance relative **prouvées par tests**, pas supposées.
- Le profil demandé (`n_embd=384, n_layer=6`) fait **22.9 M**, pas 50 M : écart
documenté, le profil `text_model_50m` (512 dim, non lié) fait bien **51.6 M**.
- Mesuré en local : overfit d'un mini-corpus FR 10.45 → **0.011** en 68 s.
---
## ADR-012 — Entraînement texte : données memmap uint16, AdamW sélectif, cosine manuel
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 3 — entraîner le transformer texte sur corpus public, en
pur PyTorch (pas de `Trainer`).
- **Décision** :
- Tokenisation en **tableau `uint16`** (`train.bin`/`val.bin`) + `np.memmap` :
2 octets/token, zéro copie RAM, découpe val **séquentielle** (pas de fuite).
- **AdamW** avec weight decay appliqué **seulement** aux tenseurs `dim >= 2`.
- **Warmup linéaire + cosinus** écrits à la main.
- **Gradient clipping** 1.0, **autocast** bf16/fp16 sur GPU.
- Checkpoints `last.pt`/`best.pt`, logs JSONL.
- **Conséquences** :
- L'entraînement réel peut tourner sur CPU (mesuré) comme sur Colab GPU.
- La cible ppl < 30 est **budgétée**, pas garantie sur CPU ; le notebook Colab
est le chemin nominal. Aucun chiffre annoncé sans mesure.
---
## ADR-013 — Encodeur image ViT : patchs, position apprise, attention bidirectionnelle
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 4 — encodeur image from scratch, aligné sur l'espace texte.
- **Décision** :
- **Patch embedding** par convolution noyau/pas = patch_size.
- **Positional embedding appris** (grille fixe) plutôt que RoPE 2D.
- **Attention bidirectionnelle** (`causal=False`) : vital, sinon le CLS ne voit
pas les patchs (bug réellement rencontré et corrigé).
- Token **CLS** pour le résumé global (pool `cls`), alternative `mean`.
- **Projecteur MLP** `LayerNorm -> Linear -> GELU -> Linear` vers `text_dim`.
- **Générateur d'images jouets maison** (9 classes forme x couleur) pour tester
sans données externes.
- **Conséquences** :
- Le drapeau `causal` de l'attention est partagé texte (causal) / encodeurs (non causal).
- Overfit vérifié : classification acc > 0.9, reconstruction MSE < 0.05 (CPU).
---
## ADR-014 — CLIP maison : lots à légendes uniques, masque de padding, clipping
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 5 — alignement image-texte from scratch.
- **Décision** :
- Loss contrastive **symétrique** écrite à la main (InfoNCE lignes+colonnes).
- **Masque d'attention** jusqu'au pooling texte (ignorer les PAD).
- Lots construits avec des **légendes uniques** (`_build_batches`) : sans cela,
l'appariement 1-1 est violé et la perte collapse.
- `logit_scale` apprise (température), embeddings **normalisés L2**.
- **Gradient clipping 1.0** ; lr modéré (2e-3) — un lr élevé collapse.
- Métrique de retrieval **au niveau des classes**.
- **Conséquences** :
- Convergence réelle mesurée : retrieval 0.81 (hasard 0.11), loss 2.20 -> 0.15.
- Deux bugs silencieux identifiés et corrigés avec tests de régression.
---
## ADR-015 — VLM : fusion par embeddings, décalage des labels, métrique de grounding
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 6 — VLM génératif image+texte from scratch.
- **Décision** :
- Fusion par **injection d'embeddings** (`inputs_embeds`) : patchs projetés insérés
entre `<|image_start|>` et `<|image_end|>`.
- **Labels décalés** (`labels[t]=ids[t+1]`) ; perte masquée par `ignore_index=-100`.
- Métrique principale = **exactitude teacher-forced** (grounding) ; la génération
libre est rapportée séparément comme métrique plus difficile.
- **Conséquences** :
- Bug silencieux évité : labels non décalés → loss 0.0006 mais génération cassée.
- Mesuré CPU : légende 0.85 / forme 0.80 / couleur 1.00 (teacher-forced),
génération libre 0.35 (biais d'exposition assumé).
---
## ADR-016 — Audio : mel maison, encodeur bidirectionnel, données synthétiques
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 7 — encodeur audio from scratch.
- **Décision** :
- Mel-spectrogramme **écrit à la main** (torch.fft + filtres mel) car torchaudio
est absent de l'environnement (plus « from scratch », zéro dépendance audio).
- Encodeur : patch embedding 2D (fréquence × temps) + blocs **bidirectionnels**.
- Données : **voyelles synthétiques** (formants F0/F1/F2, 10 classes), faute
d'accès audio dans l'environnement de dev.
- **Conséquences** :
- Transcription parfaite sur le jeu jouet (acc 1.00). Parole réelle non testée :
réduction de scope assumée et documentée.
---
## ADR-017 — Modèle complet : un décodeur pour trois modalités
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 8 — texte + image + audio dans un seul modèle.
- **Décision** :
- Un unique décodeur texte ; image et audio projetés puis injectés comme tokens
spéciaux dans la même séquence (fusion précoce).
- Dans un lot multi-tâches, n'encoder que les lignes contenant la modalité.
- Métrique principale : exactitude teacher-forced par tâche ; génération libre
rapportée séparément.
- **Conséquences** :
- Mesuré CPU : image 0.92 / audio 1.00 / mixte 0.93 (teacher-forced) ;
génération libre mixte 0.17 (limite assumée, modèle de taille jouet).
---
## ADR-018 — Vidéo : ViT partagé sur frames + transformer spatio-temporel
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 9 — vidéo (frames + audio synchronisés).
- **Décision** :
- Vidéos jouets = forme colorée qui se déplace (direction = tâche temporelle).
- `VideoEncoder` = ViT partagé par frame + embedding temporel appris + transformer
spatio-temporel bidirectionnel sur tous les `n_frames × n_patches` tokens.
- `n_frames` petit (4) à cause du coût quadratique de l'attention.
- **Conséquences** :
- Grounding mesuré CPU : vidéo 0.78, vidéo+audio 0.85 (18 classes).
- Génération libre 0.0 : limite assumée et documentée.
---
## ADR-019 — MoE 20 experts top-2 dans le FFN
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : Phase 10 — capacité accrue à coût de calcul borné.
- **Décision** :
- `MoEFFN` remplace le FFN dense ; config imposée : 20 experts, top-2.
- Routeur linéaire top-k + renormalisation des portes + bruit d'exploration.
- Loss auxiliaire Switch-Transformer (coef 0.01) pour équilibrer le routage.
- Calcul épars : chaque expert ne traite que ses tokens.
- **Conséquences** :
- Mesuré CPU : perplexité val 1.005, entropie de charge 0.9999 (routage équilibré).
- 14.86M paramètres totaux / 5.42M actifs par token (~2.7x).
- Tous les experts résidents en mémoire ; routage à vectoriser à grande échelle.
---
## ADR-020 — Mémoire : RAG (mode A) + apprentissage permanent (mode B)
- **Date** : 2026-10-10
- **Statut** : accepté (mode B : expérimental)
- **Contexte** : Phase 11 — « apprendre des cours ».
- **Décision** :
- Mode A (RAG) : embeddings maison par feature hashing IDF + index numpy cosinus ;
réponse extractive verbatim avec sources (vidéo = transcription + légendes frames).
- Mode B (apprentissage permanent) : quelques pas de gradient à l'ingestion, avec
tampon de rejeu. Marqué EXPÉRIMENTAL ET INSTABLE, tests qui prouvent l'oubli.
- **Conséquences** :
- Mode A fiable, sans hallucination ; mode B honnête mais instable, non garanti.
---
## ADR-008 — Nom du modèle « Cortex-0 » vs nom du dépôt (renommage différé)
- **Date** : 2026-10-10
- **Statut** : accepté
- **Contexte** : le modèle s'appelle **Cortex-0**, mais le dépôt Hub reste
`Frankenstein-Labs/opendevin-multimodal` pour ne pas casser les liens déjà publiés.
- **Décision** : ne **pas** renommer le dépôt à ce stade ; documenter l'écart
(nom de modèle « Cortex-0 », dépôt « opendevin-multimodal ») dans le README.
Un renommage éventuel (redirection Hub) est laissé à une décision ultérieure.
- **Conséquences** : README et carte de modèle portent « Cortex-0 » ; le remote reste stable.
---
## ADR-021 — Phase 12 : moteur unique, API FastAPI, démo Gradio, Docker
- **Date** : 2026-10-10
- **Statut** : accepté
- **Décision** :
- Un `CortexEngine` unique (texte/image/audio/vidéo) partagé par l'API et la démo.
- API FastAPI testée par `TestClient` ; handlers Gradio testés sans serveur.
- Décodage d'entrées sans dépendance lourde (numpy/Pillow/`wave`), vidéo en `(T,H,W,3)`.
- `CORTEX_CHECKPOINT`/`HF_TOKEN` au runtime ; aucun poids pré-entraîné dans l'image.
- **Conséquences** : 14 tests Phase 12 ; publication du checkpoint + carte avec licence Apache-2.0.
|