🎓 Phase 8 : Multimodal, Spécialisation & Leadership

Formation IA Générative Open Source

0%

📚 Plan de Formation (24 Leçons)

Module 8.1 : IA Multimodale (Leçons 0-7)

Module 8.2 : Spécialisation & Projets (Leçons 8-15)

Module 8.3 : Éthique, Stratégie & Leadership (Leçons 16-23)

Leçon 0 : Vision-Language Models

💡 Conseil du Mentor Le multimodal est l'avenir - les modèles qui comprennent texte+image+audio seront la norme d'ici 2027. Commencez à les maîtriser maintenant.

Introduction aux Vision-Language Models (VLM)

Les Vision-Language Models combinent la compréhension du texte et des images pour accomplir des tâches complexes comme la description d'images, le VQA (Visual Question Answering), et le raisonnement visuel.

Architecture CLIP (Contrastive Language-Image Pre-training)

CLIP d'OpenAI est le fondement de nombreux VLM modernes. Il apprend à associer images et textes dans un espace latent commun.

┌─────────────────────────────────────────────────┐ │ Architecture CLIP │ ├─────────────────────────────────────────────────┤ │ │ │ Image Encoder Text Encoder │ │ (Vision Transformer) (Transformer) │ │ │ │ │ │ ▼ ▼ │ │ Image Embedding Text Embedding │ │ (512-d) (512-d) │ │ │ │ │ │ └──────────┬───────────┘ │ │ ▼ │ │ Cosine Similarity │ │ (Contrastive Loss) │ │ │ │ Training: 400M image-text pairs │ └─────────────────────────────────────────────────┘

Principaux VLM Open Source

1. LLaVA (Large Language and Vision Assistant)

LLaVA connecte CLIP à un LLM (Vicuna/Llama) via un projecteur linéaire simple.

2. Qwen-VL (Alibaba)

Famille VL de Qwen avec support multi-images et haute résolution.

3. InternVL (OpenGVLab)

VLM haute performance avec architecture innovante.

4. CogVLM (Zhipu AI)

VLM avec grounding visuel et compréhension spatiale.

Code Pratique : Inférence VLM avec LLaVA

# Installation
pip install transformers pillow torch accelerate

# Script d'inférence LLaVA
from transformers import LlavaNextProcessor, LlavaNextForConditionalGeneration
from PIL import Image
import torch

class VLMInference:
    def __init__(self, model_name="llava-hf/llava-v1.6-mistral-7b-hf"):
        """Initialise le modèle VLM"""
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        print(f"🚀 Chargement de {model_name}...")

        self.processor = LlavaNextProcessor.from_pretrained(model_name)
        self.model = LlavaNextForConditionalGeneration.from_pretrained(
            model_name,
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
            device_map="auto"
        )

    def query_image(self, image_path, prompt):
        """
        Pose une question sur une image

        Args:
            image_path: Chemin vers l'image
            prompt: Question ou instruction

        Returns:
            Réponse du modèle
        """
        # Charger l'image
        image = Image.open(image_path).convert("RGB")

        # Format LLaVA conversation
        conversation = [
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": prompt},
                    {"type": "image"}
                ]
            }
        ]

        # Préparation des inputs
        text_prompt = self.processor.apply_chat_template(
            conversation, add_generation_prompt=True
        )

        inputs = self.processor(
            text=text_prompt,
            images=image,
            return_tensors="pt"
        ).to(self.device)

        # Génération
        with torch.no_grad():
            output = self.model.generate(
                **inputs,
                max_new_tokens=512,
                do_sample=False
            )

        # Décodage
        response = self.processor.decode(
            output[0],
            skip_special_tokens=True
        )

        # Extraire la réponse (après ASSISTANT:)
        if "ASSISTANT:" in response:
            response = response.split("ASSISTANT:")[-1].strip()

        return response

# Utilisation
vlm = VLMInference()

# Exemple 1: Description d'image
description = vlm.query_image(
    "photo.jpg",
    "Décris cette image en détail."
)
print("📝 Description:", description)

# Exemple 2: Visual Question Answering
answer = vlm.query_image(
    "chart.png",
    "Combien de personnes sont présentes dans l'image?"
)
print("❓ Réponse:", answer)

# Exemple 3: OCR et extraction d'info
text = vlm.query_image(
    "document.jpg",
    "Extrais tout le texte visible dans ce document."
)
print("📄 Texte extrait:", text)

# Exemple 4: Raisonnement visuel
reasoning = vlm.query_image(
    "scene.jpg",
    "Que se passe-t-il dans cette scène? Explique le contexte."
)
print("🧠 Analyse:", reasoning)

Cas d'Usage des VLM

Cas d'Usage Description Modèle Recommandé
Image Captioning Génération de descriptions d'images LLaVA-1.6, InternVL
VQA Répondre à des questions sur des images Qwen2-VL, CogVLM2
OCR Intelligent Extraction et compréhension de texte Qwen2-VL, CogVLM
Visual Grounding Localiser des objets dans une image CogVLM2, Qwen-VL
GUI Understanding Comprendre des interfaces utilisateur CogVLM, InternVL
Document Analysis Analyser des documents complexes Qwen2-VL, InternVL
🔍 Benchmarks VLM Les VLM sont évalués sur plusieurs benchmarks :
  • MMMU : Raisonnement multi-domaine (maths, physique, etc.)
  • MMBench : Compréhension visuelle générale
  • TextVQA : Questions nécessitant lecture de texte
  • DocVQA : Compréhension de documents
  • ChartQA : Compréhension de graphiques

Fine-Tuning d'un VLM

# Fine-tuning LLaVA avec LoRA
from transformers import TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model

# Configuration LoRA (uniquement sur le projecteur et LLM)
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    lora_dropout=0.05,
    bias="none"
)

# Appliquer LoRA
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Sortie: trainable params: 8.4M || all params: 7.2B || trainable%: 0.12%

# Dataset personnalisé (format: image + question + answer)
from torch.utils.data import Dataset

class VLMDataset(Dataset):
    def __init__(self, data, processor):
        self.data = data  # Liste de dicts: {"image": path, "question": str, "answer": str}
        self.processor = processor

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        item = self.data[idx]
        image = Image.open(item["image"]).convert("RGB")

        conversation = [
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": item["question"]},
                    {"type": "image"}
                ]
            },
            {
                "role": "assistant",
                "content": [{"type": "text", "text": item["answer"]}]
            }
        ]

        text = self.processor.apply_chat_template(conversation)
        inputs = self.processor(text=text, images=image, return_tensors="pt")

        return inputs

# Training
training_args = TrainingArguments(
    output_dir="./llava-finetuned",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    fp16=True,
    save_steps=500,
    logging_steps=100
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset
)

trainer.train()
✅ Points Clés
  • Les VLM combinent vision (CLIP) et langage (LLM) via un projecteur
  • LLaVA est simple et performant, Qwen-VL excelle en multilangue
  • InternVL offre les meilleurs résultats, CogVLM excelle en grounding
  • Fine-tuning possible avec LoRA sur datasets spécifiques
  • Applications : VQA, OCR, document analysis, GUI understanding

Leçon 1 : Génération d'Images

Introduction à la Diffusion

Les modèles de diffusion (Stable Diffusion, FLUX.1, etc.) génèrent des images en apprenant à inverser un processus de bruit progressif.

┌──────────────────────────────────────────────────────┐ │ Processus de Diffusion (Denoising) │ ├──────────────────────────────────────────────────────┤ │ │ │ t=1000 (Bruit pur) t=0 (Image) │ │ 🌫️ 🖼️ │ │ │ ▲ │ │ │ Reverse Process (Denoising) │ │ │ │ ───────────────────────────────────→│ │ │ │ │ │ │ │ U-Net / DiT à chaque timestep │ │ │ │ (conditionné par le prompt) │ │ │ └──────────────────────────────────────┘ │ │ │ │ Latent Space: Opère dans l'espace VAE compressé │ │ (512x512 image → 64x64 latent) │ └──────────────────────────────────────────────────────┘

Stable Diffusion (Stability AI)

Versions Principales

Architecture SDXL

Text Prompt
    │
    ├─→ CLIP ViT-L/14 ────┐
    │                      ├─→ Text Embeddings (Concat)
    └─→ OpenCLIP ViT-G ───┘         │
                                    ▼
    Latent (Random Noise)  ─→  U-Net (2.6B params)
          z₀                    │  Time embedding
                                │  Conditioning
                                ▼
                           Denoised Latent z_t
                                │
                                ▼
                           VAE Decoder
                                │
                                ▼
                          Image 1024x1024

FLUX.1 (Black Forest Labs)

Nouveau 2024 Créé par les fondateurs originaux de Stable Diffusion. Architecture transformer-based révolutionnaire.

