Skip to content

Multimodal Embeddings Fresh 🌱

Combine multiple modalities in a single request for unified cross-modal retrieval and understanding.

Overview

Key Concept: Interleaved Input

Gemini Embedding 2 natively understands interleaved input — multiple modalities combined in a single request. This captures the complex relationships between different media types.

Single vs Multiple Entries

  • Single content entry with multiple parts = one aggregated embedding
  • Multiple entries in contents array = separate embeddings per entry

Step 1: Text + Image (Aggregated)

Combine text description with an image to get one unified embedding.

python
from google import genai
from google.genai import types

client = genai.Client()

with open('product.jpg', 'rb') as f:
    image_bytes = f.read()

# Single content with multiple parts = ONE aggregated embedding
result = client.models.embed_content(
    model='gemini-embedding-2-preview',
    contents=[
        types.Content(parts=[
            types.Part.from_text('Red leather handbag with gold hardware'),
            types.Part.from_bytes(data=image_bytes, mime_type='image/jpeg'),
        ])
    ]
)

# One unified embedding capturing both text and image
embedding = result.embeddings[0].values
print(f"Aggregated embedding: {len(embedding)} dimensions")

Step 2: Separate Embeddings for Multiple Items

Generate individual embeddings for each content entry.

python
from google import genai
from google.genai import types

client = genai.Client()

with open('photo1.jpg', 'rb') as f:
    img1 = f.read()
with open('photo2.jpg', 'rb') as f:
    img2 = f.read()

# Multiple content entries = SEPARATE embeddings
result = client.models.embed_content(
    model='gemini-embedding-2-preview',
    contents=[
        'A description of the first photo',
        types.Part.from_bytes(data=img1, mime_type='image/jpeg'),
        types.Part.from_bytes(data=img2, mime_type='image/jpeg'),
    ]
)

# Three separate embeddings
for i, emb in enumerate(result.embeddings):
    print(f"Entry {i+1}: {len(emb.values)} dimensions")

Step 3: Cross-Modal Search System

Build a search system where text queries find relevant images, videos, or audio.

python
from google import genai
from google.genai import types
import numpy as np

client = genai.Client()

def embed_text(text):
    result = client.models.embed_content(
        model='gemini-embedding-2-preview',
        contents=text,
        config=types.EmbedContentConfig(task_type='RETRIEVAL_QUERY')
    )
    return np.array(result.embeddings[0].values)

def embed_media(file_path, mime_type):
    with open(file_path, 'rb') as f:
        result = client.models.embed_content(
            model='gemini-embedding-2-preview',
            contents=[types.Part.from_bytes(data=f.read(), mime_type=mime_type)]
        )
    return np.array(result.embeddings[0].values)

def cosine_sim(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# Index various media
media_index = [
    {'path': 'sunset.jpg', 'mime': 'image/jpeg', 'type': 'image'},
    {'path': 'podcast.mp3', 'mime': 'audio/mp3', 'type': 'audio'},
    {'path': 'demo.mp4', 'mime': 'video/mp4', 'type': 'video'},
]

# Embed all media
for item in media_index:
    item['embedding'] = embed_media(item['path'], item['mime'])

# Search with text
query_vec = embed_text('beautiful nature scenery')

results = []
for item in media_index:
    sim = cosine_sim(query_vec, item['embedding'])
    results.append((item['path'], item['type'], sim))

# Sort by similarity
results.sort(key=lambda x: x[2], reverse=True)
for path, media_type, sim in results:
    print(f"{path} ({media_type}): {sim:.4f}")

Step 4: Multimodal RAG

Combine text and image context for richer retrieval.

python
from google import genai
from google.genai import types

client = genai.Client()

def embed_document_with_figure(text, image_path):
    """Create a single embedding that captures both text and its figure."""
    with open(image_path, 'rb') as f:
        image_bytes = f.read()

    result = client.models.embed_content(
        model='gemini-embedding-2-preview',
        contents=[
            types.Content(parts=[
                types.Part.from_text(text),
                types.Part.from_bytes(data=image_bytes, mime_type='image/png'),
            ])
        ]
    )
    return result.embeddings[0].values

# Index documents that have both text and figures
docs = [
    {
        'text': 'Figure 1 shows the architecture of a transformer model...',
        'figure': 'transformer_arch.png'
    },
    {
        'text': 'The training loss curve in Figure 2 demonstrates convergence...',
        'figure': 'loss_curve.png'
    },
]

for doc in docs:
    doc['embedding'] = embed_document_with_figure(doc['text'], doc['figure'])

Supported Combinations

CombinationUse CaseNotes
Text + ImageProduct search, visual QAMost common multimodal pattern
Text + AudioPodcast indexing, voice searchCaptures both description and sound
Text + VideoVideo search with metadataText provides additional context
Image + AudioScene understandingCaptures visual + audio context
Text + Image + AudioRich media indexingMaximum context capture
Text + PDFAnnotated document searchCombines metadata with document content

Verification Checklist

  • [ ] Text + Image aggregated embedding generated
  • [ ] Separate embeddings for multiple entries verified
  • [ ] Cross-modal search returning relevant results
  • [ ] Correct understanding of single vs multiple entry behavior
  • [ ] Consistent dimensions across all modality combinations
  • [ ] Overall 8192 token limit not exceeded

Troubleshooting

IssueSolution
Too many tokensReduce input size; total must be under 8192 tokens across all modalities
Unexpected single embeddingCheck if you used Content with parts (aggregated) vs array entries (separate)
Low cross-modal scoresUse task types; ensure descriptive text matches media semantics
Mixed model embeddingsNever compare embeddings from different models

See Also

Built with VitePress. Not affiliated with Google.