search_ads_recsys / 14 · multimodal recommendation lesson 14 / 39

Multimodal recommendation

An ID embedding for a brand-new video is a row of noise — no gradient has ever touched it, because nobody has watched the video yet. But the pixels, the audio waveform, and the title text all exist on the first second of upload. Turning that raw content into vectors a recommender can use is the cold-start lifeline.

The problem: ID embeddings are undefined on day zero

A standard recommender represents item i by a learned vector eᵢ ∈ ℝ^d, pulled from an embedding table indexed by item ID — the same sparse table the ranker is mostly made of. That vector is shaped only by interaction signal: every click on i nudges eᵢ toward the users who clicked. The gradient on row i is non-zero only on training rows that contain item i:

∂L/∂eᵢ = 0  whenever item i appears in zero training rows

So a video uploaded one minute ago has an embedding equal to its random initialization — it sits nowhere meaningful, and the ranker scores it essentially at random. This is the item cold-start problem in its purest form, and no ranking architecture fixes it, because the input itself is uninformative.

A content embedding breaks the dependence on interaction history. Instead of learning a free vector per item, we compute one from the item's content with a function f trained on other items (or a giant external corpus):

cᵢ = f(pixelsᵢ, audioᵢ, textᵢ)  — defined on day zero, no clicks required

Two videos of sleeping cats land near each other in c-space because they look alike, not because the same users watched them. A fresh cat video therefore inherits the neighborhood — and the audience — of cat videos the system already understands. That single property is why every modern feed pours engineering into content encoders.

Worked numbers — where a cold item lands, with and without content
Catalogue of 10⁹ items. A clip uploaded one minute ago. ID-only: its embedding is random init, so its expected rank under any user query is the middle of the pile — around position 5×10⁸. It never enters K₁ (retrieval keeps ~10³), never gets an impression, never accrues the clicks it would need to escape — the cold-start death spiral. With a content vector: the clip lands in the embedding neighborhood of, say, the ~10⁴ visually/semantically similar clips the system already understands, whose average CTR is ~1%. So instead of competing against a billion items it competes against ten thousand — its effective rank jumps from ~5×10⁸ to ~10⁴, comfortably inside the content recall channel's reach. That rank jump is the ~10–15% cold-start CTR lift the A/B tests report: not magic, just "start near things you resemble instead of nowhere."
The cold-start arithmetic
On a large short-video platform, millions of clips upload daily and the median fresh clip gets fewer than 10 impressions in its first hour. ID-only, those clips are unrankable noise. With a content embedding they are rankable from impression zero, and A/B tests routinely report cold-start CTR lifts of ~10–15% from a CLIP-style content tower — the difference between a creator's first video dying and finding its first thousand viewers.

One encoder per modality

Each modality is a different kind of object — a pixel grid, a waveform, a token sequence — so each gets a specialist encoder that maps it into a fixed-length vector. The encoders are almost always pretrained (on ImageNet, on audio tagging, on web text) then optionally fine-tuned, because labeled recommendation data is tiny next to the corpora those backbones saw.

ModalityRaw formTypical encoderWhat it captures
Visual (cover, frames)RGB pixel grid / frame sequenceResNet, ViT; 3D-CNN / TimeSformer / VideoSwin for motionobjects, scene, composition, quality, motion
Audiowaveform → Mel-spectrogramVGGish, PANNs, HTS-ATmusic genre, tempo, mood, speech energy
Text (title, caption, ASR)token sequenceBERT / ALBERT (distilled); take the [CLS] vectortopic, keywords, sentiment, named entities

The key abstraction: after encoding, modality differences are erased at the type level — everything is now an ℝ^d vector. Pixels and a title that began as completely different data structures become two vectors that downstream layers can add, concatenate, or attend over. Encoders usually project to a modest d (often 256), because — as the engineering section shows — these vectors get stored and served for every item in the corpus, so width is a real cost.

Picking the backbone — the numbers behind the choice

"Use a ViT" is not an answer; an interviewer wants to know which encoder and why that one. The choice trades three axes against each other — alignment quality (does it match cover to title?), temporal modeling (does it see motion, not just one frame?), and FLOPs (can the upload pipeline keep up?). Concrete anchors, all reported on standard benchmarks:

EncoderModality / roleBenchmark anchorWhy you'd reach for it
CLIP-ViTcover ↔ title cross-modal matchcross-modal HIT@1 ≈ 58%already aligned image↔text — the retrieval/cold-start workhorse
TimeSformervideo temporal (play-page)Kinetics-400 Top-1 ≈ 80.7%attends across frames — catches motion a single frame misses
ResNet-3D / SlowFastvideo, efficiency-first≈ 45 GFLOPs @ 224×224real-time-capable spatiotemporal features at the ingest QPS
ResNet-50single frame / coverfinal avg-pool = 2048-dcheap, battle-tested; project the 2048-d pool5 down to 256
MoCo v3 / VideoMAEself-supervised in-domainlinear-probe +2.1% over supervisedpretrained on your clip distribution, not ImageNet
VGGishaudio embedding128-d acoustic vectorBGM mood/genre; pairs with MFCC / log-Mel front-end
BERT-wwm / ALBERT-distilledtitle, ASR, OCR text[CLS] sentence vectortopic/sentiment; distilled variant keeps upload-time cost down
MobileNetV3 / Swin-Tiny / MobileViTon-deviceFLOPs ~10× lower than ViT-Lwhen the encoder must run on the phone, not the data center

The text side is rarely just the title. A muted-with-captions clip has its words in the pixels — recover them with OCR on sampled frames; a talking-head clip has its words in the audio — recover them with ASR (speech-to-text), then encode the transcript with BERT. So one "text vector" can be the concatenation of three sources (title + OCR + ASR), and a clip with an empty title is still far from textless.

The selection rule, in one line
Cover image → CLIP-ViT (you need the image↔text alignment); play-page video → TimeSformer / SlowFast (you need motion); on-device → MobileNet-class (you need the FLOPs gone). And before reaching for an ImageNet backbone, ask whether MoCo v3 / VideoMAE self-supervised on your own clips wins — on a short-video distribution it does, by ~2%, because the data matches. One concrete payoff for getting the visual tower right: in a Douyin ablation, cover-image quality moved per-user watch-time about 30% more than the title text did — the pixels, not the words, are where the watch-time lives.

Frame sampling — you cannot afford every frame

A 30-second clip at 30 FPS is 900 frames; running a ViT over all of them per video, times millions of daily uploads, is a non-starter. The standard cut: sample 1–3 FPS (or detect shot boundaries and keep one frame per shot), center-crop vertical video, then mean/max-pool the per-frame vectors into one clip vector (a Non-local block can replace the pool when you want long-range frame dependence). 1–3 FPS over 30 s is ~30–90 frames instead of 900 — a 10–30× cut in encode cost for almost no loss in content signal, because adjacent frames are nearly identical.

Pretrained, but not blindly
An ImageNet ResNet is tuned to tell a Labrador from a beagle — a distinction a feed may not care about — while ignoring "is this a clickbait thumbnail," which it cares about a lot. Pretrained backbones give a strong starting representation; the alignment training below plus a light in-domain fine-tune bend them toward recommendation-relevant structure. Self-supervised pretraining on your own clip distribution (MoCo v3, VideoMAE) often beats an off-the-shelf supervised backbone, because the data distribution matches.

Fusion — where do the modalities meet?

Now we have v (visual), a (audio), t (text), each in ℝ^d. The question is where in the model do they combine? Three answers, in increasing order of interaction richness and cost.

StrategyMechanismStrengthWeaknessBest when
Early (feature-level)concatenate, feed one joint model: [v;a;t] → MLPfine-grained cross-modal interaction; one model to trainneeds aligned, complete inputs; fragile to a missing modality; heaviermodalities tightly coupled (cover ↔ title)
Late (decision-level)a model per modality, fuse the scores: wᵥsᵥ + wₐsₐ + wₜsₜmodules decoupled — AB-test one modality alone; robust to a missing modalityloses all cross-modal associationsmodalities update at different cadences; fast experiments
Attention (mid)cross-attention / gating across modalities before the headlearns which modality matters per example, dynamicallyO(n²) attention cost; data-hungry; weak on cold itemsrich content + enough interaction data; the ranking stage

The math makes the tradeoff concrete. Early fusion lets the joint MLP form any cross term — e.g. a hidden unit that fires only on "calm visual × upbeat audio," a contradiction signal a late model can never see. Late fusion can only mix at the very end:

late:  ŷ = σ(wᵥ·sᵥ + wₐ·sₐ + wₜ·sₜ)  — a weighted vote, no v×a term exists

Attention / gating sits between: a gate reads the input and outputs per-modality weights that change example to example — downweight audio late at night; lean on the visual modality for a fast-scrolling user. Same gating idea as MMoE in multi-task learning, repurposed across modalities:

g = softmax(W_g·x)    z = gᵥ·v + gₐ·a + gₜ·t  (gate g depends on the example)

The gate is context-driven, not just per-item

"Downweight audio at night" is one instance of a much bigger idea: the gate's input x is not only the item's content — it carries real-time context and user behavior, so the same clip is fused differently for different users and moments. This is the engineering payload of the gate, and it is worth more than one example:

There is a second, harder reason the gate reads context: load. Under latency pressure or on a low-end phone, the gate can skip an expensive modality entirely — drop the heavy video tower, serve on cached cover + text — and the model still produces a score because Modality Dropout (below) trained it to survive a missing input. So the same gate that decides "which modality matters here" doubles as the lever for "which modality can I afford to compute right now." That is why dynamic, context-aware fusion is strictly more useful than a static weighted sum: it is both an accuracy knob and a serving-cost knob.

A practical rule of thumb
Retrieval (02 · Candidate generation) lives and dies on latency, so it uses late / two-tower fusion: encode each item once, offline, into one vector, then ANN it (03 · Embeddings & ANN). Ranking (04 · Ranking models) scores only ~hundreds of candidates, so it can afford attention-style fusion. A common shape: concatenate the precomputed content vectors as features into the ranker, and let a small cross-attention block over them do the mid-fusion.

What the fusion choice is worth — the A/B numbers

The richer-but-costlier strategies earn real metric wins, and quoting them is how you justify the engineering. Reported figures from production short-video systems:

ChangeLiftWhat it bought
mid-fusion (cross-modal Transformer) vs late fusion+7.2% per-user watch-timethe v×a cross terms late fusion can never form
two-tower contrastive pretrain (CLIP-style)+15% cold-start CTRcold items land in an aligned space from impression zero
multimodal vs single-objective baseline+15% per-user timecontent signal the ID embedding alone never carried
gated fusion of 2D-CNN local + ViT global features+0.8% CTR AUCfine detail (local) plus scene (global), gated per example

None of this is free. Adding the multimodal path costs roughly +20% serving latency over an ID-only model, and a full cross-modal Transformer denoiser can add ~300ms if you run it online — which is why nobody does. The discipline that makes mid-fusion shippable is a hard split:

THE 90/10 FUSION SPLIT ────────────────────────────────────────────────────────── OFFLINE (at upload) : ~90% of the work — run the heavy encoders, precompute & cache per-modality vectors + a fused content vector ────────────────────────────────────────────────────────── ONLINE (per request) : a LIGHT fusion head only — read cached vectors, run a small gate/MLP < 50ms ────────────────────────────────────────────────────────── ROI gate to ship : +50ms latency must buy ≥ +0.02 AUC

Precompute ~90% of the multimodal features offline; online you run only a sub-50ms fusion head over cached vectors. The ROI rule that decides whether a heavier fusion ships: +50ms of added latency has to buy at least +0.02 AUC, or the win does not justify the serving cost. The industrial sweet spot most teams converge on is gated attention + a residual connection (the Kuaishou recipe) — dynamic per-example modality weighting, but cheap enough to serve, with the residual keeping a clean path for the dominant modality. One subtlety when concatenating heterogeneous vectors: a 2048-d ResNet pool and a 768-d BERT [CLS] have wildly different scales and variances, so apply modality-specific BatchNorm before fusion or the large-norm modality silently dominates the gate.

The fusion landscape, drawn

cover / frames pixels audio track waveform title / ASR tokens ViT / 3D-CNN → v ∈ ℝ²⁵⁶ PANNs / VGGish → a ∈ ℝ²⁵⁶ BERT [CLS] → t ∈ ℝ²⁵⁶ FUSION early: concat [v;a;t] mid: cross-attention late: weighted scores gate picks per example joint content vector retrieval channel · ANN ranking features into the CTR / watch model

Cross-modal alignment — the CLIP idea

Encoding each modality separately leaves a problem: the text vector and the image vector live in different coordinate systems. "a golden retriever" (text) and a photo of one (image) are about the same thing but land in unrelated regions. This is the modality gap. To search across modalities — or to make a missing modality recoverable — we need a shared space where matching pairs sit close.