Variantes FLUX

Modèle Params Licence Qualité
FLUX.1 [pro] 12B API uniquement SOTA (meilleur qualité)
FLUX.1 [dev] 12B Non-commercial Excellent (distillé de pro)
FLUX.1 [schnell] 12B Apache 2.0 (Open) Très bon (4 steps)
🚀 Avantages FLUX.1
  • Qualité supérieure : Meilleur rendu que SDXL sur texte, mains, anatomie
  • Prompt following : Comprend mieux les prompts complexes
  • Rapidité : schnell génère en 4 steps seulement (vs 25-50 pour SD)
  • Architecture : Flow Matching + Rotary Positional Embeddings

Code Pratique : Pipeline Diffusion avec diffusers

# Installation
pip install diffusers transformers accelerate safetensors

# 1. Stable Diffusion XL
from diffusers import DiffusionPipeline
import torch

class ImageGenerator:
    def __init__(self, model="stabilityai/stable-diffusion-xl-base-1.0"):
        """Initialise le pipeline de génération"""
        self.device = "cuda" if torch.cuda.is_available() else "cpu"

        print(f"🎨 Chargement de {model}...")
        self.pipe = DiffusionPipeline.from_pretrained(
            model,
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
            use_safetensors=True
        )
        self.pipe.to(self.device)

        # Optimisation (optionnel)
        if self.device == "cuda":
            self.pipe.enable_xformers_memory_efficient_attention()

    def generate(
        self,
        prompt,
        negative_prompt="",
        num_images=1,
        steps=30,
        guidance_scale=7.5,
        width=1024,
        height=1024,
        seed=None
    ):
        """
        Génère des images à partir d'un prompt

        Args:
            prompt: Description de l'image souhaitée
            negative_prompt: Ce qu'on ne veut PAS voir
            num_images: Nombre d'images à générer
            steps: Nombre de steps de denoising (↑ = meilleur qualité)
            guidance_scale: Force du prompt (7-12 optimal)
            width/height: Dimensions (multiples de 64)
            seed: Seed pour reproductibilité

        Returns:
            Liste d'images PIL
        """
        generator = torch.Generator(self.device)
        if seed is not None:
            generator.manual_seed(seed)

        images = self.pipe(
            prompt=prompt,
            negative_prompt=negative_prompt,
            num_images_per_prompt=num_images,
            num_inference_steps=steps,
            guidance_scale=guidance_scale,
            width=width,
            height=height,
            generator=generator
        ).images

        return images

# Utilisation SDXL
gen = ImageGenerator()

# Exemple 1: Génération simple
images = gen.generate(
    prompt="A majestic lion standing on a cliff at sunset, photorealistic, 8k, highly detailed",
    negative_prompt="cartoon, anime, low quality, blurry",
    num_images=4,
    seed=42
)

for i, img in enumerate(images):
    img.save(f"lion_{i}.png")

# Exemple 2: Style artistique
images = gen.generate(
    prompt="Portrait of a cyborg woman, cyberpunk style, neon lights, digital art by artgerm",
    steps=50,
    guidance_scale=9.0
)

# 2. FLUX.1 schnell (ultra-rapide)
from diffusers import FluxPipeline

flux = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-schnell",
    torch_dtype=torch.bfloat16
).to("cuda")

# Génération en 4 steps seulement!
image = flux(
    prompt="A cat holding a sign that says 'FLUX is amazing'",
    num_inference_steps=4,  # Très rapide
    guidance_scale=0.0  # FLUX schnell n'utilise pas de CFG
).images[0]

image.save("flux_cat.png")

Techniques Avancées

1. Classifier-Free Guidance (CFG)

Contrôle la force du prompt. Formula: output = unconditional + scale * (conditional - unconditional)

2. Samplers (Schedulers)

# Changer le sampler
from diffusers import EulerAncestralDiscreteScheduler

pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(
    pipe.scheduler.config
)

# Samplers populaires:
# - DPM++ 2M Karras (qualité, 20-25 steps)
# - Euler A (rapide, 15-20 steps)
# - DDIM (déterministe)
# - UniPC (très rapide, 5-10 steps)

3. ControlNet

Contrôle spatial précis (pose, edges, depth, etc.)

from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel
from controlnet_aux import OpenposeDetector
from PIL import Image

# Charger ControlNet Pose
controlnet = ControlNetModel.from_pretrained(
    "thibaud/controlnet-openpose-sdxl-1.0",
    torch_dtype=torch.float16
)

pipe = StableDiffusionXLControlNetPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

# Extraire la pose d'une image de référence
openpose = OpenposeDetector.from_pretrained("lllyasviel/ControlNet")
reference_image = Image.open("person.jpg")
pose_image = openpose(reference_image)

# Générer avec contrôle de pose
image = pipe(
    prompt="A superhero in a dynamic pose, comic book style",
    image=pose_image,
    controlnet_conditioning_scale=0.8
).images[0]

# ControlNet types disponibles:
# - openpose (pose humaine)
# - canny (contours)
# - depth (profondeur)
# - scribble (croquis)
# - mlsd (lignes architecturales)

4. LoRA (Low-Rank Adaptation)

Fine-tuning léger pour styles, personnages, concepts spécifiques.

# Charger un LoRA depuis CivitAI ou HuggingFace
pipe.load_lora_weights("path/to/lora.safetensors")

# Ajuster l'intensité du LoRA
pipe.set_adapters(["lora_name"], adapter_weights=[0.8])

# Exemple: Générer dans un style spécifique
image = pipe(
    prompt="a cute robot, ",
    cross_attention_kwargs={"scale": 0.8}
).images[0]

# Training LoRA (dataset: 10-50 images d'un concept)
# Voir Kohya's SD scripts ou SimpleTuner

Prompting Efficace

💡 Formule de Prompt Efficace [Sujet principal], [Style], [Détails], [Lighting], [Qualité]

Exemple: "A steampunk airship, victorian era, intricate brass details, golden hour lighting, highly detailed, 8k, octane render"
Composante Exemples
Sujet portrait, landscape, product, character, scene
Style photorealistic, anime, oil painting, digital art, concept art
Lighting golden hour, studio lighting, dramatic shadows, soft light
Qualité 8k, highly detailed, masterpiece, trending on artstation
Negative blurry, low quality, deformed, watermark, text
✅ Points Clés
  • SDXL = Standard actuel, FLUX.1 = Nouvelle référence qualité
  • CFG scale 7-8 optimal, samplers DPM++/Euler A recommandés
  • ControlNet pour contrôle spatial, LoRA pour styles custom
  • Prompts: Descriptifs, structurés, avec modificateurs de qualité
  • SD 3.5 et FLUX.1 schnell sont open source et production-ready

Leçon 2 : Audio & Voix

Speech-to-Text (STT) : Whisper v3

Whisper d'OpenAI est le modèle STT open source de référence, supportant 100+ langues avec une précision impressionnante.

Versions Whisper

Modèle Params WER (EN) Vitesse Usage
tiny 39M ~10% Très rapide Démo, temps réel
base 74M ~7% Rapide Usage général rapide
small 244M ~5% Moyen Bon compromis
medium 769M ~4% Lent Haute précision
large-v3 1.55B ~3% Très lent Production, meilleure qualité

Code Pratique : Whisper avec Transformers

# Installation
pip install transformers torch accelerate ffmpeg-python

from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
import torch

class WhisperSTT:
    def __init__(self, model_size="large-v3", device=None):
        """
        Initialise Whisper

        Args:
            model_size: tiny/base/small/medium/large-v3
            device: cuda/cpu (auto-détecté si None)
        """
        self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
        model_id = f"openai/whisper-{model_size}"

        print(f"🎙️ Chargement de Whisper {model_size}...")

        self.model = AutoModelForSpeechSeq2Seq.from_pretrained(
            model_id,
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
            low_cpu_mem_usage=True,
            use_safetensors=True
        ).to(self.device)

        self.processor = AutoProcessor.from_pretrained(model_id)

        self.pipe = pipeline(
            "automatic-speech-recognition",
            model=self.model,
            tokenizer=self.processor.tokenizer,
            feature_extractor=self.processor.feature_extractor,
            max_new_tokens=128,
            chunk_length_s=30,
            batch_size=16,
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
            device=self.device,
        )

    def transcribe(
        self,
        audio_path,
        language=None,
        task="transcribe",
        return_timestamps=False
    ):
        """
        Transcrit un fichier audio

        Args:
            audio_path: Chemin vers audio (mp3, wav, m4a, etc.)
            language: Code langue (fr, en, es, etc.) ou None pour auto-detect
            task: "transcribe" (langue originale) ou "translate" (→ anglais)
            return_timestamps: Renvoyer les timestamps par segment

        Returns:
            Transcription texte ou dict avec timestamps
        """
        generate_kwargs = {"task": task}
        if language:
            generate_kwargs["language"] = language

        result = self.pipe(
            audio_path,
            generate_kwargs=generate_kwargs,
            return_timestamps=return_timestamps
        )

        return result

