option
MaisonMaison Skill Science des données et ML tao-analyze-gaps-visual-changenet

tao-analyze-gaps-visual-changenet

NVIDIA/skills NVIDIA/skills

Identifie les échantillons les plus faibles pour chaque étiquette de référence dans les expériences NVIDIA TAO VCN Classify en exécutant un conteneur Docker qui effectue un balayage des seuils, une notation des faiblesses et une expansion en fonction de l'éclairage, puis met en évidence les K échantillons les plus faibles en vue d'une augmentation ou d'un réétiquetage en aval.

...Développer tout
2
Heure mise à jour 28 septembre 2026

Compétence en analyse des écarts pour TAO VCN Classify

Vous êtes analyste des résultats d’inférence de NVIDIA TAO VCN Classify (Visual Component Net). Votre mission consiste à identifier les échantillons les plus faibles pour chaque étiquette de référence en mesurant la distance signée par rapport au seuil de décision dans la mauvaise direction, puis à les mettre en évidence en vue d’une augmentation ou d’un réétiquetage en aval.

Cette compétence est volontairement légère. La tête de classification de VCN est une frontière binaire à score unique (PASS vs NO_PASS selon le siamese_score) ; l’analyse est donc de nature computationnelle, et non investigative. L’ensemble du calcul s’effectue via une seule invocation directe de Docker sur l’image tao_toolkit.data_services déclarée dans versions.yaml (résolue lors de l’exécution — voir Configuration). Le point d’entrée du conteneur prend la forme [hydra overrides...]; nous lui transmettons gap_analysis vcn_aoi key=value …. Chaque remplacement est une simple paire clé=valeur Hydra qui remplace de manière sélective le schéma GapAnalysisConfig du script (les valeurs par défaut sont intégrées au conteneur ; inspectez-le avec `docker run ... gap_analysis vcn_aoi --cfg=job`). (Il n’y a pas de mot-clé « dataset » à l’intérieur du conteneur — il s’agit du préfixe « pillar » du lanceur TAO, qui est supprimé ici.) Vous n’ avez pas besoin d’analyse déléguée, d’audits d’images en plusieurs phases ou de regroupement par type de composant — VCN n’expose pas ces dimensions. Examinez uniquement un petit ensemble d’échantillons faibles représentatifs pour qualifier les lacunes une fois que le conteneur a renvoyé ses résultats.

L’interface CLI peut varier selon les versions du conteneur data-services. Si l’appel de gap_analysis vcn_aoi échoue lors de l’analyse des arguments, examinez le schéma réel une fois par image à l’aide de la commande docker run --rm "$DS_IMAGE" gap_analysis vcn_aoi --cfg=job et harmonisez les clés renommées (par exemple, inference_csv vs inference_results_dir, output_dir vs results_dir) avant de réessayer. Le nom du fichier Parquet de sortie est kpi_gaps.parquet.

Données d’entrée

  1. Répertoire des résultats de l’expérience — contient le fichier ` inference/inference.csv ` issu de l’inférence TAO VCN Classify. Colonnes requises : `input_path`, `object_name`, `label`, `siamese_score`. Transmettez le répertoire (par exemple inference/latest/), et non le fichier CSV — le conteneur lit le fichier inference_results_dir/inference.csv.
  2. Répertoire du code/de la configuration d’entraînement — contient le fichier YAML d’entraînement VCN. Le conteneur lit les fichiers dataset.classify.input_map (liste des conditions d’éclairage) et dataset.classify.image_ext à partir de celui-ci pour développer chaque échantillon faible en une ligne par condition d’éclairage.
  3. Répertoire « dataset » — la racine des images est ajoutée au début du chemin d’accès relatif « input_path » de chaque ligne (kpi_media_path).
  4. Remplacements de schéma — min_recall, top_k_per_label et, en option, un seuil fixe sont transmis en tant que remplacements Hydra (valeurs par défaut : min_recall=1,0, top_k_per_label=50, threshold=-1,0, ce qui signifie un balayage). top_k_per_label doit être un entier positif — son omission fait basculer le conteneur en mode « filtre en dessous du seuil », ce qui, avec min_recall=1,0, ne renvoie que les erreurs de classification PASS et aucune ligne NO_PASS. Voir « Pièges courants ».

Configuration

Le balayage de seuil, le classement des faiblesses et l’expansion par éclairage s’exécutent tous au sein de l’image tao_toolkit.data_services déclarée dans versions.yaml. Résolvez l’URI concrète une seule fois au début de l’exécution, puis vérifiez que Docker, la boîte à outils de conteneurs NVIDIA et un GPU sont présents, et assurez-vous que l’image est mise en cache :

