Multimodal AI systems process and generate content across multiple modalities — text, images, audio, and video. This chapter covers the architectures, capabilities, and practical applications of models that bridge these domains.

Vision-Language Models#

How Vision-Language Models Work#

Vision-language models (VLMs) combine a visual encoder with a language model, enabling understanding of images through natural language.

graph LR
    I[Image] --> VE[Vision Encoder<br/>ViT / SigLIP]
    VE --> P[Projection Layer<br/>Linear or MLP]
    P --> LLM[Language Model<br/>Decoder]
    T[Text Prompt] --> TOK[Tokenizer]
    TOK --> LLM
    LLM --> O[Text Output]

Architecture Patterns#

ModelVision EncoderLanguage ModelConnection Method
GPT-4V / GPT-4oProprietaryGPT-4Native multimodal training
Claude 3.x VisionProprietaryClaudeNative multimodal training
LLaVACLIP ViT-L/14Vicuna / LLaMALinear projection + instruction tuning
GeminiProprietaryGeminiNative multimodal (all modalities)
Qwen-VLViT-bigGQwenCross-attention + compression
InternVLInternViT-6BInternLMDynamic resolution + pixel shuffle

Using Vision Models#

from openai import OpenAI
import base64

client = OpenAI()

def analyze_image(image_path: str, question: str) -> str:
    """Analyze an image with GPT-4o."""
    
    with open(image_path, "rb") as f:
        image_data = base64.standard_b64encode(f.read()).decode("utf-8")
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": question},
                {"type": "image_url", "image_url": {
                    "url": f"data:image/png;base64,{image_data}",
                    "detail": "high"  # "low", "high", or "auto"
                }}
            ]
        }],
        max_tokens=1000
    )
    return response.choices[0].message.content


def analyze_image_url(url: str, question: str) -> str:
    """Analyze an image from URL."""
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": question},
                {"type": "image_url", "image_url": {"url": url}}
            ]
        }]
    )
    return response.choices[0].message.content

Vision Model Capabilities#

CapabilityDescriptionUse Cases
Image descriptionDetailed natural language descriptionsAccessibility, cataloging
OCR / text extractionRead text from imagesDocument processing, receipts
Visual reasoningAnswer questions requiring inferenceDiagram understanding, charts
Object detectionIdentify and locate objectsInventory, safety monitoring
Spatial understandingUnderstand layout and relationshipsUI analysis, architecture
Multi-image comparisonCompare or relate multiple imagesBefore/after, product matching
Chart/graph readingExtract data from visualizationsReport analysis, data extraction

Practical Considerations#

Token Cost for Images (GPT-4o):

Detail Level    Tokens Used         Best For
─────────────────────────────────────────────────
low             85 tokens           Quick classification, simple questions
high            85-1105 tokens      OCR, detailed analysis, small text
auto            Model decides       General use

High detail: image split into 512x512 tiles
Each tile = 170 tokens + 85 base tokens
Max: 2048x2048 before downscaling

LLaVA Architecture Deep Dive#

LLaVA (Large Language and Vision Assistant) demonstrates a clean, reproducible VLM architecture:

LLaVA Training Pipeline:

Stage 1: Feature Alignment (pretrain)
  - Freeze: Vision encoder + LLM
  - Train: Projection layer only
  - Data: 558K image-caption pairs
  - Goal: Align visual features to language embedding space

Stage 2: Visual Instruction Tuning
  - Freeze: Vision encoder
  - Train: Projection layer + LLM (full or LoRA)
  - Data: 665K multimodal instruction-following examples
  - Goal: Follow complex visual instructions

Image Generation#

Diffusion Models — Core Concept#

Diffusion models learn to reverse a gradual noising process. Training adds noise step by step; generation removes noise step by step.

graph LR
    A[Clean Image x0] --> |"Add noise<br/>step by step"| B[Pure Noise xT]
    B --> |"Denoise<br/>step by step"| C[Generated Image]
    
    D[Text Prompt] --> E[Text Encoder<br/>CLIP]
    E --> |conditioning| F[U-Net / DiT<br/>Denoiser]