# Utilisation
stt = WhisperSTT(model_size="medium")

# Exemple 1: Transcription simple (auto-detect langue)
result = stt.transcribe("podcast.mp3")
print("📝 Transcription:", result["text"])

# Exemple 2: Avec timestamps (pour sous-titres)
result = stt.transcribe(
    "interview.wav",
    language="fr",
    return_timestamps=True
)

for chunk in result["chunks"]:
    start, end = chunk["timestamp"]
    text = chunk["text"]
    print(f"[{start:.2f}s - {end:.2f}s] {text}")

# Exemple 3: Traduction vers anglais
result = stt.transcribe(
    "french_audio.mp3",
    task="translate"  # Traduit vers anglais
)
print("🌐 Traduction EN:", result["text"])

# Exemple 4: Batch processing
import os
from pathlib import Path

def transcribe_directory(audio_dir, output_dir):
    """Transcrit tous les audios d'un dossier"""
    Path(output_dir).mkdir(exist_ok=True)

    for audio_file in Path(audio_dir).glob("*.mp3"):
        result = stt.transcribe(str(audio_file), language="fr")

        output_file = Path(output_dir) / f"{audio_file.stem}.txt"
        output_file.write_text(result["text"], encoding="utf-8")
        print(f"✓ {audio_file.name} → {output_file.name}")

transcribe_directory("./podcasts", "./transcriptions")

Text-to-Speech (TTS)

1. Bark (Suno AI)

TTS multilingue avec support émotions, musique, effets sonores.

pip install git+https://github.com/suno-ai/bark.git

from bark import SAMPLE_RATE, generate_audio, preload_models
from scipy.io.wavfile import write as write_wav

# Télécharger les modèles
preload_models()

# Générer audio avec émotions
text = """
[laughs] Bonjour! Je suis Bark, un modèle TTS open source.
Je peux parler dans différentes langues et avec des émotions!
"""

audio_array = generate_audio(text, history_prompt="v2/fr_speaker_6")
write_wav("bark_output.wav", SAMPLE_RATE, audio_array)

# Effets spéciaux
text_fx = "[clears throat] ♪ I can even sing! ♪ [laughs]"
audio = generate_audio(text_fx)

2. Coqui XTTS v2

TTS avec voice cloning (cloner une voix à partir de 6s d'audio).

pip install TTS

from TTS.api import TTS

# Initialiser XTTS
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to("cuda")

# Méthode 1: Voix pré-entraînée
tts.tts_to_file(
    text="Bonjour, ceci est un test de synthèse vocale.",
    speaker="Claribel Dervla",  # Voix disponible
    language="fr",
    file_path="output.wav"
)

# Méthode 2: Voice cloning
tts.tts_to_file(
    text="Je clone cette voix avec seulement 6 secondes d'audio de référence!",
    speaker_wav="reference_voice.wav",  # 6s minimum
    language="fr",
    file_path="cloned_voice.wav"
)

# Liste des voix disponibles
print(tts.speakers)

3. Parler-TTS (Hugging Face)

Contrôle fin de la voix (genre, pitch, vitesse, qualité audio).

pip install git+https://github.com/huggingface/parler-tts.git

from parler_tts import ParlerTTSForConditionalGeneration
from transformers import AutoTokenizer
import soundfile as sf
import torch

# Charger modèle
model = ParlerTTSForConditionalGeneration.from_pretrained(
    "parler-tts/parler-tts-large-v1"
).to("cuda")
tokenizer = AutoTokenizer.from_pretrained("parler-tts/parler-tts-large-v1")

# Description détaillée de la voix souhaitée
description = """
A female speaker with a slightly high-pitched voice delivers her words
quite expressively, in a very confined sounding environment with clear audio quality.
"""

prompt = "Bonjour, comment puis-je vous aider aujourd'hui?"

input_ids = tokenizer(description, return_tensors="pt").input_ids.to("cuda")
prompt_input_ids = tokenizer(prompt, return_tensors="pt").input_ids.to("cuda")

generation = model.generate(input_ids=input_ids, prompt_input_ids=prompt_input_ids)
audio_arr = generation.cpu().numpy().squeeze()

sf.write("parler_output.wav", audio_arr, model.config.sampling_rate)

Pipeline Audio Complet

class VoiceAssistant:
    """Assistant vocal complet STT → Process → TTS"""

    def __init__(self):
        # STT
        self.stt = WhisperSTT(model_size="base")

        # TTS
        self.tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to("cuda")

        # LLM (optionnel, pour répondre aux questions)
        from transformers import pipeline
        self.llm = pipeline("text-generation", model="meta-llama/Llama-3.1-8B-Instruct")

    def process_audio_query(self, audio_path, reference_voice=None):
        """
        Pipeline complet: Audio question → Texte → LLM → Audio réponse

        Args:
            audio_path: Question audio
            reference_voice: Voix pour la réponse (optionnel)

        Returns:
            Chemin vers réponse audio
        """
        # 1. STT: Audio → Texte
        print("🎙️ Transcription...")
        transcription = self.stt.transcribe(audio_path, language="fr")
        user_query = transcription["text"]
        print(f"❓ Question: {user_query}")

        # 2. LLM: Générer réponse
        print("🤖 Génération réponse...")
        messages = [
            {"role": "system", "content": "Tu es un assistant utile et concis."},
            {"role": "user", "content": user_query}
        ]

        response = self.llm(
            messages,
            max_new_tokens=200,
            do_sample=True,
            temperature=0.7
        )

        answer = response[0]["generated_text"][-1]["content"]
        print(f"💬 Réponse: {answer}")

        # 3. TTS: Texte → Audio
        print("🔊 Synthèse vocale...")
        output_path = "assistant_response.wav"

        if reference_voice:
            self.tts.tts_to_file(
                text=answer,
                speaker_wav=reference_voice,
                language="fr",
                file_path=output_path
            )
        else:
            self.tts.tts_to_file(
                text=answer,
                speaker="Claribel Dervla",
                language="fr",
                file_path=output_path
            )

        print(f"✓ Audio généré: {output_path}")
        return output_path

# Utilisation
assistant = VoiceAssistant()
response_audio = assistant.process_audio_query("question.wav")
🎵 Modèles Audio Supplémentaires
  • MusicGen (Meta) : Génération de musique à partir de texte
  • AudioCraft : Suite audio complète (music, sound effects)
  • Riffusion : Génération musique via Stable Diffusion
  • AudioLDM 2 : Génération audio/musique/speech
✅ Points Clés
  • Whisper large-v3 = Meilleur STT open source (100+ langues)
  • XTTS v2 pour voice cloning avec 6s d'audio seulement
  • Bark pour émotions et effets, Parler-TTS pour contrôle fin
  • Pipeline complet STT → LLM → TTS pour assistants vocaux

Leçon 3 : Vidéo & Animation

Introduction à la Génération Vidéo

La génération vidéo par IA étend les modèles de diffusion d'images au domaine temporel, en maintenant la cohérence entre les frames.

┌──────────────────────────────────────────────────┐ │ Génération Vidéo par Diffusion │ ├──────────────────────────────────────────────────┤ │ │ │ Text Prompt: "A cat walking in a garden" │ │ │ │ │ ▼ │ │ Text Encoder (T5/CLIP) │ │ │ │ │ ▼ │ │ 3D U-Net / Transformer (Spatial + Temporal) │ │ ┌────┬────┬────┬────┐ │ │ │ F1 │ F2 │ F3 │ F4 │ ... Fn (Frames) │ │ └────┴────┴────┴────┘ │ │ │ │ │ │ │ │ └────┴────┴────┴──── Temporal Attention │ │ (Cohérence temporelle) │ │ │ │ Output: Vidéo cohérente 2-10s @ 8-24 fps │ └──────────────────────────────────────────────────┘

CogVideoX (Zhipu AI)

2024 Modèle open source de génération vidéo text-to-video de haute qualité.

Versions CogVideoX

Modèle Params Résolution Durée VRAM
CogVideoX-2B 2B 720x480 6s @ 8fps ~18GB
CogVideoX-5B 5B 720x480 6s @ 8fps ~30GB
CogVideoX-5B-I2V 5B 720x480 6s @ 8fps ~30GB (image→video)

Code Pratique : CogVideoX avec Diffusers

# Installation
pip install diffusers transformers accelerate imageio[ffmpeg]

import torch
from diffusers import CogVideoXPipeline
from diffusers.utils import export_to_video

class VideoGenerator:
    def __init__(self, model="THUDM/CogVideoX-2b"):
        """
        Initialise le générateur vidéo

        Args:
            model: THUDM/CogVideoX-2b ou THUDM/CogVideoX-5b
        """
        self.device = "cuda" if torch.cuda.is_available() else "cpu"

        print(f"🎬 Chargement de {model}...")
        self.pipe = CogVideoXPipeline.from_pretrained(
            model,
            torch_dtype=torch.float16
        ).to(self.device)

        # Optimisations mémoire
        self.pipe.enable_model_cpu_offload()
        self.pipe.enable_sequential_cpu_offload()
        self.pipe.vae.enable_slicing()
        self.pipe.vae.enable_tiling()

    def generate_video(
        self,
        prompt,
        num_frames=49,
        num_inference_steps=50,
        guidance_scale=6.0,
        seed=None
    ):
        """
        Génère une vidéo à partir d'un prompt

        Args:
            prompt: Description de la vidéo
            num_frames: Nombre de frames (49 = ~6s @ 8fps)
            num_inference_steps: Steps de diffusion
            guidance_scale: Force du prompt
            seed: Seed pour reproductibilité

        Returns:
            Tensor vidéo [frames, height, width, channels]
        """
        generator = torch.Generator(device=self.device)
        if seed is not None:
            generator.manual_seed(seed)

        video = self.pipe(
            prompt=prompt,
            num_frames=num_frames,
            num_inference_steps=num_inference_steps,
            guidance_scale=guidance_scale,
            generator=generator
        ).frames[0]

        return video

# Utilisation
gen = VideoGenerator(model="THUDM/CogVideoX-2b")

# Exemple 1: Génération simple
video = gen.generate_video(
    prompt="A panda eating bamboo in a bamboo forest, natural lighting, high quality",
    seed=42
)

# Sauvegarder en MP4
export_to_video(video, "panda.mp4", fps=8)

# Exemple 2: Mouvement de caméra
video = gen.generate_video(
    prompt="""
    Cinematic shot of a futuristic city at night,
    camera slowly panning from left to right,
    neon lights reflecting on wet streets,
    cyberpunk aesthetic, 8k quality
    """,
    num_inference_steps=60,
    guidance_scale=7.0
)

export_to_video(video, "cyberpunk_city.mp4", fps=8)

# Exemple 3: Action dynamique
video = gen.generate_video(
    prompt="""
    A golden retriever running through a flower field,
    slow motion, sunset golden hour,
    flowers blowing in the wind,
    professional wildlife photography
    """
)

export_to_video(video, "dog_running.mp4", fps=8)

Image-to-Video avec CogVideoX-I2V

from diffusers import CogVideoXImageToVideoPipeline
from PIL import Image

# Charger pipeline I2V
pipe_i2v = CogVideoXImageToVideoPipeline.from_pretrained(
    "THUDM/CogVideoX-5b-I2V",
    torch_dtype=torch.float16
).to("cuda")

pipe_i2v.enable_model_cpu_offload()
pipe_i2v.vae.enable_slicing()
pipe_i2v.vae.enable_tiling()

# Image de départ
start_image = Image.open("portrait.jpg")

# Générer vidéo à partir de l'image
video = pipe_i2v(
    prompt="The person smiles and waves at the camera",
    image=start_image,
    num_frames=49,
    num_inference_steps=50,
    guidance_scale=6.0
).frames[0]

export_to_video(video, "animated_portrait.mp4", fps=8)

AnimateDiff

Technique pour animer Stable Diffusion (ajouter motion aux images SD).

pip install diffusers[torch] transformers

from diffusers import AnimateDiffPipeline, MotionAdapter, DDIMScheduler
from diffusers.utils import export_to_video
import torch

# Charger MotionAdapter (module temporel)
adapter = MotionAdapter.from_pretrained(
    "guoyww/animatediff-motion-adapter-v1-5-2",
    torch_dtype=torch.float16
)

# Charger SD 1.5 + adapter
pipe = AnimateDiffPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    motion_adapter=adapter,
    torch_dtype=torch.float16
).to("cuda")