# Résoudre tao_toolkit.data_services → URI concrète nvcr.io/... à partir de versions.yaml
DS_IMAGE=$(python3 -c "import yaml,os; print(yaml.safe_load(open(os.environ['TAO_SKILL_BANK_PATH']+'/versions.yaml'))['images']['tao_toolkit']['data_services'])")
echo "DS_IMAGE=$DS_IMAGE"

docker info > /dev/null && echo "OK : Docker"
nvidia-smi > /dev/null && echo "OK : GPU"
docker image inspect "$DS_IMAGE" > /dev/null \
  || docker pull "$DS_IMAGE"

TAO_SKILL_BANK_PATH est généralement exportée par la banque de compétences installée. Si elle n’est pas définie, pointez-la vers la racine du dépôt de la banque de compétences avant la résolution. Un GPU est requis ; interrompre la procédure dès le début sur un hôte sans GPU évite une erreur tardive source de confusion.

Trois règles de configuration sont essentielles et peuvent facilement prêter à confusion :

  • Montage des chemins d’accès — chaque chemin d’accès de l’hôte que le conteneur lit ou écrit (inference.csv, fichier YAML d’entraînement, racine de l’image du jeu de données, répertoire de sortie) doit être monté en bind mount, le plus simplement avec -v $WORKSPACE:$WORKSPACE -w $WORKSPACE afin que les chemins absolus soient résolus de manière identique des deux côtés.
  • Ne passez pas --user $(id -u):$(id -g) — cela déclenche une erreur KeyError : « getpwuid(): uid not found: » lors de l’importation des transformateurs par le conteneur ; la commande chown renvoie ensuite l’UID de l’hôte.
  • -e est obligatoire, et non facultatif — les images actuelles l’exigent impérativement et se terminent par une erreur ValueError : « La sous-tâche vcn_aoi nécessite l’argument suivant : -e/--experiment_spec_file » avant d’analyser les remplacements de la ligne de commande.

Consultez le fichier references/container-setup.md pour connaître le modèle complet de montage de chemins d’accès, la justification de l’utilisation de--user/chown et de la commande chown d’Alpine, les recommandations relatives à l’utilisation de plusieurs options --v, ainsi que les détails concernant l’exigence -e .

Méthode

L’ensemble de la compétence consiste en un seul appel `docker run` suivi d’un petit contrôle visuel ponctuel. Le conteneur effectue en interne les étapes 1 à 4 (balayage de seuil, notation des faiblesses, sélection des K meilleurs, expansion par éclairage). Vous gérez l’étape 5 (contrôle visuel ponctuel) directement avec l’outil Read.

Étapes 1 à 4 — Exécuter le conteneur

$DOCKER gap_analysis vcn_aoi \
    inference_results_dir=/inference/

Toujours passer top_k_per_label. C’est l’argument qui fait passer le conteneur du filtre par défaut « échantillons inférieurs au seuil » à un classement top-K-par-étiquette correct. Avec min_recall=1,0, le seuil est, par construction, égal ou inférieur à chaque score NO_PASS ; le filtre « en dessous du seuil » ne renvoie donc QUE les lignes PASS mal classées et aucune ligne NO_PASS — ce qui est inutile en tant que file d’attente d’augmentation. Lorsque top_k_per_label est défini sur un entier positif (soit dans la spécification, soit via une redéfinition Hydra), le conteneur calcule la faiblesse signée par rapport au seuil pour chaque ligne et fait remonter les K plus faibles par étiquette de vérité de base, ce qui constitue la sortie classée par étiquette utilisée par les étapes en aval.

Lit le fichier inference.csv, parcourt chaque valeur unique de siamese_score plus une juste en dessous du minimum, conserve les candidats dont le rappel de la classe NO_PASS est ≥ min_recall (avec une tolérance de 1e-12 ), puis choisit le seuil présentant le meilleur F1 (en cas d’égalité : précision, puis valeur du seuil). Pour chaque ligne, calcule la faiblesse signée par rapport à ce seuil (positive = classification erronée, négative = correcte, amplitude = marge). Trie par faiblesse en ordre décroissant et sélectionne les top_k_per_label les plus élevés par étiquette de référence, puis développe chaque ligne faible en une ligne par condition d’éclairage à l’aide de dataset.classify.input_map et dataset.classify.image_ext issus du fichier YAML d’entraînement.

Si aucun seuil candidat n’atteint l’objectif de rappel, le conteneur se termine avec un code de sortie non nul et écrit le fichier unreachable_kpi.txt dans le répertoire results_dir, expliquant quel rappel le modèle peut réellement atteindre. Dans ce cas, arrêtez l’analyse après l’appel Docker, rédigez un rapport d’une seule section expliquant que le modèle est fondamentalement incapable d’atteindre le KPI quel que soit le point de fonctionnement, et recommandez un réentraînement ou un réétiquetage — ignorez le contrôle visuel ponctuel.

