← Назад к вопросам

Расскажите про CLIP: идея, функция потерь, метрики дистанции

1.7 Middle🔥 81 комментариев
#NLP и обработка текста#Глубокое обучение

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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
Расскажите про CLIP: идея, функция потерь, метрики дистанции | PrepBro