Cortex-0 / docs /decisions.md
Frankenstein-Labs's picture
release: Cortex-0 (Phases 0-12)
3a2cb1d verified
|
Raw History Blame Contribute Delete
17 kB
# 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.