Le conteneur écrit dans le répertoire « results_dir »:

Artifact Contenu
kpi_gaps.parquet Les Top-K plus faibles par étiquette, détaillés par éclairage. Colonnes : filepath, label, siamese_score, weakness.
threshold.txt Seuil de décision choisi (nombre à virgule flottante unique, texte brut).
metrics.json Au seuil choisi : précision, rappel, F1, matrice de confusion {tp, fp, tn, fn}, ainsi que par étiquette {total, mean_weakness, median_weakness, max_weakness, n_misclassified}.
weak_samples_breakdown.txt Répartition des lignes conservées par étiquette : total, <%> sur l’ensemble des lignes conservées, N mal classées (faiblesse > 0), N marginales (faiblesse ≤ 0).
unreachable_kpi.txt Écrit uniquement lorsque l'objectif de rappel est inatteignable. La présence de ce fichier signifie : ignorer l'étape 5, générer le rapport abrégé, recommander un réentraînement.

Affichez le résumé de la sortie standard du conteneur (seuil choisi, nombre de lignes conservées, répartition par étiquette) sur votre propre sortie standard afin que le hook script-check puisse vérifier la sortie générée par l'exécution.

Étape 5 — Contrôle visuel ponctuel (petit, fixe)

Ignorez cette étape si le fichier unreachable_kpi.txt existe. Sinon, utilisez l’outil « Read » pour afficher les 5 échantillons PASS les plus faibles et les 5 échantillons NO_PASS les plus faibles issus du fichier kpi_gaps.parquet (dédupliqués à une ligne par échantillon, en utilisant le chemin d’accès FIRST-lighting), classez chacun d’entre eux dans l’une des catégories suivantes : « étiquetage erroné », « cas limite », « qualité des données » ou « systématique », puis copiez chaque image visualisée (redimensionnée à 128×128 si le PIL est disponible, sinon copiez-la simplement) dans /rca_images/. Il s’agit de la seule inspection d’images requise — ne consultez pas des dizaines d’images, n’effectuez pas de regroupement par mode de défaillance et ne vérifiez pas les images de référence (VCN ne dispose pas d’images de référence).

Consultez le fichier references/visual-spot-check.md pour connaître l’ordre exact de sélection des échantillons, la règle de déduplication par éclairage, la définition complète de chaque catégorie de verdict et les détails relatifs à la copie des images.

Appel de référence

Copiez-collez et modifiez l’espace de travail, les quatre chemins d’accès et les deux paramètres numériques ; cela exécute le processus de bout en bout. Capturez la sortie standard (stdout) afin que le hook script-check puisse voir le nombre de lignes.

WORKSPACE=           # monté de manière identique à l’intérieur du conteneur
EXP_DIR=     # contient inference/inference.csv et train.yaml ; doit se trouver à l’intérieur de $WORKSPACE
DATASET_ROOT=         # racine des images pour les entrées input_path du fichier inference.csv ; doit se trouver dans $WORKSPACE
MIN_RECALL=1.0                       # valeur par défaut « zéro omission » ; à réduire si les exigences des indicateurs de performance sont assouplies
TOP_K=50                             # budget d’augmentation par étiquette
OUT="$EXP_DIR/rca_results/$(date +%Y-%m-%d_%H%M%S)"
SPEC="$OUT/vcn_aoi_spec.yaml"
IMG=$(python3 -c "import yaml,os; print(yaml.safe_load(open(os.environ['TAO_SKILL_BANK_PATH']+'/versions.yaml'))['images']['tao_toolkit']['data_services'])")

mkdir -p "$OUT"

# Écrire la spécification d’analyse des écarts pour cette exécution
cat > "$SPEC" <<EOF
min_recall: $MIN_RECALL
top_k_per_label: $TOP_K
EOF

docker run --gpus all --rm --ipc=host \
    -v "$WORKSPACE:$WORKSPACE" -w "$WORKSPACE" \
    "$IMG" gap_analysis vcn_aoi \
    -e "$SPEC" \
    inference_results_dir="$EXP_DIR/inference/latest/" \
    train_config="$EXP_DIR/train.yaml" \
    kpi_media_path="$DATASET_ROOT" \
    results_dir="$OUT"

# Le conteneur écrit en tant qu’utilisateur root avec l’option --user désactivée ; réattribuer les droits de propriété à l’UID de l’hôte si nécessaire.
docker run --rm -v "$WORKSPACE:/w" alpine chown -R "$(id -u):$(id -g)" "/w/$(realpath --relative-to="$WORKSPACE" "$OUT")"

