Tao Validate Recipe Transfer
NVIDIAOfficialCreated by NVIDIA Corporation
- Skill
- Workflow
- Software development
- Apache-2.0
- Medium risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured, multi-phase workflow to port published computer vision research code to a new dataset. It emphasizes rigorous verification at every step, starting with a feasibility gate and a mandatory Phase A porting gate that requires reproducing the original paper's results using the authors' own checkpoints. The process includes domain gap analysis, recipe adjustment based on dataset characteristics, and a verification ladder to ensure the training pipeline is sound before committing significant compute resources. It is designed to prevent common pitfalls like test leakage, preprocessing mismatches, and inappropriate hyperparameter transfer.
What it does
- Validates research code by reproducing published results using official checkpoints.
- Identifies domain gaps between source benchmarks and custom datasets.
- Provides a framework for adjusting training recipes based on dataset scale and characteristics.
- Detects data leakage and near-duplicates across training, validation, and test splits.
- Verifies preprocessing parity between training, evaluation, and deployment paths.
- Generates status reports comparing model performance against baselines and original paper results.
When to use it
- When porting a computer vision paper's code and training recipe to a new, custom dataset.
- To diagnose why a fine-tuned vision model performs poorly in production despite good validation metrics.
- When investigating failed or disappointing computer vision training runs.
- To audit an existing computer vision pipeline before deployment.
Advantages
- Prevents the propagation of silent bugs by enforcing a strict Phase A verification gate.
- Reduces wasted compute time by catching pipeline errors early through a verification ladder.
- Provides objective metrics for domain gaps to justify recipe modifications.
- Ensures reproducibility by pinning commits and documenting environment configurations.
Usage examples
- “I want to train a model from this CV paper on my own dataset of factory parts.”
- The agent will initiate the Forward mode, starting with the Phase 0 feasibility gate to assess the task, license, and data requirements before proceeding to port the code.
- “My model has great validation mAP but fails in production. Can you help me figure out why?”
- The agent will enter Post-mortem mode, using the failure atlas to systematically diagnose potential issues like preprocessing mismatches or test leakage.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/nvidia/tao-validate-recipe-transfer/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip tao-validate-recipe-transfer.zip -d .agents/skills
Documentation by NVIDIA Corporation
CV Recipe Transfer
Port published CV work onto customer data without inheriting the silent bugs that make the resulting numbers meaningless.
The core idea
This work splits into two phases with fundamentally different epistemic status:
Phase A (Port) — get the official code running faithfully. There is external ground truth here: the authors' released checkpoint and reported number.
Phase B (Transfer) — move the recipe onto customer data. There is no external ground truth. The paper's number stops being meaningful the moment the dataset changes.
Everything verifiable must be verified in Phase A, because the safety net disappears at the boundary. Once you are in Phase B, an implementation bug and a genuine domain mismatch produce identical symptoms: a mediocre number with a clean-looking loss curve.
Two consequences that drive everything below:
Never skip the Phase A gate to "save time." It costs hours and it is the only thing that licenses trust in every number that follows.
Stop treating the paper's number as the target once the dataset changes. The target is beat the strongest cheap baseline on the customer's own test set, measured at a deliberately chosen operating point.
Mode selection
Read the request and pick a mode. Say which one you picked and why.
Post-mortem mode — a run already happened and the result was wrong, disappointing, or
suspiciously good. Go to references/postmortem.md. Do not start by rerunning anything.
Walk the checks in likelihood order and find the gate that was skipped.
Forward mode — no run yet, or starting over. Work Phase 0 → A → G → T → R → E below.
Audit mode — a pipeline exists and works, someone wants it checked before it goes to a customer. Run the Phase A gate and the Phase G report against the existing artifacts, then the parity and leakage scripts. Skip the porting work.
If the request is ambiguous, ask which of the three it is before doing anything expensive.
Phase 0 — Feasibility gate
Cheap, and it prevents the most expensive class of mistake. Do this before reading any code.
Ask and answer, in writing:
Does this need training at all? Zero-shot and promptable models cover a lot of what used to require a fine-tune — Grounding DINO, SAM-family, OWL-ViT, CLIP, and plain pretrained COCO detectors. If the customer's classes overlap COCO/LVIS vocabulary, or the task is "find the obvious object," measure a pretrained baseline first. A surprising fraction of three-week fine-tuning projects are answered by an afternoon of prompting.
Is this the right paper? Papers get picked by benchmark rank, but rank on COCO predicts very little about performance on 800 images of factory parts. Check that the paper's benchmark resembles the customer's data on the axes in Phase G.
License. Research-only, non-commercial, and AGPL clauses are common in CV repos and this is customer-facing work. Check the repo license, the weights license (often different from the code), and the license of any pretrained backbone. This is a hard gate — flag it now, not after three weeks of work.
Is there enough labeled data, and does a test set exist that reflects production? If the only data is a random export, the test set will be optimistic no matter what you do.
Record the answers. If the feasibility gate fails, say so plainly and stop — that is a successful outcome of this skill, not a failure.
Phase A — Port
Goal: prove the code, the data pipeline, the preprocessing, and the metric implementation
are all faithful. Read references/port-gate.md for the detailed procedure.
The gate, in short:
Pin the commit. Repos drift and released code routinely differs from the paper. Pin the hash. Then read the repo's issue threads — undocumented hyperparameters, known discrepancies with the paper, and "why can't I reproduce Table 2" live there. Assume the code and the paper disagree somewhere until proven otherwise.
Build the environment. Custom CUDA ops (deformable attention, DCN, custom NMS) in older research repos are the single biggest time sink specific to CV. Detect them early and containerize. Do not fight the host toolchain.
THE GATE: run their checkpoint through their eval on their benchmark and match the reported number. This single check validates environment, data pipeline, preprocessing, and metric implementation without training anything. Hours, not days.
Match within ~0.2 points: pass.
Off by 0.5–2 points: something in preprocessing or eval config is wrong. Do not proceed.
Off by a lot: wrong checkpoint, wrong class mapping, or a silently partial state-dict load. See the failure atlas.
Validate the training path with a short run on a benchmark subset. You are checking loss-curve shape and that it descends as expected, not the final number.
Scan for hardcoded benchmark assumptions. Official code is written for exactly one dataset. Class counts, normalization constants, anchor priors, path structure, and sometimes image size are baked in — occasionally inside the model definition rather than the config. Finding all of them is most of the porting work.
references/port-gate.mdhas the grep patterns.
Do not enter Phase B until step 3 passes. If it cannot be made to pass, say so and report the gap — "this repo does not reproduce its own paper" is a legitimate and valuable finding.
Phase G — Domain gap report
Make the gap measurable rather than assumed. Run:
python scripts/domain_gap_report.py --source <source_annotations> --target <target_annotations> --out gap_report.mdIt compares the source benchmark and the customer set on image count, resolution and aspect distribution, objects per image, class balance, and — most importantly — object size as a fraction of image area.
That last axis drives more recipe decisions than any other. COCO objects average a few percent of image area. If the customer's aerial, microscopy, or inspection imagery puts them at 0.1%, that immediately implicates input resolution, FPN level assignment, anchor scales, and rules out mosaic-style augmentation. The script emits these as explicit flags with reasons, not generic warnings.
The magnitude of the gap on each axis tells you which recipe fields in Phase T are at risk. Carry the flags forward — they are the justification for every deviation from the paper.
Also run split hygiene here, before any training:
python scripts/split_leakage_check.py --splits train=<dir> val=<dir> test=<dir> --report leakage.mdNear-duplicate images across splits are the single biggest source of inflated CV numbers. Video frames sampled at 5fps, the same scene from a fixed camera, the same patient, the same production lot — a random split gives 0.95 mAP and a model that collapses on deployment. Detectable via perceptual hashing and group metadata; almost nobody checks. If this skill does one thing well, make it this.
Phase T — Transfer the recipe
The intellectual core. Classify every field in the paper's recipe into one of three
buckets before writing a config. references/recipe-fields.md has the full field-by-field
table; the buckets are:
Transfer-invariant — architecture, loss formulation, optimizer choice, most augmentation types. Carry over unchanged.
Scale-dependent — schedule length, LR (via batch size and linear scaling), augmentation strength, weight decay, EMA decay. Must be adjusted for dataset size, and the direction of adjustment is usually predictable.
Dataset-derived — anchor and box size priors, input resolution, class weighting, FPN level assignment, NMS/score thresholds. Must be recomputed from the customer's data, never copied.
The trap that catches almost everyone
If you initialize from the authors' released checkpoint — which you almost always should on a small customer dataset — their schedule is wrong for you by a large factor. Their recipe assumes ImageNet init or from-scratch training on 118k images. You are fine-tuning on 2,000. Copying the paper's 300-epoch schedule is a common, expensive, and entirely predictable mistake. Expect: much shorter schedule, lower LR (often 10x), shorter warmup, possibly a frozen stem, and weaker augmentation.
State the initialization explicitly in the config and derive the schedule from it. If you cannot say in one sentence whether you are fine-tuning or training from scratch, stop and resolve that first.
Preprocessing is a single artifact
Write the preprocessing spec — resize policy (letterbox vs stretch), channel order, normalization constants, augmentation ordering — into one file that the training pipeline, the eval path, and the export path all read. Then verify numerically:
python scripts/preprocess_parity.py --config <preproc.yaml> --image <sample.jpg> --paths train,eval,exportPreprocessing mismatch between training and inference is the failure mode that actually kills deployments: excellent validation mAP, garbage in production, and a long debugging loop because nothing ever errors. Assert tensor equality rather than trusting that two code paths do the same thing.
Phase R — Run
Apply the verification ladder from references/verification-ladder.md before spending real
GPU hours. Abbreviated:
Shape and dtype trace; parameter count against the paper's stated count
Loss at initialization equals its analytic value
Gradient flow — every parameter that should get gradient does, nothing silently frozen
Overfit a single batch to near-zero loss. If it cannot, the pipeline is wrong, full stop
Overfit ~100 images; confirm the eval path agrees with the training path on that data
Short schedule at reduced scale; check trend
Full run
Fail early, fail cheap. Most disasters are a wrong pipeline trained for three days.
Alongside the full run, keep a regression suite: the pretrained baseline from Phase 0 and any prior best model, evaluated on the same frozen test set. Log dataset version, config, and commit hash together so any checkpoint is reproducible.
Phase E — Evaluate and report
Choose an operating point. Everyone reports mAP@50-95 and then deploys at a confidence threshold nobody chose deliberately. Pick the threshold from the PR curve against the customer's actual precision/recall tradeoff — the cost of a miss versus a false alarm is a business question, so ask it — and report metrics at that threshold alongside the aggregate.
Break down, don't average. Per-class and per-slice (object size, lighting, camera, site, time-of-day). A single mAP hides the fact that the model fails entirely on the one class the customer cares about.
Error analysis. For detection, separate classification errors, localization errors, duplicates, background false positives, and missed detections — they have different fixes. High-confidence false positives are often missing annotations, not model errors: a missing box is not a missing label, it is a wrong label, and the model was explicitly taught to suppress that object. Feed these back into annotation QC.
Status artifact — emit one at every evaluation, including mid-run. Write status.json
and render it:
python scripts/render_status.py --status status.json --out status.htmlIt shows exactly two things: the delta against the paper on top, and the best result so far
against its baseline below. assets/status.example.json is the schema by example. Two rules
that make it honest:
A metric the paper never published gets
"paper": null, which renders asn/pwith no delta. Never fill that column with a number from a different variant or a blog post.If the customer dataset has no published result — the normal case — say so in
calloutand name what the comparison is actually against. "vs the previous run" and "vs a published result" are different claims.
Mid-run is a first-class state: label the cards still climbing and say so in the footnote,
so nobody quotes a partial number as final.
Report structure — use this template:
# <Task> recipe transfer: <paper> → <customer dataset>
## Outcome
One paragraph. The number, at the chosen operating point, versus the baseline.
## Phase A verification
Did the official checkpoint reproduce the reported number? Exact figures.
## Domain gap
The measured gap and which recipe fields it forced us to change.
## Recipe deviations
Table: field | paper value | our value | bucket | reason.
## Results
Per-class and per-slice at the chosen threshold. PR curve. Regression vs baseline.
## Error analysis
Failure categories with counts and examples.
## Known risks
What we could not verify, and what would change the conclusion.The "Known risks" section is not optional. It is the difference between a number a customer can act on and a number that will embarrass someone in three months.
Reference files
Read these as needed rather than upfront:
references/postmortem.md— diagnose a failed run. Symptom-ordered, cheapest checks first. Start here in post-mortem mode.references/port-gate.md— Phase A detail: commit pinning, CUDA op survival, checkpoint gate procedure, hardcoded-assumption grep patterns.references/recipe-fields.md— the full field classification table, per task family.references/verification-ladder.md— the seven rungs, with the expected analytic values.references/failure-atlas.md— symptom → cause → check, for the ~25 recurring failures.references/stacks.md— framework-specific notes: raw research repo, MMDetection / Detectron2, Ultralytics, timm / torchvision, TAO. Read only the relevant one.
Scripts
scripts/domain_gap_report.py— quantified source-vs-target gap with risk flagsscripts/split_leakage_check.py— perceptual-hash near-duplicate detection across splitsscripts/preprocess_parity.py— numerical assertion that train/eval/export preprocessing agreescripts/render_status.py— the Phase E status artifact: paper delta + best result so far
Run them with --help for options. The first three are dependency-light (numpy + Pillow) and
render_status.py is stdlib-only, so all of them run inside a research repo's container
without disturbing its environment.
Honest framing
For a customer's private task there is no SOTA. There is no leaderboard for "find defects in this customer's parts." There is only the baseline and your number. So do not promise state-of-the-art; promise a well-validated result with no methodology bugs, defensible to someone who wants to poke at it.
That is worth more than it sounds, because a large share of impressive-looking fine-tuning results are inflated by test leakage, evaluating on the training distribution, or comparing against a deliberately weak baseline. Those errors are mechanical, and this skill exists to catch them.
Files
- ATTRIBUTION.mdAdded by AutoGineer954 B
- BENCHMARK.md8.5 KiB
- LICENSELicence file added by AutoGineer11.2 KiB
- SKILL.md16.3 KiB
- assets/status.example.json1.7 KiB
- config/skillspector-baseline.yaml834 B
- evals/evals.json7.6 KiB
- references/failure-atlas.md4.2 KiB
- references/port-gate.md6.7 KiB
- references/postmortem.md9.3 KiB
- references/recipe-fields.md6.8 KiB
- references/recipe_spec.yaml4.1 KiB
- references/stacks.md4.2 KiB
- references/verification-ladder.md4.6 KiB
- scripts/domain_gap_report.pyScript13.8 KiB
- scripts/preprocess_parity.pyScript15.8 KiB
- scripts/render_status.pyScript6.8 KiB
- scripts/split_leakage_check.pyScript8.2 KiB
- skill-card.md4.1 KiB
- skill.oms.sig7.5 KiB
scripts/domain_gap_report.py
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Quantify the gap between a source benchmark and a customer dataset, and flag which
recipe fields the gap puts at risk.
The point is to replace "their data is different" with measured axes that each map to a
specific recipe decision. Object size as a fraction of image area matters most: COCO
objects average a few percent of image area, and a target set at 0.1% implicates input
resolution, FPN level assignment, anchor scales, and rules out mosaic-style augmentation.
Input: COCO-format annotation JSON for both sides. (Convert other formats first; conversion
is lossy in specific ways -- see references/failure-atlas.md.)
Usage:
python domain_gap_report.py --source instances_val2017.json --target customer.json
python domain_gap_report.py --source a.json --target b.json --out gap_report.md
"""
import argparse
import json
import sys
from collections import Counter
from pathlib import Path
import numpy as np
def load_coco(path):
with open(path) as f:
d = json.load(f)
images = {im["id"]: im for im in d.get("images", [])}
anns = [a for a in d.get("annotations", []) if not a.get("iscrowd", 0)]
cats = {c["id"]: c.get("name", str(c["id"])) for c in d.get("categories", [])}
return images, anns, cats
def stats(path):
images, anns, cats = load_coco(path)
if not images:
raise SystemExit(f"error: no images found in {path}")
w = np.array([im.get("width", 0) for im in images.values()], dtype=float)
h = np.array([im.get("height", 0) for im in images.values()], dtype=float)
valid = (w > 0) & (h > 0)
per_img = Counter(a["image_id"] for a in anns)
counts = np.array([per_img.get(i, 0) for i in images], dtype=float)
# object area as fraction of image area
fracs, boxw, boxh = [], [], []
for a in anns:
im = images.get(a["image_id"])
if not im:
continue
iw, ih = im.get("width", 0), im.get("height", 0)
if iw <= 0 or ih <= 0:
continue
bb = a.get("bbox")
if not bb or len(bb) != 4:
continue
bw, bh = float(bb[2]), float(bb[3])
if bw <= 0 or bh <= 0:
continue
fracs.append((bw * bh) / (iw * ih))
boxw.append(bw)
boxh.append(bh)
fracs = np.array(fracs) if fracs else np.array([0.0])
boxw = np.array(boxw) if boxw else np.array([1.0])
boxh = np.array(boxh) if boxh else np.array([1.0])
aspect = boxw / np.maximum(boxh, 1e-9)
cls = Counter(cats.get(a["category_id"], str(a["category_id"])) for a in anns)
total = sum(cls.values()) or 1
freqs = np.array(sorted(cls.values(), reverse=True), dtype=float)
return {
"n_images": len(images),
"n_anns": len(anns),
"n_classes": len(cls),
"img_w_med": float(np.median(w[valid])) if valid.any() else 0.0,
"img_h_med": float(np.median(h[valid])) if valid.any() else 0.0,
"img_aspect_med": float(np.median((w[valid] / np.maximum(h[valid], 1e-9)))) if valid.any() else 0.0,
"img_aspect_iqr": float(np.subtract(*np.percentile(w[valid] / np.maximum(h[valid], 1e-9), [75, 25]))) if valid.any() else 0.0,
"objs_per_img_med": float(np.median(counts)),
"objs_per_img_p95": float(np.percentile(counts, 95)),
"objs_per_img_max": float(counts.max()) if counts.size else 0.0,
"empty_img_pct": float(100.0 * (counts == 0).mean()) if counts.size else 0.0,
"area_frac_med": float(np.median(fracs)),
"area_frac_p05": float(np.percentile(fracs, 5)),
"area_frac_p95": float(np.percentile(fracs, 95)),
"box_aspect_med": float(np.median(aspect)),
"box_aspect_p05": float(np.percentile(aspect, 5)),
"box_aspect_p95": float(np.percentile(aspect, 95)),
"imbalance": float(freqs.max() / max(freqs.min(), 1e-9)) if freqs.size else 1.0,
"top_class_pct": float(100.0 * freqs.max() / total) if freqs.size else 0.0,
"classes": cls,
}
def fmt(v, p=2):
return f"{v:.{p}f}"
def build_flags(s, t):
"""Each flag: (severity, axis, finding, recipe implication)."""
flags = []
# --- object size, the dominant axis ---
if s["area_frac_med"] > 0 and t["area_frac_med"] > 0:
ratio = t["area_frac_med"] / s["area_frac_med"]
if ratio < 0.25:
flags.append((
"HIGH", "Object size",
f"Target objects are {1/ratio:.0f}x smaller than source "
f"({fmt(100*t['area_frac_med'],3)}% vs {fmt(100*s['area_frac_med'],3)}% of image area).",
"Raise input resolution, shift FPN level assignment toward finer levels, recompute "
"anchor scales, and disable mosaic/mixup. Report per-size metric breakdown -- aggregate "
"mAP will hide near-zero small-object recall."))
elif ratio > 4:
flags.append((
"MED", "Object size",
f"Target objects are {ratio:.0f}x larger than source.",
"Consider dropping the finest FPN level for speed, and reducing input resolution. "
"Anchor scales need recomputing upward."))
else:
flags.append((
"OK", "Object size",
f"Comparable ({ratio:.2f}x source).",
"Anchor scales and FPN assignment likely transfer; still recompute anchors from data."))
if t["area_frac_p05"] < 0.0005:
flags.append((
"HIGH", "Tiny objects",
f"5th percentile object is {fmt(100*t['area_frac_p05'],4)}% of image area.",
"A meaningful fraction of targets may be below the model's minimum detectable size at the "
"paper's resolution. Evaluate tiling/sliced inference before assuming the architecture is wrong."))
# --- box aspect ratio ---
if s["box_aspect_p95"] > 0:
if t["box_aspect_p95"] > 3 * s["box_aspect_p95"] or t["box_aspect_p05"] < s["box_aspect_p05"] / 3:
flags.append((
"HIGH", "Box aspect ratio",
f"Target aspect spread p05-p95 = {fmt(t['box_aspect_p05'])}-{fmt(t['box_aspect_p95'])} "
f"vs source {fmt(s['box_aspect_p05'])}-{fmt(s['box_aspect_p95'])}.",
"Inherited anchor aspect ratios will not cover the target distribution. Recompute by "
"clustering target box dimensions; also revisit assigner IoU thresholds, which behave "
"poorly for extreme aspect ratios."))
# --- scene density ---
if t["objs_per_img_p95"] > 100:
flags.append((
"HIGH", "Scene density",
f"p95 is {t['objs_per_img_p95']:.0f} objects/image (max {t['objs_per_img_max']:.0f}).",
"The default max-detections cap (often 100) will truncate recall. Raise it, and raise the "
"NMS IoU threshold for crowded scenes."))
elif s["objs_per_img_med"] > 0 and t["objs_per_img_med"] > 3 * s["objs_per_img_med"]:
flags.append((
"MED", "Scene density",
f"Target median {fmt(t['objs_per_img_med'],1)} vs source {fmt(s['objs_per_img_med'],1)} objects/image.",
"Check the max-detections cap and NMS settings."))
if t["empty_img_pct"] > 10:
flags.append((
"MED", "Empty images",
f"{fmt(t['empty_img_pct'],1)}% of target images have no annotations.",
"Confirm these are true negatives rather than unlabeled images -- unlabeled positives train "
"the model to suppress real objects. Also confirm the loss handles empty targets; some repos "
"assume at least one object."))
# --- dataset scale ---
if t["n_images"] < s["n_images"] / 20:
flags.append((
"HIGH", "Dataset size",
f"Target has {t['n_images']} images vs source {s['n_images']} ({s['n_images']/max(t['n_images'],1):.0f}x smaller).",
"The paper's schedule will overfit badly. Fine-tune from the released checkpoint with a much "
"shorter schedule, LR reduced roughly 10x, shortened warmup, weaker augmentation, and possibly "
"a frozen stem. Reason in total gradient steps, not epochs."))
# --- class balance ---
if t["imbalance"] > 50 and t["imbalance"] > 5 * s["imbalance"]:
flags.append((
"HIGH", "Class imbalance",
f"Target max/min class frequency ratio is {t['imbalance']:.0f}x "
f"(source {s['imbalance']:.0f}x); top class is {fmt(t['top_class_pct'],1)}% of instances.",
"Loss will be dominated by the majority class. Consider class-balanced sampling or loss "
"weighting, and always report per-class metrics rather than aggregate mAP."))
if t["n_classes"] != s["n_classes"]:
flags.append((
"INFO", "Class count",
f"Target has {t['n_classes']} classes, source {s['n_classes']}.",
"Head must be reinitialized. Verify the framework's num_classes semantics (N vs N+1 for "
"background) and check every place a CLASSES list is defined."))
# --- resolution / aspect ---
if s["img_w_med"] > 0 and t["img_w_med"] > 0:
r = (t["img_w_med"] * t["img_h_med"]) / (s["img_w_med"] * s["img_h_med"])
if r > 4 or r < 0.25:
flags.append((
"MED", "Image resolution",
f"Target median {t['img_w_med']:.0f}x{t['img_h_med']:.0f} vs source "
f"{s['img_w_med']:.0f}x{s['img_h_med']:.0f}.",
"Revisit the resize policy. Downscaling high-resolution imagery to the paper's input size "
"can destroy small objects; consider tiling instead."))
if t["img_aspect_iqr"] > 0.5:
flags.append((
"MED", "Aspect variability",
f"Target image aspect ratio IQR is {fmt(t['img_aspect_iqr'])}.",
"Letterbox vs stretch resize matters more here. Fix the policy in one shared preprocessing "
"artifact and verify export parity across the full aspect range."))
return flags
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--source", required=True, help="source benchmark COCO json (what the paper trained on)")
ap.add_argument("--target", required=True, help="customer dataset COCO json")
ap.add_argument("--out", default=None, help="write markdown report here")
args = ap.parse_args()
s = stats(args.source)
t = stats(args.target)
L = ["# Domain gap report", ""]
L.append(f"Source: `{Path(args.source).name}` — Target: `{Path(args.target).name}`")
L.append("")
L.append("## Measured axes")
L.append("")
L.append("| Axis | Source | Target |")
L.append("|---|---|---|")
rows = [
("Images", f"{s['n_images']}", f"{t['n_images']}"),
("Annotations", f"{s['n_anns']}", f"{t['n_anns']}"),
("Classes", f"{s['n_classes']}", f"{t['n_classes']}"),
("Median image size", f"{s['img_w_med']:.0f}x{s['img_h_med']:.0f}", f"{t['img_w_med']:.0f}x{t['img_h_med']:.0f}"),
("Objects/image (median)", fmt(s['objs_per_img_med'], 1), fmt(t['objs_per_img_med'], 1)),
("Objects/image (p95)", fmt(s['objs_per_img_p95'], 1), fmt(t['objs_per_img_p95'], 1)),
("Images with no objects", f"{fmt(s['empty_img_pct'],1)}%", f"{fmt(t['empty_img_pct'],1)}%"),
("Object area % (median)", fmt(100 * s['area_frac_med'], 3), fmt(100 * t['area_frac_med'], 3)),
("Object area % (p05)", fmt(100 * s['area_frac_p05'], 4), fmt(100 * t['area_frac_p05'], 4)),
("Object area % (p95)", fmt(100 * s['area_frac_p95'], 2), fmt(100 * t['area_frac_p95'], 2)),
("Box aspect (p05-p95)", f"{fmt(s['box_aspect_p05'])}-{fmt(s['box_aspect_p95'])}",
f"{fmt(t['box_aspect_p05'])}-{fmt(t['box_aspect_p95'])}"),
("Class imbalance (max/min)", f"{s['imbalance']:.0f}x", f"{t['imbalance']:.0f}x"),
]
for r in rows:
L.append(f"| {r[0]} | {r[1]} | {r[2]} |")
L.append("")
flags = build_flags(s, t)
order = {"HIGH": 0, "MED": 1, "INFO": 2, "OK": 3}
flags.sort(key=lambda f: order.get(f[0], 9))
L.append("## Recipe risk flags")
L.append("")
n_high = sum(1 for f in flags if f[0] == "HIGH")
if n_high:
L.append(f"**{n_high} high-severity flag(s).** Each names a recipe field that cannot be "
"inherited from the paper unchanged.")
else:
L.append("No high-severity flags. The recipe is more likely to transfer, but still recompute "
"all DERIVED fields from the target data.")
L.append("")
for sev, axis, finding, action in flags:
L.append(f"### [{sev}] {axis}")
L.append("")
L.append(f"{finding}")
L.append("")
L.append(f"**Implication:** {action}")
L.append("")
L.append("## Target class distribution (top 15)")
L.append("")
L.append("| class | instances | % |")
L.append("|---|---|---|")
tot = sum(t["classes"].values()) or 1
for name, n in t["classes"].most_common(15):
L.append(f"| {name} | {n} | {fmt(100*n/tot,1)} |")
L.append("")
L.append("Classes with very few instances will have unreliable per-class metrics regardless of "
"model quality. Decide up front whether they are in scope.")
L.append("")
L.append("## Next")
L.append("")
L.append("Carry these flags into Phase T (`references/recipe-fields.md`). Every deviation from the "
"paper's recipe should cite one of them as its justification, and every DERIVED field must "
"be recomputed from the target data rather than copied.")
L.append("")
report = "\n".join(L)
if args.out:
Path(args.out).write_text(report)
print(f"Report written to {args.out}", file=sys.stderr)
print(report)
return 0
if __name__ == "__main__":
sys.exit(main())
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Needs Python
The author says: Methodology skill — no container of its own. Three of the four scripts need only Python 3 with numpy and Pillow; render_status.py is stdlib-only. They run inside a research repo's own environment without disturbing it. Phases A and R execute the ported repo's training/eval, so they inherit that repo's requirements (typically docker + nvidia-container-toolkit + an NVIDIA GPU).
Security
What our scanner noticed· Medium risk
- Large base64-encoded blobs
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- NVIDIA Corporation
- Publisher
- NVIDIA (Official)
- Source
- NVIDIA/skills · skills/tao-validate-recipe-transfer · commit fce3cca
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE-APACHE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by NVIDIA Corporation or NVIDIA.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tao-validate-recipe-transfer.zip