Skip to content

Add Add MSR-VTT benchmark#2235

Draft
meatybobby wants to merge 2 commits into
mainfrom
bobchen/embedding_eval
Draft

Add Add MSR-VTT benchmark#2235
meatybobby wants to merge 2 commits into
mainfrom
bobchen/embedding_eval

Conversation

@meatybobby

Copy link
Copy Markdown
Contributor

Description

Add MSR-VTT benchmark

Usage

python eval/video/bench_msrvtt_retrieval.py \
    --model-dir /path/to/models \
    --variant 336p \
    --output results.json

Checklist

  • I am familiar with the Contributing Guide.
  • New or Existing tests cover these changes.
  • The documentation is up to date with these changes.

@meatybobby
meatybobby requested a review from a team as a code owner July 21, 2026 19:35
@meatybobby
meatybobby requested review from abhinavg4 and removed request for a team July 21, 2026 19:35
@copy-pr-bot

copy-pr-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@meatybobby
meatybobby marked this pull request as draft July 21, 2026 19:35
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an end-to-end MSR-VTT text-to-video retrieval benchmark for CosmosEmbed1, covering HF Hub video download, cv2 frame sampling, cosine-similarity ranking, and standard retrieval metrics (R@1/5/K, MedR, MeanR, MRR, NDCG@K) in both T2V and V2T directions. Unit tests cover all pure helper functions with CPU-only mocks.

  • bench_msrvtt_retrieval.py: 490-line script wiring dataset loading → per-item embedding → batch ranking → metric computation; three minor issues noted (duplicate R@5 printout at --top-k 5, misleading batch_size parameters that only control log frequency, and silent stem collision in _build_video_index).
  • tests/eval/video/test_bench_msrvtt_retrieval.py: 438-line test suite with solid coverage of every helper function; no issues found.
  • eval/video/README.md: documentation-only addition with baseline table and usage examples.

Confidence Score: 4/5

Safe to merge. All three findings are cosmetic or diagnostic in nature and do not affect metric correctness on the standard --top-k 10 evaluation path.

The benchmark produces correct retrieval metrics for the default configuration. The duplicate R@5 printout only manifests when a non-default --top-k 5 is passed, the misleading batch_size parameters have no impact on computed values, and the stem-collision case is practically unreachable with the standard MSR-VTT zip. Core logic — ranking, NDCG/MRR computation, corpus deduplication, and T2V/V2T GT rank extraction — is well-tested and correct.

eval/video/bench_msrvtt_retrieval.py warrants a second look around print_summary (line 347) and the batch_size parameter naming in embed_texts/embed_videos.

Important Files Changed

Filename Overview
eval/video/bench_msrvtt_retrieval.py New 490-line benchmark script. Core retrieval logic (embedding, ranking, metrics) is correct. Three P2 issues: duplicate R@5 line in print_summary when --top-k 5 is used; batch_size parameters that only control logging stride (not actual batching); and silent stem collision in _build_video_index.
tests/eval/video/test_bench_msrvtt_retrieval.py Thorough unit tests covering all pure helper functions with CPU-only mocks. No issues found; tests correctly verify normalization, ranking, metric computation, T2V/V2T GT rank extraction, and arg parsing.
eval/video/README.md Documentation-only addition for the new MSR-VTT benchmark. Baseline numbers, metric list, and usage examples look correct.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CLI as "CLI / main()"
    participant DS as "HF Dataset"
    participant Model as "CosmosEmbed1"
    participant Rank as "rank_queries()"
    participant Metrics as "compute_metrics()"

    CLI->>DS: "load_msrvtt(split, config, limit)"
    DS-->>CLI: "records [{video_id, video_path, caption}]"
    CLI->>CLI: "build_corpus(records) -> unique video_ids/paths"
    CLI->>Model: "embed_videos(corpus_paths, batch_size, fps)"
    Model-->>CLI: "corpus_embs (N_v, D), valid_mask"
    CLI->>Model: "embed_texts(captions, batch_size)"
    Model-->>CLI: "query_embs (N_q, D)"
    alt "direction == t2v or both"
        CLI->>Rank: "t2v_gt_ranks(query_embs, corpus_embs)"
        Rank-->>CLI: "ranks (N_q,)"
        CLI->>Metrics: "compute_metrics(ranks, top_k)"
        Metrics-->>CLI: "R@1, R@5, R@K, MedR, MeanR, MRR, NDCG@K"
    end
    alt "direction == v2t or both"
        CLI->>Rank: "v2t_gt_ranks(corpus_embs, query_embs)"
        Rank-->>CLI: "ranks (N_v,)"
        CLI->>Metrics: "compute_metrics(ranks, top_k)"
        Metrics-->>CLI: "R@1, R@5, R@K, MedR, MeanR, MRR, NDCG@K"
    end
    CLI->>CLI: "print_summary() + optional JSON write"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CLI as "CLI / main()"
    participant DS as "HF Dataset"
    participant Model as "CosmosEmbed1"
    participant Rank as "rank_queries()"
    participant Metrics as "compute_metrics()"

    CLI->>DS: "load_msrvtt(split, config, limit)"
    DS-->>CLI: "records [{video_id, video_path, caption}]"
    CLI->>CLI: "build_corpus(records) -> unique video_ids/paths"
    CLI->>Model: "embed_videos(corpus_paths, batch_size, fps)"
    Model-->>CLI: "corpus_embs (N_v, D), valid_mask"
    CLI->>Model: "embed_texts(captions, batch_size)"
    Model-->>CLI: "query_embs (N_q, D)"
    alt "direction == t2v or both"
        CLI->>Rank: "t2v_gt_ranks(query_embs, corpus_embs)"
        Rank-->>CLI: "ranks (N_q,)"
        CLI->>Metrics: "compute_metrics(ranks, top_k)"
        Metrics-->>CLI: "R@1, R@5, R@K, MedR, MeanR, MRR, NDCG@K"
    end
    alt "direction == v2t or both"
        CLI->>Rank: "v2t_gt_ranks(corpus_embs, query_embs)"
        Rank-->>CLI: "ranks (N_v,)"
        CLI->>Metrics: "compute_metrics(ranks, top_k)"
        Metrics-->>CLI: "R@1, R@5, R@K, MedR, MeanR, MRR, NDCG@K"
    end
    CLI->>CLI: "print_summary() + optional JSON write"