# Affichage de contrôle pour que le hook script-check voie des nombres réels
python3 - "$OUT" << 'PYEOF'
import json, os, sys
out = sys.argv[1]
unreachable = os.path.join(out, "unreachable_kpi.txt")
if os.path.isfile(unreachable):
    print("KPI INACCESSIBLE — voir", unreachable)
    sys.exit(0)
with open(os.path.join(out, "threshold.txt")) as f:
    print("seuil :", f.read().strip())
with open(os.path.join(out, "metrics.json")) as f:
    m = json.load(f)
print(f"précision={m['precision']:.4f} rappel={m['recall']:.4f} F1={m['f1']:.4f}")
import pandas as pd
df = pd.read_parquet(os.path.join(out, "kpi_gaps.parquet"))
print(f"kpi_gaps.parquet : rows={len(df)}, cols={list(df.columns)}")
print(df['label'].value_counts())
PYEOF

Résultats

Enregistrer tout dans un dossier horodaté situé dans le répertoire des résultats de l'expérience. Les résultats du conteneur y sont directement enregistrés ; le contrôle visuel ponctuel écrit dans le répertoire rca_images/; tout hook de packaging d'exécution peut ajouter des artefacts de capture session/config après l'écriture du fichier RCA_Report.md.

/rca_results/AAAA-MM-JJ_HHMMSS/
├── RCA_Report.md              # Rapport complet d’analyse des écarts (à rédiger par vos soins)
├── kpi_gaps.parquet           # Conteneur : les K plus faibles par étiquette, détaillés par éclairage
├── threshold.txt              # Conteneur : seuil de décision choisi (nombre à virgule flottante unique)
├── metrics.json               # Conteneur : matrice de confusion + statistiques de distribution par étiquette
├── weak_samples_breakdown.txt # Conteneur : nombres par étiquette / échantillons mal classés / nombres marginaux
├── unreachable_kpi.txt        # Conteneur : UNIQUEMENT lorsqu’aucun seuil ne satisfait à min_recall
├── rca_images/                # Vous : vignettes des 10 échantillons faibles visualisés
├── rca_config/                # Copié automatiquement par le hook
└── session log/artifacts      # Facultatif, capture du packaging dépendant de l’exécution

Au début de l’exécution, récupérez l’horodatage réel en exécutant la commande `date +%Y-%m-%d_%H%M%S ` dans Bash. Ne l’indiquez PAS de manière statique et ne le devinez PAS. Si l’utilisateur spécifie un chemin de sortie personnalisé, utilisez-le à la place tout en conservant la même structure interne.

Pièges courants

Le mode de défaillance le plus grave consiste à oublier top_k_per_label lorsque min_recall=1.0: à ce niveau de rappel, le seuil choisi est égal ou inférieur à chaque score NO_PASS ; ainsi, sans top_k_per_label, le conteneur revient à un filtre « échantillons inférieurs au seuil » qui renvoie UNIQUEMENT les lignes PASS mal classées et zéro ligne NO_PASS, ce qui perturbe la file d’attente d’augmentation. Incluez toujours une valeur positive explicite pour top_k_per_label (valeur par défaut : 50) dans la spécification ou en tant que paramètre de remplacement Hydra.

Consultez references/pitfalls.md pour la liste de contrôle complète, couvrant : l’oubli de top_k_per_label; le passage de l’option --user; l’appel avec uniquement des modifications Hydra (sans -e ) ; un fichier de spécification en dehors de $WORKSPACE; un fichier de spécification contenant des sentinelles ??? non résolues ; une image non récupérée / une balise erronée ; incompatibilité de chemin de montage ; fichier unreachable_kpi.txt écrit ; fichier inference.csv manquant des colonnes obligatoires ; YAML d’entraînement manquant dataset.classify.input_map ou image_ext; kpi_media_path ne correspondant pas aux préfixes d’input_path; et aucun GPU détecté depuis l’intérieur du conteneur.

Structure du rapport

Rédigez le fichier RCA_Report.md sous la forme d’une analyse concise (1 000 à 1 800 mots) des lacunes de calcul — la profondeur provient de chiffres précis et d’une liste d’actions claire, et non d’un récit. Le modèle complet du rapport (7 sections : Verdict, Sélection des seuils, Répartition des faiblesses, Échantillons les plus faibles Top-K, Vérification visuelle ponctuelle, Ventilation par étiquette, Actions recommandées — avec la matrice de confusion et la mise en page des tableaux) se trouve dans references/output-template.md. Lorsque le fichier unreachable_kpi.txt existe, remplacez les sections 3 à 6 par une seule section concise reprenant le contenu de ce fichier et réduisez la section 7 à une seule recommandation : réentraînement ou réétiquetage.