Contrastive learning builds that space. Take a batch of N true (image, text) pairs from the same videos. Encode and L2-normalize to get image vectors u₁…u_N and text vectors w₁…w_N. The matched pair (uᵢ, wᵢ) should be most similar; every other text in the batch is a negative for image i. The InfoNCE loss (CLIP's loss) makes that true:

L = −(1/N) · Σᵢ log [ exp(uᵢ·wᵢ / τ) / Σⱼ exp(uᵢ·wⱼ / τ) ]  (+ the symmetric text→image term)

Read it as an N-way classification: "given image i, pick its caption out of the N captions in the batch." The numerator rewards the true pair; the denominator's sum over j≠i pushes mismatched pairs apart. The temperature τ sharpens (small τ) or softens (large τ) the softmax. After training, cosine similarity in this space means semantic relatedness across modalities — exactly the property we wanted. The denominator's N−1 in-batch negatives are why CLIP trains on enormous batches (32k+): more negatives per anchor sharpen the gradient.

Why alignment is a cold-start superpower
Once image and text share a space, three things become free. (1) Cross-modal retrieval: a text query embeds into the same space, so "calm piano study music" retrieves videos by content with zero clicks. (2) Missing-modality fill-in: a video with no title can be described by the nearest text vectors to its image vector. (3) Zero-shot tagging: embed candidate tag phrases once; a new clip's tags are its nearest tag vectors. All three work on items the recommender has never served.

Beyond a flat shared space, richer models (ViLBERT, co-attention) let modalities cross-attend token-by-token — the word "cat" binding to the cat region — catching fine associations at higher cost. Alignment (a shared space) and interaction (cross-attention) are complementary: align first so the spaces are comparable, then attend for fine-grained fusion when the budget allows.

CLIP vs BLIP — alignment vs fusion

CLIP is alignment only. Two independent towers — an image encoder and a text encoder — trained by the InfoNCE loss above to land matching pairs close in one shared space. The towers never talk to each other; the only coupling is the contrastive objective. That independence is exactly the property retrieval wants: because the image side needs no text and the text side needs no image, you can run the image tower over the entire catalog offline, write 256-dim vectors to a feature store, and serve them through an ANN index (03 · Embeddings & ANN) — the same two-tower shape candidate generation already relies on. The weakness is the flip side: a dual encoder only scores similarity. It cannot generate a caption, answer a question about the image, or reason about fine cross-modal detail — there is no path for the pixels and the tokens to interact.

BLIP (and BLIP-2) add a fusion / generation path. On top of contrastive alignment, BLIP wires image–text cross-attention into the encoder and trains generative objectives — captioning and image-grounded text — so the model can produce language conditioned on pixels, not just match the two. BLIP-2 makes this cheap with a Q-Former: a small set of learned query tokens that cross-attend to a frozen vision encoder and feed a frozen LLM, bridging the two without retraining either. The payoff is rich captions, VQA, and fine cross-modal understanding — exactly what you want to describe a brand-new clip. The cost: the fused encoder mixes image and text inside the network, so it is no longer a clean dual tower. You cannot pre-encode a billion items into one ANN-able vector from it; it is a re-rank / understanding tool, not a retrieval index.

The recsys verdict: use CLIP-style dual encoders at the retrieval stage for cold-start content embeddings, where the offline-precompute-then-ANN property is non-negotiable; use BLIP-style models offline to generate captions and content tags for cold items, then feed those generated tags back in as features (or as the text side of the CLIP tower). Aligned-but-separate beats fused the moment you need to pre-index a billion items.

CLIP (dual encoder)BLIP / BLIP-2 (fusion)
Architecturetwo independent towers, no cross-talkimage–text cross-attention; BLIP-2 Q-Former bridges frozen ViT → frozen LLM
Objectivecontrastive alignment (InfoNCE) onlycontrastive + generative (captioning / image-grounded text)
Retrieval-friendlyyes — pre-encode the catalog offline, ANN itno — fused encoder can't be split into one ANN-able vector
Best recsys useretrieval-stage cold-start embeddingsoffline tag / caption generation → features

Interactive · the contrastive temperature demo

A batch of N pairs. The cosine of the matched pair versus the average mismatched cosine, run through the InfoNCE softmax. Watch how a tighter matched gap, more negatives, and a smaller temperature drive the matched probability — and the loss — the way training wants.

Contrastive batch — feel the loss
Defaults are roughly a converged CLIP-style encoder (τ = 0.07, matched cos 0.70). Slide τ up and the softmax goes blind; drop the matched cosine and the pair drowns in its negatives. The verdict and note are the interesting part.
P(true pair)
InfoNCE loss
negatives / anchor
verdict
Reading

Putting content vectors to work in the funnel

Content embeddings are not a model — they are inputs, and they plug into two stages of the cascade you already know.

As a retrieval channel. Build an ANN index (03 · Embeddings & ANN) over every item's joint content vector. Now you have a recall source from candidate generation that, unlike collaborative-filtering recall, works for items with zero interactions — the explicit cold-start lifeline. A practical feed runs several recall channels in parallel (hot items, CF neighbors, content-similar, social) and merges; the content channel is the one that carries fresh uploads. The user side can query it by their long-term content-taste vector or, for cross-modal search, by a text query.

As ranking features. Feed the per-modality vectors (and a fused vector) into the ranking model as dense features alongside the ID embeddings. For a warm item the ID embedding dominates and content adds a little; for a cold item the ID embedding is noise and content carries the prediction. The model learns this split on its own — which is why a sensible design is to blend, letting a learned gate decay the content weight as interaction signal accumulates, rather than switching hard between two code paths.

Measuring whether it helped
Isolate the effect: train an identical model with and without the content features, compare a duration-sensitive offline metric (watch-time-weighted NDCG), then run a stratified A/B test with the headline metric (per-user watch time) and guardrails (CTR, retention). Slice by item age — the lift should concentrate on fresh items. A SHAP analysis tells you which modality each objective leans on (covers drive clicks; first-3-seconds audio drives completion).

When a modality is missing — and why training must rehearse it

Real catalogs are ragged. A clip uploads with no title; a user mutes the audio; the cover is a black frame; ASR returns nothing for a wordless dance clip. The naive fix — feed a zero vector for the absent modality — is a quiet disaster, and understanding why is the whole point.

The failure mode: a model that never saw a hole collapses on one
Train on only complete examples and the fusion head learns to rely on all three vectors always being present and informative. At serve time, hand it a zero where the audio used to be and that zero is not "no information" — it is an out-of-distribution input the model was never trained to read. A learned audio weight still multiplies the zero into the sum, the gate's softmax was never calibrated for an absent input, and the score degrades unpredictably — often worse than if the audio modality had never existed. The model didn't "ignore" the missing modality; it confidently used a value that means nothing.

Fix part 1 — tell the model it's missing. Add a missing-modality flag bit per modality to the two-tower / ranker input. Now "audio = 0, audio_present = 0" is a distinct, learnable state, not indistinguishable from "audio = 0 because the track is silent." The model can learn to route around an absent modality instead of trusting a fake zero.

Fix part 2 — rehearse the hole during training: Modality Dropout. Randomly zero out a whole modality for a fraction of training examples (the multimodal analogue of ordinary dropout). The model is forced to make good predictions from any subset of modalities, so a missing modality at serve time is a state it has seen thousands of times, not a novel shock. This is also what makes the gate's "skip a modality under load" trick safe — the skip lands the model in a rehearsed state. Modality Dropout is the single highest-leverage robustness trick in this lesson: it is cheap, it is one line in the data loader, and it converts a serve-time cliff into a graceful degradation.

Fix part 3 — fill the hole with a real proxy. When you want better than "route around it," synthesize a plausible vector for the absent modality. Per-modality recipes that work in production:

Missing modalityFill recipe
Numeric / visualcross-video temporal imputation — the same creator's historical visual median (a creator's clips look alike)
Text (no title)the mean title-embedding of same-category hot videos; or describe the image via its nearest text vectors in the CLIP space
Audioa light autoencoder (or VAE) that predicts the missing log-Mel spectrogram from the present modalities
Key modality (e.g. cover)front-end QC interception — reject the upload before it ever reaches the model rather than fill a load-bearing modality
Worked case — gate the fill on cross-modal similarity (+2.3% watch-time)
Filling blindly is risky: a synthesized vector can contradict the real content and inject noise. So gate it. On a platform handling no-subtitle uploads: extract the CLIP visual vector, generate an ASR-based subtitle embedding, and compute their cross-modal cosine similarity. If similarity < threshold — the generated text doesn't match what's on screen — fall back to the fill strategy instead of trusting the bad ASR. That single similarity gate on when-to-fill lifted per-user watch-time +2.3%. The lesson: a fill is only worth using when an independent modality agrees it's plausible. Note the Pareto tension — every fill adds compute, so the gain has to clear the latency it costs (the +50ms ⇒ +0.02 AUC rule from the fusion section applies here too).

Modality conflict is a feature, not noise

The instinct when two modalities disagree is to average them away. That throws out signal. When the title screams one thing and the pixels show another, the disagreement itself is the most informative thing about the item — most often a clickbait tell. Treat conflict as an explicit input.

Cold-start hybrids beyond CLIP

A CLIP content vector is the first lifeline for a cold item, but it is not the only architecture, and a senior answer names the complements. None of these re-derive the cold-start problem — see 15 · Cold start and 38 · Cold start (advanced) for the full treatment; here is how the multimodal stack plugs into them.

The staged blend — content now, behavior later
These compose into a lifecycle. A common short-video recipe scores a brand-new clip as 60% content-similarity + 30% popularity prior + 10% diversity, then switches to a behavioral two-tower DNN after ~50 interactions once the ID embedding has real gradient behind it. This is the same "decay content weight as interaction signal arrives" idea from the funnel section, made concrete as a hand-off schedule — the multimodal content vector carries the item until behavior can take over.

The engineering bill — encoding is the expensive part

The catch: running a ViT over every frame of every uploaded video is orders of magnitude more expensive than a table lookup. The discipline that makes multimodal practical is precompute offline, serve a vector.

TWO CLOCKS FEEDING ONE RANKER ────────────────────────────────────────────────────────── CONTENT vectors : encoded once at UPLOAD, quasi-static refresh daily/weekly · 256-dim, cached BEHAVIORAL feats : updated in REAL TIME as clicks arrive ────────────────────────────────────────────────────────── serving path : read a 256-dim vector (NO CNN online) upload pipeline : ViT / 3D-CNN / BERT → feature store

