Embeddings Multimodais: Conectando as Modalidades de IA
Unifique texto, imagens e áudio em espaços de incorporação compartilhados.
Embeddings multimodais representam um avanço na inteligência artificial, permitindo compreensão e raciocínio através de diferentes tipos de dados dentro de um espaço de representação unificado.
Esta tecnologia impulsiona aplicações multimodais modernas, desde busca por imagens até geração de conteúdo.
Para um guia completo sobre a construção de sistemas de geração aumentada por recuperação com embeddings multimodais, consulte o Tutorial de Geração Aumentada por Recuperação (RAG): Arquitetura, Implementação e Guia de Produção.
Esta imagem é do artigo:
CrossCLR: aprendizado contrastivo multimodal para representações de vídeo multimodais, por Mohammadreza Zolfaghari e outros
Compreendendo Embeddings Multimodais
Embeddings multimodais são representações vetoriais que codificam informações de diferentes modalidades — como texto, imagens, áudio e vídeo — em um espaço de embedding compartilhado. Diferente dos embeddings tradicionais de modalidade única, as abordagens multimodais aprendem uma representação unificada onde conceitos semanticamente semelhantes se agrupam juntos, independentemente do seu formato original.
O Que São Embeddings Multimodais?
Em sua essência, embeddings multimodais resolvem um desafio crítico em IA: como comparar e relacionar informações através de diferentes tipos de dados. Um classificador de imagens tradicional só funciona com imagens, enquanto um modelo de texto lida apenas com texto. Embeddings multimodais preenchem essa lacuna projetando diferentes modalidades em um espaço vetorial comum onde:
- Uma imagem de um gato e a palavra “gato” possuem vetores de embedding semelhantes
- Relações semânticas são preservadas entre modalidades
- Métricas de distância (similaridade cosseno, distância euclidiana) medem a similaridade multimodal
Esta representação unificada habilita capacidades poderosas, como buscar imagens usando consultas de texto, gerar legendas a partir de imagens ou até classificação zero-shot sem treinamento específico para a tarefa.
A Arquitetura por Trás do Aprendizado Multimodal
Sistemas multimodais modernos normalmente empregam arquiteturas de codificadores duplos com objetivos de aprendizado contrastivo:
Codificadores Duplos: Redes neurais separadas codificam cada modalidade. Por exemplo, o CLIP usa:
- Um transformador de visão (ViT) ou ResNet para imagens
- Um codificador de texto baseado em transformador para linguagem
Aprendizado Contrastivo: O modelo aprende maximizando a similaridade entre pares correspondentes (ex: imagem e sua legenda) enquanto minimiza a similaridade entre pares não correspondentes. A função de perda contrastiva pode ser expressa como:
[ \mathcal{L} = -\log \frac{\exp(\text{sim}(v_i, t_i) / \tau)}{\sum_{j=1}^{N} \exp(\text{sim}(v_i, t_j) / \tau)} ]
onde (v_i) é o embedding da imagem, (t_i) é o embedding do texto, (\text{sim}) é a similaridade (tipicamente cosseno) e (\tau) é um parâmetro de temperatura.
Tecnologias e Modelos Principais
CLIP: Pioneirismo na Compreensão Visão-Linguagem
O CLIP (Contrastive Language-Image Pre-training) da OpenAI revolucionou o campo ao ser treinado em 400 milhões de pares de imagem-texto da internet. A arquitetura do modelo consiste em:
import torch
from transformers import CLIPProcessor, CLIPModel
from PIL import Image
# Carregar modelo CLIP pré-treinado
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
# Preparar entradas
image = Image.open("example.jpg")
texts = ["a photo of a cat", "a photo of a dog", "a photo of a bird"]
# Processar e obter embeddings
inputs = processor(text=texts, images=image, return_tensors="pt", padding=True)
# Obter pontuações de similaridade multimodal
outputs = model(**inputs)
logits_per_image = outputs.logits_per_image
probs = logits_per_image.softmax(dim=1)
print(f"Probabilidades: {probs}")
A inovação chave do CLIP foi escala e simplicidade. Ao ser treinado em dados massivos em escala web sem anotações manuais, ele alcançou capacidades notáveis de transferência zero-shot. Como o CLIP difere de modelos de visão tradicionais? Ao contrário de classificadores supervisionados treinados em conjuntos de rótulos fixos, o CLIP aprende com supervisão de linguagem natural, tornando-o adaptável a qualquer conceito visual describível em texto.
ImageBind: Estendendo-se a Seis Modalidades
O ImageBind da Meta estende embeddings multimodais além de visão e linguagem para incluir:
- Áudio (sons ambientais, fala)
- Informações de profundidade
- Imagem térmica
- Dados de IMU (sensor de movimento)
Isso cria um espaço de embedding verdadeiramente multimodal onde todas as seis modalidades estão alinhadas. A percepção chave é que as imagens servem como uma modalidade de “ligação” — ao emparelhar imagens com outras modalidades e alavancar o alinhamento visão-linguagem existente, o ImageBind cria um espaço unificado sem exigir todos os pares possíveis de modalidades durante o treinamento.
Alternativas de Código Aberto
O ecossistema expandiu-se com várias implementações de código aberto:
OpenCLIP: Uma implementação comunitária oferecendo modelos maiores e receitas de treinamento diversificadas:
import open_clip
model, _, preprocess = open_clip.create_model_and_transforms(
'ViT-L-14',
pretrained='laion2b_s32b_b82k'
)
tokenizer = open_clip.get_tokenizer('ViT-L-14')
Modelos LAION-5B: Treinados no massivo conjunto de dados aberto LAION-5B, fornecendo alternativas a modelos proprietários com desempenho comparável ou superior.
Para desenvolvedores interessados em soluções de embedding de texto de última geração com código aberto, os Modelos de Embedding e Reranker Qwen3 no Ollama oferecem excelente desempenho multilíngue com implantação local fácil.
Estratégias de Implementação
Construindo um Sistema de Busca Multimodal
Uma implementação prática de embeddings multimodais para busca semântica envolve vários componentes. Quais são as principais aplicações de embeddings multimodais? Eles impulsionam casos de uso desde busca de produtos no comércio eletrônico até moderação de conteúdo e ferramentas criativas.
import numpy as np
from typing import List, Tuple
import faiss
from transformers import CLIPModel, CLIPProcessor
class CrossModalSearchEngine:
def __init__(self, model_name: str = "openai/clip-vit-base-patch32"):
self.model = CLIPModel.from_pretrained(model_name)
self.processor = CLIPProcessor.from_pretrained(model_name)
self.image_index = None
self.image_metadata = []
def encode_images(self, images: List) -> np.ndarray:
"""Codifica imagens em embeddings"""
inputs = self.processor(images=images, return_tensors="pt", padding=True)
with torch.no_grad():
embeddings = self.model.get_image_features(**inputs)
return embeddings.cpu().numpy()
def encode_text(self, texts: List[str]) -> np.ndarray:
"""Codifica consultas de texto em embeddings"""
inputs = self.processor(text=texts, return_tensors="pt", padding=True)
with torch.no_grad():
embeddings = self.model.get_text_features(**inputs)
return embeddings.cpu().numpy()
def build_index(self, image_embeddings: np.ndarray):
"""Constrói índice FAISS para busca eficiente de similaridade"""
dimension = image_embeddings.shape[1]
# Normalizar embeddings para similaridade cosseno
faiss.normalize_L2(image_embeddings)
# Criar índice (usando HNSW para implantação em grande escala)
self.image_index = faiss.IndexHNSWFlat(dimension, 32)
self.image_index.hnsw.efConstruction = 40
self.image_index.add(image_embeddings)
def search(self, query: str, k: int = 10) -> List[Tuple[int, float]]:
"""Busca imagens usando consulta de texto"""
query_embedding = self.encode_text([query])
faiss.normalize_L2(query_embedding)
distances, indices = self.image_index.search(query_embedding, k)
return list(zip(indices[0], distances[0]))
# Exemplo de uso
engine = CrossModalSearchEngine()
# Construir índice a partir de coleção de imagens
image_embeddings = engine.encode_images(image_collection)
engine.build_index(image_embeddings)
# Buscar com texto
results = engine.search("sunset over mountains", k=5)
Ajuste Fino para Tarefas Específicas de Domínio
Enquanto modelos pré-treinados funcionam bem para propósitos gerais, aplicações específicas de domínio se beneficiam de ajuste fino:
from transformers import CLIPModel, CLIPProcessor, AdamW
import torch.nn as nn
class FineTuneCLIP:
def __init__(self, model_name: str, num_epochs: int = 10):
self.model = CLIPModel.from_pretrained(model_name)
self.processor = CLIPProcessor.from_pretrained(model_name)
self.num_epochs = num_epochs
def contrastive_loss(self, image_embeddings, text_embeddings, temperature=0.07):
"""Computa perda contrastiva InfoNCE"""
# Normalizar embeddings
image_embeddings = nn.functional.normalize(image_embeddings, dim=1)
text_embeddings = nn.functional.normalize(text_embeddings, dim=1)
# Computar matriz de similaridade
logits = torch.matmul(image_embeddings, text_embeddings.T) / temperature
# Rótulos são diagonais (pares correspondentes)
labels = torch.arange(len(logits), device=logits.device)
# Perda simétrica
loss_i = nn.functional.cross_entropy(logits, labels)
loss_t = nn.functional.cross_entropy(logits.T, labels)
return (loss_i + loss_t) / 2
def train(self, dataloader, learning_rate=5e-6):
"""Ajuste fino em dados específicos de domínio"""
optimizer = AdamW(self.model.parameters(), lr=learning_rate)
self.model.train()
for epoch in range(self.num_epochs):
total_loss = 0
for batch in dataloader:
images, texts = batch['images'], batch['texts']
# Processar entradas
inputs = self.processor(
text=texts,
images=images,
return_tensors="pt",
padding=True
)
# Passa para frente
outputs = self.model(**inputs, return_loss=True)
loss = outputs.loss
# Passa para trás
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
avg_loss = total_loss / len(dataloader)
print(f"Epoch {epoch+1}/{self.num_epochs}, Loss: {avg_loss:.4f}")
Considerações de Implantação em Produção
Otimizando Desempenho de Inferência
Como posso otimizar embeddings multimodais para produção? A otimização de desempenho é crítica para implantação no mundo real:
Quantização de Modelo: Reduzir o tamanho do modelo e aumentar a velocidade de inferência:
import torch
from torch.quantization import quantize_dynamic
# Quantização dinâmica para inferência em CPU
quantized_model = quantize_dynamic(
model,
{torch.nn.Linear},
dtype=torch.qint8
)
Conversão ONNX: Exportar para ONNX para inferência otimizada:
import torch.onnx
dummy_input = processor(text=["sample"], return_tensors="pt")
torch.onnx.export(
model,
tuple(dummy_input.values()),
"clip_model.onnx",
input_names=['input_ids', 'attention_mask'],
output_names=['output'],
dynamic_axes={
'input_ids': {0: 'batch_size'},
'attention_mask': {0: 'batch_size'}
}
)
Processamento em Lote: Maximizar a utilização de GPU através de loteamento:
def batch_encode(items: List, batch_size: int = 32):
"""Processar itens em lotes para eficiência"""
embeddings = []
for i in range(0, len(items), batch_size):
batch = items[i:i+batch_size]
batch_embeddings = encode_batch(batch)
embeddings.append(batch_embeddings)
return np.concatenate(embeddings, axis=0)
Armazenamento Vetorial Escalável
Para aplicações em grande escala, bancos de dados vetoriais são essenciais. Quais frameworks suportam implementações de embeddings multimodais? Além dos próprios modelos, a infraestrutura importa:
FAISS (Facebook AI Similarity Search): Biblioteca eficiente de busca de similaridade
- Suporta bilhões de vetores
- Múltiplos tipos de índice (flat, IVF, HNSW)
- Aceleração GPU disponível
Milvus: Banco de dados vetorial de código aberto
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
# Definir esquema
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=512),
FieldSchema(name="metadata", dtype=DataType.JSON)
]
schema = CollectionSchema(fields, description="Image embeddings")
# Criar coleção
collection = Collection("images", schema)
# Criar índice
index_params = {
"metric_type": "IP", # Produto interno (similaridade cosseno)
"index_type": "IVF_FLAT",
"params": {"nlist": 1024}
}
collection.create_index("embedding", index_params)
Pinecone/Weaviate: Serviços gerenciados de banco de dados vetoriais oferecendo escalabilidade e manutenção fáceis.
Casos de Uso Avançados e Aplicações
Classificação Zero-Shot
Embeddings multimodais habilitam classificação sem treinamento específico para a tarefa. Esta capacidade vai além das abordagens tradicionais de visão computacional — por exemplo, se você estiver interessado em detecção de objetos com TensorFlow mais especializada, isso representa uma abordagem complementar de aprendizado supervisionado para tarefas de detecção específicas.
def zero_shot_classify(image, candidate_labels: List[str], model, processor):
"""Classifica imagem em categorias arbitrárias"""
# Criar prompts de texto
text_inputs = [f"a photo of a {label}" for label in candidate_labels]
# Obter embeddings
inputs = processor(
text=text_inputs,
images=image,
return_tensors="pt",
padding=True
)
outputs = model(**inputs)
# Computar probabilidades
logits = outputs.logits_per_image
probs = logits.softmax(dim=1)
# Retornar previsões classificadas
sorted_indices = probs.argsort(descending=True)[0]
return [(candidate_labels[idx], probs[0][idx].item()) for idx in sorted_indices]
# Exemplo de uso
labels = ["cat", "dog", "bird", "fish", "horse"]
predictions = zero_shot_classify(image, labels, model, processor)
print(f"Top prediction: {predictions[0]}")
RAG Multimodal (Geração Aumentada por Recuperação)
Combinar embeddings multimodais com modelos de linguagem cria sistemas RAG multimodais poderosos. Uma vez que você recuperou documentos relevantes, a reclassificação com modelos de embedding pode melhorar significativamente a qualidade dos resultados reordenando os candidatos recuperados com base na relevância:
class MultimodalRAG:
def __init__(self, clip_model, llm_model):
self.clip = clip_model
self.llm = llm_model
self.knowledge_base = []
def add_documents(self, images, texts, metadata):
"""Adiciona documentos multimodais à base de conhecimento"""
image_embeds = self.clip.encode_images(images)
text_embeds = self.clip.encode_text(texts)
# Armazenar informações combinadas
for i, (img_emb, txt_emb, meta) in enumerate(
zip(image_embeds, text_embeds, metadata)
):
self.knowledge_base.append({
'image_embedding': img_emb,
'text_embedding': txt_emb,
'metadata': meta,
'index': i
})
def retrieve(self, query: str, k: int = 5):
"""Recupera conteúdo multimodal relevante"""
query_embedding = self.clip.encode_text([query])[0]
# Computar similaridades
similarities = []
for doc in self.knowledge_base:
# Similaridade média entre modalidades
img_sim = np.dot(query_embedding, doc['image_embedding'])
txt_sim = np.dot(query_embedding, doc['text_embedding'])
combined_sim = (img_sim + txt_sim) / 2
similarities.append((combined_sim, doc))
# Retornar top-k
similarities.sort(reverse=True)
return [doc for _, doc in similarities[:k]]
def answer_query(self, query: str):
"""Responde à consulta usando contexto multimodal recuperado"""
retrieved_docs = self.retrieve(query)
# Construir contexto a partir de documentos recuperados
context = "\n".join([doc['metadata']['text'] for doc in retrieved_docs])
# Gerar resposta com LLM
prompt = f"Context:\n{context}\n\nQuestion: {query}\n\nAnswer:"
answer = self.llm.generate(prompt)
return answer, retrieved_docs
Se você estiver implementando sistemas RAG de produção em Go, pode achar este guia sobre reclassificação de documentos de texto com Ollama e modelo de embedding Qwen3 em Go particularmente útil para otimizar a qualidade da recuperação.
Moderação de Conteúdo e Segurança
Embeddings multimodais são excelentes na detecção de conteúdo inadequado entre modalidades:
class ContentModerator:
def __init__(self, model, processor):
self.model = model
self.processor = processor
# Definir categorias de segurança
self.unsafe_categories = [
"violent content",
"adult content",
"hateful imagery",
"graphic violence",
"explicit material"
]
self.safe_categories = [
"safe for work",
"family friendly",
"educational content"
]
def moderate_image(self, image, threshold: float = 0.3):
"""Verifica se a imagem contém conteúdo inseguro"""
# Combinar todas as categorias
all_categories = self.unsafe_categories + self.safe_categories
text_inputs = [f"image containing {cat}" for cat in all_categories]
# Obter previsões
inputs = self.processor(
text=text_inputs,
images=image,
return_tensors="pt"
)
outputs = self.model(**inputs)
probs = outputs.logits_per_image.softmax(dim=1)[0]
# Verificar categorias inseguras
unsafe_scores = probs[:len(self.unsafe_categories)]
max_unsafe_score = unsafe_scores.max().item()
return {
'is_safe': max_unsafe_score < threshold,
'confidence': 1 - max_unsafe_score,
'flagged_categories': [
self.unsafe_categories[i]
for i, score in enumerate(unsafe_scores)
if score > threshold
]
}
Melhores Práticas e Armadilhas Comuns
Pré-processamento de Dados
Pré-processamento adequado é crucial para desempenho ótimo:
from torchvision import transforms
# Pré-processamento padrão do CLIP
clip_transform = transforms.Compose([
transforms.Resize(224, interpolation=transforms.InterpolationMode.BICUBIC),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.48145466, 0.4578275, 0.40821073],
std=[0.26862954, 0.26130258, 0.27577711]
)
])
Lidando com Viés e Justiça
Modelos multimodais podem herdar vieses dos dados de treinamento:
Estratégias de mitigação:
- Avaliar em grupos demográficos diversos
- Usar técnicas de desviesamento durante o ajuste fino
- Implementar recuperação consciente de justiça
- Auditoria e monitoramento regulares em produção
Avaliação da Qualidade de Embedding
Monitore a qualidade de embedding em produção:
def assess_embedding_quality(embeddings: np.ndarray):
"""Computa métricas para qualidade de embedding"""
# Computar distância par a par média
distances = np.linalg.norm(
embeddings[:, None] - embeddings[None, :],
axis=2
)
avg_distance = distances.mean()
# Verificar agrupamento (baixa distância intra-cluster)
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=10)
labels = kmeans.fit_predict(embeddings)
# Computar pontuação de silhueta
from sklearn.metrics import silhouette_score
score = silhouette_score(embeddings, labels)
return {
'avg_pairwise_distance': avg_distance,
'silhouette_score': score
}
Exemplo de Implantação Docker
Embalhe sua aplicação multimodal para fácil implantação:
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
# Instalar Python e dependências
RUN apt-get update && apt-get install -y python3-pip
WORKDIR /app
# Instalar requisitos
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt
# Copiar código da aplicação
COPY . .
# Expor porta da API
EXPOSE 8000
# Executar servidor API
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000"]
# api.py - Servidor FastAPI para embeddings multimodais
from fastapi import FastAPI, File, UploadFile
from pydantic import BaseModel
import torch
from PIL import Image
import io
app = FastAPI()
# Carregar modelo na inicialização
@app.on_event("startup")
async def load_model():
global model, processor
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
if torch.cuda.is_available():
model = model.cuda()
class TextQuery(BaseModel):
text: str
@app.post("/embed/text")
async def embed_text(query: TextQuery):
inputs = processor(text=[query.text], return_tensors="pt", padding=True)
if torch.cuda.is_available():
inputs = {k: v.cuda() for k, v in inputs.items()}
with torch.no_grad():
embeddings = model.get_text_features(**inputs)
return {"embedding": embeddings.cpu().numpy().tolist()}
@app.post("/embed/image")
async def embed_image(file: UploadFile = File(...)):
image = Image.open(io.BytesIO(await file.read()))
inputs = processor(images=image, return_tensors="pt")
if torch.cuda.is_available():
inputs = {k: v.cuda() for k, v in inputs.items()}
with torch.no_grad():
embeddings = model.get_image_features(**inputs)
return {"embedding": embeddings.cpu().numpy().tolist()}
@app.post("/similarity")
async def compute_similarity(
text: TextQuery,
file: UploadFile = File(...)
):
# Obter ambos os embeddings
image = Image.open(io.BytesIO(await file.read()))
inputs = processor(text=[text.text], images=image, return_tensors="pt", padding=True)
if torch.cuda.is_available():
inputs = {k: v.cuda() for k, v in inputs.items()}
outputs = model(**inputs)
similarity = outputs.logits_per_image[0][0].item()
return {"similarity": similarity}
Direções Futuras
O campo de embeddings multimodais continua a evoluir rapidamente:
Cobertura de Maior Modalidade: Modelos futuros provavelmente incorporarão modalidades adicionais como tato (feedback háptico), cheiro e dados de sabor para compreensão multimodal verdadeiramente abrangente.
Eficiência Melhorada: Pesquisa em destilação e arquiteturas eficientes tornará modelos multimodais poderosos acessíveis em dispositivos de borda.
Melhor Alinhamento: Técnicas avançadas para alinhar modalidades com mais precisão, incluindo perdas de consistência cíclica e treinamento adversarial.
Compreposição Composicional: Mover além do reconhecimento de objetos simples para entender relacionamentos complexos e composições entre modalidades.
Modelagem Temporal: Melhor manuseio de vídeo e dados de séries temporais com raciocínio temporal explícito em espaços de embedding.
Links Úteis
- Embeddings de texto para RAG e busca — APIs de embedding apenas texto e fundamentos de recuperação RAG neste site
- Repositório OpenAI CLIP
- OpenCLIP: Implementação de Código Aberto
- Documentação Hugging Face Transformers
- Meta ImageBind
- Conjunto de Dados LAION-5B
- Documentação FAISS
- Banco de Dados Vetorial Milvus
- Banco de Dados Vetorial Pinecone
- Artigo: Learning Transferable Visual Models From Natural Language Supervision
- Artigo: ImageBind: One Embedding Space To Bind Them All
- Detecção de objetos com TensorFlow usando concreto Reinforcement Bar Caps
- Reranking com modelos de embedding
- Reranking de documentos de texto com Ollama e modelo de embedding Qwen3 - em Go
- Modelos de Embedding e Reranker Qwen3 no Ollama: Desempenho de Última Geração
Embeddings multimodais representam uma mudança de paradigma em como os sistemas de IA processam e compreendem informações. Ao quebrar as barreiras entre diferentes tipos de dados, essas técnicas habilitam aplicações de IA mais naturais e capazes. Seja você construindo sistemas de busca, ferramentas de moderação de conteúdo ou aplicações criativas, dominar embeddings multimodais abre um mundo de possibilidades para inovação em IA multimodal.