Ordre d’exécution

  1. Résolvez DS_IMAGE à partir de versions.yaml (images.tao_toolkit.data_services), puis exécutez une fois les commandes docker info, nvidia-smi et docker image inspect "$DS_IMAGE" (en effectuant un pull si l’image est manquante) pour valider l’environnement. Interrompez le processus avec un message clair en cas d’échec de l’une de ces commandes.
  2. Exécutez « date +%Y-%m-%d_%H%M%S » pour obtenir l’horodatage ; créez les répertoires « /rca_results/ » et «/ ».
  3. Écrivez le fichier ` vcn_aoi_spec.yaml ` dans le répertoire horodaté en renseignant les champs `min_recall ` et `top_k_per_label`. Conservez-le sous `$WORKSPACE ` afin que le chemin `-e ` soit résolu à l’intérieur du conteneur.
  4. Exécutez ` docker run … "$DS_IMAGE" gap_analysis vcn_aoi -e vcn_aoi_spec.yaml inference_results_dir=… train_config=… kpi_media_path=… output_dir=…`. Le conteneur écrit les fichiers kpi_gaps.parquet, threshold.txt, metrics.json et weak_samples_breakdown.txt dans le répertoire results_dir. Affichez le seuil choisi et le nombre de lignes conservées sur la sortie standard (stdout) afin que le hook script-check puisse vérifier la sortie générée par l’exécution.
  5. Si le fichier unreachable_kpi.txt existe, ignorez l’étape 6 et générez le rapport abrégé. Sinon, continuez.
  6. Sélectionnez 10 échantillons faibles (les 5 PASS les plus faibles + les 5 NO_PASS les plus faibles) dans kpi_gaps.parquet, affichez chaque image de test avec Read, classez-les, puis copiez-les dans le répertoire rca_images/.
  7. Écrivez RCA_Report.md en dernier — son écriture déclenche le hook de packaging, qui copie les journaux de session et la configuration de la compétence en parallèle.
Voir sur GitHub
---
name: tao-analyze-gaps-visual-changenet
description: Identifies the weakest samples per ground-truth label in NVIDIA TAO VCN Classify experiments by running a Docker container that performs threshold sweep, weakness scoring, and per-lighting expansion, then surfaces top-K weak samples for downstream augmentation or relabeling.
license: Apache-2.0
---

# TAO VCN Classify Gap Analysis Skill

You are an analyst for NVIDIA TAO VCN Classify (Visual Component Net) inference results. Your job is to identify the **weakest samples per ground-truth label** by measuring signed distance from the decision threshold *in the wrong direction*, then surface them for downstream augmentation or relabeling.

This skill is intentionally lightweight. VCN's classify head is a single-score binary boundary (PASS vs NO_PASS by `siamese_score`), so the analysis is computational, not investigative. The whole computation lives behind one direct `docker run` invocation against the `tao_toolkit.data_services` image declared in `versions.yaml` (resolved at runtime — see Setup). The container's entrypoint takes `<category> <action> [hydra overrides...]`; we pass `gap_analysis vcn_aoi key=value …`. Each override is a bare Hydra `key=value` that selectively overrides the script's `GapAnalysisConfig` schema (defaults are baked into the container; introspect with `docker run ... gap_analysis vcn_aoi --cfg=job`). (There is no `dataset` keyword inside the container — that's the TAO launcher's pillar prefix and is dropped here.) You do **not** need delegated analysis, multi-phase image audits, or component-type clustering — VCN does not expose those dimensions. View only a small set of representative weak samples to qualify the gaps after the container returns.

CLI surface can shift between data-services container builds. If a `gap_analysis vcn_aoi` invocation fails on argument parsing, introspect the actual schema once per image with `docker run --rm "$DS_IMAGE" gap_analysis vcn_aoi --cfg=job` and reconcile any renamed keys (e.g. `inference_csv` vs `inference_results_dir`, `output_dir` vs `results_dir`) before retrying. Output parquet name is `kpi_gaps.parquet`.

---

## Inputs

1. **Experiment result directory** — contains `inference/inference.csv` from TAO VCN Classify inference. Required columns: `input_path`, `object_name`, `label`, `siamese_score`. Pass the **directory** (e.g. `inference/latest/`), not the CSV file — the container reads `inference_results_dir/inference.csv`.
2. **Training code/config directory** — contains the VCN train YAML. The container reads `dataset.classify.input_map` (lighting condition list) and `dataset.classify.image_ext` from it to expand each weak sample into one row per lighting.
3. **Dataset directory** — image root prepended to the relative `input_path` from each row (`kpi_media_path`).
4. **Schema overrides** — `min_recall`, `top_k_per_label`, and optionally a hard-pinned `threshold` are passed as Hydra overrides (defaults: `min_recall=1.0`, `top_k_per_label=50`, `threshold=-1.0` meaning sweep). **`top_k_per_label` must be a positive integer** — omitting it flips the container into "below-threshold filter" mode, which at `min_recall=1.0` returns only PASS misclassifications and zero NO_PASS rows. See Common pitfalls.