# Utiliser DDIM scheduler
pipe.scheduler = DDIMScheduler.from_config(
    pipe.scheduler.config,
    beta_schedule="linear",
    steps_offset=1,
    clip_sample=False
)

# Générer animation
output = pipe(
    prompt="A magical castle in the clouds, camera zoom out, epic fantasy, volumetric lighting",
    negative_prompt="bad quality, blur, low resolution",
    num_frames=16,
    guidance_scale=7.5,
    num_inference_steps=25
)

export_to_video(output.frames[0], "castle_animation.mp4", fps=8)

# Utiliser avec LoRA custom
pipe.load_lora_weights("path/to/style.safetensors")
output = pipe(
    prompt="A cute anime character waving, ",
    num_frames=16
)

Défis de la Génération Vidéo

1. Cohérence Temporelle

Maintenir la cohérence des objets/personnages entre frames.

⚠️ Problèmes Courants
  • Morphing : Objets qui changent de forme entre frames
  • Flicker : Instabilité visuelle (clignotement)
  • Motion blur : Flou sur mouvements rapides
  • Disparitions : Objets qui apparaissent/disparaissent

Solutions :
  • Utiliser models avec temporal attention (CogVideoX, AnimateDiff)
  • Augmenter num_inference_steps (50-80)
  • Utiliser guidance_scale modéré (6-8)
  • Prompts très descriptifs sur continuité

2. Résolution et Durée

Résolution Durée Max VRAM Requise Temps Génération
480p (720x480) 6-10s 18-30GB 5-15 min
720p (1280x720) 4-6s 40-48GB 15-30 min
1080p (1920x1080) 2-4s 80GB+ 30-60 min

Video Understanding (Multimodal)

Analyser et comprendre le contenu vidéo avec des VLM.

# Utiliser un VLM pour analyser une vidéo
from transformers import LlavaNextVideoForConditionalGeneration, LlavaNextVideoProcessor
import cv2
import torch

model = LlavaNextVideoForConditionalGeneration.from_pretrained(
    "llava-hf/LLaVA-NeXT-Video-7B-hf",
    torch_dtype=torch.float16
).to("cuda")

processor = LlavaNextVideoProcessor.from_pretrained(
    "llava-hf/LLaVA-NeXT-Video-7B-hf"
)

# Extraire frames de la vidéo
def extract_frames(video_path, num_frames=8):
    """Extrait N frames uniformément espacées"""
    cap = cv2.VideoCapture(video_path)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    indices = np.linspace(0, total_frames - 1, num_frames, dtype=int)

    frames = []
    for idx in indices:
        cap.set(cv2.CAP_PROP_POS_FRAMES, idx)
        ret, frame = cap.read()
        if ret:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            frames.append(Image.fromarray(frame))

    cap.release()
    return frames

# Analyser vidéo
frames = extract_frames("video.mp4", num_frames=8)

prompt = "Décris ce qui se passe dans cette vidéo en détail."

inputs = processor(
    text=prompt,
    images=frames,
    return_tensors="pt"
).to("cuda")

output = model.generate(**inputs, max_new_tokens=256)
description = processor.decode(output[0], skip_special_tokens=True)

print("📹 Analyse vidéo:", description)
💡 Conseil du Mentor La génération vidéo est encore jeune (2024-2025). Attendez-vous à des limitations. Pour production, combinez : génération IA + post-processing (stabilisation, upscaling avec Topaz Video AI, color grading). Les meilleurs résultats viennent de l'hybridation IA + techniques traditionnelles.
✅ Points Clés
  • CogVideoX = Meilleur T2V open source actuel (2B/5B)
  • AnimateDiff pour animer Stable Diffusion avec motion
  • I2V (image-to-video) plus stable que T2V (text-to-video)
  • Cohérence temporelle = Défi principal, résolu par attention temporelle
  • VRAM élevée requise (18-30GB minimum pour 480p)

Leçon 4 : Multimodal End-to-End

Unified Multimodal Models (Any-to-Any)

Les modèles multimodaux unifiés peuvent accepter et générer plusieurs modalités (texte, image, audio, vidéo) dans une seule architecture.

┌────────────────────────────────────────────────────┐ │ Architecture Any-to-Any Unifiée │ ├────────────────────────────────────────────────────┤ │ │ │ Inputs (Multi-modalités) │ │ ┌──────┬──────┬──────┬──────┐ │ │ │ Text │Image │Audio │Video │ │ │ └──┬───┴──┬───┴──┬───┴──┬───┘ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ ┌────────────────────────────┐ │ │ │ Encoders Modality-Specific│ │ │ │ (Text, Vision, Audio, etc)│ │ │ └──────────┬─────────────────┘ │ │ ▼ │ │ ┌─────────────────────────┐ │ │ │ Shared Latent Space │ │ │ │ (Unified Transformer) │ │ │ └──────────┬──────────────┘ │ │ ▼ │ │ ┌─────────────────────────┐ │ │ │ Decoders Multi-Modaux │ │ │ └──┬────┬─────┬──────┬────┘ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ ┌──────┬─────┬──────┬─────┐ │ │ │ Text │Image│Audio │Video│ │ │ └──────┴─────┴──────┴─────┘ │ │ │ │ Outputs (Génération multi-modalités) │ └────────────────────────────────────────────────────┘

1. Gemma 2 avec PaliGemma (Vision)

PaliGemma est la version vision-language de Gemma, optimisée pour VQA, captioning, OCR.

pip install transformers torch pillow

from transformers import AutoProcessor, PaliGemmaForConditionalGeneration
from PIL import Image
import torch

class PaliGemmaVision:
    def __init__(self, model_size="3b-mix-448"):
        """
        Initialise PaliGemma

        Args:
            model_size: 3b-pt-224, 3b-mix-448, 3b-pt-896
        """
        model_id = f"google/paligemma-{model_size}"

        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        self.model = PaliGemmaForConditionalGeneration.from_pretrained(
            model_id,
            torch_dtype=torch.bfloat16
        ).to(self.device)

        self.processor = AutoProcessor.from_pretrained(model_id)

    def query(self, image_path, prompt, max_tokens=256):
        """
        Question sur une image

        Args:
            image_path: Chemin image
            prompt: Question ou task (describe, detect, segment, etc.)
        """
        image = Image.open(image_path).convert("RGB")

        inputs = self.processor(
            text=prompt,
            images=image,
            return_tensors="pt"
        ).to(self.device)

        with torch.no_grad():
            output = self.model.generate(
                **inputs,
                max_new_tokens=max_tokens,
                do_sample=False
            )

        response = self.processor.decode(output[0], skip_special_tokens=True)
        return response

# Utilisation
pali = PaliGemmaVision()

# Tâches supportées
tasks = {
    "caption": "caption en",  # Description courte
    "detailed_caption": "describe en detail",  # Description détaillée
    "detect": "detect person ; dog",  # Détection objets (bounding boxes)
    "segment": "segment car",  # Segmentation
    "ocr": "ocr",  # OCR
    "answer": "question answering en What is the weather?"  # VQA
}

for task_name, prompt in tasks.items():
    result = pali.query("photo.jpg", prompt)
    print(f"{task_name}: {result}")

2. Pixtral (Mistral AI)

2024 Modèle multimodal de Mistral AI avec support natif images + texte.

pip install mistral-inference

from mistral_inference.transformer import Transformer
from mistral_inference.generate import generate
from mistral_common.tokens.tokenizers.mistral import MistralTokenizer
from mistral_common.protocol.instruct.messages import UserMessage, ImageChunk, TextChunk
from PIL import Image
import base64
from io import BytesIO

class PixtralMultimodal:
    def __init__(self):
        """Initialise Pixtral 12B"""
        self.tokenizer = MistralTokenizer.from_model("pixtral")
        self.model = Transformer.from_folder("path/to/pixtral-12b")

    def image_to_base64(self, image_path):
        """Convertit image en base64 pour Pixtral"""
        img = Image.open(image_path)
        buffered = BytesIO()
        img.save(buffered, format="PNG")
        return base64.b64encode(buffered.getvalue()).decode()

    def query(self, image_path, prompt):
        """Question multimodale"""
        img_b64 = self.image_to_base64(image_path)

        messages = [
            UserMessage(
                content=[
                    ImageChunk(image=img_b64),
                    TextChunk(text=prompt)
                ]
            )
        ]

        tokens = self.tokenizer.encode_chat_completion(messages).tokens

        out_tokens, _ = generate(
            [tokens],
            self.model,
            max_tokens=512,
            temperature=0.0,
            eos_id=self.tokenizer.instruct_tokenizer.tokenizer.eos_id
        )

        result = self.tokenizer.decode(out_tokens[0])
        return result

# Alternative: Via Mistral API (hosted)
from mistralai import Mistral
import os

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

def query_pixtral_api(image_path, prompt):
    """Utiliser Pixtral via API Mistral"""
    with open(image_path, "rb") as f:
        img_b64 = base64.b64encode(f.read()).decode()

    messages = [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": prompt},
                {"type": "image_url", "image_url": f"data:image/jpeg;base64,{img_b64}"}
            ]
        }
    ]

    response = client.chat.complete(
        model="pixtral-12b-2409",
        messages=messages
    )

    return response.choices[0].message.content

result = query_pixtral_api("chart.png", "Analyse ce graphique")

3. Qwen2-Audio

Modèle audio multilingue de Qwen pour analyse audio + génération texte.

from transformers import Qwen2AudioForConditionalGeneration, AutoProcessor
import torch

model = Qwen2AudioForConditionalGeneration.from_pretrained(
    "Qwen/Qwen2-Audio-7B-Instruct",
    torch_dtype=torch.bfloat16
).to("cuda")

processor = AutoProcessor.from_pretrained("Qwen/Qwen2-Audio-7B-Instruct")

# Tâches supportées
conversation = [
    {
        "role": "user",
        "content": [
            {"type": "audio", "audio_url": "audio.wav"},
            {"type": "text", "text": "Transcris et résume cet audio."}
        ]
    }
]

text = processor.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)
audios = processor.process_audio_urls([{"audio_url": "audio.wav"}])

inputs = processor(text=text, audios=audios, return_tensors="pt").to("cuda")

output = model.generate(**inputs, max_new_tokens=512)
result = processor.decode(output[0], skip_special_tokens=True)

print("Analyse audio:", result)

Comparaison Architecture VLM

Modèle Architecture Modalités Forces
LLaVA CLIP → Linear → LLM Image + Text Simple, efficace, fine-tunable
Qwen2-VL Dynamic ViT → QFormer → Qwen Image + Text Multi-images, haute résolution, multilangue
InternVL InternViT-6B → MLP → InternLM Image + Text SOTA benchmarks, scaling (1B-76B)
PaliGemma SigLIP → Linear → Gemma Image + Text Tasks spécialisés (detect, segment, OCR)
Pixtral Vision Encoder → Mistral Image + Text Intégration native, prompt long (128k)
Qwen2-Audio Audio Encoder → Qwen Audio + Text Analyse audio, transcription, compréhension

Pipeline Multimodal Complet

class UnifiedMultimodalAgent:
    """Agent unifié gérant texte, image, audio, vidéo"""

    def __init__(self):
        # Vision-Language
        self.vlm = LlavaNextForConditionalGeneration.from_pretrained(
            "llava-hf/llava-v1.6-mistral-7b-hf",
            torch_dtype=torch.float16
        ).to("cuda")

        # Audio (STT + TTS)
        self.stt = WhisperSTT(model_size="medium")
        self.tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2")

        # Texte (LLM)
        from transformers import pipeline
        self.llm = pipeline(
            "text-generation",
            model="mistralai/Mistral-7B-Instruct-v0.3",
            device="cuda"
        )

    def process_multimodal_query(
        self,
        text_query=None,
        image_path=None,
        audio_path=None
    ):
        """
        Traite une requête multimodale

        Exemples:
        - Texte seul: "Explique-moi la photosynthèse"
        - Image + Texte: image="photo.jpg", text="Que vois-tu?"
        - Audio: audio="question.wav" (converti en texte puis traité)
        - Multi: audio + image (transcrit audio puis analyse image)
        """
        # 1. Traiter l'audio si présent (STT)
        if audio_path:
            transcription = self.stt.transcribe(audio_path)
            text_query = transcription["text"]
            print(f"🎙️ Transcription: {text_query}")

        # 2. Traiter l'image si présente (VLM)
        if image_path and text_query:
            print("👁️ Analyse multimodale image + texte...")
            response = self.vlm_query(image_path, text_query)

        # 3. Sinon, LLM texte seul
        elif text_query:
            print("💬 Traitement texte seul...")
            messages = [{"role": "user", "content": text_query}]
            response = self.llm(messages, max_new_tokens=300)[0]["generated_text"][-1]["content"]

        else:
            response = "Aucune entrée fournie."

        return response

    def vlm_query(self, image_path, prompt):
        """Helper pour VLM"""
        # Code VLM simplifié
        # ... (voir leçon 0)
        pass

# Utilisation
agent = UnifiedMultimodalAgent()

# Cas 1: Question vocale
response = agent.process_multimodal_query(
    audio_path="question.wav"
)

# Cas 2: Analyse image
response = agent.process_multimodal_query(
    text_query="Décris cette scène",
    image_path="landscape.jpg"
)

# Cas 3: Audio + Image
response = agent.process_multimodal_query(
    audio_path="question_about_image.wav",
    image_path="diagram.png"
)

print("🤖 Réponse:", response)
💡 Conseil du Mentor L'avenir de l'IA est clairement multimodal. GPT-4o, Gemini 1.5 Pro ont démontré la puissance de l'any-to-any. En open source, nous en sommes aux débuts (2024-2025), mais LLaVA, Qwen-VL, InternVL montrent que l'écart se réduit. Investissez dans la maîtrise de ces architectures maintenant.
✅ Points Clés
  • Multimodal unifié = 1 modèle pour plusieurs modalités
  • PaliGemma excellent pour tâches vision spécialisées
  • Pixtral (Mistral) offre intégration native texte+vision
  • Qwen2-Audio/VL leaders en multilangue
  • Architecture clé: Encoders spécialisés → Latent partagé → Decoders

Leçon 5 : Document AI

Introduction au Document AI

Document AI combine OCR, layout analysis, et compréhension sémantique pour extraire et structurer l'information de documents (PDF, scans, images).

Pipeline Document AI

┌───────────────────────────────────────────────────┐ │ Pipeline Document AI Complet │ ├───────────────────────────────────────────────────┤ │ │ │ 1. Preprocessing │ │ ┌──────────────┐ │ │ │ Document PDF │ │ │ │ Image / Scan │ │ │ └──────┬───────┘ │ │ │ │ │ ▼ │ │ 2. Layout Analysis │ │ ┌─────────────────────────────┐ │ │ │ Détection des régions: │ │ │ │ - Titres, paragraphes │ │ │ │ - Tableaux, images │ │ │ │ - En-têtes, pieds de page │ │ │ └─────────┬───────────────────┘ │ │ │ │ │ ▼ │ │ 3. OCR (Optical Character Recognition) │ │ ┌─────────────────────────────┐ │ │ │ Extraction texte par région │ │ │ │ (Tesseract, EasyOCR, etc.) │ │ │ └─────────┬───────────────────┘ │ │ │ │ │ ▼ │ │ 4. Post-Processing & Structuring │ │ ┌─────────────────────────────┐ │ │ │ - Table extraction │ │ │ │ - Entity extraction (NER) │ │ │ │ - Document classification │ │ │ │ - Semantic understanding │ │ │ └─────────┬───────────────────┘ │ │ │ │ │ ▼ │ │ 5. Output: JSON structuré, Markdown, etc. │ └───────────────────────────────────────────────────┘

1. OCR avec Tesseract & EasyOCR

Tesseract (Google)

OCR classique, excellent pour documents imprimés de qualité.

# Installation
# Windows: https://github.com/UB-Mannheim/tesseract/wiki
# Linux: sudo apt install tesseract-ocr tesseract-ocr-fra
pip install pytesseract pillow pdf2image

import pytesseract
from PIL import Image
from pdf2image import convert_from_path

class TesseractOCR:
    def __init__(self, lang="fra+eng"):
        """
        Initialise Tesseract

        Args:
            lang: Langues (fra, eng, deu, etc.) combinables avec +
        """
        # Spécifier chemin Tesseract si Windows
        # pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
        self.lang = lang

    def ocr_image(self, image_path, config=""):
        """OCR sur une image"""
        img = Image.open(image_path)
        text = pytesseract.image_to_string(img, lang=self.lang, config=config)
        return text

    def ocr_with_boxes(self, image_path):
        """OCR avec bounding boxes (coordonnées)"""
        img = Image.open(image_path)
        data = pytesseract.image_to_data(img, lang=self.lang, output_type=pytesseract.Output.DICT)

        # Filtrer résultats (conf > 60)
        results = []
        for i in range(len(data["text"])):
            if int(data["conf"][i]) > 60:
                results.append({
                    "text": data["text"][i],
                    "bbox": (data["left"][i], data["top"][i],
                             data["width"][i], data["height"][i]),
                    "confidence": data["conf"][i]
                })

        return results

    def ocr_pdf(self, pdf_path, dpi=300):
        """OCR sur PDF (converti en images d'abord)"""
        pages = convert_from_path(pdf_path, dpi=dpi)

        full_text = []
        for i, page in enumerate(pages):
            print(f"📄 OCR page {i+1}/{len(pages)}")
            text = pytesseract.image_to_string(page, lang=self.lang)
            full_text.append(f"=== Page {i+1} ===\n{text}")

        return "\n\n".join(full_text)

# Utilisation
ocr = TesseractOCR(lang="fra+eng")

# Image simple
text = ocr.ocr_image("document.png")
print(text)

# Avec coordonnées (pour layout preservation)
boxes = ocr.ocr_with_boxes("invoice.jpg")
for box in boxes[:10]:
    print(f"{box['text']} @ {box['bbox']} (conf: {box['confidence']}%)")

# PDF complet
text = ocr.ocr_pdf("contract.pdf")
with open("contract_extracted.txt", "w", encoding="utf-8") as f:
    f.write(text)

EasyOCR

OCR deep learning, meilleur pour écritures manuscrites et 80+ langues.

pip install easyocr

import easyocr
import cv2
import numpy as np

class EasyOCRExtractor:
    def __init__(self, languages=["fr", "en"]):
        """Initialise EasyOCR (download models au 1er usage)"""
        self.reader = easyocr.Reader(languages, gpu=True)

    def ocr_image(self, image_path):
        """OCR avec EasyOCR"""
        results = self.reader.readtext(image_path)

        # results = [(bbox, text, confidence), ...]
        extracted = []
        for (bbox, text, conf) in results:
            extracted.append({
                "text": text,
                "bbox": bbox,
                "confidence": conf
            })

        return extracted

    def visualize(self, image_path, output_path="annotated.jpg"):
        """Visualise les détections"""
        img = cv2.imread(image_path)
        results = self.reader.readtext(image_path)

        for (bbox, text, conf) in results:
            # Dessiner bbox
            pts = np.array(bbox, dtype=np.int32)
            cv2.polylines(img, [pts], True, (0, 255, 0), 2)

            # Ajouter texte
            cv2.putText(img, text, tuple(pts[0]),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)

        cv2.imwrite(output_path, img)
        print(f"✓ Visualisation sauvegardée: {output_path}")

# Utilisation
easy_ocr = EasyOCRExtractor(languages=["fr", "en"])

results = easy_ocr.ocr_image("handwritten.jpg")
for r in results:
    print(f"{r['text']} (conf: {r['confidence']:.2f})")

# Visualisation
easy_ocr.visualize("form.jpg", "form_annotated.jpg")

2. DocTR (Document Text Recognition)

Framework complet end-to-end de Mindee pour OCR + layout analysis.

pip install python-doctr[torch]

from doctr.io import DocumentFile
from doctr.models import ocr_predictor
import json

class DocTRProcessor:
    def __init__(self):
        """Initialise DocTR (detección + OCR)"""
        self.model = ocr_predictor(
            det_arch="db_resnet50",
            reco_arch="crnn_vgg16_bn",
            pretrained=True
        )

    def process_document(self, file_path):
        """
        Traite document (image ou PDF)

        Returns:
            dict structuré avec pages, blocs, lignes, mots
        """
        # Charger document
        doc = DocumentFile.from_images(file_path)

        # OCR
        result = self.model(doc)

        # Exporter en dict
        output = result.export()

        return output

    def extract_text(self, file_path):
        """Extrait uniquement le texte brut"""
        output = self.process_document(file_path)

        full_text = []
        for page in output["pages"]:
            page_text = []
            for block in page["blocks"]:
                for line in block["lines"]:
                    line_text = " ".join([word["value"] for word in line["words"]])
                    page_text.append(line_text)
            full_text.append("\n".join(page_text))

        return "\n\n".join(full_text)

    def extract_structured(self, file_path):
        """Extrait avec structure (coordonnées, hiérarchie)"""
        output = self.process_document(file_path)

        structured = []
        for page_idx, page in enumerate(output["pages"]):
            page_data = {
                "page": page_idx + 1,
                "blocks": []
            }

            for block in page["blocks"]:
                block_data = {
                    "geometry": block["geometry"],
                    "lines": []
                }

                for line in block["lines"]:
                    line_text = " ".join([w["value"] for w in line["words"]])
                    block_data["lines"].append({
                        "text": line_text,
                        "geometry": line["geometry"]
                    })

                page_data["blocks"].append(block_data)

            structured.append(page_data)

        return structured

# Utilisation
doctr = DocTRProcessor()

# Extraction simple
text = doctr.extract_text("invoice.pdf")
print(text)

# Extraction structurée (pour layout-aware RAG)
structured = doctr.extract_structured("contract.pdf")
with open("contract_structured.json", "w", encoding="utf-8") as f:
    json.dump(structured, f, indent=2, ensure_ascii=False)

3. Table Extraction

pip install img2table camelot-py[cv]

# Option 1: img2table (images)
from img2table.document import Image as Img2TableImage
from img2table.ocr import TesseractOCR

def extract_tables_from_image(image_path):
    """Extrait tableaux d'une image"""
    ocr = TesseractOCR(lang="fra+eng")
    doc = Img2TableImage(src=image_path)

    # Extraire tableaux
    tables = doc.extract_tables(ocr=ocr)

    # Convertir en DataFrame
    import pandas as pd
    dfs = []
    for table in tables:
        df = table.df
        dfs.append(df)

    return dfs

# Option 2: Camelot (PDF uniquement)
import camelot

def extract_tables_from_pdf(pdf_path):
    """Extrait tableaux d'un PDF"""
    tables = camelot.read_pdf(pdf_path, pages="all", flavor="lattice")

    dfs = []
    for table in tables:
        df = table.df
        dfs.append(df)

    return dfs

# Utilisation
tables = extract_tables_from_image("table.png")
for i, df in enumerate(tables):
    print(f"Table {i+1}:")
    print(df)
    df.to_csv(f"table_{i+1}.csv", index=False)

4. Donut : OCR-Free Document Understanding

Innovant Modèle transformer end-to-end qui comprend directement les documents sans OCR intermédiaire.

pip install donut-python

from transformers import DonutProcessor, VisionEncoderDecoderModel
from PIL import Image
import torch

class DonutDocumentAI:
    def __init__(self, task="docvqa"):
        """
        Initialise Donut

        Args:
            task: docvqa, cord-v2 (receipts), rvl-cdip (classification)
        """
        model_map = {
            "docvqa": "naver-clova-ix/donut-base-finetuned-docvqa",
            "cord": "naver-clova-ix/donut-base-finetuned-cord-v2",
            "rvl": "naver-clova-ix/donut-base-finetuned-rvlcdip"
        }

        model_name = model_map.get(task, model_map["docvqa"])

        self.processor = DonutProcessor.from_pretrained(model_name)
        self.model = VisionEncoderDecoderModel.from_pretrained(model_name).to("cuda")

    def document_vqa(self, image_path, question):
        """
        Visual Question Answering sur document

        Args:
            image_path: Document (scan, photo)
            question: Question en langage naturel
        """
        image = Image.open(image_path).convert("RGB")

        # Préparer input
        task_prompt = f"{question}"
        pixel_values = self.processor(image, return_tensors="pt").pixel_values.to("cuda")

        # Générer réponse
        decoder_input_ids = self.processor.tokenizer(
            task_prompt,
            add_special_tokens=False,
            return_tensors="pt"
        ).input_ids.to("cuda")

        outputs = self.model.generate(
            pixel_values,
            decoder_input_ids=decoder_input_ids,
            max_length=self.model.decoder.config.max_position_embeddings,
            early_stopping=True,
            pad_token_id=self.processor.tokenizer.pad_token_id,
            eos_token_id=self.processor.tokenizer.eos_token_id,
            use_cache=True,
            num_beams=1,
            bad_words_ids=[[self.processor.tokenizer.unk_token_id]],
            return_dict_in_generate=True
        )

        # Décoder
        sequence = self.processor.batch_decode(outputs.sequences)[0]
        sequence = sequence.replace(self.processor.tokenizer.eos_token, "").replace(self.processor.tokenizer.pad_token, "")
        sequence = sequence.split("")[-1].split("")[0].strip()

        return sequence

# Utilisation
donut = DonutDocumentAI(task="docvqa")

# Questions sur une facture
questions = [
    "What is the invoice number?",
    "What is the total amount?",
    "Who is the vendor?",
    "What is the date?"
]

for q in questions:
    answer = donut.document_vqa("invoice.jpg", q)
    print(f"Q: {q}\nA: {answer}\n")

Pipeline Document AI Complet

class DocumentAISystem:
    """Système complet de traitement de documents"""

    def __init__(self):
        self.doctr = DocTRProcessor()
        self.donut = DonutDocumentAI()
        self.table_extractor = extract_tables_from_image

    def process_invoice(self, invoice_path):
        """
        Pipeline complet pour facture

        Returns:
            dict avec toutes les infos extraites
        """
        result = {
            "raw_text": "",
            "structured_fields": {},
            "tables": [],
            "metadata": {}
        }

        # 1. OCR complet
        print("📄 Extraction texte...")
        result["raw_text"] = self.doctr.extract_text(invoice_path)

        # 2. Extraction champs structurés (VQA)
        print("🔍 Extraction champs...")
        fields_to_extract = {
            "invoice_number": "What is the invoice number?",
            "total_amount": "What is the total amount?",
            "date": "What is the invoice date?",
            "vendor": "Who is the vendor?",
            "customer": "Who is the customer?"
        }

        for field, question in fields_to_extract.items():
            answer = self.donut.document_vqa(invoice_path, question)
            result["structured_fields"][field] = answer

        # 3. Extraction tableaux
        print("📊 Extraction tableaux...")
        try:
            tables = self.table_extractor(invoice_path)
            result["tables"] = [df.to_dict() for df in tables]
        except Exception as e:
            print(f"⚠️ Erreur extraction tableaux: {e}")

        return result

# Utilisation
doc_ai = DocumentAISystem()

invoice_data = doc_ai.process_invoice("invoice_scan.jpg")

print("Numéro facture:", invoice_data["structured_fields"]["invoice_number"])
print("Montant total:", invoice_data["structured_fields"]["total_amount"])
print("Nombre de tableaux:", len(invoice_data["tables"]))

# Sauvegarder résultat structuré
with open("invoice_data.json", "w") as f:
    json.dump(invoice_data, f, indent=2)
✅ Points Clés
  • Tesseract pour documents imprimés, EasyOCR pour manuscrit
  • DocTR offre pipeline complet OCR + layout analysis
  • Donut révolutionne avec approche OCR-free (end-to-end)
  • Table extraction spécialisée avec Camelot/img2table
  • Pipeline complet: OCR → Structure → VQA → Export JSON

Leçon 6 : 3D & Robotique

Introduction au Spatial AI

L'IA spatiale combine vision 3D, génération de scènes 3D, et applications robotiques pour créer des systèmes qui comprennent et interagissent avec le monde physique.

NeRF (Neural Radiance Fields)

NeRF représente des scènes 3D comme des champs de radiance neuronaux, permettant de synthétiser des vues photo-réalistes à partir de quelques photos 2D.

# Installation Nerfstudio (framework NeRF complet)
pip install nerfstudio

# Entraîner un NeRF à partir de photos
ns-train nerfacto --data path/to/images

# Render une vidéo
ns-render camera-path --load-config outputs/path/config.yml \
    --camera-path-filename path/to/camera_path.json \
    --output-path renders/output.mp4

# Alternative: Instant-NGP (ultra-rapide, NVIDIA)
# https://github.com/NVlabs/instant-ngp

Point Cloud Processing

pip install open3d

import open3d as o3d
import numpy as np

# Charger point cloud
pcd = o3d.io.read_point_cloud("scene.ply")

# Visualiser
o3d.visualization.draw_geometries([pcd])

# Segmentation avec RANSAC (détecter plan)
plane_model, inliers = pcd.segment_plane(
    distance_threshold=0.01,
    ransac_n=3,
    num_iterations=1000
)

# Points du plan (ex: sol)
inlier_cloud = pcd.select_by_index(inliers)
inlier_cloud.paint_uniform_color([1, 0, 0])

# Points hors plan (objets)
outlier_cloud = pcd.select_by_index(inliers, invert=True)

o3d.visualization.draw_geometries([inlier_cloud, outlier_cloud])

Diffusion 3D

Génération d'objets 3D à partir de texte ou images.

# Shap-E (OpenAI)
pip install shap-e

from shap_e.diffusion.sample import sample_latents
from shap_e.diffusion.gaussian_diffusion import diffusion_from_config
from shap_e.models.download import load_model, load_config
import torch

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# Charger modèle
xm = load_model('transmitter', device=device)
model = load_model('text300M', device=device)
diffusion = diffusion_from_config(load_config('diffusion'))

# Générer 3D à partir de texte
prompt = "a chair that looks like an avocado"

latents = sample_latents(
    batch_size=1,
    model=model,
    diffusion=diffusion,
    guidance_scale=15.0,
    model_kwargs=dict(texts=[prompt]),
    progress=True,
    clip_denoised=True,
    use_fp16=True,
    use_karras=True,
    karras_steps=64,
    sigma_min=1e-3,
    sigma_max=160,
    s_churn=0,
)