Forward Process (Training):
  x0 → x1 → x2 → ... → xT (pure Gaussian noise)
  Each step adds a small amount of noise

Reverse Process (Generation):
  xT → xT-1 → ... → x1 → x0 (clean image)
  Model learns to predict and remove noise at each step

The model learns: given noisy image xt and timestep t,
predict the noise that was added (or predict x0 directly)

Latent Diffusion (Stable Diffusion)#

Instead of operating in pixel space (expensive), latent diffusion works in a compressed latent space:

Stable Diffusion Architecture:

Text Prompt ──→ CLIP Text Encoder ──→ Cross-Attention
                                           │
Random Noise ──→ U-Net (in latent space) ──┘
                      │
                      ↓ (iterative denoising)
                 Clean Latent
                      │
                      ↓
                 VAE Decoder ──→ Final Image (512x512 or higher)

Key insight: 512x512 image = 64x64 latent (64x less compute)

Image Generation Models Comparison#

ModelArchitectureResolutionKey Feature
Stable Diffusion 1.5Latent Diffusion (U-Net)512x512Open source, huge community
SDXLLatent Diffusion (larger U-Net)1024x1024Two-stage refinement
Stable Diffusion 3Latent Diffusion (DiT)1024x1024MMDiT architecture, better text
DALL-E 3Proprietary diffusion1024x1024Excellent prompt following
Midjourney v6ProprietaryUp to 2048x2048Aesthetic quality, artistic style
FluxRectified Flow (DiT)1024x1024+Fast, high quality, open weights
Imagen 3Cascaded diffusion1024x1024Google’s text-to-image

Using Image Generation APIs#

from openai import OpenAI

client = OpenAI()

def generate_image(prompt: str, size: str = "1024x1024") -> str:
    """Generate an image with DALL-E 3."""
    
    response = client.images.generate(
        model="dall-e-3",
        prompt=prompt,
        size=size,  # "1024x1024", "1792x1024", "1024x1792"
        quality="hd",  # "standard" or "hd"
        n=1
    )
    return response.data[0].url


def edit_image(image_path: str, mask_path: str, prompt: str) -> str:
    """Edit an image with inpainting (DALL-E 2)."""
    
    response = client.images.edit(
        model="dall-e-2",
        image=open(image_path, "rb"),
        mask=open(mask_path, "rb"),  # Transparent area = edit region
        prompt=prompt,
        size="1024x1024"
    )
    return response.data[0].url

Key Concepts in Image Generation#

ConceptDescription
CFG (Classifier-Free Guidance)Controls how strongly the model follows the prompt (higher = more literal)
Negative promptsDescribe what you don’t want in the image
Steps / iterationsMore steps = higher quality but slower
SeedRandom seed for reproducibility
ControlNetAdditional conditioning (pose, depth, edges)
LoRALightweight fine-tuning for specific styles or subjects
InpaintingEdit specific regions of an existing image
Img2imgGenerate new image guided by a reference image
UpscalingIncrease resolution of generated images

Speech and Audio Models#

Speech-to-Text (ASR)#

ModelProviderKey Features
WhisperOpenAI (open source)Multilingual, robust, multiple sizes
Whisper large-v3OpenAIBest open ASR, 99 languages
Deepgram Nova-2DeepgramFast, streaming, enterprise
Google USMGoogle300+ languages
Assembly AIAssembly AISpeaker diarization, sentiment
from openai import OpenAI

client = OpenAI()

def transcribe_audio(audio_path: str, language: str = None) -> str:
    """Transcribe audio using Whisper."""
    
    with open(audio_path, "rb") as audio_file:
        transcript = client.audio.transcriptions.create(
            model="whisper-1",
            file=audio_file,
            language=language,  # ISO 639-1 code, or None for auto-detect
            response_format="verbose_json",
            timestamp_granularities=["segment"]
        )
    return transcript