---

## Setup

The threshold sweep, weakness ranking, and per-lighting expansion all run inside the `tao_toolkit.data_services` image declared in `versions.yaml`. Resolve the concrete URI once at the top of the run, then confirm Docker, the NVIDIA container toolkit, and a GPU are present and ensure the image is cached:

```bash
# Resolve tao_toolkit.data_services → concrete nvcr.io/... URI from versions.yaml
DS_IMAGE=$(python3 -c "import yaml,os; print(yaml.safe_load(open(os.environ['TAO_SKILL_BANK_PATH']+'/versions.yaml'))['images']['tao_toolkit']['data_services'])")
echo "DS_IMAGE=$DS_IMAGE"

docker info > /dev/null && echo "OK: docker"
nvidia-smi > /dev/null && echo "OK: GPU"
docker image inspect "$DS_IMAGE" > /dev/null \
  || docker pull "$DS_IMAGE"
```

`TAO_SKILL_BANK_PATH` is usually exported by the installed skill bank. If it is unset, point it at the skill-bank repo root before resolving. A GPU is required; aborting early on a GPU-less host saves a confusing late error.

Three setup rules are load-bearing and easy to get wrong:

- **Path mounting** — every host path the container reads or writes (`inference.csv`, train YAML, dataset image root, output dir) must be bind-mounted, simplest with `-v $WORKSPACE:$WORKSPACE -w $WORKSPACE` so absolute paths resolve identically on both sides.
- **Do not pass `--user $(id -u):$(id -g)`** — it triggers `KeyError: 'getpwuid(): uid not found: <uid>'` during the container's `transformers` import; `chown` outputs back to the host UID afterwards instead.
- **`-e <spec>` is required, not optional** — current images hard-require it and exit with `ValueError: The subtask vcn_aoi requires the following argument: -e/--experiment_spec_file` before parsing CLI overrides.

See `references/container-setup.md` for the full path-mounting pattern, the `--user`/`chown` rationale and `alpine` chown command, multi-`-v` guidance, and the `-e <spec>` requirement detail.

---

## Method

The whole skill is a single `docker run` invocation followed by a small visual spot-check. The container does Steps 1–4 internally (threshold sweep, weakness scoring, top-K selection, per-lighting expansion). You handle Step 5 (visual spot-check) directly with the Read tool.

### Step 1–4 — Run the container

```bash
$DOCKER gap_analysis vcn_aoi \
    inference_results_dir=<exp_dir>/inference/<label>/ \
    train_config=<exp_dir>/train.yaml \
    kpi_media_path=<dataset_root> \
    results_dir=<rca_results_dir> \
    top_k_per_label=50
```

> **Always pass `top_k_per_label`.** This is the argument that switches the container
> from the default "samples below threshold" filter into proper top-K-per-label
> ranking. At `min_recall=1.0` the threshold is by construction at-or-below every
> NO_PASS score, so the below-threshold filter returns ONLY misclassified PASS rows
> and zero NO_PASS rows — useless as an augmentation queue. With `top_k_per_label`
> set to a positive integer (either in the spec or as a Hydra override), the
> container computes signed weakness against the threshold for every row and
> surfaces the K weakest **per ground-truth label**, which is the per-label ranked
> output downstream steps consume.

Reads `inference.csv`, sweeps every unique `siamese_score` plus one value just below the minimum, keeps the candidates with NO_PASS-class recall ≥ `min_recall` (with `1e-12` tolerance), then picks the threshold with the best F1 (tie-break: precision, then threshold value). For every row, computes signed weakness from that threshold (positive = misclassified, negative = correct, magnitude = margin). Sorts by weakness descending and takes the top `top_k_per_label` per ground-truth label, then expands each weak row into one row per lighting condition using `dataset.classify.input_map` and `dataset.classify.image_ext` from the train YAML.

If **no** candidate threshold meets the recall target, the container exits non-zero and writes `unreachable_kpi.txt` into `results_dir` explaining which recall the model can actually achieve. In that case, stop the analysis after the docker call, write a one-section report explaining the model fundamentally cannot reach the KPI at any operating point, and recommend retraining or relabeling — skip the visual spot-check.

**Container writes into `results_dir`:**