Loading

Reviews (1): Last reviewed commit: "Add README and fix ruff" | Re-trigger Greptile

Comment on lines +346 to +348
print(f"R@1 : {metrics['recall_at_1']:.4f}")
print(f"R@5 : {metrics['recall_at_5']:.4f}")
print(f"R@{top_k:<4d} : {metrics[f'recall_at_{top_k}']:.4f}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicate R@5 line printed when --top-k 5 is used. print_summary unconditionally accesses metrics['recall_at_5'] on line 347 and then metrics[f'recall_at_{top_k}'] on line 348 — when top_k == 5 both resolve to the same dict key, so R@5 is printed twice and the R@{top_k} line never shows the actual top-K result. The fix is to only print the hardcoded R@5 row when it is distinct from the dynamic row.

Suggested change
print(f"R@1 : {metrics['recall_at_1']:.4f}")
print(f"R@5 : {metrics['recall_at_5']:.4f}")
print(f"R@{top_k:<4d} : {metrics[f'recall_at_{top_k}']:.4f}")
print(f"R@1 : {metrics['recall_at_1']:.4f}")
if top_k != _RECALL_AT_5_K:
print(f"R@5 : {metrics['recall_at_5']:.4f}")
print(f"R@{top_k:<4d} : {metrics[f'recall_at_{top_k}']:.4f}")

Comment on lines +187 to +196
def embed_texts(model: CosmosEmbed1, texts: list[str], batch_size: int) -> np.ndarray:
"""Embed a list of strings; returns L2-normalised (N, D) float32 array."""
embs: list[np.ndarray] = []
n = len(texts)
for i, text in enumerate(texts):
if i % batch_size == 0:
LOG.info("Text embed %d / %d", i + 1, n)
t = model.get_text_embedding(text) # (1, D) float16 cpu tensor
embs.append(t.float().numpy()[0])
return _normalise(np.stack(embs, axis=0))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 batch_size parameter controls only logging frequency, not actual batching. embed_texts calls model.get_text_embedding(text) once per string regardless of batch_size, so the CLI flag --text-batch-size is misleading to users who expect it to affect throughput or memory. The same pattern appears in embed_videos (line 213: j % max(batch_size, 1) == 0). Consider renaming both parameters to log_interval (or documenting the limitation) so users aren't surprised when changing --text-batch-size has no effect on performance.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +97 to +102
def _build_video_index(video_dir: Path) -> dict[str, str]:
"""Map {video_id_stem: absolute mp4 path} for everything under video_dir."""
index: dict[str, str] = {}
for p in video_dir.rglob("*.mp4"):
index.setdefault(p.stem, str(p))
return index

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Silent stem collision in _build_video_index. If two .mp4 files in different subdirectories share the same filename stem (e.g., train/video0.mp4 and test/video0.mp4), setdefault keeps the first one found and silently drops the second. rglob traversal order is OS-dependent, so the "winning" path is non-deterministic. A debug-level warning when a duplicate stem is encountered would make this easier to diagnose if the dataset zip structure ever changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant