Подготовка к собеседованию по мультимодальному ML

Вопросы для собеседования инженера по мультимодальному машинному обучению

15 отобранных вопросов для собеседования по мультимодальному машинному обучению, сгруппированных по уровню seniority. Используйте их для повторения основ, практических компромиссов и рассуждений старших специалистов о производственных аспектах.

Начать AI-собеседование по мультимодальному MLКредитная карта не требуется. Доступна 1 бесплатная сессия.
Практика технических собеседований на английскомРежим, в котором не носители языка могут потренироваться проходить интервью.

Вопросы для Junior

1Как контрастивное обучение согласует представления изображений и текста и зачем им общее пространство эмбеддингов?

Кодировщики изображения и текста переводят входы в одно векторное пространство. При обучении повышают сходство согласованных пар и понижают сходство несогласованных, например по косинусной мере. Затем можно искать изображения по тексту и подписи по изображению сравнением векторов. Для классификации без дообучения под задачу эмбеддинг изображения сравнивают с эмбеддингами текстовых описаний классов, например «фотография собаки», без отдельной классификационной головы для каждого набора классов.

import torch
import torch.nn.functional as F

# image_features: [B, D], text_features: [B, D]
image_norm = F.normalize(image_features, dim=-1)
text_norm = F.normalize(text_features, dim=-1)

# Pairwise cosine similarity matrix: [B, B]
sim_matrix = image_norm @ text_norm.T
# Positive pairs lie on the diagonal; off-diagonals are negatives
Попробовать ответить на вопрос AI-тренеру

2Как работает архитектура с двумя кодировщиками в стиле CLIP и какое ограничение она накладывает на взаимодействие изображения и текста?

Отдельные сети, например ViT или ResNet для изображения и Transformer для текста, кодируют входы независимо. Они проецируют результаты в общее пространство и оценивают пару скалярным произведением нормированных векторов или косинусным сходством. На промежуточных слоях нет перекрёстного внимания между токенами и областями изображения: взаимодействие при оценке пары ограничено сходством итоговых векторов. Это позволяет заранее вычислить представления изображений и быстро искать по большому индексу, но затрудняет локализацию по отдельным словам и анализ сложных пространственных отношений.

import torch
import torch.nn as nn
import torch.nn.functional as F

class CLIPDualEncoder(nn.Module):
    def __init__(self, visual_encoder, text_encoder, img_dim, txt_dim, embed_dim):
        super().__init__()
        self.visual_encoder = visual_encoder
        self.text_encoder = text_encoder
        self.img_proj = nn.Linear(img_dim, embed_dim, bias=False)
        self.txt_proj = nn.Linear(txt_dim, embed_dim, bias=False)
        self.logit_scale = nn.Parameter(torch.ones([]) * 2.659)

    def forward(self, images, text_tokens):
        # Independent uncoupled encoding
        v_feat = self.img_proj(self.visual_encoder(images))
        t_feat = self.txt_proj(self.text_encoder(text_tokens))
        # L2 normalize
        v_norm = F.normalize(v_feat, p=2, dim=-1)
        t_norm = F.normalize(t_feat, p=2, dim=-1)
        # Late interaction via dot product
        return torch.matmul(v_norm, t_norm.T) * self.logit_scale.exp()
Попробовать ответить на вопрос AI-тренеру

3Зачем image/text-признаки проецируют в общее нормированное пространство и оптимизируют cosine?

L2-проекция на единичную гиперсферу отделяет семантическое направление от длины. Без неё свободный dot-product позволяет уменьшать loss раздуванием норм лёгких или частых примеров вместо согласования направлений. Cosine строго в пределах −1…1 и зависит от угла. Это убирает масштаб норм, но само не предотвращает collapse и не уравнивает градиенты примеров; negatives и objective дают дополнительную защиту. Обучаемая температура управляет остротой вероятностей, балансируя совпадение настоящих пар и равномерное распределение отрицательных.

import torch
import torch.nn.functional as F

# Unnormalized embeddings with extreme norm differences
v = torch.tensor([[10.0, 0.0], [0.1, 0.0]])
t = torch.tensor([[5.0, 0.0], [0.1, 0.0]])

# Raw dot products: heavily distorted by magnitude (50.0 vs 0.01)
raw_dot = v @ t.T

# L2 Normalized:
v_norm = F.normalize(v, p=2, dim=-1)
t_norm = F.normalize(t, p=2, dim=-1)
cosine_sim = v_norm @ t_norm.T
print('Cosine similarity matrix:\n', cosine_sim)
Попробовать ответить на вопрос AI-тренеру

4Что такое привязка текста к области изображения и распознавание объекта по описанию (REC)? Чем они отличаются от оценки сходства целого изображения и текста?

Модель находит область изображения по естественному описанию, например человека в синей куртке рядом с фонарём. Для этого нужно различать объекты, их признаки и пространственные отношения. Результат — локализация, например ограничивающая рамка или маска. Оценка сходства возвращает общий балл для изображения и текста: кадр может подходить подписи «человек в парке», но балл не показывает, где этот человек и какой из нескольких объектов имеется в виду.

# Image-level similarity:
# Input: Image I, Text T ("black dog sitting on the left")
# Output: scalar score s in [0, 1] or dot product
similarity_score = image_text_model(image, text)

# Referring Expression Comprehension (Visual Grounding):
# Input: Image I, Text T ("black dog sitting on the left")
# Output: Bounding box coordinates [x_min, y_min, x_max, y_max]
bbox = grounding_model.predict_box(image, text)
Попробовать ответить на вопрос AI-тренеру

5Как перекрёстное внимание объединяет визуальные и текстовые токены в мультимодальном трансформере?

Запросы Q формируются из одной последовательности, ключи K и значения V — из другой. Если Q получены из визуальных токенов, каждый из них получает взвешенную сумму текстовых значений: softmax(QKᵀ/√dₖ)V. Здесь dₖ — размерность ключа, а softmax применяется по текстовым позициям. Длины последовательностей могут различаться. Для двустороннего обмена применяют второй блок с обратным направлением или чередуют направления в нескольких блоках.

import torch
import torch.nn as nn

class MultimodalCrossAttention(nn.Module):
    def __init__(self, d_model, n_heads):
        super().__init__()
        self.mha = nn.MultiheadAttention(embed_dim=d_model, num_heads=n_heads, batch_first=True)
        self.norm = nn.LayerNorm(d_model)

    def forward(self, visual_query_tokens, text_kv_tokens):
        # visual_query_tokens: [B, N_v, D], text_kv_tokens: [B, N_t, D]
        attn_out, _ = self.mha(
            query=visual_query_tokens, 
            key=text_kv_tokens, 
            value=text_kv_tokens
        )
        # Residual connection and normalization
        output = self.norm(visual_query_tokens + attn_out)
        return output
Попробовать ответить на вопрос AI-тренеру

6Как обрезка, изменение цвета и сжатие изображения помогают или мешают контрастивному согласованию изображения с текстом?

Умеренные преобразования снижают зависимость от фона, водяных знаков и масштаба. В отличие от сопоставления двух вариантов одного изображения, здесь преобразованный кадр должен по-прежнему соответствовать подписи. Сильная обрезка может убрать упомянутую птицу, смена оттенка — превратить жёлтое такси в синее, а сжатие или размытие — скрыть надпись и мелкие детали. Такие пары дают неверный обучающий сигнал. Настраивайте силу аугментации по типу подписей и проверяйте результат на задачах поиска.

from torchvision import transforms

# Multimodal contrastive transform: conservative to preserve caption semantics
clip_transforms = transforms.Compose([
    transforms.RandomResizedCrop(224, scale=(0.5, 1.0)),  # Conservative crop avoids dropping described entities
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.481, 0.457, 0.408], std=[0.268, 0.261, 0.275])
])

# Unimodal self-supervised transform: aggressive, risks breaking image-text alignment
simclr_heavy_transforms = transforms.Compose([
    transforms.RandomResizedCrop(224, scale=(0.08, 1.0)), # Extreme crop can remove captioned subjects
    transforms.RandomApply([transforms.ColorJitter(0.8, 0.8, 0.8, 0.2)], p=0.8), # Alters color adjectives
    transforms.RandomGrayscale(p=0.2),
    transforms.GaussianBlur(kernel_size=23, sigma=(0.1, 2.0)), # Destroys fine OCR/textures
    transforms.ToTensor()
])
Попробовать ответить на вопрос AI-тренеру

7Что такое поиск изображений по тексту и текста по изображениям и чем поиск в фиксированном наборе отличается от поиска по большой открытой коллекции?