# Export en .ply ou .obj
from shap_e.util.notebooks import decode_latent_mesh

for idx, latent in enumerate(latents):
    mesh = decode_latent_mesh(xm, latent).tri_mesh()
    with open(f'chair_{idx}.ply', 'wb') as f:
        mesh.write_ply(f)
🤖 Embodied AI & Robotique

L'IA incarnée (embodied AI) intègre perception, raisonnement et action physique. Applications :

  • Manipulation robotique : RT-1/RT-2 (Google), OpenVLA
  • Navigation autonome : LLMs pour planification de trajectoires
  • Vision robotique : VLMs pour compréhension de scène
✅ Points Clés
  • NeRF révolutionne la reconstruction 3D à partir de photos 2D
  • Point clouds pour représentation et traitement de scènes 3D
  • Diffusion 3D (Shap-E, DreamFusion) génère objets à partir de texte
  • Embodied AI combine VLMs + LLMs + contrôle robotique

Leçon 7 : Quiz Module 8.1 - IA Multimodale

Testez vos connaissances (8 questions)

Question 1 : Architecture CLIP

Quelle est la fonction principale de CLIP dans un VLM?

Question 2 : Stable Diffusion vs FLUX.1

Quel avantage principal FLUX.1 schnell offre-t-il par rapport à SDXL?

Question 3 : Whisper

Combien de langues Whisper large-v3 supporte-t-il approximativement?

Question 4 : Voice Cloning

Quelle est la durée minimum d'audio de référence pour cloner une voix avec XTTS v2?

Question 5 : Génération Vidéo

Quel est le défi principal de la génération vidéo par IA?

Question 6 : VLM Unified

Quelle caractéristique définit un modèle "any-to-any"?

Question 7 : Document AI

Quelle est l'innovation principale de Donut par rapport aux pipelines OCR traditionnels?

Question 8 : NeRF

Que permet de faire NeRF (Neural Radiance Fields)?

Leçon 8 : IA pour la Santé

Medical LLMs & Applications

L'IA transforme la médecine : diagnostic assisté, analyse d'imagerie médicale, découverte de médicaments, personnalisation de traitements.

LLMs Médicaux Open Source

Modèle Base Spécialisation Performance
Med-PaLM 2 PaLM QA médical général 85% MedQA (expert-level)
BioGPT GPT-2 Littérature biomédicale PubMed fine-tuned
ClinicalBERT BERT Notes cliniques Extraction entités médicales
Llama-3-Med Llama 3 Multi-tâches médical Community fine-tuned

Code: Assistant Médical RAG

from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from transformers import pipeline
import torch

class MedicalRAGAssistant:
    def __init__(self, medical_docs_path):
        """
        Assistant médical avec RAG sur documentation médicale

        IMPORTANT: Pour usage informatif uniquement, pas de diagnostic réel
        """
        # Embeddings médicaux
        self.embeddings = HuggingFaceEmbeddings(
            model_name="microsoft/BiomedNLP-PubMedBERT-base-uncased-abstract"
        )

        # LLM médical
        self.llm = pipeline(
            "text-generation",
            model="meta-llama/Meta-Llama-3-8B-Instruct",
            torch_dtype=torch.float16,
            device_map="auto"
        )

        # Charger documents médicaux
        self.load_knowledge_base(medical_docs_path)

    def load_knowledge_base(self, docs_path):
        """Charge et indexe la base de connaissances"""
        # Charger documents (PDF, txt, etc.)
        from langchain_community.document_loaders import DirectoryLoader

        loader = DirectoryLoader(docs_path, glob="**/*.pdf")
        docs = loader.load()

        # Chunking
        splitter = RecursiveCharacterTextSplitter(
            chunk_size=1000,
            chunk_overlap=200
        )
        chunks = splitter.split_documents(docs)

        # Créer vector store
        self.vectorstore = FAISS.from_documents(chunks, self.embeddings)
        print(f"✓ Base médicale chargée: {len(chunks)} chunks")

    def query(self, question, k=5):
        """
        Répondre à une question médicale

        Args:
            question: Question médicale
            k: Nombre de documents à récupérer
        """
        # Récupérer contexte pertinent
        docs = self.vectorstore.similarity_search(question, k=k)
        context = "\n\n".join([doc.page_content for doc in docs])

        # Prompt médical
        prompt = f"""Tu es un assistant médical expert. Utilise le contexte suivant pour répondre à la question.

IMPORTANT:
- Réponds de manière précise et factuelle
- Cite les sources si possible
- Si tu n'es pas sûr, dis-le clairement
- Recommande toujours de consulter un professionnel de santé

Contexte médical:
{context}

Question: {question}

Réponse détaillée:"""

        messages = [{"role": "user", "content": prompt}]

        response = self.llm(
            messages,
            max_new_tokens=500,
            temperature=0.3,  # Basse pour précision
            do_sample=True
        )

        answer = response[0]["generated_text"][-1]["content"]

        return {
            "answer": answer,
            "sources": [doc.metadata for doc in docs]
        }

# Utilisation
assistant = MedicalRAGAssistant("./medical_knowledge/")

result = assistant.query(
    "Quels sont les symptômes et traitements de l'hypertension?"
)

print("Réponse:", result["answer"])
print("\nSources:", result["sources"])

Compliance & Réglementation

⚠️ Exigences Légales Santé
  • HIPAA (USA) : Protection données patients, encryption, audit trails
  • RGPD (EU) : Consentement explicite, droit à l'oubli, pseudonymisation
  • FDA/CE Marking : Dispositifs médicaux IA nécessitent certification
  • Anonymisation : Retirer PII avant traitement (noms, dates, IDs)

Anonymisation de Données Médicales

import re
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine

def anonymize_medical_text(text):
    """Anonymise informations sensibles dans texte médical"""
    # Presidio (Microsoft) pour détection PII
    analyzer = AnalyzerEngine()
    anonymizer = AnonymizerEngine()

    # Analyser
    results = analyzer.analyze(
        text=text,
        language="fr",
        entities=["PERSON", "LOCATION", "DATE_TIME", "PHONE_NUMBER", "EMAIL"]
    )

    # Anonymiser
    anonymized = anonymizer.anonymize(
        text=text,
        analyzer_results=results
    )

    return anonymized.text

# Exemple
text = """
Patient: Jean Dupont, né le 15/03/1975
Adresse: 123 Rue de la Santé, Paris
Tel: 01 23 45 67 89
Diagnostic: Hypertension artérielle
"""

anonymized = anonymize_medical_text(text)
print(anonymized)
# Output: Patient: , né le 
#         Adresse: 
#         Tel: 
#         Diagnostic: Hypertension artérielle
💡 Conseil du Mentor La santé est un domaine ultra-régulé. Ne déployez JAMAIS une IA médicale sans validation par des experts médicaux et conformité légale. L'IA est un assistant, jamais un remplaçant du professionnel de santé.

Leçon 9 : IA pour la Finance

Détection de fraude, analyse financière, trading algorithmique, compliance...

Leçon 10 : IA pour le Juridique

Analyse de contrats, extraction de clauses, recherche jurisprudentielle...

Leçon 11 : IA pour l'Éducation

Tuteurs adaptatifs, génération de contenu pédagogique, évaluation automatisée...

Leçon 12 : IA pour le Code & DevOps

Agents de code, revue automatique, tests intelligents, CI/CD amélioré...

Leçon 13 : Contribution Open Source

Trouver des issues, écrire des PRs, code review, guidelines communautaires...

Leçon 14 : Certifications & Portfolio

HuggingFace, AWS ML, GCP ML, structure portfolio, blog, speaking...

Leçon 15 : Quiz Module 8.2

8 questions sur spécialisation & projets

Leçon 16 : Éthique de l'IA

Bias, fairness, transparency, explainability, frameworks IEEE/EU...

Leçon 17 : EU AI Act & Réglementation

Classification des risques, obligations par niveau, documentation requise...

Leçon 18 : Souveraineté Numérique

Modèles souverains, données locales, cloud souverain (Scaleway, OVH)...

Leçon 19 : Green AI

Empreinte carbone, efficacité énergétique, calcul avec CodeCarbon...

Leçon 20 : Stratégie IA en Entreprise

Framework ROI, build vs buy, roadmap adoption, change management...

Leçon 21 : Tendances 2026-2030

Reasoning models, world models, AGI debate, évolution régulation...

Leçon 22 : Lab Final - Projet Capstone

Projet Complet : Architecture IA Open Source Entreprise

Concevez une architecture complète intégrant RAG + agents + fine-tuning + MLOps + monitoring

Leçon 23 : Examen Final de Certification

Examen Complet : 16 Questions + Présentation Projet

Évaluation finale couvrant les 8 phases de la formation