def translate_audio(audio_path: str) -> str:
    """Translate any language audio to English text."""
    
    with open(audio_path, "rb") as audio_file:
        translation = client.audio.translations.create(
            model="whisper-1",
            file=audio_file
        )
    return translation.text

Text-to-Speech (TTS)#

ModelProviderKey Features
OpenAI TTSOpenAI6 voices, fast, natural
ElevenLabsElevenLabsVoice cloning, emotion control, 29 languages
BarkSuno (open source)Music, sound effects, multilingual
XTTSCoqui (open source)Voice cloning with 6s reference
Azure Neural TTSMicrosoft400+ voices, SSML control
def text_to_speech(text: str, voice: str = "alloy") -> bytes:
    """Generate speech from text using OpenAI TTS."""
    
    response = client.audio.speech.create(
        model="tts-1-hd",  # "tts-1" (fast) or "tts-1-hd" (quality)
        voice=voice,  # alloy, echo, fable, onyx, nova, shimmer
        input=text,
        speed=1.0  # 0.25 to 4.0
    )
    return response.content  # Raw audio bytes (mp3)

Audio Understanding#

Modern multimodal models can directly process audio:

# GPT-4o with audio input (native audio understanding)
def analyze_audio(audio_path: str, question: str) -> str:
    """Analyze audio content with GPT-4o audio."""
    
    import base64
    with open(audio_path, "rb") as f:
        audio_data = base64.standard_b64encode(f.read()).decode()
    
    response = client.chat.completions.create(
        model="gpt-4o-audio-preview",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": question},
                {"type": "input_audio", "input_audio": {
                    "data": audio_data,
                    "format": "mp3"
                }}
            ]
        }]
    )
    return response.choices[0].message.content

Video Generation#

Current State of Video Generation#

ModelProviderDurationResolutionKey Feature
SoraOpenAIUp to 60s1080pRealistic physics, long coherence
Runway Gen-3 AlphaRunway10s1080pFast, controllable motion
KlingKuaishou5-10s1080pOpen access, good quality
PikaPika Labs4s1080pEasy to use, style control
Stable Video DiffusionStability AI4s576x1024Open source
Veo 2Google8s+4KCinematic quality

Video Generation Architecture#

Video Diffusion Pipeline:

Text Prompt ──→ Text Encoder
                    │
                    ↓
Random Noise ──→ Spatial-Temporal U-Net/DiT ──→ Clean Latent Frames
(T frames)       │                                    │
                 │ Temporal attention                  ↓
                 │ (consistency across frames)    VAE Decoder
                 │                                    │
                 └── Spatial attention                ↓
                     (quality per frame)         Video Output