По запросу одной модальности система ранжирует кандидатов другой, например сравнивая их эмбеддинги в общем пространстве. В фиксированном тестовом наборе кандидаты и эталонные пары заранее заданы; при малом размере можно сравнить все пары. В большой открытой коллекции состав меняется, есть похожие нерелевантные элементы и неразмеченные релевантные пары. Обычно кандидатов находят приближённым поиском ближайших соседей, например через индекс HNSW, а более дорогую модель оставляют для повторного ранжирования короткого списка.

import torch
import torch.nn.functional as F

# Closed-set matching: full pairwise similarity matrix over fixed evaluation set
image_embeds = F.normalize(torch.randn(1000, 512), dim=-1)
text_embeds = F.normalize(torch.randn(1000, 512), dim=-1)
sim_matrix = torch.matmul(text_embeds, image_embeds.T)  # (1000, 1000)

# Open-world retrieval: query vector queried against an indexed corpus using ANN
# index = faiss.IndexHNSWFlat(512, 32)
# distances, indices = index.search(query_embed, k=10)
Попробовать ответить на вопрос AI-тренеру

Вопросы для Middle

8Сравните InfoNCE-softmax и попарный sigmoid-loss обучения image/text-эмбеддингов.

CLIP-подобный InfoNCE — многоклассовая задача: softmax нормирует положительную и отрицательные пары по batch. SigLIP считает каждую ячейку матрицы независимой бинарной задачей с sigmoid и BCE: 1 положительным,0 отрицательным, либо эквивалентно ±1 в logistic loss. InfoNCE связывает negatives общим знаменателем, самые трудные доминируют градиент; sigmoid оценивает пары отдельно. Распределённый CLIP обычно собирает embeddings разных устройств ради negatives. SigLIP не требует глобального softmax и поддерживает расчёт частями, но cross-device-пары всё равно требуют обмена векторами. Эффективность и качество зависят от состава batch и обучения.

import torch
import torch.nn.functional as F

def siglip_loss(sim_matrix, temperature, bias):
    # sim_matrix: [B, B] dot product of normalized embeddings
    # targets: 1 on diagonal (positive), -1 on off-diagonals (negative)
    B = sim_matrix.size(0)
    targets = 2 * torch.eye(B, device=sim_matrix.device) - 1.0
    logits = sim_matrix * temperature + bias
    # Pairwise binary cross entropy with log-sigmoid
    return -torch.mean(F.logsigmoid(targets * logits))
Попробовать ответить на вопрос AI-тренеру

9Как сложные и ложноотрицательные пары влияют на контрастивное обучение изображений и текста?

Сложная отрицательная пара действительно не совпадает, хотя выглядит похожей, например фото хаски с подписью «аляскинский маламут». Она учит различать мелкие признаки. Ложноотрицательная пара на деле подходит, например другая фотография ретривера к общей подписи, но функция потерь отталкивает её как отрицательную. Это ухудшает представления и поиск. Помогают удаление дублей, обнаружение нескольких положительных соответствий и функции потерь, учитывающие неопределённость отрицательных меток.

import torch
import torch.nn.functional as F

# Image 0 and Image 1 both depict 'a red sports car'
# Text 0: 'a red sports car', Text 1: 'a fast red car'
# In standard InfoNCE, Text 1 is treated as a negative for Image 0.
sim = torch.tensor([[0.95, 0.90],   # Image 0 similarities
                    [0.88, 0.94]])  # Image 1 similarities

loss_img0 = -F.log_softmax(sim / 0.07, dim=-1)[0, 0]
print(f'InfoNCE loss for Image 0: {loss_img0.item():.4f}')
# High similarity to false negative Text 1 (0.90) heavily penalizes the model
Попробовать ответить на вопрос AI-тренеру

10Чем глобальное согласование изображения и текста отличается от согласования областей со словами или токенами?

Глобальная схема кодирует изображение и текст одним вектором на вход и согласует их, например через InfoNCE. Векторы удобно индексировать, но один вектор теряет часть сведений о количестве, пространстве и связи признака с объектом. Детальная схема сравнивает участки или области изображения со словами и их частями, например через максимум сходства токенов, оптимальный транспорт или перекрёстное внимание. Это полезно для локализации и сопоставления атрибутов, но увеличивает вычисления и память, а локальные соответствия могут быть шумными.

import torch
import torch.nn.functional as F

# visual_tokens: [B, N_v, D], text_tokens: [B, N_t, D]
def token_level_maxsim_similarity(visual_tokens, text_tokens):
    v_norm = F.normalize(visual_tokens, dim=-1)
    t_norm = F.normalize(text_tokens, dim=-1)
    # Compute similarity matrix between all visual and text tokens: [B, N_t, N_v]
    sim_matrix = torch.bmm(t_norm, v_norm.transpose(1, 2))
    # Text-to-image: For each word token, find maximum visual token similarity and average
    t2i_sim = sim_matrix.max(dim=-1).values.mean(dim=-1) # [B]
    return t2i_sim
Попробовать ответить на вопрос AI-тренеру

11Сравните раннее, промежуточное и позднее объединение модальностей, перекрёстное внимание, обучаемый выбор вклада и конкатенацию.

Раннее объединение подаёт токены обеих модальностей в общую сеть сразу: взаимодействие глубокое, но вычисления дороги. Промежуточное даёт отдельным сетям обработать вход до обмена признаками. Позднее сравнивает или объединяет итоговые векторы и удобно для индексируемого поиска. В перекрёстном внимании запросы одной модальности обращаются к ключам и значениям другой, даже при разной длине. Обучаемый затвор взвешивает вклад по контексту. Конкатенация соединяет признаки совместимой формы или последовательности токенов; обычное полное самовнимание на удлинённой последовательности имеет квадратичную стоимость.

import torch
import torch.nn as nn
import torch.nn.functional as F

# 1. Concatenation along feature dimension
z_concat = torch.cat([img_feat, txt_feat], dim=-1) # [B, D_img + D_txt]

# 2. Gated Fusion
gate = torch.sigmoid(gate_layer(z_concat)) # [B, D]
z_gated = gate * img_proj + (1 - gate) * txt_proj

# 3. Late Fusion (Score level)
similarity = torch.sum(F.normalize(img_feat, dim=-1) * F.normalize(txt_feat, dim=-1), dim=-1)
Попробовать ответить на вопрос AI-тренеру

12Чем модель с двумя кодировщиками отличается от совместного кодировщика для повторного ранжирования изображений и текста по точности, взаимодействию и задержке?

Два кодировщика независимо вычисляют векторы изображения и текста и сравнивают их. Векторы коллекции можно подготовить заранее и искать приближённо, но при таком сравнении нет взаимодействия отдельных слов и участков. Совместный кодировщик обрабатывает пару, позволяя токенам взаимодействовать через внимание. Это часто точнее при сложных атрибутах и пространственных отношениях, но совместную обработку приходится выполнять для каждой пары запроса и кандидата. Поэтому сначала обычно быстро находят кандидатов двумя кодировщиками, а затем повторно ранжируют короткий список совместной моделью.

# Stage 1: Dual-Encoder First-Stage Retrieval (Fast, Vector Index)
query_emb = text_encoder(query_text) # [1, D]
# Cosine similarity over pre-indexed image embeddings [N, D]
scores = query_emb @ precomputed_image_embeddings.T # [1, N]
top_k_indices = torch.topk(scores, k=100).indices[0]

# Stage 2: Cross-Encoder Reranker (Accurate, Token Interaction)
candidate_images = [load_image(idx) for idx in top_k_indices]
rerank_scores = cross_encoder(candidate_images, [query_text] * 100) # Full cross-attention
final_ranking = top_k_indices[torch.argsort(rerank_scores, descending=True)]
Попробовать ответить на вопрос AI-тренеру

Вопросы для Senior

13Спроектируйте мультимодальный RAG по PDF и руководствам с текстом, таблицами, графиками, скриншотами и изображениями.

1. Layout-aware LayoutLM/DocTR или vision-parser делит страницы на текст, таблицы с сохранением объединённых ячеек через HTML или другую структуру — обычный Markdown их не представляет, — графики с рядами и отдельные диаграммы/снимки с box. 2. Изображения храните crop с SigLIP/CLIP или ColPali patch-token и синтетическим описанием/OCR VLM в dense-text-index. Таблицы — structured Markdown/HTML в vector/BM25; текст — по разделам в dense и sparse. 3. Параллельно ищите BM25 по словам/OCR/таблице, text-vector и visual ANN. RRF объединяет; top ранжирует multimodal cross-encoder или late-interaction по high-res crop и тексту. 4. Top-k image-patch, схема таблицы и фрагменты с document/page передаются instruction-VLM, например GPT-4o, Claude3.5 Sonnet или Qwen2-VL. Prompt просит цитаты; код проверяет существование, доступ и поддержку утверждений document/page/region ID. Сохраняйте provenance через parsing/index, применяйте права до сборки, найденные инструкции считайте недоверенными данными.

from dataclasses import dataclass
from typing import Optional, List

@dataclass
class MultimodalChunk:
    chunk_id: str
    document_id: str
    page_number: int
    modality_type: str  # 'text', 'table', 'chart', 'diagram'
    text_payload: str   # Raw text or structured Markdown/HTML
    vlm_caption: Optional[str] = None  # Synthetic summary of visual content
    bbox: Optional[List[float]] = None  # [x0, y0, x1, y1]
    image_crop_path: Optional[str] = None
    dense_embedding: Optional[List[float]] = None

# Indexing strategy: Text search covers text_payload + vlm_caption;
# Multimodal search matches dense_embedding or visual tokens.
Попробовать ответить на вопрос AI-тренеру

14Как оценивать мультимодальную генерацию с поиском (RAG) отдельно по поиску, использованию подтверждений, ссылкам и итоговому ответу?

Для поиска измеряйте полноту и порядок релевантных страниц, фрагментов и областей, например Recall@K и NDCG@K, отдельно для текста, таблиц и графиков. Чтобы проверить использование материала, убирайте или заменяйте нужный фрагмент и проверяйте реакцию ответа; карта внимания сама этого не доказывает. Оценивайте, верны ли ссылки на документ, страницу и область и содержат ли они подтверждение. Для ответа отдельно измеряйте фактическую корректность и то, следуют ли утверждения из найденных источников. Проверяйте уместное воздержание при недостатке или конфликте данных.

# Evaluation record schema for a single Multimodal RAG query:
eval_record = {
    'retrieval_quality': {
        'hit_at_k': True,          # Top-3 chunks contain target diagram
        'visual_bbox_iou': 0.85    # Retrieved crop overlap with true figure region
    },
    'evidence_utilization': {
        'necessity_ablation_passed': True  # Removing chart causes answer to fail
    },
    'provenance': {
        'page_match': True,        # Cited Page 12 (Ground Truth Page 12)
        'figure_id_match': True    # Cited 'Figure 4'
    },
    'faithfulness': {
        'claim_entailment_score': 1.0,  # All claims entailed by context
        'hallucination_detected': False
    }
}
all_passed = all(all(metrics.values()) for metrics in eval_record.values())
print(f'System passes all decoupled checks: {all_passed}')
Попробовать ответить на вопрос AI-тренеру

15Как обновлять мультимодальную систему поиска, если нужно пересчитать эмбеддинги большой коллекции и перестроить индекс?

Новый кодировщик обычно меняет пространство векторов, поэтому нельзя без проверки сравнивать его запросы со старой коллекцией. Постройте новый индекс отдельно, пока прежний обслуживает запросы. Догоняйте изменения и удаления двойной записью или журналом изменений. Проверьте полноту, задержку и актуальность в теневом режиме, затем согласованно переключите кодировщик и индекс. Сохраните прежнюю пару для отката. Если специально обучили совместимый кодировщик или адаптер, проверьте качество на старом индексе до поэтапного пересчёта.

class MultimodalRetrievalRouter:
    def __init__(self, v1_service, v2_service, dual_write=True):
        self.v1 = v1_service  # Model v1 + Index v1
        self.v2 = v2_service  # Model v2 + Index v2
        self.active_version = "v1"
        self.dual_write = dual_write

    def search(self, query_multimodal):
        # Route query strictly to the encoder matching the active index
        if self.active_version == "v1":
            return self.v1.search(query_multimodal)
        return self.v2.search(query_multimodal)

    def ingest_new_item(self, item):
        # Dual-write during migration window so the new index is up-to-date at cutover
        self.v1.index_item(item)
        if self.dual_write and self.v2.is_ready_for_ingest:
            self.v2.index_item(item)
Попробовать ответить на вопрос AI-тренеру

Собеседования на телефоне: Multimodal ML

Отвечайте голосом и получайте обратную связь от ИИ в приложении для Android.

Скачать EngineerSpeak в Google Play
Сканируйте для скачивания