| Artifact | Contents |
|----------|----------|
| `kpi_gaps.parquet` | Top-K weakest per label, expanded per lighting. Columns: `filepath`, `label`, `siamese_score`, `weakness`. |
| `threshold.txt` | Chosen decision threshold (single float, plain text). |
| `metrics.json` | At the chosen threshold: `precision`, `recall`, `f1`, confusion matrix `{tp, fp, tn, fn}`, plus per-label `{total, mean_weakness, median_weakness, max_weakness, n_misclassified}`. |
| `weak_samples_breakdown.txt` | Per-label kept-row breakdown: `<count>` total, `<%>` of all kept rows, `N` misclassified (weakness > 0), `N` marginal (weakness ≤ 0). |
| `unreachable_kpi.txt` | Only written when the recall target is unreachable. Presence of this file means: skip Step 5, write the abridged report, recommend retrain. |

Print the container's stdout summary (chosen threshold, kept-row counts, per-label breakdown) to your own stdout so the script-check hook can verify the run produced output.

### Step 5 — Visual spot check (small, fixed)

Skip this step if `unreachable_kpi.txt` exists. Otherwise use the Read tool to **view** the 5 weakest PASS samples and the 5 weakest NO_PASS samples from `kpi_gaps.parquet` (deduplicated to one row per sample, using the FIRST-lighting `filepath`), classify each as exactly one of **mislabeled** / **edge case** / **data quality** / **systematic**, and copy each viewed image (resized to 128×128 if PIL is available, otherwise just copy) into `<results_dir>/rca_images/`. This is the only image inspection required — do not view dozens of images, run failure mode clustering, or audit goldens (VCN has no golden images).

See `references/visual-spot-check.md` for the exact sample-selection sort, the per-lighting deduplication rule, the full definition of each verdict category, and the image-copy detail.

---

## Reference invocation

Paste-and-edit the workspace, the four paths, and the two numeric knobs; this runs end-to-end. Capture stdout so the script-check hook sees row counts.

```bash
WORKSPACE=<absolute path>            # mounted identically inside the container
EXP_DIR=<experiment_result_dir>      # contains inference/inference.csv and train.yaml; must be inside $WORKSPACE
DATASET_ROOT=<dataset_root>          # image root for inference.csv input_path entries; must be inside $WORKSPACE
MIN_RECALL=1.0                       # zero-miss default; lower if KPI relaxes
TOP_K=50                             # per-label augmentation budget
OUT="$EXP_DIR/rca_results/$(date +%Y-%m-%d_%H%M%S)"
SPEC="$OUT/vcn_aoi_spec.yaml"
IMG=$(python3 -c "import yaml,os; print(yaml.safe_load(open(os.environ['TAO_SKILL_BANK_PATH']+'/versions.yaml'))['images']['tao_toolkit']['data_services'])")

mkdir -p "$OUT"

# Write the gap-analysis spec for this run
cat > "$SPEC" <<EOF
min_recall: $MIN_RECALL
top_k_per_label: $TOP_K
EOF

docker run --gpus all --rm --ipc=host \
    -v "$WORKSPACE:$WORKSPACE" -w "$WORKSPACE" \
    "$IMG" gap_analysis vcn_aoi \
    -e "$SPEC" \
    inference_results_dir="$EXP_DIR/inference/latest/" \
    train_config="$EXP_DIR/train.yaml" \
    kpi_media_path="$DATASET_ROOT" \
    results_dir="$OUT"

# Container writes as root with --user dropped; chown back to host UID if needed.
docker run --rm -v "$WORKSPACE:/w" alpine chown -R "$(id -u):$(id -g)" "/w/$(realpath --relative-to="$WORKSPACE" "$OUT")"

# Sanity print so the script-check hook sees real numbers
python3 - "$OUT" << 'PYEOF'
import json, os, sys
out = sys.argv[1]
unreachable = os.path.join(out, "unreachable_kpi.txt")
if os.path.isfile(unreachable):
    print("KPI UNREACHABLE — see", unreachable)
    sys.exit(0)
with open(os.path.join(out, "threshold.txt")) as f:
    print("threshold:", f.read().strip())
with open(os.path.join(out, "metrics.json")) as f:
    m = json.load(f)
print(f"precision={m['precision']:.4f} recall={m['recall']:.4f} f1={m['f1']:.4f}")
import pandas as pd
df = pd.read_parquet(os.path.join(out, "kpi_gaps.parquet"))
print(f"kpi_gaps.parquet: rows={len(df)}, cols={list(df.columns)}")
print(df['label'].value_counts())
PYEOF
```

---

## Outputs

Write everything into a timestamped folder under the experiment result directory. The container's outputs go straight there; the visual spot-check writes `rca_images/`; any runtime packaging hook may add session/config capture artifacts after `RCA_Report.md` is written.

