Mike0021's picture
Eliminate mobile horizontal overflow
7f00c7b verified
Raw History Blame Contribute Delete
29.2 kB
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Three.js GRPO — autopsie d’un système d’entraînement 3D</title>
<style>
:root{--ink:#17211d;--muted:#627068;--paper:#f5f3ea;--card:#fffdf7;--line:#d8d7ca;--green:#176b4d;--mint:#dcefe5;--orange:#d15f35;--yellow:#f4c95d;--blue:#386b8c;--red:#a43d36;--mono:#24342d}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--paper);color:var(--ink);font:16px/1.62 system-ui,-apple-system,Segoe UI,sans-serif}a{color:var(--green)}nav{position:sticky;top:0;z-index:9;background:rgba(245,243,234,.94);backdrop-filter:blur(12px);border-bottom:1px solid var(--line)}nav div{max-width:1180px;margin:auto;padding:12px 20px;display:flex;gap:18px;overflow:auto;white-space:nowrap;font-size:13px;font-weight:700}nav a{text-decoration:none;color:var(--muted)}nav a:hover{color:var(--green)}main{max-width:1180px;margin:auto;padding:0 20px 96px}.hero{padding:72px 0 48px;display:grid;grid-template-columns:1.25fr .75fr;gap:40px;align-items:end}.kicker{font:700 12px/1 system-ui;letter-spacing:.15em;text-transform:uppercase;color:var(--orange)}h1{font-size:clamp(42px,7vw,86px);letter-spacing:-.055em;line-height:.94;margin:18px 0 24px;max-width:900px}h2{font-size:clamp(30px,4vw,52px);letter-spacing:-.035em;line-height:1.05;margin:0 0 20px}h3{font-size:22px;margin:0 0 12px;line-height:1.2}p{margin:0 0 16px}.lead{font-size:21px;line-height:1.48;max-width:760px}.hero-fact{border-left:4px solid var(--orange);padding:18px 22px;background:var(--card);font-size:14px}.hero-fact strong{display:block;font-size:34px;letter-spacing:-.04em}.chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:22px}.chip{padding:7px 11px;border:1px solid var(--line);border-radius:99px;background:var(--card);font-size:12px;font-weight:700}.section{padding:72px 0;border-top:1px solid var(--line)}.intro{max-width:800px;font-size:18px;color:#344039;margin-bottom:34px}.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:18px}.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:22px;box-shadow:0 2px 0 rgba(23,33,29,.03)}.span4{grid-column:span 4}.span6{grid-column:span 6}.span8{grid-column:span 8}.span12{grid-column:span 12}.number{font-size:42px;line-height:1;font-weight:800;letter-spacing:-.04em;color:var(--green)}.small{font-size:13px;color:var(--muted)}.status{display:inline-flex;padding:4px 8px;border-radius:5px;font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.05em}.used{background:#dcefe5;color:#15573f}.configured{background:#dbe8f2;color:#285674}.separate{background:#f8e7b8;color:#72520d}.notdone{background:#f1d8d5;color:#7e2e29}.model-table{display:grid;grid-template-columns:1fr 1.2fr 2.2fr;border-top:1px solid var(--line)}.model-table>div{padding:13px 10px;border-bottom:1px solid var(--line)}.model-table .head{font-size:11px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted);font-weight:800}.warning{padding:22px;border:1px solid #e0bdb7;background:#fff3f0;border-radius:12px}.code{background:var(--mono);color:#e6f2e9;padding:19px;border-radius:10px;font:13px/1.55 ui-monospace,SFMono-Regular,monospace;overflow:auto;white-space:pre}.diagram{background:#fff;border:1px solid var(--line);border-radius:14px;padding:16px;overflow:auto}.diagram svg{display:block;width:100%;height:auto;min-width:720px}.timeline{border-left:2px solid var(--green);margin-left:12px}.event{position:relative;margin:0 0 10px 26px;padding:16px 18px;background:var(--card);border:1px solid var(--line);border-radius:10px}.event:before{content:"";position:absolute;left:-34px;top:23px;width:14px;height:14px;border-radius:50%;background:var(--green);border:4px solid var(--paper)}.event time{font-size:12px;font-weight:800;color:var(--orange)}.event b{display:block;margin:3px 0}.reward{height:18px;border-radius:4px;overflow:hidden;display:flex;margin:10px 0 24px}.reward span:nth-child(1){width:35%;background:#176b4d}.reward span:nth-child(2){width:25%;background:#386b8c}.reward span:nth-child(3){width:20%;background:#d15f35}.reward span:nth-child(4){width:15%;background:#e6aa3c}.reward span:nth-child(5){width:5%;background:#8d7b68}.legend{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;font-size:12px}.result{display:grid;grid-template-columns:110px 1fr;gap:16px;align-items:center}.score{font-size:36px;font-weight:800;letter-spacing:-.04em}.gallery{display:grid;grid-template-columns:1fr 1fr;gap:20px}.shot{margin:0;background:var(--card);border:1px solid var(--line);border-radius:14px;overflow:hidden}.shot img{width:100%;display:block;background:white}.shot figcaption{padding:14px 17px;font-size:14px}.shot.wide{grid-column:span 2}.proof{font:12px/1.5 ui-monospace,SFMono-Regular,monospace;word-break:break-word;background:#edf2ed;padding:12px;border-radius:8px}.est{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}.phase{padding:18px;background:var(--card);border-top:5px solid var(--green);border-radius:8px;border-left:1px solid var(--line);border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.phase strong{display:block;font-size:24px}.bar{height:12px;background:#e3e2d9;border-radius:99px;overflow:hidden;margin:8px 0}.bar i{display:block;height:100%;background:var(--green)}.callout{font-size:24px;line-height:1.35;padding:28px;background:var(--mint);border-radius:14px}.footer{font-size:13px;color:var(--muted)}button{font:inherit;border:1px solid var(--line);background:var(--card);border-radius:7px;padding:7px 10px;cursor:pointer}.tabs{display:flex;gap:8px;margin-bottom:12px}.tab-panel{display:none}.tab-panel.active{display:block}@media(max-width:800px){.hero{grid-template-columns:1fr;padding-top:48px}.span4,.span6,.span8{grid-column:span 12}.gallery{grid-template-columns:1fr}.shot.wide{grid-column:auto}.est{grid-template-columns:1fr 1fr}.model-table{grid-template-columns:1fr}.model-table .head{display:none}.model-table>div{border-bottom:0}.model-table>div:nth-child(3n){border-bottom:1px solid var(--line);padding-bottom:20px}.legend{grid-template-columns:1fr 1fr}.result{grid-template-columns:1fr}h1{font-size:48px}}@media(max-width:480px){main{padding-left:15px;padding-right:15px}.est{grid-template-columns:1fr}.section{padding:54px 0}.card{padding:17px}.lead{font-size:18px}}
html,body{overflow-x:hidden}nav{max-width:100vw;overflow:hidden}nav div{width:100%}.hero{grid-template-columns:minmax(0,1.25fr) minmax(0,.75fr)}.hero>*,.grid>*{min-width:0}.grid{grid-template-columns:repeat(12,minmax(0,1fr))}.kicker{line-height:1.2;overflow-wrap:anywhere}
</style></head>
<body>
<nav><div><a href="#verdict">Verdict</a><a href="#chronologie">Chronologie</a><a href="#architecture">Architecture</a><a href="#modeles">Modèles</a><a href="#evaluation">Évaluation</a><a href="#resultats">Résultats</a><a href="#entrainement">Entraînement</a><a href="#duree">Durée</a><a href="#limites">Limites</a></div></nav>
<main>
<header class="hero"><div><div class="kicker">Rapport technique interactif · 24 août 2026</div><h1>Construire une usine d’entraînement 3D, pas juste une jolie chaise.</h1><p class="lead">Deux sessions Codex ont transformé une idée de GRPO pour Three.js en environnement exécutable : code paramétrique → sandbox → GLB → vues cachées → tests structurels et d’édition → récompense → mise à jour des poids.</p><div class="chips"><span class="chip">38/38 tests TypeScript</span><span class="chip">7/7 tests Python</span><span class="chip">162 PNG réels</span><span class="chip">RunPod L4 validé</span></div></div><aside class="hero-fact"><span class="kicker">Ce qui a réellement été livré</span><strong>L’usine</strong><p>Un système de training/evaluation robuste et un checkpoint de preuve à une étape. <b>Pas encore un modèle 3D convergé.</b></p></aside></header>
<section id="verdict" class="section"><h2>Le verdict en 60 secondes</h2><p class="intro">Le projet répond à une question difficile : comment récompenser un programme qui produit un <em>vrai objet 3D éditable</em>, plutôt qu’une image qui trompe une seule caméra ?</p><div class="grid"><div class="card span4"><div class="number">20+1</div><h3>surface ModelAPI</h3><p>20 opérations de géométrie/structure, plus <code>params</code>. Le modèle ne contrôle ni caméra, ni rendu, ni réseau.</p></div><div class="card span4"><div class="number">25</div><h3>vues par artefact</h3><p>Clay, beauty, angles cachés, dessous, silhouettes, normales, profondeur et wireframe.</p></div><div class="card span4"><div class="number">0.2953</div><h3>écart-type des rewards</h3><p>Le groupe n’est ni uniforme ni saturé : il contient un signal relatif exploitable par GRPO.</p></div><div class="card span8"><h3>La cible d’apprentissage</h3><p class="callout">« Produire une construction 3D exécutable, paramétrique, correcte depuis des vues inconnues, structurellement sensée, exportable et modifiable sans dégâts collatéraux. »</p></div><div class="card span4"><h3>État réel</h3><p><span class="status used">Validé</span> pipeline complet, tests, GLB, reward, OpenRouter live, update CPU + CUDA.</p><p><span class="status notdone">Non fait</span> campagne SFT+GRPO longue et convergence d’un modèle de production.</p></div></div></section>
<section id="chronologie" class="section"><h2>Deux sessions, dans l’ordre</h2><p class="intro">Les journaux bruts ont été relus chronologiquement. La première session construit la tranche verticale. La seconde reprend la liste de lacunes, élargit les contrats et clôt la vérification.</p><div class="timeline"><div class="event"><time>13:38 UTC · session 01a033fe…</time><b>Départ : traduire l’idée en architecture exécutable</b><span>Inventaire du runtime, choix TypeScript/Three.js, Docker, Chromium, OpenRouter et RunPod.</span></div><div class="event"><time>13:45–14:30</time><b>Première verticale : ModelAPI, AST, sandbox, GLB, evaluator</b><span>L’API restreinte et la frontière de confiance existent. Les premières vues et les premiers tests détectent déjà les faux objets plans.</span></div><div class="event"><time>14:30–16:05</time><b>Pipeline initial, références, juge pairwise, training natif</b><span>Le système sait constituer un groupe, scorer, écrire des rollouts et effectuer une mise à jour locale. Mais l’évidence visuelle et les rewards restent trop étroits.</span></div><div class="event"><time>16:14</time><b>Arrêt administratif, pas technique</b><span>L’ancien goal avait un plafond arbitraire de 250 000 tokens et passe en <code>budget_limited</code>. Les fichiers restent intacts ; les manques sont consignés dans TODO et QUEUED_FOLLOWUP.</span></div><div class="event"><time>16:15 UTC · session 01a0348e…</time><b>Continuation sans plafond</b><span>Audit du travail existant, puis plan de finition en huit blocs. Le socle est conservé et les contrats sont étendus.</span></div><div class="event"><time>16:30–17:05</time><b>Passage au bundle 25 vues et reward orthogonale</b><span>Ajout des vues clay/beauty/hidden/diagnostics, spécification privée, contact graph, edit tests, corrélations, stages et suite de tests complète.</span></div><div class="event"><time>17:05–17:20</time><b>Incidents récupérés</b><span>Vitest comptait les copies compilées ; Docker manquait d’espace ; deux premiers essais RunPod ont buté sur le champ SSH puis PEP 668. Chaque pod a été supprimé et chaque cause corrigée.</span></div><div class="event"><time>17:20–17:29</time><b>Preuves finales</b><span>Smoke complet, update local, initialisation TRL, appel Gemini live, update CUDA sur L4, récupération du checkpoint, suppression du pod et ledger final.</span></div></div></section>
<section id="architecture" class="section"><h2>Architecture : où passe la confiance ?</h2><p class="intro">Le principe décisif est la séparation. Le programme candidat ne voit ni les tests privés, ni les caméras cachées, ni les références. Son GLB est rechargé dans un autre processus.</p><div class="diagram"><svg viewBox="0 0 1080 360" role="img" aria-label="Flux complet de génération et évaluation"><defs><marker id="a" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0L8 4L0 8z" fill="#627068"/></marker></defs><g fill="none" stroke="#627068" stroke-width="2" marker-end="url(#a)"><path d="M155 130H235"/><path d="M365 130H445"/><path d="M575 130H655"/><path d="M785 130H865"/><path d="M930 180V250H745"/><path d="M655 285H575"/><path d="M445 285H365"/></g><g font-family="system-ui" text-anchor="middle"><g fill="#fffdf7" stroke="#d8d7ca"><rect x="25" y="85" width="130" height="90" rx="10"/><rect x="235" y="85" width="130" height="90" rx="10"/><rect x="445" y="85" width="130" height="90" rx="10"/><rect x="655" y="85" width="130" height="90" rx="10"/><rect x="865" y="85" width="170" height="90" rx="10"/><rect x="575" y="245" width="170" height="80" rx="10"/><rect x="235" y="245" width="130" height="80" rx="10"/></g><g fill="#17211d" font-size="14" font-weight="700"><text x="90" y="122">Prompt public</text><text x="90" y="145">+ 4–8 programmes</text><text x="300" y="122">AST allowlist</text><text x="300" y="145">contrat build(m)</text><text x="510" y="115">Docker</text><text x="510" y="139">réseau coupé</text><text x="510" y="160">root read-only</text><text x="720" y="122">Export GLB</text><text x="720" y="145">+ manifeste</text><text x="950" y="115">Nouveau process</text><text x="950" y="140">vues cachées + tests</text><text x="950" y="160">+ edits</text><text x="660" y="277">Reward 35/25/20/15/5</text><text x="660" y="300">+ matrice corrélation</text><text x="300" y="277">GRPO</text><text x="300" y="300">update relatif</text></g></g></svg></div><div class="grid" style="margin-top:18px"><div class="card span6"><h3>Dans la sandbox</h3><p>Seulement <code>candidateId</code>, le source et les overrides de paramètres. Exécution bornée, utilisateur non privilégié, réseau désactivé, filesystem en lecture seule.</p></div><div class="card span6"><h3>Sur l’hôte privé</h3><p>Spécification cachée, caméras, références, tests de contact et d’édition. Ces données ne sont jamais montées dans le conteneur candidat.</p></div></div></section>
<section id="modeles" class="section"><h2>Qui fait quoi ? Tous les modèles, sans ambiguïté</h2><p class="intro">Le mot « modèle » recouvre cinq rôles différents ici. Le smoke final n’a pas été généré ni jugé live par les mêmes systèmes que le chemin de production configuré.</p><div class="model-table"><div class="head">Nom</div><div class="head">Statut</div><div class="head">Rôle exact</div><div><b>gpt-5.6-sol</b></div><div><span class="status used">utilisé</span></div><div>Agent Codex qui a écrit, testé et réparé le dépôt pendant les deux sessions. Ce n’est ni le policy model entraîné ni le judge de reward.</div><div><b>Fixtures TypeScript</b></div><div><span class="status used">smoke final</span></div><div>Les 4 candidats excellent/good/pedestal/billboard ont été lus depuis des fichiers déterministes. Donc aucun sampler LLM n’a généré ces quatre candidats lors du smoke final.</div><div><b>openai/gpt-4.1-mini</b></div><div><span class="status configured">sampler par défaut</span></div><div>Valeur par défaut de l’<code>OpenRouterGroupSampler</code> pour générer des programmes en ligne. Implémenté et configurable, mais non utilisé pour les quatre fixtures du smoke final.</div><div><b>DeterministicPairwiseJudge</b></div><div><span class="status used">scores smoke</span></div><div><b>C’est lui qui produit le composant pairwise des quatre scores 0.9342 / 0.9359 / 0.5447 / 0.2307.</b> Il combine évidence sémantique, validité, volumétrie et edits. C’est un test double reproductible, pas un VLM.</div><div><b>google/gemini-2.5-flash</b></div><div><span class="status separate">test live séparé</span></div><div>Juge multimodal OpenRouter validé sur une comparaison candidate excellent ↔ référence craftsman. Request ID : <code>gen-1787591814-oBYfdhessH05rFtueOy6</code>. Cet appel ne remplace pas rétroactivement les scores smoke.</div><div><b>sshleifer/tiny-gpt2</b></div><div><span class="status used">preuve d’update</span></div><div>Petit policy model utilisé pour démontrer une vraie modification des poids, une fois en CPU local puis une fois en CUDA sur NVIDIA L4. Choisi pour la vérification, pas pour la qualité 3D.</div><div><b>Qwen/Qwen2.5-Coder-1.5B-Instruct</b></div><div><span class="status configured">production example</span></div><div>Exemple de policy model dans la configuration RunPod/TRL/vLLM de production. Le pipeline est câblé, mais ce Qwen n’a pas été entraîné jusqu’à convergence dans ce projet.</div></div><div class="warning" style="margin-top:22px"><h3>La réponse directe à « quel judge model ? »</h3><p><b>Pour les quatre rewards publiées : aucun VLM, c’est <code>DeterministicPairwiseJudge</code>.</b> Pour la validation live séparée via OpenRouter : <b><code>google/gemini-2.5-flash</code></b>. En production, le judge OpenRouter est sélectionnable par variable d’environnement.</p></div></section>
<section id="evaluation" class="section"><h2>Comment un programme devient une reward</h2><div class="grid"><div class="card span6"><h3>Le contrat de sortie</h3><div class="code">export function build(m: ModelAPI) {
const p = m.params({ seatWidth: 2.2, legHeight: 2.6 });
const seat = m.roundedBox({ name: "seat", ... });
const legs = m.array({ name: "legs", ... });
return m.group("stool", [seat, legs]);
}</div><p class="small">Une seule fonction synchrone. Noms sémantiques et paramètres conservés dans le manifeste.</p></div><div class="card span6"><h3>Les triches refusées</h3><p>ShaderMaterial, sprites, canvas/data textures, imports dynamiques, fetch/XHR/WebSocket, assets externes, loaders/URLs, caméras ou renderers candidats, post-processing et branchement selon la caméra d’évaluation.</p><p>Le billboard du smoke montre pourquoi : joli de face, inexistant de profil.</p></div><div class="card span12"><h3>Reward après hard gate</h3><div class="reward"><span></span><span></span><span></span><span></span><span></span></div><div class="legend"><b>35% spécification cachée</b><b>25% pairwise multi-vues</b><b>20% géométrie</b><b>15% éditabilité</b><b>5% efficacité</b></div></div><div class="card span6"><h3>Tests cachés de structure</h3><p>Comptage de pièces, contacts attendus, relations spatiales, matériaux/couleurs, ratios de dimensions, bounds globaux et parties posées au sol.</p></div><div class="card span6"><h3>Tests contrefactuels</h3><p><code>seatWidth ×1.25</code>, <code>seatHeight ×0.8</code>, <code>seatTilt +10°</code>, matériau du siège → plastique bleu. Le bon élément doit changer ; les autres doivent rester stables ; les contacts doivent survivre.</p></div></div></section>
<section id="resultats" class="section"><h2>Résultats réels, image par image</h2><p class="intro">Chaque grande grille ci-dessous provient du smoke final. Elle combine les suites de vues qui empêchent un objet de cacher ses défauts derrière une caméra flatteuse.</p><div class="grid"><div class="card span6 result"><div><div class="score">0.9342</div><span class="status used">passe</span></div><p><b>Excellent.</b> Spécification 1.0, géométrie 1.0, éditabilité 1.0. Siège volumétrique, quatre pieds posés, traverses cohérentes.</p></div><div class="card span6 result"><div><div class="score">0.9359</div><span class="status used">passe</span></div><p><b>Good.</b> Plus simple et légèrement mieux classé par la combinaison exacte des composantes. Structure et edits passent.</p></div><div class="card span6 result"><div><div class="score">0.5447</div><span class="status separate">mismatch</span></div><p><b>Pedestal.</b> Vrai volume exportable, mais mauvaise structure : le piédestal ne respecte pas les quatre pieds et les traverses attendues.</p></div><div class="card span6 result"><div><div class="score">0.2307</div><span class="status notdone">anti-triche</span></div><p><b>Billboard.</b> Une surface plane qui s’effondre vue de côté. Le bundle révèle exactement la triche qu’un screenshot unique laisserait passer.</p></div></div><div class="gallery" style="margin-top:22px"><figure class="shot wide"><img src="assets/excellent-bundle.png" alt="Bundle 25 vues du candidat excellent"><figcaption><b>Excellent — bundle complet.</b> Clay, beauty, hidden, dessous, silhouettes, normales, profondeur, wireframe.</figcaption></figure><figure class="shot"><img src="assets/good-bundle.png" alt="Bundle du candidat good"><figcaption><b>Good.</b> Une construction plus légère mais cohérente.</figcaption></figure><figure class="shot"><img src="assets/pedestal-bundle.png" alt="Bundle du candidat pedestal"><figcaption><b>Pedestal.</b> Géométriquement valide, sémantiquement incorrect.</figcaption></figure><figure class="shot wide"><img src="assets/billboard-bundle.png" alt="Bundle du billboard détecté"><figcaption><b>Billboard détecté.</b> Les vues latérales et cartes diagnostiques exposent la planéité.</figcaption></figure><figure class="shot"><img src="assets/reference-craftsman.png" alt="Référence craftsman"><figcaption>Référence humaine « craftsman » utilisée dans les comparaisons.</figcaption></figure><figure class="shot"><img src="assets/reference-simple.png" alt="Référence simple"><figcaption>Deuxième référence approuvée, plus simple.</figcaption></figure><figure class="shot"><img src="assets/excellent-clay.png" alt="Rendu clay"><figcaption><b>Clay :</b> juge la forme sans camouflage de matériau.</figcaption></figure><figure class="shot"><img src="assets/excellent-beauty.png" alt="Rendu beauty"><figcaption><b>Beauty :</b> juge les choix d’apparence.</figcaption></figure><figure class="shot"><img src="assets/excellent-normal.png" alt="Carte de normales"><figcaption><b>Normales :</b> inspecte l’orientation des surfaces.</figcaption></figure><figure class="shot"><img src="assets/excellent-depth.png" alt="Carte de profondeur"><figcaption><b>Profondeur :</b> rend la volumétrie mesurable.</figcaption></figure></div></section>
<section id="entrainement" class="section"><h2>Du test à l’entraînement</h2><div class="diagram"><svg viewBox="0 0 1080 270" role="img" aria-label="Stages 0 à 5"><defs><marker id="b" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0L8 4L0 8z" fill="#627068"/></marker></defs><g fill="none" stroke="#627068" stroke-width="2" marker-end="url(#b)"><path d="M165 128H205"/><path d="M335 128H375"/><path d="M505 128H545"/><path d="M675 128H715"/><path d="M845 128H885"/></g><g font-family="system-ui" text-anchor="middle"><g stroke="#d8d7ca" fill="#fffdf7"><rect x="35" y="70" width="130" height="116" rx="10"/><rect x="205" y="70" width="130" height="116" rx="10"/><rect x="375" y="70" width="130" height="116" rx="10"/><rect x="545" y="70" width="130" height="116" rx="10"/><rect x="715" y="70" width="130" height="116" rx="10"/><rect x="885" y="70" width="150" height="116" rx="10"/></g><g fill="#17211d"><text x="100" y="102" font-weight="800">Stage 0</text><text x="100" y="127" font-size="13">valider le judge</text><text x="100" y="148" font-size="13">avec humains</text><text x="270" y="102" font-weight="800">Stage 1</text><text x="270" y="127" font-size="13">SFT syntaxe</text><text x="270" y="148" font-size="13">+ représentation</text><text x="440" y="102" font-weight="800">Stage 2</text><text x="440" y="127" font-size="13">GRPO objets</text><text x="440" y="148" font-size="13">one-shot</text><text x="610" y="102" font-weight="800">Stage 3</text><text x="610" y="127" font-size="13">repair + feedback</text><text x="610" y="148" font-size="13">1–2 révisions</text><text x="780" y="102" font-weight="800">Stage 4</text><text x="780" y="127" font-size="13">édition</text><text x="780" y="148" font-size="13">+ préservation</text><text x="960" y="102" font-weight="800">Stage 5</text><text x="960" y="127" font-size="13">scènes / monde</text><text x="960" y="148" font-size="13">encore gated</text></g></g></svg></div><div class="grid" style="margin-top:20px"><div class="card span4"><h3>Preuve locale</h3><p><code>tiny-gpt2</code>, CPU, une étape. Checksum <code>ba0dbc… → 7cf978…</code>. Gradient fini, paramètres changés.</p></div><div class="card span4"><h3>TRL + vLLM</h3><p>TRL 1.10.0 initialise réellement <code>GRPOTrainer</code>, <code>GRPOConfig</code>, Dataset prompt-only et reward HTTP authentifiée. C’est du wiring, pas une convergence.</p></div><div class="card span4"><h3>Preuve RunPod</h3><p>NVIDIA L4, CUDA, une étape sur le vrai rollout. <code>ba0dbc… → 1e24c5…</code>. Checkpoint récupéré ; pod <code>oxzeba7ql863gm</code> supprimé.</p></div></div></section>
<section id="duree" class="section"><h2>Combien de temps pour le modèle final ?</h2><p class="intro">L’estimation doit séparer quatre horloges : calcul, constitution des données, notation humaine et itérations de recherche. Le renderer est mesuré ; la convergence ne l’est pas encore.</p><div class="grid"><div class="card span4"><div class="number">≈113 s</div><h3>smoke end-to-end</h3><p>4 candidats + 2 références, chacun avec 25 vues, edits et rapports. C’est la mesure de pipeline observée, pas le temps d’une update GPU.</p></div><div class="card span4"><div class="number">6.5–7 s</div><h3>rendu pur / bundle</h3><p>Somme des 25 renders pour les bons candidats : 6.92–6.98 s. Les autres bundles sont du même ordre.</p></div><div class="card span4"><div class="number">3,000 + 500×6</div><h3>cible pilote</h3><p>3 000 exemples SFT validés ; 500 prompts RL ; 6 rollouts par prompt ; 100–200 prompts notés en Stage 0.</p></div></div><h3 style="margin-top:34px">Calendrier réaliste si l’infrastructure et les annotateurs sont disponibles</h3><div class="est"><div class="phase"><span class="kicker">Premier signal</span><strong>5–7 jours</strong><p>Nettoyer/générer le SFT, lancer un premier SFT, établir Stage 0, faire une courte boucle GRPO et produire un modèle testable.</p></div><div class="phase"><span class="kicker">Pilote robuste</span><strong>10–20 jours</strong><p>Plusieurs cycles reward ↔ humains ↔ entraînement, corrections de reward hacking, held-out edits, reproductibilité.</p></div><div class="phase"><span class="kicker">Formulation simple</span><strong>2–3 semaines</strong><p>Ordre de grandeur à communiquer pour un bon modèle du domaine meubles/props — si les données humaines avancent en parallèle.</p></div><div class="phase"><span class="kicker">Généralisation</span><strong>1–3 mois</strong><p>Plus de catégories, scènes et comportements ; montée en capacité du policy model ; études humaines plus larges ; Stage 5.</p></div></div><div class="warning" style="margin-top:24px"><h3>Pourquoi le calcul pur n’est pas le goulot unique</h3><p>À 113 s par groupe séquentiel, 500 groupes représenteraient ≈15,7 h de pipeline brut. Mais 6 rollouts × 500 prompts, plusieurs époques, appels sampler/judge, retries, SFT et évaluations held-out déplacent le total. Le temps humain pour 100–200 prompts Stage 0 et l’analyse des « high-reward failures » peut dominer. Le vrai délai est donc un calendrier d’itération, pas une simple multiplication GPU.</p></div></section>
<section id="limites" class="section"><h2>Ce que les preuves autorisent — et interdisent — de dire</h2><div class="grid"><div class="card span6"><h3>On peut affirmer</h3><p>Le pipeline restreint le code, exporte/recharge des GLB, produit les bundles, teste structure et edits, calcule les rewards, expose TRL/vLLM, appelle Gemini via OpenRouter, et modifie réellement des poids sur CPU et L4.</p></div><div class="card span6"><h3>On ne peut pas affirmer</h3><p>Que Qwen est entraîné, que le système converge, que le judge est validé sur 100–200 prompts, que GEPA a tourné live, ou que scènes/physique/UI avancées fonctionnent.</p></div><div class="card span12"><h3>Limites consignées dans le ledger</h3><p>Fallback process non sécurisé ; seulement 8 comparaisons humaines Stage 0 (accord 0.875) au lieu de la cible 100–200 ; GEPA = adaptateur local ; updates = une seule étape ; Stage 5 = squelette gated.</p></div><div class="card span12"><h3>Preuves primaires</h3><p class="proof">artifacts/verification/final-ledger.json<br>docs/verification.md<br>artifacts/smoke/verification/reward-report.json<br>artifacts/openrouter-check/report.json<br>artifacts/training/local-one-step/training-report.json<br>artifacts/runpod/final-one-step/training-report.json</p></div></div></section>
<footer class="section footer"><p><b>Méthode.</b> Synthèse reconstruite à partir des deux journaux Codex complets (01a033fe… puis 01a0348e…), des rollouts archivés, du code, des configurations et des artefacts machine. Les images sont les sorties réelles du smoke final. Aucun résultat de convergence n’a été inventé.</p></footer>
</main>
</body></html>