Skip to content

Commit 848391d

Browse files
committed
chore: Scripts d'amélioration et validation notebooks - Phase 21
1 parent c631a3e commit 848391d

File tree

2 files changed

+920
-0
lines changed

2 files changed

+920
-0
lines changed
Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Script d'amélioration du notebook Forge SD XL Turbo
4+
Phase 21 - Itérations notebooks
5+
6+
Ajoute 3 cellules pédagogiques à des positions spécifiques:
7+
1. Introduction Visuelle (index 2)
8+
2. Exemples Avancés (index 10 après modification)
9+
3. Tips & Troubleshooting (index 12 après modification)
10+
"""
11+
12+
import json
13+
import sys
14+
from pathlib import Path
15+
from typing import Dict, Any, List
16+
17+
# Chemin notebook relatif au script
18+
NOTEBOOK_PATH = Path(__file__).parent.parent / "MyIA.AI.Notebooks" / "GenAI" / "01-Images-Foundation" / "01-4-Forge-SD-XL-Turbo.ipynb"
19+
20+
def create_code_cell(source: List[str]) -> Dict[str, Any]:
21+
"""Crée une cellule code avec métadonnées standards"""
22+
return {
23+
"cell_type": "code",
24+
"execution_count": None,
25+
"metadata": {},
26+
"outputs": [],
27+
"source": source
28+
}
29+
30+
def create_markdown_cell(source: List[str]) -> Dict[str, Any]:
31+
"""Crée une cellule markdown avec métadonnées standards"""
32+
return {
33+
"cell_type": "markdown",
34+
"metadata": {},
35+
"source": source
36+
}
37+
38+
# --- CELLULE 1: Introduction Visuelle (index 2) ---
39+
CELL_INTRO_VISUELLE = create_code_cell([
40+
'"""\n',
41+
'Vérification du statut de l\'API et affichage d\'une bannière visuelle\n',
42+
'"""\n',
43+
'\n',
44+
'# Test de connectivité API\n',
45+
'try:\n',
46+
' response = requests.get(f"{API_BASE_URL}/sdapi/v1/options", timeout=10)\n',
47+
' response.raise_for_status()\n',
48+
' \n',
49+
' # Bannière de succès\n',
50+
' from IPython.display import display, HTML\n',
51+
' \n',
52+
' banner_html = """\n',
53+
' <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n',
54+
' padding: 20px;\n',
55+
' border-radius: 10px;\n',
56+
' text-align: center;\n',
57+
' color: white;\n',
58+
' font-family: Arial, sans-serif;\n',
59+
' box-shadow: 0 4px 6px rgba(0,0,0,0.1);">\n',
60+
' <h1 style="margin: 0; font-size: 28px;">🎨 Stable Diffusion Forge</h1>\n',
61+
' <h2 style="margin: 10px 0 0 0; font-weight: normal; font-size: 18px;">SD XL Turbo API</h2>\n',
62+
' <p style="margin: 15px 0 0 0; font-size: 14px; opacity: 0.9;">✅ API Connectée et Opérationnelle</p>\n',
63+
' <p style="margin: 5px 0 0 0; font-size: 12px; opacity: 0.7;">Génération rapide 4-steps • Performance ~18s</p>\n',
64+
' </div>\n',
65+
' """\n',
66+
' \n',
67+
' display(HTML(banner_html))\n',
68+
' print("\\n✅ API Forge accessible et prête à l\'utilisation")\n',
69+
' \n',
70+
'except requests.exceptions.RequestException as e:\n',
71+
' print(f"⚠️ Avertissement: Impossible de contacter l\'API Forge")\n',
72+
' print(f" Erreur: {e}")\n',
73+
' print(f" Vérifiez que l\'API est accessible à {API_BASE_URL}")\n'
74+
])
75+
76+
# --- CELLULE 2: Exemples Avancés (index 10 après ajout cellule 1) ---
77+
CELL_EXEMPLES_AVANCES = create_code_cell([
78+
'"""\n',
79+
'Techniques Avancées de Génération\n',
80+
'\n',
81+
'Ce bloc explore des techniques avancées pour améliorer vos générations:\n',
82+
'1. Reproductibilité avec seed fixe\n',
83+
'2. Exploration créative avec seeds aléatoires\n',
84+
'3. Génération batch optimisée\n',
85+
'"""\n',
86+
'\n',
87+
'import time\n',
88+
'import random\n',
89+
'\n',
90+
'# --- Technique 1: Reproductibilité avec Seed Fixe ---\n',
91+
'print("🎯 Technique 1: Génération Reproductible (seed fixe)\\n")\n',
92+
'\n',
93+
'def generate_with_seed(prompt: str, seed: int):\n',
94+
' """Génère une image avec seed fixe pour reproductibilité"""\n',
95+
' payload = {\n',
96+
' "prompt": prompt,\n',
97+
' "steps": 4,\n',
98+
' "cfg_scale": 2.0,\n',
99+
' "width": 512,\n',
100+
' "height": 512,\n',
101+
' "seed": seed,\n',
102+
' "sampler_name": "DPM++ 2M"\n',
103+
' }\n',
104+
' \n',
105+
' try:\n',
106+
' response = requests.post(\n',
107+
' f"{API_BASE_URL}/sdapi/v1/txt2img",\n',
108+
' json=payload,\n',
109+
' timeout=TIMEOUT\n',
110+
' )\n',
111+
' response.raise_for_status()\n',
112+
' result = response.json()\n',
113+
' \n',
114+
' if "images" in result and len(result["images"]) > 0:\n',
115+
' image_data = base64.b64decode(result["images"][0])\n',
116+
' return Image.open(BytesIO(image_data))\n',
117+
' except Exception as e:\n',
118+
' print(f"❌ Erreur: {e}")\n',
119+
' return None\n',
120+
'\n',
121+
'# Test avec seed fixe (42)\n',
122+
'prompt_seed = "a mystical forest with glowing mushrooms"\n',
123+
'print(f"Prompt: \\"{prompt_seed}\\"\\nSeed: 42\\n")\n',
124+
'\n',
125+
'img_seed = generate_with_seed(prompt_seed, seed=42)\n',
126+
'if img_seed:\n',
127+
' plt.figure(figsize=(6, 6))\n',
128+
' plt.imshow(img_seed)\n',
129+
' plt.axis(\'off\')\n',
130+
' plt.title("Génération Reproductible (seed=42)", fontsize=11)\n',
131+
' plt.tight_layout()\n',
132+
' plt.show()\n',
133+
' print("💡 Astuce: Réexécutez cette cellule → même résultat garanti\\n")\n',
134+
'\n',
135+
'# --- Technique 2: Exploration Créative (seeds aléatoires) ---\n',
136+
'print("\\n🎲 Technique 2: Exploration Créative (seeds aléatoires)\\n")\n',
137+
'\n',
138+
'prompt_creative = "a steampunk airship in the clouds"\n',
139+
'seeds_random = [random.randint(1, 999999) for _ in range(3)]\n',
140+
'\n',
141+
'print(f"Génération de 3 variations avec seeds aléatoires...\\n")\n',
142+
'images_seeds = []\n',
143+
'\n',
144+
'for i, seed in enumerate(seeds_random, 1):\n',
145+
' print(f"{i}. Seed: {seed}")\n',
146+
' img = generate_with_seed(prompt_creative, seed)\n',
147+
' if img:\n',
148+
' images_seeds.append((img, seed))\n',
149+
'\n',
150+
'# Affichage grille\n',
151+
'if len(images_seeds) == 3:\n',
152+
' fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n',
153+
' for ax, (img, seed) in zip(axes, images_seeds):\n',
154+
' ax.imshow(img)\n',
155+
' ax.axis(\'off\')\n',
156+
' ax.set_title(f"Seed: {seed}", fontsize=9)\n',
157+
' plt.suptitle("Exploration Créative: Même Prompt, Seeds Différents", fontsize=12)\n',
158+
' plt.tight_layout()\n',
159+
' plt.show()\n',
160+
' print("\\n💡 Observation: Même prompt → résultats visuels différents\\n")\n',
161+
'\n',
162+
'# --- Technique 3: Batch Generation Optimisé ---\n',
163+
'print("\\n⚡ Technique 3: Génération Batch Optimisée\\n")\n',
164+
'\n',
165+
'def generer_batch_optimise(prompts_list: List[str]):\n',
166+
' """Génère plusieurs images en batch avec gestion erreurs"""\n',
167+
' results = []\n',
168+
' start_time = time.time()\n',
169+
' \n',
170+
' for i, prompt in enumerate(prompts_list, 1):\n',
171+
' print(f"[{i}/{len(prompts_list)}] {prompt[:50]}...")\n',
172+
' img = generate_image_forge(\n',
173+
' prompt=prompt,\n',
174+
' steps=4,\n',
175+
' cfg_scale=2.0,\n',
176+
' width=512,\n',
177+
' height=512\n',
178+
' )\n',
179+
' if img:\n',
180+
' results.append((prompt, img))\n',
181+
' \n',
182+
' elapsed = time.time() - start_time\n',
183+
' print(f"\\n✅ Batch terminé: {len(results)}/{len(prompts_list)} succès en {elapsed:.1f}s")\n',
184+
' return results\n',
185+
'\n',
186+
'# Batch de 3 prompts thématiques\n',
187+
'batch_prompts = [\n',
188+
' "a futuristic city at sunset, cyberpunk style",\n',
189+
' "a peaceful zen garden with cherry blossoms",\n',
190+
' "an underwater scene with colorful coral reefs"\n',
191+
']\n',
192+
'\n',
193+
'resultats_batch = generer_batch_optimise(batch_prompts)\n',
194+
'\n',
195+
'# Affichage résultats batch\n',
196+
'if len(resultats_batch) >= 2:\n',
197+
' n_cols = len(resultats_batch)\n',
198+
' fig, axes = plt.subplots(1, n_cols, figsize=(5*n_cols, 5))\n',
199+
' if n_cols == 1:\n',
200+
' axes = [axes]\n',
201+
' \n',
202+
' for ax, (prompt, img) in zip(axes, resultats_batch):\n',
203+
' ax.imshow(img)\n',
204+
' ax.axis(\'off\')\n',
205+
' title = prompt if len(prompt) <= 35 else prompt[:32] + "..."\n',
206+
' ax.set_title(title, fontsize=9)\n',
207+
' \n',
208+
' plt.suptitle("Génération Batch Thématique", fontsize=12, y=1.02)\n',
209+
' plt.tight_layout()\n',
210+
' plt.show()\n',
211+
' \n',
212+
' print("\\n💡 Usage: Idéal pour prototypage rapide de concepts multiples")\n'
213+
])
214+
215+
# --- CELLULE 3: Tips & Troubleshooting (index 12 après ajout cellules 1+2) ---
216+
CELL_TIPS_TROUBLESHOOTING = create_markdown_cell([
217+
'## 💡 Tips & Troubleshooting\n',
218+
'\n',
219+
'### Erreurs Courantes et Solutions\n',
220+
'\n',
221+
'#### 1. ⏱️ Timeout Error (`requests.exceptions.Timeout`)\n',
222+
'\n',
223+
'**Symptôme**: La requête échoue après 60 secondes\n',
224+
'\n',
225+
'**Causes possibles**:\n',
226+
'- Charge serveur élevée\n',
227+
'- Paramètres non-optimaux (steps > 4)\n',
228+
'- Résolution trop élevée (>768×768)\n',
229+
'\n',
230+
'**Solutions**:\n',
231+
'```python\n',
232+
'# Augmenter timeout si nécessaire\n',
233+
'TIMEOUT = 90 # au lieu de 60\n',
234+
'\n',
235+
'# Vérifier paramètres Turbo\n',
236+
'steps = 4 # ✅ Optimal\n',
237+
'cfg_scale = 2.0 # ✅ Optimal\n',
238+
'width = 512 # ✅ Optimal\n',
239+
'```\n',
240+
'\n',
241+
'#### 2. ❌ Bad Request Error (HTTP 400)\n',
242+
'\n',
243+
'**Symptôme**: `response.status_code == 400`\n',
244+
'\n',
245+
'**Causes possibles**:\n',
246+
'- Payload JSON mal formé\n',
247+
'- Paramètres invalides (sampler inconnu, résolution impaire)\n',
248+
'- Prompt trop long (>77 tokens)\n',
249+
'\n',
250+
'**Solutions**:\n',
251+
'```python\n',
252+
'# Valider payload avant envoi\n',
253+
'import json\n',
254+
'try:\n',
255+
' json.dumps(payload) # Test sérialisation\n',
256+
'except TypeError as e:\n',
257+
' print(f"Payload invalide: {e}")\n',
258+
'\n',
259+
'# Vérifier résolution (multiple de 64)\n',
260+
'width = (width // 64) * 64\n',
261+
'height = (height // 64) * 64\n',
262+
'```\n',
263+
'\n',
264+
'#### 3. 🖼️ Image Non Générée (payload vide)\n',
265+
'\n',
266+
'**Symptôme**: `result["images"]` est vide ou absent\n',
267+
'\n',
268+
'**Causes possibles**:\n',
269+
'- Prompt contient mots-clés bloqués (NSFW filter)\n',
270+
'- Modèle non chargé côté serveur\n',
271+
'\n',
272+
'**Solutions**:\n',
273+
'```python\n',
274+
'# Vérifier réponse complète\n',
275+
'if "images" not in result:\n',
276+
' print(f"Réponse API: {result}")\n',
277+
' \n',
278+
'# Tester avec prompt simple\n',
279+
'prompt_test = "a landscape" # Minimal, neutre\n',
280+
'```\n',
281+
'\n',
282+
'### 🚀 Tips Performance\n',
283+
'\n',
284+
'#### Optimisation Vitesse\n',
285+
'\n',
286+
'| Action | Gain | Trade-off |\n',
287+
'|--------|------|----------|\n',
288+
'| `steps=4` (vs 20) | **~4× plus rapide** | Légère réduction qualité |\n',
289+
'| `512×512` (vs 768×768) | **~2× plus rapide** | Résolution inférieure |\n',
290+
'| `cfg_scale=2.0` (vs 7.0) | **~1.5× plus rapide** | Moins de guidage |\n',
291+
'\n',
292+
'#### Optimisation Qualité\n',
293+
'\n',
294+
'```python\n',
295+
'# Pour génération finale haute qualité\n',
296+
'image_hq = generate_image_forge(\n',
297+
' prompt=prompt,\n',
298+
' steps=6, # ⚠️ Compromis: +50% temps\n',
299+
' cfg_scale=2.5, # Guidage légèrement renforcé\n',
300+
' width=768,\n',
301+
' height=768\n',
302+
')\n',
303+
'```\n',
304+
'\n',
305+
'### 📚 Ressources Complémentaires\n',
306+
'\n',
307+
'- **Guide Prompts Efficaces**: [Lexica.art](https://lexica.art) (exemples visuels)\n',
308+
'- **Samplers Comparés**: [Stable Diffusion Wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#sampling-method-selection)\n',
309+
'- **Negative Prompts Templates**: [PromptHero](https://prompthero.com/stable-diffusion-negative-prompts)\n',
310+
'\n',
311+
'### 🔗 Liens Utiles\n',
312+
'\n',
313+
'- **Documentation API Complète**: [`GUIDE-APIS-ETUDIANTS.md`](../../../docs/suivis/genai-image/GUIDE-APIS-ETUDIANTS.md)\n',
314+
'- **Notebook Qwen (Édition Images)**: [`01-5-Qwen-Image-Edit.ipynb`](01-5-Qwen-Image-Edit.ipynb)\n',
315+
'- **Status API Temps Réel**: `https://turbo.stable-diffusion-webui-forge.myia.io/sdapi/v1/progress`\n'
316+
])
317+
318+
def main():
319+
"""Fonction principale d'amélioration du notebook"""
320+
321+
print("🔧 Amélioration Notebook Forge SD XL Turbo - Phase 21")
322+
print("=" * 60)
323+
324+
# Lecture notebook
325+
print(f"\n📖 Lecture: {NOTEBOOK_PATH}")
326+
try:
327+
with open(NOTEBOOK_PATH, 'r', encoding='utf-8') as f:
328+
notebook = json.load(f)
329+
except FileNotFoundError:
330+
print(f"❌ Erreur: Fichier introuvable: {NOTEBOOK_PATH}")
331+
return 1
332+
except json.JSONDecodeError as e:
333+
print(f"❌ Erreur parsing JSON: {e}")
334+
return 1
335+
336+
cells = notebook.get('cells', [])
337+
initial_count = len(cells)
338+
print(f" Cellules initiales: {initial_count}")
339+
340+
# Insertion cellules aux positions spécifiées
341+
insertions = [
342+
(2, CELL_INTRO_VISUELLE, "Introduction Visuelle"),
343+
(10 + 1, CELL_EXEMPLES_AVANCES, "Exemples Avancés"), # +1 car cellule 1 déjà insérée
344+
(12 + 2, CELL_TIPS_TROUBLESHOOTING, "Tips & Troubleshooting") # +2 car cellules 1+2 déjà insérées
345+
]
346+
347+
print("\n🔨 Insertion cellules:")
348+
for idx, cell, name in insertions:
349+
cells.insert(idx, cell)
350+
print(f" ✅ [{idx:2d}] {name} ({cell['cell_type']})")
351+
352+
notebook['cells'] = cells
353+
final_count = len(cells)
354+
print(f"\n📊 Résultat: {initial_count}{final_count} cellules (+{final_count - initial_count})")
355+
356+
# Sauvegarde
357+
print(f"\n💾 Sauvegarde: {NOTEBOOK_PATH}")
358+
try:
359+
with open(NOTEBOOK_PATH, 'w', encoding='utf-8') as f:
360+
json.dump(notebook, f, ensure_ascii=False, indent=1)
361+
print(" ✅ Notebook sauvegardé avec succès")
362+
except Exception as e:
363+
print(f" ❌ Erreur sauvegarde: {e}")
364+
return 1
365+
366+
print("\n✅ Amélioration terminée avec succès!")
367+
print("\n📝 Modifications appliquées:")
368+
print(" • [Index 2] Introduction Visuelle + Vérification API")
369+
print(" • [Index 11] Techniques Avancées (seed, batch, exploration)")
370+
print(" • [Index 14] Tips & Troubleshooting complets")
371+
372+
return 0
373+
374+
if __name__ == "__main__":
375+
sys.exit(main())

0 commit comments

Comments
 (0)