```
<experiment_result_dir>/rca_results/YYYY-MM-DD_HHMMSS/
├── RCA_Report.md              # Full gap analysis report (you write this)
├── kpi_gaps.parquet           # Container: top-K weakest per label, expanded per lighting
├── threshold.txt              # Container: chosen decision threshold (single float)
├── metrics.json               # Container: confusion matrix + per-label distribution stats
├── weak_samples_breakdown.txt # Container: per-label count/misclassified/marginal counts
├── unreachable_kpi.txt        # Container: ONLY when no threshold meets min_recall
├── rca_images/                # You: thumbnails of the 10 viewed weak samples
├── rca_config/                # Auto-copied by hook
└── session log/artifacts      # Optional, runtime-dependent packaging capture
```

At the start of the run, get the real timestamp by running `date +%Y-%m-%d_%H%M%S` in Bash. Do NOT hardcode or guess. If the user specifies a custom output path, use that instead but maintain the same internal structure.

---

## Common pitfalls

The single most consequential failure mode is **forgetting `top_k_per_label` when `min_recall=1.0`**: at that recall the chosen threshold sits at or below every NO_PASS score, so without `top_k_per_label` the container falls back to a "samples below threshold" filter that returns ONLY misclassified PASS rows and zero NO_PASS rows, breaking the augmentation queue. Always include an explicit positive `top_k_per_label` (default 50) in the spec or as a Hydra override.

See `references/pitfalls.md` for the complete checklist, covering: forgetting `top_k_per_label`; passing `--user`; calling with only Hydra overrides (no `-e <spec>`); spec file outside `$WORKSPACE`; spec file with unresolved `???` sentinels; image not pulled / wrong tag; path-mount mismatch; `unreachable_kpi.txt` written; `inference.csv` missing required columns; train YAML missing `dataset.classify.input_map` or `image_ext`; `kpi_media_path` not matching `input_path` prefixes; and no GPU detected from inside the container.

---

## Report Structure

Write `RCA_Report.md` as a tight (1000–1800 word) computational gap analysis — depth comes from accurate numbers and a clear action list, not narrative. The full report template (7 sections: Verdict, Threshold Selection, Weakness Distribution, Top-K Weakest Samples, Visual Spot Check, Per-Label Breakdown, Recommended Actions — with the confusion-matrix and table layouts) is in `references/output-template.md`. When `unreachable_kpi.txt` exists, replace sections 3–6 with a single short section quoting that file's contents and collapse section 7 to one recommendation: retrain or relabel.

---

## Execution Order

1. Resolve `DS_IMAGE` from `versions.yaml` (`images.tao_toolkit.data_services`), then run `docker info`, `nvidia-smi`, and `docker image inspect "$DS_IMAGE"` (pulling if missing) once to confirm the environment. Abort with a clear message if any fail.
2. Run `date +%Y-%m-%d_%H%M%S` to get the timestamp; create `<experiment_result_dir>/rca_results/<timestamp>/`.
3. Write `vcn_aoi_spec.yaml` into the timestamped dir with `min_recall` and `top_k_per_label` filled in. Keep it under `$WORKSPACE` so the `-e` path resolves inside the container.
4. Run `docker run … "$DS_IMAGE" gap_analysis vcn_aoi -e vcn_aoi_spec.yaml inference_results_dir=… train_config=… kpi_media_path=… output_dir=…`. The container writes `kpi_gaps.parquet`, `threshold.txt`, `metrics.json`, `weak_samples_breakdown.txt` into `results_dir`. Print the chosen threshold and kept-row counts to stdout so the script-check hook can verify the run produced output.
5. If `unreachable_kpi.txt` exists, skip Step 6 and write the abridged report. Otherwise continue.
6. Pick 10 weak samples (5 weakest PASS + 5 weakest NO_PASS) from `kpi_gaps.parquet`, view each test image with Read, classify, and copy each into `rca_images/`.
7. Write `RCA_Report.md` last — writing it triggers the packaging hook, which copies session logs and skill config alongside.

Tous les fichiers

1 fichiers

Installer tao-analyze-gaps-visual-changenet

Téléchargez et décompressez les fichiers de compétences dans votre répertoire .claude/skills/.

Télécharger le ZIP

Clonez le dépôt et copiez les fichiers de compétence dans votre projet.

git clone https://github.com/NVIDIA/skills/tree/main/skills/tao-analyze-gaps-visual-changenet # Copy SKILL.md to your .claude/skills/ directory

Copier Copier
Configuration rapide: Copiez le dossier de la compétence dans .claude/skills/ Claude détectera automatiquement la compétence et l'utilisera
Dépôt NVIDIA/skills

Compétences similaires

web-search
Heure mise à jour 29 juin 2026
webapp-testing
Heure mise à jour 29 juin 2026
lark-base
Heure mise à jour 5 juillet 2026
agentmail
Heure mise à jour 29 juin 2026
OR