Tao Generate Anomalies
NVIDIAOfficialCreated by NVIDIA Corporation
- Skill
- Workflow
- Data and AI
- Apache-2.0
- Medium risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The TAO Generate Anomalies skill provides a comprehensive pipeline for training and using PAIDF AnomalyGen models. It supports three operational modes: full (fine-tuning and generation), fine-tune only, and inference only. The pipeline manages the entire lifecycle from checkpoint verification and dataset fine-tuning to synthetic data generation, quality evaluation, and iterative per-sample parameter refinement.
What it does
- Fine-tunes AnomalyGen models on custom anomaly datasets.
- Generates synthetic anomaly images using trained checkpoints.
- Evaluates synthetic image quality using nn_score metrics.
- Performs per-sample search for optimal guidance and crop_ratio parameters.
- Filters generated samples based on quality thresholds and performs automated regeneration.
- Provides guardrail checks to identify and replace unsafe generated content.
When to use it
- When you need to fine-tune an AnomalyGen model on a new dataset.
- When you need to generate synthetic anomaly images for training or testing.
- When you need to evaluate the quality of synthetic anomaly images.
- When you need to optimize generation parameters for specific anomaly samples.
Advantages
- Automates the end-to-end workflow from training to final evaluation.
- Supports iterative refinement of generation parameters per sample.
- Includes automated filtering and regeneration to meet target sample counts.
- Provides built-in guardrail checks for generated image content.
Usage examples
- “Fine-tune AnomalyGen on my new PCB dataset.”
- The agent initiates the fine-tuning phase (Phase 1) using the provided dataset directory and configuration.
- “Generate 50 synthetic anomaly images using an existing checkpoint.”
- The agent runs the inference-only mode to generate images based on the specified checkpoint and step.
- “Evaluate the quality of my generated anomaly images.”
- The agent executes the evaluation phase to calculate nn_score metrics and generate an evaluation log.
- “Run the full PAIDF AnomalyGen pipeline for my experiment.”
- The agent executes the full mode (Phases 0-7), covering checkpoint verification, fine-tuning, generation, search, and final filtering.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/nvidia/tao-generate-anomalies/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip tao-generate-anomalies.zip -d .agents/skills
Documentation by NVIDIA Corporation
TAO Generate Anomalies
Standalone install? If this session was not initialized by the TAO skill bank plugin, run the
tao-setupskill first (host preflight, credentials, cross-skill discovery).
Multi-phase pipeline (0–7); the mode flag selects which phases run.
| Phase | What runs | Mode(s) |
|---|---|---|
| 0 | Verify / download pretrained checkpoints | all |
| 1 | Fine-tune on dataset_dir | full, finetune_only |
| 2 | Prepare inference JSONL (AMP routing) | full, inference_only |
| 3 | SDG — generate synthetic anomaly images → original/ | full, inference_only |
| 4 | Eval original/ — emit per_sample.csv + eval.log, merge nn_score into SDG_result.csv | full, inference_only |
| 5 | Per-sample (guidance, crop_ratio) search rounds → rounds/round_NN/ (each round runs SDG + eval) | full, inference_only |
| 6 | Assemble best-of-rounds into searched/ (stitch only), plus rounds/search_summary.csv | full, inference_only |
| 7 | Filter searched/ by nn_threshold (default 0.4), regen dropped samples, then canonical bucket eval → searched/{per_sample.csv, eval.log} | full, inference_only |
Run every phase through to completion without mid-run pauses. Collect all required parameters up front, and run every command from the repo root.
Shell setup. All ${ANOMALYGEN_SCRIPTS} references resolve to the packaged
helper-script directory. Inside the container this is preset (ENV ANOMALYGEN_SCRIPTS=<dir>/scripts/utilities); on the host, export it once per
shell:
export ANOMALYGEN_SCRIPTS="$(git rev-parse --show-toplevel)/scripts/utilities"python3 -m scripts.utilities.<name> invocations work from any CWD inside the
container (PYTHONPATH is preset) and from the repo root on the host. When inside
a product container (ANOMALYGEN_PRODUCT_MODE=1), invoke anomalygen-guard
before any GPU work; if it reports BLOCKED, fix the listed issues before
continuing.
Quick Start
The pipeline runs inside the metropolis_sdg.paidf_anomalygen container
(declared in versions.yaml) or any host with the cosmos-predict2 conda env
active. All phase commands assume that environment, at the repo root, with
ANOMALYGEN_SCRIPTS exported.
Minimal end-to-end run (mode=full):
# 1. Set the shared variables (see "Shared variables" for the full set).
export ANOMALYGEN_SCRIPTS="$(git rev-parse --show-toplevel)/scripts/utilities"
MODE=full
NAME=my_exp
DATASET_DIR=/data/uc1
DEFECT_DESC=assets/defect_spec_template.jsonl
NUM_SDG=20
MODEL_SIZE=2b
# 2. Phase 0 — verify / download checkpoints (~40 GB for the 2B default; needs HF_TOKEN).
${ANOMALYGEN_SCRIPTS}/check.sh --model-sizes ${MODEL_SIZE^^} \
|| ${ANOMALYGEN_SCRIPTS}/download_checkpoints.sh --model-sizes ${MODEL_SIZE^^}
# 3. Walk Phases 1→7 in order (see each Phase section).For mode=inference_only (reuse a checkpoint) also set CKPT/STEP and skip
Phase 1. For mode=finetune_only run only Phases 0–1.
Running in Docker — container launch, mounts & permissions
The paidf-anomalygen image runs as a non-root baked-in user (USER anomalygen, uid=10000), independent of your host uid. Docker does not remap
uids on bind mounts, so a host directory owned by your uid is not writable by
uid 10000 and the container fails the instant it tries to create a file there.
Run with the full host identity: --user "$(id -u):$(id -g)", matching
USER/LOGNAME, HOME=/tmp, and read-only /etc/passwd+/etc/group
mounts, plus the cache redirects. Run the fail-fast write preflight before
Phase 0. See references/docker.md for the full docker run command, the
load-bearing-flag table, the preflight snippet, and the uid-10000
chown/chmod fallback.
Reference files — read before executing phases
Read references/finetune.md before Phase 0/1 and references/inference.md
before any of Phases 2–7; for mode=full read both before starting. The
remaining references below are on-demand — read when troubleshooting or needing
full detail for a specific phase.
| File | Read when |
|---|---|
references/finetune.md | Before Phase 0/1: env check, checkpoint download, dataset validation, config generation, training commands, best-checkpoint selection |
references/finetune-commands.md | Exact Phase 1 Step 1–4 commands and CKPT/STEP derivation |
references/inference-commands.md | Exact Phase 5 run_round.sh and Phase 7 filter_with_regen commands |
references/inference.md | Before Phases 2–7: AMP routing, JSONL validation, SDG flags, eval interpretation, search loop, filtering |
references/setup.md | Checkpoint download fails; first-time setup; HF_TOKEN / disk issues |
references/datasets.md | User needs to prepare or obtain a UC1 / UC2 / UC3 dataset; dataset_dir doesn't exist yet |
references/prep-testcase.md | AMP fails; need full param table, helper script descriptions, allocation invariant |
references/sdg-inference.md | NCCL hang; checkpoint validation error; multi-GPU VRAM question; full step list |
references/eval.md | Unexpected scores; FID column order confusion; eval output format reference |
references/sdg-refine.md | draws.json alignment; re-AMP heuristics; search output layout |
references/guard-and-custom-counts.md | Full guard preflight command; --per-defect-counts example |
references/docker.md | Container launch command, mount-permission flags, write preflight, uid-10000 fallback |
references/output-layout.md | Full results/<name>/ directory tree with per-file annotations; post-run Verification checklist |
references/error-handling.md | Pipeline-level failure modes: missing mask dirs, short/empty AMP, mid-round resume, off-boundary step |
Required parameters
num_SDG allocation depends on prep_testcase.sh --mode: inference (default,
Phase 2) is uniform across defect types, override per-defect via
--per-defect-counts; validation (Phase 1's validation JSONL) is proportional
to training mask counts (largest-remainder rounding) and enforces ≥1 per defect.
See references/prep-testcase.md for the full mode table.
| Parameter | Description |
|---|---|
mode | full (Phase 0→7), inference_only (skip Phase 1), or finetune_only (Phase 0→1 only). |
name | Experiment label. |
dataset_dir | Training/reference dataset root. Drives mask-count allocation, AMP submask templates, and holds semantic_segmentation_labels.json for cad defects. |
defect_spec | JSONL tagging each defect spatial_dependency as free/text/cad. text entries need roi_prompt_defect_location. Template: assets/defect_spec_template.jsonl. |
num_SDG | Total output samples per bucket. (Ignored when mode=finetune_only.) |
Conditionally required
| Parameter | Required when | Description |
|---|---|---|
checkpoint_dir / step | mode=inference_only | Pre-existing fine-tuned model. In mode=full these are auto-derived after Phase 1; passing them is an error. In mode=finetune_only silently ignored — Phase 1 always trains from scratch (no resume-from-checkpoint support). Both must be present together — supplying only one is an error. |
Optional parameters
| Parameter | Default | Description |
|---|---|---|
clean_dir | dataset_dir | Clean images. Set only when they live outside the training dataset. Forwarded as --clean-dir to prep-testcase and --clean-image-path to finetune. |
validation_jsonl | auto-generated | Pre-built validation JSONL for Phase 1. When supplied, preflight verifies every defect_spec type appears and paths exist. |
num_search_run | 3 | Per-sample search budget for Phase 5. 0 skips search (only original/). (Ignored when mode=finetune_only.) |
nn_threshold | 0.4 | nn_score cutoff for Phase 7 (DINOv2 correspondence to real defects — key KPI). Samples below are regenerated; final searched/ always has num_SDG. 0 disables filtering. |
max_iter | 75000 | Phase 1 only. Total fine-tune iterations. |
save_iter | 5000 | Phase 1 only. Checkpoint save interval. |
validation_iter | 5000 | Phase 1 only. Validation (nn_score) logging interval. |
num_gpus | 1 | Forwarded to Phase 1 (finetune) and Phase 3 (SDG). Eval and search rounds stay single-GPU. |
model_size | 2b | 2b or 14b. Used by finetune and SDG. On-disk checkpoint path encodes in upper-case (2b→2B, 14b→14B). |
lr | 0.02 | Phase 1 only. Learning rate. |
batch_size | 2 | Phase 1 only. Per-GPU batch size. |
image_size | 512 | Phase 1 only. Training resolution (square). |
guidance_range | 1.5 10.0 | Phase 5 search draw range for guidance. |
crop_ratio_range | 1.5 10.0 | Phase 5 search draw range for crop_ratio. |
Mode validation (fail fast before any phase)
modeunset → halt: "modeis required (full|inference_only|finetune_only)."mode=inference_onlymissing eithercheckpoint_dirorstep→ halt: "inference_only requires bothcheckpoint_dirandstep."mode=fullwithcheckpoint_dirorstepsupplied → halt: "full mode runs finetune; usemode=inference_onlyto reuse an existing checkpoint."
Shared variables
Set once before Phase 0:
MODE=<full|inference_only|finetune_only>
NAME=<exp>
DATASET_DIR=<dataset_dir>
CLEAN_DIR=${clean_dir:-${DATASET_DIR}}
CKPT=<checkpoint_dir> # required iff MODE=inference_only; auto-derived after Phase 1 when MODE=full
STEP=<iter> # required iff MODE=inference_only; auto-derived after Phase 1 when MODE=full
NUM_SDG=<N>
DEFECT_DESC=<defect_spec.jsonl>
DEFECTS=(T+A T+B) # TEXTURE+TYPE names. For mode=inference_only, derive from ${CKPT}/ag_config.yaml → dataloader_train.dataset.anomaly_types (also printed by validate_checkpoint.py in Phase 0). For mode=full, take from DEFECT_DESC entries. See references/inference.md §Phase 0.
NUM_SEARCH_RUN=${num_search_run:-3}
NN_THRESHOLD=${nn_threshold:-0.4}
MODEL_SIZE=<2b|14b>
NUM_GPUS=${num_gpus:-1}
MAX_ITER=${max_iter:-75000}
SAVE_ITER=${save_iter:-5000}
VALIDATION_ITER=${validation_iter:-5000}
LR=${lr:-0.02}
BATCH_SIZE=${batch_size:-2}
IMAGE_SIZE=${image_size:-512}
VALIDATION_JSONL=${validation_jsonl:-} # optional; set by Phase 1 Step 2 if not user-supplied
BASE=results/${NAME}
JSONL=ag_inference/${NAME}/testcase.jsonl
ORIGINAL=${BASE}/original
SEARCHED=${BASE}/searched
ROUNDS=${BASE}/rounds
REGENS=${BASE}/regensGuard preflight (product mode only)
When ANOMALYGEN_PRODUCT_MODE=1, run
.agents/skills/anomalygen-guard/scripts/preflight.py before any GPU work and
fix any BLOCKED issues. --validation-jsonl is forwarded only when the user
supplied one; for MODE=finetune_only omit --num-sdg if not supplied. See
references/guard-and-custom-counts.md for the full preflight command with all
forwarded flags and the validation-JSONL / allocate_samples.py 0-entry
checks.
Phase 0 — checkpoints
Read references/finetune.md §Phase 0 for HF_TOKEN requirements and what gets
downloaded. Both scripts default to the 2B base + t5-large (~40 GB); pass
--model-sizes ${MODEL_SIZE^^} so the chain checks and fetches the base size
this run actually uses (2b→2B, 14b→14B) — otherwise a 14b run
silently passes the 2B-only check and never downloads its checkpoint. Verify
first; download only what is missing.
${ANOMALYGEN_SCRIPTS}/check.sh --model-sizes ${MODEL_SIZE^^} \
|| ${ANOMALYGEN_SCRIPTS}/download_checkpoints.sh --model-sizes ${MODEL_SIZE^^}Phase 1 — fine-tune (skip when MODE=inference_only)
Read references/finetune.md §Phase 1 for dataset structure, config template
details, and best-checkpoint selection. Four steps: (1) validate dataset /
derive anomaly types, (2) generate the validation JSONL (skip if user supplied
VALIDATION_JSONL), (3) generate the training config — show it to the user
and confirm before writing — (4) launch training in the background. Then
derive CKPT (path encodes upper-case MODEL_SIZE) and STEP (highest
nn_score step from validation logs). If MODE=finetune_only, stop after
training. See references/finetune-commands.md for the exact Step 1–4 commands
and the CKPT/STEP derivation snippet.
Phase 2 — prep-testcase (skip when MODE=finetune_only)
Read references/inference.md §Phase 2 for AMP routing detail and n_seeds
sizing. Do NOT pass --seeds — it is auto-computed and is not a recognized
flag. prep_testcase.sh defaults to --mode inference (uniform allocation
across defect types, no KPI floor), which Phase 2 always uses.
${ANOMALYGEN_SCRIPTS}/prep_testcase.sh \
--name ${NAME} --num-sdg ${NUM_SDG} \
--dataset-dir ${DATASET_DIR} \
--clean-dir ${CLEAN_DIR} \
--defect-spec ${DEFECT_DESC} \
--amp-output-dir ag_inference/${NAME}/amp \
--output-jsonl ${JSONL}Custom per-defect counts: when the user specifies counts per defect type,
translate to --num-sdg plus a --per-defect-counts JSON dict (types absent
from the dict get 0; sum should equal --num-sdg, else the script warns on
stderr and uses the override sum). Confirm the allocation when intent is
ambiguous. See references/guard-and-custom-counts.md for the full
--per-defect-counts command example and the ambiguity-handling detail.
Phase 3 — SDG → original/
Read references/inference.md §Phase 3 for JSONL validation against the
checkpoint, multi-GPU caveats, and output verification.
python3 -m scripts.utilities.validate_checkpoint ${CKPT} --step ${STEP}
python3 -m scripts.utilities.validate_jsonl ${CKPT} ${JSONL}
${ANOMALYGEN_SCRIPTS}/run_sdg.sh \
--checkpoint_dir ${CKPT} --step ${STEP} \
--input_jsonl ${JSONL} --output_dir ${ORIGINAL} \
--model_size ${MODEL_SIZE} --num_gpus ${NUM_GPUS}
${ANOMALYGEN_SCRIPTS}/verify_output.sh ${JSONL} ${ORIGINAL}Phase 4 — eval original/
Read references/inference.md §Eval for score interpretation and feature-count
explanation. run_eval.sh writes per_sample.csv and eval.log inside
original/ and merges nn_score into SDG_result.csv.
${ANOMALYGEN_SCRIPTS}/run_eval.sh \
--real-path ${DATASET_DIR} --generated-path ${ORIGINAL} \
--anomaly-types ${DEFECTS[@]}Phase 5 — per-sample search rounds
Read references/inference.md §Phase 5 for draw strategy, ranges, and re-AMP
guidance. For r in 1..NUM_SEARCH_RUN:
Read prior round's
per_sample.csv(or${ORIGINAL}/per_sample.csvforr=1).Write
${ROUNDS}/round_${r}/draws.jsonwith selected(guidance, crop_ratio)per sample.Run round via
${ANOMALYGEN_SCRIPTS}/run_round.sh(SDG + eval; the round dir gets its ownsdg/{SDG_result.csv, per_sample.csv, eval.log}). Seereferences/inference-commands.md §Phase 5for the full command and flags.
NUM_SEARCH_RUN=0 is valid — skip this phase entirely and let Phase 6
clone original/ into searched/.
Phase 6 — assemble searched/ (stitch only)
Always run assemble (works with 0 rounds — searched/ clones original/, so
downstream always reads searched/ regardless of num_search_run). Stitch-only:
copies winning images per sample-index into searched/ and carries over
per-sample nn_score / mnn_score from each pick's source-round per_sample.csv.
No eval — Phase 7 emits the canonical searched/eval.log.
mkdir -p ${ROUNDS}
python3 -m scripts.utilities.assemble_searched \
--original-dir ${ORIGINAL} --original-csv ${ORIGINAL}/per_sample.csv \
--rounds-dir ${ROUNDS} --searched-dir ${SEARCHED}Phase 7 — filter + regen + eval (default nn_threshold=0.4)
Phase 7 runs by default (nn_threshold=0.4) on every mode=full and
mode=inference_only invocation; pass nn_threshold=0 to skip it. It filters
searched/ by nn_threshold, regenerates dropped samples via re-AMP (fresh
(clean, submask) pairing in the same defect type) for up to 5 attempts, then
falls back to best-scoring non-passing regens and finally to dropped originals,
so the final bucket always equals num_SDG.
Run python3 -m scripts.utilities.filter_with_regen. Pass --allocation ag_inference/${NAME}/allocation.json so regen targets the intended
per-defect counts — without it a bucket left short (e.g. by an interrupted SDG)
cannot be topped back up to num_SDG. It runs the final run_eval.sh
internally — the only eval against searched/. Read
references/inference.md §Phase 7 for regen mechanics, source-column tracing,
and the regens/regen_summary.csv schema; see
references/inference-commands.md §Phase 7 for the full command and flags.
Output layout
Every bucket that gets eval'd carries the same triad of files:
SDG_result.csv (generation params + nn_score + guardrail_pass),
per_sample.csv (per-sample nn + mnn), and eval.log (aggregate FID /
per-defect avg). Buckets live under results/<name>/ as original/ (Phase
3+4), searched/ (Phase 6 stitch + Phase 7 filter+regen+eval),
rounds/round_NN/ (Phase 5, plus search_summary.csv), and regens/regen_NN/
(Phase 7, plus regen_summary.csv).
Image content guardrail. A SigLIP content-safety check runs on every
generated image and records its verdict in SDG_result.csv.guardrail_pass
(1 safe / 0 blocked). A blocked image is replaced with an all-black
image that still occupies its slot on disk, so counting files cannot detect
it — never hand a guardrail_pass=0 sample downstream. Blacked-out samples
score near-zero nn_score, so Phase 7 regenerates them like any other
sub-threshold sample. Disable with ANOMALYGEN_IMAGE_GUARDRAIL=0.
See references/output-layout.md for the full directory tree with per-file
annotations, the guardrail semantics, and the post-run Verification
checklist (image counts per bucket, search_summary.csv /
regen_summary.csv row checks, the per-type nn_score / mnn_score / fid
fields in each eval.log, and the guardrail_pass sweep).
Error handling
Common pipeline failure modes (missing mask dirs, short/empty AMP output and
the 0 entries written halt, mid-round SDG failure resume, off-boundary
step, validate_dataset.py's non-zero exit on any pairing issue, the
2B-only check.sh default, and guardrail-blocked black images) are covered in
references/error-handling.md; see also references/finetune.md and
references/inference.md for phase-specific error handling.
Files
- ATTRIBUTION.mdAdded by AutoGineer936 B
- BENCHMARK.md7.8 KiB
- LICENSELicence file added by AutoGineer11.2 KiB
- SKILL.md19.1 KiB
- assets/ag_config.yaml1.4 KiB
- assets/defect_spec_template.jsonl462 B
- config/skillspector-baseline.yaml1.7 KiB
- evals/evals.json789 B
- references/datasets.md4.8 KiB
- references/docker.md3.5 KiB
- references/error-handling.md1.9 KiB
- references/eval.md7.4 KiB
- references/finetune-commands.md2.3 KiB
- references/finetune.md8.2 KiB
- references/guard-and-custom-counts.md2.0 KiB
- references/inference-commands.md1.4 KiB
- references/inference.md14.9 KiB
- references/output-layout.md3.8 KiB
- references/prep-testcase.md7.6 KiB
- references/sdg-inference.md4.8 KiB
- references/sdg-refine.md4.9 KiB
- references/setup.md4.0 KiB
- skill-card.md5.5 KiB
- skill.oms.sig8.5 KiB
references/inference.md
Inference Reference — Phases 2–7
Detail for anomalygen Phases 2–7. Read before executing any inference phase.
Operational summaries for each phase — deep detail is in the per-phase reference files
(prep-testcase.md, sdg-inference.md, eval.md, sdg-refine.md).
When updating, keep those files in sync.
Contents
Pre-flight
Phase 2: AMP Routing and JSONL Preparation
Phase 3: SDG Inference
Eval (Phases 4 and 6)
Phase 5: Per-Sample Search
Phase 6: Assemble (stitch only)
Phase 7: Filter + Regen + Eval
Pre-flight: Environment and Checkpoint Validation
Environment check
nvidia-smi --query-gpu=name,memory.total --format=csv,noheader
python -c "import torch; print(f'torch={torch.__version__}, CUDA={torch.cuda.is_available()}, devices={torch.cuda.device_count()}')"Stop if CUDA unavailable (conda activate cosmos-predict2); stop if num_gpus
exceeds detected count. If compilation errors: export CUDA_HOME=$CONDA_PREFIX.
Checkpoint validation (run before Phase 3)
validate_checkpoint.py ensures ag_config.yaml exists and is well-formed.
It prints the supported TEXTURE+ANOMALY set — use this as DEFECTS if not
already set from defect_spec.
# What validate_checkpoint.py reads:
cfg["dataloader_train"]["dataset"]["anomaly_types"]
# Each entry is [texture, anomaly] → rendered as "texture+anomaly"Stop if ag_config.yaml is missing — ask the user to verify checkpoint_dir.
Phase 2: AMP Routing and JSONL Preparation
Read references/prep-testcase.md for the full parameter table, helper script
descriptions, pipeline invariant, two calling conventions (validation vs
inference), and AMP error diagnosis.
AMP routing table
spatial_dependency | AMP branch | Extra inputs required |
|---|---|---|
free | Whole-image ROI | none |
text | text2roi (Qwen VL + SAM2) | roi_prompt_defect_location in defect_spec |
cad | cad2roi | <dataset>/<TEXTURE>/cad_mask/<stem>.png + <dataset>/semantic_segmentation_labels.json |
Unrecognized values default to text. Auto-cad routing fires whenever a
record has a non-null cad_mask regardless of spatial_dependency.
Clean image discovery
prep_testcase.sh probes these layouts in order:
<clean_dir>/<TEXTURE>/clean_image/*<clean_dir>/<TEXTURE>/*flat
<clean_dir>/*
When clean images live at <dataset_dir>/<TEXTURE>/clean_image/, omit
--clean-dir (defaults to dataset_dir).
Pairing strategy
Budget per defect = num_submasks[d] × num_cleans[texture] (every submask
× clean combination). Pairing iterates every combination once (deterministic
shuffle per defect) before any pair repeats.
JSONL defaults: guidance=7.0, crop_ratio=2.0, seed=42 — overridden
by Phase 5 search.
n_seeds sizing
Auto-computed from allocation — do NOT pass --seeds:
n_seeds = max_d ⌈allocation[d] / (num_submasks[d] × num_cleans[texture])⌉n_seeds > 1 only when allocation exceeds the pair budget.
Submask handling
AMP's preprocess_submask defaults submask_split_largest=True — a
multi-component training submask is reduced to its largest connected component.
Accept this default unless preserving multiple components is required for a
specific defect. To override, set submask_split_largest: false on the
individual record in amp_samples.json before build_jsonl.py runs.
Verification after prep_testcase.sh
JSONL has
num_SDGentries (minus logged AMP skips).allocation.jsonsums tonum_SDG.amp_samples.jsonhas exactlynum_SDGrecords.
Phase 2 errors
Validator failure → stop with itemised report (missing submask/clean/cad/prompt).
AMP output count < allocation →
build_jsonl.pyerrors (checkrun_auto_roi_amp.pylogs for NO_DETECTION / FAILED).Mask-size mismatch →
verify_jsonl.pyauto-resizes intoresized_masks/cache.
Phase 3: SDG Inference
Read references/sdg-inference.md for the full parameter table, step-by-step
detail, multi-GPU VRAM constraints, and verification checklist.
JSONL validation against checkpoint
validate_jsonl.py cross-checks anomaly types in the JSONL against the
checkpoint's supported set. If any are unsupported, stop and show the mismatch:
Supported: TEXTURE+TYPE_A, TEXTURE+TYPE_B
JSONL has: TEXTURE+TYPE_C ← unsupportedOptions: retrain with extended defect set, use an isolated model for the new
defect, or trim defect_spec to supported types.
If many image/mask paths are missing → stop; ask the user to verify paths before burning GPU time.
run_sdg.sh flags
${ANOMALYGEN_SCRIPTS}/run_sdg.sh \
--checkpoint_dir <CKPT> \
--step <STEP> \
--input_jsonl <JSONL> \
--output_dir <OUTPUT_DIR> \
--model_size <2b|14b> \
[--num_gpus N] \
[--seed 0]Multi-GPU caveats
| Config | Behavior |
|---|---|
14B + num_gpus > 1 | FSDP auto-disabled at inference; each rank holds the full 14B model (~80 GB VRAM per GPU) |
| 14B + single GPU | FSDP enabled; fits on smaller GPUs |
| 2B + any num_gpus | Standard DDP; no special VRAM constraint |
NCCL hang controls (set as env vars before run_sdg.sh if needed):
| Var | Default | Effect |
|---|---|---|
ANOMALY_GEN_FINALIZE_BACKEND | gloo | Set to nccl only if gloo finalization hangs |
TORCH_DISTRIBUTED_TIMEOUT_SEC | 1800 | Process-group timeout in seconds |
TORCH_NCCL_HEARTBEAT_TIMEOUT_SEC | (alias) | Same effect as TORCH_DISTRIBUTED_TIMEOUT_SEC; set whichever is defined in your environment |
MASTER_PORT | 12341 | Override if port is occupied |
SDG output structure
<output_dir>/
├── reconstructed_image/ # final synthetic anomaly images
├── annotated_image/
├── cropped_image/
├── cropped_mask/
├── original_image/
├── original_mask/
└── SDG_result.csvVerify before eval
${ANOMALYGEN_SCRIPTS}/verify_output.sh ${JSONL} ${OUTPUT_DIR}Counts in reconstructed_image/ must match JSONL entry count. If SDG was
interrupted, do NOT eval the partial output — re-run SDG first.
Phase 3 errors
Missing
ag_config.yaml→ stop; verifycheckpoint_dir.stepnot on asave_iterboundary →torch.loadFileNotFoundError; runls ${CKPT}/checkpoints/model/iter_*.ptand pick a valid step.OOM → reduce
num_gpusor switch tomodel_size=2b.Non-binary masks → pipeline raises
ValueError; masks must be 0 or 255 only.Port conflict → increment
MASTER_PORT.
Eval (run_eval.sh — used by Phase 4, search rounds, and Phase 7)
Read references/eval.md for the full parameter reference, example output
table (FID column comes first — surprises people), detailed pre-flight, and
all error cases. Summary of the essentials below.
run_eval.sh flags
${ANOMALYGEN_SCRIPTS}/run_eval.sh \
--real-path <real_path> \
--generated-path <generated_path> \
--anomaly-types <TEXTURE+TYPE> [<TEXTURE+TYPE> ...] \
[--per-sample-csv <path>] \
[--backbone cradio_v3_base]per_sample.csv is always emitted to <generated_path>/per_sample.csv by
default. Columns: anomaly_type, path, nn_score, mnn_score.
sdg-refine and filter_with_regen.py both consume this file.
Score interpretation
nn_score (DINOv2 nearest-neighbour, locked to dinov2-large) is the key
KPI — higher is better. It is relative: compare within the same dataset
and backbone, not across datasets. Scoring uses top-K=3 averaging
(compute_correspondence_kpi): each generated image is scored against every
real reference of its type and the three highest-NN values are averaged.
mnn_score (mutual nearest-neighbour) is a diagnostic for refinement internals.
fid (using backbone flag, default cradio_v3_base) is a secondary diagnostic.
If a type has < 2 real or generated samples → FID is skipped for that type
and logged as None; nn_score/mnn_score still run.
If a type has < 10 real images → warn that nn_score may be noisy. Interpret
results cautiously and consider acquiring more training data.
Feature counts
The eval log reports feature counts that may exceed image count. This is
expected: mask_crop_images extracts individual defect instances via DBSCAN
clustering (eps=30) on mask contours. A single image with a multi-region mask
produces multiple feature crops.
Eval pre-flight
Check before running:
reconstructed_image/exists and is non-empty.No SDG process is still writing to the output directory.
Auto-detect anomaly types from generated filenames if not supplied:
ls <generated_path>/reconstructed_image/ | sed 's/_[0-9]*\.\(png\|jpg\)$//' | sort -u
Eval errors
Empty
reconstructed_image/→ SDG may not have finished; stop.Anomaly type not found in real data → 0 real features; check
<TEXTURE>/anomaly_image/<TYPE>/structure.SDG still running → wait for completion; partial output produces incorrect scores.
Phase 5: Per-Sample Search
Read references/sdg-refine.md for the full inputs table, draws.json
alignment detail (SDG_result.csv index column), output layout, and
re-AMP heuristics.
Agent draw strategy
For each round r:
Read
per_sample.csvfrom roundr-1(ororiginal/per_sample.csvforr=1).For each sample, pick new
(guidance, crop_ratio). Focus search budget on low-scoring samples. Skip samples already scoring well to save inference time.Write
draws.jsonto${ROUNDS}/round_${r}/draws.json.
draws.json format
{
"<sample_index>": {"guidance": 4.2, "crop_ratio": 2.7},
"<sample_index>": {"guidance": 8.1, "crop_ratio": 1.8}
}Sample indices are 0-based JSONL line numbers.
Omitted samples are not retried this round (stay at current best-seen).
Ranges to consider:
guidance ∈ [1.5, 10.0],crop_ratio ∈ [1.5, 10.0]. Narrow or widen based on prior-round feedback.
run_round.sh produces rounds/round_r/testcase.jsonl, sdg/, and
per_sample.csv. The sdg/SDG_result.csv.index column carries the
base-JSONL sample_index, keeping assemble_searched.py aligned across rounds.
Re-rolling AMP (optional)
By default the (clean_image, mask) pair per sample index is fixed across all
rounds — only (guidance, crop_ratio) change. If nn_score stays flat for a
sample across ≥ 2 rounds of (g, c) variation, the mask placement itself may
be the blocker. Add --reamp-seed to re-run AMP
with a fresh base seed on the same (clean, submask) pairs:
${ANOMALYGEN_SCRIPTS}/run_round.sh \
...standard args... \
--reamp-seed $((1000 + r)) \
--defect-spec ${DEFECT_DESC}Use sparingly — each re-AMP is one extra run_auto_roi_amp.py pass. The
text2roi ROI cache is reused, so Qwen VL + SAM2 are not re-run.
num_search_run = 0
Valid. Skip run_round.sh entirely; Phase 6 (assemble_searched.py) still
runs with an empty rounds dir and clones original/ into searched/,
preserving the downstream invariant that the final SDG bucket is always
searched/.
search_summary.csv
After Phase 6 assemble: rounds_dir/search_summary.csv has one row per
sample with best_round, best_guidance, best_crop_ratio, best_nn_score,
attempts.
Phase 6: Assemble searched/ (stitch only)
Runs assemble_searched.py to pick best-of-rounds (or clone original/
when num_search_run=0) into searched/. No eval runs here. The
script copies each picked sample's images into searched/ and stitches
searched/per_sample.csv by carrying over per-sample nn_score and
mnn_score from the picked sample's source round per_sample.csv —
correspondence-to-real-set scoring is per-sample-independent (one
generated image against the real set, no sibling-generation coupling),
so the stitched values are exact, not approximate. The same nn_score
gets merged into searched/SDG_result.csv. Phase 7 owns the canonical
post-pipeline eval against the final regen-aware bucket.
Phase 7: Filter + Regen + Eval
Runs by default (nn_threshold=0.4). Set nn_threshold=0 to disable.
Updates searched/ in place — downstream always reads searched/
as the final SDG bucket regardless of whether Phase 7 ran or whether
num_search_run was 0.
filter_with_regen.py orchestrates a re-AMP + re-pair regen flow:
Initial filter — partition source bucket into
passing_per_defectanddropped_per_defect. Target allocation (per-defect count) comes from--allocation(the pipeline'sallocation.json) when given, else from the source bucket. Samples with nonn_scoreinper_sample.csvare excluded from keep/fallback (logged; regen replaces them).Regen loop — for each attempt up to 5:
Compute
needed_per_defect = target_alloc - kept_per_defect. If zero everywhere, stop.Write a subset
allocation.jsonfor the still-needed defects.Run
build_amp_samples.py --seed=attempt_seed— per-defect(clean, submask)lists get shuffled before pairing, so each attempt's pairings are distinct.Run
run_auto_roi_amp.py --seed=attempt_seed(new placement).Run
build_jsonl.py, then overwrite each row'sseedfield withattempt_seed(so diffusion noise viaAnomalyInpaintCondition.seed→misc.arch_invariant_randalso varies).Run SDG into
regens/regen_NN/sdg/, eval.Admit new samples scoring ≥
thresholdintoadmitted_regens_by_defect, greedy by nn descending up to the defect's quota.
Per-defect fallback fill — if a defect is still short of its target, top up with the best non-admitted regens for that defect, then with the highest-scoring dropped originals (last resort).
Atomic-ish in-place swap — stage to
searched.staging/, rename oversearched/. Atomic on same filesystem.
regens/ (sibling of rounds/) holds Phase 7 artifacts:
regens/regen_NN/{allocation.json, amp_samples.json, amp/, testcase.jsonl, sdg/} per attempt, plus regens/regen_summary.csv with columns
sample_index (-1 for regen), source, clean_image, mask_filename,
prev_nn, nn_score, passed_threshold, output_filename.
For at-a-glance tracing, searched/SDG_result.csv carries a source
column:
original— survived Phase 5 assemble straight from Phase 3 SDGround_<N>— Phase 5 search roundNproduced this sample's best attemptregen_<k>— Phase 7 regen attemptkproduced this sample
searched/SDG_result.csv alone suffices for most tracing (it has
image_filename, mask_filename, nn_score, source). regen_summary.csv
adds prev_nn and passed_threshold for deeper audit.
Per-attempt eval is invoked with only the anomaly types present in that attempt's subset, suppressing harmless "No generated defect patches" warnings for types absent from the subset.
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: Requires docker + nvidia-container-toolkit and a CUDA GPU. Pulls the `metropolis_sdg.paidf_anomalygen` image declared in `versions.yaml` at the skill bank root.
Security
What our scanner noticed· Medium risk
- Large base64-encoded blobs
- References to credential files
- Instructions that use the network
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-generate-anomalies · 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-generate-anomalies.zip