Расскажите про CLIP: идея, функция потерь, метрики дистанции
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
CLIP (Contrastive Language-Image Pre-training)
CLIP — революционная архитектура от OpenAI, которая обучает единое представление для текста и изображений через контрастное обучение.
Основная идея
CLIP решает задачу zero-shot classification: классифицировать изображение на любой класс, даже если модель на нём не обучалась. Ключевая идея: изображение и его текстовое описание должны быть близки в общем embedding пространстве.
Архитектура
import torch
import torch.nn as nn
from torch.nn import functional as F
class CLIP(nn.Module):
def __init__(self, image_encoder, text_encoder, embedding_dim=512):
super().__init__()
self.image_encoder = image_encoder # ResNet-50, ViT, etc.
self.text_encoder = text_encoder # Transformer
# Проекции в общее пространство embeddings
self.image_projection = nn.Linear(image_encoder.output_dim, embedding_dim)
self.text_projection = nn.Linear(text_encoder.output_dim, embedding_dim)
# Learnable temperature parameter для softmax
self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07))
def forward(self, images, texts):
# Энкодируем изображения
image_features = self.image_encoder(images) # (batch, d_img)
image_embeddings = self.image_projection(image_features) # (batch, d)
# Энкодируем текст
text_features = self.text_encoder(texts) # (batch, d_text)
text_embeddings = self.text_projection(text_features) # (batch, d)
# Нормализуем embeddings на единичную сферу
image_embeddings = F.normalize(image_embeddings, dim=-1)
text_embeddings = F.normalize(text_embeddings, dim=-1)
return image_embeddings, text_embeddings
Функция потерь (Contrastive Loss)
CLIP использует симметричную contrastive loss, основанную на concept из SimCLR.
def clip_loss(image_embeddings, text_embeddings, batch_size):
"""
Идея: для каждой пары (image_i, text_i), они должны быть близки,
а (image_i, text_j) где i != j должны быть далеко.
"""
# image_embeddings: (batch_size, embedding_dim)
# text_embeddings: (batch_size, embedding_dim)
# Вычисляем матрицу similarity между всеми image-text парами
# logits: (batch_size, batch_size)
logits = image_embeddings @ text_embeddings.T # dot product
# Масштабируем на learnable temperature
logit_scale = torch.nn.Parameter(torch.ones([]) * np.log(1 / 0.07))
logits = logit_scale * logits
# Целевые индексы: диагональ матрицы (i должен быть близок к i)
labels = torch.arange(batch_size, device=logits.device)
# Loss из перспективы изображения: какой текст наиболее похож?
loss_img_to_text = F.cross_entropy(logits, labels)
# Loss из перспективы текста: какое изображение наиболее похоже?
loss_text_to_img = F.cross_entropy(logits.T, labels)
# Финальная симметричная loss
total_loss = (loss_img_to_text + loss_text_to_img) / 2
return total_loss
Пример вычисления loss
import numpy as np
# Допустим, batch_size = 3
# Истинные пары: (image_0, text_0), (image_1, text_1), (image_2, text_2)
# Матрица similarity (dot products):
# ┌ ┐
# │ 0.9 0.1 0.2 │ image_0
# │ 0.2 0.95 0.15 │ image_1
# │ 0.1 0.2 0.92 │ image_2
# └ ┘
# text_0 text_1 text_2
# Диагональ содержит правильные пары (high similarity)
# Остальные элементы — неправильные пары (low similarity)
# Cross-entropy loss потом "штрафует" за неправильные пары
# Оптимизация уменьшает loss, стягивая диагональ к 1, остальное к 0
Роль temperature parameter
import matplotlib.pyplot as plt
# Temperature масштабирует logits перед softmax
# Высокая температура → мягкий softmax (распределение близко к uniform)
# Низкая температура → жёсткий softmax (one-hot like)
def softmax_with_temperature(logits, temperature=1.0):
scaled = logits / temperature
return np.exp(scaled) / np.sum(np.exp(scaled), axis=-1, keepdims=True)
logits = np.array([10.0, 0.0, -10.0])
for temp in [0.1, 1.0, 10.0]:
probs = softmax_with_temperature(logits, temp)
print(f'T={temp}: {probs}')
# T=0.1: [1.00 0.00 0.00] # very sharp
# T=1.0: [1.00 0.00 0.00] # moderate
# T=10.0: [0.38 0.33 0.29] # very soft
# В CLIP, temperature изучается как параметр, оптимизирует баланс
# между confidence и regularization
Метрики дистанции в CLIP
1. Cosine Similarity (самая важная!)
def cosine_similarity(x, y):
# Поскольку embeddings нормализованы (F.normalize),
# dot product = cosine similarity
return (x @ y.T) # (0, 1) после normalize
# Пример
image_emb = np.array([0.8, 0.6]) # нормализовано
text_emb = np.array([0.7, 0.71])
# После normalize:
image_emb = image_emb / np.linalg.norm(image_emb)
text_emb = text_emb / np.linalg.norm(text_emb)
sim = np.dot(image_emb, text_emb)
print(f'Cosine similarity: {sim:.3f}') # 0.99 — очень похожи
2. Euclidean Distance
# L2 расстояние в embedding пространстве
def euclidean_distance(x, y):
return np.linalg.norm(x - y)
# В нормализованном пространстве:
# dist^2 = ||x||^2 + ||y||^2 - 2 * (x · y) = 1 + 1 - 2*cos_sim = 2(1 - cos_sim)
dist = euclidean_distance(image_emb, text_emb)
print(f'Euclidean distance: {dist:.3f}') # smaller = closer
3. Manhattan Distance
# L1 расстояние
def manhattan_distance(x, y):
return np.sum(np.abs(x - y))
На практике: Zero-shot Classification
import torch
from torchvision import transforms
from PIL import Image
# Загруженная CLIP модель (от OpenAI)
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = torch.hub.load(
'openai/CLIP:main',
'ViT-B/32',
device=device,
pretrain=True
)
# Изображение
image = Image.open("cat.jpg")
image_input = preprocess(image).unsqueeze(0).to(device)
# Текстовые описания классов
classes = ["a photo of a cat", "a photo of a dog", "a photo of a bird"]
text_inputs = torch.cat([
torch.from_numpy(model.tokenize(c)).to(device)
for c in classes
])
# Forward pass (без gradients, только inference)
with torch.no_grad():
image_embeddings, text_embeddings = model(image_input, text_inputs)
# Вычисляем similarity
similarities = (image_embeddings @ text_embeddings.T).softmax(dim=-1)
# Предсказание
predicted_class_idx = similarities[0].argmax()
predicted_class = classes[predicted_class_idx]
print(f'Predicted: {predicted_class}')
print(f'Confidence: {similarities[0, predicted_class_idx]:.3f}')
Многомодальный поиск (Multi-modal Retrieval)
# После обучения CLIP можно использовать для:
# 1. Поиск изображений по тексту
# 2. Поиск текста по изображению
# 3. Рекомендации
# Индексируем всё изображения
image_embeddings_db = model.encode_image(all_images) # (N, 512)
# User query as text
query = "красная кошка на диване"
query_embedding = model.encode_text(query) # (512,)
# Найти самые похожие изображения (cosine similarity)
similarities = query_embedding @ image_embeddings_db.T # (N,)
top_k_idx = similarities.argsort()[-5:][::-1] # top 5
for idx in top_k_idx:
print(f'Image {idx}: similarity {similarities[idx]:.3f}')
Ключевые преимущества CLIP
1. Zero-shot способность — классифицирует классы, на которых не обучалась 2. Многомодальность — единое пространство для текста и изображений 3. Масштабируемость — обучена на 400M image-text пар (LAION dataset) 4. Простота потерь — контрастная loss интуитивна и работает 5. Robust representations — работает хорошо на different domains
Вариации и развитие
- OpenCLIP — open-source вариация с разными архитектурами
- ALIGN — от Google, более масштабная версия
- LiT — Locked-in-Time, эффективная альтернатива
- CoCa — контрастное-каузальное внимание для caption generation