Key challenges:
- Temporal consistency (objects don't flicker/morph)
- Physics simulation (gravity, collisions, fluid)
- Long-range coherence (beginning matches end)
- Camera motion (smooth, intentional movement)

Video Generation Approaches#

ApproachDescriptionExample
Text-to-videoGenerate video from text descriptionSora, Runway Gen-3
Image-to-videoAnimate a still imageStable Video Diffusion
Video-to-videoTransform existing video styleRunway, Pika
InterpolationGenerate frames between keyframesFilm VFI models

Multimodal Embeddings#

CLIP (Contrastive Language-Image Pre-training)#

CLIP learns a shared embedding space for text and images through contrastive learning:

graph TD
    subgraph "Training: Contrastive Learning"
        I1[Image 1] --> IE[Image Encoder<br/>ViT]
        I2[Image 2] --> IE
        I3[Image N] --> IE
        T1[Text 1] --> TE[Text Encoder<br/>Transformer]
        T2[Text 2] --> TE
        T3[Text N] --> TE
        IE --> S[Similarity Matrix<br/>Maximize diagonal]
        TE --> S
    end
CLIP Contrastive Learning:

Given a batch of N (image, text) pairs:
- Encode all images → N image embeddings
- Encode all texts → N text embeddings
- Compute N×N similarity matrix
- Maximize similarity for matching pairs (diagonal)
- Minimize similarity for non-matching pairs (off-diagonal)

Result: images and text that describe the same thing
        land near each other in embedding space
from transformers import CLIPProcessor, CLIPModel
from PIL import Image
import torch

model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")

def get_image_embedding(image_path: str) -> torch.Tensor:
    """Get CLIP embedding for an image."""
    image = Image.open(image_path)
    inputs = processor(images=image, return_tensors="pt")
    embedding = model.get_image_features(**inputs)
    return embedding / embedding.norm(dim=-1, keepdim=True)

def get_text_embedding(text: str) -> torch.Tensor:
    """Get CLIP embedding for text."""
    inputs = processor(text=text, return_tensors="pt", padding=True)
    embedding = model.get_text_features(**inputs)
    return embedding / embedding.norm(dim=-1, keepdim=True)

def image_text_similarity(image_path: str, texts: list[str]) -> list[float]:
    """Score how well each text describes the image."""
    image = Image.open(image_path)
    inputs = processor(text=texts, images=image, return_tensors="pt", padding=True)
    outputs = model(**inputs)
    # Cosine similarity scaled to [0, 1]
    similarities = outputs.logits_per_image.softmax(dim=1)
    return similarities[0].tolist()

Applications of Multimodal Embeddings#

ApplicationHow It Works
Image search by textEmbed query text, find nearest images
Image search by imageEmbed query image, find similar images
Zero-shot classificationCompare image embedding to class label embeddings
Content moderationDetect similarity to known harmful content
Product matchingMatch product photos to catalog descriptions
AccessibilityAuto-generate alt text by finding closest descriptions

Architecture Patterns for Multimodal Systems#

Early vs Late Fusion#

Early Fusion:
  Modalities combined BEFORE main processing
  ┌─────────┐   ┌─────────┐
  │  Image  │   │  Text   │
  └────┬────┘   └────┬────┘
       │              │
       └──────┬───────┘  ← Combine early (concatenate, project)
              │
       ┌──────┴──────┐
       │  Shared      │
       │  Transformer │
       └──────┬──────┘
              │
         Output

Late Fusion:
  Modalities processed SEPARATELY, combined at the end
  ┌─────────┐   ┌─────────┐
  │  Image  │   │  Text   │
  └────┬────┘   └────┬────┘
       │              │
  ┌────┴────┐   ┌────┴────┐
  │ Vision  │   │Language │
  │ Model   │   │ Model   │
  └────┬────┘   └────┬────┘
       │              │
       └──────┬───────┘  ← Combine late (attention, MLP)
              │
         Output
PatternProsConsExamples
Early fusionDeep cross-modal interactionExpensive to train from scratchGemini, GPT-4o
Late fusionReuse pretrained unimodal modelsLimited cross-modal reasoningLLaVA, BLIP-2
Cross-attentionFlexible, modularAdded complexityFlamingo, Qwen-VL
Shared embeddingSimple, efficientShallow interactionCLIP-based systems

Cross-Attention Fusion#

graph TD
    V[Vision Features] --> KV[Keys + Values]
    T[Text Tokens] --> Q[Queries]
    Q --> CA[Cross-Attention<br/>Text attends to vision]
    KV --> CA
    CA --> FF[Feed-Forward]
    FF --> O[Multimodal Output]

Used in models like Flamingo — language model tokens attend to visual features at specific layers, allowing the LLM to “look at” the image when generating each token.

Choosing an Architecture#

Use CaseRecommended PatternReasoning
General multimodal chatEarly fusion (native)Best cross-modal understanding
Add vision to existing LLMLate fusion + projectionReuse pretrained LLM weights
Image-text retrievalShared embedding (CLIP-style)Efficient similarity search
Document understandingCross-attentionNeed fine-grained text-image alignment
Real-time applicationsLate fusionCan cache modality encodings separately

Practical Applications#

Document Understanding Pipeline#

def process_document(image_path: str) -> dict:
    """Extract structured data from a document image."""
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": """Analyze this document and extract:
1. Document type (invoice, receipt, form, letter, etc.)
2. Key fields as structured JSON
3. Any tables present (as arrays)
4. Handwritten notes if any

Return as JSON."""},
                {"type": "image_url", "image_url": {
                    "url": f"data:image/png;base64,{encode_image(image_path)}",
                    "detail": "high"
                }}
            ]
        }],
        response_format={"type": "json_object"}
    )
    return json.loads(response.choices[0].message.content)

Multimodal RAG#

graph TD
    D[Document with<br/>Text + Images] --> C[Chunking]
    C --> TE[Text Chunks<br/>→ Text Embeddings]
    C --> IE[Image Chunks<br/>→ CLIP Embeddings]
    TE --> VS[Vector Store]
    IE --> VS
    
    Q[User Query] --> QE[Query Embedding]
    QE --> VS
    VS --> R[Retrieved Chunks<br/>text + images]
    R --> VLM[Vision-Language Model]
    VLM --> A[Answer with<br/>visual references]

Real-World Multimodal Applications#

DomainApplicationModalitiesModel Choice
E-commerceProduct search and matchingImage + TextCLIP + LLM
HealthcareMedical image analysis + reportImage + TextSpecialized VLM
AccessibilityImage descriptions for blind usersImage → TextGPT-4o, Claude
EducationDiagram explanationImage + TextVLM with reasoning
ManufacturingDefect detection + reportingImage + TextFine-tuned VLM
MediaAuto-captioning, content moderationImage/Video + TextCLIP + classifiers
Customer supportVisual troubleshootingImage + Text + AudioMultimodal chat
LegalContract analysis with signaturesImage + TextDocument VLM

Building a Multimodal Application#

class MultimodalAssistant:
    """Assistant that handles text, images, and audio."""
    
    def __init__(self):
        self.client = OpenAI()
        self.conversation = []
    
    def process_input(self, text: str = None, image_path: str = None, 
                      audio_path: str = None) -> str:
        """Process any combination of modalities."""
        
        content = []
        
        if text:
            content.append({"type": "text", "text": text})
        
        if image_path:
            with open(image_path, "rb") as f:
                img_data = base64.standard_b64encode(f.read()).decode()
            content.append({"type": "image_url", "image_url": {
                "url": f"data:image/png;base64,{img_data}"
            }})
        
        if audio_path:
            # Transcribe audio first, then include as text
            transcript = self.client.audio.transcriptions.create(
                model="whisper-1",
                file=open(audio_path, "rb")
            )
            content.append({"type": "text", 
                          "text": f"[Audio transcript]: {transcript.text}"})
        
        self.conversation.append({"role": "user", "content": content})
        
        response = self.client.chat.completions.create(
            model="gpt-4o",
            messages=self.conversation,
            max_tokens=2000
        )
        
        assistant_msg = response.choices[0].message.content
        self.conversation.append({"role": "assistant", "content": assistant_msg})
        return assistant_msg

Key Takeaways#

  1. Vision-language models bridge seeing and understanding — architectures like LLaVA show that connecting a frozen vision encoder to an LLM via a simple projection layer, followed by instruction tuning, produces capable visual assistants.

  2. Diffusion models dominate image generation — by learning to reverse a noising process in latent space, models like Stable Diffusion and DALL-E 3 generate high-quality images from text descriptions with fine-grained control.

  3. Audio AI is mature and accessible — Whisper provides near-human transcription across 99 languages, while TTS models like ElevenLabs enable natural voice synthesis with emotion and style control.

  4. Video generation is the current frontier — models like Sora demonstrate emergent physics understanding, but temporal consistency and long-form coherence remain active research challenges.

  5. CLIP’s shared embedding space enables zero-shot multimodal search — by training image and text encoders contrastively, CLIP enables searching images with text (and vice versa) without task-specific training.

  6. Architecture choice depends on the use case — early fusion (Gemini, GPT-4o) gives deepest cross-modal understanding; late fusion (LLaVA) lets you reuse existing models; shared embeddings (CLIP) enable efficient retrieval.

  7. Multimodal applications combine multiple models — real-world systems often chain specialized models (Whisper → LLM → TTS, or CLIP → VLM → structured output) rather than relying on a single model for everything.