The "behavioral features update in real time" clock is itself an engineering system, not a single arrow. Content vectors are encoded once and cached; the other clock — real-time CTR, dwell, scroll speed — runs on a streaming pipeline that a serving lesson like 19 · Large-scale optimization covers in depth, but the multimodal-specific shape is worth pinning down:

THE REAL-TIME (BEHAVIORAL) CLOCK ────────────────────────────────────────────────────────── events → Kafka → Flink State → tiered feature store ────────────────────────────────────────────────────────── high-freq (real-time CTR) : Flink State, every ~10s low-freq (interest cluster): hourly incremental store : Redis (second-level) · HBase (minute-level) consistency : atomic version-snapshot swap on new features SLA tiers : core behavior <500ms · auxiliary <5s degrade : Kafka backlog > threshold → last-1h features ────────────────────────────────────────────────────────── P99 serving target <100ms (FP16/INT8 quant · dynamic batching · early-exit)

The pieces that matter for an interview: a dual-cadence update (real-time CTR refreshed ~every 10s via Flink State; slower interest clusters hourly); a tiered store (Redis for second-level features, HBase for minute-level); an atomic version-snapshot swap so inference always reads a consistent feature set, never a half-updated one; SLA tiers (core behavioral features must arrive in <500ms, auxiliary in <5s); a degrade-on-backlog rule (when the Kafka lag exceeds a threshold, fall back to the last hour's features rather than block); and a P99 serving target under 100ms, held with FP16/INT8 quantization, dynamic batching, and early-exit. Feature-warmup preloads hot-video features to the edge, and a KL-divergence monitor watches for feature drift.

Where it bites
Missing modalities (no title, muted clip) and modality conflict (clickbait) each get a full treatment above — the short version: flag-bit + Modality Dropout + a similarity-gated fill for the former, an explicit contradiction feature for the latter. Two more edges live only here. Over-alignment can erase modality-specific detail, so don't push the shared space so hard that "what's unique to the audio" disappears — keep some decoupled, modality-private capacity. And the ethical edge: leaning hard on facial/visual features risks judging by appearance, so fairness constraints belong in the loss, not as an afterthought.

Interview prompts you should be ready for

  1. "Why does an ID embedding fail for a new item but a content embedding doesn't?" (The ID-table row only receives gradient on rows containing that item; zero interactions means it stays at random init. A content embedding is computed by f(pixels, audio, text) trained on other items, so it's defined on day zero and places the item near things it resembles.)
  2. "Compare early, late, and attention fusion." (Early = concat → joint MLP: any cross term, but fragile to missing modalities and heavier. Late = per-modality scores summed: decoupled and robust, but no cross-modal term exists. Attention/gated = per-example weights, dynamic and accurate but O(n²) and data-hungry. Retrieval leans late; ranking can afford attention.)
  3. "Write the InfoNCE loss and explain τ and the batch size." (L = −(1/N)Σ log[exp(uᵢ·wᵢ/τ) / Σⱼ exp(uᵢ·wⱼ/τ)], plus the symmetric term. It's N-way classification of the true caption; τ sharpens/softens the softmax; the N−1 in-batch negatives drive the gradient, which is why CLIP needs huge batches.)
  4. "You have content embeddings — where exactly do they enter the system?" (Two places: a content-similarity ANN recall channel in candidate generation that works at zero interactions, and dense features in the ranker alongside ID embeddings, with a gate that decays content weight as the item warms up.)
  5. "Running a ViT on every request is too slow. What do you do?" (Never encode online. Precompute content vectors once at upload into a feature store; serving reads a cached 256-dim vector. Distill heavy backbones, sample key frames, tier the cache, refresh on a daily/weekly clock since pixels are static.)
  6. "How do you handle a video with no title or no audio?" (Zero-mask the missing vector with a learned missing-indicator so the model knows it's absent, or synthesize a proxy via the aligned shared space — describe the image with its nearest text vectors. Don't let a missing modality silently read as a zero signal.)
  7. "How would you prove the content tower actually helped?" (Ablate: same model with/without content features, compare watch-time-weighted NDCG offline, then a stratified A/B on per-user watch time with CTR/retention guardrails. Slice by item age — the lift should concentrate on fresh items, which is the whole point.)
  8. "Your model trains only on clips that have all three modalities. At serve time 8% of clips have no audio. What breaks, and how do you fix it before launch?" (A zero-padded missing modality is an OOD input the model never learned to read — the learned audio weight multiplies a meaningless zero and the score degrades unpredictably, often worse than no-audio-ever. Fix: a missing-flag bit so "absent" is a distinct learnable state, plus Modality Dropout in training so the model has rehearsed every modality subset thousands of times; optionally a similarity-gated fill via the aligned space, only used when an independent modality agrees it's plausible. Modality Dropout is the cheap one-line fix that turns a serve-time cliff into graceful degradation.)
  9. "Title and thumbnail disagree — average the modalities to smooth it out?" (No — the disagreement is the signal, usually clickbait. Compute a cross-modal consistency score (title-vector vs visual-vector cosine in the aligned space) and feed the divergence as an explicit contradiction feature to discount the item; an aligning loss trains honest items' towers to agree. Same idea fixes ASR: when on-screen OCR text and ASR text disagree, OCR wins. Averaging destroys exactly the cross-modal information you'd want.)
  10. "You quote mid-fusion at +7.2% watch-time but it adds ~20% latency and a full cross-modal Transformer is O(n²). How do you ship it?" (Split 90/10: precompute ~90% of the multimodal work offline at upload — heavy encoders, cached per-modality + fused vectors — and run only a sub-50ms light fusion head online over cached vectors. Use the gated-attention + residual recipe for cheap dynamic weighting. Gate the decision with the ROI rule: +50ms must buy ≥+0.02 AUC, or it doesn't ship. Never run a CNN or a full cross-modal Transformer in the serving path.)
Takeaway
An ID embedding is undefined for an item with no interactions; a content embedding is computed from raw signal, so it exists on day zero — the cold-start lifeline. One pretrained encoder per modality maps pixels/audio/text into a common ℝ^d; fusion is early (rich, fragile), late (decoupled, robust), or attention/gated (dynamic, costly). Contrastive InfoNCE aligns modalities into a shared space where cosine means cross-modal relatedness, buying retrieval, fill-in, and zero-shot tags. Use the vectors as a retrieval channel and as ranking features, decaying their weight as interaction signal arrives — and never run the CNN in the serving path.