/dali-dynamic-mode
DALI imperative dynamic mode (`nvidia.dali.experimental.dynamic`, ndd): use when working on ndd code or migrating pipelines; skip pipeline-only tasks.
Claude Code
$ npx -y skills add NVIDIA/skills --skill dali-dynamic-mode --agent claude-code
Codex
$ npx skills add NVIDIA/skills --skill dali-dynamic-mode -a codex -y
Cursor
$ npx skills add NVIDIA/skills --skill dali-dynamic-mode -a cursor -y
Gemini CLI
$ npx skills add NVIDIA/skills --skill dali-dynamic-mode -a gemini-cli -y
GitHub Copilot
$ npx skills add NVIDIA/skills --skill dali-dynamic-mode -a github-copilot -y
OpenCode
$ npx skills add NVIDIA/skills --skill dali-dynamic-mode -a opencode -y
Windsurf
$ npx skills add NVIDIA/skills --skill dali-dynamic-mode -a windsurf -y
SKILL.md
dali-dynamic-mode.SKILL.mdname: dali-dynamic-mode
description: "DALI imperative dynamic mode (`nvidia.dali.experimental.dynamic`, ndd): use when working on ndd code or migrating pipelines; skip pipeline-only tasks."
license: Apache-2.0
metadata:
author: "DALI Team <dali-team@nvidia.com>"
tags:
- dali
- dynamic-mode
- ndd
- data-loading
- data-processing
- gpu-processing
languages:
- python
team: dali
domain: deep-learningDALI Dynamic Mode
Purpose
Guide AI agents in writing, reviewing, and migrating code that uses DALI's imperative dynamic-mode API, `nvidia.dali.experimental.dynamic` (`ndd`).
Instructions
- Import dynamic mode as `nvidia.dali.experimental.dynamic as ndd` and write code as direct `ndd` calls in ordinary Python; do not use pipeline-mode APIs such as `Pipeline`, `@pipeline_def`, `pipe.build()`, or `pipe.run()`.
- Treat readers as stateful: create them once, reuse them across epochs, and pass `batch_size` to `next_epoch(...)`.
- Pass explicit `batch_size` to random ops; there is no pipeline-level batch size to inherit.
- Use dynamic-mode API conventions: `device="gpu"` instead of pipeline-mode `"mixed"`, `Batch.tensors[...]` for sample selection, and `Batch.slice[...]` for per-sample slicing.
- Use `.torch()` to convert a tensor or batch to a PyTorch tensor. Use `pad=True` for batches with variable shapes.
Prerequisites
- To run or validate code, NVIDIA DALI must be installed with dynamic mode importable as `nvidia.dali.experimental.dynamic`.
- GPU decode or GPU operators require a CUDA-capable DALI build and an available NVIDIA GPU/driver.
- Framework conversion examples require the target framework installed, such as PyTorch for `.torch()`.
Introduction
Dynamic mode is DALI's imperative Python API. It lets code call DALI operators directly from normal Python control flow instead of building and running a pipeline graph.
Core Data Types
Tensor -- single sample
t = ndd.tensor(data) # copy
t = ndd.as_tensor(data) # wrap, no copy if possible
t.cpu() # move to CPU
t.gpu() # move to GPU
t.torch(copy=False) # conversion to PyTorch tensor with no copy (default)
t[1:3] # slicing supported
np.asarray(t) # NumPy via __array__ (CPU only)
Supports `__dlpack__`, `__cuda_array_interface__`, `__array__`, arithmetic operators.
Batch -- collection of samples (variable shapes OK)
b = ndd.batch([arr1, arr2]) # copy
b = ndd.as_batch(data) # wrap, no copy if possible
**Batch has no `__getitem__`** -- `batch[i]` raises `TypeError` because indexing is ambiguous (sample selection vs. per-sample slicing). Use the explicit APIs instead:
| Intent | Method | Returns | |--------|--------|---------| | Get sample i | `batch.tensors[i]` | `Tensor` | | Get subset of samples | `batch.tensors[slice_or_list]` | `Batch` | | Slice within each sample | `batch.slice[...]` | `Batch` (same batch_size) | | Sample-wise slicing | `batch.slice[batch_of_indices]` | `Batch` (same batch_size) |
`.tensors[]` picks **which samples**. `.slice` indexes **inside each sample**.
xy = ndd.random.uniform(batch_size=16, range=[0, 1], shape=2)
crop_x = xy.slice[0] # Batch of 16 scalars, first element from each sample
crop_y = xy.slice[1] # Batch of 16 scalars, second element from each sample
sample_0 = xy.tensors[0] # Tensor, the entire first sample [x, y]
Advanced slicing
The `.slice[]` API accepts batches of indices, allowing the user to mix and match batches and scalar values, e.g.:
imgs = ndd.imread(filenames) # a batch of images, if `filenames` is a list
sliced = imgs.slice[
42 : # the range start is broadcast to all samples
ndd.batch(imgs.shape).slice[0] // 2 # per-sample range stop (half of each image)
]**PyTorch conversion:**
- `batch.torch()` -- works for uniform shapes; raises for ragged batches
- `batch.torch(pad=True)` -- zero-pads ragged batches to max shape (use for variable-length audio, detection boxes, etc.)
- `batch.torch(copy=None)` is the default (avoids copy if possible)
- Batch has **no `__dlpack__`** -- use `ndd.as_tensor(batch)` first for DLPack consumers. `ndd.as_tensor` supports `pad` as well.
- `Tensor.torch(copy=False)` is default (no copy)
**Iteration:** `for sample in batch:` yields Tensors.
Readers
Readers are **stateful objects** -- create once, reuse across epochs. This matters because readers track internal state like shuffle order and shard position.
reader = ndd.readers.File(file_root=image_dir, random_shuffle=True)
for epoch in range(num_epochs):
for jpegs, labels in reader.next_epoch(batch_size=64):
# jpegs, labels are Batch objects
...Key points:
- Reader outputs (jpegs, labels, etc.) are **CPU** tensors/batches. Labels typically stay on CPU until you convert them for your framework (e.g. `labels.torch().to(device)`).
- Reader classes are **PascalCase**: `ndd.readers.File(...)`, `ndd.readers.COCO(...)`, `ndd.readers.TFRecord(...)`
- `batch_size` goes to `next_epoch()`, not to the reader constructor
- `next_epoch(batch_size=N)` yields tuples of `Batch`; `next_epoch()` without batch_size yields tuples of `Tensor`
- The iterator from `next_epoch()` must be fully consumed before calling `next_epoch()` again
- Once a reader is used with a given batch_size, it cannot be changed. Similarly, a reader used in batch mode cannot switch to sample mode or vice versa.
Sharded reading for distributed training:
reader = ndd.readers.File(
file_root=image_dir,
shard_id=rank, num_shards=world_size,
stick_to_shard=True,
pad_last_batch=True,
)Device Handling
- Device is **inferred from inputs** -- GPU if any input is on GPU
- For hybrid decode: use `device="gpu"` (NOT `"mixed"`). The `"mixed"` keyword is a pipeline-mode concept for implicit CPU-to-GPU transfer;
Read more
name: dali-dynamic-mode
description: "DALI imperative dynamic mode (`nvidia.dali.experimental.dynamic`, ndd): use when working on ndd code or migrating pipelines; skip pipeline-only tasks."
license: Apache-2.0
metadata:
author: "DALI Team <dali-team@nvidia.com>"
tags:
- dali
- dynamic-mode
- ndd
- data-loading
- data-processing
- gpu-processing
languages:
- python
team: dali
domain: deep-learningDALI Dynamic Mode
Purpose
Guide AI agents in writing, reviewing, and migrating code that uses DALI's imperative dynamic-mode API, `nvidia.dali.experimental.dynamic` (`ndd`).
Instructions
- Import dynamic mode as `nvidia.dali.experimental.dynamic as ndd` and write code as direct `ndd` calls in ordinary Python; do not use pipeline-mode APIs such as `Pipeline`, `@pipeline_def`, `pipe.build()`, or `pipe.run()`.
- Treat readers as stateful: create them once, reuse them across epochs, and pass `batch_size` to `next_epoch(...)`.
- Pass explicit `batch_size` to random ops; there is no pipeline-level batch size to inherit.
- Use dynamic-mode API conventions: `device="gpu"` instead of pipeline-mode `"mixed"`, `Batch.tensors[...]` for sample selection, and `Batch.slice[...]` for per-sample slicing.
- Use `.torch()` to convert a tensor or batch to a PyTorch tensor. Use `pad=True` for batches with variable shapes.
Prerequisites
- To run or validate code, NVIDIA DALI must be installed with dynamic mode importable as `nvidia.dali.experimental.dynamic`.
- GPU decode or GPU operators require a CUDA-capable DALI build and an available NVIDIA GPU/driver.
- Framework conversion examples require the target framework installed, such as PyTorch for `.torch()`.
Introduction
Dynamic mode is DALI's imperative Python API. It lets code call DALI operators directly from normal Python control flow instead of building and running a pipeline graph.
Core Data Types
Tensor -- single sample
t = ndd.tensor(data) # copy t = ndd.as_tensor(data) # wrap, no copy if possible t.cpu() # move to CPU t.gpu() # move to GPU t.torch(copy=False) # conversion to PyTorch tensor with no copy (default) t[1:3] # slicing supported np.asarray(t) # NumPy via __array__ (CPU only)
Supports `__dlpack__`, `__cuda_array_interface__`, `__array__`, arithmetic operators.
Batch -- collection of samples (variable shapes OK)
b = ndd.batch([arr1, arr2]) # copy b = ndd.as_batch(data) # wrap, no copy if possible
**Batch has no `__getitem__`** -- `batch[i]` raises `TypeError` because indexing is ambiguous (sample selection vs. per-sample slicing). Use the explicit APIs instead:
| Intent | Method | Returns | |--------|--------|---------| | Get sample i | `batch.tensors[i]` | `Tensor` | | Get subset of samples | `batch.tensors[slice_or_list]` | `Batch` | | Slice within each sample | `batch.slice[...]` | `Batch` (same batch_size) | | Sample-wise slicing | `batch.slice[batch_of_indices]` | `Batch` (same batch_size) |
`.tensors[]` picks **which samples**. `.slice` indexes **inside each sample**.
xy = ndd.random.uniform(batch_size=16, range=[0, 1], shape=2) crop_x = xy.slice[0] # Batch of 16 scalars, first element from each sample crop_y = xy.slice[1] # Batch of 16 scalars, second element from each sample sample_0 = xy.tensors[0] # Tensor, the entire first sample [x, y]
Advanced slicing
The `.slice[]` API accepts batches of indices, allowing the user to mix and match batches and scalar values, e.g.:
imgs = ndd.imread(filenames) # a batch of images, if `filenames` is a list
sliced = imgs.slice[
42 : # the range start is broadcast to all samples
ndd.batch(imgs.shape).slice[0] // 2 # per-sample range stop (half of each image)
]**PyTorch conversion:**
- `batch.torch()` -- works for uniform shapes; raises for ragged batches
- `batch.torch(pad=True)` -- zero-pads ragged batches to max shape (use for variable-length audio, detection boxes, etc.)
- `batch.torch(copy=None)` is the default (avoids copy if possible)
- Batch has **no `__dlpack__`** -- use `ndd.as_tensor(batch)` first for DLPack consumers. `ndd.as_tensor` supports `pad` as well.
- `Tensor.torch(copy=False)` is default (no copy)
**Iteration:** `for sample in batch:` yields Tensors.
Readers
Readers are **stateful objects** -- create once, reuse across epochs. This matters because readers track internal state like shuffle order and shard position.
reader = ndd.readers.File(file_root=image_dir, random_shuffle=True)
for epoch in range(num_epochs):
for jpegs, labels in reader.next_epoch(batch_size=64):
# jpegs, labels are Batch objects
...Key points:
- Reader outputs (jpegs, labels, etc.) are **CPU** tensors/batches. Labels typically stay on CPU until you convert them for your framework (e.g. `labels.torch().to(device)`).
- Reader classes are **PascalCase**: `ndd.readers.File(...)`, `ndd.readers.COCO(...)`, `ndd.readers.TFRecord(...)`
- `batch_size` goes to `next_epoch()`, not to the reader constructor
- `next_epoch(batch_size=N)` yields tuples of `Batch`; `next_epoch()` without batch_size yields tuples of `Tensor`
- The iterator from `next_epoch()` must be fully consumed before calling `next_epoch()` again
- Once a reader is used with a given batch_size, it cannot be changed. Similarly, a reader used in batch mode cannot switch to sample mode or vice versa.
Sharded reading for distributed training:
reader = ndd.readers.File(
file_root=image_dir,
shard_id=rank, num_shards=world_size,
stick_to_shard=True,
pad_last_batch=True,
)Device Handling
- Device is **inferred from inputs** -- GPU if any input is on GPU
- For hybrid decode: use `device="gpu"` (NOT `"mixed"`). The `"mixed"` keyword is a pipeline-mode concept for implicit CPU-to-GPU transfer;
Official, NVIDIA-verified Agent Skills for Claude Code, Codex, and other coding agents.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about dali-dynamic-mode.
- Which coding agents can use dali-dynamic-mode?
- dali-dynamic-mode installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

