chore: import upstream snapshot with attribution
CICD NeMo / cicd-main-unit-tests (push) Blocked by required conditions
CICD NeMo / cicd-main-speech (push) Blocked by required conditions
CICD NeMo / cicd-test-container-build (push) Blocked by required conditions
CICD NeMo / cicd-import-tests (push) Blocked by required conditions
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Blocked by required conditions
CICD NeMo / Nemo_CICD_Test (push) Blocked by required conditions
CICD NeMo / Coverage (e2e) (push) Blocked by required conditions
CICD NeMo / Coverage (unit-test) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
CICD NeMo / cicd-wait-in-queue (push) Waiting to run
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
CICD NeMo / cicd-main-unit-tests (push) Blocked by required conditions
CICD NeMo / cicd-main-speech (push) Blocked by required conditions
CICD NeMo / cicd-test-container-build (push) Blocked by required conditions
CICD NeMo / cicd-import-tests (push) Blocked by required conditions
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Blocked by required conditions
CICD NeMo / Nemo_CICD_Test (push) Blocked by required conditions
CICD NeMo / Coverage (e2e) (push) Blocked by required conditions
CICD NeMo / Coverage (unit-test) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
CICD NeMo / cicd-wait-in-queue (push) Waiting to run
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "jq -r '.tool_input.file_path' | { read -r f; f=\"${f#\"$(git rev-parse --show-toplevel)/\"}\"; case \"$f\" in *.py) ;; *) exit 0;; esac; case \"$f\" in docs/*|external/*|examples/*) exit 0;; nemo/collections/speechlm2/*) ;; nemo/collections/*) exit 0;; esac; black --quiet \"$f\"; isort --quiet \"$f\"; } 2>/dev/null || true",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
name: babysit-pr
|
||||
description: Get a pull request to green CI. Diagnose and fix CI failures, push fixes, re-trigger CI via the "Run CICD" label, and repeat until all checks pass. Does not post comments — this is a local developer tool.
|
||||
---
|
||||
|
||||
# babysit-pr
|
||||
|
||||
Get a PR's CI to green. Nothing else — no review comments, no PR comments, no status summaries. This skill is run locally by developers in their own sandboxes.
|
||||
|
||||
## Inputs
|
||||
|
||||
The PR number is the primary input. It may come from:
|
||||
- An explicit argument: `/babysit-pr 567`
|
||||
- The conversation context: "check on PR #567"
|
||||
- A GitHub PR URL pasted into the chat
|
||||
|
||||
If no PR number is clear, ask for it before proceeding.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Get the full picture
|
||||
|
||||
```bash
|
||||
gh pr view <PR_NUMBER> --repo NVIDIA-NeMo/NeMo
|
||||
gh pr checks <PR_NUMBER> --repo NVIDIA-NeMo/NeMo
|
||||
gh pr diff <PR_NUMBER> --repo NVIDIA-NeMo/NeMo
|
||||
```
|
||||
|
||||
Determine the current state:
|
||||
|
||||
| State | What to do |
|
||||
|------------------------------|--------------------------------------|
|
||||
| CI failing | Diagnose and fix (Step 2) |
|
||||
| Merge conflicts | Resolve (Step 3) |
|
||||
| Formatting check failing | Wait — do NOT fix (see below) |
|
||||
| CI green | Done, nothing to do |
|
||||
| CI pending | Wait for it to finish, then reassess |
|
||||
|
||||
### Checks to ignore
|
||||
|
||||
The **"Isort and Black Formatting"** workflow (`reformat_with_isort_and_black` job) auto-pushes formatting fixes. If that check is failing or pending:
|
||||
- Do NOT fix formatting yourself — the auto-formatter will push a commit shortly.
|
||||
- Wait for it to complete and for the new commit to land before assessing other failures, since the formatting push changes the HEAD SHA and may re-trigger CI.
|
||||
|
||||
### Step 2 — Fix CI failures
|
||||
|
||||
Check out the PR branch and inspect the failure logs:
|
||||
|
||||
```bash
|
||||
gh pr checkout <PR_NUMBER> --repo NVIDIA-NeMo/NeMo
|
||||
gh run list --repo NVIDIA-NeMo/NeMo --branch <branch-name>
|
||||
gh run view <RUN_ID> --repo NVIDIA-NeMo/NeMo --log-failed
|
||||
```
|
||||
|
||||
Before attempting a fix, check `git log` for recent commits. If you see a previous fix attempt that addressed the same failure and it is still failing, **stop and tell the user** — the issue needs human attention. Do not keep retrying the same fix.
|
||||
|
||||
Otherwise, identify the root cause, fix the code, and push:
|
||||
|
||||
```bash
|
||||
git add <changed files>
|
||||
git commit -s -m "<brief summary of fix>"
|
||||
git push
|
||||
```
|
||||
|
||||
### Step 3 — Re-trigger CI
|
||||
|
||||
After pushing a fix, add the "Run CICD" label to re-trigger the CI pipeline:
|
||||
|
||||
```bash
|
||||
gh pr edit <PR_NUMBER> --repo NVIDIA-NeMo/NeMo --add-label "Run CICD"
|
||||
```
|
||||
|
||||
The "CICD NeMo" workflow is triggered by this label and removes it automatically when done.
|
||||
|
||||
Then wait for CI to complete and reassess. Go back to Step 1.
|
||||
|
||||
### Step 4 — Resolve merge conflicts
|
||||
|
||||
If the PR branch has fallen behind main and has conflicts:
|
||||
|
||||
```bash
|
||||
git fetch origin main
|
||||
git rebase origin/main
|
||||
# Resolve conflicts — keep the PR's intent, adopt refactors from main
|
||||
git add <resolved files>
|
||||
git rebase --continue
|
||||
git push --force-with-lease
|
||||
```
|
||||
|
||||
After rebasing, go back to Step 3 to re-trigger CI.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Do NOT post comments on the PR.** This is a local tool — communicate with the user directly.
|
||||
- **Do NOT address review comments.** The goal is green CI, not review resolution.
|
||||
- **Do NOT fix formatting.** The `Isort and Black Formatting` action handles that automatically.
|
||||
- **Do NOT retry a failed fix.** If your previous commit didn't resolve the failure, tell the user.
|
||||
- **Do NOT create new PRs.** Push to the existing branch only.
|
||||
- **Do NOT attempt to merge the PR or push to main.**
|
||||
|
||||
## What done looks like
|
||||
|
||||
CI is green (or the only remaining failures are pre-existing / flaky and you've told the user about them). That's it.
|
||||
@@ -0,0 +1,320 @@
|
||||
---
|
||||
name: debug-training-logs
|
||||
description: Debug distributed training failures (NeMo, Megatron, PyTorch) from worker stderr logs and optional AIStore daemon logs. Finds root cause across NCCL timeouts, data loading errors, and storage failures.
|
||||
disable-model-invocation: true
|
||||
allowed-tools: Bash Read Grep Glob Agent
|
||||
argument-hint: <path-to-logs-dir> [ais-logs-dir]
|
||||
---
|
||||
|
||||
# Distributed Training Log Debugger
|
||||
|
||||
You are debugging a distributed training job failure. The user will provide one or two directories:
|
||||
- **Worker logs** (stderr from SLURM/torchrun): `$ARGUMENTS[0]` (required)
|
||||
- **AIStore daemon logs** (proxy/target tarballs or extracted dirs): `$ARGUMENTS[1]` (optional)
|
||||
|
||||
Your goal: find the **root cause**, not just the symptom. There are often cascading failures — one root cause triggers many downstream errors. Trace backwards from the final crash to the original trigger.
|
||||
|
||||
## CRITICAL: Verification discipline
|
||||
|
||||
**You MUST double-check every conclusion before presenting it to the user.** Distributed training failures have cascading effects that make it easy to mistake a symptom for the root cause. Follow these rules:
|
||||
|
||||
1. **Check ALL ranks, not a sample.** Do not look at 5 ranks and assume the other 123 are the same. Use `sort -u` on the full output to find outliers. A single outlier rank can be the entire root cause.
|
||||
2. **Verify NCCL state for EVERY rank.** Extract `last enqueued work` and `last completed work` for all ranks and check for ANY rank that differs. A rank with `enqueued == completed` (no pending ops) is fundamentally different from ranks with `enqueued > completed` (ops pending) — it means that rank never entered the stuck collective.
|
||||
3. **Distinguish "First PG on this rank to signal dumping" from "Observed flight recorder dump signal from another rank".** The first is the initiator (its own watchdog fired). The second was notified (it may not even be in the collective). These are DIFFERENT failure modes.
|
||||
4. **Before stating a root cause, re-verify it against the raw data.** Re-read the actual log lines. Do not rely on earlier summaries you wrote — they may have been wrong.
|
||||
5. **If your analysis changes during the investigation, explicitly state what was wrong before and why.** Do not silently shift conclusions.
|
||||
|
||||
## Phase 0: Obtain logs
|
||||
|
||||
If the user has not provided worker logs, ask them to download and provide the SLURM error logs. The logs are typically on the compute nodes or in a shared filesystem. Suggest:
|
||||
|
||||
```bash
|
||||
# From the user's machine, SCP the SLURM error logs:
|
||||
scp <cluster>:/path/to/slurm/logs/error-<JOBID>-*.out ./training_logs/
|
||||
|
||||
# Or if the logs are on a shared filesystem accessible from a login node:
|
||||
mkdir -p ./training_logs
|
||||
cp /path/to/slurm/error-<JOBID>-*.out ./training_logs/
|
||||
```
|
||||
|
||||
The user needs to provide ALL per-node error files (e.g., `error-JOBID-0.out` through `error-JOBID-N.out`) — not just one node. Without all files you cannot identify which specific rank caused the failure.
|
||||
|
||||
## Phase 1: Triage worker logs
|
||||
|
||||
### 1.1 Understand the job
|
||||
Read the first 80 lines of a few log files to determine:
|
||||
- Framework (NeMo, Megatron, PyTorch Lightning, DeepSpeed, etc.)
|
||||
- Scale (GPU count, node count, ranks per node)
|
||||
- What the job is doing (training, fine-tuning, inference)
|
||||
- Whether it resumed from a checkpoint
|
||||
|
||||
### 1.2 Find the fatal errors
|
||||
Search ALL log files in parallel for these patterns (priority order):
|
||||
|
||||
**Tier 1 — Process killers:**
|
||||
```
|
||||
NCCL.*timeout|Watchdog caught collective operation timeout
|
||||
taking the entire process down
|
||||
SIGTERM|SIGKILL|SIGABRT
|
||||
CUDA error|CUDA out of memory|OOM
|
||||
```
|
||||
|
||||
**Tier 2 — Training loop crashes:**
|
||||
```
|
||||
RuntimeError|Exception.*Error
|
||||
AISBatchLoaderError|StopIteration
|
||||
Traceback \(most recent call last\)
|
||||
```
|
||||
|
||||
**Tier 3 — Data loading / IO:**
|
||||
```
|
||||
Connection reset|Connection broken|Connection refused
|
||||
retrying [0-9]+/[0-9]+
|
||||
timed out|deadline exceeded
|
||||
broken pipe
|
||||
```
|
||||
|
||||
### 1.3 Identify the initiator and the straggler
|
||||
For NCCL timeouts, you MUST check ALL ranks — do not sample. Extract the full NCCL state for every rank:
|
||||
|
||||
```bash
|
||||
# Get watchdog state for all ranks that fired their own watchdog:
|
||||
grep "failure detected by watchdog" error-*.out | grep -o "Rank [0-9]*.*last enqueued work: [0-9]*, last completed work: [0-9]*" | sort -u
|
||||
|
||||
# Get ranks that were NOTIFIED (did not fire their own watchdog):
|
||||
grep "Observed flight recorder dump signal from another rank" error-*.out | grep -o "Rank [0-9]*"
|
||||
|
||||
# Count total unique ranks found vs expected:
|
||||
grep "failure detected by watchdog" error-*.out | grep -o "Rank [0-9]*" | sort -t' ' -k2 -n -u | wc -l
|
||||
```
|
||||
|
||||
**The rank that "Observed" instead of "detected" is likely the straggler** — it had no pending NCCL operations because it never entered the collective. Check its `Last enqueued NCCL work` — if it equals `last completed NCCL work`, the rank was stuck OUTSIDE NCCL (in the training loop, data loading, etc.), not inside a collective.
|
||||
|
||||
For NCCL BROADCAST/ALLREDUCE timeouts, calculate when the collective started:
|
||||
`start_time = timeout_time - timeout_ms` (usually 1800000ms = 30min)
|
||||
|
||||
### 1.4 Count and classify
|
||||
- Count `Connection reset` across all files (total and per-file)
|
||||
- Check retry patterns: are retries always 1/N (recovering) or do they escalate to N/N (exhausted)?
|
||||
- Count unique error types and affected ranks
|
||||
- Look for `AISBatchLoaderError` or similar batch loader errors — these indicate AIStore returned fewer objects than requested
|
||||
|
||||
### 1.5 Build the timeline
|
||||
Determine the **causal chain**: which error happened first, which are consequences.
|
||||
The pattern is usually:
|
||||
```
|
||||
Data loading error (root cause)
|
||||
-> Some ranks crash out of training loop
|
||||
-> Crashed ranks can't participate in NCCL collective
|
||||
-> NCCL collective hangs for timeout period (usually 30min)
|
||||
-> Watchdog kills all remaining ranks
|
||||
```
|
||||
|
||||
### 1.6 Check NeMo-specific synchronization points
|
||||
|
||||
NeMo has per-step collective operations that can cause rank desynchronization:
|
||||
|
||||
- **PreemptionCallback** (`nemo/utils/callbacks/preemption.py`): Calls `torch.distributed.broadcast(interrupted, 0)` at the end of EVERY training step via `on_train_batch_end`. If one rank's training step takes >30 min longer than others, this broadcast times out.
|
||||
- **NeMoModelCheckpoint** (`nemo/utils/callbacks/nemo_model_checkpoint.py`): Multiple `trainer.strategy.broadcast()` calls during checkpoint save/load.
|
||||
- **DDP gradient all-reduce**: Automatic per-step synchronization during backward pass.
|
||||
- **`broadcast_buffers`** (DDP default=True): Broadcasts model buffers (e.g., batch norm stats) from rank 0 at each forward pass.
|
||||
|
||||
If rank 0 is ahead of other ranks in NCCL SeqNum, check whether the gap matches the number of per-step collectives (PreemptionCallback broadcast + DDP allreduce + broadcast_buffers = ~3 ops per step).
|
||||
|
||||
## Phase 1.7: Request AIStore logs if not provided
|
||||
|
||||
If the analysis points to storage I/O issues (connection resets, data loading errors, timeouts) and the user did NOT provide AIS daemon logs, suggest downloading them. First check if the `ais` CLI is available (`which ais`). If it is, guide the user through:
|
||||
|
||||
1. **Set the cluster endpoint** (ask the user for the endpoint URL):
|
||||
```bash
|
||||
export AIS_ENDPOINT=https://<ais-cluster-endpoint>:<port>
|
||||
```
|
||||
|
||||
2. **Set the auth token** (ask the user for the token value):
|
||||
```bash
|
||||
export AIS_AUTHN_TOKEN=<token>
|
||||
```
|
||||
|
||||
3. **Handle TLS** — skip cert verification or point to the CA cert:
|
||||
```bash
|
||||
# Option A: skip verification
|
||||
ais config cli set cluster.skip_verify_crt=true
|
||||
# Option B: set CA cert
|
||||
export AIS_SERVER_CRT=/path/to/ca.crt
|
||||
```
|
||||
|
||||
4. **Download all cluster logs** to the current log directory:
|
||||
```bash
|
||||
ais log get cluster <path-to-worker-logs-dir>/ais_logs
|
||||
```
|
||||
This downloads TAR.GZ archives from all proxy and target nodes.
|
||||
|
||||
5. Extract and analyze per Phase 2 below.
|
||||
|
||||
If the `ais` CLI is not installed, it can be built from the AIStore repository (`cd cmd/cli && go install .`) or downloaded as a binary. Alternatively, ask the user to download the logs manually from the AIS cluster.
|
||||
|
||||
## Phase 2: Debug AIStore logs (if provided)
|
||||
|
||||
### 2.1 Extract and orient
|
||||
If tarballs (`.tar.gz`), extract them:
|
||||
```bash
|
||||
mkdir -p extracted && cd extracted
|
||||
for f in ../*.tar.gz; do
|
||||
name=$(basename "$f" .tar.gz)
|
||||
mkdir -p "$name" && tar xzf "$f" -C "$name"
|
||||
done
|
||||
```
|
||||
|
||||
**AIS log file naming and time ranges:**
|
||||
|
||||
AIS daemon logs follow the naming convention:
|
||||
```
|
||||
aistarget.ais-target-N.INFO.MMDD-HHMMSS.1 # target logs
|
||||
aisproxy.ais-proxy-N.INFO.MMDD-HHMMSS.1 # proxy logs
|
||||
```
|
||||
|
||||
A single daemon (target or proxy) may have **multiple log files** — each file covers a specific time range:
|
||||
- The `MMDD-HHMMSS` in the filename is the **start time** of that file
|
||||
- The file covers from its start time **until the start time of the next file** for the same daemon
|
||||
- If there is no next file, it covers until the daemon stopped or the logs were collected
|
||||
- A new file is created when the daemon restarts (crash, upgrade, maintenance cycle)
|
||||
|
||||
**To find the correct file for a failure window:**
|
||||
1. List all files for each daemon, sorted by the timestamp in the filename
|
||||
2. For each daemon, find the file whose start time is BEFORE the failure window AND whose next file's start time is AFTER the failure window (or there is no next file)
|
||||
3. A daemon may have been restarted DURING the failure window — check if any file starts within the window (this indicates a restart, which is itself significant)
|
||||
4. Log lines within a file only have TIME (HH:MM:SS), not dates. If a file spans midnight, the same time (e.g., "21:30") may appear twice — once for each day. Use surrounding context (stats counter values, known events) to disambiguate which day a log line belongs to.
|
||||
|
||||
**Always check ALL files for a daemon, not just the latest.** The latest file may only cover minutes after a restart, while the failure evidence is in an earlier file.
|
||||
|
||||
**Timezone verification:**
|
||||
AIS daemon logs and worker (SLURM/torchrun) logs may be on different machines in different timezones. NEVER assume they match. To verify:
|
||||
1. Look for AIS periodic timestamp markers: `common:NNN DD Mon YY HH:MM UTC =============` — these explicitly state the timezone (typically UTC).
|
||||
2. Look for absolute timestamps in worker logs: NeMo uses `YYYY-MM-DD HH:MM:SS`, SLURM uses `YYYY-MM-DDThh:mm:ss` — but neither includes timezone by default.
|
||||
3. **Cross-reference a known event** visible in both log sources. The best anchor is the job death: find the SLURM `CANCELLED` timestamp in worker logs, then find the exact moment `get.n` stops incrementing in AIS stats. If they align, the timezones match. If they're offset by a round number of hours, one system is in a different timezone.
|
||||
4. If timezones don't match, apply the offset consistently before correlating events.
|
||||
|
||||
### 2.2 Check target stats during failure window
|
||||
AIStore targets emit periodic stats lines (every ~3 min). Extract key counters around the failure time:
|
||||
|
||||
**Critical counters to track:**
|
||||
- `err.get.n` — GET errors (should be stable; spikes indicate problems)
|
||||
- `err.getbatch.n` — batch GET errors
|
||||
- `err.http.write.n` — broken HTTP responses to clients
|
||||
- `err.put.n`, `err.head.n`, `err.lst.n` — other operation errors
|
||||
- `get.n` vs `err.get.n` — compute error rate
|
||||
- `getbatch.n`, `getbatch.obj.n` — batch operation counts
|
||||
|
||||
Compare values between consecutive stats lines to find the **delta** (new errors in that interval).
|
||||
|
||||
### 2.3 Search for error-level messages
|
||||
```
|
||||
grep "^E " <logfile> # Error messages
|
||||
grep "^W " <logfile> # Warning messages
|
||||
```
|
||||
|
||||
**Key error patterns in AIStore:**
|
||||
- `x-get-batch.*out-of-bounds index` — batch GET lost objects during inter-target streaming
|
||||
- `shared-dm.*terminated.*broken pipe` — inter-target data mover stream failure
|
||||
- `shared-dm: xid.*not found, dropping recv` — objects dropped because batch job already aborted
|
||||
- `resource pressure: load=critical` — target under disk/memory/CPU pressure
|
||||
- `lcache.*hk.*dsk=critical` — disk at critical level, housekeeping skipped
|
||||
- `gc:.*free mem` / `oom:` — memory pressure / forced GC
|
||||
|
||||
### 2.4 Check proxy logs
|
||||
The proxy orchestrates batch GETs. Check proxy stats for:
|
||||
- `err.get.n` — should be near zero; high = proxy-level routing failures
|
||||
- `err.http.write.n` — proxy dropping client connections
|
||||
|
||||
If proxy errors are stable but target errors are spiking, the problem is target-side (disk, memory, inter-target networking).
|
||||
|
||||
### 2.5 Correlate the `x-get-batch` failure mechanism
|
||||
The typical AIStore batch GET failure chain:
|
||||
```
|
||||
1. Target under resource pressure (dsk=critical, mem=low)
|
||||
2. Inter-target shared-dm streams break (broken pipe)
|
||||
3. x-get-batch gets out-of-bounds index (recv'd len=0)
|
||||
4. Batch job aborted, subsequent objects dropped (xid not found)
|
||||
5. Client receives fewer objects than requested
|
||||
6. Lhotse/client batch loader raises error (iterator exhausted prematurely)
|
||||
```
|
||||
|
||||
## Phase 3: Synthesize
|
||||
|
||||
### 3.1 Write the report
|
||||
Structure your output as:
|
||||
|
||||
**Job Details** — framework, scale, start time, data source
|
||||
|
||||
**Timeline Table** — chronological events with timestamps, source file:line
|
||||
|
||||
**Root Cause Chain** — numbered chain from trigger to final crash, with arrows
|
||||
|
||||
**Key Files** — which log files contain the critical evidence
|
||||
|
||||
**Recommendations** — actionable fixes (storage-side, client-side, training config)
|
||||
|
||||
### 3.2 Classify the failure
|
||||
Common root cause categories:
|
||||
- **Storage I/O**: disk pressure, broken pipes, connection resets, batch object loss
|
||||
- **Network**: NCCL timeout without data errors, NIC failures, switch issues
|
||||
- **GPU/CUDA**: OOM, ECC errors, CUDA assertions
|
||||
- **Data**: corrupt/missing data files, manifest mismatches, schema errors
|
||||
- **Software**: version mismatches, config errors, OOM in Python process
|
||||
- **Infrastructure**: node failure, preemption, SLURM timeout
|
||||
- **Data loading stall**: single rank stuck in data loading (no timeout on read), blocking all other ranks at the next collective
|
||||
|
||||
## Reference: AIStore error counter meanings
|
||||
|
||||
| Counter | Meaning |
|
||||
|---|---|
|
||||
| `err.get.n` | Individual object GET failures on this target |
|
||||
| `err.getbatch.n` | Batch GET operation failures |
|
||||
| `err.http.write.n` | Failed HTTP writes to client (connection dropped) |
|
||||
| `err.append.n` | Append operation failures |
|
||||
| `err.put.n` | PUT operation failures |
|
||||
| `err.head.n` | HEAD (metadata) operation failures |
|
||||
| `err.lst.n` | List operation failures |
|
||||
| `err.kalive.n` | Keep-alive failures |
|
||||
| `getbatch.n` | Total batch GET operations |
|
||||
| `getbatch.obj.n` | Total objects served via batch GET |
|
||||
| `getbatch.throttle.ns` | Time spent throttling batch GETs |
|
||||
|
||||
## Reference: NCCL timeout anatomy
|
||||
|
||||
- `Watchdog caught collective operation timeout` — the NCCL watchdog detected a stuck collective
|
||||
- `SeqNum=N, OpType=BROADCAST/ALLREDUCE` — which collective and sequence number
|
||||
- `last enqueued work: N, last completed work: M` — work M completed, work M+1 is stuck
|
||||
- `Timeout(ms)=1800000` — 30-minute timeout (default)
|
||||
- `First PG on this rank to signal dumping` — THIS rank initiated the cascade
|
||||
- `Observed flight recorder dump signal from another rank` — this rank is reacting to another's timeout
|
||||
- `To avoid data inconsistency, we are taking the entire process down` — watchdog kills the process
|
||||
|
||||
### Distinguishing data loading stalls from GPU/NCCL hangs
|
||||
|
||||
The `last enqueued work` vs `last completed work` state is critical for determining whether the hang is in the CPU (data loading, training loop) or in the GPU (NCCL communication):
|
||||
|
||||
- **If `enqueued == completed` (no pending ops)**: This rank has NO NCCL work in-flight. It is stuck in the CPU-side training loop (data loading, audio decoding, batch prep) and never entered the collective. **This is the straggler — the rank that caused the hang.**
|
||||
- **If `enqueued == completed + 1`**: The rank has submitted exactly one operation that hasn't completed. It entered the collective but can't complete because the straggler rank hasn't joined.
|
||||
- **If `enqueued > completed + 1`**: Multiple operations are queued — the CPU has progressed past the stuck point and submitted additional operations asynchronously (e.g., DDP gradient allreduce via hooks). Still waiting for the straggler.
|
||||
- **If rank 0 has higher `enqueued`/`completed` than others**: Rank 0 (often the broadcast root) completed its side of a collective (send) but receivers can't complete (receive) because the straggler hasn't joined.
|
||||
|
||||
**The key pattern for a data loading stall:**
|
||||
- 1 rank: `enqueued == completed`, `active collectives: 0`, "Observed flight recorder dump signal from another rank" — **this is the straggler**
|
||||
- N-1 ranks: `enqueued > completed`, "failure detected by watchdog" — these are waiting for the straggler
|
||||
- Rank 0: may be further ahead if it's the broadcast root (can complete send without receivers)
|
||||
|
||||
**The key pattern for a GPU fabric issue:**
|
||||
- ALL ranks: `enqueued > completed` (all entered the collective), all show "First PG on this rank to signal dumping" — no straggler, the collective itself is broken
|
||||
|
||||
Compare `enqueued` counts across ALL ranks. Even ONE outlier changes the entire diagnosis.
|
||||
|
||||
## Reference: NeMo/Lhotse data loading pitfalls
|
||||
|
||||
Common data loading issues that cause single-rank stalls:
|
||||
|
||||
- **No timeout on Lhotse URL audio reads**: `AudioSource._prepare_for_reading()` calls `f.read()` with no Lhotse-level timeout. A stalled download blocks the DataLoader worker indefinitely.
|
||||
- **`fault_tolerant=True` silently drops failed audio**: Failed audio files are skipped, reducing effective batch size per rank. Different ranks may have different failure rates depending on their shard assignments.
|
||||
- **`.m4a` files lose extension in BytesIO**: When audio is downloaded from URLs and wrapped in BytesIO, the file extension is lost. Lhotse's `CompositeAudioBackend` cannot use the fast-path for m4a (TorchaudioFFMPEGBackend) and falls through to expensive cascading backend trials.
|
||||
- **Connection reset on idle keep-alive**: AIStore closes idle HTTP connections after 30s (`DfltMaxIdleTimeout`). The Python SDK's urllib3 pool doesn't match this timeout, causing `Connection reset by peer` on stale pooled connections. These are caught and retried (always succeed on 1st retry) — they are noise, NOT a root cause.
|
||||
- **Each rank gets disjoint data shards**: Lhotse splits shards via `src[rank::world_size]`. One rank may get shards with more corrupt files, larger audio, or slower storage targets.
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
name: fix-issue
|
||||
description: Fix a GitHub issue in NeMo Speech (NVIDIA-NeMo/NeMo). Read the issue, reproduce the bug with a failing test, implement the fix, and verify tests pass. Only opens a PR if the user explicitly asks for it.
|
||||
---
|
||||
|
||||
# fix-issue
|
||||
|
||||
Fix a GitHub issue in NeMo Speech from first principles: understand the bug, prove it with a test, and fix the root cause.
|
||||
|
||||
The key discipline is **reproduce first, fix second**. Writing the failing test before touching the source code forces a precise understanding of what broken actually means, which keeps the fix focused and correct.
|
||||
|
||||
## Inputs
|
||||
|
||||
The issue number is the primary input. It may come from:
|
||||
- An explicit argument: `/fix-issue 1234`
|
||||
- The conversation context: "fix issue #1234"
|
||||
- A GitHub issue URL pasted into the chat
|
||||
|
||||
If no issue number is clear, and the description provided is not good enough, ask for more details before proceeding.
|
||||
|
||||
## Before starting
|
||||
|
||||
Read the issue description carefully. Identify:
|
||||
- What is the reported failure (error message, wrong output, crash)?
|
||||
- Which collections in `nemo/collections` and components are affected?
|
||||
- Are there any repro steps already in the issue? If so, run them first.
|
||||
- Is there a linked PR or related issue that gives more context?
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Read the issue: `gh issue view <ISSUE_NUMBER> --repo NVIDIA-NeMo/NeMo`
|
||||
2. Understand the bug — identify the relevant code
|
||||
3. Write a minimal reproduction test in `tests/` that demonstrates the failure
|
||||
4. Run the test to confirm it fails: `pytest <your_test_file> -v`
|
||||
5. Implement the fix in the source code
|
||||
6. Run the test again to confirm it now passes
|
||||
7. Run the broader test suite for the affected collection to check for regressions:
|
||||
```bash
|
||||
pytest tests/collections/<collection>/ -m "not pleasefixme" -v --timeout=120
|
||||
```
|
||||
8. Tell the user the fix is ready and what was changed
|
||||
|
||||
## Opening a PR
|
||||
|
||||
Only create a branch and open a PR if the user explicitly asks for it. When they do:
|
||||
|
||||
```bash
|
||||
git checkout -b fix/<ISSUE_NUMBER>-<short-description>
|
||||
git add <changed files>
|
||||
git commit -s -m "Fix <short-description> (closes #<ISSUE_NUMBER>)"
|
||||
git push origin fix/<ISSUE_NUMBER>-<short-description>
|
||||
gh pr create --repo NVIDIA-NeMo/NeMo \
|
||||
--title "Fix <short-description>" \
|
||||
--body "$(cat <<'EOF'
|
||||
# What does this PR do ?
|
||||
<one-line overview>
|
||||
|
||||
# Changelog
|
||||
<line-by-line high-level changes>
|
||||
|
||||
# Usage
|
||||
<usage example if behavior changed>
|
||||
|
||||
Fixes #<ISSUE_NUMBER>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Add specific files by name — do not use `git add -A` or `git add .`.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Reproduce first.** Do not attempt a fix without a failing test that demonstrates the bug.
|
||||
- **Do NOT open a PR unless the user asks.** The default is local-only: fix the code, verify it works, report back.
|
||||
- **Do NOT post comments on the GitHub issue.** Communicate with the user directly.
|
||||
- **Do NOT reformat files outside your changes.** The `Isort and Black Formatting` action handles formatting automatically.
|
||||
- **Never push to `main` directly.**
|
||||
- **Never attempt to merge the PR yourself.**
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: nemo-speech-asr-finetune
|
||||
description: Guide NeMo Speech users through ASR fine-tuning with container setup and Lhotse training.
|
||||
---
|
||||
|
||||
# NeMo Speech ASR Fine-Tuning
|
||||
|
||||
Use this skill when a user wants to fine-tune a NeMo Speech ASR model, choose a checkpoint, adapt a tokenizer,
|
||||
configure Lhotse dataloading, train, average checkpoints, or evaluate a fine-tuned ASR `.nemo` checkpoint.
|
||||
Also use it for post-run refinement planning after fine-tuning.
|
||||
|
||||
Default posture:
|
||||
|
||||
- Use the NeMo container unless the user explicitly asks for local execution.
|
||||
- Prefer Lhotse for train and validation dataloaders.
|
||||
- Use `trainer.max_steps`, not `trainer.max_epochs`.
|
||||
- Use `val_wer` as the checkpoint monitor for validation.
|
||||
- By default, evaluate WER without capitalization and punctuation effects. Change that only when the user explicitly
|
||||
asks for raw/cased/punctuated scoring.
|
||||
- Report final quality from standalone evaluation, not only in-training validation logs.
|
||||
|
||||
## Staged Workflow
|
||||
|
||||
Load only the reference file needed for the current stage:
|
||||
|
||||
1. Setup and checkpoint selection: read `references/setup-checkpoints.md`.
|
||||
2. Data prep, transcript-style preflight, Lhotse, bucketing, validation dataloader, and blends: read
|
||||
`references/data-lhotse.md`.
|
||||
3. Architecture detection, tokenizer changes, and AED/Canary multitask metrics: read
|
||||
`references/architecture-tokenizer-metrics.md`.
|
||||
4. Training, checkpoint averaging, and evaluation: read `references/training-evaluation.md` and, when reporting WER,
|
||||
`references/evaluation-style-contract.md`.
|
||||
5. Post-run refinement, error analysis, curriculum, and general-vs-domain evaluation: read
|
||||
`references/refinement-iteration.md`.
|
||||
|
||||
If the user explicitly asks for parallel/sub-agent work, split the work by these same stages. Keep each agent scoped to
|
||||
one stage and have the main agent integrate the final command/config.
|
||||
|
||||
## Core Commands
|
||||
|
||||
Generic fine-tuning uses `examples/asr/speech_to_text_finetune.py`. For architecture-specific recipes, route to:
|
||||
|
||||
- CTC: `examples/asr/asr_ctc/speech_to_text_ctc_bpe.py`
|
||||
- RNNT: `examples/asr/asr_transducer/speech_to_text_rnnt_bpe.py`
|
||||
- Hybrid RNNT/CTC or TDT/CTC: `examples/asr/asr_hybrid_transducer_ctc/speech_to_text_hybrid_rnnt_ctc_bpe.py`
|
||||
- AED/Canary: `examples/asr/speech_multitask/speech_to_text_aed.py`
|
||||
|
||||
Always check the current repo docs before giving version-sensitive claims:
|
||||
|
||||
- `README.md`
|
||||
- `docs/source/asr/fine_tuning.rst`
|
||||
- `docs/source/asr/datasets.rst`
|
||||
- `docs/source/dataloaders.rst`
|
||||
- `docs/source/asr/featured_models.rst`
|
||||
- `docs/source/asr/asr_checkpoints.rst`
|
||||
- `nemo/collections/common/data/lhotse/dataloader.py`
|
||||
|
||||
## Non-Negotiable Pitfalls
|
||||
|
||||
- When changing Lhotse batch modes, explicitly null conflicting options. For OOMptimizer profiles, set
|
||||
`batch_size=null`, `batch_duration=null`, and `quadratic_duration=null` when adding `bucket_batch_size`.
|
||||
- Set `model.validation_ds.use_lhotse=true`, but prefer static validation `batch_size` with bucketing disabled.
|
||||
- Do not use fused loss/WER or tune `fused_batch_size` for RNNT/TDT fine-tuning guidance from this skill.
|
||||
- Run the first OOMptimizer pass with default CLI settings; lower `--memory-fraction` only after a real training OOM.
|
||||
- Run preflight checks before long jobs: disk space, free GPUs, manifest validity, and duration/text sanity.
|
||||
- Before any fine-tuning, audit transcript style within and across all fine-tuning/validation/test sources. Do not
|
||||
train on mixed casing, punctuation, inverse-text-normalization, or symbol conventions; choose and fix one target style
|
||||
first, and compare it with the original checkpoint's prediction style when applicable.
|
||||
- For small domain adaptation, start with a lower LR than large-data fine-tuning; do not blindly use `1e-4`.
|
||||
- Do not train a tokenizer on validation or test transcripts.
|
||||
- Do not ignore silent Lhotse filtering from `min_duration`, `max_duration`, `min_tps`, and `max_tps`.
|
||||
- Do not use `amp=true` for inference/evaluation; use `amp=false compute_dtype=bfloat16`.
|
||||
- Unless the user asks otherwise, report the default WER with capitalization and punctuation removed, and record any raw
|
||||
WER separately when it helps diagnose transcript-style mismatch.
|
||||
- For AED/Canary, configure `multitask_metrics_cfg` so ASR and translation/task-specific samples are evaluated with
|
||||
the right constrained metrics.
|
||||
- If checkpoint averaging is used, evaluate the averaged checkpoint and keep it only if it beats the best individual
|
||||
checkpoint.
|
||||
@@ -0,0 +1,92 @@
|
||||
# ASR Fine-Tuning Experiment Ledger
|
||||
|
||||
## Goal
|
||||
|
||||
- User goal:
|
||||
- Target checkpoint:
|
||||
- Architecture:
|
||||
- Success metric:
|
||||
- Optional guardrail metrics:
|
||||
|
||||
## Data
|
||||
|
||||
- Train manifests or input config:
|
||||
- Validation manifest:
|
||||
- Test manifest:
|
||||
- Data sources and weights:
|
||||
- Tarred/non-tarred:
|
||||
- Manifest sharding:
|
||||
- Transcript style policy:
|
||||
- Style transform artifact:
|
||||
- Original checkpoint output style:
|
||||
- Style mismatch decision:
|
||||
|
||||
## Preflight
|
||||
|
||||
- Disk space:
|
||||
- GPUs:
|
||||
- Container/image:
|
||||
- Manifest validation:
|
||||
- Duration distribution:
|
||||
- Text/token distribution:
|
||||
- Duration/token filters:
|
||||
- Examples/hours filtered:
|
||||
|
||||
## Lhotse And OOMptimizer
|
||||
|
||||
- Lhotse train:
|
||||
- Lhotse validation:
|
||||
- Bucketing mode:
|
||||
- Duration bins:
|
||||
- Bucket batch sizes:
|
||||
- Static batch size:
|
||||
- `bucket_buffer_size`:
|
||||
- `shuffle_buffer_size`:
|
||||
- `seed`:
|
||||
- `shard_seed`:
|
||||
- OOMptimizer settings:
|
||||
- Training pilot utilization:
|
||||
- CPU memory notes:
|
||||
|
||||
## Training
|
||||
|
||||
- Init checkpoint:
|
||||
- Script/config:
|
||||
- Precision:
|
||||
- `sync_batchnorm`:
|
||||
- `max_steps`:
|
||||
- `limit_train_batches`:
|
||||
- `val_check_interval`:
|
||||
- LR:
|
||||
- Scheduler:
|
||||
- Warmup:
|
||||
- Min LR:
|
||||
- Save top K:
|
||||
- Command/log path:
|
||||
|
||||
## Evaluation
|
||||
|
||||
| Model | Artifact | Prediction Manifest | Raw WER | Default WER | CER | Notes |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| baseline | | | | | | |
|
||||
| final `.nemo` | | | | | | |
|
||||
| best single | | | | | | |
|
||||
| averaged | | | | | | |
|
||||
|
||||
Default WER uses capitalization and punctuation removal unless the user requested a different metric.
|
||||
|
||||
## Error Analysis
|
||||
|
||||
- Raw vs default WER gap:
|
||||
- Worst sources/domains:
|
||||
- Worst categories:
|
||||
- Label/audio defects:
|
||||
- Decoding findings:
|
||||
|
||||
## Decision
|
||||
|
||||
- Keep artifact:
|
||||
- Drop artifacts:
|
||||
- Next intervention:
|
||||
- Reason:
|
||||
- If validation/test influenced data or weights, blind holdout plan:
|
||||
@@ -0,0 +1,153 @@
|
||||
# Stage 3: Architecture, Tokenizer, And AED Metrics
|
||||
|
||||
## Architecture Detection
|
||||
|
||||
Inspect the model config before choosing scripts and overrides:
|
||||
|
||||
```python
|
||||
from nemo.collections.asr.models import ASRModel
|
||||
|
||||
cfg = ASRModel.from_pretrained("nvidia/parakeet-tdt-0.6b-v3", return_config=True)
|
||||
print(cfg.target)
|
||||
print(cfg.get("decoder", None))
|
||||
print(cfg.get("joint", None))
|
||||
print(cfg.get("loss", None))
|
||||
print(cfg.get("decoding", None))
|
||||
```
|
||||
|
||||
Classify:
|
||||
|
||||
- CTC: `EncDecCTC*`, `ConvASRDecoder`, no RNNT-style `joint`.
|
||||
- RNNT: `EncDecRNNT*` with `decoder` and `joint`.
|
||||
- TDT: RNNT-family config with `loss.loss_name: tdt`, `decoding.model_type: tdt`, durations, or extra duration
|
||||
outputs.
|
||||
- Hybrid RNNT/CTC or TDT/CTC: `EncDecHybridRNNTCTC*`, `aux_ctc`, `ctc_decoder`.
|
||||
- AED/Canary: `EncDecMultiTaskModel`, Transformer decoder, `prompt_format`.
|
||||
|
||||
Use `examples/asr/speech_to_text_finetune.py` for compatible-architecture fine-tuning. For architecture-specific
|
||||
recipes:
|
||||
|
||||
- CTC: `examples/asr/asr_ctc/speech_to_text_ctc_bpe.py`
|
||||
- RNNT: `examples/asr/asr_transducer/speech_to_text_rnnt_bpe.py`
|
||||
- Hybrid RNNT/CTC or TDT/CTC: `examples/asr/asr_hybrid_transducer_ctc/speech_to_text_hybrid_rnnt_ctc_bpe.py`
|
||||
- AED/Canary: `examples/asr/speech_multitask/speech_to_text_aed.py`
|
||||
|
||||
Reference configs to inspect before writing overrides:
|
||||
|
||||
- CTC: `examples/asr/conf/fastconformer/fast-conformer_ctc_bpe.yaml`
|
||||
- RNNT: `examples/asr/conf/fastconformer/fast-conformer_transducer_bpe.yaml`
|
||||
- TDT: `examples/asr/conf/conformer/tdt/conformer_tdt_bpe.yaml`
|
||||
- Hybrid TDT/CTC: `examples/asr/conf/fastconformer/hybrid_transducer_ctc/fastconformer_hybrid_tdt_ctc_bpe.yaml`
|
||||
- AED/Canary: `examples/asr/conf/speech_multitask/fast-conformer_aed.yaml`
|
||||
|
||||
## Tokenizer Decisions
|
||||
|
||||
Keep the pretrained tokenizer when language/script, casing, punctuation, and symbols match. Replace or extend it when
|
||||
the target language/script is new, important symbols are missing, normalization changes substantially, the run is
|
||||
multilingual/code-switching, or Canary/AED prompt/special tokens change.
|
||||
|
||||
Train from training text only:
|
||||
|
||||
```bash
|
||||
python scripts/tokenizers/process_asr_text_tokenizer.py \
|
||||
--manifest=/data/train.json \
|
||||
--data_root=/data/tokenizers/my_tokenizer \
|
||||
--vocab_size=1024 \
|
||||
--tokenizer=spe \
|
||||
--spe_type=unigram \
|
||||
--log
|
||||
```
|
||||
|
||||
Replace in generic fine-tuning:
|
||||
|
||||
```bash
|
||||
model.tokenizer.update_tokenizer=true \
|
||||
model.tokenizer.dir=/data/tokenizers/my_tokenizer/tokenizer_spe_unigram_v1024 \
|
||||
model.tokenizer.type=bpe
|
||||
```
|
||||
|
||||
Changing tokenizer size usually reinitializes decoder-side parameters such as CTC projection or RNNT/TDT
|
||||
decoder/joint pieces. Use conservative LR and validate early.
|
||||
|
||||
Aggregate tokenizer:
|
||||
|
||||
```yaml
|
||||
tokenizer:
|
||||
type: agg
|
||||
langs:
|
||||
en:
|
||||
dir: /data/tokenizers/en/tokenizer_spe_unigram_v1024
|
||||
type: bpe
|
||||
es:
|
||||
dir: /data/tokenizers/es/tokenizer_spe_unigram_v1024
|
||||
type: bpe
|
||||
```
|
||||
|
||||
Standard aggregate ASR configs expect a manifest language field such as `lang`. AED/Canary configs use their own
|
||||
prompt and language fields; follow `examples/asr/conf/speech_multitask/fast-conformer_aed.yaml`.
|
||||
|
||||
## Architecture-Specific Knobs
|
||||
|
||||
CTC:
|
||||
|
||||
- Main tokenizer-sensitive module is the decoder projection.
|
||||
- Useful when alignments or non-autoregressive decoding matter.
|
||||
- Long transcripts can violate CTC length constraints; subword tokenization helps reduce target length.
|
||||
|
||||
RNNT:
|
||||
|
||||
- Disable fused loss/WER for this skill: `model.joint.fuse_loss_wer=false`.
|
||||
- Do not tune `fused_batch_size`; use Lhotse bucketing plus OOMptimizer-generated `bucket_batch_size`.
|
||||
- `model.compute_eval_loss=false` is common when validation samples are long and WER is the main metric.
|
||||
- Use CUDA graphs for inference/evaluation when supported.
|
||||
|
||||
TDT:
|
||||
|
||||
- Preserve `loss.loss_name=tdt`, duration settings, extra outputs, and `decoding.model_type=tdt`.
|
||||
- Disable fused loss/WER and use Lhotse bucketing plus OOMptimizer.
|
||||
- Use CUDA graphs for inference/evaluation when supported.
|
||||
|
||||
Hybrid:
|
||||
|
||||
- Check `model.aux_ctc.ctc_loss_weight`; reference configs often use `0.3`.
|
||||
- Evaluate both decoder paths when relevant with `decoder_type=ctc` and `decoder_type=rnnt`.
|
||||
|
||||
AED/Canary:
|
||||
|
||||
- Use `examples/asr/speech_multitask/speech_to_text_aed.py`.
|
||||
- Preserve `prompt_format` and expected manifest fields.
|
||||
- Prefer 2D Lhotse buckets plus OOMptimizer.
|
||||
|
||||
## AED/Canary Multitask Metrics
|
||||
|
||||
`EncDecMultiTaskModel` reads `model.multitask_metrics_cfg` and constructs `MultiTaskMetric`
|
||||
(`nemo/collections/asr/metrics/multitask.py`). Metric constraints are evaluated against each Lhotse cut's `custom`
|
||||
dict, including manifest fields and `input_cfg.tags`.
|
||||
|
||||
Reference config:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
multitask_metrics_cfg:
|
||||
log_predictions: true
|
||||
metrics:
|
||||
wer:
|
||||
_target_: nemo.collections.asr.metrics.WER
|
||||
constraint: ".source_lang==.target_lang"
|
||||
bleu:
|
||||
_target_: nemo.collections.asr.metrics.BLEU
|
||||
constraint: ".source_lang!=.target_lang"
|
||||
bleu_tokenizer: 13a
|
||||
check_cuts_for_bleu_tokenizers: false
|
||||
```
|
||||
|
||||
Use constraints to route ASR samples to WER and translation samples to BLEU. Add dataset/task/domain metadata through
|
||||
manifest fields or `input_cfg.tags`, then reference it in constraints such as `.domain==target` or
|
||||
`.task==asr and .source_lang==.target_lang`.
|
||||
|
||||
Current implementation supports only one instance of each metric class in a single `multitask_metrics_cfg`. For
|
||||
multiple WER slices by language/domain, prefer separate validation manifests/dataloaders or extend metric aggregation
|
||||
rather than defining duplicate WER metrics.
|
||||
|
||||
For AED validation data, set `use_lhotse: true`, `use_bucketing: false`, static `batch_size`, `text_field: "text"`,
|
||||
and `lang_field: "target_lang"`.
|
||||
@@ -0,0 +1,255 @@
|
||||
# Stage 2: Data, Lhotse, Bucketing, And Blends
|
||||
|
||||
Use Lhotse by default for training and validation. For validation, prefer Lhotse with static `batch_size` and no
|
||||
bucketing; dynamic validation batches can rarely starve DDP ranks.
|
||||
|
||||
## Manifest Preparation
|
||||
|
||||
Standard ASR JSONL:
|
||||
|
||||
```json
|
||||
{"audio_filepath": "/data/audio/sample.wav", "text": "transcript text", "duration": 3.42}
|
||||
```
|
||||
|
||||
Use separate train, validation, and test manifests. Prefer 16 kHz mono audio unless the model card/config says
|
||||
otherwise.
|
||||
|
||||
## Transcript Style Preflight
|
||||
|
||||
Do this before sharding, tokenizer changes, OOMptimizer, or fine-tuning. Treat mixed transcript style as an experiment
|
||||
setup failure.
|
||||
|
||||
For every train, validation, and test manifest, and for each source inside a blend, audit a representative sample of
|
||||
reference transcripts. At minimum, report:
|
||||
|
||||
- Fraction with uppercase letters.
|
||||
- Fraction with punctuation or symbols, including sentence punctuation, quotes, dashes, and brackets.
|
||||
- Number, abbreviation, currency, unit, and special-symbol conventions.
|
||||
- Unicode normalization and script/language consistency.
|
||||
- A few examples before and after the intended text transform.
|
||||
|
||||
The target style must be consistent within and across all fine-tuning sources, and the validation/test style must match
|
||||
the metric being optimized. Do not rely on blend weights to average away style conflicts. If one source is cased and
|
||||
punctuated and another is lowercase/no-punctuation, fix the manifests or split the task before launching training.
|
||||
|
||||
Also check the original checkpoint's prediction style when applicable:
|
||||
|
||||
- First read the model card/config/docs for punctuation, capitalization, ITN, language, and prompt behavior.
|
||||
- If the checkpoint can already transcribe the target language or a close proxy, run a small baseline transcription on
|
||||
representative fine-tuning audio and compute the same style rates on predictions.
|
||||
- If the checkpoint is being moved to a new language/script and baseline predictions are unreliable, do not infer style
|
||||
from bad transcripts; record the checkpoint style as documented or unknown.
|
||||
|
||||
If all fine-tuning data is internally consistent but differs from the original checkpoint's prediction style, that can
|
||||
be acceptable. Flag it to the user because the model may shift output style and raw WER may move even when recognition
|
||||
improves. In autonomous runs, consider adapting the fine-tuning transcripts to the checkpoint's output style when that
|
||||
style is required by deployment or evaluation. If that requires adding missing punctuation/case/ITN labels, treat the
|
||||
new labels as generated data: validate them and keep the raw manifests.
|
||||
|
||||
Choose one target style and materialize new manifests before training:
|
||||
|
||||
- Normalized ASR style: lowercase if appropriate for the language, remove punctuation/symbols that are not part of the
|
||||
target transcript, normalize whitespace and Unicode, and score with the same normalization.
|
||||
- Cased/punctuated/ITN style: use data that already has reliable labels in that style, or restore missing style labels
|
||||
with a validated post-processing pipeline. Do not mix unstyled labels into this target style without relabeling.
|
||||
- Prompted AED/Canary style: use task/language/punctuation prompts only when the selected script and metrics support
|
||||
separating those behaviors; otherwise still enforce one transcript style per fine-tuning objective.
|
||||
|
||||
Preserve raw manifests. Write derived manifests with a clear suffix such as `_style_normalized.json`, record the exact
|
||||
transform, and include the style audit in the run ledger.
|
||||
|
||||
Shard training manifests even when not tarred. For resume-heavy fine-tuning with an unsharded non-tarred manifest,
|
||||
each restart can begin iterating from the start again. Split into shards like `manifest_0.json` ... `manifest_N.json`
|
||||
with roughly 200 utterances per shard. This is less important for one-shot runs that will not be interrupted.
|
||||
|
||||
Strongly prefer tarred data for slow filesystems and object stores. If data is small or there is abundant fast local
|
||||
SSD, non-tarred audio is fine, but still shard the manifest. Do not use tarred datasets for validation/test unless the
|
||||
target script and docs explicitly support the behavior you need.
|
||||
|
||||
Before training, inspect duration and token-per-second distributions. Set `min_duration`, `max_duration`, `min_tps`,
|
||||
and `max_tps` so they do not silently filter out a large or important part of the fine-tuning set.
|
||||
|
||||
If OOMptimizer cannot fit batch size 1 for an extreme duration bucket, inspect the duration tail before changing the
|
||||
model or precision. When only a tiny outlier fraction is affected, set `max_duration` to cap that tail, record the
|
||||
number and hours filtered, and rerun OOMptimizer with the capped final bucket. Do not silently cap substantial or
|
||||
domain-critical long-form data.
|
||||
|
||||
When duration-tail capping is used, include the chosen cap as the final duration bucket passed to OOMptimizer. This
|
||||
keeps the last bucket aligned with the longest sample that training will actually admit.
|
||||
|
||||
Run the first OOMptimizer pass with its default CLI settings. Do not lower `--memory-fraction`, disable DDP simulation,
|
||||
change dtype, or adjust the search threshold unless there is a concrete reason. The default profile is intended to be
|
||||
aggressive enough for high GPU utilization while reserving non-training-loop memory. If the resulting real training run
|
||||
OOMs, then rerun OOMptimizer with a lower `--memory-fraction` and document why.
|
||||
|
||||
Validate the generated profile with a short multi-GPU pilot and sample GPU utilization during training steps, not just
|
||||
startup, checkpointing, or validation. OOMptimizer sizes worst-case synthetic batches at the bucket upper bounds; real
|
||||
training memory can be noticeably lower when the sampler draws shorter cuts or smaller buckets. Focus on peak training
|
||||
memory and sustained SM utilization over enough steps to exercise several buckets.
|
||||
|
||||
Sample utilization while training is inside the train loop:
|
||||
|
||||
```bash
|
||||
nvidia-smi --query-gpu=index,utilization.gpu,utilization.memory,memory.used,memory.total \
|
||||
--format=csv,noheader,nounits
|
||||
```
|
||||
|
||||
Do not judge utilization from checkpoint save, validation, export, dataloader prefill, or process shutdown windows.
|
||||
Record peak memory and sustained SM utilization in the run ledger. If memory remains much lower than expected, first
|
||||
confirm the run is using the OOMptimizer `bucket_batch_size` profile and that conflicting batch settings are null.
|
||||
|
||||
For non-tarred data, Lhotse may tokenize samples during sampling when `pretokenize=true`. This enables token-per-second
|
||||
filtering and 2D token bucketing, but tokenization happens in the main training process and can slow training with large
|
||||
tokenizers. If the run does not use token-per-second filters or 2D bucketing, consider `pretokenize=false`.
|
||||
|
||||
## Batch Mode Compatibility
|
||||
|
||||
When changing Lhotse batch settings, explicitly null conflicting options. `dataloader.py` accepts these batch sizing
|
||||
modes:
|
||||
|
||||
- Static batch size: set `batch_size=<N>`, `use_bucketing=false`, and set `batch_duration=null`,
|
||||
`quadratic_duration=null`, `bucket_duration_bins=null`, `bucket_batch_size=null`, `batch_tokens=null`.
|
||||
- OOMptimizer profile: set `bucket_duration_bins=[...]` and `bucket_batch_size=[...]`, and set `batch_size=null`,
|
||||
`batch_duration=null`, `quadratic_duration=null`, `batch_tokens=null`.
|
||||
- Heuristic dynamic duration batching: set `batch_duration=<seconds>`, optionally `quadratic_duration=<seconds>`, and
|
||||
set `bucket_batch_size=null`, `batch_tokens=null`. Prefer OOMptimizer instead.
|
||||
|
||||
`bucket_batch_size` requires `bucket_duration_bins`. Setting either auto-enables `use_bucketing=true`, but set it
|
||||
explicitly for clarity.
|
||||
|
||||
## Training Lhotse Defaults
|
||||
|
||||
Use integer `val_check_interval`, `limit_train_batches` as pseudo-epoch length, and `max_steps` as the real duration.
|
||||
Do not use `trainer.max_epochs`.
|
||||
|
||||
```bash
|
||||
++model.train_ds.use_lhotse=true \
|
||||
++model.train_ds.use_bucketing=true \
|
||||
++model.train_ds.batch_size=null \
|
||||
++model.train_ds.batch_duration=null \
|
||||
++model.train_ds.quadratic_duration=null \
|
||||
++model.train_ds.bucket_duration_bins='[...]' \
|
||||
++model.train_ds.bucket_batch_size='[...]' \
|
||||
++model.train_ds.bucket_buffer_size=10000 \
|
||||
++model.train_ds.shuffle_buffer_size=10 \
|
||||
++trainer.use_distributed_sampler=false \
|
||||
+trainer.limit_train_batches=1000 \
|
||||
trainer.val_check_interval=1000 \
|
||||
trainer.max_steps=<steps>
|
||||
```
|
||||
|
||||
Validation should use Lhotse but static batches:
|
||||
|
||||
```bash
|
||||
++model.validation_ds.use_lhotse=true \
|
||||
++model.validation_ds.use_bucketing=false \
|
||||
model.validation_ds.batch_size=8 \
|
||||
++model.validation_ds.batch_duration=null \
|
||||
++model.validation_ds.quadratic_duration=null \
|
||||
++model.validation_ds.bucket_duration_bins=null \
|
||||
++model.validation_ds.bucket_batch_size=null \
|
||||
model.validation_ds.shuffle=false
|
||||
```
|
||||
|
||||
Use Hydra `++` for dataloader keys that are not declared in the selected YAML. This commonly applies to Lhotse-specific
|
||||
validation keys such as `use_lhotse`, `use_bucketing`, `batch_duration`, `bucket_duration_bins`, and sometimes
|
||||
`min_duration`/`max_duration`.
|
||||
|
||||
## Bucketing Policy
|
||||
|
||||
- For CTC/RNNT/TDT, prefer 1D duration bucketing.
|
||||
- For AED/Canary, prefer 2D bucketing over duration and token count.
|
||||
- Strongly prefer OOMptimizer-generated `bucket_batch_size` over manually tuning `batch_duration` and
|
||||
`quadratic_duration`.
|
||||
- If all utterances are fixed length, disable bucketing and use static `batch_size`.
|
||||
- For very small fine-tuning datasets around 100 hours, especially on one GPU, consider disabling bucketing and using
|
||||
fully random static `batch_size`.
|
||||
|
||||
1D bins and OOMptimizer:
|
||||
|
||||
```bash
|
||||
python scripts/speech_recognition/estimate_duration_bins.py -b 30 /data/train_input_cfg.yaml
|
||||
python scripts/speech_recognition/oomptimizer.py \
|
||||
--pretrained-name nvidia/parakeet-tdt-0.6b-v2 \
|
||||
--buckets '[2.0,3.1,5.6,8.4,12.0,18.0,30.0]'
|
||||
```
|
||||
|
||||
If `max_duration` was capped to 30.0, make sure 30.0 is present as the final bucket.
|
||||
|
||||
2D AED/Canary bins and OOMptimizer:
|
||||
|
||||
```bash
|
||||
python scripts/speech_recognition/estimate_duration_bins_2d.py \
|
||||
--prompt-format canary \
|
||||
--prompt "[{'role':'user','slots':{'source_lang':'en','target_lang':'en','pnc':'yes'}}]" \
|
||||
--tokenizer /data/tokenizers/spl_tokens/tokenizer.model /data/tokenizers/en/tokenizer.model \
|
||||
--langs spl_tokens en \
|
||||
--buckets 30 \
|
||||
--sub-buckets 2 \
|
||||
/data/train_input_cfg.yaml
|
||||
|
||||
python scripts/speech_recognition/oomptimizer.py \
|
||||
--config-path examples/asr/conf/speech_multitask/fast-conformer_aed.yaml \
|
||||
--module-name nemo.collections.asr.models.EncDecMultiTaskModel \
|
||||
--buckets '[[3.9,30],[3.9,48],[5.0,37]]'
|
||||
```
|
||||
|
||||
Nested `bucket_duration_bins` automatically activate 2D bucketing.
|
||||
|
||||
## Buffers, Memory, And RNG
|
||||
|
||||
- `bucket_buffer_size=10000` is a good default.
|
||||
- With bucketing enabled, set `shuffle_buffer_size=10`; it is added on top of the bucket buffer.
|
||||
- With bucketing disabled, set `shuffle_buffer_size=1000` to `10000`, but do not rely on it as the only source of
|
||||
randomness. Shard the data and blend datasets when applicable.
|
||||
- With tarred data, larger buffers and more workers directly increase CPU memory pressure. Segfaults, CPU OOM, or
|
||||
unexplained dataloader errors often indicate CPU memory pressure.
|
||||
- `seed` controls base dataloading RNGs.
|
||||
- `shard_seed` controls sharded/tarred randomization. Default `shard_seed="trng"` gives different non-reproducible
|
||||
orders per run, rank, and worker, but is simpler to manage. Use `shard_seed="randomized"` with a managed `seed` when deterministic
|
||||
dataloading is needed. For 100% determinism, disable `concurrent_bucketing=false` at the cost of longer tarred data bucket prefill.
|
||||
|
||||
## Data Blends
|
||||
|
||||
Use Lhotse `input_cfg` for mixed datasets rather than concatenating manifests blindly.
|
||||
|
||||
Before running helper scripts such as duration-bin estimation, OOMptimizer, or data-weight estimation, validate the
|
||||
input YAML shape they expect in the current checkout. Some scripts consume a list-form input config directly, while
|
||||
training configs may wrap the same list under `input_cfg:`. Run a tiny dry run or inspect the script help before a long
|
||||
OOMptimizer pass.
|
||||
|
||||
For a large generic dataset plus a smaller domain dataset, either manually upweight the domain data or estimate weights
|
||||
then apply temperature reweighting. Lower temperature oversamples smaller datasets; `1.0` is neutral.
|
||||
|
||||
For small-domain adaptation, target-domain real data should usually dominate synthetic or heavily augmented data.
|
||||
Synthetic data can help coverage, but it can also dilute the real-domain signal. Start conservatively, e.g. keep
|
||||
synthetic sources below roughly 30% total weight, evaluate ablations, and only increase synthetic weight when standalone
|
||||
domain WER improves. This applies especially when the synthetic data is generated from TTS, noisy augmentation, or
|
||||
generic prompts rather than real target-domain audio.
|
||||
|
||||
```bash
|
||||
python scripts/speech_recognition/estimate_data_weights.py \
|
||||
generic.yaml domain.yaml blended.yaml \
|
||||
--temperature 0.5 \
|
||||
--strategy num_hours
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
input_cfg:
|
||||
- type: nemo
|
||||
manifest_filepath: /data/generic/manifest__OP_0..512_CL_.json
|
||||
weight: 0.7
|
||||
tags:
|
||||
domain: generic
|
||||
- type: nemo
|
||||
manifest_filepath: /data/domain/manifest__OP_0..128_CL_.json
|
||||
weight: 0.3
|
||||
tags:
|
||||
domain: target
|
||||
```
|
||||
|
||||
For tarred data, use `type: nemo_tarred` with matching `manifest_filepath` and `tarred_audio_filepath`. Avoid mixing
|
||||
tarred and non-tarred inputs in one Lhotse multi-source setup unless the current dataloader docs say the selected mode
|
||||
supports it.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Evaluation Style Contract
|
||||
|
||||
Use this reference whenever reporting WER/CER for an ASR fine-tune.
|
||||
|
||||
## Default Metric
|
||||
|
||||
Unless the user explicitly requests a different metric, evaluate WER with capitalization and punctuation removed. This
|
||||
default keeps model quality comparisons focused on lexical recognition instead of punctuation/casing style drift.
|
||||
|
||||
Still preserve raw prediction manifests. Raw WER is useful as a diagnostic, especially when the fine-tuning labels use
|
||||
a different style than the original checkpoint, but it is not the default success metric for this skill.
|
||||
|
||||
Change the default only when the user asks for raw/cased/punctuated scoring. If the target product requires
|
||||
punctuation, casing, ITN, or symbolic formatting to be part of the ASR metric, treat that as an explicit user
|
||||
requirement and record the changed metric contract.
|
||||
|
||||
## Two-Step Evaluation
|
||||
|
||||
First transcribe with stable decoding, no AMP, and bfloat16 compute:
|
||||
|
||||
```bash
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=/exp/asr-ft/checkpoints/best.nemo \
|
||||
dataset_manifest=/data/test.json \
|
||||
output_filename=/exp/asr-ft/test_predictions.json \
|
||||
batch_size=32 \
|
||||
amp=false \
|
||||
compute_dtype=bfloat16 \
|
||||
matmul_precision=high
|
||||
```
|
||||
|
||||
For RNNT/TDT, enable CUDA graphs when supported:
|
||||
|
||||
```bash
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=/exp/asr-ft/checkpoints/best.nemo \
|
||||
dataset_manifest=/data/test.json \
|
||||
output_filename=/exp/asr-ft/test_predictions.json \
|
||||
batch_size=32 \
|
||||
amp=false \
|
||||
compute_dtype=bfloat16 \
|
||||
matmul_precision=high \
|
||||
rnnt_decoding.strategy=greedy_batch \
|
||||
rnnt_decoding.greedy.use_cuda_graph_decoder=true
|
||||
```
|
||||
|
||||
Then score the saved predictions with the default text-processing contract:
|
||||
|
||||
```bash
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
dataset_manifest=/exp/asr-ft/test_predictions.json \
|
||||
only_score_manifest=true \
|
||||
text_processing.do_lowercase=true \
|
||||
text_processing.rm_punctuation=true \
|
||||
use_cer=false
|
||||
```
|
||||
|
||||
The WER printed by the first transcription command is raw unless the same `text_processing` overrides were passed to
|
||||
that command. Treat the score-only command above as the default metric.
|
||||
|
||||
If the user requested raw scoring, run score-only without `text_processing.do_lowercase` and
|
||||
`text_processing.rm_punctuation`, and label the result as raw WER.
|
||||
|
||||
## Punctuation Coverage
|
||||
|
||||
`text_processing.rm_punctuation=true` removes the punctuation marks configured in
|
||||
`text_processing.punctuation_marks`. If predictions contain punctuation or symbols outside that list, either:
|
||||
|
||||
- Override `text_processing.punctuation_marks` with the full set relevant to the target style.
|
||||
- Or create a derived scored manifest where both `text` and `pred_text` are transformed by the same documented
|
||||
normalizer, then score that manifest.
|
||||
|
||||
Use the second option when the target style removes broad Unicode punctuation/symbol categories, normalizes whitespace,
|
||||
or applies language-specific text normalization that NeMo's built-in punctuation helper does not express.
|
||||
|
||||
## Reporting
|
||||
|
||||
For each model variant, report:
|
||||
|
||||
- Default WER/CER from score-only evaluation with capitalization and punctuation removed.
|
||||
- Raw WER/CER only when it helps explain style mismatch or the user requested it.
|
||||
- The exact prediction manifest and text-processing settings.
|
||||
- Whether the target references were raw labels or derived style-normalized labels.
|
||||
|
||||
If raw WER and default WER differ substantially, treat that as a transcript-style finding before changing training
|
||||
hyperparameters. Do not claim an acoustic/model regression from raw WER alone when the default normalized WER improved.
|
||||
@@ -0,0 +1,132 @@
|
||||
# Stage 5: Refinement And Iteration
|
||||
|
||||
Use this stage after the first fine-tuning run has a standalone evaluation result. The goal is to decide what to change
|
||||
next from evidence, not from guesswork.
|
||||
|
||||
## Evaluation Matrix
|
||||
|
||||
Always compare:
|
||||
|
||||
- Baseline pretrained model on the domain validation/test set.
|
||||
- Fine-tuned model on the same domain validation/test set.
|
||||
|
||||
Add a general or out-of-domain guardrail set only when it matches the user's goal, such as preserving same-language
|
||||
general ASR quality, broad production behavior, or a known existing benchmark. If the user is intentionally changing
|
||||
language, task, script, domain, or deployment scope, do not assume a generic guardrail is meaningful; pick validation
|
||||
sets that reflect the desired behavior.
|
||||
|
||||
Report standalone `speech_to_text_eval.py` WER for each row. In-training `val_wer` is only for checkpoint selection.
|
||||
For small-domain adaptation, avoid optimizing only one tiny target set when the model must still work broadly; track the
|
||||
smallest goal-relevant guardrail set that would reveal unacceptable regressions.
|
||||
|
||||
Example tracking table:
|
||||
|
||||
| Run | Checkpoint | Target WER | Optional Guardrail WER | Notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| baseline | pretrained | | | no fine-tune |
|
||||
| ft-001 | best single | | | first pass |
|
||||
| ft-001-avg | averaged | | | keep only if better |
|
||||
|
||||
## Error Analysis Loop
|
||||
|
||||
1. Transcribe the held-out domain set with the current best checkpoint.
|
||||
2. Compute per-utterance WER/CER and sort from worst to best.
|
||||
3. Before treating raw WER as acoustic/model quality, rescore with the chosen transcript normalization and compare
|
||||
raw vs the default capitalization/punctuation-normalized WER. This is mandatory. A large gap usually means the
|
||||
Stage 2 transcript-style preflight or the evaluation-style contract is incomplete.
|
||||
4. Categorize errors by actionable patterns: numbers, named entities, abbreviations, rare domain words, commands,
|
||||
readbacks, punctuation/capitalization, language/task tags, accents, noise, long utterances, and clipping/VAD issues.
|
||||
5. Count errors by category and inspect representative audio. Separate model errors from label/audio defects.
|
||||
6. Choose one or two interventions for the next run. Avoid changing LR, data mix, tokenizer, and decoding all at once.
|
||||
|
||||
Do not train on validation or test transcripts. If error analysis uses a public or user-visible test set to guide new
|
||||
data generation, create a new blind holdout before claiming final quality.
|
||||
|
||||
## Intervention Choices
|
||||
|
||||
Prefer the least invasive intervention that matches the error pattern:
|
||||
|
||||
- Data issue: fix labels/audio, remove broken samples, adjust `min_duration`, `max_duration`, `min_tps`, or `max_tps`.
|
||||
- Rare vocabulary or entities: add more real examples if available; otherwise add carefully reviewed synthetic examples.
|
||||
- Overfitting or regression: lower LR, reduce `max_steps`, stop at an earlier checkpoint, or increase a generic/guardrail
|
||||
blend only when preserving that behavior is part of the user's objective.
|
||||
- Domain underfitting: raise target-domain real-data weight, add targeted data, or run a lower-LR domain-focus phase.
|
||||
- Decoding issue: compare decoder options, prompts, punctuation/capitalization settings, or CTC/RNNT head for hybrids.
|
||||
- Tokenization issue: revisit tokenizer only when transcript language/domain coverage cannot be represented well by the
|
||||
existing tokenizer.
|
||||
|
||||
## Source-Weighted Rebalancing
|
||||
|
||||
Use source-weighted rebalancing when standalone evaluation shows that a few train sources, domains, or acoustic styles
|
||||
are underfitting and those sources are important to the user's goal. Do not use it to chase a public test set without a
|
||||
new blind holdout.
|
||||
|
||||
Recipe:
|
||||
|
||||
1. Ensure every sample has a stable source/domain tag, either in manifest metadata such as `source_dataset` or in
|
||||
Lhotse `input_cfg.tags`.
|
||||
2. Run standalone evaluation and compute source-wise WER using the default evaluation-style contract.
|
||||
3. Decide whether weights should reflect user priority, source-wise validation errors, dataset hours, or a blend of
|
||||
those signals.
|
||||
4. Apply a floor so low-error sources are not removed entirely, and cap extreme weight jumps unless the user explicitly
|
||||
wants domain-only adaptation.
|
||||
5. Write a new Lhotse `input_cfg` with normalized weights. Prefer changing weights over duplicating manifests.
|
||||
6. Re-run duration-bin estimation and OOMptimizer, or at least verify the previous profile is still valid when the
|
||||
source union is unchanged.
|
||||
7. Fine-tune from the current best checkpoint with a lower LR refinement phase, commonly `1e-5` or `5e-6`.
|
||||
8. Compare the previous best, the rebalanced final checkpoint, the best individual checkpoint, and any averaged model
|
||||
with the same standalone scoring contract.
|
||||
|
||||
When using `estimate_data_weights.py`, validate the YAML shape expected by the current script before running it. If the
|
||||
new weights were influenced by validation/test performance, record that in the ledger. For final claims, evaluate on a
|
||||
holdout that did not influence the reweighting decision.
|
||||
|
||||
## Targeted Synthetic Data
|
||||
|
||||
Synthetic data is most useful when it fills a measured gap. Generate small, targeted batches for the worst categories
|
||||
instead of flooding the run with generic synthetic audio.
|
||||
|
||||
Recommendations:
|
||||
|
||||
- Keep synthetic text TTS-friendly: expand symbols and ambiguous abbreviations when needed, and avoid text forms that a
|
||||
synthesizer will read incorrectly.
|
||||
- Match target-domain acoustics only when the target deployment needs them; generic noise can hurt.
|
||||
- Filter synthetic audio with ASR or manual spot checks before adding it to training.
|
||||
- Add synthetic data as a separately weighted Lhotse input source so it can be ablated.
|
||||
- For small-domain adaptation, keep real target-domain audio dominant unless standalone WER proves otherwise.
|
||||
|
||||
## Run Ledger
|
||||
|
||||
Use `assets/experiment-ledger-template.md` as the starting template for experiment notes.
|
||||
|
||||
Maintain a compact run ledger with:
|
||||
|
||||
- Data sources and blend weights.
|
||||
- Transcript style policy, any manifest transform, and whether it differs from the original checkpoint output style.
|
||||
- LR, `max_steps`, warmup, precision, and batch profile.
|
||||
- Duration/token filters and number of examples filtered.
|
||||
- Final `.nemo`, best individual checkpoint-derived artifact, averaged artifact, and the keep/drop decision.
|
||||
- Raw WER/CER and default capitalization/punctuation-normalized WER/CER when both are informative.
|
||||
- Target-set WER and any goal-relevant guardrail WER.
|
||||
- GPU utilization evidence from training steps, not validation/checkpoint/export windows.
|
||||
- Decision for the next run.
|
||||
|
||||
## Late-Stage Curriculum Pattern
|
||||
|
||||
Use curriculum-style staged fine-tuning only after simpler refinements stop improving standalone WER. Try data cleanup,
|
||||
filter fixes, blend-weight changes, targeted data additions, decoding choices, and tokenizer decisions first.
|
||||
|
||||
For small-data domain adaptation, use short lower-LR phases instead of one long aggressive run:
|
||||
|
||||
1. Foundation phase: preserve required broad behavior with a mix of source and target-domain data when that is a goal.
|
||||
2. Domain-focus phase: increase real target-domain and targeted data weight, lower LR.
|
||||
3. Final refinement phase: lower LR again and evaluate carefully; this phase can regress.
|
||||
|
||||
Typical starting points:
|
||||
|
||||
- First small-domain phase: `model.optim.lr=3e-5`.
|
||||
- Follow-up domain-focus phase: `model.optim.lr=1e-5`.
|
||||
- Final refinement phase: `model.optim.lr=5e-6` or lower.
|
||||
|
||||
Use `trainer.max_steps` for each phase, checkpoint by `val_wer`, and run standalone evaluation after each phase. Keep
|
||||
the best phase, not necessarily the last phase.
|
||||
@@ -0,0 +1,98 @@
|
||||
# Stage 1: Setup And Checkpoint Selection
|
||||
|
||||
Default to the NeMo release container. Use local execution only when the user explicitly asks for it.
|
||||
|
||||
Check `README.md`, `docker/Dockerfile.speech`, and the NGC catalog before quoting container tags. The container avoids
|
||||
most local CUDA, torch, audio backend, `sox`, `ffmpeg`, and `libsndfile` issues.
|
||||
|
||||
When running repo scripts from a mounted source checkout inside a container, ensure Python imports the mounted checkout,
|
||||
not an unrelated package already installed in the image:
|
||||
|
||||
```bash
|
||||
export PYTHONPATH=/workspace/NeMo
|
||||
python -c "import nemo.collections.asr as asr; print(asr.__file__)"
|
||||
```
|
||||
|
||||
If the source checkout must be installed in the container, prefer `pip install -e '.[asr]'`.
|
||||
|
||||
For Hugging Face models or datasets that require authentication, verify access before launching long jobs. Containers
|
||||
often do not include `huggingface-cli`; mounting a populated `HF_HOME` or token/cache directory is usually enough:
|
||||
|
||||
```bash
|
||||
docker run -e HF_HOME=/tmp/hf_home -v /host/hf_home:/tmp/hf_home ...
|
||||
```
|
||||
|
||||
For explicit local setup, verify:
|
||||
|
||||
```bash
|
||||
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"
|
||||
python -c "import nemo.collections.asr as asr; print(asr.__file__)"
|
||||
nvidia-smi
|
||||
```
|
||||
|
||||
For a source checkout local install, prefer:
|
||||
|
||||
```bash
|
||||
pip install -e '.[asr]'
|
||||
```
|
||||
|
||||
If CUDA, torch, audio backend, or dependency issues appear, steer back to the container.
|
||||
|
||||
## Preflight Checks
|
||||
|
||||
Before launching a long fine-tune, spend a few minutes on cheap failure checks:
|
||||
|
||||
- Confirm the intended NeMo checkout is imported from inside the container.
|
||||
- Confirm each training/validation manifest exists, has non-empty `text`, valid `audio_filepath`, and usable
|
||||
`duration` values.
|
||||
- Run the transcript-style preflight in `data-lhotse.md` before training. Mixed casing, punctuation, number, or symbol
|
||||
conventions across fine-tuning sources are an experiment setup issue, not a tuning detail.
|
||||
- Inspect duration and token-per-second distributions before setting filters; Lhotse filtering is silent.
|
||||
- Check GPU state with `nvidia-smi` and stop stale training processes before sizing batches.
|
||||
- Check free disk space for experiment logs and checkpoints. Large `.nemo` exports, `save_top_k`, checkpoint averaging,
|
||||
and EMA can produce tens of GB per run.
|
||||
- Confirm `HF_HOME`/tokens and any object-store credentials are visible inside the container before launching jobs that
|
||||
will download checkpoints or data.
|
||||
|
||||
Useful quick checks:
|
||||
|
||||
```bash
|
||||
nvidia-smi
|
||||
df -h /exp /tmp /data
|
||||
python - /data/train.json <<'PY'
|
||||
import json, pathlib, statistics, sys
|
||||
manifest = pathlib.Path(sys.argv[1])
|
||||
durations, missing_audio, empty_text = [], 0, 0
|
||||
for line in manifest.open():
|
||||
item = json.loads(line)
|
||||
if not pathlib.Path(item["audio_filepath"]).exists():
|
||||
missing_audio += 1
|
||||
if not item.get("text", "").strip():
|
||||
empty_text += 1
|
||||
if "duration" in item:
|
||||
durations.append(float(item["duration"]))
|
||||
print({"items": len(durations), "missing_audio": missing_audio, "empty_text": empty_text})
|
||||
if durations:
|
||||
print({"hours": sum(durations) / 3600, "min": min(durations), "p50": statistics.median(durations), "max": max(durations)})
|
||||
PY
|
||||
```
|
||||
|
||||
## Checkpoint Candidates
|
||||
|
||||
Use the user's constraints first: language, streaming vs offline, latency, memory budget, punctuation/capitalization,
|
||||
translation, speaker overlap, CTC alignments, and deployment target.
|
||||
|
||||
Good candidates from local docs:
|
||||
|
||||
- `nvidia/parakeet-tdt-0.6b-v3` or `nvidia/parakeet-tdt-0.6b-v2`: general Parakeet/TDT fine-tuning. Check the
|
||||
model card for exact language and punctuation support.
|
||||
- `nvidia/parakeet-tdt_ctc-110m`: compact hybrid candidate.
|
||||
- `nvidia/parakeet-ctc-0.6b` or `nvidia/parakeet-ctc-1.1b`: CTC when simple non-autoregressive decoding, alignments,
|
||||
or vocabulary replacement matter.
|
||||
- `nvidia/nemotron-speech-streaming-en-0.6b`: real-time English streaming.
|
||||
- `nvidia/multitalker-parakeet-streaming-0.6b-v1`: streaming multi-speaker input.
|
||||
- `nvidia/canary-1b-v2`: AED/Canary for multilingual ASR, speech translation, and prompt-controlled behavior.
|
||||
- `nvidia/canary-1b-flash` or `nvidia/canary-180m-flash`: faster Canary-family models.
|
||||
|
||||
Always check current `README.md`, `docs/source/asr/featured_models.rst`, `docs/source/asr/asr_checkpoints.rst`, and
|
||||
the model card or HF collection before finalizing the checkpoint list.
|
||||
@@ -0,0 +1,216 @@
|
||||
# Stage 4: Training, Checkpoint Averaging, And Evaluation
|
||||
|
||||
## Optimizer And Trainer
|
||||
|
||||
Use `trainer.max_steps`, not `trainer.max_epochs`. Use a cosine LR schedule with the same `max_steps` as the trainer.
|
||||
Choose the initial LR by data size and risk:
|
||||
|
||||
- For large or mixed fine-tuning runs, start with `model.optim.lr=1e-4`, then tune.
|
||||
- For small domain adaptation, especially below roughly 20 hours of high-value target-domain audio, start around
|
||||
`3e-5` and watch early validation closely.
|
||||
- For later refinement phases or unstable/diverging runs, try `1e-5` or lower.
|
||||
|
||||
Set warmup to 1-2% of `trainer.max_steps`.
|
||||
|
||||
```bash
|
||||
+init_from_pretrained_model=<hf-or-ngc-name> \
|
||||
trainer.max_steps=50000 \
|
||||
+trainer.limit_train_batches=1000 \
|
||||
trainer.val_check_interval=1000 \
|
||||
model.optim.lr=1e-4 \
|
||||
model.optim.sched.name=CosineAnnealing \
|
||||
++model.optim.sched.max_steps=50000 \
|
||||
model.optim.sched.warmup_steps=500 \
|
||||
model.optim.sched.min_lr=5e-6
|
||||
```
|
||||
|
||||
`examples/asr/speech_to_text_finetune.py` supports `init_from_pretrained_model`, but some fine-tune YAMLs do not
|
||||
declare that key. Use `+init_from_pretrained_model=...` when Hydra says the key is not in struct. Use `+` or `++` for
|
||||
other trainer/model keys that are valid for the script but absent from the selected YAML; do not remove the plus just
|
||||
because a similar key exists in another config.
|
||||
|
||||
Prefer `trainer.precision=bf16-true` for memory savings and larger batch sizes, especially for datasets over 1k
|
||||
hours. If it diverges or has stability issues, fall back to a more stable precision mode. Prefer `bf16-true` over
|
||||
`bf16-mixed` as the first bfloat16 option.
|
||||
|
||||
Monitor `val_wer` for all validation runs and checkpoint selection:
|
||||
|
||||
```bash
|
||||
exp_manager.checkpoint_callback_params.monitor=val_wer \
|
||||
exp_manager.checkpoint_callback_params.mode=min \
|
||||
exp_manager.checkpoint_callback_params.save_top_k=5 \
|
||||
exp_manager.checkpoint_callback_params.always_save_nemo=true
|
||||
```
|
||||
|
||||
## Multi-GPU Launch Troubleshooting
|
||||
|
||||
For routine single-node runs, start with the normal Lightning launch (`trainer.devices=<num_gpus>`). If a container run
|
||||
hangs immediately after NCCL registration or one rank restores the model on the wrong GPU before DDP takes ownership,
|
||||
use `torchrun --nproc_per_node=<num_gpus>` with the normal script entry point. Keep `trainer.devices=<num_gpus>` and
|
||||
set `trainer.use_distributed_sampler=false` for Lhotse.
|
||||
|
||||
Container `torchrun` template:
|
||||
|
||||
```bash
|
||||
docker run --rm --gpus all --ipc=host \
|
||||
--ulimit memlock=-1 --ulimit stack=67108864 \
|
||||
-e PYTHONPATH=/workspace/NeMo \
|
||||
-v /path/to/NeMo:/workspace/NeMo \
|
||||
-v /data:/data \
|
||||
nemo-speech:<tag> bash -lc '
|
||||
cd /workspace/NeMo &&
|
||||
torchrun --nproc_per_node=2 examples/asr/speech_to_text_finetune.py \
|
||||
+init_from_pretrained_model=nvidia/parakeet-tdt-0.6b-v3 \
|
||||
model.train_ds.manifest_filepath=/data/train.json \
|
||||
model.validation_ds.manifest_filepath=/data/val.json \
|
||||
++model.train_ds.use_lhotse=true \
|
||||
++model.validation_ds.use_lhotse=true \
|
||||
+trainer.use_distributed_sampler=false \
|
||||
trainer.devices=2 \
|
||||
trainer.max_steps=10000
|
||||
'
|
||||
```
|
||||
|
||||
Keep `trainer.num_nodes` for real multi-node jobs only. For single-node container fine-tuning, avoid manual
|
||||
one-visible-GPU-per-process launch patterns unless the user explicitly asks to debug that environment.
|
||||
|
||||
On systems with NCCL P2P or CUDA memory allocator issues, retry with conservative distributed environment settings
|
||||
after confirming the job is stuck in communication setup:
|
||||
|
||||
```bash
|
||||
export NCCL_CUMEM_ENABLE=0
|
||||
export NCCL_P2P_DISABLE=1
|
||||
```
|
||||
|
||||
Keep `trainer.sync_batchnorm` the same as it was set in the original model's config unless the user explicitly asks to
|
||||
change it.
|
||||
|
||||
## Checkpoint Averaging
|
||||
|
||||
At the end of a run, optionally average the N best checkpoints saved by `save_top_k=N`. Then evaluate the averaged
|
||||
model and keep it only if it beats the best individual checkpoint on validation/test.
|
||||
|
||||
The simple `.nemo` averaging utility averages all non-`-last.ckpt` checkpoints in the same folder as the `.nemo` file,
|
||||
so control N with `save_top_k`:
|
||||
|
||||
```bash
|
||||
python scripts/checkpoint_averaging/checkpoint_averaging.py \
|
||||
/exp/asr-ft/checkpoints/best.nemo
|
||||
```
|
||||
|
||||
This produces `*-averaged.nemo`. If model class loading fails, use the script's `--class_path` or `--import_fname_list`
|
||||
options. Some checkpoint averaging scripts are marked deprecated in the repo; verify they still work in the current
|
||||
checkout before relying on them.
|
||||
|
||||
With PyTorch 2.6+, the deprecated averaging utility may fail because `torch.load` defaults to `weights_only=True`.
|
||||
Only for checkpoints you trust, rerun with:
|
||||
|
||||
```bash
|
||||
TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD=1 python scripts/checkpoint_averaging/checkpoint_averaging.py \
|
||||
/exp/asr-ft/checkpoints/best.nemo
|
||||
```
|
||||
|
||||
Do not assume the latest `.nemo` is the best validation checkpoint. `always_save_nemo=true` can overwrite the `.nemo`
|
||||
artifact on later saves. Check the `.ckpt` filenames and validation logs, then evaluate the exported `.nemo`, the best
|
||||
checkpoint-derived artifact, and any averaged model before deciding what to keep.
|
||||
|
||||
Decision rule: evaluate the final exported `.nemo`, the best individual validation checkpoint-derived artifact, and the
|
||||
averaged artifact with the same standalone command and scoring contract. Keep the artifact with the best standalone
|
||||
default WER. If averaging is worse, record it and drop the averaged artifact.
|
||||
|
||||
Always run the same standalone evaluation command for the baseline model, the final exported `.nemo`, the best
|
||||
checkpoint-derived artifact, and any averaged model. In-training `val_wer` is useful for checkpoint selection, but it is
|
||||
not the final number to report. Standalone WER is the fair comparison because it holds decoding options, text
|
||||
processing, precision, and output scoring constant.
|
||||
|
||||
Evaluate both:
|
||||
|
||||
```bash
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=/exp/asr-ft/checkpoints/best.nemo \
|
||||
dataset_manifest=/data/val.json \
|
||||
output_filename=/exp/asr-ft/val_best.json \
|
||||
batch_size=32 \
|
||||
amp=false \
|
||||
compute_dtype=bfloat16
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=/exp/asr-ft/checkpoints/best-averaged.nemo \
|
||||
dataset_manifest=/data/val.json \
|
||||
output_filename=/exp/asr-ft/val_avg.json \
|
||||
batch_size=32 \
|
||||
amp=false \
|
||||
compute_dtype=bfloat16
|
||||
```
|
||||
|
||||
## Evaluation
|
||||
|
||||
Do not use AMP for inference/evaluation. Use `compute_dtype=bfloat16` and `amp=false`. Report standalone
|
||||
`speech_to_text_eval.py` results for every model variant being compared; do not report only trainer logs.
|
||||
|
||||
Use `evaluation-style-contract.md` for scoring. By default, report WER with capitalization and punctuation removed via
|
||||
score-only evaluation of the saved prediction manifest. Report raw WER separately only when the user requests it or it
|
||||
helps diagnose transcript-style mismatch.
|
||||
|
||||
```bash
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=/exp/asr-ft/checkpoints/best.nemo \
|
||||
dataset_manifest=/data/test.json \
|
||||
output_filename=/exp/asr-ft/test_predictions.json \
|
||||
batch_size=32 \
|
||||
amp=false \
|
||||
compute_dtype=bfloat16 \
|
||||
matmul_precision=high \
|
||||
use_cer=False
|
||||
```
|
||||
|
||||
For RNNT/TDT evaluation, enable CUDA graphs when supported:
|
||||
|
||||
```bash
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=/exp/asr-ft/checkpoints/best.nemo \
|
||||
dataset_manifest=/data/test.json \
|
||||
output_filename=/exp/asr-ft/test_predictions.json \
|
||||
batch_size=32 \
|
||||
amp=false \
|
||||
compute_dtype=bfloat16 \
|
||||
matmul_precision=high \
|
||||
rnnt_decoding.strategy=greedy_batch \
|
||||
rnnt_decoding.greedy.use_cuda_graph_decoder=true
|
||||
```
|
||||
|
||||
For a hybrid model, compare decoder choices:
|
||||
|
||||
```bash
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=/exp/asr-ft/checkpoints/best.nemo \
|
||||
dataset_manifest=/data/test.json \
|
||||
decoder_type=ctc \
|
||||
output_filename=/exp/asr-ft/test_predictions_ctc.json \
|
||||
amp=false \
|
||||
compute_dtype=bfloat16
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=/exp/asr-ft/checkpoints/best.nemo \
|
||||
dataset_manifest=/data/test.json \
|
||||
decoder_type=rnnt \
|
||||
output_filename=/exp/asr-ft/test_predictions_rnnt.json \
|
||||
amp=false \
|
||||
compute_dtype=bfloat16 \
|
||||
rnnt_decoding.strategy=greedy_batch \
|
||||
rnnt_decoding.greedy.use_cuda_graph_decoder=true
|
||||
```
|
||||
|
||||
If predictions already exist:
|
||||
|
||||
```bash
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
dataset_manifest=/exp/asr-ft/test_predictions.json \
|
||||
only_score_manifest=True \
|
||||
text_processing.do_lowercase=true \
|
||||
text_processing.rm_punctuation=true \
|
||||
use_cer=False
|
||||
```
|
||||
|
||||
Use `examples/asr/transcribe_speech.py` for direct offline transcription and streaming or chunked inference scripts for
|
||||
streaming models.
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: verify
|
||||
description: Run style checks and tests on changed files to verify code quality before committing.
|
||||
---
|
||||
|
||||
Run verification on the current changes:
|
||||
|
||||
1. **Find changed files**:
|
||||
```bash
|
||||
git diff --name-only HEAD
|
||||
```
|
||||
Also include any files you've been editing in this session.
|
||||
|
||||
2. **Style check** on each changed Python file or its parent directory:
|
||||
```bash
|
||||
python setup.py style --scope <path>
|
||||
```
|
||||
If issues are found, fix them with `--fix` and report what changed.
|
||||
|
||||
3. **Run relevant tests** based on which collection was modified:
|
||||
- `nemo/collections/asr/` → `pytest tests/collections/asr --download -m "not pleasefixme" -v --timeout=300`
|
||||
- `nemo/collections/tts/` → `pytest tests/collections/tts --download -m "not pleasefixme" -v --timeout=300`
|
||||
- `nemo/collections/audio/` → `pytest tests/collections/audio --download -m "not pleasefixme" -v --timeout=300`
|
||||
- `nemo/collections/speechlm2/` → `pytest tests/collections/speechlm2 -m "not pleasefixme" -v --timeout=300`
|
||||
- `nemo/collections/common/` → `pytest tests/collections/common -m "not pleasefixme" -v --timeout=300`
|
||||
- `nemo/core/` → `pytest tests/core -m "not pleasefixme" -v --timeout=300`
|
||||
|
||||
4. **Report results**: summarize passes and failures. For failures, show relevant error output and suggest fixes.
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../.claude/skills
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
[run]
|
||||
concurrency = thread,multiprocessing
|
||||
omit =
|
||||
/tmp/*
|
||||
/home/TestData/*
|
||||
/workspace/Megatron-LM/*
|
||||
nemo/collections/multimodal/*
|
||||
nemo/collections/multimodal_autoregressive/*
|
||||
nemo/collections/vision/*
|
||||
nemo/collections/diffusion/*
|
||||
nemo/collections/nlp/*
|
||||
|
||||
nemo/collections/asr/*
|
||||
nemo/collections/speechlm/*
|
||||
nemo/collections/tts/*
|
||||
|
||||
# omit from audio
|
||||
nemo/collections/audio/data/data_simulation.py
|
||||
nemo/collections/audio/metrics/squim.py
|
||||
nemo/collections/audio/losses/maxine/*
|
||||
nemo/collections/audio/models/maxine/*
|
||||
nemo/collections/audio/parts/utils/maxine.py
|
||||
|
||||
nemo/core/*
|
||||
nemo/collections/common/*
|
||||
|
||||
/workspace/config-3.12.py
|
||||
/workspace/config-3.py
|
||||
/workspace/config.py
|
||||
/workspace/_remote_module_non_scriptable*
|
||||
|
||||
[report]
|
||||
omit =
|
||||
/workspace/_remote_module_non_scriptable*
|
||||
|
||||
[paths]
|
||||
source =
|
||||
nemo/
|
||||
/home/runner/work/NeMo/NeMo/nemo
|
||||
/workspace/nemo
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../.claude/skills
|
||||
@@ -0,0 +1,19 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
env
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
.tox
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
*.log
|
||||
.git
|
||||
**/*.nemo
|
||||
**/*.ckpt
|
||||
@@ -0,0 +1,9 @@
|
||||
[flake8]
|
||||
max-line-length = 119
|
||||
select =
|
||||
F541, # f-string without any placeholders
|
||||
F841, # local variable 'x' is assigned to but never used
|
||||
F401, # 'x' imported but unused
|
||||
E741, # ambiguous variable name 'l'
|
||||
F821, # undefined name 'x'
|
||||
E266, # too many leading '#' for block comment
|
||||
@@ -0,0 +1,9 @@
|
||||
[flake8]
|
||||
max-line-length = 119
|
||||
select =
|
||||
F541, # f-string without any placeholders
|
||||
F841, # local variable 'x' is assigned to but never used
|
||||
F401, # 'x' imported but unused
|
||||
E741, # ambiguous variable name 'l'
|
||||
F821, # undefined name 'x'
|
||||
E266, # too many leading '#' for block comment
|
||||
@@ -0,0 +1,9 @@
|
||||
[flake8]
|
||||
max-line-length = 119
|
||||
select =
|
||||
F541, # f-string without any placeholders
|
||||
F841, # local variable 'x' is assigned to but never used
|
||||
F401, # 'x' imported but unused
|
||||
E741, # ambiguous variable name 'l'
|
||||
F821, # undefined name 'x'
|
||||
E266, # too many leading '#' for block comment
|
||||
@@ -0,0 +1,6 @@
|
||||
.github/ @nvidia-nemo/automation
|
||||
docker/ @nvidia-nemo/automation
|
||||
requirements/ @nvidia-nemo/automation
|
||||
pyproject.toml @nvidia-nemo/automation
|
||||
setup.py @nvidia-nemo/automation
|
||||
uv.lock @nvidia-nemo/automation
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Steps/Code to reproduce bug**
|
||||
|
||||
Please list *minimal* steps or code snippet for us to be able to reproduce the bug.
|
||||
|
||||
A helpful guide on on how to craft a minimal bug report http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports.
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Environment overview (please complete the following information)**
|
||||
|
||||
- Environment location: [Bare-metal, Docker, Cloud(specify cloud provider - AWS, Azure, GCP, Collab)]
|
||||
- Method of NeMo install: [pip install or from source]. Please specify exact commands you used to install.
|
||||
- If method of install is [Docker], provide `docker pull` & `docker run` commands used
|
||||
|
||||
**Environment details**
|
||||
|
||||
If NVIDIA docker image is used you don't need to specify these.
|
||||
Otherwise, please provide:
|
||||
- OS version
|
||||
- PyTorch version
|
||||
- Python version
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context about the problem here.
|
||||
Example: GPU model
|
||||
@@ -0,0 +1,2 @@
|
||||
blank_issues_enabled: false
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
container pulled on date: mm/dd/yyyy
|
||||
name: Dev container - Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Steps/Code to reproduce bug**
|
||||
|
||||
Please list *minimal* steps or code snippet for us to be able to reproduce the bug.
|
||||
|
||||
A helpful guide on on how to craft a minimal bug report http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports.
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Environment overview (please complete the following information)**
|
||||
|
||||
- Environment location: Docker
|
||||
- Method of install: Please specify exact commands you used to install.
|
||||
- If method of install is [Docker], provide `docker pull` & `docker run` commands used
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context about the problem here.
|
||||
Example: GPU model
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
|
||||
A clear and concise description of what you want to happen.
|
||||
Provide a code snippet on how new APIs/changes would be used by others.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,57 @@
|
||||
> [!IMPORTANT]
|
||||
> The `Update branch` button must only be pressed in very rare occassions.
|
||||
> An outdated branch is never blocking the merge of a PR.
|
||||
> Please reach out to the automation team before pressing that button.
|
||||
|
||||
# What does this PR do ?
|
||||
|
||||
Add a one line overview of what this PR aims to accomplish.
|
||||
|
||||
**Collection**: [Note which collection this PR will affect]
|
||||
|
||||
# Changelog
|
||||
|
||||
- Add specific line by line info of high level changes in this PR.
|
||||
|
||||
# Usage
|
||||
|
||||
- You can potentially add a usage example below
|
||||
|
||||
```python
|
||||
# Add a code snippet demonstrating how to use this
|
||||
```
|
||||
|
||||
# GitHub Actions CI
|
||||
|
||||
The Jenkins CI system has been replaced by GitHub Actions self-hosted runners.
|
||||
|
||||
The GitHub Actions CI will run automatically when the "Run CICD" label is added to the PR.
|
||||
To re-run CI remove and add the label again.
|
||||
To run CI on an untrusted fork, a NeMo user with write access must first click "Approve and run".
|
||||
|
||||
# Before your PR is "Ready for review"
|
||||
|
||||
**Pre checks**:
|
||||
|
||||
- [ ] Make sure you read and followed [Contributor guidelines](https://github.com/NVIDIA/NeMo/blob/main/CONTRIBUTING.md)
|
||||
- [ ] Did you write any new necessary tests?
|
||||
- [ ] Did you add or update any necessary documentation?
|
||||
- [ ] Does the PR affect components that are optional to install? (Ex: Numba, Pynini, Apex etc)
|
||||
- [ ] Reviewer: Does the PR have correct import guards for all optional libraries?
|
||||
|
||||
**PR Type**:
|
||||
|
||||
- [ ] New Feature
|
||||
- [ ] Bugfix
|
||||
- [ ] Documentation
|
||||
|
||||
If you haven't finished some of the above items you can still open "Draft" PR.
|
||||
|
||||
## Who can review?
|
||||
|
||||
Anyone in the community is free to review the PR once the checks have passed.
|
||||
[Contributor guidelines](https://github.com/NVIDIA/NeMo/blob/main/CONTRIBUTING.md) contains specific people who can review PRs to various areas.
|
||||
|
||||
# Additional Information
|
||||
|
||||
- Related to # (issue)
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Cancel Workflow
|
||||
description: >
|
||||
Cancels the current workflow run, i.e. all jobs. Useful if you want to cancel the rest of the workflow when one job
|
||||
fails. Note that this will cause the workflow to appear cancelled, not failed.
|
||||
|
||||
# Cancelling the workflow in a post-script (like this:
|
||||
# https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runspost; can also be done with
|
||||
# this action: https://github.com/webiny/action-post-run, see Git history of this file) wouldn't help the status, it
|
||||
# would still be cancelled. It actually indeed is, but it would be nicer to set it to failed, but there seems to be no
|
||||
# way to do this.
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Cancel Workflow
|
||||
# # Fork PRs won't have a token with write access to Actions, thus won't be able to cancel the workflow.
|
||||
# if: github.event.pull_request == '' || github.event.pull_request.head.repo.fork == false
|
||||
shell: bash
|
||||
run: |
|
||||
curl --verbose \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ github.token }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/cancel
|
||||
@@ -0,0 +1,234 @@
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: "Test Template"
|
||||
description: "Template for running NeMo tests in a containerized environment"
|
||||
|
||||
inputs:
|
||||
runner:
|
||||
description: "Runner to use for test"
|
||||
required: true
|
||||
timeout:
|
||||
description: "Max runtime of test in minutes"
|
||||
required: false
|
||||
default: "10"
|
||||
script:
|
||||
description: "Test script to execute"
|
||||
required: true
|
||||
after_script:
|
||||
description: "Script to run after main test"
|
||||
required: false
|
||||
default: ":"
|
||||
is_optional:
|
||||
description: "Failure will cancel all other tests if set to true"
|
||||
required: false
|
||||
default: "false"
|
||||
is_unit_test:
|
||||
description: "Upload coverage as unit test"
|
||||
required: false
|
||||
default: "false"
|
||||
tests_to_run:
|
||||
description: "Tests to run"
|
||||
required: false
|
||||
default: '["all"]'
|
||||
image:
|
||||
description: "Full image URL with tag (e.g., 766267172432.dkr.ecr.us-east-1.amazonaws.com/nemo-speech:nemo_container-12345)"
|
||||
required: true
|
||||
test-data-path:
|
||||
description: "Host path to mount as /home/TestData inside the container"
|
||||
required: false
|
||||
default: /mnt/datadrive/TestData
|
||||
cpu-only:
|
||||
description: "Run tests on CPU only"
|
||||
required: false
|
||||
default: "false"
|
||||
test_dir:
|
||||
description: "Directory under tests/ containing the test scripts"
|
||||
required: false
|
||||
default: "functional_tests"
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Noop
|
||||
shell: bash
|
||||
run: |
|
||||
chmod -R u+rwX ${{ github.run_id }}
|
||||
echo "noop"
|
||||
|
||||
- name: Docker system cleanup
|
||||
shell: bash
|
||||
run: |
|
||||
docker system prune -af --filter "until=24h" --filter "label!=nemo.pr_number=${{ github.event.pull_request.number || 0 }}" --force || true
|
||||
|
||||
- name: Docker pull image
|
||||
shell: bash
|
||||
run: |
|
||||
docker pull ${{ inputs.image }}
|
||||
|
||||
- name: Clean repos
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
- name: Create UUID
|
||||
id: uuid
|
||||
shell: bash
|
||||
run: |
|
||||
echo "id=$(uuidgen)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
env:
|
||||
DIR: ${{ github.run_id }}
|
||||
with:
|
||||
path: ${{ github.run_id }}/${{steps.uuid.outputs.id }}/NeMo
|
||||
|
||||
- name: Start container
|
||||
shell: bash
|
||||
env:
|
||||
DIR: ${{ github.run_id }}
|
||||
run: |
|
||||
mkdir -p $DIR
|
||||
|
||||
# Map of runner names to GPU device configurations
|
||||
declare -A GPU_CONFIGS=(
|
||||
["myVm-01"]="0,1"
|
||||
["myVm-02"]="2,3"
|
||||
["myVm-03"]="4,5"
|
||||
["myVm-04"]="6,7"
|
||||
)
|
||||
|
||||
ARG=("")
|
||||
if [[ "${{ inputs.cpu-only }}" == "false" ]]; then
|
||||
ARG=("--runtime=nvidia --gpus all")
|
||||
fi
|
||||
|
||||
cmd=$(cat <<RUN_TEST_EOF
|
||||
#!/bin/bash
|
||||
docker container rm -f nemo_container_${{ github.run_id }}_${{ inputs.runner }} || true
|
||||
docker run \
|
||||
--rm \
|
||||
-d \
|
||||
--name nemo_container_${{ github.run_id }}_${{ inputs.runner }} ${ARG[@]} \
|
||||
--shm-size=64g \
|
||||
--env TRANSFORMERS_OFFLINE=0 \
|
||||
--env NEMO_HOME="/home/TestData/nemo_home" \
|
||||
--env HYDRA_FULL_ERROR=1 \
|
||||
--env HF_HOME=/home/TestData/HF_HOME \
|
||||
--env RUN_ID=${{ github.run_id }} \
|
||||
--volume $(pwd)/${{ github.run_id }}/${{steps.uuid.outputs.id }}/NeMo:/workspace \
|
||||
--volume ${{ inputs.test-data-path }}:/home/TestData ${{ inputs.image }} \
|
||||
bash -c "sleep $(( ${{ inputs.timeout }} * 60 + 60 ))"
|
||||
RUN_TEST_EOF
|
||||
)
|
||||
|
||||
echo "$cmd" | tee "$DIR/retry_job.sh"
|
||||
bash $DIR/retry_job.sh
|
||||
|
||||
- name: Create run-script
|
||||
id: create
|
||||
env:
|
||||
DIR: ${{ github.run_id }}
|
||||
shell: bash
|
||||
run: |
|
||||
COVERAGE_PREFIX=$([[ "${{ inputs.is_unit_test }}" == "true" ]] && echo "unit-test" || echo "e2e")
|
||||
echo "coverage-prefix=$COVERAGE_PREFIX" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
mkdir -p $DIR
|
||||
rm $DIR/.coverage || true
|
||||
rm $DIR/err.log || true
|
||||
|
||||
cmd=$(cat <<RUN_TEST_EOF
|
||||
#!/bin/bash
|
||||
|
||||
(
|
||||
set -e
|
||||
|
||||
docker exec -t nemo_container_${{ github.run_id }}_${{ inputs.runner }} bash -c '\
|
||||
bash tests/${{ inputs.test_dir }}/${{ inputs.script }}.sh && \
|
||||
echo "Finished successfully." || echo "Did not finish."'
|
||||
) 2>&1 | tee $DIR/err.log
|
||||
|
||||
RUN_TEST_EOF
|
||||
)
|
||||
|
||||
echo "timeout_in_seconds=$(( ${{ inputs.timeout }} * 60 ))" | tee -a "$GITHUB_OUTPUT"
|
||||
echo "$cmd" | tee "$DIR/job.sh"
|
||||
|
||||
- name: Run main script
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_seconds: ${{ steps.create.outputs.timeout_in_seconds }}
|
||||
max_attempts: 3
|
||||
shell: bash
|
||||
retry_on: timeout
|
||||
command: /bin/bash ${{ github.run_id }}/job.sh
|
||||
on_retry_command: /bin/bash ${{ github.run_id }}/retry_job.sh
|
||||
|
||||
- name: Check result
|
||||
id: check
|
||||
shell: bash
|
||||
env:
|
||||
DIR: ${{ github.run_id }}
|
||||
run: |
|
||||
cat $DIR/err.log
|
||||
|
||||
log=$(tail -c 2000 $DIR/err.log | base64 -w 0)
|
||||
echo "log=$log" >> "$GITHUB_OUTPUT"
|
||||
|
||||
potential_infra_failure=$(cat $DIR/err.log | grep -Eqiw "device" && echo true || echo false)
|
||||
echo "potential_infra_failure=$potential_infra_failure" >> "$GITHUB_OUTPUT"
|
||||
|
||||
docker exec nemo_container_${{ github.run_id }}_${{ inputs.runner }} coverage combine
|
||||
docker exec nemo_container_${{ github.run_id }}_${{ inputs.runner }} coverage xml
|
||||
docker cp nemo_container_${{ github.run_id }}_${{ inputs.runner }}:/workspace/.coverage $DIR/.coverage
|
||||
docker cp nemo_container_${{ github.run_id }}_${{ inputs.runner }}:/workspace/coverage.xml $DIR/coverage.xml
|
||||
|
||||
coverage_report=coverage-${{ steps.create.outputs.coverage-prefix }}-${{ github.run_id }}-$(python3 -c "import uuid; print(uuid.uuid4())")
|
||||
echo "coverage_report=$coverage_report" >> "$GITHUB_OUTPUT"
|
||||
|
||||
IS_SUCCESS=$(tail -n 1 $DIR/err.log | grep -q "Finished successfully." && echo "true" || echo "false")
|
||||
|
||||
if [[ "$IS_SUCCESS" == "false" && "${{ inputs.is_optional }}" == "true" ]]; then
|
||||
echo "::warning:: Test failed, but displayed as successful because it is marked as optional."
|
||||
IS_SUCCESS=true
|
||||
fi
|
||||
|
||||
if [[ "$IS_SUCCESS" == "false" ]]; then
|
||||
echo Test did not finish successfully.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
|
||||
- name: Test coverage
|
||||
shell: bash -x -e -u -o pipefail {0}
|
||||
run: |
|
||||
docker exec -t nemo_container_${{ github.run_id }}_${{ inputs.runner }} coverage report -i
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
if: ${{ steps.check.outputs.coverage_report != 'none' }}
|
||||
with:
|
||||
name: ${{ steps.check.outputs.coverage_report }}
|
||||
path: |
|
||||
${{ github.run_id }}/coverage.xml
|
||||
${{ github.run_id }}/.coverage
|
||||
include-hidden-files: true
|
||||
|
||||
- name: Container shutdown
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
docker exec nemo_container_${{ github.run_id }}_${{ inputs.runner }} bash -c "chown -R $(id -u):$(id -g) /workspace"
|
||||
rm -rf $(pwd)/${{ github.run_id }}/${{steps.uuid.outputs.id }} || true
|
||||
docker container rm -f nemo_container_${{ github.run_id }}_${{ inputs.runner }} || true
|
||||
@@ -0,0 +1,3 @@
|
||||
enabled: true
|
||||
auto_sync_draft: false
|
||||
auto_sync_ready: true
|
||||
@@ -0,0 +1,41 @@
|
||||
ASR:
|
||||
- nemo/collections/asr/**/*
|
||||
- examples/asr/**/*
|
||||
- tutorials/asr/**/*
|
||||
- docs/source/asr/**/*
|
||||
- tests/collections/asr/**
|
||||
|
||||
Speaker Tasks:
|
||||
- examples/speaker_tasks/**/*
|
||||
- tutorials/speaker_tasks/**/*
|
||||
|
||||
TTS:
|
||||
- nemo/collections/tts/**/*
|
||||
- nemo/collections/common/tokenizers/text_to_speech/**
|
||||
- examples/tts/**/*
|
||||
- tutorials/tts/**/*
|
||||
- docs/source/tts/**/*
|
||||
- scripts/dataset_processing/tts/**
|
||||
- scripts/tts_dataset_files/**
|
||||
- tests/collections/tts/**
|
||||
- tests/collections/common/tokenizers/text_to_speech/**
|
||||
|
||||
Audio:
|
||||
- nemo/collections/audio/**/*
|
||||
- examples/audio/**/*
|
||||
- tutorials/audio/**/*
|
||||
- docs/source/audio/**/*
|
||||
- tests/collections/audio/**
|
||||
|
||||
core:
|
||||
- nemo/core/**/*
|
||||
- tests/core/**
|
||||
|
||||
common:
|
||||
- nemo/collections/common/**/*
|
||||
|
||||
CI:
|
||||
- .github/**/*
|
||||
- Jenkinsfile
|
||||
- Dockerfile
|
||||
- ci.groovy
|
||||
@@ -0,0 +1,79 @@
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import os
|
||||
|
||||
import requests
|
||||
from github import Github
|
||||
|
||||
|
||||
def send_slack_notification():
|
||||
# Get environment variables
|
||||
gh_token = os.environ.get('GH_TOKEN')
|
||||
webhook_url = os.environ.get('SLACK_WEBHOOK')
|
||||
repository = os.environ.get('REPOSITORY')
|
||||
run_id = os.environ.get('RUN_ID')
|
||||
server_url = os.environ.get('SERVER_URL', 'https://github.com')
|
||||
pr_number = int(os.environ.get('PR_NUMBER', 0))
|
||||
branch_name = os.environ.get('BRANCH_NAME')
|
||||
|
||||
# Get failure info from GitHub API
|
||||
gh = Github(gh_token)
|
||||
repo = gh.get_repo(repository)
|
||||
|
||||
# Get failed jobs
|
||||
failed_jobs = [job.name for job in repo.get_workflow_run(int(run_id)).jobs() if job.conclusion == 'failure']
|
||||
|
||||
if pr_number != 0:
|
||||
pr = repo.get_pull(pr_number)
|
||||
|
||||
title = f"*<{server_url}/{repository}/pull/{pr_number}|PR#{pr_number}>: {pr.title.replace('`', '')}*"
|
||||
author = f"<{server_url}/{pr.user.login}|{pr.user.login}>"
|
||||
branch = f"<{server_url}/{pr.head.repo.full_name}/tree/{pr.head.ref}|{pr.head.ref}>"
|
||||
else:
|
||||
title = f"*Run on <{server_url}/{repository}/tree/{branch_name}|{branch_name}>*"
|
||||
author = "No author"
|
||||
branch = f"<{server_url}/{repository}/tree/{branch_name}|{branch_name}>"
|
||||
|
||||
blocks = [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": (
|
||||
f"{title}\n"
|
||||
f"• Author: {author}\n"
|
||||
f"• Branch: {branch}\n"
|
||||
f"• Pipeline: <{server_url}/{repository}/actions/runs/{run_id}|View Run>\n"
|
||||
f"• Failed Jobs:\n"
|
||||
+ "\n".join(
|
||||
[
|
||||
f" • <{server_url}/{repository}/actions/runs/{run_id}|{job.split('/')[-1]}>"
|
||||
for job in failed_jobs
|
||||
if job.split('/')[-1] != 'Nemo_CICD_Test'
|
||||
]
|
||||
)
|
||||
),
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
print({"blocks": blocks})
|
||||
|
||||
# Send to Slack
|
||||
response = requests.post(webhook_url, json={"blocks": blocks})
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
send_slack_notification()
|
||||
@@ -0,0 +1,111 @@
|
||||
name: ~Build container template
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image-name:
|
||||
required: true
|
||||
type: string
|
||||
description: "The name of the image to build"
|
||||
dockerfile:
|
||||
required: true
|
||||
type: string
|
||||
runner:
|
||||
required: false
|
||||
default: nemo-ci-aws-gpu-x2
|
||||
type: string
|
||||
description: "The runner to use for the build"
|
||||
registry:
|
||||
required: false
|
||||
default: 766267172432.dkr.ecr.us-east-1.amazonaws.com
|
||||
type: string
|
||||
description: "Container registry"
|
||||
outputs:
|
||||
image:
|
||||
description: "Full image URL (registry/nemo-speech:image-name-run_id)"
|
||||
value: ${{ jobs.build.outputs.image }}
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
pre-flight:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
build_args: ${{ steps.manifest.outputs.BUILD_ARGS }}
|
||||
cache-from: ${{ steps.cache_from.outputs.LAST_PRS }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get last merged PR
|
||||
id: cache_from
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
LAST_PRS=$(gh api graphql -f query='
|
||||
query {
|
||||
repository(owner: "NVIDIA", name: "NeMo") {
|
||||
pullRequests(states: MERGED, first: 100, orderBy: {field: UPDATED_AT, direction: DESC}) {
|
||||
nodes {
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
}' | jq -r '.data.repository.pullRequests.nodes[].number' | while read -r number; do
|
||||
echo "type=registry,ref=${{ inputs.registry }}/nemo-speech:${{ inputs.image-name }}-buildcache-$number,mode=max"
|
||||
done)
|
||||
|
||||
echo "LAST_PRS<<EOF" | tee -a $GITHUB_OUTPUT
|
||||
echo "$LAST_PRS" | tee -a $GITHUB_OUTPUT
|
||||
echo "EOF" | tee -a $GITHUB_OUTPUT
|
||||
|
||||
build:
|
||||
runs-on: ${{ inputs.runner }}
|
||||
needs: [pre-flight]
|
||||
outputs:
|
||||
image: ${{ steps.image-tag.outputs.image }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Compute image tag
|
||||
id: image-tag
|
||||
run: |
|
||||
echo "image=${{ inputs.registry }}/nemo-speech:${{ inputs.image-name }}-${{ github.run_id }}" | tee -a $GITHUB_OUTPUT
|
||||
|
||||
- name: Compute cache keys
|
||||
id: cache-keys
|
||||
run: |
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
||||
KEY="main"
|
||||
elif [[ -n "$PR_NUMBER" ]]; then
|
||||
KEY="$PR_NUMBER"
|
||||
else
|
||||
KEY=$(echo "${{ github.ref_name }}" | tr '/' '-' | tr -cd '[:alnum:]._-')
|
||||
fi
|
||||
echo "cache-to=type=registry,ref=${{ inputs.registry }}/nemo-speech:${{ inputs.image-name }}-buildcache-${KEY},mode=max" | tee -a $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
file: ${{ inputs.dockerfile }}
|
||||
push: true
|
||||
context: .
|
||||
build-args: |
|
||||
IMAGE_LABEL=nemo-core
|
||||
NEMO_TAG=${{ github.sha }}
|
||||
NEMO_REPO=https://github.com/NVIDIA/NeMo
|
||||
PR_NUMBER=${{ github.event.pull_request.number || 0 }}
|
||||
cache-from: |
|
||||
type=registry,ref=${{ inputs.registry }}/nemo-speech:${{ inputs.image-name }}-buildcache-main,mode=max
|
||||
type=registry,ref=${{ inputs.registry }}/nemo-speech:${{ inputs.image-name }}-buildcache-${{ github.event.pull_request.number || 0 }},mode=max
|
||||
${{ needs.pre-flight.outputs.cache-from }}
|
||||
cache-to: ${{ steps.cache-keys.outputs.cache-to }}
|
||||
tags: |
|
||||
${{ inputs.registry }}/nemo-speech:${{ inputs.image-name }}-${{ github.run_id }}
|
||||
${{ inputs.registry }}/nemo-speech:${{ inputs.image-name }}-${{ github.sha }}
|
||||
@@ -0,0 +1,56 @@
|
||||
name: ~Bump Megatron Tag template
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
nemo-target-branch:
|
||||
required: true
|
||||
type: string
|
||||
description: "The target branch to bump"
|
||||
mcore-target-branch:
|
||||
required: true
|
||||
type: string
|
||||
description: "The target branch to bump"
|
||||
secrets:
|
||||
PAT:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
update-branch:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.nemo-target-branch }}
|
||||
|
||||
- name: Set Git config
|
||||
run: |
|
||||
git config --local user.email "actions@github.com"
|
||||
git config --local user.name "Github Actions"
|
||||
- name: Merge weekly-bump-${{ inputs.nemo-target-branch }} back to base branch
|
||||
env:
|
||||
SOURCE_BRANCH: weekly-bump-${{ inputs.nemo-target-branch }}
|
||||
TARGET_BRANCH: ${{ inputs.nemo-target-branch }}
|
||||
run: |
|
||||
if git ls-remote --exit-code origin $SOURCE_BRANCH; then
|
||||
git fetch --unshallow
|
||||
git checkout $SOURCE_BRANCH
|
||||
git pull
|
||||
git merge --no-ff $TARGET_BRANCH -m "chore: Auto-merge $TARGET_BRANCH into $SOURCE_BRANCH"
|
||||
else
|
||||
git checkout -b $SOURCE_BRANCH $TARGET_BRANCH
|
||||
fi
|
||||
git push -u origin $SOURCE_BRANCH
|
||||
|
||||
mcore:
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_bump_yamlfile.yml@v0.27.1
|
||||
needs: [update-branch]
|
||||
with:
|
||||
source-repository: NVIDIA/Megatron-LM
|
||||
source-ref: ${{ inputs.mcore-target-branch }}
|
||||
yaml-path: '."vcs-dependencies"."megatron-lm".ref'
|
||||
file: requirements/manifest.json
|
||||
base-branch: weekly-bump-${{ inputs.nemo-target-branch }}
|
||||
cicd-labels: Run CICD,no-fail-fast
|
||||
pr-reviewers: ${{ inputs.pr-reviewers }}
|
||||
secrets:
|
||||
PAT: ${{ secrets.PAT }}
|
||||
@@ -0,0 +1,14 @@
|
||||
name: Create PR to main with cherry-pick from release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
cherry-pick:
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_cherry_pick.yml@v0.63.0
|
||||
secrets:
|
||||
PAT: ${{ secrets.PAT }}
|
||||
SLACK_WEBHOOK_ADMIN: ${{ secrets.SLACK_WEBHOOK_ADMIN }}
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
@@ -0,0 +1,245 @@
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Approve Test Queue
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/5 * * * *' # Runs every 5 minutes
|
||||
workflow_dispatch: # Allows manual triggering
|
||||
|
||||
jobs:
|
||||
approve-queue:
|
||||
runs-on: ubuntu-latest
|
||||
environment: main
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
- name: Approve waiting deployments
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
MAX_CONCURRENCY: ${{ vars.MAX_CONCURRENCY || 1 }}
|
||||
run: |
|
||||
python - <<EOF
|
||||
import os
|
||||
import requests
|
||||
|
||||
|
||||
# GitHub API configuration
|
||||
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
|
||||
REPO = os.environ["GITHUB_REPOSITORY"]
|
||||
MAX_CONCURRENCY = int(os.environ["MAX_CONCURRENCY"])
|
||||
API_BASE = f"https://api.github.com/repos/{REPO}"
|
||||
|
||||
# Headers for GitHub API
|
||||
headers = {
|
||||
"Authorization": f"token {GITHUB_TOKEN}",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
}
|
||||
|
||||
def make_request(endpoint, method="GET", data=None, allow_no_pending_deployments=False):
|
||||
"""Make a request to the GitHub API with error handling."""
|
||||
url = f"{API_BASE}/{endpoint}"
|
||||
try:
|
||||
if method == "GET":
|
||||
response = requests.get(url, headers=headers)
|
||||
else:
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
response.raise_for_status()
|
||||
response_json = response.json()
|
||||
if hasattr(response, "links") and "actions/runs?status" in endpoint:
|
||||
response_json["next"] = response.links.get("next", {}).get("url")
|
||||
|
||||
return response_json
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if (
|
||||
allow_no_pending_deployments
|
||||
and e.response is not None
|
||||
and e.response.status_code == 422
|
||||
):
|
||||
try:
|
||||
response_json = e.response.json()
|
||||
except ValueError:
|
||||
response_json = {}
|
||||
if "No pending deployment requests to approve or reject" in str(response_json.get("errors", "")):
|
||||
print(f"No pending deployment requests remain for {endpoint}; skipping")
|
||||
return {"skipped": True, "reason": "no_pending_deployments"}
|
||||
|
||||
print(f"Error making request to {endpoint}: {str(e)}")
|
||||
if e.response is not None:
|
||||
print(f"Response: {e.response.text}")
|
||||
return None
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error making request to {endpoint}: {str(e)}")
|
||||
if e.response is not None:
|
||||
print(f"Response: {e.response.text}")
|
||||
return None
|
||||
|
||||
|
||||
def get_workflow_runs(status):
|
||||
"""Get all workflow runs for a given status."""
|
||||
all_results = []
|
||||
endpoint = f"actions/runs?status={status}"
|
||||
while endpoint:
|
||||
response = make_request(endpoint)
|
||||
if not response:
|
||||
break
|
||||
|
||||
all_results.extend(response.get("workflow_runs", []))
|
||||
endpoint = None
|
||||
next_url = response.get("next")
|
||||
if next_url:
|
||||
endpoint = f"actions/runs?{next_url.split('?')[1]}"
|
||||
|
||||
return all_results
|
||||
|
||||
|
||||
def filter_cicd_runs(workflow_runs):
|
||||
"""Keep only CICD workflow runs."""
|
||||
return [run for run in workflow_runs if run.get("name") == "CICD NeMo"]
|
||||
|
||||
|
||||
def print_workflow_run_details(label, workflow_runs):
|
||||
"""Print the runs that are counted against concurrency."""
|
||||
if not workflow_runs:
|
||||
print(f"{label}: none")
|
||||
return
|
||||
|
||||
print(f"{label}:")
|
||||
for run in workflow_runs:
|
||||
print(
|
||||
" "
|
||||
f"id={run.get('id')} "
|
||||
f"status={run.get('status')} "
|
||||
f"branch={run.get('head_branch')} "
|
||||
f"title={run.get('display_title')}"
|
||||
)
|
||||
|
||||
|
||||
# Get current running and queued workflows
|
||||
print("Fetching workflow runs...")
|
||||
queued_workflow_runs = filter_cicd_runs(get_workflow_runs("queued"))
|
||||
in_progress_workflow_runs = filter_cicd_runs(get_workflow_runs("in_progress"))
|
||||
print_workflow_run_details("Queued CICD workflows counted against concurrency", queued_workflow_runs)
|
||||
print_workflow_run_details("Running CICD workflows counted against concurrency", in_progress_workflow_runs)
|
||||
|
||||
# Count running and queued workflows
|
||||
queued_workflows = len(queued_workflow_runs)
|
||||
in_progress_workflows = len(in_progress_workflow_runs)
|
||||
|
||||
total_workflows = queued_workflows + in_progress_workflows
|
||||
print(f"Current queued workflows: {queued_workflows}")
|
||||
print(f"Current running workflows: {in_progress_workflows}")
|
||||
print(f"Total workflows: {total_workflows}")
|
||||
print(f"Max concurrency: {MAX_CONCURRENCY}")
|
||||
|
||||
if total_workflows >= MAX_CONCURRENCY:
|
||||
print("Maximum concurrency reached, no new approvals will be made")
|
||||
exit(0)
|
||||
|
||||
# Get waiting CI workflows for test environment
|
||||
print("Fetching deployments...")
|
||||
pending_workflows = filter_cicd_runs(get_workflow_runs("waiting"))
|
||||
|
||||
# Sort deployments by creation date (oldest first)
|
||||
print("Sorting workflows...")
|
||||
pending_workflows = sorted(pending_workflows, key=lambda x: x.get("created_at", ""))
|
||||
|
||||
# Process each deployment
|
||||
print("Processing ...")
|
||||
for workflow in pending_workflows:
|
||||
if total_workflows >= MAX_CONCURRENCY:
|
||||
print("Maximum concurrency reached, stopping approvals")
|
||||
break
|
||||
|
||||
workflow_id = workflow.get("id")
|
||||
workflow_name = workflow.get("display_title") or workflow.get("name") or "<unknown>"
|
||||
if not workflow_id:
|
||||
print(f"Skipping workflow without a run id: {workflow_name}")
|
||||
continue
|
||||
print(f"Approving workflow {workflow_name} with Run Id: {workflow_id}")
|
||||
|
||||
deployment_url = f"actions/runs/{workflow_id}/pending_deployments"
|
||||
pending_deployments = make_request(deployment_url)
|
||||
if not pending_deployments:
|
||||
print(f"No pending deployments found for workflow {workflow_name}; skipping")
|
||||
continue
|
||||
|
||||
environment_ids = []
|
||||
environment_names = []
|
||||
for deployment in pending_deployments:
|
||||
environment = deployment.get("environment") or {}
|
||||
environment_id = environment.get("id")
|
||||
environment_name = environment.get("name") or "<unknown>"
|
||||
if not environment_id:
|
||||
print(f"Skipping deployment without an environment id for workflow {workflow_name}")
|
||||
continue
|
||||
|
||||
environment_ids.append(environment_id)
|
||||
environment_names.append(environment_name)
|
||||
|
||||
if not environment_ids:
|
||||
print(f"No pending deployments with environment ids found for workflow {workflow_name}")
|
||||
exit(1)
|
||||
|
||||
# Approve the deployment
|
||||
status_data = {
|
||||
"environment_ids": environment_ids,
|
||||
"state": "approved",
|
||||
"comment": "Automatically approved by queue manager"
|
||||
}
|
||||
result = make_request(
|
||||
deployment_url,
|
||||
method="POST",
|
||||
data=status_data,
|
||||
allow_no_pending_deployments=True,
|
||||
)
|
||||
|
||||
if result is None:
|
||||
print(f"Failed to approve environments {environment_names} for workflow {workflow_name}")
|
||||
exit(1)
|
||||
if isinstance(result, dict) and result.get("skipped"):
|
||||
continue
|
||||
|
||||
total_workflows += 1
|
||||
|
||||
EOF
|
||||
notify:
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
needs: [approve-queue]
|
||||
steps:
|
||||
- name: Notify
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_WEBHOOK_ADMIN: <!subteam^${{ secrets.SLACK_WEBHOOK_ADMIN }}>
|
||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H 'Content-type: application/json' \
|
||||
--data "{\"text\":\":robot_joy: <https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}|Test-queue-approval-bot workflow> failed. Please review manually.\n\ncc ${SLACK_WEBHOOK_ADMIN}\"}" \
|
||||
$SLACK_WEBHOOK
|
||||
@@ -0,0 +1,578 @@
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: NeMo E2E Speech Tests
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
test_to_run:
|
||||
required: true
|
||||
type: string
|
||||
image-name:
|
||||
required: false
|
||||
default: nemo_container_speech
|
||||
type: string
|
||||
runner:
|
||||
required: false
|
||||
default: nemo-ci-aws-gpu-x2
|
||||
type: string
|
||||
description: "Runner to use for GPU jobs"
|
||||
run_nightly:
|
||||
required: false
|
||||
default: 'false'
|
||||
type: string
|
||||
description: "Set to 'true' to run the nightly e2e suite"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/_build_container.yml
|
||||
with:
|
||||
image-name: ${{ inputs.image-name }}
|
||||
dockerfile: docker/Dockerfile
|
||||
runner: ${{ inputs.runner }}
|
||||
|
||||
unit-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- script: L0_Unit_Tests_GPU_ASR_1
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 15
|
||||
- script: L0_Unit_Tests_GPU_ASR_2
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 15
|
||||
- script: L0_Unit_Tests_GPU_ASR_3
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 15
|
||||
- script: L0_Unit_Tests_GPU_ASR_4
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 15
|
||||
- script: L0_Unit_Tests_GPU_ASR_5
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 15
|
||||
- script: L0_Unit_Tests_GPU_ASR_6
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 15
|
||||
- script: L0_Unit_Tests_CPU_ASR_1
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_CPU_ASR_2
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_CPU_ASR_3
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_CPU_ASR_4
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_CPU_ASR_5
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_CPU_ASR_6
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_GPU_TTS
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 30
|
||||
- script: L0_Unit_Tests_CPU_TTS
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
- script: L0_Unit_Tests_GPU_Audio_1
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 15
|
||||
- script: L0_Unit_Tests_GPU_Audio_2
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 15
|
||||
- script: L0_Unit_Tests_CPU_Audio
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 20
|
||||
- script: L0_Unit_Tests_GPU_SpeechLM2
|
||||
runner: ${{ inputs.runner }}
|
||||
timeout: 20
|
||||
- script: L0_Unit_Tests_CPU_SpeechLM2_1
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_CPU_SpeechLM2_2
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_CPU_SpeechLM2_3
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
- script: L0_Unit_Tests_CPU_SpeechLM2_4
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
timeout: 12
|
||||
needs: [build]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.script }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
- name: main
|
||||
uses: ./.github/actions/test-template
|
||||
with:
|
||||
runner: ${{ runner.name }}
|
||||
script: ${{ matrix.script }}
|
||||
is_unit_test: true
|
||||
tests_to_run: ${{ inputs.test_to_run }}
|
||||
image: ${{ needs.build.outputs.image }}
|
||||
test-data-path: ${{ vars.DEFAULT_TEST_DATA_PATH || '/mnt/datadrive/TestData' }}
|
||||
timeout: ${{ matrix.timeout || 10 }}
|
||||
cpu-only: ${{ matrix.cpu-only || false }}
|
||||
is_optional: ${{ matrix.is-optional || false }}
|
||||
|
||||
e2e-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: ASR_dev_run_Speech_to_Text
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: Optional_ASR_dev_run_Speech_To_Text_Finetuning
|
||||
is-optional: true
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: Optional_ASR_dev_run_Speech_To_Text_HF_Finetuning
|
||||
is-optional: true
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: ASR_dev_run_Speech_to_Text_WPE_-_Conformer
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: ASR_dev_run_Speech_to_Text_Hybrid_RNNT_CTC_Prompt
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: ASR_dev_run_Speech_to_Text_RNNT_Prompt
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_ASR_Multi-dataloader_dev_run_Speech_to_Text_multi-dataloader
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_ASR_Multi-dataloader_dev_run_Speech_to_Label_multi-dataloader
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_ASR_Adapters_Linear_Adapters
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_ASR_Adapters_RelPos_MHA_Adapters
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_to_Text_EMA
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_to_Text_AED
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speaker_dev_run_Speech_to_Label
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Estimate_Duration_Bins
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Estimate_Token_Bins
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Batch_Size_OOMptimizer
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Distributed_Batch_Size_OOMptimizer
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: Optional_L2_Speech_Batch_Size_OOMptimizer_Canary
|
||||
is-optional: true
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Speech_to_Text_Transcribe
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Speech_to_Text_Streaming_Infer
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Speech_to_Text_Cache_Aware_Infer
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Speech_to_Text_Cache_Aware_with_Prompt_Infer
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Streaming_Inference
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Speech_to_Text_Inference_Boost_GT
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Speech_to_Text_Transcribe_Boost_GT
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Canary_Transcribe_Full_Manifest
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Canary_Transcribe_With_Prompt
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Canary_Transcribe_Audio_Dir
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speech_Transcription_Canary_Streaming_Full_Manifest
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Longform_Speech_Transcription_Canary_Chunked_Infer_from_Audio_Dir
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Longform_Speech_Transcription_with_TimeStamps_Canary_Chunked_Infer_from_Audio_Dir
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Longform_Speech_Transcription_with_TimeStamps_Canary_Chunked_Infer_from_Manifest
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: Speech_Checkpoints_tests
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speaker_dev_run_Speaker_Recognition
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speaker_dev_run_EndtoEnd_Speaker_Diarization_Sortformer
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speaker_dev_run_EndtoEnd_Diarizer_Inference
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speaker_dev_run_Speaker_Diarization_with_ASR_Inference
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speaker_dev_run_Clustering_Diarizer_Inference
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Speaker_dev_run_Multispeaker_ASR_Data_Simulation
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_1_FastPitch
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_1_Hifigan
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_G2P_Models_G2P_Conformer_training_evaluation_and_inference
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: SPEECHLM_HF_Training_DuplexS2S
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: SPEECHLM_HF_Training_DuplexS2SSpeechDecoder
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: SPEECHLM_HF_Training_SALM
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_Magpietts_DecoderContext
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_Magpietts_MultiEncoder
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_Magpietts_MoE
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_Magpietts_OnlinePO
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_InferEvaluate_Magpietts_ZeroShot
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_InferEvaluate_Magpietts_SeenSpeakers
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_InferEvaluatelongform_Magpietts_ZeroShot
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_InferEvaluate_Magpietts_MoE_ZeroShot
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_InferEvaluatelongform_Magpietts_MoE_ZeroShot
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_InferEvaluate_Magpietts_FrameStacking
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_Magpietts_OnlineCFGDistillation
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_Magpietts_MoE_OnlineCFGDistillation
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_Magpietts_FrameStacking_OnlineCFGDistillation
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_EasyMagpietts_Qwen
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_EasyMagpietts_Nemotron
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_Fast_dev_runs_EasyMagpietts_OnlinePO
|
||||
timeout: 20
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_TTS_InferEvaluate_EasyMagpietts
|
||||
timeout: 20
|
||||
needs: [build, unit-tests]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.is-optional && 'PLEASEFIXME_' || '' }}${{ matrix.script }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
- name: main
|
||||
uses: ./.github/actions/test-template
|
||||
with:
|
||||
runner: ${{ runner.name }}
|
||||
script: ${{ matrix.script }}
|
||||
tests_to_run: ${{ inputs.test_to_run }}
|
||||
image: ${{ needs.build.outputs.image }}
|
||||
test-data-path: ${{ vars.DEFAULT_TEST_DATA_PATH || '/mnt/datadrive/TestData' }}
|
||||
timeout: ${{ matrix.timeout || 10 }}
|
||||
is_optional: ${{ matrix.is-optional || false }}
|
||||
|
||||
e2e-nightly:
|
||||
if: ${{ github.event_name == 'schedule' || inputs.run_nightly == 'true' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_de_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_es_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_it_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_ua_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_pl_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_hr_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_be_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_fr_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_ru_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_nl_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_fa_fastconformer_hybrid_large
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_ka_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_kk_ru_fastconformer_hybrid_large
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_ka_fastconformer_hybrid_transducer_ctc_large_streaming_80ms_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_uz_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_ar_fastconformer_hybrid_large_pc_v1_0
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_hy_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_hybrid_medium_streaming_80ms_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_hybrid_medium_streaming_80ms
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_pt_fastconformer_hybrid_large_pc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_es_fastconformer_hybrid_large_pc_nc
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_ar_fastconformer_hybrid_large_pcd_v1_0
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_hybrid_large_streaming_multi
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_ctc_large
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_transducer_large
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_ctc_xlarge
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_transducer_xlarge
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_transducer_xxlarge
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_ctc_xxlarge
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__stt_en_fastconformer_tdt_large
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_stt_en_fastconformer_hybrid_large_streaming_1040ms
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_stt_multilingual_fastconformer_hybrid_large_pc_blend_eu
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_rnnt_1_1b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_ctc_1_1b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_rnnt_0_6b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_ctc_0_6b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_tdt_1_1b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_tdt_ctc_1_1b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_tdt_ctc_0_6b_ja
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_tdt_ctc_110m
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_tdt_0_6b_v2
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_rnnt_110m_da_dk
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_tdt_0_6b_v3
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_ctc_0_6b_Vietnamese
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__canary_1b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__canary_1b_flash
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__canary_180m_flash
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__canary_1b_v2
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__parakeet_realtime_eou_120m_v1
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__multitalker_parakeet_streaming_0_6b_v1
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__nemotron_speech_streaming_en_0_6b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__canary_qwen_2_5b
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__diar_sortformer_4spk_v1
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__diar_streaming_sortformer_4spk_v2
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__diar_streaming_sortformer_4spk_v2_1
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_titanet_large
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__speakerverification_en_titanet_large
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__ssl_en_nest_large_v1_0
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__ssl_en_nest_xlarge_v1_0
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_vad_multilingual_marblenet
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_vad_multilingual_frame_marblenet
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__Frame_VAD_Multilingual_MarbleNet_v2_0
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__se_den_sb_16k_small
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__se_der_sb_16k_small
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__sr_ssl_flowmatching_16k_430m
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_mel_codec_44khz_medium
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_mel_codec_22khz_fullband_medium
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__low_frame_rate_speech_codec_22khz
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__audio_codec_22khz
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__audio_codec_44khz
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__mel_codec_22khz
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__mel_codec_44khz
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__nemo_nano_codec_22khz_1_78kbps_12_5fps
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__nemo_nano_codec_22khz_1_89kbps_21_5fps
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__nemo_nano_codec_22khz_0_6kbps_12_5fps
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__tts_en_fastpitch
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__tts_hifigan
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_nvidia__magpie_tts_multilingual_357m
|
||||
timeout: 15
|
||||
- runner: ${{ inputs.runner }}
|
||||
script: L2_Model_Support_tts_en_e2e_fastspeech2hifigan
|
||||
timeout: 15
|
||||
needs: [build, unit-tests]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.script }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
- name: main
|
||||
uses: ./.github/actions/test-template
|
||||
with:
|
||||
runner: ${{ runner.name }}
|
||||
script: ${{ matrix.script }}
|
||||
tests_to_run: ${{ inputs.test_to_run }}
|
||||
image: ${{ needs.build.outputs.image }}
|
||||
test-data-path: ${{ vars.DEFAULT_TEST_DATA_PATH || '/mnt/datadrive/TestData' }}
|
||||
timeout: ${{ matrix.timeout || 10 }}
|
||||
test_dir: e2e_nightly
|
||||
@@ -0,0 +1,126 @@
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: NeMo Unit Tests
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
test_to_run:
|
||||
required: true
|
||||
type: string
|
||||
runner:
|
||||
required: false
|
||||
default: nemo-ci-aws-gpu-x2
|
||||
type: string
|
||||
description: "Runner to use for GPU jobs"
|
||||
container-image:
|
||||
required: true
|
||||
type: string
|
||||
description: "Full image URL with tag for the NeMo container"
|
||||
|
||||
jobs:
|
||||
collections-common-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- script: L0_Unit_Tests_GPU_Common
|
||||
runner: ${{ inputs.runner }}
|
||||
- script: L0_Unit_Tests_CPU_Common
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.script }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
- name: main
|
||||
uses: ./.github/actions/test-template
|
||||
with:
|
||||
runner: ${{ runner.name }}
|
||||
script: ${{ matrix.script }}
|
||||
is_unit_test: true
|
||||
tests_to_run: ${{ inputs.test_to_run }}
|
||||
image: ${{ inputs.container-image }}
|
||||
test-data-path: ${{ vars.DEFAULT_TEST_DATA_PATH || '/mnt/datadrive/TestData' }}
|
||||
cpu-only: ${{ matrix.cpu-only || false }}
|
||||
|
||||
core-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- script: L0_Unit_Tests_GPU_Core
|
||||
runner: ${{ inputs.runner }}
|
||||
- script: L0_Unit_Tests_CPU_Core
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
- script: L0_Unit_Tests_GPU_Hydra
|
||||
runner: ${{ inputs.runner }}
|
||||
- script: L0_Unit_Tests_CPU_Hydra
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.script }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
- name: main
|
||||
uses: ./.github/actions/test-template
|
||||
with:
|
||||
runner: ${{ runner.name }}
|
||||
script: ${{ matrix.script }}
|
||||
is_unit_test: true
|
||||
tests_to_run: ${{ inputs.test_to_run }}
|
||||
image: ${{ inputs.container-image }}
|
||||
test-data-path: ${{ vars.DEFAULT_TEST_DATA_PATH || '/mnt/datadrive/TestData' }}
|
||||
cpu-only: ${{ matrix.cpu-only || false }}
|
||||
|
||||
other-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- script: L0_Unit_Tests_GPU_Others
|
||||
runner: ${{ inputs.runner }}
|
||||
- script: L0_Unit_Tests_CPU_Others
|
||||
runner: ${{ inputs.runner }}
|
||||
cpu-only: true
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.script }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
- name: main
|
||||
uses: ./.github/actions/test-template
|
||||
with:
|
||||
runner: ${{ runner.name }}
|
||||
script: ${{ matrix.script }}
|
||||
is_unit_test: true
|
||||
tests_to_run: ${{ inputs.test_to_run }}
|
||||
image: ${{ inputs.container-image }}
|
||||
test-data-path: ${{ vars.DEFAULT_TEST_DATA_PATH || '/mnt/datadrive/TestData' }}
|
||||
cpu-only: ${{ matrix.cpu-only || false }}
|
||||
is_optional: ${{ matrix.is-optional || false }}
|
||||
@@ -0,0 +1,416 @@
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: CICD NeMo
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 0 * * *
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- r**
|
||||
- weekly-bump*
|
||||
- "pull-request/[0-9]+"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
test_to_run:
|
||||
required: false
|
||||
default: all
|
||||
type: string
|
||||
description: Comma-separated list of tests to run. Use "all" to run the full test suite.
|
||||
|
||||
concurrency:
|
||||
# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}-${{ github.event_name }}
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
pre-flight:
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_cicd_preflight.yml@v0.89.0
|
||||
with:
|
||||
default_runner_prefix: ${{ vars.DEFAULT_RUNNER_PREFIX }}
|
||||
non_nvidia_runner_prefix: ${{ vars.NON_NVIDIA_RUNNER_PREFIX }}
|
||||
default_test_data_path: ${{ vars.DEFAULT_TEST_DATA_PATH }}
|
||||
non_nvidia_test_data_path: ${{ vars.NON_NVIDIA_TEST_DATA_PATH }}
|
||||
default_registry: ${{ vars.DEFAULT_CONTAINER_REGISTRY }}
|
||||
non_nvidia_registry: ${{ vars.NON_NVIDIA_CONTAINER_REGISTRY }}
|
||||
sso_users_filename: ${{ vars.SSO_USERS_FILENAME }}
|
||||
secrets:
|
||||
NVIDIA_MANAGEMENT_ORG_PAT: ${{ secrets.NVIDIA_MANAGEMENT_ORG_PAT }}
|
||||
|
||||
configure:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [pre-flight]
|
||||
outputs:
|
||||
test_to_run: ${{ steps.test_to_run.outputs.main }}
|
||||
is_ci_workload: ${{ steps.is_ci_workload.outputs.main }}
|
||||
no_fail_fast: ${{ steps.no_fail_fast.outputs.main }}
|
||||
components_to_run: ${{ steps.components_to_run.outputs.main }}
|
||||
run_nightly: ${{ steps.pr_labels.outputs.run_nightly }}
|
||||
skip_docs: ${{ steps.pr_labels.outputs.skip_docs }}
|
||||
skip_linting: ${{ steps.pr_labels.outputs.skip_linting }}
|
||||
env:
|
||||
TESTS_TO_RUN: ${{ inputs.test_to_run }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Select components to run
|
||||
id: components_to_run
|
||||
run: |
|
||||
echo "main=[\"speech\"]" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Select tests to run
|
||||
id: test_to_run
|
||||
run: |
|
||||
# For manual dispatch, use the provided input
|
||||
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
|
||||
TESTS_TO_RUN=$TESTS_TO_RUN
|
||||
|
||||
# For push (PR branches via copy-pr-bot, main, release) and schedule, run all tests
|
||||
elif [[ "$EVENT_NAME" == "push" || "$EVENT_NAME" == "schedule" ]]; then
|
||||
TESTS_TO_RUN=all
|
||||
|
||||
else
|
||||
echo "Unsupported event_name $EVENT_NAME provided".
|
||||
exit 1
|
||||
fi
|
||||
|
||||
parsed_string=$(echo "$TESTS_TO_RUN" | jq -c --raw-input 'split(",")')
|
||||
echo "main=${parsed_string}" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check if this is a CI workload
|
||||
shell: bash
|
||||
id: is_ci_workload
|
||||
run: |
|
||||
branch_name=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}
|
||||
|
||||
if [[ "$branch_name" =~ ^bump-ci-container || "$EVENT_NAME" == "schedule" ]]; then
|
||||
is_ci_workload=true
|
||||
echo "main=true" | tee -a "$GITHUB_OUTPUT"
|
||||
else
|
||||
is_ci_workload=false
|
||||
fi
|
||||
|
||||
echo "main=$is_ci_workload" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Fetch PR labels
|
||||
id: pr_labels
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
REF="${GITHUB_REF#refs/heads/}"
|
||||
if [[ "$REF" =~ ^pull-request/([0-9]+)$ ]]; then
|
||||
PR_NUMBER="${BASH_REMATCH[1]}"
|
||||
LABELS=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json labels -q '[.labels[].name]')
|
||||
else
|
||||
LABELS='[]'
|
||||
fi
|
||||
echo "run_nightly=$(echo "$LABELS" | jq 'any(. == "Run e2e nightly")')" | tee -a "$GITHUB_OUTPUT"
|
||||
echo "skip_docs=$(echo "$LABELS" | jq 'any(. == "skip-docs")')" | tee -a "$GITHUB_OUTPUT"
|
||||
echo "skip_linting=$(echo "$LABELS" | jq 'any(. == "skip-linting")')" | tee -a "$GITHUB_OUTPUT"
|
||||
echo "no_fail_fast=$(echo "$LABELS" | jq 'any(. == "no-fail-fast")')" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check if no-fail-fast is set
|
||||
shell: bash
|
||||
id: no_fail_fast
|
||||
env:
|
||||
HAS_FAIL_FAST_LABEL: ${{ steps.pr_labels.outputs.no_fail_fast }}
|
||||
run: |
|
||||
if [[ "$HAS_FAIL_FAST_LABEL" == "true" || "$EVENT_NAME" == "schedule" ]]; then
|
||||
no_fail_fast=true
|
||||
else
|
||||
no_fail_fast=false
|
||||
fi
|
||||
|
||||
echo "main=$no_fail_fast" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
code-linting:
|
||||
if: needs.configure.outputs.test_to_run != '[]'
|
||||
needs: [configure]
|
||||
uses: ./.github/workflows/code-linting.yml
|
||||
with:
|
||||
skip_docs: ${{ needs.configure.outputs.skip_docs }}
|
||||
skip_linting: ${{ needs.configure.outputs.skip_linting }}
|
||||
|
||||
cicd-wait-in-queue:
|
||||
needs: [configure, code-linting]
|
||||
runs-on: ubuntu-latest
|
||||
environment: test
|
||||
if: |
|
||||
needs.configure.outputs.test_to_run != '[]'
|
||||
&& needs.configure.outputs.components_to_run != '[]'
|
||||
&& needs.configure.outputs.is_ci_workload == 'false'
|
||||
steps:
|
||||
- name: Running CI tests
|
||||
run: |
|
||||
echo "Running CI tests"
|
||||
|
||||
cicd-test-container-build:
|
||||
uses: ./.github/workflows/_build_container.yml
|
||||
needs: [pre-flight, configure, code-linting, cicd-wait-in-queue]
|
||||
if: |
|
||||
needs.configure.outputs.test_to_run != '[]'
|
||||
&& needs.configure.outputs.components_to_run != '[]'
|
||||
&& (
|
||||
success()
|
||||
|| (
|
||||
needs.cicd-wait-in-queue.result == 'skipped'
|
||||
&& needs.configure.outputs.is_ci_workload == 'true'
|
||||
)
|
||||
)
|
||||
&& !cancelled()
|
||||
with:
|
||||
image-name: nemo_container
|
||||
dockerfile: docker/Dockerfile
|
||||
runner: ${{ needs.pre-flight.outputs.runner_prefix }}
|
||||
registry: ${{ needs.pre-flight.outputs.registry }}
|
||||
|
||||
cicd-import-tests:
|
||||
if: |
|
||||
needs.configure.outputs.test_to_run != '[]'
|
||||
&& needs.configure.outputs.components_to_run != '[]'
|
||||
&& (
|
||||
success()
|
||||
|| (
|
||||
needs.cicd-wait-in-queue.result == 'skipped'
|
||||
&& needs.configure.outputs.is_ci_workload == 'true'
|
||||
)
|
||||
)
|
||||
&& !cancelled()
|
||||
needs: [cicd-test-container-build, configure, pre-flight]
|
||||
runs-on: ${{ needs.pre-flight.outputs.runner_prefix }}
|
||||
steps:
|
||||
- name: Create UUID
|
||||
id: uuid
|
||||
run: |
|
||||
echo "id=$(uuidgen)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}/${{steps.uuid.outputs.id }}/NeMo
|
||||
|
||||
- name: Run some checks
|
||||
run: |
|
||||
docker run \
|
||||
--rm \
|
||||
--device=/dev/nvidia0 \
|
||||
--gpus all \
|
||||
--shm-size=8g \
|
||||
--volume $(pwd)/${{ github.run_id }}/${{steps.uuid.outputs.id }}/NeMo:/workspace \
|
||||
--env TRANSFORMERS_OFFLINE=0 \
|
||||
--env HYDRA_FULL_ERROR=1 --env PYTHONUNBUFFERED=1 ${{ needs.cicd-test-container-build.outputs.image }} bash -c '\
|
||||
# PyTorch Lightning version
|
||||
python -c "import lightning.pytorch; print(lightning.pytorch.__version__)"
|
||||
|
||||
# Basic Import Checks
|
||||
python tests/core_ptl/check_imports.py --domain asr
|
||||
python tests/core_ptl/check_imports.py --domain tts
|
||||
'
|
||||
|
||||
L0_Setup_Test_Data_And_Models:
|
||||
needs: [configure, cicd-test-container-build, cicd-wait-in-queue, pre-flight]
|
||||
runs-on: ${{ needs.pre-flight.outputs.runner_prefix }}
|
||||
if: |
|
||||
needs.configure.outputs.test_to_run != '[]'
|
||||
&& needs.configure.outputs.components_to_run != '[]'
|
||||
&& (
|
||||
success()
|
||||
|| (
|
||||
needs.cicd-wait-in-queue.result == 'skipped'
|
||||
&& needs.configure.outputs.is_ci_workload == 'true'
|
||||
)
|
||||
)
|
||||
&& !cancelled()
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout NeMo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
|
||||
- name: main
|
||||
uses: ./.github/actions/test-template
|
||||
with:
|
||||
runner: ${{ runner.name }}
|
||||
script: L0_Setup_Test_Data_And_Models
|
||||
tests_to_run: '["L0_Setup_Test_Data_And_Models"]'
|
||||
image: ${{ needs.cicd-test-container-build.outputs.image }}
|
||||
test-data-path: ${{ vars.DEFAULT_TEST_DATA_PATH || '/mnt/datadrive/TestData' }}
|
||||
|
||||
cicd-main-unit-tests:
|
||||
needs: [pre-flight, configure, cicd-test-container-build]
|
||||
uses: ./.github/workflows/cicd-main-unit-tests.yml
|
||||
if: |
|
||||
needs.configure.outputs.test_to_run != '[]'
|
||||
&& needs.configure.outputs.components_to_run != '[]'
|
||||
&& (
|
||||
success()
|
||||
|| (
|
||||
needs.cicd-wait-in-queue.result == 'skipped'
|
||||
&& needs.configure.outputs.is_ci_workload == 'true'
|
||||
)
|
||||
)
|
||||
&& !cancelled()
|
||||
with:
|
||||
test_to_run: ${{ needs.configure.outputs.test_to_run }}
|
||||
runner: ${{ needs.pre-flight.outputs.runner_prefix }}
|
||||
container-image: ${{ needs.cicd-test-container-build.outputs.image }}
|
||||
|
||||
cicd-main-speech:
|
||||
needs: [pre-flight, configure, cicd-test-container-build, cicd-main-unit-tests]
|
||||
uses: ./.github/workflows/cicd-main-speech.yml
|
||||
if: |
|
||||
(
|
||||
needs.configure.outputs.test_to_run != '[]'
|
||||
&& (
|
||||
contains(fromJson(needs.configure.outputs.components_to_run), 'speech')
|
||||
)
|
||||
)
|
||||
&& (
|
||||
success()
|
||||
|| (
|
||||
needs.cicd-wait-in-queue.result == 'skipped'
|
||||
&& needs.configure.outputs.is_ci_workload == 'true'
|
||||
)
|
||||
)
|
||||
&& !cancelled()
|
||||
with:
|
||||
test_to_run: ${{ needs.configure.outputs.test_to_run }}
|
||||
runner: ${{ needs.pre-flight.outputs.runner_prefix }}
|
||||
run_nightly: ${{ needs.configure.outputs.run_nightly }}
|
||||
|
||||
Nemo_CICD_Test:
|
||||
needs:
|
||||
- configure
|
||||
- cicd-test-container-build
|
||||
- cicd-import-tests
|
||||
- L0_Setup_Test_Data_And_Models
|
||||
- cicd-main-unit-tests
|
||||
- cicd-main-speech
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get workflow result
|
||||
id: result
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
run: |
|
||||
# Get workflow run details and check job conclusions
|
||||
LATEST_ATTEMPT=$(gh run view $RUN_ID --json jobs -q '[.jobs[] | select(.conclusion != null) | .conclusion] | last')
|
||||
NUM_FAILED=$(gh run view $RUN_ID --json jobs -q '[.jobs[] | select(.conclusion == "failure") | .name] | length')
|
||||
NUM_CANCELLED=$(gh run view $RUN_ID --json jobs -q '[.jobs[] | select(.conclusion == "cancelled") | .name] | length')
|
||||
|
||||
if [[ $NUM_FAILED -eq 0 && $NUM_CANCELLED -eq 0 ]]; then
|
||||
RESULT="success"
|
||||
elif [[ $NUM_CANCELLED -gt 0 ]]; then
|
||||
RESULT="cancelled"
|
||||
else
|
||||
RESULT="failure"
|
||||
fi
|
||||
|
||||
# Output the final status
|
||||
echo "code=$RESULT" | tee -a $GITHUB_OUTPUT
|
||||
|
||||
- name: Pipeline successful, add PR comment
|
||||
if: |
|
||||
steps.result.outputs.code == 'success'
|
||||
&& github.event_name == 'push'
|
||||
&& startsWith(github.ref, 'refs/heads/pull-request/')
|
||||
&& env.SLACK_WEBHOOK != ''
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PR_NUMBER="${GITHUB_REF#refs/heads/pull-request/}"
|
||||
PR_AUTHOR=$(gh pr view "$PR_NUMBER" --repo "$REPOSITORY" --json author -q '.author.login')
|
||||
gh pr comment "$PR_NUMBER" --repo "$REPOSITORY" --body "[🤖]: Hi @${PR_AUTHOR} 👋,
|
||||
|
||||
We wanted to let you know that a [CICD pipeline](https://github.com/${REPOSITORY}/actions/runs/${RUN_ID}) for this PR just finished successfully.
|
||||
|
||||
So it might be time to merge this PR or get some approvals."
|
||||
|
||||
- name: Exit
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
RESULT: ${{ steps.result.outputs.code }}
|
||||
run: |
|
||||
if [ $RESULT == "success" ]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Coverage:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [configure, Nemo_CICD_Test]
|
||||
if: |
|
||||
needs.configure.outputs.test_to_run != '[]'
|
||||
&& needs.configure.outputs.components_to_run != '[]'
|
||||
&& (
|
||||
success()
|
||||
|| needs.Nemo_CICD_Test.result == 'success'
|
||||
)
|
||||
&& !cancelled()
|
||||
strategy:
|
||||
matrix:
|
||||
flag: [unit-test, e2e]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download coverage reports of current branch
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: coverage-${{ matrix.flag }}-*
|
||||
|
||||
- name: Get total coverage of current branch
|
||||
shell: bash -x -e -u -o pipefail {0}
|
||||
if: always()
|
||||
run: |
|
||||
pip install coverage
|
||||
|
||||
ls -al .
|
||||
ls -al coverage-*/
|
||||
coverage combine --keep $(ls coverage-*/.coverage)
|
||||
coverage report -i
|
||||
rm -rf coverage-*
|
||||
ls -al
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
verbose: true
|
||||
flags: ${{ matrix.flag }}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: coverage-${{ matrix.flag }}-aggregated
|
||||
path: |
|
||||
.coverage
|
||||
include-hidden-files: true
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Claude Answer Issue
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
authorize:
|
||||
if: >-
|
||||
!github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/claude answer')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check team membership
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.ORG_TEAM_READ_TOKEN }}
|
||||
script: |
|
||||
const username = context.payload.comment.user.login;
|
||||
try {
|
||||
const res = await github.rest.teams.getMembershipForUserInOrg({
|
||||
org: 'NVIDIA-NeMo',
|
||||
team_slug: 'speech_team',
|
||||
username,
|
||||
});
|
||||
if (res.data.state !== 'active') {
|
||||
core.setFailed(`${username} is not an active member of NVIDIA Speech Team`);
|
||||
}
|
||||
} catch (e) {
|
||||
core.setFailed(`${username} is not a member of NVIDIA Speech Team`);
|
||||
}
|
||||
|
||||
acknowledge:
|
||||
needs: authorize
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add eyes reaction to comment
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
content: 'eyes'
|
||||
});
|
||||
|
||||
claude-answer:
|
||||
needs: acknowledge
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
env:
|
||||
ANTHROPIC_BASE_URL: ${{ secrets.NVIDIA_INFERENCE_URL }}
|
||||
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"
|
||||
DISABLE_PROMPT_CACHING: "1"
|
||||
with:
|
||||
prompt: |
|
||||
You are a helpful assistant for the NeMo repository.
|
||||
Answer the user's question based on the issue description, comments, and the codebase.
|
||||
Be concise and provide code references where relevant.
|
||||
Do NOT make any code changes or create PRs — only answer the question.
|
||||
anthropic_api_key: ${{ secrets.NVIDIA_INFERENCE_KEY }}
|
||||
claude_args: --model "${{ vars.CLAUDE_MODEL }}"
|
||||
@@ -0,0 +1,648 @@
|
||||
# PR Babysitter — half-autonomous CI fix loop, speech_team-only.
|
||||
#
|
||||
# Master switch: the "Has Babysitter" label. Adding it activates, removing
|
||||
# it deactivates and cancels any in-flight work.
|
||||
#
|
||||
# Authorization model:
|
||||
# - The entire workflow is restricted to the NVIDIA-NeMo/speech_team team.
|
||||
# Every job that mutates state starts with a preflight that verifies the
|
||||
# acting user (label sender / comment author / review author) is an active
|
||||
# speech_team member. Non-members cause the job to post a refusal comment
|
||||
# and revert the triggering label, leaving no misleading state behind.
|
||||
# - The workflow never runs for PRs from forks. Every PR-scoped job
|
||||
# fork-guards on head.repo.full_name == github.repository.
|
||||
#
|
||||
# Lifecycle (all participants must be speech_team members):
|
||||
# 1. "Has Babysitter" added -> activate posts a takeover comment and adds
|
||||
# "Run CICD" to start the pipeline.
|
||||
# 2. CI runs (CICD NeMo workflow).
|
||||
# 3. If all checks pass -> babysitter stays silent, done.
|
||||
# If a CI workflow fails -> workflow_run completion event triggers
|
||||
# investigate-and-propose. The Isort+Black
|
||||
# workflow isn't in the trigger list — it
|
||||
# auto-pushes its own fixes.
|
||||
# 4. investigate-and-propose: Claude investigates root cause, posts a *plan
|
||||
# comment* on the PR (tagged with `<!-- babysit-plan -->`), and adds
|
||||
# "Agent Plan Awaiting Approval". It does NOT push code.
|
||||
# 5. The PR author (who must also be speech_team) approves by replying with a
|
||||
# comment that mentions `@claude` affirmatively (e.g. `@claude go ahead`).
|
||||
# evaluate-comment-approval classifies the reply via an LLM and, on APPROVE,
|
||||
# swaps "Agent Plan Awaiting Approval" for "Agent Plan Approved".
|
||||
# 6. "Agent Plan Approved" added -> execute-fix verifies the sender is
|
||||
# speech_team, verifies a bot-authored plan comment exists, then reads the
|
||||
# plan, pushes the fix, re-adds "Run CICD", and goes back to #2.
|
||||
#
|
||||
# Termination:
|
||||
# - "Has Babysitter" removed at any time -> deactivate cancels in-progress
|
||||
# work via a shared concurrency group and clears plan-state labels.
|
||||
# - Claude can't investigate or can't execute -> ping-author-on-failure pings
|
||||
# the author and clears labels.
|
||||
#
|
||||
# Additionally, speech_team members can @claude in a PR review comment on a
|
||||
# babysitter-enabled PR to ask questions or request changes.
|
||||
#
|
||||
# Required secrets: NVIDIA_INFERENCE_URL, NVIDIA_INFERENCE_KEY, ORG_TEAM_READ_TOKEN, NEMO_RELABEL_TOKEN
|
||||
|
||||
name: Claude Code — PR Babysitter
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, unlabeled]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
# `workflow_run` rather than `check_run`: check-runs produced by GHA jobs
|
||||
# authenticated with GITHUB_TOKEN do not fire `check_run` events on
|
||||
# downstream workflows (GitHub's recursion guard), so the babysitter never
|
||||
# saw real CI failures. `workflow_run` bypasses that restriction.
|
||||
# Omitted intentionally: "Isort and Black Formatting" (auto-pushes fixes),
|
||||
# this workflow itself, and labeler/relabel bots.
|
||||
workflow_run:
|
||||
workflows:
|
||||
- "CICD NeMo"
|
||||
- "PyLint and flake8 linting"
|
||||
- "Build, test, and publish a PyPi wheel (to testpypi)."
|
||||
- "Check __init__ files"
|
||||
- "Copyright check"
|
||||
- "CI-Install-Check"
|
||||
- "CodeQL"
|
||||
- "Secrets detector"
|
||||
types: [completed]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
# ---- Activation: one-time setup when "Has Babysitter" is added ----
|
||||
|
||||
activate:
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.action == 'labeled' &&
|
||||
github.event.label.name == 'Has Babysitter' &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: babysit-activate-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
# Preflight: only speech_team members may enable the babysitter. If a
|
||||
# non-member adds the label, revert it and explain.
|
||||
- name: Verify label sender is speech_team
|
||||
id: authz
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.ORG_TEAM_READ_TOKEN }}
|
||||
script: |
|
||||
const username = context.payload.sender.login;
|
||||
try {
|
||||
const res = await github.rest.teams.getMembershipForUserInOrg({
|
||||
org: 'NVIDIA-NeMo',
|
||||
team_slug: 'speech_team',
|
||||
username,
|
||||
});
|
||||
if (res.data.state !== 'active') {
|
||||
core.setOutput('authorized', 'false');
|
||||
return;
|
||||
}
|
||||
core.setOutput('authorized', 'true');
|
||||
} catch (e) {
|
||||
core.setOutput('authorized', 'false');
|
||||
}
|
||||
|
||||
- name: Checkout for GH CLI
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Refuse activation from non-speech_team sender
|
||||
if: steps.authz.outputs.authorized != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
SENDER: ${{ github.event.sender.login }}
|
||||
run: |
|
||||
gh pr comment "$PR_NUMBER" --body \
|
||||
"@${SENDER} the PR Babysitter is restricted to the NVIDIA-NeMo \`speech_team\`. Removing the \`Has Babysitter\` label."
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Has Babysitter"
|
||||
exit 0
|
||||
|
||||
- name: Post takeover comment
|
||||
if: steps.authz.outputs.authorized == 'true'
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: "I'll monitor this PR until CI is green. I'll post a plan for any fix and wait for your approval before pushing anything. Ping me by removing 'Has Babysitter' to cancel."
|
||||
});
|
||||
|
||||
- name: Trigger CI
|
||||
if: steps.authz.outputs.authorized == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: gh pr edit "$PR_NUMBER" --add-label "Run CICD"
|
||||
|
||||
# ---- @claude mentions in PR reviews (speech_team only) ----
|
||||
|
||||
authorize-review:
|
||||
if: >-
|
||||
github.event.pull_request.head.repo.full_name == github.repository &&
|
||||
((github.event_name == 'pull_request_review_comment' &&
|
||||
contains(github.event.comment.body, '@claude') &&
|
||||
contains(github.event.pull_request.labels.*.name, 'Has Babysitter')) ||
|
||||
(github.event_name == 'pull_request_review' &&
|
||||
contains(github.event.review.body, '@claude') &&
|
||||
contains(github.event.pull_request.labels.*.name, 'Has Babysitter')))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check team membership
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.ORG_TEAM_READ_TOKEN }}
|
||||
script: |
|
||||
const username =
|
||||
context.payload.comment?.user?.login ||
|
||||
context.payload.review?.user?.login;
|
||||
try {
|
||||
const res = await github.rest.teams.getMembershipForUserInOrg({
|
||||
org: 'NVIDIA-NeMo',
|
||||
team_slug: 'speech_team',
|
||||
username,
|
||||
});
|
||||
if (res.data.state !== 'active') {
|
||||
core.setFailed(`${username} is not an active member of NVIDIA Speech Team`);
|
||||
}
|
||||
} catch (e) {
|
||||
core.setFailed(`${username} is not a member of NVIDIA Speech Team`);
|
||||
}
|
||||
|
||||
acknowledge-review:
|
||||
needs: authorize-review
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add eyes reaction
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
if (context.payload.comment) {
|
||||
await github.rest.reactions.createForPullRequestReviewComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
content: 'eyes'
|
||||
});
|
||||
}
|
||||
|
||||
respond-to-review:
|
||||
needs: acknowledge-review
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
env:
|
||||
ANTHROPIC_BASE_URL: ${{ secrets.NVIDIA_INFERENCE_URL }}
|
||||
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"
|
||||
DISABLE_PROMPT_CACHING: "1"
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.NVIDIA_INFERENCE_KEY }}
|
||||
# No prompt — Claude reads the @claude mention and responds in context
|
||||
claude_args: "--max-turns 15 --model ${{ vars.CLAUDE_MODEL }}"
|
||||
|
||||
# ---- CI failure: investigate, propose a plan, DO NOT push ----
|
||||
|
||||
check-label-for-ci:
|
||||
# Fires on CI workflow completion. The `workflow_run.pull_requests` array is
|
||||
# populated for same-repo PRs whose head SHA matches the completed run; for
|
||||
# fork PRs it is empty, which doubles as an implicit fork-guard here.
|
||||
if: >-
|
||||
github.event_name == 'workflow_run' &&
|
||||
github.event.workflow_run.conclusion == 'failure' &&
|
||||
github.event.workflow_run.pull_requests[0] != null
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
pr_number: ${{ steps.lookup.outputs.pr_number }}
|
||||
should_propose: ${{ steps.lookup.outputs.should_propose }}
|
||||
head_ref: ${{ steps.lookup.outputs.head_ref }}
|
||||
head_sha: ${{ steps.lookup.outputs.head_sha }}
|
||||
author_login: ${{ steps.lookup.outputs.author_login }}
|
||||
steps:
|
||||
- name: Look up PR and assess state
|
||||
id: lookup
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const prNumber = context.payload.workflow_run.pull_requests[0].number;
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
const labels = pr.labels.map(l => l.name);
|
||||
const hasBabysitter = labels.includes('Has Babysitter');
|
||||
const hasPending = labels.includes('Agent Plan Awaiting Approval');
|
||||
const hasApproved = labels.includes('Agent Plan Approved');
|
||||
const failedSha = context.payload.workflow_run.head_sha;
|
||||
const isStale = pr.head.sha !== failedSha;
|
||||
// Fork-guard: the babysitter never runs on PRs from forks.
|
||||
const isFork = pr.head.repo.full_name !== `${context.repo.owner}/${context.repo.repo}`;
|
||||
// Propose a new plan only if: babysitter is on, no plan is already
|
||||
// in flight, the failure isn't stale, and the PR is not from a fork.
|
||||
const shouldPropose = hasBabysitter && !hasPending && !hasApproved && !isStale && !isFork;
|
||||
core.setOutput('pr_number', prNumber);
|
||||
core.setOutput('should_propose', shouldPropose ? 'true' : 'false');
|
||||
core.setOutput('head_ref', pr.head.ref);
|
||||
core.setOutput('head_sha', pr.head.sha);
|
||||
core.setOutput('author_login', pr.user.login);
|
||||
if (!shouldPropose) {
|
||||
core.info(`Skipping investigate-and-propose: babysitter=${hasBabysitter}, pending=${hasPending}, approved=${hasApproved}, stale=${isStale}, fork=${isFork}`);
|
||||
}
|
||||
|
||||
investigate-and-propose:
|
||||
needs: check-label-for-ci
|
||||
if: needs.check-label-for-ci.outputs.should_propose == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: babysit-fix-${{ needs.check-label-for-ci.outputs.pr_number }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.check-label-for-ci.outputs.head_ref }}
|
||||
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
env:
|
||||
ANTHROPIC_BASE_URL: ${{ secrets.NVIDIA_INFERENCE_URL }}
|
||||
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"
|
||||
DISABLE_PROMPT_CACHING: "1"
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.NVIDIA_INFERENCE_KEY }}
|
||||
prompt: |
|
||||
The CI workflow "${{ github.event.workflow_run.name }}" just failed
|
||||
on PR #${{ needs.check-label-for-ci.outputs.pr_number }}.
|
||||
|
||||
You are in HALF-AUTONOMOUS mode. You MUST NOT edit files, commit,
|
||||
or push code in this job. Investigation and planning only.
|
||||
|
||||
Instructions:
|
||||
1. Inspect the failure logs for ALL currently failing CI checks on
|
||||
this PR, not just the one named above.
|
||||
2. Check `git log` for recent commits — if a prior babysitter plan
|
||||
already tried the same approach and it's still failing, call
|
||||
that out in your plan so the author can reconsider.
|
||||
3. Identify the root cause and draft a concrete fix plan covering:
|
||||
- What's broken and why
|
||||
- Which files/lines you'd change
|
||||
- The minimal diff you'd apply
|
||||
4. Post a SINGLE PR comment with this exact structure:
|
||||
|
||||
**CI Fix Plan** — awaiting approval from @${{ needs.check-label-for-ci.outputs.author_login }}
|
||||
|
||||
<your analysis and proposed change>
|
||||
|
||||
---
|
||||
To approve, **reply with an affirmative comment that mentions `@claude`**
|
||||
(e.g., `@claude go ahead`). Approval is restricted to the PR author.
|
||||
Remove the `Has Babysitter` label to cancel.
|
||||
|
||||
<!-- babysit-plan -->
|
||||
|
||||
The `<!-- babysit-plan -->` marker MUST be the last line — later
|
||||
jobs use it to find the approved plan.
|
||||
5. If you conclude the issue cannot be fixed from this PR alone,
|
||||
DO NOT include the `<!-- babysit-plan -->` marker. Instead post
|
||||
a comment that mentions @${{ needs.check-label-for-ci.outputs.author_login }}
|
||||
describing what you found and asking for help.
|
||||
claude_args: "--max-turns 10 --model ${{ vars.CLAUDE_MODEL }}"
|
||||
|
||||
- name: Mark plan awaiting approval (or stop if Claude gave up)
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ needs.check-label-for-ci.outputs.pr_number }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
# Detect whether Claude actually posted a plan comment.
|
||||
PLAN_COUNT=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" \
|
||||
--jq '[.[] | select(.body | contains("<!-- babysit-plan -->"))] | length')
|
||||
if [ "$PLAN_COUNT" -gt 0 ]; then
|
||||
echo "Plan comment posted; adding 'Agent Plan Awaiting Approval'"
|
||||
gh pr edit "$PR_NUMBER" --add-label "Agent Plan Awaiting Approval"
|
||||
else
|
||||
echo "No plan posted; Claude decided it couldn't fix. Disabling babysitter."
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Has Babysitter"
|
||||
fi
|
||||
|
||||
# ---- Approval Path A: affirmative comment from PR author mentioning @claude ----
|
||||
|
||||
evaluate-comment-approval:
|
||||
if: >-
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.action == 'created' &&
|
||||
github.event.issue.pull_request != null &&
|
||||
contains(github.event.issue.labels.*.name, 'Has Babysitter') &&
|
||||
contains(github.event.issue.labels.*.name, 'Agent Plan Awaiting Approval') &&
|
||||
github.event.comment.user.type != 'Bot' &&
|
||||
github.event.comment.user.login == github.event.issue.user.login &&
|
||||
contains(github.event.comment.body, '@claude')
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: babysit-fix-${{ github.event.issue.number }}
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
# Preflight: the commenter is the PR author (enforced by `if:`) AND must
|
||||
# be an active speech_team member. Also fork-guard by fetching the PR.
|
||||
- name: Verify commenter is speech_team and PR is not a fork
|
||||
id: authz
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.ORG_TEAM_READ_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.issue.number;
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
const isFork = pr.head.repo.full_name !== `${context.repo.owner}/${context.repo.repo}`;
|
||||
if (isFork) {
|
||||
core.info(`PR #${prNumber} is a fork; babysitter does not operate on forks.`);
|
||||
core.setOutput('authorized', 'false');
|
||||
core.setOutput('reason', 'fork');
|
||||
return;
|
||||
}
|
||||
const username = context.payload.comment.user.login;
|
||||
try {
|
||||
const res = await github.rest.teams.getMembershipForUserInOrg({
|
||||
org: 'NVIDIA-NeMo',
|
||||
team_slug: 'speech_team',
|
||||
username,
|
||||
});
|
||||
if (res.data.state !== 'active') {
|
||||
core.setOutput('authorized', 'false');
|
||||
core.setOutput('reason', 'not-speech-team');
|
||||
return;
|
||||
}
|
||||
core.setOutput('authorized', 'true');
|
||||
} catch (e) {
|
||||
core.setOutput('authorized', 'false');
|
||||
core.setOutput('reason', 'not-speech-team');
|
||||
}
|
||||
|
||||
- name: Checkout for GH CLI
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Refuse non-speech_team approval
|
||||
if: steps.authz.outputs.authorized != 'true' && steps.authz.outputs.reason == 'not-speech-team'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.issue.number }}
|
||||
SENDER: ${{ github.event.comment.user.login }}
|
||||
run: |
|
||||
gh pr comment "$PR_NUMBER" --body \
|
||||
"@${SENDER} only NVIDIA-NeMo \`speech_team\` members can approve the babysitter's plan. Ignoring this reply."
|
||||
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
if: steps.authz.outputs.authorized == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.issue.number }}
|
||||
ANTHROPIC_BASE_URL: ${{ secrets.NVIDIA_INFERENCE_URL }}
|
||||
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"
|
||||
DISABLE_PROMPT_CACHING: "1"
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.NVIDIA_INFERENCE_KEY }}
|
||||
prompt: |
|
||||
Decide whether the PR author has approved the fix plan, then act.
|
||||
|
||||
Context:
|
||||
- PR #${{ github.event.issue.number }} has label "Agent Plan Awaiting Approval".
|
||||
- The PR author (@${{ github.event.issue.user.login }}) just posted a reply.
|
||||
- The approved plan is a previous PR comment containing `<!-- babysit-plan -->`.
|
||||
|
||||
Steps:
|
||||
1. Read the latest `<!-- babysit-plan -->` comment on the PR for the plan.
|
||||
2. Read the PR author's reply (use `gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}`).
|
||||
3. Classify the reply as exactly one of:
|
||||
- APPROVE: the author clearly agrees to proceed with the plan.
|
||||
- REJECT: the author clearly disagrees or wants changes.
|
||||
- NEITHER: the reply is a question, side remark, or ambiguous.
|
||||
4. Take action:
|
||||
- APPROVE: run exactly
|
||||
`gh pr edit $PR_NUMBER --remove-label "Agent Plan Awaiting Approval" --add-label "Agent Plan Approved"`
|
||||
and do nothing else.
|
||||
- REJECT: post a short PR comment acknowledging the rejection
|
||||
and suggesting they either remove `Has Babysitter` to cancel
|
||||
or reply with a new direction. Do NOT change labels.
|
||||
- NEITHER: post a short PR comment asking for an explicit
|
||||
approve/reject. Do NOT change labels.
|
||||
5. Do not push any code. Do not edit files in the repository.
|
||||
claude_args: "--max-turns 8 --model ${{ vars.CLAUDE_MODEL }}"
|
||||
|
||||
# ---- Execute the approved plan (the only job allowed to push code) ----
|
||||
|
||||
execute-fix:
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.action == 'labeled' &&
|
||||
github.event.label.name == 'Agent Plan Approved' &&
|
||||
contains(github.event.pull_request.labels.*.name, 'Has Babysitter') &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: babysit-fix-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
# Preflight 1: the sender (who added `Agent Plan Approved`) must be
|
||||
# speech_team. Defense-in-depth against a non-member adding the label.
|
||||
- name: Verify label sender is speech_team
|
||||
id: authz
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.ORG_TEAM_READ_TOKEN }}
|
||||
script: |
|
||||
const username = context.payload.sender.login;
|
||||
try {
|
||||
const res = await github.rest.teams.getMembershipForUserInOrg({
|
||||
org: 'NVIDIA-NeMo',
|
||||
team_slug: 'speech_team',
|
||||
username,
|
||||
});
|
||||
core.setOutput('authorized', res.data.state === 'active' ? 'true' : 'false');
|
||||
} catch (e) {
|
||||
core.setOutput('authorized', 'false');
|
||||
}
|
||||
|
||||
# Preflight 2: there must exist a bot-authored `<!-- babysit-plan -->`
|
||||
# comment on this PR. Prevents execution when someone adds the approved
|
||||
# label manually without an investigator-posted plan.
|
||||
- name: Verify a bot-authored plan comment exists
|
||||
id: plan
|
||||
if: steps.authz.outputs.authorized == 'true'
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
per_page: 100,
|
||||
});
|
||||
const planComment = [...comments].reverse().find(c =>
|
||||
c.body.includes('<!-- babysit-plan -->') && c.user && c.user.type === 'Bot'
|
||||
);
|
||||
core.setOutput('found', planComment ? 'true' : 'false');
|
||||
|
||||
- name: Checkout for GH CLI (refusal paths)
|
||||
if: steps.authz.outputs.authorized != 'true' || steps.plan.outputs.found != 'true'
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Refuse execution from non-speech_team sender
|
||||
if: steps.authz.outputs.authorized != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
SENDER: ${{ github.event.sender.login }}
|
||||
run: |
|
||||
gh pr comment "$PR_NUMBER" --body \
|
||||
"@${SENDER} only NVIDIA-NeMo \`speech_team\` members can approve the babysitter's plan. Removing the \`Agent Plan Approved\` label."
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Agent Plan Approved" || true
|
||||
exit 0
|
||||
|
||||
- name: Refuse execution without a plan comment
|
||||
if: steps.authz.outputs.authorized == 'true' && steps.plan.outputs.found != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
gh pr comment "$PR_NUMBER" --body \
|
||||
"No approved plan found for execution. Remove \`Agent Plan Approved\` and wait for the investigator to post a plan first."
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Agent Plan Approved" || true
|
||||
exit 0
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
if: steps.authz.outputs.authorized == 'true' && steps.plan.outputs.found == 'true'
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
|
||||
- name: Record starting SHA
|
||||
id: start
|
||||
if: steps.authz.outputs.authorized == 'true' && steps.plan.outputs.found == 'true'
|
||||
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
if: steps.authz.outputs.authorized == 'true' && steps.plan.outputs.found == 'true'
|
||||
env:
|
||||
ANTHROPIC_BASE_URL: ${{ secrets.NVIDIA_INFERENCE_URL }}
|
||||
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"
|
||||
DISABLE_PROMPT_CACHING: "1"
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.NVIDIA_INFERENCE_KEY }}
|
||||
prompt: |
|
||||
The PR author has approved your previously posted fix plan. Execute it now.
|
||||
|
||||
Instructions:
|
||||
1. Read the PR comment thread and find the most recent comment
|
||||
containing the marker `<!-- babysit-plan -->`. That is the
|
||||
approved plan.
|
||||
2. Implement exactly what the plan described. Do not expand scope.
|
||||
Do not refactor code outside the plan.
|
||||
3. Commit with DCO sign-off and push to the same branch.
|
||||
4. If the plan is no longer applicable (e.g., merge conflicts, the
|
||||
code has moved on, the reproduction no longer triggers the bug):
|
||||
post a PR comment mentioning @${{ github.event.pull_request.user.login }}
|
||||
explaining what changed and DO NOT push.
|
||||
claude_args: "--max-turns 10 --model ${{ vars.CLAUDE_MODEL }}"
|
||||
|
||||
- name: Re-trigger CI or stop loop
|
||||
if: steps.authz.outputs.authorized == 'true' && steps.plan.outputs.found == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
ORIGINAL_SHA: ${{ steps.start.outputs.sha }}
|
||||
run: |
|
||||
# Clear "Agent Plan Approved" so the next iteration can re-use it.
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Agent Plan Approved" || true
|
||||
NEW_SHA=$(gh pr view "$PR_NUMBER" --json headRefOid -q '.headRefOid')
|
||||
if [ "$NEW_SHA" != "$ORIGINAL_SHA" ]; then
|
||||
echo "Fix pushed (SHA changed); re-triggering CI"
|
||||
gh pr edit "$PR_NUMBER" --add-label "Run CICD"
|
||||
else
|
||||
echo "No fix pushed; disabling babysitter"
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Has Babysitter"
|
||||
fi
|
||||
|
||||
# ---- Safety net: ping author and clear state if investigate-and-propose crashes ----
|
||||
|
||||
ping-author-on-failure:
|
||||
needs: [check-label-for-ci, investigate-and-propose]
|
||||
if: failure() && needs.check-label-for-ci.outputs.should_propose == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Ping PR author
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.check-label-for-ci.outputs.pr_number }}
|
||||
CHECK_NAME: ${{ github.event.workflow_run.name }}
|
||||
AUTHOR_LOGIN: ${{ needs.check-label-for-ci.outputs.author_login }}
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: parseInt(process.env.PR_NUMBER, 10),
|
||||
body: `I wasn't able to investigate the CI failure in \`${process.env.CHECK_NAME}\`. @${process.env.AUTHOR_LOGIN}, could you take a look?`
|
||||
});
|
||||
|
||||
- name: Checkout for GH CLI
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Clear all babysitter labels
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ needs.check-label-for-ci.outputs.pr_number }}
|
||||
run: |
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Has Babysitter" || true
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Agent Plan Awaiting Approval" || true
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Agent Plan Approved" || true
|
||||
|
||||
# ---- Deactivation: cancel in-progress fixes and clear state ----
|
||||
|
||||
deactivate:
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.action == 'unlabeled' &&
|
||||
github.event.label.name == 'Has Babysitter'
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: babysit-fix-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Post deactivation comment
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: "Babysitter deactivated for this PR."
|
||||
});
|
||||
|
||||
- name: Checkout for GH CLI
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Clear plan-state labels
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.NEMO_RELABEL_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Agent Plan Awaiting Approval" || true
|
||||
gh pr edit "$PR_NUMBER" --remove-label "Agent Plan Approved" || true
|
||||
@@ -0,0 +1,119 @@
|
||||
name: Claude Fix Issue
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
authorize:
|
||||
if: >-
|
||||
!github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/claude fix')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check team membership
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.ORG_TEAM_READ_TOKEN }}
|
||||
script: |
|
||||
const username = context.payload.comment.user.login;
|
||||
try {
|
||||
const res = await github.rest.teams.getMembershipForUserInOrg({
|
||||
org: 'NVIDIA-NeMo',
|
||||
team_slug: 'speech_team',
|
||||
username,
|
||||
});
|
||||
if (res.data.state !== 'active') {
|
||||
core.setFailed(`${username} is not an active member of NVIDIA Speech Team`);
|
||||
}
|
||||
} catch (e) {
|
||||
core.setFailed(`${username} is not a member of NVIDIA Speech Team`);
|
||||
}
|
||||
|
||||
acknowledge:
|
||||
needs: authorize
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add eyes reaction to comment
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
content: 'eyes'
|
||||
});
|
||||
|
||||
claude-fix:
|
||||
needs: acknowledge
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
id: claude
|
||||
env:
|
||||
ANTHROPIC_BASE_URL: ${{ secrets.NVIDIA_INFERENCE_URL }}
|
||||
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"
|
||||
DISABLE_PROMPT_CACHING: "1"
|
||||
with:
|
||||
prompt: |
|
||||
You are a developer working on the NeMo repository.
|
||||
Implement a focused fix for the issue based on the issue description and comments.
|
||||
|
||||
Requirements:
|
||||
- Always use `git commit -s` to sign off all commits (DCO requirement).
|
||||
- Prioritize correctness and minimal scope; avoid unrelated refactors.
|
||||
- Reproduce or reason about the failure first, then implement the smallest robust fix.
|
||||
- If required, add or update tests for the changed behavior. If tests are not feasible, explain why.
|
||||
- If required, update related docs or comments when behavior or usage changes.
|
||||
|
||||
PR expectations:
|
||||
- Create a new branch and open a pull request with your changes.
|
||||
- Include a concise summary of root cause and fix based on the following PR template:
|
||||
```
|
||||
# What does this PR do ?
|
||||
Add a one line overview of what this PR aims to accomplish.
|
||||
|
||||
# Changelog
|
||||
Add specific line by line info of high level changes in this PR.
|
||||
|
||||
# Usage
|
||||
Add a usage example of the changed functionality.
|
||||
|
||||
Related to #${{ github.event.issue.number }}
|
||||
|
||||
This PR is created by Claude.
|
||||
```
|
||||
|
||||
|
||||
anthropic_api_key: ${{ secrets.NVIDIA_INFERENCE_KEY }}
|
||||
claude_args: --model "${{ vars.CLAUDE_MODEL }}"
|
||||
|
||||
- name: Label PR with agent-contribution
|
||||
if: steps.claude.outputs.branch_name
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const prs = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
head: `${context.repo.owner}:${process.env.BRANCH_NAME}`,
|
||||
state: 'open'
|
||||
});
|
||||
if (prs.data.length > 0) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prs.data[0].number,
|
||||
labels: ['agent-contribution']
|
||||
});
|
||||
}
|
||||
env:
|
||||
BRANCH_NAME: ${{ steps.claude.outputs.branch_name }}
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Claude Code Review
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
acknowledge:
|
||||
if: >-
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/claude review')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add eyes reaction to comment
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
content: 'eyes'
|
||||
});
|
||||
|
||||
claude-review:
|
||||
needs: acknowledge
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_claude_review.yml@v1.7.0
|
||||
with:
|
||||
model: ${{ vars.CLAUDE_MODEL }}
|
||||
prompt: |
|
||||
You are doing a light code review. Keep it concise and actionable.
|
||||
|
||||
Focus ONLY on:
|
||||
- Critical bugs or logic errors
|
||||
- Typos in code, comments, or strings
|
||||
- Missing or insufficient test coverage for changed code
|
||||
- Outdated or inaccurate documentation affected by the changes
|
||||
|
||||
Do NOT comment on:
|
||||
- Style preferences or formatting
|
||||
- Minor naming suggestions
|
||||
- Architectural opinions or refactoring ideas
|
||||
- Performance unless there is a clear, measurable issue
|
||||
|
||||
Provide feedback using inline comments for specific code suggestions.
|
||||
Use top-level comments for general observations.
|
||||
|
||||
IMPORTANT: Do NOT approve the pull request. Only leave comments.
|
||||
|
||||
It's perfectly acceptable to not have anything to comment on.
|
||||
If you do not have anything to comment on, post "LGTM".
|
||||
secrets:
|
||||
NVIDIA_INFERENCE_URL: ${{ secrets.NVIDIA_INFERENCE_URL }}
|
||||
NVIDIA_INFERENCE_KEY: ${{ secrets.NVIDIA_INFERENCE_KEY }}
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Stale-Close-Inactive-Issues-PRs
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
operations-per-run: 100
|
||||
days-before-issue-stale: 30
|
||||
days-before-issue-close: 7
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale."
|
||||
days-before-pr-stale: 14
|
||||
days-before-pr-close: 7
|
||||
stale-pr-message: "This PR is stale because it has been open for 14 days with no activity. Remove stale label or comment or update or this will be closed in 7 days."
|
||||
close-pr-message: "This PR was closed because it has been inactive for 7 days since being marked as stale."
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Isort and Black Formatting Check
|
||||
# Check that changed files are formatted with black and isort.
|
||||
# Fails if any file needs reformatting — run `pre-commit run --all-files` locally to fix.
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.py"
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -x -e -u -o pipefail {0}
|
||||
|
||||
jobs:
|
||||
check_isort_and_black:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: step-security/changed-files@v45.0.1
|
||||
with:
|
||||
files: |
|
||||
**.py
|
||||
|
||||
- name: Setup Python env
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: black
|
||||
uses: psf/black@stable
|
||||
if: ${{ steps.changed-files.outputs.any_changed == 'true' }}
|
||||
with:
|
||||
options: "--check --verbose"
|
||||
# check only changed files (pass explicitly the files)
|
||||
src: "${{ steps.changed-files.outputs.all_changed_files }}"
|
||||
version: "~= 24.3"
|
||||
|
||||
- name: isort
|
||||
uses: isort/isort-action@v1
|
||||
if: ${{ steps.changed-files.outputs.any_changed == 'true' }}
|
||||
with:
|
||||
isort-version: "5.13.2"
|
||||
configuration: "--check-only --diff"
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Check __init__ files
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
check-init-files:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install init-file-checker
|
||||
run: pip install init-file-checker
|
||||
|
||||
- name: Run init-file-checker
|
||||
run: init-file-checker nemo/
|
||||
@@ -0,0 +1,178 @@
|
||||
name: PyLint and flake8 linting
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip_docs:
|
||||
required: false
|
||||
default: 'false'
|
||||
type: string
|
||||
skip_linting:
|
||||
required: false
|
||||
default: 'false'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
linting:
|
||||
name: "Domain: ${{ matrix.domain }}"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
domain: [speech, other]
|
||||
env:
|
||||
DOMAIN: ${{ matrix.domain }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get PR info
|
||||
id: get-pr-info
|
||||
if: startsWith(github.ref, 'refs/heads/pull-request/')
|
||||
uses: nv-gha-runners/get-pr-info@main
|
||||
|
||||
- name: Select filter
|
||||
id: filter
|
||||
run: |
|
||||
if [[ "$DOMAIN" == "speech" ]]; then
|
||||
FILTER=$(jq -crn '[
|
||||
"nemo/collections/common/data/lhotse/*.py",
|
||||
"nemo/collections/asr/**/*.py",
|
||||
"nemo/collections/tts/**/*.py",
|
||||
"nemo/collections/audio/**/*.py",
|
||||
"nemo/collections/multimodal/speech_llm/**/*.py",
|
||||
"nemo/collections/speechlm/**/*.py",
|
||||
"nemo/collections/speechlm2/**/*.py"
|
||||
] | join(",")')
|
||||
|
||||
else
|
||||
FILTER=$(jq -crn '[
|
||||
"nemo/**/*.py",
|
||||
"!nemo/collections/common/data/lhotse/*.py",
|
||||
"!nemo/collections/asr/**/*.py",
|
||||
"!nemo/collections/tts/**/*.py",
|
||||
"!nemo/collections/audio/**/*.py",
|
||||
"!nemo/collections/multimodal/speech_llm/**/*.py",
|
||||
"!nemo/collections/speechlm/**/*.py",
|
||||
"!nemo/collections/speechlm2/**/*.py",
|
||||
"!nemo/export/**/*.py"
|
||||
] | join(",")')
|
||||
fi
|
||||
|
||||
echo "main=$FILTER" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: step-security/changed-files@v45.0.1
|
||||
with:
|
||||
sha: ${{ github.sha }}
|
||||
base_sha: ${{ fromJSON(steps.get-pr-info.outputs.pr-info || '{}').base.sha || github.event.pull_request.base.sha }}
|
||||
files: ${{ steps.filter.outputs.main }}
|
||||
files_separator: ","
|
||||
separator: " "
|
||||
|
||||
- name: Run PyLint
|
||||
id: pylint
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
SKIP_DOCS: ${{ inputs.skip_docs == 'true' || contains(github.event.pull_request.labels.*.name, 'skip-docs') }}
|
||||
SKIP_LINTING: ${{ inputs.skip_linting == 'true' || contains(github.event.pull_request.labels.*.name, 'skip-linting') }}
|
||||
run: |
|
||||
if [[ -z "$CHANGED_FILES" ]]; then
|
||||
echo Nothing to lint.
|
||||
echo "exit-code=0" | tee -a "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $SKIP_DOCS == true ]]; then
|
||||
ADDITIONAL_PYLINT_ARGS="--disable=C0115,C0116"
|
||||
else
|
||||
ADDITIONAL_PYLINT_ARGS=""
|
||||
fi
|
||||
|
||||
if [[ $SKIP_LINTING == true ]]; then
|
||||
ADDITIONAL_PYLINT_ARGS="--exit-zero"
|
||||
fi
|
||||
|
||||
pip install pylint
|
||||
set +e
|
||||
pylint $ADDITIONAL_PYLINT_ARGS --output "pylintrc.$DOMAIN.txt" --rcfile ".pylintrc.$DOMAIN" ${CHANGED_FILES[@]}
|
||||
echo "exit-code=$?" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Run flake8
|
||||
id: flake8
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
SKIP_LINTING: ${{ inputs.skip_linting == 'true' || contains(github.event.pull_request.labels.*.name, 'skip-linting') }}
|
||||
run: |
|
||||
if [[ -z "$CHANGED_FILES" ]]; then
|
||||
echo Nothing to lint.
|
||||
echo "exit-code=0" | tee -a "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $SKIP_LINTING == true ]]; then
|
||||
ADDITIONAL_FLAKE8_ARGS="--exit-zero"
|
||||
else
|
||||
ADDITIONAL_FLAKE8_ARGS=""
|
||||
fi
|
||||
|
||||
pip install flake8
|
||||
set +e
|
||||
flake8 $ADDITIONAL_FLAKE8_ARGS --output "flake8.$DOMAIN.txt" --config ".flake8.$DOMAIN" ${CHANGED_FILES[@]}
|
||||
echo "exit-code=$?" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Summary
|
||||
env:
|
||||
PYLINT: ${{ steps.pylint.outputs.exit-code == 0 }}
|
||||
FLAKE8: ${{ steps.flake8.outputs.exit-code == 0 }}
|
||||
run: |
|
||||
|
||||
if [[ "$PYLINT" != "true" ]]; then
|
||||
echo "Pylint output:" | tee -a $GITHUB_STEP_SUMMARY
|
||||
|
||||
echo '```' | tee -a $GITHUB_STEP_SUMMARY
|
||||
cat pylintrc.$DOMAIN.txt | tee -a $GITHUB_STEP_SUMMARY
|
||||
echo '```' | tee -a $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [[ "$FLAKE8" != "true" ]]; then
|
||||
echo "Flake8 output:" | tee -a $GITHUB_STEP_SUMMARY
|
||||
|
||||
echo '```' | tee -a $GITHUB_STEP_SUMMARY
|
||||
cat flake8.$DOMAIN.txt | tee -a $GITHUB_STEP_SUMMARY
|
||||
echo '```' | tee -a $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [[ "$PYLINT" != "true" || "$FLAKE8" != "true" ]]; then
|
||||
echo "The following directories got scanned:" | tee -a $GITHUB_STEP_SUMMARY
|
||||
|
||||
echo '```' | tee -a $GITHUB_STEP_SUMMARY
|
||||
echo ${{ steps.filter.outputs.main }} | tee -a $GITHUB_STEP_SUMMARY
|
||||
echo '```' | tee -a $GITHUB_STEP_SUMMARY
|
||||
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Nemo_Linting_Test:
|
||||
needs: linting
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Main
|
||||
env:
|
||||
RESULTS: ${{ toJson(needs.linting) }}
|
||||
run: |
|
||||
RESULT=$(echo "$RESULTS" | jq -r '.result')
|
||||
|
||||
if [[ "$RESULT" == "success" ]]; then
|
||||
echo "All passed."
|
||||
exit 0
|
||||
else
|
||||
echo "Some linting domains failed."
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,75 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "[rv][0-9]*", "gh-pages-src" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '19 1 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
queries: security-and-quality # security-extended,
|
||||
config-file: ./.github/workflows/config/codeql.yml
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -0,0 +1,15 @@
|
||||
name: Community Bot
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited, reopened, closed, deleted]
|
||||
issue_comment:
|
||||
types: [created, edited, deleted]
|
||||
|
||||
jobs:
|
||||
community-bot:
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_community_bot.yml@v0.62.0
|
||||
with:
|
||||
community_project_id: ${{ vars.COMMUNITY_PROJECT_ID }}
|
||||
secrets:
|
||||
GH_TOKEN: ${{ secrets.PAT }}
|
||||
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"title": "## ASR\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"asr"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "</details>\n\n## TTS\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"tts"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "</details>\n\n## NLP / NMT\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"nlp",
|
||||
"nmt",
|
||||
"megatron"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "</details>\n\n## Text Normalization / Inverse Text Normalization\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"tn",
|
||||
"itn"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "</details>\n\n## NeMo Tools\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"tools"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "</details>\n\n## Export\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"export"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "</details>\n\n## Documentation\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"docs"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "</details>\n\n## Bugfixes\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"bug"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "</details>\n\n## Cherrypick\n\n<details><summary>Changelog</summary>",
|
||||
"labels": [
|
||||
"cherry-pick"
|
||||
],
|
||||
"exclude_labels": [
|
||||
"cherry-pick"
|
||||
]
|
||||
}
|
||||
],
|
||||
"ignore_labels": [
|
||||
"ignore"
|
||||
],
|
||||
"sort": "ASC",
|
||||
"template": "\n${{CHANGELOG}}</details>\n\n## Uncategorized:\n\n<details><summary>Changelog</summary>\n\n${{UNCATEGORIZED}}\n</details>\n",
|
||||
"pr_template": "- ${{TITLE}} by @${{AUTHOR}} :: PR: #${{NUMBER}}",
|
||||
"empty_template": "${{OWNER}}\n${{REPO}}\n${{FROM_TAG}}\n${{TO_TAG}}",
|
||||
"label_extractor": [
|
||||
{
|
||||
"pattern": "(.*tts.*)|(.*g2p.*)",
|
||||
"target": "tts",
|
||||
"flags": "gimu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*asr.*)|(.*ctc.*)|(.*rnnt.*)|(.*transducer.*)|(.*dali.*)|(.*k2.*)",
|
||||
"target": "asr",
|
||||
"flags": "gimu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*nlp.*)|(.*punctuation.*)|(.*capitalization.*)|(.*entity.*)|(.*glue.*)|(.*entity.*)|(.*retrieval.*)|(.*entity.*)|(.*intent.*)|(.*slot.*)|(.*entity.*)|(.*language.*)|(.*qa.*)|(.*token class.*)|(.*text class.*)",
|
||||
"target": "nlp",
|
||||
"flags": "gimu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*nmt.*)|(.*bignlp.*)|(.*megatron.*)|(.*machine.*)|(.*translation.*)|(.*gpt.*)",
|
||||
"target": "nmt",
|
||||
"flags": "gimu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*tn.*)|(.*itn.*)|(.*text norm.*)",
|
||||
"target": "tn",
|
||||
"flags": "gimu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*sde.*)|(.*ctc segment.*)",
|
||||
"target": "tools",
|
||||
"flags": "gimu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*trt.*)|(.*onnx.*)|(.*export.*)",
|
||||
"target": "export",
|
||||
"flags": "gimu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*\\[x\\] Documentation.*)",
|
||||
"target": "docs",
|
||||
"flags": "gmu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*\\[x\\] Bugfix.*)|(.*patch.*)",
|
||||
"target": "bug",
|
||||
"flags": "gmu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pattern": "(.*cherry-pick.*)|(.*cherrypick.*)",
|
||||
"target": "cherrypick",
|
||||
"flags": "gimu",
|
||||
"on_property": [
|
||||
"title",
|
||||
"body"
|
||||
]
|
||||
}
|
||||
],
|
||||
"duplicate_filter": {
|
||||
"pattern": ".+",
|
||||
"on_property": "title",
|
||||
"method": "match"
|
||||
},
|
||||
"transformers": [
|
||||
{
|
||||
"pattern": "^cp:\\s*`(.+?)`\\s*into\\s*\\S+",
|
||||
"target": "$1"
|
||||
}
|
||||
],
|
||||
"max_tags_to_fetch": 100,
|
||||
"max_pull_requests": 500,
|
||||
"max_back_track_time_days": 365,
|
||||
"exclude_merge_branches": [],
|
||||
"tag_resolver": {
|
||||
"method": "semver"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
name: "CodeQL config"
|
||||
|
||||
paths:
|
||||
- nemo/
|
||||
- tests/
|
||||
- tools/
|
||||
- scripts/
|
||||
- examples/
|
||||
- .github/
|
||||
@@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2020-2021, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Copyright check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
copyright-check:
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_copyright_check.yml@v0.2.0
|
||||
@@ -0,0 +1,283 @@
|
||||
name: CI-Install-Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test-installs-macos:
|
||||
name: ${{ matrix.os }}-py${{ matrix.python }}-${{ matrix.installer }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest]
|
||||
python: ["3.10", "3.11", "3.12"]
|
||||
installer: ["pip-install", "nemo-install"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check disk space before cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Free up disk space
|
||||
run: |
|
||||
# Remove unnecessary files on macOS
|
||||
sudo rm -rf /usr/local/lib/android || true
|
||||
sudo rm -rf /usr/local/.ghcup || true
|
||||
sudo rm -rf /usr/local/lib/node_modules || true
|
||||
brew cleanup || true
|
||||
# Clear pip cache
|
||||
pip cache purge || true
|
||||
|
||||
- name: Check disk space after cleanup
|
||||
run: df -h
|
||||
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "${{ matrix.python }}"
|
||||
|
||||
- name: Install NeMo
|
||||
env:
|
||||
INSTALLER: ${{ matrix.installer }}
|
||||
NEMO_TAG: ${{ github.sha }}
|
||||
NEMO_REPO: ${{ github.server_url }}/${{ github.repository }}
|
||||
run: |
|
||||
if [[ "$INSTALLER" == "pip-install" ]]; then
|
||||
pip install --no-cache-dir -U pip
|
||||
pip install --no-cache-dir ".[all]"
|
||||
else
|
||||
export NEMO_TAG
|
||||
export NEMO_REPO
|
||||
export INSTALL_DIR=$(pwd)
|
||||
|
||||
pip install --no-cache-dir ".[all]"
|
||||
fi
|
||||
|
||||
- name: Check disk space after installation
|
||||
run: df -h
|
||||
|
||||
- name: Run import checks
|
||||
run: |
|
||||
# Run import checks
|
||||
for collection in "asr" "tts" "lightning" "core"; do
|
||||
python tests/core_ptl/check_imports.py --domain "$collection"
|
||||
done
|
||||
|
||||
test-installs-linux-amd:
|
||||
name: ubuntu-22.04-amd-py${{ matrix.python }}-${{ matrix.installer }}
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python: ["3.10", "3.11", "3.12"]
|
||||
installer: ["pip-install", "nemo-install"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check disk space before cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Free up disk space
|
||||
run: |
|
||||
# Remove unnecessary packages and files on Ubuntu
|
||||
sudo apt-get clean
|
||||
sudo rm -rf /usr/local/lib/android || true
|
||||
sudo rm -rf /opt/ghc || true
|
||||
sudo rm -rf /usr/local/.ghcup || true
|
||||
sudo rm -rf /usr/share/dotnet || true
|
||||
sudo rm -rf /opt/az || true
|
||||
# Clear pip and npm caches
|
||||
pip cache purge || true
|
||||
sudo npm cache clean --force || true
|
||||
|
||||
- name: Check disk space after cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Install NeMo
|
||||
env:
|
||||
INSTALLER: ${{ matrix.installer }}
|
||||
run: |
|
||||
if [ "$INSTALLER" = "pip-install" ]; then
|
||||
pip install --no-cache-dir --upgrade pip
|
||||
pip install --no-cache-dir ".[all]"
|
||||
else
|
||||
export INSTALL_DIR=$(pwd)
|
||||
pip install --no-cache-dir ".[all]"
|
||||
fi
|
||||
|
||||
- name: Check disk space after installation
|
||||
run: df -h
|
||||
|
||||
- name: Run import checks
|
||||
run: |
|
||||
# Run import checks
|
||||
for collection in "asr" "tts" "lightning" "core"; do
|
||||
python tests/core_ptl/check_imports.py --domain "$collection"
|
||||
done
|
||||
|
||||
test-asr-install-linux-amd:
|
||||
name: ubuntu-22.04-amd-py${{ matrix.python }}-asr
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python: ["3.10", "3.11", "3.12"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check disk space before cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Free up disk space
|
||||
run: |
|
||||
# Remove unnecessary packages and files on Ubuntu
|
||||
sudo apt-get clean
|
||||
sudo rm -rf /usr/local/lib/android || true
|
||||
sudo rm -rf /opt/ghc || true
|
||||
sudo rm -rf /usr/local/.ghcup || true
|
||||
sudo rm -rf /usr/share/dotnet || true
|
||||
sudo rm -rf /opt/az || true
|
||||
# Clear pip and npm caches
|
||||
pip cache purge || true
|
||||
sudo npm cache clean --force || true
|
||||
|
||||
- name: Check disk space after cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Install NeMo
|
||||
run: |
|
||||
pip install --no-cache-dir --upgrade pip
|
||||
pip install --no-cache-dir ".[asr]"
|
||||
|
||||
- name: Check disk space after installation
|
||||
run: df -h
|
||||
|
||||
- name: Run import checks
|
||||
run: |
|
||||
# Run import checks
|
||||
python tests/core_ptl/check_imports.py --domain asr
|
||||
|
||||
test-installs-linux-arm:
|
||||
name: ubuntu-22.04-arm-py${{ matrix.python }}-${{ matrix.installer }}
|
||||
runs-on: ubuntu-22.04-arm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python: ["3.10", "3.11", "3.12"]
|
||||
installer: ["pip-install", "nemo-install"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check disk space before cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Free up disk space
|
||||
run: |
|
||||
# Remove unnecessary packages and files on Ubuntu ARM
|
||||
sudo apt-get clean
|
||||
sudo rm -rf /usr/local/lib/android || true
|
||||
sudo rm -rf /opt/ghc || true
|
||||
sudo rm -rf /usr/local/.ghcup || true
|
||||
sudo rm -rf /usr/share/dotnet || true
|
||||
sudo rm -rf /opt/az || true
|
||||
# Clear pip and npm caches
|
||||
pip cache purge || true
|
||||
sudo npm cache clean --force || true
|
||||
|
||||
- name: Check disk space after cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Install NeMo
|
||||
env:
|
||||
INSTALLER: ${{ matrix.installer }}
|
||||
run: |
|
||||
if [ "$INSTALLER" = "pip-install" ]; then
|
||||
pip install --no-cache-dir --upgrade pip
|
||||
pip install --no-cache-dir ".[all]"
|
||||
else
|
||||
export INSTALL_DIR=$(pwd)
|
||||
pip install --no-cache-dir ".[all]"
|
||||
fi
|
||||
|
||||
- name: Check disk space after installation
|
||||
run: df -h
|
||||
|
||||
- name: Run import checks
|
||||
run: |
|
||||
# Run import checks
|
||||
for collection in "asr" "tts" "lightning" "core"; do
|
||||
python tests/core_ptl/check_imports.py --domain "$collection"
|
||||
done
|
||||
|
||||
test-asr-installs-linux-arm:
|
||||
name: ubuntu-22.04-arm-py${{ matrix.python }}-asr
|
||||
runs-on: ubuntu-22.04-arm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python: ["3.10", "3.11", "3.12"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check disk space before cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Free up disk space
|
||||
run: |
|
||||
# Remove unnecessary packages and files on Ubuntu ARM
|
||||
sudo apt-get clean
|
||||
sudo rm -rf /usr/local/lib/android || true
|
||||
sudo rm -rf /opt/ghc || true
|
||||
sudo rm -rf /usr/local/.ghcup || true
|
||||
sudo rm -rf /usr/share/dotnet || true
|
||||
sudo rm -rf /opt/az || true
|
||||
# Clear pip and npm caches
|
||||
pip cache purge || true
|
||||
sudo npm cache clean --force || true
|
||||
|
||||
- name: Check disk space after cleanup
|
||||
run: df -h
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Install NeMo
|
||||
run: |
|
||||
pip install --no-cache-dir --upgrade pip
|
||||
pip install --no-cache-dir ".[asr]"
|
||||
|
||||
- name: Check disk space after installation
|
||||
run: df -h
|
||||
|
||||
- name: Run import checks
|
||||
run: |
|
||||
# Run import checks
|
||||
python tests/core_ptl/check_imports.py --domain asr
|
||||
@@ -0,0 +1,14 @@
|
||||
name: "Pull Request Labeler"
|
||||
on:
|
||||
- pull_request_target
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v4
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -0,0 +1,62 @@
|
||||
# Regularly updates the CI container
|
||||
name: Megatron Tag Bump Bot
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 0 * * *
|
||||
|
||||
jobs:
|
||||
get-release-branch-names:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
mcore: ${{ steps.get-branch.outputs.mcore_release_branch }}
|
||||
nemo: ${{ steps.get-branch.outputs.nemo_release_branch }}
|
||||
steps:
|
||||
- name: Get release branch names
|
||||
id: get-branch
|
||||
run: |
|
||||
latest_branch=$(git ls-remote --heads https://github.com/NVIDIA/Megatron-LM.git 'refs/heads/core_r*' |
|
||||
grep -o 'core_r[0-9]\+\.[0-9]\+\.[0-9]\+' |
|
||||
sort -V |
|
||||
tail -n1)
|
||||
echo "mcore_release_branch=$latest_branch" >> $GITHUB_OUTPUT
|
||||
|
||||
latest_branch=$(git ls-remote --heads https://github.com/NVIDIA/NeMo.git 'refs/heads/r*' |
|
||||
grep -o 'r[0-9]\+\.[0-9]\+\.[0-9]\+' |
|
||||
sort -V |
|
||||
tail -n1)
|
||||
echo "nemo_release_branch=$latest_branch" >> $GITHUB_OUTPUT
|
||||
|
||||
bump-tags:
|
||||
needs: [get-release-branch-names]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- nemo-target-branch: ${{ needs.get-release-branch-names.outputs.nemo }}
|
||||
mcore-target-branch: ${{ needs.get-release-branch-names.outputs.mcore }}
|
||||
- nemo-target-branch: main
|
||||
mcore-target-branch: main
|
||||
uses: ./.github/workflows/_bump_mcore_tag.yml
|
||||
with:
|
||||
nemo-target-branch: ${{ matrix.nemo-target-branch }}
|
||||
mcore-target-branch: ${{ matrix.mcore-target-branch }}
|
||||
secrets:
|
||||
PAT: ${{ secrets.PAT }}
|
||||
|
||||
notify:
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
needs: [bump-tags]
|
||||
steps:
|
||||
- name: Notify
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_WEBHOOK_ADMIN: <!subteam^${{ secrets.SLACK_WEBHOOK_ADMIN }}>
|
||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H 'Content-type: application/json' \
|
||||
--data "{\"text\":\":robot_joy: <https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}|Mcore-bump-bot workflow> failed. Please fix manually.\n\ncc ${SLACK_WEBHOOK_ADMIN}\"}" \
|
||||
$SLACK_WEBHOOK
|
||||
@@ -0,0 +1,54 @@
|
||||
name: ~shut down a single VM
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
vm:
|
||||
type: string
|
||||
description: Name of VM
|
||||
required: true
|
||||
n_gpus:
|
||||
type: string
|
||||
description: Number of GPUs this VM has
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
check-status-and-maybe-shutdown:
|
||||
environment: main
|
||||
runs-on: ${{ inputs.vm }}
|
||||
outputs:
|
||||
status: ${{ steps.status.outputs.main }}
|
||||
steps:
|
||||
- name: Check status
|
||||
id: status
|
||||
run: |
|
||||
docker run --rm --runtime=nvidia --gpus ${{ inputs.n_gpus }} ubuntu nvidia-smi
|
||||
|
||||
NUM_GPUS=$(nvidia-smi --query-gpu=name --format=csv,noheader | wc -l)
|
||||
|
||||
if [[ $NUM_GPUS -ne ${{ inputs.n_gpus }} ]]; then
|
||||
echo "Issues with GPU detected, will take this runner offline."
|
||||
echo "main=degraded" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "main=healthy" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Send Slack message & Disconnect runner from GitHub
|
||||
if: ${{ steps.status.outputs.main == 'degraded' || failure() }}
|
||||
run: |
|
||||
MESSAGE='{
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": ":alert: VM bot 🤖: Hey <!subteam^${{ secrets.SLACK_WEBHOOK_ADMIN }}>: VM `${{ inputs.vm }}` is having not the best day of their life, maybe bring them an apple or so."
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
curl -X POST -H "Content-type: application/json" --data "$MESSAGE" ${{ secrets.SLACK_WEBHOOK }}
|
||||
|
||||
cd /home/azureuser/actions-runner
|
||||
echo ${{ secrets.VM_KEY }} | sudo -S ./svc.sh stop
|
||||
@@ -0,0 +1,54 @@
|
||||
# Regularly updates the CI container
|
||||
name: Reboots VMs in a controlled way
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0/15 * * * *
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pre-flight:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'NVIDIA'
|
||||
outputs:
|
||||
list-of-vms: ${{ steps.main.outputs.main }}
|
||||
environment: main
|
||||
steps:
|
||||
- name: Get list of VMs
|
||||
id: main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
run: |
|
||||
RUNNERS=$(curl -L \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/NVIDIA/NeMo/actions/runners)
|
||||
|
||||
MATRIX=$(echo $RUNNERS \
|
||||
| jq -c '[
|
||||
.runners[]
|
||||
| select(.status == "online")
|
||||
| select(.name | contains("cpu") | not)
|
||||
| {
|
||||
"vm": .name,
|
||||
"n_gpus": [
|
||||
.labels[]
|
||||
| select(.name | endswith("gpu")) | .name
|
||||
][0][:1]
|
||||
}
|
||||
]
|
||||
'
|
||||
)
|
||||
echo main=$MATRIX | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
maintenance:
|
||||
needs: pre-flight
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJSON(needs.pre-flight.outputs.list-of-vms )}}
|
||||
uses: ./.github/workflows/monitor-single-vm.yml
|
||||
with:
|
||||
vm: ${{ matrix.vm }}
|
||||
n_gpus: ${{ matrix.n_gpus }}
|
||||
secrets: inherit # pragma: allowlist secret
|
||||
@@ -0,0 +1,123 @@
|
||||
# Copyright (c) 2026, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: Release docs
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry-run:
|
||||
description: Whether to run the workflow in dry-run mode
|
||||
required: true
|
||||
type: boolean
|
||||
default: true
|
||||
publish-as-latest:
|
||||
description: Publish as Latest stable version.
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
docs-version-override:
|
||||
description: Docs version if commit is not tagged
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
update-version-picker:
|
||||
description: Update version picker.
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
notify-emails:
|
||||
description: Email addresses to send the notification to. Format as "me@me.com,you@you.com".
|
||||
required: false
|
||||
type: string
|
||||
github-ref:
|
||||
description: Github ref to checkout
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
workflow_call:
|
||||
inputs:
|
||||
dry-run:
|
||||
description: Whether to run the workflow in dry-run mode
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
publish-as-latest:
|
||||
description: Publish as Latest stable version.
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
docs-version-override:
|
||||
description: Docs version if commit is not tagged
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
update-version-picker:
|
||||
description: Update version picker.
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
notify-emails:
|
||||
description: Email addresses to send the notification to. Format as "me@me.com,you@you.com".
|
||||
required: false
|
||||
type: string
|
||||
github-ref:
|
||||
description: Github ref to checkout
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
jobs:
|
||||
build-docs:
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_build_docs.yml@v0.83.0
|
||||
with:
|
||||
ref: ${{ inputs.github-ref }}
|
||||
docs-directory: docs/source
|
||||
sync-all: true
|
||||
no-extras: "--no-extra cu12 --no-extra compiled --no-extra compiled-a100"
|
||||
|
||||
publish-docs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-docs]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
repository: NVIDIA-NeMo/FW-CI-templates
|
||||
ref: v0.74.0
|
||||
path: FW-CI-templates
|
||||
|
||||
- uses: ./FW-CI-templates/.github/actions/publish-docs
|
||||
# This workflow runs either on main, or on a version tag. Any other git ref will lead
|
||||
# to an error.
|
||||
# If its on main, it will publish to "latest" directory in Akamai.
|
||||
# If its on a versioned tag, it will extract the version number from the tag (strip `v` prefix)
|
||||
# and publish to the versioned directory in Akamai.
|
||||
with:
|
||||
dry-run: ${{ inputs.dry-run }}
|
||||
artifacts-name: docs-html
|
||||
artifacts-path: _build/html
|
||||
emails-csv: ${{ inputs.notify-emails && format('{0},{1}', vars.docs_release_emails, inputs.notify-emails) || vars.docs_release_emails }}
|
||||
overwrite-latest-on-tag: ${{ inputs.publish-as-latest }}
|
||||
docs-version-override: ${{ inputs.docs-version-override }}
|
||||
update-version-picker: ${{ inputs.update-version-picker }}
|
||||
run-on-version-tag-only: ${{ github.ref_name != 'main' }}
|
||||
request-name: nemo-speech-publish-docs-${{ github.run_id }}
|
||||
aws-region: ${{ vars.DOCS_AWS_REGION }}
|
||||
aws-role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
akamai-host: ${{ secrets.AKAMAI_HOST }}
|
||||
akamai-client-token: ${{ secrets.AKAMAI_CLIENT_TOKEN }}
|
||||
akamai-client-secret: ${{ secrets.AKAMAI_CLIENT_SECRET }}
|
||||
akamai-access-token: ${{ secrets.AKAMAI_ACCESS_TOKEN }}
|
||||
s3-target-root: ${{ secrets.S3_BUCKET_NAME }}
|
||||
s3-target-path: nemo/speech
|
||||
@@ -0,0 +1,80 @@
|
||||
name: "Code freeze"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
type_of_release:
|
||||
type: choice
|
||||
description: Type of release
|
||||
options:
|
||||
- major
|
||||
- minor
|
||||
freeze-commit:
|
||||
type: string
|
||||
description: Commit SHA to use for cut-off
|
||||
required: false
|
||||
default: main
|
||||
mcore_version:
|
||||
description: "Version of MCore to use (must be a valid git ref)"
|
||||
required: true
|
||||
type: string
|
||||
dry-run:
|
||||
type: boolean
|
||||
description: Dry-run of code-freeze
|
||||
required: false
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
code-freeze:
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_code_freeze.yml@v0.86.0
|
||||
with:
|
||||
library-name: NeMo-Toolkit
|
||||
python-package: nemo
|
||||
release-type: ${{ inputs.type_of_release }}
|
||||
freeze-commit: ${{ inputs.freeze-commit }}
|
||||
dry-run: ${{ inputs.dry-run }}
|
||||
use-pat: true
|
||||
secrets:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_RELEASE_ENDPOINT }}
|
||||
SLACK_WEBHOOK_ADMIN: ${{ secrets.SLACK_WEBHOOK_ADMIN }}
|
||||
PAT: ${{ secrets.PAT }}
|
||||
|
||||
freeze-tags:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [code-freeze]
|
||||
environment: main
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
token: ${{ secrets.PAT }}
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
ref: ${{ inputs.dry-run == true && inputs.freeze-commit || needs.code-freeze.outputs.release-branch }}
|
||||
|
||||
- name: Pin branch name in Notebooks
|
||||
run: |
|
||||
cd ${{ github.run_id }}
|
||||
find tutorials -type f -name "*.ipynb" -exec sed -i "s/BRANCH = 'main'/BRANCH = '${{ needs.code-freeze.outputs.release-branch }}'/g" {} +
|
||||
|
||||
- name: Show status
|
||||
run: |
|
||||
cd ${{ github.run_id }}
|
||||
git status
|
||||
|
||||
- name: Create PR
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
id: create-pull-request
|
||||
if: ${{ inputs.dry-run != true }}
|
||||
with:
|
||||
path: ${{ github.run_id }}
|
||||
base: ${{ needs.code-freeze.outputs.release-branch }}
|
||||
branch: ci/freeze-tags-${{ needs.code-freeze.outputs.release-branch }}
|
||||
title: "Freeze tags in in `${{ needs.code-freeze.outputs.release-branch }}`"
|
||||
body: |
|
||||
🚀 PR to freeze tags in `${{ needs.code-freeze.outputs.release-branch }}`.
|
||||
|
||||
commit-message: "[🤠]: Howdy folks, let's release NeMo `${{ needs.code-freeze.outputs.release-branch }}` !"
|
||||
signoff: true
|
||||
assignees: okoenig
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (c) 2026, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Release Nightly Docs
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 10 * * *"
|
||||
|
||||
jobs:
|
||||
call-release-docs:
|
||||
uses: ./.github/workflows/release-docs.yml
|
||||
with:
|
||||
dry-run: false
|
||||
publish-as-latest: false
|
||||
docs-version-override: "nightly"
|
||||
update-version-picker: false
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,127 @@
|
||||
# Copyright (c) 2020-2026, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: "Build, validate, and release Neural Modules"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- "r**"
|
||||
- "pull-request/**"
|
||||
- "deploy-release/*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-ref:
|
||||
description: Ref (SHA or branch name) to release
|
||||
required: true
|
||||
type: string
|
||||
version-bump-branch:
|
||||
description: Branch for version bump
|
||||
required: true
|
||||
type: string
|
||||
dry-run:
|
||||
description: Compute the release but do not publish wheel, GH release, or docs.
|
||||
required: true
|
||||
default: true
|
||||
type: boolean
|
||||
create-gh-release:
|
||||
description: Create a GitHub release
|
||||
required: true
|
||||
default: true
|
||||
type: boolean
|
||||
generate-changelog:
|
||||
description: Generate changelog
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
publish-docs:
|
||||
description: Publish docs
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
gh-release-from-tag:
|
||||
description: Tag of previous release for changelog builder
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -x -e -u -o pipefail {0}
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: ${{ github.event_name == 'push' }}
|
||||
|
||||
jobs:
|
||||
pre-flight:
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_cicd_preflight.yml@v0.94.1
|
||||
|
||||
release:
|
||||
needs: [pre-flight]
|
||||
if: |
|
||||
!cancelled() && !failure()
|
||||
&& !(needs.pre-flight.outputs.docs_only == 'true'
|
||||
|| needs.pre-flight.outputs.is_deployment_workflow == 'true')
|
||||
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_release_library.yml@v1.4.3
|
||||
with:
|
||||
release-ref: ${{ inputs.release-ref || github.sha }}
|
||||
python-package: nemo
|
||||
python-version: "3.10"
|
||||
library-name: Neural Modules
|
||||
validate-only: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
dry-run: ${{ inputs.dry-run || false }}
|
||||
wheel-content-ignore: "W002,W004,W005,W009"
|
||||
skip-check-manifest: true
|
||||
version-bump-branch: ${{ inputs.version-bump-branch || github.ref_name }}
|
||||
create-gh-release: ${{ inputs.create-gh-release || true }}
|
||||
app-id: ${{ vars.BOT_ID }}
|
||||
gh-release-use-changelog-builder: ${{ inputs.generate-changelog || false }}
|
||||
publish-docs: ${{ inputs.publish-docs || true }}
|
||||
docs-target-path: nemo
|
||||
docs-directory: docs/source
|
||||
docs-requirements-file: requirements/requirements_docs.txt
|
||||
docs-sync-all: true
|
||||
docs-no-extras: "--no-extra cu12 --no-extra compiled --no-extra compiled-a100"
|
||||
publish-as-latest: true
|
||||
run-on-version-tag-only: ${{ github.ref_name != 'main' }}
|
||||
gh-release-from-tag: ${{ inputs.gh-release-from-tag || '' }}
|
||||
restrict-to-admins: true
|
||||
secrets: inherit # pragma: allowlist secret
|
||||
|
||||
release-summary:
|
||||
needs: [pre-flight, release]
|
||||
if: ${{ !cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Result
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
FAILED_JOBS=$(gh run view $GITHUB_RUN_ID --repo ${{ github.repository }} --json jobs --jq '[.jobs[] | select(.conclusion == "failure" or .conclusion == "timed_out" or .conclusion == "action_required")] | length')
|
||||
|
||||
if [ "${FAILED_JOBS:-0}" -eq 0 ]; then
|
||||
echo "✅ All previous jobs completed successfully"
|
||||
exit 0
|
||||
else
|
||||
echo "❌ Found $FAILED_JOBS failed job(s)"
|
||||
gh run view $GITHUB_RUN_ID --repo ${{ github.repository }} --json jobs --jq '.jobs[] | select(.conclusion == "failure" or .conclusion == "timed_out" or .conclusion == "action_required") | .name'
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright (c) 2020-2021, NVIDIA CORPORATION.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: Secrets detector
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.NEMO_REFORMAT_TOKEN }}
|
||||
|
||||
- name: Install secrets detector
|
||||
run: pip install detect-secrets
|
||||
|
||||
- name: Run on change-set
|
||||
run: |
|
||||
git diff --name-only --diff-filter=d --merge-base origin/main -z | xargs -0 detect-secrets-hook --disable-plugin HexHighEntropyString --baseline .secrets.baseline
|
||||
|
||||
- uses: EndBug/add-and-commit@v9
|
||||
# Commit changes. Nothing is committed if no changes.
|
||||
if: always()
|
||||
with:
|
||||
message: Update baseline
|
||||
commit: --signoff
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
# log and data files
|
||||
*.model
|
||||
*.pkl
|
||||
#*.ipynb
|
||||
output
|
||||
output_2048
|
||||
result
|
||||
*.pt
|
||||
tests/data/asr
|
||||
.DS_Store
|
||||
bert.pt.json
|
||||
work
|
||||
runs
|
||||
fastspeech_output
|
||||
.hydra
|
||||
.bash_history.local
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
**.pyc
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.idea
|
||||
.Python
|
||||
wandb
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
#parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/build
|
||||
docs/source/_build
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# Override Jupyter in Github Language states for more accurate estimate of repo code.
|
||||
# Reference: https://github.com/github/linguist/blob/master/docs/overrides.md#generated-code
|
||||
*.ipynb linguist-generated
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don’t work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# VSCode project settins
|
||||
.vscode/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
/docs/html
|
||||
/docs/docs_zh/zh
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# Emacs backup files
|
||||
*~
|
||||
|
||||
cifar-10-batches-py
|
||||
*.tar.gz
|
||||
|
||||
# Test data.
|
||||
tests/.data
|
||||
tests/data
|
||||
|
||||
# outputs folder
|
||||
examples/*/outputs
|
||||
examples/*/NeMo_experiments
|
||||
examples/*/nemo_experiments
|
||||
examples/*/.hydra
|
||||
examples/*/wandb
|
||||
examples/*/data
|
||||
wandb
|
||||
dump.py
|
||||
|
||||
docs/sources/source/test_build/
|
||||
|
||||
# Checkpoints, config files and temporary files created in tutorials.
|
||||
examples/neural_graphs/*.chkpt
|
||||
examples/neural_graphs/*.yml
|
||||
|
||||
.hydra/
|
||||
nemo_experiments/
|
||||
|
||||
slurm*.out
|
||||
|
||||
# Onelogger
|
||||
onelogger.*
|
||||
|
||||
# voice agent
|
||||
node_modules/
|
||||
.vite/
|
||||
bot_server.*
|
||||
audio_logs/
|
||||
eval_results/
|
||||
|
||||
# agents
|
||||
.claude/settings.local.json
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
default_language_version:
|
||||
python: python3
|
||||
|
||||
ci:
|
||||
autofix_prs: true
|
||||
autoupdate_commit_msg: '[pre-commit.ci] pre-commit suggestions'
|
||||
autoupdate_schedule: quarterly
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
- id: check-added-large-files
|
||||
args: ['--maxkb=1000']
|
||||
- id: requirements-txt-fixer
|
||||
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
name: Format imports
|
||||
exclude: docs/
|
||||
|
||||
# Using this mirror lets us use mypyc-compiled black, which is about 2x faster
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 24.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
@@ -0,0 +1,5 @@
|
||||
[MAIN]
|
||||
ignore-paths=tests
|
||||
max-line-length=119
|
||||
ignore-patterns=.*\.ipynb$
|
||||
recursive=y
|
||||
@@ -0,0 +1,12 @@
|
||||
[MAIN]
|
||||
ignore-paths=tests
|
||||
max-line-length=119
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=all
|
||||
|
||||
enable=C0115,C0116,W0611,C0301
|
||||
# C0115: missing-class-docstring
|
||||
# C0116: missing-function-docstring
|
||||
# W0611: unused-import
|
||||
# C0301: line-too-long
|
||||
@@ -0,0 +1,9 @@
|
||||
[MAIN]
|
||||
ignore-paths=tests
|
||||
max-line-length=119
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=all
|
||||
|
||||
enable=W0611
|
||||
# W0611: unused-import
|
||||
@@ -0,0 +1,35 @@
|
||||
# =============================================================================
|
||||
# Copyright (c) 2020 NVIDIA. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# =============================================================================
|
||||
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required field.
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.10"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx.
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# Set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: requirements/requirements_docs.txt
|
||||
+2082
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
cff-version: 1.2.0
|
||||
message: "If you use this software, please cite it as below."
|
||||
title: "NeMo: a toolkit for Conversational AI and Large Language Models"
|
||||
url: https://nvidia.github.io/NeMo/
|
||||
repository-code: https://github.com/NVIDIA/NeMo
|
||||
authors:
|
||||
- family-names: Harper
|
||||
given-names: Eric
|
||||
- family-names: Majumdar
|
||||
given-names: Somshubra
|
||||
- family-names: Kuchaiev
|
||||
given-names: Oleksii
|
||||
- family-names: Jason
|
||||
given-names: Li
|
||||
- family-names: Zhang
|
||||
given-names: Yang
|
||||
- family-names: Bakhturina
|
||||
given-names: Evelina
|
||||
- family-names: Noroozi
|
||||
given-names: Vahid
|
||||
- family-names: Subramanian
|
||||
given-names: Sandeep
|
||||
- family-names: Nithin
|
||||
given-names: Koluguri
|
||||
- family-names: Jocelyn
|
||||
given-names: Huang
|
||||
- family-names: Jia
|
||||
given-names: Fei
|
||||
- family-names: Balam
|
||||
given-names: Jagadeesh
|
||||
- family-names: Yang
|
||||
given-names: Xuesong
|
||||
- family-names: Livne
|
||||
given-names: Micha
|
||||
- family-names: Dong
|
||||
given-names: Yi
|
||||
- family-names: Naren
|
||||
given-names: Sean
|
||||
- family-names: Ginsburg
|
||||
given-names: Boris
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# CLAUDE.md / AGENTS.md
|
||||
|
||||
This file provides guidance when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
NeMo Speech — toolkit for training/deploying speech models (ASR, TTS, Speech LLM). Active collections: `asr`, `tts`, `audio`, `speechlm2`, `common`. No Megatron / Megatron Core / Transformer Engine — parallelism is PyTorch-native (DDP, FSDP2, TP/SP via DTensor).
|
||||
|
||||
## Build & Install
|
||||
|
||||
See the canonical installation guide — [`docs/source/starthere/install.rst`](docs/source/starthere/install.rst) (published at https://docs.nvidia.com/nemo/speech/nightly/) — for the uv, pip (bring-your-own Python/PyTorch/CUDA), Docker, and optional `compiled` (SpeechLM2/Automodel) install paths.
|
||||
|
||||
Dev quickstart: `uv sync --extra all --extra cu13` (Python 3.12+, PyTorch 2.7+; `test`/`docs` are `--group`s, not extras).
|
||||
|
||||
## Code Style
|
||||
|
||||
- **Line length: 119** (not default 88) — consistent across black, isort, flake8
|
||||
- Black with `skip_string_normalization = true`
|
||||
- isort with `profile = black`
|
||||
- Check: `isort --check <path> && black --check <path>` or `isort --check . && black --check .`
|
||||
- Fix: `isort <path> && black <path>` or `isort . && black .`
|
||||
- Jupyter Notebooks are excluded from automatic black reformatting (see `extend-exclude`), but can be still reformatted when passed directly. Do not reformat notebooks outside your changes.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
pytest tests/collections/asr -m "not pleasefixme" -v # ASR tests, skip broken
|
||||
pytest tests/collections/tts -m unit -v # TTS unit tests
|
||||
pytest -k "test_name" tests/ # Single test by name
|
||||
```
|
||||
|
||||
Markers: `unit`, `integration`, `system`, `pleasefixme` (broken — skip), `skipduringci`.
|
||||
|
||||
## CI & PRs
|
||||
|
||||
- NVIDIA developers: feature branches off `main`; community: fork-based workflow
|
||||
- CI triggered by adding **"Run CICD"** label to the PR
|
||||
- E2E nightly tests: only when really needed. Add both **"Run e2e nightly"** and **"Run CICD"** labels
|
||||
- `skip-linting` / `skip-docs` labels bypass those checks
|
||||
- Formatting CI auto-commits black/isort fixes back to the PR branch
|
||||
- CI: GitHub Actions in `.github/workflows/`
|
||||
|
||||
## Documentation
|
||||
|
||||
Sphinx-based docs live in `docs/source/`. Build with:
|
||||
|
||||
```bash
|
||||
uv sync --locked --group docs # one-time setup (matches CI)
|
||||
uv run make -C docs clean html # full rebuild
|
||||
uv run make -C docs html # incremental rebuild
|
||||
```
|
||||
|
||||
Output goes to `docs/build/html/`. Open `docs/build/html/index.html` to preview locally.
|
||||
|
||||
Other useful targets: `make -C docs linkcheck` (verify external links), `make -C docs doctest` (run embedded doctests).
|
||||
|
||||
## Training & Inference
|
||||
|
||||
Entry-point scripts live under `examples/<collection>/`.
|
||||
|
||||
All scripts follow the same Hydra pattern — a `@hydra_runner` decorator points to a YAML config in a nearby `conf/` directory:
|
||||
|
||||
```python
|
||||
@hydra_runner(config_path="conf", config_name="fast-conformer_transducer_bpe")
|
||||
def main(cfg):
|
||||
trainer = pl.Trainer(**resolve_trainer_cfg(cfg.trainer))
|
||||
exp_manager(trainer, cfg.get("exp_manager", None))
|
||||
model = EncDecRNNTBPEModel(cfg=cfg.model, trainer=trainer)
|
||||
trainer.fit(model)
|
||||
```
|
||||
|
||||
Override any config value from the CLI with Hydra syntax: `python script.py model.optim.lr=1e-4 trainer.max_epochs=50`. Browse configs with `ls examples/<collection>/conf/` to see which models and variants are supported.
|
||||
|
||||
## Handy Scripts
|
||||
|
||||
Utility scripts live under `scripts/`. Key subdirectories: `speech_recognition/`, `speechlm2/`, `speaker_tasks/`, `tokenizers/`, `dataset_processing/`, `asr_language_modeling/`. Browse with `ls scripts/`.
|
||||
|
||||
Four frequently used data/training helpers:
|
||||
|
||||
- **`scripts/speech_recognition/estimate_duration_bins.py`** — estimate Lhotse dynamic-bucketing duration bins from a manifest or YAML input config. Usage: `python scripts/speech_recognition/estimate_duration_bins.py <input> -b 30 -n 100000`
|
||||
- **`scripts/speech_recognition/oomptimizer.py`** — find the largest batch size per bucket that fits in GPU memory. Usage: `python scripts/speech_recognition/oomptimizer.py --pretrained-name nvidia/canary-1b` or point to a config with `--config-path`.
|
||||
- **`scripts/speech_recognition/estimate_data_weights.py`** — compute per-dataset sampling weights from YAML input configs, with optional temperature re-weighting. Usage: `python scripts/speech_recognition/estimate_data_weights.py input.yaml output.yaml -t 0.5`
|
||||
- **`scripts/speech_recognition/convert_to_tarred_audio_dataset.py`** — shard audio+manifest into tar files. Usage: `python scripts/speech_recognition/convert_to_tarred_audio_dataset.py --manifest_path=m.json --target_dir=./tar --num_shards=512 --max_duration=60.0`
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Hydra + OmegaConf** for all config management (YAML configs)
|
||||
- **PyTorch Lightning** for training orchestration
|
||||
- **Lhotse** (>=1.32.2) for audio data loading
|
||||
- Collections are semi-isolated domains sharing `nemo.core` and `nemo.collections.common`
|
||||
|
||||
## Subdirectory Instructions
|
||||
|
||||
Module-specific instructions can be added as `CLAUDE.md` or `AGENTS.md` files in subdirectories.
|
||||
|
||||
## Issue Reproduction
|
||||
|
||||
When fixing a bug, always:
|
||||
1. First reproduce the issue with a minimal test case
|
||||
2. Add the reproduction as a unit test
|
||||
3. Then fix the issue
|
||||
4. Verify the test passes
|
||||
|
||||
## Forbidden Operations
|
||||
|
||||
- Never push directly to `main`
|
||||
- Never modify `.github/workflows/` without explicit instruction
|
||||
- Never delete test files without explicit instruction
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
# Contributions are welcome!
|
||||
|
||||
We do all of NeMo's development in the open. Contributions from NeMo community are welcome.
|
||||
|
||||
|
||||
# Pull Requests (PR) Guidelines
|
||||
|
||||
**Send your PRs to the `main` branch**
|
||||
|
||||
1) Make sure your PR does one thing. Have a clear answer to "What does this PR do?".
|
||||
2) Read General Principles and style guide below
|
||||
3) Make sure you sign your commits. E.g. use ``git commit -s`` when before your commit
|
||||
4) Make sure relevant unittests finish successfully before sending PR
|
||||
5) Send your PR and request a review
|
||||
|
||||
## Unit tests
|
||||
Quick tests (locally, while developing)
|
||||
```
|
||||
pytest
|
||||
# If you don't have NVIDIA GPU do:
|
||||
# pytest -m "not pleasefixme" --cpu path/to/relevant_tests
|
||||
```
|
||||
Full tests, including pre-trained model downloads
|
||||
```
|
||||
pytest -m "not pleasefixme" --with_downloads path/to/relevant_tests
|
||||
```
|
||||
|
||||
Replace `path/to/relevant_tests` with the test directory to run such as `tests/collections/asr`. Check the test scripts in `tests/functional_tests`
|
||||
that begin with `L0_Unit_Tests_` for the specific test configuration used by different parts of the unit test suite. Different suites may expect
|
||||
different environment variables to be set.
|
||||
|
||||
## Running the Github CI
|
||||
|
||||
CI is powered by [copy-pr-bot](https://github.com/apps/copy-pr-bot), which mirrors each PR to a `pull-request/<number>` branch and triggers the workflow on push.
|
||||
|
||||
**CI runs automatically** when all of the following are true:
|
||||
- Every commit in the PR is [GPG-signed](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)
|
||||
- Every committer is a member of the NVIDIA-NeMo GitHub org (or listed as an `additional_trustee`)
|
||||
- The PR has no more than 249 commits
|
||||
|
||||
If any of those conditions are not met, copy-pr-bot posts a comment and skips branch creation — CI will not run. A maintainer (anyone with write access or greater) can trigger it manually by commenting:
|
||||
```
|
||||
/ok to test <sha>
|
||||
```
|
||||
where `<sha>` is the full or abbreviated SHA of the PR's HEAD commit. The bot also accepts `/okay to test` and `/ok-to-test`.
|
||||
|
||||
To re-run CI after a new push, the bot will sync automatically if the PR is still trusted. Otherwise comment `/ok to test <sha>` again with the new HEAD SHA.
|
||||
|
||||
The CI test suites are selectively ran based on the files that are changed. In some cases, no tests may be ran such as when docs are updated.
|
||||
|
||||
Lint checks using flake8 and pylint are ran on the code based on the files that were changed. Please resolve any lint errors. It is possible but discouraged
|
||||
to ignore the lint errors by adding the "skip-linting" label to the PR.
|
||||
|
||||
To run the nightly e2e test suite on a PR, add the "Run e2e nightly" label. Labels are read once by the `pre-flight` job at the start of each run, so the label must be present before CI starts. If CI is already running when you add the label, cancel the active workflow run and re-trigger by pushing a new commit.
|
||||
|
||||
## Whom should you ask for review:
|
||||
Please tag @nithinraok for NeMo core and ASR related PRs and @blisc for TTS related PRs.
|
||||
|
||||
Note that some people may self-assign to review your PR - in which case, please wait for them to add a review.
|
||||
|
||||
Your pull requests must pass all checks and peer-review before they can be merged.
|
||||
|
||||
# General principles
|
||||
1. **User-oriented**: make it easy for end users, even at the cost of writing more code in the background
|
||||
1. **Robust**: make it hard for users to make mistakes.
|
||||
1. **Well-tested**: please add simple, fast unittests. Consider adding CI tests for end-to-end functionality.
|
||||
1. **Reusable**: for every piece of code, think about how it can be reused in the future and make it easy to be reused.
|
||||
1. **Readable**: code should be easier to read.
|
||||
1. **Legal**: if you copy even one line of code from the Internet, make sure that the code allows the license that NeMo supports. Give credit and link back to the code.
|
||||
1. **Sensible**: code should make sense. If you think a piece of code might be confusing, write comments.
|
||||
|
||||
## Class naming conventions
|
||||
* No “I”, “Interface”, “NM” nor “NeMo” pre/postfixes anywhere
|
||||
* Core interfaces have simple names: Typing, Cloud, Serialization, FileIO*
|
||||
* Core classes have the simplest names ever: NeuralModule, Model, Graph, Dataset, Loss, Module*
|
||||
* Abstract classes in the Model hierarchy have Model postfix
|
||||
* A config class for MyModel should be called MyModelConfig
|
||||
* Leaf Neural Module classes have simple names without any postfixes (e.g. AudioPreprocess)
|
||||
* Leaf Datasets have Dataset postfix (e.g. AudioToSpeechLabelDataset)
|
||||
* Leaf Losses have Loss postfix (e.g. CTCLoss)
|
||||
* Leaf Models do not have any postfix, just name (e.g. QuartzNet)
|
||||
|
||||
## Python style
|
||||
We use ``black`` as our style guide. To check whether your code will pass style check (from the NeMo's repo folder) run:
|
||||
``python setup.py style --scope path/to/changed/files`` and if it does not pass run ``python setup.py style --scope path/to/changed/files --fix``.
|
||||
|
||||
1. Include docstrings for every class and method exposed to the user.
|
||||
1. Use Python 3 type hints for every class and method exposed to the user.
|
||||
1. Avoid wild import: ``from X import *`` unless in ``X.py``, ``__all__`` is defined.
|
||||
1. Minimize the use of ``**kwargs``.
|
||||
1. ``RaiseError`` is preferred to ``assert``. Write: ```if X: raise Error``` instead of ```assert X```.
|
||||
1. Classes are preferred to standalone methods.
|
||||
1. Methods should be atomic. A method shouldn't be longer than 75 lines, e.g. can be fit into the computer screen without scrolling.
|
||||
1. If a method has arguments that don't fit into one line, each argument should be in its own line for readability.
|
||||
1. Add ``__init__.py`` for every folder.
|
||||
1. F-strings are prefered to formatted strings.
|
||||
1. Loggers are preferred to print. In NeMo, you can use logger from ``from nemo.utils import logging``
|
||||
1. Private functions (functions start with ``_``) shouldn't be called outside its host file.
|
||||
1. If a comment lasts multiple lines, use ``'''`` instead of ``#``.
|
||||
|
||||
# Collections
|
||||
Collection is a logical grouping of related Neural Modules. It is a grouping of modules that share a domain area or semantics.
|
||||
When contributing module to a collection, please make sure it belongs to that category.
|
||||
If you would like to start a new one and contribute back to the platform, you are very welcome to do so.
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1 @@
|
||||
include requirements/*
|
||||
@@ -0,0 +1,128 @@
|
||||
[](http://www.repostatus.org/#active)
|
||||
[](https://docs.nvidia.com/nemo/speech/nightly/)
|
||||
[](https://github.com/nvidia/nemo/actions/workflows/codeql.yml)
|
||||
[](https://github.com/NVIDIA/NeMo/blob/master/LICENSE)
|
||||
[](https://badge.fury.io/py/nemo-toolkit)
|
||||
[](https://badge.fury.io/py/nemo-toolkit)
|
||||
[](https://pepy.tech/project/nemo-toolkit)
|
||||
[](https://github.com/psf/black)
|
||||
|
||||
# **NVIDIA NeMo Speech**
|
||||
Checkout our [HuggingFace🤗 collection](https://huggingface.co/collections/nvidia/nemotron-speech) for the latest open
|
||||
weight checkpoints and demos!
|
||||
|
||||
## Updates
|
||||
|
||||
> The first release of NeMo Speech after NeMo repository split is scheduled for June 2026, as the repo undergoes transformation.
|
||||
> For the latest stable released version, please use [the 26.02 NGC container](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/nemo?version=26.02).
|
||||
|
||||
- 2026-06: [Nemotron-3.5-ASR-Streaming-0.6B](https://huggingface.co/nvidia/nemotron-3.5-asr-streaming-0.6b) has been released with 40 languages supported, controllable latency 80ms-1s, and 240-2400 1xH100 concurrent streams. Built on cache-aware Fastconformer architecture.
|
||||
- 2026-04: [Parakeet-unified-en-0.6b](https://huggingface.co/nvidia/parakeet-unified-en-0.6b) has been released with high-quality offline and streaming (with a minimum latency of 160ms) inference in one model for English language with punctuation and capitalization support.
|
||||
- 2026-03: [Nemotron 3 VoiceChat](https://build.nvidia.com/nvidia/nemotron-voicechat/modelcard) is now released in Early Access. Built on the Nemotron Nano v2 LLM backbone with Nemotron speech and TTS decoder, VoiceChat delivers full-duplex, natural, interruptible conversations with low latency. Try out [the demo](https://build.nvidia.com/nvidia/nemotron-voicechat) and apply for [early access](https://developer.nvidia.com/nemotron-voicechat-early-access).
|
||||
- 2026-03: [Nemotron-Speech-Streaming v2603](https://huggingface.co/nvidia/nemotron-speech-streaming-en-0.6b) has been
|
||||
updated. It has been trained on a larger and more diverse corpus, resulting in lower WER across all latency modes.
|
||||
Try out [the demo](https://huggingface.co/spaces/nvidia/nemotron-speech-streaming-en-0.6b) and check out
|
||||
[the NIM](https://build.nvidia.com/nvidia/nemotron-asr-streaming).
|
||||
- 2026-03: [MagpieTTS v2602](https://huggingface.co/nvidia/magpie_tts_multilingual_357m) has been released with support
|
||||
for 9 languages(En, Es, De, Fr, Vi, It, Zh, Hi, Ja). Try out
|
||||
[the demo](https://huggingface.co/nvidia/magpie_tts_multilingual_357m) and check out
|
||||
[the NIM](https://build.nvidia.com/nvidia/magpie-tts-multilingual).
|
||||
- 2026-01: Nemotron-Speech-Streaming was released: One checkpoint that enables users to pick their optimal point
|
||||
on the latency-accuracy Pareto curve!
|
||||
- 2026-01: MagpieTTS was released.
|
||||
- 2026: This repo has pivoted to focus on audio, speech, and multimodal LLM. For the last NeMo release with support for more
|
||||
modalities, see [v2.7.0](https://github.com/NVIDIA-NeMo/NeMo/releases/tag/v2.7.0)
|
||||
- 2025-08: [Parakeet V3](https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3) and
|
||||
[Canary V2](https://huggingface.co/nvidia/canary-1b-v2) have been released with speech recognition and translation
|
||||
support for 25 European languages.
|
||||
- 2025-06: [Canary-Qwen-2.5B](https://huggingface.co/nvidia/canary-qwen-2.5b) has been released with record-setting
|
||||
5.63% WER on English Open ASR Leaderboard.
|
||||
|
||||
## Introduction
|
||||
|
||||
NVIDIA NeMo Speech is built for researchers and PyTorch developers working on Speech models including Automatic Speech
|
||||
Recognition (ASR), Text to Speech (TTS), and Speech LLMs. It is designed to help you efficiently create, customize, and
|
||||
deploy new AI models by leveraging existing code and pre-trained model checkpoints.
|
||||
|
||||
For technical documentation, please see the
|
||||
[NeMo Framework User Guide](https://docs.nvidia.com/nemo/speech/nightly/).
|
||||
|
||||
## Requirements
|
||||
|
||||
NeMo Speech works with the **Python, PyTorch, and CUDA versions of your choosing**:
|
||||
|
||||
- Python 3.12 or above
|
||||
- PyTorch 2.7 or above (CPU, CUDA, etc. — your choice)
|
||||
- NVIDIA GPU + CUDA (required for training; recommended for inference)
|
||||
|
||||
If you already have a Python/PyTorch/CUDA stack that satisfies those minimums, NeMo Speech installs on top of it **without replacing it**, so your existing PyTorch build is kept (see the install options below). The versions pinned in `uv.lock` and shipped in the official container — Python 3.13, PyTorch 2.12, CUDA 12.6/13.2 — are simply the combination we actively test and support. They make setup turnkey and reproducible, but they are **not** a hard requirement.
|
||||
|
||||
As of [Pytorch 2.6](https://docs.pytorch.org/docs/stable/notes/serialization.html#torch-load-with-weights-only-true),
|
||||
`torch.load` defaults to using `weights_only=True`. Some model checkpoints may require using `weights_only=False`.
|
||||
In this case, you can set the env var `TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD=1` before running code that uses `torch.load`.
|
||||
However, this should only be done with trusted files. Loading files from untrusted sources with more than weights only
|
||||
can have the risk of arbitrary code execution.
|
||||
|
||||
## Developer Documentation
|
||||
|
||||
| Version | Status | Description |
|
||||
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Latest | [](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/main/) | [Documentation of the latest (i.e. main) branch.](https://docs.nvidia.com/nemo/speech/nightly/) |
|
||||
| Stable | [](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/) | Documentation of the stable (i.e. most recent release) - To be added |
|
||||
|
||||
## Install NeMo Speech
|
||||
|
||||
The recommended way to install NeMo Speech is from source with [uv](https://docs.astral.sh/uv/), which reproduces our actively-tested stack from the committed `uv.lock`. If you need different Python/PyTorch/CUDA versions, NeMo also installs over your existing environment via pip — see the [pip fallback](#from-pypi-with-pip-fallback--bring-your-own-versions) below.
|
||||
|
||||
### From source with uv (recommended)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/NVIDIA-NeMo/NeMo.git
|
||||
cd NeMo
|
||||
uv sync --extra all --extra cu13 # CUDA 13.x (recommended) — use --extra cu12 for CUDA 12.x
|
||||
```
|
||||
|
||||
This installs our supported stack (Python 3.13, PyTorch 2.12, CUDA 13.2) into `.venv/` with NeMo editable. Add `--group test` for the test suite or `--group docs` to build the docs; run tools via `uv run <cmd>` or activate with `source .venv/bin/activate`. On Linux, `cu12` and `cu13` are mutually exclusive — pass exactly one (`cu13` is the default). For the **exact** container baseline, add `--locked --python 3.13` (the path the Dockerfile and CI use).
|
||||
|
||||
> **SpeechLM2 / Automodel:** the Automodel backend runs **without** any compiled dependencies. It can *optionally* benefit from dedicated accelerated backends (Transformer Engine, FlashAttention, Mamba, grouped-GEMM/MoE, DeepEP) for better performance — these source-built kernels come from the `compiled` (Hopper/Blackwell) or `compiled-a100` (A100) extras, built by `docker/Dockerfile` (`GPU_TARGET=h100plus` / `a100`). See the [installation guide](https://docs.nvidia.com/nemo/speech/nightly/) for the full list and build details.
|
||||
|
||||
### Docker (turnkey, our supported stack)
|
||||
|
||||
> **NGC container:** _Coming soon — the pull command for the prebuilt NeMo Speech container image will be published here._
|
||||
|
||||
To build the container from source (CUDA 13 / H100+ by default):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/NVIDIA-NeMo/NeMo.git
|
||||
cd NeMo
|
||||
docker buildx build -f docker/Dockerfile -t nemo-speech . # CUDA 13 / H100+ (default)
|
||||
docker run --rm -it --gpus all -v "$PWD:/workspace" nemo-speech bash
|
||||
```
|
||||
|
||||
For A100, set `GPU_TARGET=a100` — A100 works with **both CUDA 12 and CUDA 13** (CUDA 13, the default base image, is recommended; the CUDA 12 base is a convenience). See the header of [`docker/Dockerfile`](docker/Dockerfile) for all build arguments (`BASE_IMAGE`, `GPU_TARGET`).
|
||||
|
||||
### From PyPI with pip (fallback — bring your own versions)
|
||||
|
||||
Prefer your own Python/PyTorch/CUDA? Install your PyTorch first (any version ≥ 2.7 for your CPU/CUDA/etc. target — see the [PyTorch install matrix](https://pytorch.org/get-started/locally/)), then add NeMo and it **keeps your build**. `uv pip` (uv's fast, pip-compatible installer) works like `pip`:
|
||||
|
||||
```bash
|
||||
uv pip install 'nemo-toolkit[asr,tts]' # or plain: pip install 'nemo-toolkit[asr,tts]'
|
||||
```
|
||||
|
||||
> ⚠️ Do **not** use `uv sync --locked` for a bring-your-own stack — it applies `uv.lock` and replaces your Python/PyTorch/CUDA with the supported baseline. Use `uv pip`/`pip` here; reserve `uv sync --locked` for reproducing our stack.
|
||||
|
||||
To instead pull *our* pinned PyTorch build, add the CUDA extra and the matching wheel index (pip/uv pip do not read uv's project index config, so `--extra-index-url` is required):
|
||||
|
||||
```bash
|
||||
pip install 'nemo-toolkit[asr,tts,cu13]' --extra-index-url https://download.pytorch.org/whl/cu132 # CUDA 13.x
|
||||
pip install 'nemo-toolkit[asr,tts,cu12]' --extra-index-url https://download.pytorch.org/whl/cu126 # CUDA 12.x
|
||||
```
|
||||
|
||||
## Contribute to NeMo
|
||||
|
||||
We welcome community contributions! Please refer to
|
||||
[CONTRIBUTING.md](https://github.com/NVIDIA-NeMo/NeMo/blob/main/CONTRIBUTING.md) for the process.
|
||||
|
||||
## Licenses
|
||||
|
||||
NeMo is licensed under the [Apache License 2.0](https://github.com/NVIDIA/NeMo?tab=Apache-2.0-1-ov-file).
|
||||
@@ -0,0 +1,7 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`NVIDIA-NeMo/Speech`
|
||||
- 原始仓库:https://github.com/NVIDIA-NeMo/Speech
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
## Security
|
||||
|
||||
NVIDIA is dedicated to the security and trust of our software products and services, including all source code repositories managed through our organization.
|
||||
|
||||
If you need to report a security issue, please use the appropriate contact points outlined below. **Please do not report security vulnerabilities through GitHub.** If a potential security issue is inadvertently reported via a public issue or pull request, NVIDIA maintainers may limit public discussion and redirect the reporter to the appropriate private disclosure channels.
|
||||
|
||||
## Reporting Potential Security Vulnerability in an NVIDIA Product
|
||||
|
||||
To report a potential security vulnerability in any NVIDIA product:
|
||||
|
||||
- Web: [Security Vulnerability Submission Form](https://www.nvidia.com/object/submit-security-vulnerability.html)
|
||||
- E-Mail: psirt@nvidia.com
|
||||
- We encourage you to use the following PGP key for secure email communication: [NVIDIA public PGP Key for communication](https://www.nvidia.com/en-us/security/pgp-key)
|
||||
- Please include the following information:
|
||||
- Product/Driver name and version/branch that contains the vulnerability
|
||||
- Type of vulnerability (code execution, denial of service, buffer overflow, etc.)
|
||||
- Instructions to reproduce the vulnerability
|
||||
- Proof-of-concept or exploit code
|
||||
- Potential impact of the vulnerability, including how an attacker could exploit the vulnerability
|
||||
|
||||
While NVIDIA currently does not have a bug bounty program, we do offer acknowledgement when an externally reported security issue is addressed under our coordinated vulnerability disclosure policy. Please visit our [Product Security Incident Response Team (PSIRT)](https://www.nvidia.com/en-us/security/psirt-policies/) policies page for more information.
|
||||
|
||||
## NVIDIA Product Security
|
||||
|
||||
For all security-related concerns, please visit NVIDIA's Product Security portal at https://www.nvidia.com/en-us/security
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
comment: false
|
||||
coverage:
|
||||
status:
|
||||
project: false
|
||||
patch:
|
||||
default:
|
||||
target: 80%
|
||||
threshold: 0%
|
||||
base: auto
|
||||
if_ci_failed: error
|
||||
paths:
|
||||
- "!nemo/collections/asr"
|
||||
- "!nemo/collections/audio"
|
||||
- "!nemo/collections/speechlm"
|
||||
- "!nemo/collections/tts"
|
||||
- "!nemo/core"
|
||||
- "!nemo/collections/common"
|
||||
- "!nemo/collections/multimodal_autoregressive"
|
||||
- "!nemo/collections/diffusion"
|
||||
- "!nemo/collections/nlp"
|
||||
|
||||
fixes:
|
||||
- "/workspace/::"
|
||||
@@ -0,0 +1,294 @@
|
||||
# syntax=docker/dockerfile:1-labs
|
||||
|
||||
# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This CI Dockerfile supports CUDA 13 and CUDA 12 from a single BASE_IMAGE
|
||||
# build arg. The default is the recommended CUDA 13 image:
|
||||
# nvcr.io/nvidia/cuda-dl-base:26.04-cuda13.2-devel-ubuntu24.04
|
||||
# The current recommended CUDA 12 image is:
|
||||
# nvcr.io/nvidia/cuda-dl-base:25.06-cuda12.9-devel-ubuntu24.04
|
||||
#
|
||||
# The build derives CUDA_FLAVOR internally from BASE_IMAGE by matching
|
||||
# "cuda13" or "cuda12" in the image tag. That flavor selects the matching uv
|
||||
# extra (cu13 or cu12) and CUDA Python package include path. If BASE_IMAGE does
|
||||
# not contain either token, the build fails early.
|
||||
#
|
||||
# Example CUDA 13 H100+ build:
|
||||
# docker buildx build -f docker/Dockerfile \
|
||||
# --build-arg GPU_TARGET=h100plus .
|
||||
# Example CUDA 12 A100 build:
|
||||
# docker buildx build -f docker/Dockerfile \
|
||||
# --build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.06-cuda12.9-devel-ubuntu24.04 \
|
||||
# --build-arg GPU_TARGET=a100 .
|
||||
#
|
||||
# GPU_TARGET controls compiled Automodel dependency tuning. "h100plus" builds
|
||||
# for SM90/SM100/SM120 and includes H100+ features such as DeepEP and
|
||||
# flash-attn-4. "a100" builds only SM80 and uses an A100-specific DeepEP patch
|
||||
# to avoid unsupported newer-GPU/NVSHMEM build paths. This keeps CI images
|
||||
# smaller and avoids compiling kernels for architectures a target image cannot
|
||||
# use.
|
||||
ARG BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:26.04-cuda13.2-devel-ubuntu24.04
|
||||
FROM ${BASE_IMAGE} AS base-image
|
||||
ARG BASE_IMAGE
|
||||
ARG UV_VERSION=0.11.27
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TRANSFORMERS_OFFLINE=0
|
||||
ENV HYDRA_FULL_ERROR=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV UV_PYTHON=3.13
|
||||
ENV UV_PROJECT_ENVIRONMENT=/opt/venv
|
||||
ENV UV_LINK_MODE=copy
|
||||
ENV UV_NO_CACHE=1
|
||||
ENV UV_NO_PROGRESS=1
|
||||
ENV VIRTUAL_ENV=/opt/venv
|
||||
ENV PATH="/root/.local/bin:${VIRTUAL_ENV}/bin:${PATH}"
|
||||
|
||||
RUN <<"EOF" bash -ex
|
||||
apt-get update
|
||||
apt-get install -y \
|
||||
curl \
|
||||
git
|
||||
apt-get clean
|
||||
EOF
|
||||
# NOTE: removed libsndfile1. Soundfile ships with its own version of libsndfile1
|
||||
|
||||
RUN <<"EOF" bash -ex
|
||||
curl -LsSf "https://astral.sh/uv/${UV_VERSION}/install.sh" | sh
|
||||
uv --version
|
||||
EOF
|
||||
|
||||
RUN <<"EOF" bash -euxo pipefail
|
||||
case "${BASE_IMAGE}" in
|
||||
*cuda12*) cuda_flavor=cu12 ;;
|
||||
*cuda13*) cuda_flavor=cu13 ;;
|
||||
*)
|
||||
echo "Cannot derive CUDA flavor from BASE_IMAGE='${BASE_IMAGE}'. Expected image tag containing 'cuda12' or 'cuda13'."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
cuda_major_minor="$(sed -n 's/.*cuda\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/p' <<<"${BASE_IMAGE}")"
|
||||
if [[ -z "${cuda_major_minor}" ]]; then
|
||||
echo "Cannot derive CUDA major.minor from BASE_IMAGE='${BASE_IMAGE}'. Expected image tag containing e.g. 'cuda12.9' or 'cuda13.2'."
|
||||
exit 1
|
||||
fi
|
||||
cat >/usr/local/bin/nemo-cuda-flavor <<SCRIPT
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
echo "${cuda_flavor}"
|
||||
SCRIPT
|
||||
cat >/usr/local/bin/nemo-install-cuda-python <<SCRIPT
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cuda_major_minor="${cuda_major_minor}"
|
||||
cuda_major="\${cuda_major_minor%%.*}"
|
||||
cuda_minor="\${cuda_major_minor#*.}"
|
||||
cuda_next_minor="\$((cuda_minor + 1))"
|
||||
uv pip install \
|
||||
"cuda-bindings>=\${cuda_major_minor}.0,<\${cuda_major}.\${cuda_next_minor}" \
|
||||
"cuda-python>=\${cuda_major_minor}.0,<\${cuda_major}.\${cuda_next_minor}"
|
||||
SCRIPT
|
||||
chmod +x /usr/local/bin/nemo-cuda-flavor /usr/local/bin/nemo-install-cuda-python
|
||||
echo "Derived CUDA flavor: ${cuda_flavor}"
|
||||
echo "Derived CUDA Python major.minor: ${cuda_major_minor}"
|
||||
EOF
|
||||
|
||||
WORKDIR /workspace
|
||||
COPY pyproject.toml uv.lock /workspace/
|
||||
COPY nemo/__init__.py nemo/package_info.py /workspace/nemo/
|
||||
RUN <<"EOF" bash -ex
|
||||
cuda_flavor="$(nemo-cuda-flavor)"
|
||||
uv sync --link-mode copy --locked --extra all --extra "${cuda_flavor}" --group test
|
||||
nemo-install-cuda-python
|
||||
EOF
|
||||
|
||||
RUN <<"EOF" bash -ex
|
||||
# Container-only runtime utilities. Keep these out of pyproject.toml so they do
|
||||
# not become NeMo package dependencies.
|
||||
uv pip install \
|
||||
dill \
|
||||
orjson
|
||||
|
||||
case "$(nemo-cuda-flavor)" in
|
||||
cu12) torchcodec_index=https://download.pytorch.org/whl/cu126 ;;
|
||||
cu13) torchcodec_index=https://download.pytorch.org/whl/cu132 ;;
|
||||
esac
|
||||
uv pip install --index-url "${torchcodec_index}" torchcodec
|
||||
EOF
|
||||
|
||||
FROM base-image AS automodel-deps
|
||||
ARG GPU_TARGET=h100plus
|
||||
ARG DEEPEP_REPO=https://github.com/deepseek-ai/DeepEP.git
|
||||
ARG DEEPEP_REF=v1.2.1
|
||||
ARG FLASH_ATTN_MAX_JOBS=8
|
||||
ARG FLASH_ATTN_NVCC_THREADS=1
|
||||
|
||||
ENV PIP_NO_CACHE_DIR=1
|
||||
|
||||
COPY external/patches/deepep_v1_a100_disable_nvshmem.patch /tmp/patches/
|
||||
RUN <<"EOF" bash -euxo pipefail
|
||||
apt-get update
|
||||
apt-get install -y \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
ninja-build \
|
||||
patch
|
||||
apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
EOF
|
||||
|
||||
RUN <<"EOF" bash -euxo pipefail
|
||||
cat >/usr/local/bin/automodel-build-env <<'SCRIPT'
|
||||
case "${GPU_TARGET}" in
|
||||
h100plus)
|
||||
export DEEPEP_TORCH_CUDA_ARCH_LIST="9.0+PTX"
|
||||
export TORCH_CUDA_ARCH_LIST="9.0;10.0;12.0"
|
||||
export NVTE_CUDA_ARCHS="90;100;120"
|
||||
export FLASH_ATTN_CUDA_ARCHS="90;100;120"
|
||||
export CMAKE_CUDA_ARCHITECTURES="90;100;120"
|
||||
;;
|
||||
a100)
|
||||
export DEEPEP_TORCH_CUDA_ARCH_LIST="8.0"
|
||||
export TORCH_CUDA_ARCH_LIST="8.0"
|
||||
export NVTE_CUDA_ARCHS="80"
|
||||
export FLASH_ATTN_CUDA_ARCHS="80"
|
||||
export CMAKE_CUDA_ARCHITECTURES="80"
|
||||
export DISABLE_SM90_FEATURES=1
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported GPU_TARGET='${GPU_TARGET}'. Expected 'h100plus' or 'a100'."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
CUDA_FLAVOR="$(nemo-cuda-flavor)"
|
||||
AUTOMODEL_CCCL_INCLUDES="/usr/local/cuda/include/cccl"
|
||||
PYTHON_CCCL_INCLUDE="${VIRTUAL_ENV}/lib/python${UV_PYTHON}/site-packages/nvidia/${CUDA_FLAVOR}/include/cccl"
|
||||
if [[ -d "${PYTHON_CCCL_INCLUDE}" ]]; then
|
||||
AUTOMODEL_CCCL_INCLUDES="${AUTOMODEL_CCCL_INCLUDES}:${PYTHON_CCCL_INCLUDE}"
|
||||
fi
|
||||
export CPATH="${AUTOMODEL_CCCL_INCLUDES}${CPATH:+:${CPATH}}"
|
||||
export CPLUS_INCLUDE_PATH="${AUTOMODEL_CCCL_INCLUDES}${CPLUS_INCLUDE_PATH:+:${CPLUS_INCLUDE_PATH}}"
|
||||
export NVTE_FRAMEWORK=pytorch
|
||||
export MAX_JOBS="${FLASH_ATTN_MAX_JOBS}"
|
||||
export NVCC_THREADS="${FLASH_ATTN_NVCC_THREADS}"
|
||||
SCRIPT
|
||||
mkdir -p /opt/automodel-src
|
||||
EOF
|
||||
|
||||
COPY external/patches/flash_attn_4_sm120_disable_tma_o.patch /tmp/patches/
|
||||
RUN <<"EOF" bash -euxo pipefail
|
||||
source /usr/local/bin/automodel-build-env
|
||||
uv pip install --upgrade \
|
||||
cmake \
|
||||
ninja \
|
||||
nvidia-mathdx \
|
||||
packaging \
|
||||
pip \
|
||||
psutil \
|
||||
pybind11 \
|
||||
setuptools \
|
||||
setuptools-scm \
|
||||
wheel
|
||||
|
||||
automodel_extra=compiled
|
||||
if [[ "${GPU_TARGET}" == "h100plus" ]]; then
|
||||
automodel_extra=compiled
|
||||
elif [[ "${GPU_TARGET}" == "a100" ]]; then
|
||||
automodel_extra=compiled-a100
|
||||
fi
|
||||
cuda_flavor="$(nemo-cuda-flavor)"
|
||||
uv sync \
|
||||
--inexact \
|
||||
--link-mode copy \
|
||||
--locked \
|
||||
--extra all \
|
||||
--extra "${cuda_flavor}" \
|
||||
--extra "${automodel_extra}" \
|
||||
--group test
|
||||
nemo-install-cuda-python
|
||||
|
||||
if [[ "${GPU_TARGET}" == "a100" ]]; then
|
||||
git clone "${DEEPEP_REPO}" /opt/automodel-src/DeepEP
|
||||
git -C /opt/automodel-src/DeepEP checkout -f "${DEEPEP_REF}"
|
||||
patch -d /opt/automodel-src/DeepEP -p1 < /tmp/patches/deepep_v1_a100_disable_nvshmem.patch
|
||||
TORCH_CUDA_ARCH_LIST="${DEEPEP_TORCH_CUDA_ARCH_LIST}" \
|
||||
uv pip install \
|
||||
--no-build-isolation \
|
||||
--no-deps \
|
||||
/opt/automodel-src/DeepEP
|
||||
fi
|
||||
|
||||
if [[ "${GPU_TARGET}" == "h100plus" ]]; then
|
||||
# flash-attn-4 requires apache-tvm-ffi 0.1.11, while mamba-ssm
|
||||
# currently constrains the solved environment to apache-tvm-ffi<=0.1.9.
|
||||
cutlass_packages=(
|
||||
"nvidia-cutlass-dsl==4.5.2"
|
||||
"nvidia-cutlass-dsl-libs-base==4.5.2"
|
||||
)
|
||||
if [[ "$(nemo-cuda-flavor)" == "cu13" ]]; then
|
||||
cutlass_packages+=("nvidia-cutlass-dsl-libs-cu13==4.5.2")
|
||||
fi
|
||||
uv pip install \
|
||||
--no-deps \
|
||||
"apache-tvm-ffi==0.1.11" \
|
||||
"${cutlass_packages[@]}" \
|
||||
"quack-kernels==0.5.0" \
|
||||
"torch-c-dlpack-ext==0.1.5"
|
||||
|
||||
mkdir -p /opt/automodel-src/flash-attn-4 /tmp/flash-attn-4
|
||||
python -m pip download \
|
||||
--no-binary=flash-attn-4 \
|
||||
--no-deps \
|
||||
--dest /tmp/flash-attn-4 \
|
||||
"flash-attn-4==4.0.0b15"
|
||||
tar -xf /tmp/flash-attn-4/flash_attn_4-*.tar.gz -C /opt/automodel-src/flash-attn-4 --strip-components=1
|
||||
patch -d /opt/automodel-src/flash-attn-4 -p1 < /tmp/patches/flash_attn_4_sm120_disable_tma_o.patch
|
||||
|
||||
MAX_JOBS="${FLASH_ATTN_MAX_JOBS}" \
|
||||
NVCC_THREADS="${FLASH_ATTN_NVCC_THREADS}" \
|
||||
uv pip install \
|
||||
--no-build-isolation \
|
||||
--no-deps \
|
||||
/opt/automodel-src/flash-attn-4
|
||||
fi
|
||||
EOF
|
||||
|
||||
FROM base-image
|
||||
COPY --from=automodel-deps /opt/venv /opt/venv
|
||||
|
||||
ENV NVIDIA_BUILD_ID=${NVIDIA_BUILD_ID:-<unknown>}
|
||||
LABEL com.nvidia.build.id="${NVIDIA_BUILD_ID}"
|
||||
ARG NVIDIA_BUILD_REF
|
||||
LABEL com.nvidia.build.ref="${NVIDIA_BUILD_REF}"
|
||||
ARG RC_DATE=00.00
|
||||
ARG TARGETARCH
|
||||
|
||||
ARG INSTALL_FFMPEG=false
|
||||
RUN <<"EOF" bash -ex
|
||||
if [ "${INSTALL_FFMPEG}" = "true" ]; then
|
||||
apt-get update
|
||||
apt-get install -y ffmpeg
|
||||
apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
EOF
|
||||
|
||||
COPY nemo /workspace/nemo
|
||||
|
||||
# NOTICES.txt file points to where the OSS source code is archived
|
||||
RUN echo "This distribution includes open source which is archived at the following URL: https://opensource.nvidia.com/oss/teams/nvidia/nemo/${RC_DATE}:linux-${TARGETARCH}/index.html" > NOTICES.txt && \
|
||||
echo "For further inquiries or assistance, contact us at oss-requests@nvidia.com" >> NOTICES.txt
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " applehelp to make an Apple Help Book"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " coverage to run coverage check of the documentation (if enabled)"
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
.PHONY: html
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
.PHONY: dirhtml
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
.PHONY: singlehtml
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
.PHONY: pickle
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
.PHONY: json
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
.PHONY: htmlhelp
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
.PHONY: qthelp
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenSeq2Seq.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenSeq2Seq.qhc"
|
||||
|
||||
.PHONY: applehelp
|
||||
applehelp:
|
||||
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
||||
@echo
|
||||
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
||||
@echo "N.B. You won't be able to view it unless you put it in" \
|
||||
"~/Library/Documentation/Help or install it in your application" \
|
||||
"bundle."
|
||||
|
||||
.PHONY: devhelp
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/OpenSeq2Seq"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenSeq2Seq"
|
||||
@echo "# devhelp"
|
||||
|
||||
.PHONY: epub
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
.PHONY: latex
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
.PHONY: latexpdf
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: latexpdfja
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: text
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
.PHONY: man
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
.PHONY: texinfo
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
.PHONY: info
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
.PHONY: gettext
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
.PHONY: changes
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
.PHONY: linkcheck
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
.PHONY: doctest
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
||||
@echo "Testing of coverage in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/coverage/python.txt."
|
||||
|
||||
.PHONY: xml
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
.PHONY: pseudoxml
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
@@ -0,0 +1,61 @@
|
||||
# Documentation Process for NeMo
|
||||
|
||||
## Building the Documentation
|
||||
|
||||
1. Install the documentation dependencies into the locked `uv` environment:
|
||||
|
||||
```console
|
||||
$ uv sync --locked --group docs
|
||||
```
|
||||
|
||||
1. Build the documentation:
|
||||
|
||||
```console
|
||||
$ uv run make -C docs html
|
||||
```
|
||||
|
||||
## Checking for Broken Links
|
||||
|
||||
1. Build the documentation, as described in the preceding section, but use the following command:
|
||||
|
||||
```shell
|
||||
uv run make -C docs clean linkcheck
|
||||
```
|
||||
|
||||
1. Run the link-checking script:
|
||||
|
||||
```shell
|
||||
./docs/check_for_broken_links.sh
|
||||
```
|
||||
|
||||
If there are no broken links, then the script exits with `0`.
|
||||
|
||||
If the script produces any output, cut and paste the `uri` value into your browser to confirm
|
||||
that the link is broken.
|
||||
|
||||
```json
|
||||
{
|
||||
"filename": "nlp/text_normalization/nn_text_normalization.rst",
|
||||
"lineno": 247,
|
||||
"status": "broken",
|
||||
"code": 0,
|
||||
"uri": "https://research.fb.com/wp-content/uploads/2019/03/Neural-Models-of-Text-Normalization-for-Speech-Applications.pdf",
|
||||
"info": "400 Client Error: Bad Request for url: https://research.facebook.com/wp-content/uploads/2019/03/Neural-Models-of-Text-Normalization-for-Speech-Applications.pdf"
|
||||
}
|
||||
```
|
||||
|
||||
If the link is OK, and this is the case with many URLs that reference GitHub repository file headings,
|
||||
then cut and paste the JSON output and add it to `docs/false_positives.json`.
|
||||
Run the script again to confirm that the URL is no longer reported as a broken link.
|
||||
|
||||
There may be false positives due to Sphinx not being able to detect links from built html files.
|
||||
Instead of adding those to the `docs/false_positives.json` file, it would be best to rewrite the
|
||||
reference using a [:ref:](https://www.sphinx-doc.org/en/master/usage/referencing.html#role-ref).
|
||||
|
||||
For example, instead of writing `Modules <../api.html#modules>` to link to the modules section of
|
||||
a `api.rst` file, write it as ``:ref:`Modules <asr-api-modules>` ``. And in the `api.rst` file, add
|
||||
this label before the section being linked to:
|
||||
|
||||
```
|
||||
.. _asr-api-modules:
|
||||
```
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DOCS_DIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
FALSE_POSITIVES_JSON="${DOCS_DIR}/false_positives.json"
|
||||
NEEDS_REVIEW_JSON="${DOCS_DIR}/links_needing_review.json"
|
||||
LINKCHECK_JSON="${DOCS_DIR}/build/linkcheck/output.json"
|
||||
|
||||
function check_environment {
|
||||
local err=0
|
||||
if ! [ -x "$(command -v jq)" ]; then
|
||||
>&2 echo "jq is required but is not found."
|
||||
((err++))
|
||||
fi
|
||||
if [ ! -f "${FALSE_POSITIVES_JSON}" ]; then
|
||||
>&2 echo "A JSON file with false positives is required: ${FALSE_POSITIVES_JSON}"
|
||||
((err++))
|
||||
fi
|
||||
if [ ! -f "${LINKCHECK_JSON}" ]; then
|
||||
>&2 echo "Did not find linkcheck output JSON file: ${LINKCHECK_JSON}."
|
||||
>&2 echo "Run Sphinx with the linkcheck arg: make -C docs clean linkcheck"
|
||||
((err++))
|
||||
fi
|
||||
if [ "${err}" -gt 0 ]; then
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
function check_links {
|
||||
local err=0
|
||||
broken=$(jq -s 'map(select(.status=="broken"))' "$LINKCHECK_JSON")
|
||||
count=$(echo "${broken}" | jq 'length')
|
||||
for i in $(seq 0 $(($count - 1)))
|
||||
do
|
||||
entry=$(echo "${broken}" | jq ".[${i}]")
|
||||
link=$(echo "${entry}" | jq -r '.uri')
|
||||
[ -n "${DEBUG}" ] && {
|
||||
echo >&2 "Checking for false positive: ${link}"
|
||||
}
|
||||
local false_positive_resp; false_positive_resp=$(jq --arg check "${link}" -s 'any(.uri == $check)' < "${FALSE_POSITIVES_JSON}")
|
||||
local needs_review_resp; needs_review_resp=$(jq --arg check "${link}" -s 'any(.uri == $check)' < "${NEEDS_REVIEW_JSON}")
|
||||
# "false" indicates that the URL did not match any of the URIs in the false positive file.
|
||||
if [[ "false" = "${false_positive_resp}" && "false" = "${needs_review_resp}" ]]; then
|
||||
((err++))
|
||||
echo "${entry}"
|
||||
fi
|
||||
done
|
||||
exit "${err}"
|
||||
}
|
||||
|
||||
check_environment
|
||||
check_links
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,381 @@
|
||||
@import url("theme.css");
|
||||
|
||||
body {
|
||||
font-size: 100%;
|
||||
font-family: 'NVIDIA Sans', sans-serif;
|
||||
}
|
||||
|
||||
|
||||
/* Width of template */
|
||||
|
||||
.wy-nav-content {
|
||||
max-width: 1200px !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Standard Text Formatting */
|
||||
|
||||
h1 {
|
||||
color: #76b900;
|
||||
text-align: center;
|
||||
/* background-color: #ffffff; */
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #ffffff;
|
||||
/* background-color: #ffffff; */
|
||||
/* #76b900 */
|
||||
Padding: 5px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding-top: 0px;
|
||||
border-top: solid 3px #000000;
|
||||
/* #76b900 */
|
||||
border-bottom: solid 3px #000000;
|
||||
/* #76b900 */
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* Link Colors */
|
||||
/*
|
||||
a {
|
||||
color: #76b900;
|
||||
}
|
||||
/*
|
||||
|
||||
/*
|
||||
a:visited {
|
||||
color: #218219;
|
||||
}
|
||||
*/
|
||||
|
||||
.container-xl {
|
||||
margin-right: unset;
|
||||
margin-left: unset;
|
||||
}
|
||||
|
||||
section {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------TABLES--------------------------------------- */
|
||||
section table {
|
||||
overflow-x: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
/* Table head Color */
|
||||
thead td {
|
||||
background-color: #333333 !important;
|
||||
}
|
||||
|
||||
.row-odd p {
|
||||
/*padding-bottom: 0px;*/
|
||||
/*margin-bottom: 0px;*/
|
||||
}
|
||||
|
||||
/* even rows*/
|
||||
|
||||
.row-even tr {
|
||||
background-color: #e5f1e6 !important;
|
||||
}
|
||||
|
||||
/* odd rows*/
|
||||
|
||||
|
||||
.wy-table-responsive table tr {
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.wy-table-responsive table td {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
|
||||
/* Removes bottom margin in tables*/
|
||||
|
||||
.rst-content .line-block {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.wy-table-responsive {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/* reduces the size of text in multiline table columns. */
|
||||
|
||||
.rst-content table.docutils td {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.rst-content dl:not(.docutils) dt {
|
||||
|
||||
background-color: inherit;
|
||||
color: #000000;
|
||||
border-top: solid 0px #000000;
|
||||
|
||||
}
|
||||
|
||||
.rst-content dl:not(.docutils) dt:before {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.rst-content .line-block {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.wy-side-nav-search,
|
||||
.wy-nav-top {
|
||||
background-color: #000000;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wy-side-nav-search img {
|
||||
padding: 0px;
|
||||
padding: 0px 0px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.wy-side-nav-search input[type=text] {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
|
||||
.wy-menu-vertical p.caption {
|
||||
color: #76b900;
|
||||
}
|
||||
|
||||
|
||||
.wy-side-nav-search>a img.logo,
|
||||
.wy-side-nav-search .wy-dropdown>a img.logo {
|
||||
margin: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.wy-nav-content {
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
/* NVIDIA green hover on table rows */
|
||||
table tbody tr:hover,
|
||||
table.table tbody tr:hover,
|
||||
table.docutils tbody tr:hover,
|
||||
.pst-scrollable-table-container table tbody tr:hover,
|
||||
.rst-content table tbody tr:hover {
|
||||
background-color: rgba(118, 185, 0, 0.15) !important;
|
||||
}
|
||||
|
||||
/* List (numbered, bulleted) padding Fix */
|
||||
|
||||
|
||||
.wy-plain-list-decimal li {
|
||||
margin-top: -6px;
|
||||
margin-bottom: -6px;
|
||||
}
|
||||
|
||||
.rst-content .section ol.loweralpha {
|
||||
margin-top: -6px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.wy-plain-list-disc,
|
||||
.rst-content .toctree-wrapper ul,
|
||||
article ul {
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* Alert Boxes */
|
||||
/* Background color of Alert Box Title */
|
||||
|
||||
.rst-content .section ul {
|
||||
margin-top: -12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.wy-alert.wy-alert-info .wy-alert-title,
|
||||
.rst-content .note .wy-alert-title,
|
||||
.rst-content .wy-alert-info.attention .wy-alert-title,
|
||||
.rst-content .wy-alert-info.caution .wy-alert-title,
|
||||
.rst-content .wy-alert-info.danger .wy-alert-title,
|
||||
.rst-content .wy-alert-info.error .wy-alert-title,
|
||||
.rst-content .wy-alert-info.hint .wy-alert-title,
|
||||
.rst-content .wy-alert-info.important .wy-alert-title,
|
||||
.rst-content .wy-alert-info.tip .wy-alert-title,
|
||||
.rst-content .wy-alert-info.warning .wy-alert-title,
|
||||
.rst-content .seealso .wy-alert-title,
|
||||
.rst-content .wy-alert-info.admonition-todo .wy-alert-title,
|
||||
.rst-content .wy-alert-info.admonition .wy-alert-title,
|
||||
.wy-alert.wy-alert-info .rst-content .admonition-title,
|
||||
.rst-content .wy-alert.wy-alert-info .admonition-title,
|
||||
.rst-content .note .admonition-title,
|
||||
.rst-content .wy-alert-info.attention .admonition-title,
|
||||
.rst-content .wy-alert-info.caution .admonition-title,
|
||||
.rst-content .wy-alert-info.danger .admonition-title,
|
||||
.rst-content .wy-alert-info.error .admonition-title,
|
||||
.rst-content .wy-alert-info.hint .admonition-title,
|
||||
.rst-content .wy-alert-info.important .admonition-title,
|
||||
.rst-content .wy-alert-info.tip .admonition-title,
|
||||
.rst-content .wy-alert-info.warning .admonition-title,
|
||||
.rst-content .seealso .admonition-title,
|
||||
.rst-content .wy-alert-info.admonition-todo .admonition-title,
|
||||
.rst-content .wy-alert-info.admonition .admonition-title {
|
||||
background: #76b900;
|
||||
}
|
||||
|
||||
/* Background and Font Color of Alert Box Main Body*/
|
||||
.wy-alert.wy-alert-info,
|
||||
.rst-content .note,
|
||||
.rst-content .wy-alert-info.attention,
|
||||
.rst-content .wy-alert-info.caution,
|
||||
.rst-content .wy-alert-info.danger,
|
||||
.rst-content .wy-alert-info.error,
|
||||
.rst-content .wy-alert-info.hint,
|
||||
.rst-content .wy-alert-info.important,
|
||||
.rst-content .wy-alert-info.tip,
|
||||
.rst-content .wy-alert-info.warning,
|
||||
.rst-content .seealso,
|
||||
.rst-content .wy-alert-info.admonition-todo,
|
||||
.rst-content .wy-alert-info.admonition {
|
||||
background: #333333;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
.navbar-brand-box {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- Media Queries --------------------------------------- */
|
||||
@media (min-width: 1200px) {
|
||||
.container-xl {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: none) {
|
||||
body {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#site-navigation nav ul.nav {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#site-navigation nav.bd-links p {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#site-navigation {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.toc-h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.toc-h3 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.toc-h4 {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.header-article .bd-toc {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#main-content>div {
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- NVIDIA Sans --------------------------------------- */
|
||||
|
||||
:root {
|
||||
--md-text-font: "NVIDIA Sans";
|
||||
/* --md-code-font: "NVIDIA Sans"; */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NVIDIA Sans";
|
||||
src: url(https://aws1.discourse-cdn.com/nvidia/original/3X/5/2/52891dda673228d54e5d57bf1e4a3880d4b22405.woff2) format("woff2"),
|
||||
url(https://aws1.discourse-cdn.com/nvidia/original/3X/e/0/e090b7dda7a582522c7f9045c6ce949cce60134f.woff) format("woff");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NVIDIA Sans";
|
||||
src: url(https://aws1.discourse-cdn.com/nvidia/original/3X/a/1/a107baabcbf6b241099122336bce7429bcfd377a.woff2) format("woff2"),
|
||||
url(https://aws1.discourse-cdn.com/nvidia/original/3X/3/a/3a6060a4e3bce70e5552ba0de8af4b22c6cf9144.woff) format("woff");
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NVIDIA Sans";
|
||||
src: url(https://aws1.discourse-cdn.com/nvidia/original/3X/9/9/9920d2b172b01d92fc9c1c0e521dcf45b59c47c3.woff2) format("woff2"),
|
||||
url(https://aws1.discourse-cdn.com/nvidia/original/3X/6/c/6c7d947928a7e4ef3e80ed409bef6c243f2148cb.woff) format("woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NVIDIA Sans";
|
||||
src: url(https://aws1.discourse-cdn.com/nvidia/original/3X/e/8/e8e63fe1244372cd942d957f44a5616a1eba0644.woff2) format("woff2"),
|
||||
url(https://aws1.discourse-cdn.com/nvidia/original/3X/0/f/0f1fb2af0283ab09d36e7097bb07d895c3228f12.woff) format("woff");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NVIDIA Sans";
|
||||
src: url(https://aws1.discourse-cdn.com/nvidia/original/3X/7/9/79d3c513a9cd72c59f65354f39f89ca52dc17dd2.woff2) format("woff2"),
|
||||
url(https://aws1.discourse-cdn.com/nvidia/original/3X/2/5/2581ac533f5d01f4985d8a7245b0766b4630ced8.woff) format("woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NVIDIA Sans";
|
||||
src: url(https://aws1.discourse-cdn.com/nvidia/original/3X/3/9/39d9ef1ee9770dd503f19bb2ace2fdb4eff3bb50.woff2) format("woff2"),
|
||||
url(https://aws1.discourse-cdn.com/nvidia/original/3X/7/b/7bb5d5e2e71b2e13c8098b2e67c0a0ed9258e6c7.woff) format("woff");
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NVIDIA Sans";
|
||||
src: url(https://aws1.discourse-cdn.com/nvidia/original/3X/0/5/05276a55a43eb3f74981ec1e93252727afcd9d16.woff2) format("woff2"),
|
||||
url(https://aws1.discourse-cdn.com/nvidia/original/3X/9/c/9cfec7ed941b06564aa4d5ca14610e81542d070f.woff) format("woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NVIDIA Sans";
|
||||
src: url(https://aws1.discourse-cdn.com/nvidia/original/3X/a/e/aebd14d09ba56f541e1b8735fb051e33710f9ae7.woff2) format("woff2"),
|
||||
url(https://aws1.discourse-cdn.com/nvidia/original/3X/e/d/edbdabef43acc5c12e84a94baaa5542c9404cfeb.woff) format("woff");
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var params = window.location.search.substring(1).split("&").reduce(function (params, param) {
|
||||
if (!param) {
|
||||
return params;
|
||||
}
|
||||
|
||||
var values = param.split("=");
|
||||
var name = values[0];
|
||||
var value = values[1];
|
||||
params[name] = value;
|
||||
return params;
|
||||
}, {});
|
||||
|
||||
var form = document.getElementById("feedback-form");
|
||||
for (var name in params) {
|
||||
var input = form.querySelector("[name=" + name + "]");
|
||||
input.value = params[name];
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
:orphan:
|
||||
|
||||
=========
|
||||
NeMo APIs
|
||||
=========
|
||||
|
||||
You can learn more about the underlying principles of the NeMo codebase in this section.
|
||||
|
||||
The `NeMo Toolkit codebase <https://github.com/NVIDIA/NeMo>`__ is composed of a `core <https://github.com/NVIDIA/NeMo/tree/main/nemo/core>`__ section which contains the main building blocks of the framework, and various `collections <https://github.com/NVIDIA/NeMo/tree/main/nemo/collections>`__ which help you
|
||||
build specialized AI models.
|
||||
|
||||
You can learn more about aspects of the NeMo "core" by following the links below:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: core
|
||||
:titlesonly:
|
||||
|
||||
core/core
|
||||
core/neural_modules
|
||||
core/exp_manager
|
||||
core/neural_types
|
||||
core/adapters/intro
|
||||
|
||||
You can learn more about aspects of the NeMo APIs by following the links below:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: API
|
||||
:titlesonly:
|
||||
|
||||
core/api
|
||||
common/intro
|
||||
asr/api
|
||||
tts/api
|
||||
audio/api
|
||||
|
||||
|
||||
Alternatively, you can jump straight to the documentation for the individual collections:
|
||||
|
||||
* :doc:`Automatic Speech Recognition (ASR) <../asr/intro>`
|
||||
|
||||
* :doc:`Text-to-Speech (TTS) <../tts/intro>`
|
||||
|
||||
* :doc:`Audio Processing <../audio/intro>`
|
||||
|
||||
* :doc:`SpeechLM2 <../speechlm2/intro>`
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
:orphan:
|
||||
|
||||
All Checkpoints
|
||||
===============
|
||||
English
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_en.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
German
|
||||
^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_de.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Spanish
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_es.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
French
|
||||
^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_fr.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Arabic
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_ar.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
------------------------------
|
||||
|
||||
Russian
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_ru.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Portuguese
|
||||
^^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_pt.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Belarusian
|
||||
^^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_be.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Japanese
|
||||
^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_jp.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Armenian
|
||||
^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_hy.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Georgian
|
||||
^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_ka.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Kazakh
|
||||
^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_kz.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Persian
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_fa.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Uzbek
|
||||
^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_uz.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Ukrainian
|
||||
^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_ua.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Polish
|
||||
^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_pl.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Italian
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_it.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
|
||||
Croatian
|
||||
^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_hr.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Esperanto
|
||||
^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_eo.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Kabyle
|
||||
^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_kab.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Dutch
|
||||
^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_nl.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Catalan
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_ca.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Hindi
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_hi.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Marathi
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_mr.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Mandarin
|
||||
^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_zh.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Czech
|
||||
^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_cs.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Bulgarian
|
||||
^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_bg.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Danish
|
||||
^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_da.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Estonian
|
||||
^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_et.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Finnish
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_fi.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Greek
|
||||
^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_el.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Hungarian
|
||||
^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_hu.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Latvian
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_lv.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Lithuanian
|
||||
^^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_lt.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Maltese
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_mt.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Romanian
|
||||
^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_ro.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Slovak
|
||||
^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_sk.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Slovenian
|
||||
^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_sl.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Swedish
|
||||
^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_sv.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
|
||||
-----------------------------
|
||||
|
||||
Kinyarwanda
|
||||
^^^^^^^^^^^
|
||||
.. csv-table::
|
||||
:file: data/benchmark_rw.csv
|
||||
:align: left
|
||||
:widths: 50,50
|
||||
:header-rows: 1
|
||||
@@ -0,0 +1,367 @@
|
||||
NeMo ASR API
|
||||
============
|
||||
|
||||
|
||||
Model Classes
|
||||
-------------
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecCTCModel
|
||||
:show-inheritance:
|
||||
:members: transcribe, change_vocabulary, setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecCTCModelBPE
|
||||
:show-inheritance:
|
||||
:members: transcribe, change_vocabulary, setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecRNNTModel
|
||||
:show-inheritance:
|
||||
:members: transcribe, change_vocabulary, setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecRNNTBPEModel
|
||||
:show-inheritance:
|
||||
:members: transcribe, change_vocabulary, setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecRNNTBPEModelWithPrompt
|
||||
:show-inheritance:
|
||||
:members: transcribe, set_inference_prompt, initialize_prompt_feature, change_vocabulary, setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecHybridRNNTCTCBPEModelWithPrompt
|
||||
:show-inheritance:
|
||||
:members: transcribe, set_inference_prompt, initialize_prompt_feature, change_vocabulary, setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecMultiTalkerRNNTBPEModel
|
||||
:show-inheritance:
|
||||
:members: transcribe, change_vocabulary, setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecClassificationModel
|
||||
:show-inheritance:
|
||||
:members: setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. autoclass:: nemo.collections.asr.models.EncDecSpeakerLabelModel
|
||||
:show-inheritance:
|
||||
:members: setup_training_data, setup_optimization, setup_validation_data, setup_test_data, register_artifact
|
||||
|
||||
|
||||
.. _asr-api-modules:
|
||||
|
||||
Modules
|
||||
-------
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.ConvASREncoder
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.ConvASRDecoder
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.ConvASRDecoderClassification
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.SpeakerDecoder
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. _conformer-encoder-api:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.ConformerEncoder
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
|
||||
.. _rnn-encoder-api:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.RNNEncoder
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. _rnnt-decoder-api:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.RNNTDecoder
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.StatelessTransducerDecoder
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. _rnnt-joint-api:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.RNNTJoint
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.SampledRNNTJoint
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
|
||||
Mixins
|
||||
------
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.mixins.mixins.ASRBPEMixin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.mixins.mixins.ASRModuleMixin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.mixins.transcription.TranscriptionMixin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.mixins.transcription.TranscribeConfig
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.mixins.interctc_mixin.InterCTCMixin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.mixins.multitalker_asr_mixins.SpeakerKernelMixin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. _asr-api-datasets:
|
||||
|
||||
Datasets
|
||||
--------
|
||||
|
||||
Character Encoding Datasets
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.audio_to_text.AudioToCharDataset
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.audio_to_text.TarredAudioToCharDataset
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
|
||||
Text-to-Text Datasets
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.text_to_text.TextToTextDataset
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.text_to_text.TextToTextIterableDataset
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
|
||||
Speaker-Tagged Datasets for Multitalker ASR models
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.audio_to_text_lhotse_speaker.LhotseSpeechToTextSpkBpeDataset
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.audio_to_diar_label_lhotse.LhotseAudioToSpeechE2ESpkDiarDataset
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.data_simulation.MultiSpeakerSimulator
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.data_simulation.RIRMultiSpeakerSimulator
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
Subword Encoding Datasets
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.audio_to_text.AudioToBPEDataset
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.data.audio_to_text.TarredAudioToBPEDataset
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. _asr-audio-preprocessors:
|
||||
|
||||
Audio Preprocessors
|
||||
-------------------
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.AudioToMelSpectrogramPreprocessor
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.AudioToMFCCPreprocessor
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. _asr-api-audio-augmentors:
|
||||
|
||||
Audio Augmentors
|
||||
----------------
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.SpectrogramAugmentation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.modules.CropOrPadSpectrogramAugmentation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.SpeedPerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.TimeStretchPerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.GainPerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.ImpulsePerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.ShiftPerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.NoisePerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.WhiteNoisePerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.RirAndNoisePerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.preprocessing.perturb.TranscodePerturbation
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
Miscellaneous Classes
|
||||
---------------------
|
||||
|
||||
CTC Decoding
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.ctc_decoding.CTCDecoding
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.ctc_decoding.CTCBPEDecoding
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.ctc_greedy_decoding.GreedyCTCInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.ctc_beam_decoding.BeamCTCInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
RNNT Decoding
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.rnnt_decoding.RNNTDecoding
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.rnnt_decoding.RNNTBPEDecoding
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.rnnt_greedy_decoding.GreedyRNNTInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.rnnt_greedy_decoding.GreedyBatchedRNNTInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.rnnt_beam_decoding.BeamRNNTInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.rnnt_beam_decoding.BeamBatchedRNNTInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
TDT Decoding
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.rnnt_greedy_decoding.GreedyTDTInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.rnnt_greedy_decoding.GreedyBatchedTDTInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.tdt_beam_decoding.BeamTDTInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.tdt_beam_decoding.BeamBatchedTDTInfer
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
Hypotheses
|
||||
~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.utils.rnnt_utils.Hypothesis
|
||||
:show-inheritance:
|
||||
:no-members:
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.utils.rnnt_utils.NBestHypotheses
|
||||
:show-inheritance:
|
||||
:no-members:
|
||||
|
||||
Adapter Networks
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.adapters.multi_head_attention_adapter_module.MultiHeadAttentionAdapter
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.adapters.multi_head_attention_adapter_module.RelPositionMultiHeadAttentionAdapter
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.adapters.multi_head_attention_adapter_module.PositionalEncodingAdapter
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.adapters.multi_head_attention_adapter_module.RelPositionalEncodingAdapter
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
|
||||
Adapter Strategies
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: nemo.collections.asr.parts.submodules.adapters.multi_head_attention_adapter_module.MHAResidualAddAdapterStrategy
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: adapter_module_names
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,396 @@
|
||||
.. _asr-checkpoints-list:
|
||||
|
||||
=======================
|
||||
ASR Model Checkpoints
|
||||
=======================
|
||||
|
||||
This page lists all supported ASR model checkpoints released by NVIDIA NeMo.
|
||||
Benchmark scores for each model can be found on its `HuggingFace model card <https://huggingface.co/nvidia>`__.
|
||||
|
||||
For community fine-tunes built on these checkpoints, see :doc:`Featured Community Checkpoints <./featured_community_checkpoints>`.
|
||||
|
||||
Glossary
|
||||
--------
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Term
|
||||
- Definition
|
||||
* - **ASR**
|
||||
- Automatic Speech Recognition — transcribing speech to text
|
||||
* - **AST**
|
||||
- Automatic Speech Translation — translating speech to text from one language to another
|
||||
* - **AED**
|
||||
- Attention Encoder-Decoder — autoregressive decoder using cross-attention (Canary family)
|
||||
* - **CTC**
|
||||
- Connectionist Temporal Classification — non-autoregressive decoder
|
||||
* - **RNN-T**
|
||||
- Recurrent Neural Network Transducer — autoregressive streaming-friendly decoder
|
||||
* - **TDT**
|
||||
- Token-and-Duration Transducer — extends RNN-T with duration prediction for faster inference
|
||||
* - **Hybrid**
|
||||
- Joint RNN-T + CTC model — both decoders trained together, either usable at inference
|
||||
* - **PnC**
|
||||
- Punctuation and Capitalization in the output
|
||||
* - **SALM**
|
||||
- Speech Augmented Language Model — combines a speech encoder with a large language model
|
||||
* - **Streaming**
|
||||
- Real-time / cache-aware inference capability
|
||||
* - **EU4**
|
||||
- English, German, Spanish, French
|
||||
* - **EU25**
|
||||
- English, German, Spanish, French, Italian, Polish, Portuguese, Dutch, Russian, Ukrainian, Belarusian, Croatian, Czech, Bulgarian, Danish, Estonian, Finnish, Greek, Hungarian, Latvian, Lithuanian, Maltese, Romanian, Slovak, Slovenian, Swedish
|
||||
|
||||
Canary Models (AED)
|
||||
-------------------
|
||||
|
||||
Multi-task encoder-decoder models supporting ASR, AST, PnC, and timestamps across multiple languages.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Model
|
||||
- Decoder
|
||||
- Capabilities
|
||||
- Language
|
||||
- Size
|
||||
* - `canary-1b-v2 <https://huggingface.co/nvidia/canary-1b-v2>`__
|
||||
- AED
|
||||
- ASR, AST, PnC, timestamps
|
||||
- EU25
|
||||
- 1B
|
||||
* - `canary-qwen-2.5b <https://huggingface.co/nvidia/canary-qwen-2.5b>`__
|
||||
- SALM
|
||||
- ASR, AST, PnC, timestamps
|
||||
- EU25
|
||||
- 2.5B
|
||||
* - `canary-1b-flash <https://huggingface.co/nvidia/canary-1b-flash>`__
|
||||
- AED
|
||||
- ASR, AST, PnC, timestamps, fast
|
||||
- EU4
|
||||
- 1B
|
||||
* - `canary-180m-flash <https://huggingface.co/nvidia/canary-180m-flash>`__
|
||||
- AED
|
||||
- ASR, AST, PnC, timestamps, fast
|
||||
- EU4
|
||||
- 180M
|
||||
* - `canary-1b <https://huggingface.co/nvidia/canary-1b>`__
|
||||
- AED
|
||||
- ASR, AST, PnC
|
||||
- EU4
|
||||
- 1B
|
||||
|
||||
|
||||
Parakeet Models
|
||||
-----------------
|
||||
|
||||
High-accuracy ASR models built on the FastConformer encoder architecture.
|
||||
Parakeet, Nemotron Speech, and the ``stt_*_fastconformer_*`` models below all share the same underlying FastConformer encoder;
|
||||
the different names reflect release branding, not architectural differences.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Model
|
||||
- Decoder
|
||||
- Capabilities
|
||||
- Language
|
||||
- Size
|
||||
* - `parakeet-tdt-0.6b-v3 <https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3>`__
|
||||
- TDT
|
||||
- ASR, PnC, timestamps
|
||||
- English
|
||||
- 0.6B
|
||||
* - `parakeet-tdt-0.6b-v2 <https://huggingface.co/nvidia/parakeet-tdt-0.6b-v2>`__
|
||||
- TDT
|
||||
- ASR, PnC, timestamps
|
||||
- English
|
||||
- 0.6B
|
||||
* - `parakeet-tdt-1.1b <https://huggingface.co/nvidia/parakeet-tdt-1.1b>`__
|
||||
- TDT
|
||||
- ASR, timestamps
|
||||
- English
|
||||
- 1.1B
|
||||
* - `parakeet-tdt_ctc-1.1b <https://huggingface.co/nvidia/parakeet-tdt_ctc-1.1b>`__
|
||||
- Hybrid TDT+CTC
|
||||
- ASR, timestamps
|
||||
- English
|
||||
- 1.1B
|
||||
* - `parakeet-tdt_ctc-0.6b-ja <https://huggingface.co/nvidia/parakeet-tdt_ctc-0.6b-ja>`__
|
||||
- Hybrid TDT+CTC
|
||||
- ASR, timestamps
|
||||
- Japanese
|
||||
- 0.6B
|
||||
* - `parakeet-tdt_ctc-110m <https://huggingface.co/nvidia/parakeet-tdt_ctc-110m>`__
|
||||
- Hybrid TDT+CTC
|
||||
- ASR, timestamps
|
||||
- English
|
||||
- 110M
|
||||
* - `parakeet-rnnt-1.1b <https://huggingface.co/nvidia/parakeet-rnnt-1.1b>`__
|
||||
- RNN-T
|
||||
- ASR, timestamps
|
||||
- English
|
||||
- 1.1B
|
||||
* - `parakeet-rnnt-0.6b <https://huggingface.co/nvidia/parakeet-rnnt-0.6b>`__
|
||||
- RNN-T
|
||||
- ASR, timestamps
|
||||
- English
|
||||
- 0.6B
|
||||
* - `parakeet-ctc-1.1b <https://huggingface.co/nvidia/parakeet-ctc-1.1b>`__
|
||||
- CTC
|
||||
- ASR
|
||||
- English
|
||||
- 1.1B
|
||||
* - `parakeet-ctc-0.6b <https://huggingface.co/nvidia/parakeet-ctc-0.6b>`__
|
||||
- CTC
|
||||
- ASR
|
||||
- English
|
||||
- 0.6B
|
||||
* - `parakeet-ctc-0.6b-Vietnamese <https://huggingface.co/nvidia/parakeet-ctc-0.6b-Vietnamese>`__
|
||||
- CTC
|
||||
- ASR
|
||||
- Vietnamese
|
||||
- 0.6B
|
||||
* - `parakeet-rnnt-110m-da-dk <https://huggingface.co/nvidia/parakeet-rnnt-110m-da-dk>`__
|
||||
- RNN-T
|
||||
- ASR
|
||||
- Danish
|
||||
- 110M
|
||||
|
||||
|
||||
Streaming Models
|
||||
-----------------
|
||||
|
||||
Cache-aware models for real-time / low-latency inference.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Model
|
||||
- Decoder
|
||||
- Capabilities
|
||||
- Language
|
||||
- Size
|
||||
* - `nemotron-3.5-asr-streaming-0.6b <https://huggingface.co/nvidia/nemotron-3.5-asr-streaming-0.6b>`__
|
||||
- Hybrid
|
||||
- ASR, streaming
|
||||
- 40 languages
|
||||
- 0.6B
|
||||
* - `multitalker-parakeet-streaming-0.6b-v1 <https://huggingface.co/nvidia/multitalker-parakeet-streaming-0.6b-v1>`__
|
||||
- RNN-T
|
||||
- ASR, multitalker, streaming
|
||||
- English
|
||||
- 0.6B
|
||||
* - `parakeet_realtime_eou_120m-v1 <https://huggingface.co/nvidia/parakeet_realtime_eou_120m-v1>`__
|
||||
- RNN-T
|
||||
- ASR, end-of-utterance, streaming
|
||||
- English
|
||||
- 120M
|
||||
* - `stt_en_fastconformer_hybrid_large_streaming_multi <https://huggingface.co/nvidia/stt_en_fastconformer_hybrid_large_streaming_multi>`__
|
||||
- Hybrid
|
||||
- ASR, streaming, multiple look-aheads
|
||||
- English
|
||||
- Large
|
||||
* - `stt_en_fastconformer_hybrid_medium_streaming_80ms_pc <https://huggingface.co/nvidia/stt_en_fastconformer_hybrid_medium_streaming_80ms_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC, streaming
|
||||
- English
|
||||
- Medium
|
||||
* - `stt_en_fastconformer_hybrid_medium_streaming_80ms <https://huggingface.co/nvidia/stt_en_fastconformer_hybrid_medium_streaming_80ms>`__
|
||||
- Hybrid
|
||||
- ASR, streaming
|
||||
- English
|
||||
- Medium
|
||||
* - `stt_ka_fastconformer_hybrid_transducer_ctc_large_streaming_80ms_pc <https://huggingface.co/nvidia/stt_ka_fastconformer_hybrid_transducer_ctc_large_streaming_80ms_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC, streaming
|
||||
- Georgian
|
||||
- Large
|
||||
* - `stt_en_fastconformer_hybrid_large_streaming_1040ms <https://catalog.ngc.nvidia.com/orgs/nvidia/teams/nemo/models/stt_en_fastconformer_hybrid_large_streaming_1040ms>`__
|
||||
- Hybrid
|
||||
- ASR, streaming
|
||||
- English
|
||||
- Large
|
||||
|
||||
|
||||
FastConformer English Models (Non-Streaming)
|
||||
----------------------------------------------
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Model
|
||||
- Decoder
|
||||
- Capabilities
|
||||
- Language
|
||||
- Size
|
||||
* - `stt_en_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_en_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- English
|
||||
- Large
|
||||
* - `stt_en_fastconformer_ctc_large <https://huggingface.co/nvidia/stt_en_fastconformer_ctc_large>`__
|
||||
- CTC
|
||||
- ASR
|
||||
- English
|
||||
- Large
|
||||
* - `stt_en_fastconformer_ctc_xlarge <https://huggingface.co/nvidia/stt_en_fastconformer_ctc_xlarge>`__
|
||||
- CTC
|
||||
- ASR
|
||||
- English
|
||||
- XLarge
|
||||
* - `stt_en_fastconformer_ctc_xxlarge <https://huggingface.co/nvidia/stt_en_fastconformer_ctc_xxlarge>`__
|
||||
- CTC
|
||||
- ASR
|
||||
- English
|
||||
- XXLarge
|
||||
* - `stt_en_fastconformer_transducer_large <https://huggingface.co/nvidia/stt_en_fastconformer_transducer_large>`__
|
||||
- RNN-T
|
||||
- ASR
|
||||
- English
|
||||
- Large
|
||||
* - `stt_en_fastconformer_transducer_xlarge <https://huggingface.co/nvidia/stt_en_fastconformer_transducer_xlarge>`__
|
||||
- RNN-T
|
||||
- ASR
|
||||
- English
|
||||
- XLarge
|
||||
* - `stt_en_fastconformer_transducer_xxlarge <https://huggingface.co/nvidia/stt_en_fastconformer_transducer_xxlarge>`__
|
||||
- RNN-T
|
||||
- ASR
|
||||
- English
|
||||
- XXLarge
|
||||
* - `stt_en_fastconformer_tdt_large <https://huggingface.co/nvidia/stt_en_fastconformer_tdt_large>`__
|
||||
- TDT
|
||||
- ASR
|
||||
- English
|
||||
- Large
|
||||
|
||||
|
||||
FastConformer Multilingual Models
|
||||
----------------------------------
|
||||
|
||||
Single-language FastConformer Hybrid models. Models with ``_pc`` suffix support punctuation and capitalization.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Model
|
||||
- Decoder
|
||||
- Capabilities
|
||||
- Language
|
||||
- Size
|
||||
* - `stt_multilingual_fastconformer_hybrid_large_pc_blend_eu <https://catalog.ngc.nvidia.com/orgs/nvidia/teams/nemo/models/stt_multilingual_fastconformer_hybrid_large_pc_blend_eu>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Multilingual EU
|
||||
- Large
|
||||
* - `stt_de_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_de_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- German
|
||||
- Large
|
||||
* - `stt_es_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_es_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Spanish
|
||||
- Large
|
||||
* - `stt_es_fastconformer_hybrid_large_pc_nc <https://huggingface.co/nvidia/stt_es_fastconformer_hybrid_large_pc_nc>`__
|
||||
- Hybrid
|
||||
- ASR, Punctuation only
|
||||
- Spanish
|
||||
- Large
|
||||
* - `stt_fr_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_fr_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- French
|
||||
- Large
|
||||
* - `stt_it_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_it_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Italian
|
||||
- Large
|
||||
* - `stt_ru_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_ru_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Russian
|
||||
- Large
|
||||
* - `stt_ua_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_ua_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Ukrainian
|
||||
- Large
|
||||
* - `stt_pl_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_pl_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Polish
|
||||
- Large
|
||||
* - `stt_hr_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_hr_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Croatian
|
||||
- Large
|
||||
* - `stt_be_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_be_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Belarusian
|
||||
- Large
|
||||
* - `stt_nl_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_nl_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Dutch
|
||||
- Large
|
||||
* - `stt_pt_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_pt_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Portuguese
|
||||
- Large
|
||||
* - `stt_fa_fastconformer_hybrid_large <https://huggingface.co/nvidia/stt_fa_fastconformer_hybrid_large>`__
|
||||
- Hybrid
|
||||
- ASR
|
||||
- Farsi
|
||||
- Large
|
||||
* - `stt_ka_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_ka_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Georgian
|
||||
- Large
|
||||
* - `stt_hy_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_hy_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Armenian
|
||||
- Large
|
||||
* - `stt_ar_fastconformer_hybrid_large_pc_v1.0 <https://huggingface.co/nvidia/stt_ar_fastconformer_hybrid_large_pc_v1.0>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Arabic
|
||||
- Large
|
||||
* - `stt_ar_fastconformer_hybrid_large_pcd_v1.0 <https://huggingface.co/nvidia/stt_ar_fastconformer_hybrid_large_pcd_v1.0>`__
|
||||
- Hybrid
|
||||
- ASR, PnC (diacritized)
|
||||
- Arabic
|
||||
- Large
|
||||
* - `stt_uz_fastconformer_hybrid_large_pc <https://huggingface.co/nvidia/stt_uz_fastconformer_hybrid_large_pc>`__
|
||||
- Hybrid
|
||||
- ASR, PnC
|
||||
- Uzbek
|
||||
- Large
|
||||
* - `stt_kk_ru_fastconformer_hybrid_large <https://huggingface.co/nvidia/stt_kk_ru_fastconformer_hybrid_large>`__
|
||||
- Hybrid
|
||||
- ASR
|
||||
- Kazakh + Russian
|
||||
- Large
|
||||
|
||||
|
||||
Loading Models
|
||||
--------------
|
||||
|
||||
All models (except SALM — see :doc:`SpeechLM2 </speechlm2/intro>`) can be loaded via the ``from_pretrained()`` API:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import nemo.collections.asr as nemo_asr
|
||||
|
||||
model = nemo_asr.models.ASRModel.from_pretrained("nvidia/parakeet-tdt-0.6b-v2")
|
||||
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
.. _ngram_modeling:
|
||||
|
||||
****************************
|
||||
N-gram Language Model Fusion
|
||||
****************************
|
||||
|
||||
In this approach, an N-gram LM is trained on text data, then it is used in fusion with beam search decoding to find the
|
||||
best candidates. The beam search decoders in NeMo support language models trained with KenLM library (
|
||||
`https://github.com/kpu/kenlm <https://github.com/kpu/kenlm>`__).
|
||||
The beam search decoders and KenLM library are not installed by default in NeMo.
|
||||
You need to install them to be able to use beam search decoding and N-gram LM.
|
||||
Please refer to `scripts/asr_language_modeling/ngram_lm/install_beamsearch_decoders.sh <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/install_beamsearch_decoders.sh>`__
|
||||
on how to install them. Alternatively, you can build Docker image
|
||||
`scripts/installers/Dockerfile.ngramtools <https://github.com/NVIDIA/NeMo/blob/stable/scripts/installers/Dockerfile.ngramtools>`__ with all the necessary dependencies.
|
||||
|
||||
Please, refer to :ref:`train-ngram-lm` for more details on how to train an N-gram LM using KenLM library.
|
||||
|
||||
NeMo supports both character-based and BPE-based models for N-gram LMs. An N-gram LM can be used with beam search
|
||||
decoders on top of the ASR models to produce more accurate candidates. The beam search decoder would incorporate
|
||||
the scores produced by the N-gram LM into its score calculations as the following:
|
||||
|
||||
.. code-block::
|
||||
|
||||
final_score = acoustic_score + beam_alpha*lm_score + beam_beta*seq_length
|
||||
|
||||
where acoustic_score is the score predicted by the acoustic encoder and lm_score is the one estimated by the LM.
|
||||
The parameter 'beam_alpha' determines the weight given to the N-gram language model, while 'beam_beta' is a penalty term that accounts for sequence length in the scores. A larger 'beam_alpha' places more emphasis on the language model and less on the acoustic model. Negative values for 'beam_beta' penalize longer sequences, encouraging the decoder to prefer shorter predictions. Conversely, positive values for 'beam_beta' favor longer candidates.
|
||||
|
||||
Evaluate by Beam Search Decoding and N-gram LM
|
||||
==============================================
|
||||
|
||||
NeMo's beam search decoders are capable of using the KenLM's N-gram models to find the best candidates.
|
||||
The script to evaluate an ASR model with beam search decoding and N-gram models can be found at
|
||||
`scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_ctc.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_ctc.py>`__.
|
||||
|
||||
This script has a large number of possible argument overrides; therefore, it is recommended that you use ``python eval_beamsearch_ngram_ctc.py --help`` to see the full list of arguments.
|
||||
|
||||
You can evaluate an ASR model using the following:
|
||||
|
||||
.. code-block::
|
||||
|
||||
python eval_beamsearch_ngram_ctc.py nemo_model_file=<path to the .nemo file of the model> \
|
||||
input_manifest=<path to the evaluation JSON manifest file \
|
||||
kenlm_model_file=<path to the binary KenLM model> \
|
||||
beam_width=[<list of the beam widths, separated with commas>] \
|
||||
beam_alpha=[<list of the beam alphas, separated with commas>] \
|
||||
beam_beta=[<list of the beam betas, separated with commas>] \
|
||||
preds_output_folder=<optional folder to store the predictions> \
|
||||
probs_cache_file=null \
|
||||
decoding_mode=beamsearch_ngram \
|
||||
decoding_strategy="<Beam library such as beam, pyctcdecode or flashlight>"
|
||||
|
||||
It can evaluate a model in the following three modes by setting the argument ``--decoding_mode``:
|
||||
|
||||
* greedy: Just greedy decoding is done and no beam search decoding is performed.
|
||||
* beamsearch: The beam search decoding is done, but without using the N-gram language model. Final results are equivalent to setting the weight of LM (beam_beta) to zero.
|
||||
* beamsearch_ngram: The beam search decoding is done with N-gram LM.
|
||||
|
||||
In ``beamsearch`` mode, the evaluation is performed using beam search decoding without any language model. The performance is reported in terms of Word Error Rate (WER) and Character Error Rate (CER). Moreover, when the best candidate is selected among the candidates, it is also reported as the best WER/CER. This can serve as an indicator of the quality of the predicted candidates.
|
||||
|
||||
|
||||
The script initially loads the ASR model and predicts the outputs of the model's encoder as log probabilities. This part is computed in batches on a device specified by --device, which can be either a CPU (`--device=cpu`) or a single GPU (`--device=cuda:0`).
|
||||
The batch size for this part is specified by ``--acoustic_batch_size``. Using the largest feasible batch size can speed up the calculation of log probabilities. Additionally, you can use `--use_amp` to accelerate the calculation and allow for larger --acoustic_batch_size values.
|
||||
Currently, multi-GPU support is not available for calculating log probabilities. However, using ``--probs_cache_file`` can help. This option stores the log probabilities produced by the model's encoder in a pickle file, allowing you to skip the first step in future runs.
|
||||
|
||||
The following is the list of the important arguments for the evaluation script:
|
||||
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| **Argument** | **Type** | **Default** | **Description** |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| nemo_model_file | str | Required | The path of the `.nemo` file of the ASR model to extract the tokenizer. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| input_manifest | str | Required | Path to the training file, it can be a text file or JSON manifest. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| kenlm_model_file | str | Required | The path to store the KenLM binary model file. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| preds_output_folder | str | None | The path to an optional folder to store the predictions. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| probs_cache_file | str | None | The cache file for storing the outputs of the model. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| acoustic_batch_size | int | 16 | The batch size to calculate log probabilities. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| use_amp | bool | False | Whether to use AMP if available to calculate log probabilities. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| device | str | cuda | The device to load the model onto to calculate log probabilities. |
|
||||
| | | | It can `cpu`, `cuda`, `cuda:0`, `cuda:1`, ... |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| decoding_mode | str | beamsearch_ngram | The decoding scheme to be used for evaluation. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| beam_width | float | Required | List of the width or list of the widths of the beam search decoding. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| beam_alpha | float | Required | List of the alpha parameter for the beam search decoding. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| beam_beta | float | Required | List of the beta parameter for the beam search decoding. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| beam_batch_size | int | 128 | The batch size to be used for beam search decoding. |
|
||||
| | | | Larger batch size can be a little faster, but uses larger memory. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| decoding_strategy | str | beam | String argument for type of decoding strategy for the model. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| decoding | Dict | BeamCTC | Subdict of beam search configs. Values found via |
|
||||
| | Config | InferConfig | python eval_beamsearch_ngram_ctc.py --help |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| text_processing.do_lowercase | bool | ``False`` | Whether to make the training text all lower case. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| text_processing.punctuation_marks | str | ``""`` | String with punctuation marks to process. Example: ".\,?" |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| text_processing.rm_punctuation | bool | ``False`` | Whether to remove punctuation marks from text. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
| text_processing.separate_punctuation | bool | ``True`` | Whether to separate punctuation with the previous word by space. |
|
||||
+--------------------------------------+----------+------------------+-------------------------------------------------------------------------+
|
||||
|
||||
The width of the beam search (``--beam_width``) specifies the number of top candidates or predictions the beam search decoder will consider. Larger beam widths result in more accurate but slower predictions.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``eval_beamsearch_ngram_ctc.py`` script contains the entire subconfig used for CTC Beam Decoding.
|
||||
Therefore it is possible to forward arguments for various beam search libraries such as ``flashlight``
|
||||
and ``pyctcdecode`` via the ``decoding`` subconfig.
|
||||
|
||||
To learn more about evaluating the ASR models with N-gram LM, refer to the tutorial here: Offline ASR Inference with Beam Search and External Language Model Rescoring
|
||||
`Offline ASR Inference with Beam Search and External Language Model Rescoring <https://colab.research.google.com/github/NVIDIA/NeMo/blob/main/tutorials/asr/Offline_ASR.ipynb>`_
|
||||
|
||||
Beam Search Engines
|
||||
-------------------
|
||||
|
||||
NeMo ASR CTC supports multiple beam search engines for decoding. The default engine is beam, which is the OpenSeq2Seq decoding library.
|
||||
|
||||
OpenSeq2Seq (``beam``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
CPU-based beam search engine that is quite efficient and supports char and subword models. It requires a character/subword
|
||||
KenLM model to be provided.
|
||||
|
||||
The config for this decoding library is described above.
|
||||
|
||||
Flashlight (``flashlight``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Flashlight is a C++ library for ASR decoding provided at `https://github.com/flashlight/flashlight <https://github.com/flashlight/flashlight>`_. It is a CPU- and CUDA-based beam search engine that is quite efficient and supports char and subword models. It requires an ARPA KenLM file.
|
||||
|
||||
It supports several advanced features, such as lexicon-based decoding, lexicon-free decoding, beam pruning threshold, and more.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@dataclass
|
||||
class FlashlightConfig:
|
||||
lexicon_path: Optional[str] = None
|
||||
boost_path: Optional[str] = None
|
||||
beam_size_token: int = 16
|
||||
beam_threshold: float = 20.0
|
||||
unk_weight: float = -math.inf
|
||||
sil_weight: float = 0.0
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Lexicon-based decoding
|
||||
python eval_beamsearch_ngram_ctc.py ... \
|
||||
decoding_strategy="flashlight" \
|
||||
decoding.beam.flashlight_cfg.lexicon_path='/path/to/lexicon.lexicon' \
|
||||
decoding.beam.flashlight_cfg.beam_size_token = 32 \
|
||||
decoding.beam.flashlight_cfg.beam_threshold = 25.0
|
||||
|
||||
# Lexicon-free decoding
|
||||
python eval_beamsearch_ngram_ctc.py ... \
|
||||
decoding_strategy="flashlight" \
|
||||
decoding.beam.flashlight_cfg.beam_size_token = 32 \
|
||||
decoding.beam.flashlight_cfg.beam_threshold = 25.0
|
||||
|
||||
PyCTCDecode (``pyctcdecode``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
PyCTCDecode is a Python library for ASR decoding provided at `https://github.com/kensho-technologies/pyctcdecode <https://github.com/kensho-technologies/pyctcdecode>`_. It is a CPU-based beam search engine that is somewhat efficient for a pure Python library, and supports char and subword models. It requires a character/subword KenLM ARPA / BINARY model to be provided.
|
||||
|
||||
|
||||
It has advanced features, such as word boosting, which can be useful for transcript customization.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@dataclass
|
||||
class PyCTCDecodeConfig:
|
||||
beam_prune_logp: float = -10.0
|
||||
token_min_logp: float = -5.0
|
||||
prune_history: bool = False
|
||||
hotwords: Optional[List[str]] = None
|
||||
hotword_weight: float = 10.0
|
||||
|
||||
.. code-block::
|
||||
|
||||
# PyCTCDecoding
|
||||
python eval_beamsearch_ngram_ctc.py ... \
|
||||
decoding_strategy="pyctcdecode" \
|
||||
decoding.beam.pyctcdecode_cfg.beam_prune_logp = -10. \
|
||||
decoding.beam.pyctcdecode_cfg.token_min_logp = -5. \
|
||||
decoding.beam.pyctcdecode_cfg.hotwords=[<List of str words>] \
|
||||
decoding.beam.pyctcdecode_cfg.hotword_weight=10.0
|
||||
|
||||
|
||||
Hyperparameter Grid Search
|
||||
--------------------------
|
||||
|
||||
Beam search decoding with N-gram LM has three main hyperparameters: `beam_width`, `beam_alpha`, and `beam_beta`.
|
||||
The accuracy of the model is dependent on the values of these parameters, specifically, beam_alpha and beam_beta. To perform grid search, you can specify a single value or a list of values for each of these parameters. In this case, it would perform the beam search decoding on all combinations of the three hyperparameters.
|
||||
For example, the following set of parameters would result in 212=4 beam search decodings:
|
||||
|
||||
.. code-block::
|
||||
|
||||
python eval_beamsearch_ngram_ctc.py ... \
|
||||
beam_width=[64,128] \
|
||||
beam_alpha=[1.0] \
|
||||
beam_beta=[1.0,0.5]
|
||||
|
||||
|
||||
Beam Search ngram Decoding for Transducer Models (RNNT and HAT)
|
||||
===============================================================
|
||||
|
||||
You can also find a similar script to evaluate an RNNT/HAT model with beam search decoding and N-gram models at:
|
||||
`scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_transducer.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_transducer.py>`_
|
||||
|
||||
.. code-block::
|
||||
|
||||
python eval_beamsearch_ngram_transducer.py nemo_model_file=<path to the .nemo file of the model> \
|
||||
input_manifest=<path to the evaluation JSON manifest file \
|
||||
kenlm_model_file=<path to the binary KenLM model> \
|
||||
beam_width=[<list of the beam widths, separated with commas>] \
|
||||
beam_alpha=[<list of the beam alphas, separated with commas>] \
|
||||
preds_output_folder=<optional folder to store the predictions> \
|
||||
probs_cache_file=null \
|
||||
decoding_strategy=<greedy_batch or maes decoding>
|
||||
maes_prefix_alpha=[<list of the maes prefix alphas, separated with commas>] \
|
||||
maes_expansion_gamma=[<list of the maes expansion gammas, separated with commas>] \
|
||||
hat_subtract_ilm=<in case of HAT model: subtract internal LM or not (True/False)> \
|
||||
hat_ilm_weight=[<in case of HAT model: list of the HAT internal LM weights, separated with commas>] \
|
||||
|
||||
|
||||
.. _wfst-ctc-decoding:
|
||||
|
||||
WFST CTC decoding
|
||||
=================
|
||||
Weighted Finite-State Transducers (WFST) are finite-state machines with input and output symbols on each transition and some weight element of a semiring. WFSTs can act as N-gram LMs in a special type of LM-forced beam search, called WFST decoding.
|
||||
|
||||
.. note::
|
||||
|
||||
More precisely, WFST decoding is more of a greedy N-depth search with LM.
|
||||
Thus, it is asymptotically worse than conventional beam search decoding algorithms, but faster.
|
||||
|
||||
**WARNING**
|
||||
At the moment, NeMo supports WFST decoding only for CTC models and word-based LMs.
|
||||
|
||||
To run WFST decoding in NeMo, one needs to provide a NeMo ASR model and either an ARPA LM or a WFST LM (advanced). An ARPA LM can be built from source text with KenLM as follows: ``<kenlm_bin_path>/lmplz -o <ngram_length> --arpa <out_arpa_path> --prune <ngram_prune>``.
|
||||
|
||||
The script to evaluate an ASR model with WFST decoding and N-gram models can be found at
|
||||
`scripts/asr_language_modeling/ngram_lm/eval_wfst_decoding_ctc.py
|
||||
<https://github.com/NVIDIA/NeMo/blob/main/scripts/asr_language_modeling/ngram_lm/eval_wfst_decoding_ctc.py>`__.
|
||||
|
||||
This script has a large number of possible argument overrides, therefore it is advised to use ``python eval_wfst_decoding_ctc.py --help`` to see the full list of arguments.
|
||||
|
||||
You may evaluate an ASR model as the following:
|
||||
|
||||
.. code-block::
|
||||
|
||||
python eval_wfst_decoding_ctc.py nemo_model_file=<path to the .nemo file of the model> \
|
||||
input_manifest=<path to the evaluation JSON manifest file> \
|
||||
arpa_model_file=<path to the ARPA LM model> \
|
||||
decoding_wfst_file=<path to the decoding WFST file> \
|
||||
beam_width=[<list of the beam widths, separated with commas>] \
|
||||
lm_weight=[<list of the LM weight multipliers, separated with commas>] \
|
||||
open_vocabulary_decoding=<whether to use open vocabulary mode for WFST decoding> \
|
||||
decoding_mode=<decoding mode, affects output. Usually "nbest"> \
|
||||
decoding_search_type=<WFST decoding library. Usually "riva"> \
|
||||
preds_output_folder=<optional folder to store the predictions> \
|
||||
probs_cache_file=null
|
||||
|
||||
.. note::
|
||||
|
||||
Since WFST decoding is LM-forced (the search goes over the WIDEST graph), only word sequences accepted by the WFST can appear in the decoding results.
|
||||
To circumvent this restriction, one can pass ``open_vocabulary_decoding=true`` (experimental feature).
|
||||
|
||||
|
||||
Quick start example
|
||||
-------------------
|
||||
|
||||
.. code-block::
|
||||
|
||||
wget -O - https://www.openslr.org/resources/11/3-gram.pruned.1e-7.arpa.gz | \
|
||||
gunzip -c | tr '[:upper:]' '[:lower:]' > 3-gram.pruned.1e-7.arpa && \
|
||||
python eval_wfst_decoding_ctc.py nemo_model_file="stt_en_conformer_ctc_small_ls" \
|
||||
input_manifest="<data_dir>/Librispeech/test_other.json" \
|
||||
arpa_model_file="3-gram.pruned.1e-7.arpa" \
|
||||
decoding_wfst_file="3-gram.pruned.1e-7.fst" \
|
||||
beam_width=[8] \
|
||||
lm_weight=[0.5,0.6,0.7,0.8,0.9]
|
||||
|
||||
.. note::
|
||||
|
||||
Building a decoding WFST is a long process, so it is better to provide a ``decoding_wfst_file`` path even if you don't have it.
|
||||
This way, the decoding WFST will be buffered to the specified file path and there will be no need to re-build it on the next run.
|
||||
@@ -0,0 +1,105 @@
|
||||
.. _neural_rescoring:
|
||||
|
||||
****************
|
||||
Neural Rescoring
|
||||
****************
|
||||
|
||||
When using the neural rescoring approach, a neural network is used to score candidates. A candidate is the text transcript predicted by the ASR model's decoder. The top K candidates produced by beam search decoding (with a beam width of K) are given to a neural language model for ranking. The language model assigns a score to each candidate, which is usually combined with the scores from beam search decoding to produce the final scores and rankings.
|
||||
|
||||
Train Neural Rescorer
|
||||
=====================
|
||||
|
||||
An example script to train such a language model with Transformer can be found at `examples/nlp/language_modeling/transformer_lm.py <https://github.com/NVIDIA/NeMo/blob/stable/examples/nlp/language_modeling/transformer_lm.py>`__.
|
||||
It trains a ``TransformerLMModel`` which can be used as a neural rescorer for an ASR system. For more information on language models training, see LLM/NLP documentation.
|
||||
|
||||
|
||||
You can also use a pretrained language model from the Hugging Face library, such as Transformer-XL and GPT, instead of training your model.
|
||||
Models like BERT and RoBERTa are not supported by this script because they are trained as Masked Language Models. As a result, they are not efficient or effective for scoring sentences out of the box.
|
||||
|
||||
|
||||
Evaluation
|
||||
==========
|
||||
|
||||
Given a trained TransformerLMModel `.nemo` file or a pretrained HF model, the script available at
|
||||
`scripts/asr_language_modeling/neural_rescorer/eval_neural_rescorer.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/neural_rescorer/eval_neural_rescorer.py>`__
|
||||
can be used to re-score beams obtained with ASR model. You need the `.tsv` file containing the candidates produced
|
||||
by the acoustic model and the beam search decoding to use this script. The candidates can be the result of just the beam
|
||||
search decoding or the result of fusion with an N-gram LM. You can generate this file by specifying `--preds_output_folder` for
|
||||
`scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_ctc.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_ctc.py>`__.
|
||||
|
||||
The neural rescorer would rescore the beams/candidates by using two parameters of `rescorer_alpha` and `rescorer_beta`, as follows:
|
||||
|
||||
.. code-block::
|
||||
|
||||
final_score = beam_search_score + rescorer_alpha*neural_rescorer_score + rescorer_beta*seq_length
|
||||
|
||||
The parameter `rescorer_alpha` specifies the importance placed on the neural rescorer model, while `rescorer_beta` is a penalty term that accounts for sequence length in the scores. These parameters have similar effects to `beam_alpha` and `beam_beta` in the beam search decoder and N-gram language model.
|
||||
|
||||
Use the following steps to evaluate a neural LM:
|
||||
|
||||
#. Obtain `.tsv` file with beams and their corresponding scores. Scores can be from a regular beam search decoder or
|
||||
in fusion with an N-gram LM scores. For a given beam size `beam_size` and a number of examples
|
||||
for evaluation `num_eval_examples`, it should contain (`num_eval_examples` x `beam_size`) lines of
|
||||
form `beam_candidate_text \t score`. This file can be generated by `scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_ctc.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/eval_beamsearch_ngram_ctc.py>`__
|
||||
|
||||
#. Rescore the candidates by `scripts/asr_language_modeling/neural_rescorer/eval_neural_rescorer.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/neural_rescorer/eval_neural_rescorer.py>`__.
|
||||
|
||||
.. code-block::
|
||||
|
||||
python eval_neural_rescorer.py
|
||||
--lm_model=[path to .nemo file of the LM or the name of a HF pretrained model]
|
||||
--beams_file=[path to beams .tsv file]
|
||||
--beam_size=[size of the beams]
|
||||
--eval_manifest=[path to eval manifest .json file]
|
||||
--batch_size=[batch size used for inference on the LM model]
|
||||
--alpha=[the value for the parameter rescorer_alpha]
|
||||
--beta=[the value for the parameter rescorer_beta]
|
||||
--scores_output_file=[the optional path to store the rescored candidates]
|
||||
|
||||
The candidates, along with their new scores, are stored at the file specified by `--scores_output_file`.
|
||||
|
||||
The following is the list of the arguments for the evaluation script:
|
||||
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| **Argument** |**Type**| **Default** | **Description** |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| lm_model | str | Required | The path of the '.nemo' file of an ASR model, or the name of a |
|
||||
| | | | Hugging Face pretrained model like 'transfo-xl-wt103' or 'gpt2'. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| eval_manifest | str | Required | Path to the evaluation manifest file (.json manifest file). |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| beams_file | str | Required | Path to beams file (.tsv) containing the candidates and their scores. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| beam_size | int | Required | The width of the beams (number of candidates) generated by the decoder. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| alpha | float | None | The value for parameter rescorer_alpha |
|
||||
| | | | Not passing value would enable linear search for rescorer_alpha. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| beta | float | None | The value for parameter rescorer_beta |
|
||||
| | | | Not passing value would enable linear search for rescorer_beta. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| batch_size | int | 16 | The batch size used to calculate the scores. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| max_seq_length | int | 512 | Maximum sequence length (in tokens) for the input. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| scores_output_file | str | None | The optional file to store the rescored beams. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| use_amp | bool | ``False`` | Whether to use AMP if available calculate the scores. |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
| device | str | cuda | The device to load LM model onto to calculate the scores |
|
||||
| | | | It can be 'cpu', 'cuda', 'cuda:0', 'cuda:1', ... |
|
||||
+---------------------+--------+------------------+-------------------------------------------------------------------------+
|
||||
|
||||
|
||||
Hyperparameter Linear Search
|
||||
----------------------------
|
||||
|
||||
The hyperparameter linear search script also supports linear search for parameters `alpha` and `beta`. If any of the two is not
|
||||
provided, a linear search is performed to find the best value for that parameter. When linear search is used, initially
|
||||
`beta` is set to zero and the best value for `alpha` is found, then `alpha` is fixed with
|
||||
that value and another linear search is done to find the best value for `beta`.
|
||||
If any of the of these two parameters is already specified, then search for that one is skipped. After each search for a
|
||||
parameter, the plot of WER% for different values of the parameter is also shown.
|
||||
|
||||
It is recommended to first use the linear search for both parameters on a validation set by not providing any values for `--alpha` and `--beta`.
|
||||
Then check the WER curves and decide on the best values for each parameter. Finally, evaluate the best values on the test set.
|
||||
@@ -0,0 +1,346 @@
|
||||
.. _ngpulm_ngram_modeling:
|
||||
|
||||
***************************************************************
|
||||
NGPU-LM (GPU-based N-gram Language Model) Language Model Fusion
|
||||
***************************************************************
|
||||
|
||||
ASR systems can achieve significantly improved accuracy by leveraging **external language model (LM) shallow fusion** during the decoding process.
|
||||
This technique integrates knowledge from an external LM without requiring the ASR model itself to be retrained.
|
||||
|
||||
**How Shallow Fusion Works:**
|
||||
|
||||
During shallow fusion, the output probabilities generated by the ASR model are combined with those from a separate, external language model.
|
||||
The final transcription is then determined by selecting the word sequence that yields the highest combined score.
|
||||
These external LMs are typically trained on vast text datasets, allowing them to capture the statistical patterns, syntactic structures, and contextual dependencies of language.
|
||||
This enables them to predict more plausible word sequences, thereby correcting potential errors from the ASR model.
|
||||
|
||||
**Domain Adaptation Benefits:**
|
||||
|
||||
Shallow fusion is particularly valuable for **adapting ASR systems to new or specialized domains**.
|
||||
By training the external LM on domain-specific text-such as medical, legal, or technical documents-it learns the vocabulary of that field.
|
||||
This specialized knowledge guides the ASR decoding process towards more accurate and contextually relevant transcriptions.
|
||||
|
||||
Traditionally, shallow fusion has been performed during **beam search decoding**, a method that explores multiple promising hypotheses to find the most likely transcription.
|
||||
|
||||
|
||||
NGPU-LM
|
||||
=======
|
||||
|
||||
A widely used library for training traditional n-gram language models is KenLM.
|
||||
While KenLM (https://github.com/kpu/kenlm) is known for its efficient CPU-based implementation, its reliance on the CPU can limit performance in high-throughput scenarios, especially when dealing with large-scale data.
|
||||
|
||||
NGPU-LM on contrast is a GPU-accelerated implementation of a statistical n-gram language model.
|
||||
It uses a **universal trie-based data structure**, which enables fast, batched queries. For full details, please refer to the paper [ngpulm]_.
|
||||
|
||||
This enables shallow fusion during **greedy decoding**, creating a middle ground between standard greedy decoding and full beam search with a language model.
|
||||
It preserves the speed and simplicity of greedy decoding while regaining much of the accuracy typically achieved with beam search with external LM fusion.
|
||||
While not as accurate as full beam search, greedy decoding with NGPU-LM fusion offers a compelling balance between speed and accuracy.
|
||||
|
||||
NeMo provides efficient, fully GPU-based beam search implementations for all major ASR model types,
|
||||
allowing **beam decoding to operate with real-time factors (RTFx) close to those of greedy decoding**.
|
||||
At a batch size of 32, the RTFx difference between beam and greedy decoding is only about 20%.
|
||||
These implementations incorporate NGPU-LM, enabling fast, fully GPU-based decoding and customization.
|
||||
This enables users to customize decoding while maintaining reasonable speed, even in beam search mode.
|
||||
For full details, please refer to the [beamsearch]_.
|
||||
|
||||
NGPU-LM fusion is supported for BPE-based ASR models (CTC, RNNT, TDT, AED) during both greedy and beam decoding.
|
||||
|
||||
Train NGPU-LM
|
||||
=============
|
||||
|
||||
NGPU-LM is built using `.ARPA` files generated by the KenLM library. You can train an n-gram LM using the following script:
|
||||
`train_kenlm.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/train_kenlm.py>`__.
|
||||
|
||||
The generated `.ARPA` files can be directly used for GPU-based decoding.
|
||||
However, for faster performance, it is recommended to convert the model to the `.nemo` format by setting the ``save_nemo`` flag to ``true``.
|
||||
|
||||
.. code-block::
|
||||
|
||||
python train_kenlm.py nemo_model_file=<path to the .nemo file of the model> \
|
||||
train_paths=<list of paths to the training text or JSON manifest files> \
|
||||
kenlm_bin_path=<path to the bin folder of KenLM library> \
|
||||
kenlm_model_file=<path to store the binary KenLM model> \
|
||||
ngram_length=<order of N-gram model> \
|
||||
preserve_arpa=true \
|
||||
save_nemo=True
|
||||
|
||||
For a complete list of arguments and usage details, refer to the :ref:`train-ngram-lm`.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
It is recommended that you use 6 as the order of the N-gram model for BPE-based models. Higher orders may require re-compiling KenLM to support them.
|
||||
|
||||
.. _ctc-decoding-with-ngpulm:
|
||||
|
||||
Decoding with NGPU-LM
|
||||
=====================
|
||||
|
||||
To run inference with NGPU-LM fusion, the ``ngram_lm_model`` and ``ngram_lm_alpha`` fields must be specified in the decoding configuration.
|
||||
|
||||
.. note::
|
||||
|
||||
For CTC, RNNT, and TDT models, these fields should be set within the respective ``greedy`` or ``beam`` sub-configurations.
|
||||
For AED models running in greedy mode, set the beam size to 1 and specify these fields under the ``beam`` sub-configuration.
|
||||
|
||||
Examples for different model types are provided below.
|
||||
|
||||
|
||||
|
||||
CTC Decoding with NGPU-LM
|
||||
-------------------------
|
||||
|
||||
**Greedy Search:**
|
||||
|
||||
You can run NGPU-LM shallow fusion during greedy CTC decoding using the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
pretrained_name=nvidia/parakeet-ctc-1.1b \
|
||||
amp=false \
|
||||
amp_dtype=bfloat16 \
|
||||
matmul_precision=high \
|
||||
compute_dtype=bfloat16 \
|
||||
presort_manifest=true \
|
||||
cuda=0 \
|
||||
batch_size=32 \
|
||||
dataset_manifest=<path to the evaluation JSON manifest file> \
|
||||
ctc_decoding.greedy.ngram_lm_model=<path to the .nemo/.ARPA file of the NGPU-LM model> \
|
||||
ctc_decoding.greedy.ngram_lm_alpha=0.2 \
|
||||
ctc_decoding.greedy.allow_cuda_graphs=True \
|
||||
ctc_decoding.strategy="greedy_batch"
|
||||
|
||||
**Beam Search:**
|
||||
|
||||
During CTC beam search, each hypothesis is scored using the following formula:
|
||||
|
||||
.. code-block::
|
||||
|
||||
final_score = acoustic_score + ngram_lm_alpha * lm_score + beam_beta * seq_length
|
||||
|
||||
where:
|
||||
- ``acoustic_score`` is the score predicted by the ASR.
|
||||
- ``lm_score`` is the score predicted by the NGPU-LM LM.
|
||||
- ``ngram_lm_alpha`` is the weight given to the language model.
|
||||
- ``beam_beta`` is a penalty term that accounts for sequence length in the scores.
|
||||
|
||||
For running fully batched GPU-based CTC decoding with NGPU-LM, you can use the following command:
|
||||
|
||||
The following is the list of the adjustable arguments of batched CTC decoding algorithm ``beam_batch``:
|
||||
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| **Argument** | **Type** | **Default** | **Description** |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ngram_lm_alpha | float | Required | Weight factor applied to the language model scores. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| beam_size | int | 4 | Beam size. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| beam_beta | float | 1 | Penalty applied to word insertions to control the trade-off between insertion and deletion errors during beam search decoding. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| beam_threshold | float | 20 | Threshold used to prune candidate hypotheses by comparing their scores to the best hypothesis. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
pretrained_name=nvidia/parakeet-ctc-1.1b \
|
||||
amp=false \
|
||||
amp_dtype=bfloat16 \
|
||||
matmul_precision=high \
|
||||
compute_dtype=bfloat16 \
|
||||
presort_manifest=true \
|
||||
cuda=0 \
|
||||
batch_size=32 \
|
||||
dataset_manifest=<path to the evaluation JSON manifest file> \
|
||||
ctc_decoding.beam.ngram_lm_model=<path to the .nemo/.ARPA file of the NGPU-LM model> \
|
||||
ctc_decoding.beam.ngram_lm_alpha=0.2 \
|
||||
ctc_decoding.beam.beam_size=12 \
|
||||
ctc_decoding.beam.beam_beta=1.0 \
|
||||
ctc_decoding.strategy="beam_batch" \
|
||||
ctc_decoding.beam.allow_cuda_graphs=True
|
||||
|
||||
|
||||
RNN-T/TDT decoding with NGPU-LM
|
||||
-------------------------------
|
||||
|
||||
**Greedy Search:**
|
||||
|
||||
You can run NGPU-LM shallow fusion during greedy RNN-T / TDT decoding using the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
pretrained_name=nvidia/parakeet-rnnt-1.1b \
|
||||
amp=false \
|
||||
amp_dtype=bfloat16 \
|
||||
matmul_precision=high \
|
||||
compute_dtype=bfloat16 \
|
||||
presort_manifest=true \
|
||||
cuda=0 \
|
||||
batch_size=32 \
|
||||
dataset_manifest=<path to the evaluation JSON manifest file> \
|
||||
rnnt_decoding.greedy.ngram_lm_model=<path to the .nemo/.ARPA file of the NGPU-LM model> \
|
||||
rnnt_decoding.greedy.ngram_lm_alpha=0.2 \
|
||||
rnnt_decoding.greedy.allow_cuda_graphs=True \
|
||||
rnnt_decoding.strategy="greedy_batch"
|
||||
|
||||
.. note::
|
||||
|
||||
To run the inference with TDT model, you need to provide pretrained TDT model in ``pretrained_name`` field (for example ``nvidia/parakeet-tdt_ctc-1.1b`` ).
|
||||
|
||||
|
||||
**Beam Search:**
|
||||
|
||||
During RNN-T / TDT beam search, each hypothesis is scored using the following formula:
|
||||
|
||||
.. code-block::
|
||||
|
||||
final_score = acoustic_score + ngram_lm_alpha * lm_score
|
||||
|
||||
where:
|
||||
- ``acoustic_score`` is the score predicted by the ASR.
|
||||
- ``lm_score`` is the score predicted by the NGPU-LM LM.
|
||||
- ``ngram_lm_alpha`` is the weight given to the language model.
|
||||
|
||||
Final hypotheses is chosen based on the normalized score ``final_score / seq_length``.
|
||||
|
||||
|
||||
*Blank Scoring in Transducer Models*
|
||||
|
||||
Transducer models include a blank symbol (``∅``) for frame transitions, while LMs do not model blanks.
|
||||
During shallow fusion, the LM is typically applied only to non-blank tokens:
|
||||
|
||||
.. math::
|
||||
|
||||
\ln p_{\text{tot}}[k] =
|
||||
\begin{cases}
|
||||
\ln p[k] + \lambda \ln p_{\text{LM}}[k], & k \in V \\
|
||||
\ln p[\emptyset], & k = \emptyset
|
||||
\end{cases}
|
||||
|
||||
This can lead to excessive blank predictions at higher LM weights, increasing deletion errors.
|
||||
NeMo supports a blank-aware scoring method that adjusts LM contributions to better balance predictions:
|
||||
|
||||
.. math::
|
||||
|
||||
\ln p_{\text{tot}}[k] =
|
||||
\begin{cases}
|
||||
\ln p[k] + \lambda \ln((1 - p[\emptyset]) \cdot p_{\text{LM}}[k]), & k \in V \\
|
||||
(1 + \lambda) \ln p[\emptyset], & k = \emptyset
|
||||
\end{cases}
|
||||
|
||||
*Early vs. Late Pruning*
|
||||
|
||||
In shallow fusion, LM and ASR scores can be combined at different stages:
|
||||
|
||||
- **Early pruning:** ASR selects top hypotheses, then LM rescoring is applied. Efficient for small beams.
|
||||
- **Late pruning:** ASR and LM scores are combined before pruning. More accurate but requires full-vocab LM queries.
|
||||
|
||||
For Transducer models, late pruning with the blank-aware scoring method generally yields better performance than the standard approach.
|
||||
|
||||
*Beam Search Strategies:*
|
||||
|
||||
In NeMo fully batched implementation of following strategies are supported:
|
||||
|
||||
- **malsd_batch:** fully batched implemention of modified Alignment-Length Synchronous Decoding [alsd]_, supporting both RNNT and TDT models.
|
||||
- **maes_batch:** fully batched implemention of modified Adaptive Expansion Search [aes]_, supporting for only RNNT models. CudaGraphs are not supported.
|
||||
|
||||
The following is the list of the adjustable arguments of batched CTC decoding algorithm ``beam_batch``:
|
||||
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| **Argument** | **Type** | **Strategy** | **Default** | **Description** |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ngram_lm_alpha | float | malsd_batch, maes_batch | Required | Weight factor applied to the language model scores. |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| beam_size | int | malsd_batch, maes_batch | 4 | Beam size. |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| pruning_mode | str | malsd_batch, maes_batch | late | Mode for hypotheses pruning. Can be ``early`` or ``late``. |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| blank_lm_score_mode | str | malsd_batch, maes_batch | lm_weighted_full | Mode for blank symbol scoring. Can be ``no_score`` or ``lm_weighted_full`` |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| max_symbols_per_step | int | malsd_batch | 10 | Max symbols to emit on each step to avoid infinite looping. |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| maes_num_step | int | maes_batch | 2 | Number of adaptive steps to take. |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| maes_expansion_beta | float | maes_batch | 1.0 | Maximum number of prefix expansions allowed, in addition to the beam size. |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| maes_expansion_gamma | float | maes_batch | 2.3 | Threshold used to prune candidate hypotheses by comparing their scores to the best hypothesis. |
|
||||
+-----------------------+-----------+-------------------------+------------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
You can run NGPU-LM shallow fusion during beam RNN-T / TDT decoding using the following command:
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
pretrained_name=nvidia/parakeet-rnnt-1.1b \
|
||||
amp=false \
|
||||
amp_dtype=bfloat16 \
|
||||
matmul_precision=high \
|
||||
compute_dtype=bfloat16 \
|
||||
presort_manifest=true \
|
||||
cuda=0 \
|
||||
batch_size=32 \
|
||||
dataset_manifest=<path to the evaluation JSON manifest file> \
|
||||
rnnt_decoding.beam.ngram_lm_model=<path to the .nemo/.ARPA file of the NGPU-LM model> \
|
||||
rnnt_decoding.beam.ngram_lm_alpha=0.2 \
|
||||
rnnt_decoding.beam.beam_size=12 \
|
||||
rnnt_decoding.beam.pruning_mode="late" \
|
||||
rnnt_decoding.beam.blank_lm_score_mode="lm_weighted_full" \
|
||||
rnnt_decoding.beam.allow_cuda_graphs=True \
|
||||
rnnt_decoding.strategy="malsd_batch"
|
||||
|
||||
.. note::
|
||||
|
||||
To run the inference with TDT model, you need to provide pretrained TDT model in ``pretrained_name`` field (for example ``nvidia/parakeet-tdt_ctc-1.1b`` ).
|
||||
|
||||
AED Decoding with NGPU-LM
|
||||
-------------------------
|
||||
|
||||
**Beam Search:**
|
||||
|
||||
You can run NGPU-LM shallow fusion during greedy CTC decoding using the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
pretrained_name="nvidia/canary-1b" \
|
||||
amp=false \
|
||||
amp_dtype=bfloat16 \
|
||||
matmul_precision=high \
|
||||
compute_dtype=bfloat16 \
|
||||
presort_manifest=true \
|
||||
cuda=0 \
|
||||
batch_size=32 \
|
||||
dataset_manifest=<dataset_manifest> \
|
||||
multitask_decoding.beam.beam_size=4 \
|
||||
multitask_decoding.beam.ngram_lm_model=<path to the .nemo/.ARPA file of the NGPU-LM model> \
|
||||
multitask_decoding.beam.ngram_lm_alpha=0.2 \
|
||||
multitask_decoding.strategy="beam"
|
||||
|
||||
.. note::
|
||||
|
||||
For greedy decoding with NGPU-LM, use beam search with beam_size=1.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [ngpulm] V. Bataev, A. Andrusenko, L. Grigoryan, A. Laptev, V. Lavrukhin, and B. Ginsburg.
|
||||
*NGPU-LM: GPU-Accelerated N-Gram Language Model for Context-Biasing in Greedy ASR Decoding*.
|
||||
arXiv:2505.22857, 2025. Available at: https://arxiv.org/abs/2505.22857
|
||||
|
||||
.. [beamsearch] L. Grigoryan, V. Bataev, A. Andrusenko, H. Xu, V. Lavrukhin, and B. Ginsburg.
|
||||
*Pushing the Limits of Beam Search Decoding for Transducer-based ASR Models*.
|
||||
arXiv:2506.00185, 2025. Available at: https://arxiv.org/abs/2506.00185
|
||||
|
||||
.. [alsd] G. Saon, Z. Tüske, and K. Audhkhasi.
|
||||
*Alignment-Length Synchronous Decoding for RNN Transducer*.
|
||||
In: ICASSP 2020 – IEEE International Conference on Acoustics, Speech and Signal Processing, pp. 7804–7808, 2020.
|
||||
doi: https://doi.org/10.1109/ICASSP40776.2020.9053040
|
||||
|
||||
.. [aes] J. Kim, Y. Lee, and E. Kim.
|
||||
*Accelerating RNN Transducer Inference via Adaptive Expansion Search*.
|
||||
IEEE Signal Processing Letters, vol. 27, pp. 2019–2023, 2020.
|
||||
doi: https://doi.org/10.1109/LSP.2020.3036335
|
||||
@@ -0,0 +1,151 @@
|
||||
.. _ngram-utils:
|
||||
|
||||
Scripts for building and merging N-gram Language Models
|
||||
=======================================================
|
||||
|
||||
.. _train-ngram-lm:
|
||||
|
||||
Train N-gram LM
|
||||
===============
|
||||
|
||||
NeMo utilizes the KenLM library (`https://github.com/kpu/kenlm`) for building efficient n-gram language models.
|
||||
|
||||
.. note::
|
||||
|
||||
KenLM is not installed by default in NeMo.
|
||||
Please see the installation instructions in the script:
|
||||
`scripts/asr_language_modeling/ngram_lm/install_beamsearch_decoders.sh <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/install_beamsearch_decoders.sh>`__.
|
||||
|
||||
Alternatively, you can build a Docker image with all required dependencies using:
|
||||
`scripts/installers/Dockerfile.ngramtools <https://github.com/NVIDIA/NeMo/blob/stable/scripts/installers/Dockerfile.ngramtools>`__.
|
||||
|
||||
The script for training an n-gram language model with KenLM is available here:
|
||||
`scripts/asr_language_modeling/ngram_lm/train_kenlm.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/train_kenlm.py>`__.
|
||||
|
||||
This script supports training n-gram LMs on both character-level and BPE-level encodings, which are automatically detected from the model type. The resulting language models can then be used with beam search decoders integrated on top of ASR models.
|
||||
|
||||
You can train an n-gram model using the following command:
|
||||
|
||||
.. code-block::
|
||||
|
||||
python train_kenlm.py nemo_model_file=<path to the .nemo file of the model> \
|
||||
train_paths=<list of paths to the training text or JSON manifest files> \
|
||||
kenlm_bin_path=<path to the bin folder of KenLM library> \
|
||||
kenlm_model_file=<path to store the binary KenLM model> \
|
||||
ngram_length=<order of N-gram model> \
|
||||
preserve_arpa=true
|
||||
|
||||
The `train_paths` parameter allows for various input types, such as a list of text files, JSON manifests, or directories, to be used as the training data.
|
||||
If the file's extension is anything other than `.json`, it assumes that data format is plain text. For plain text format, each line should contain one
|
||||
sample. For the JSON manifests, the file must contain JSON-formatted samples per each line like this:
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"audio_filepath": "/data_path/file1.wav", "text": "The transcript of the audio file."}
|
||||
|
||||
This code extracts the `text` field from each line to create the training text file. After the N-gram model is trained, it is stored at the path specified by `kenlm_model_file`.
|
||||
|
||||
The following is the list of the arguments for the training script:
|
||||
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| **Argument** | **Type** | **Default** | **Description** |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| nemo_model_file | str | Required | The path to `.nemo` file of the ASR model, or name of a pretrained NeMo model to extract a tokenizer. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| train_paths | List[str] | Required | List of training files or folders. Files can be a plain text file or ".json" manifest or ".json.gz". |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| kenlm_model_file | str | Required | The path to store the KenLM binary model file. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| kenlm_bin_path | str | Required | The path to the bin folder of KenLM. It is a folder named `bin` under where KenLM is installed. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ngram_length** | int | Required | Specifies order of N-gram LM. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ngram_prune | List[int] | [0] | List of thresholds to prune N-grams. Example: [0,0,1]. See Pruning section on the https://kheafield.com/code/kenlm/estimation |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| cache_path | str | ``""`` | Cache path to save tokenized files. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| preserve_arpa | bool | ``False`` | Whether to preserve the intermediate ARPA file after construction of the BIN file. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| verbose | int | 1 | Verbose level. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
| save_nemo | bool | ``False`` | Whether to save LM in .nemo format. |
|
||||
+------------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
..note::
|
||||
It is recommended that you use 6 as the order of the N-gram model for BPE-based models. Higher orders may require re-compiling KenLM to support them.
|
||||
|
||||
|
||||
Combine N-gram Language Models
|
||||
==============================
|
||||
|
||||
Before combining N-gram LMs, install the required OpenGrm NGram library using `scripts/installers/install_opengrm.sh <https://github.com/NVIDIA/NeMo/blob/stable/scripts/installers/install_opengrm.sh>`__.
|
||||
Alternatively, you can use Docker image `scripts/installers/Dockerfile.ngramtools <https://github.com/NVIDIA/NeMo/blob/stable/scripts/installers/Dockerfile.ngramtools>`__ with all the necessary dependencies.
|
||||
|
||||
Alternatively, you can use the Docker image at:
|
||||
`scripts/asr_language_modeling/ngram_lm/ngram_merge.py <https://github.com/NVIDIA/NeMo/blob/stable/scripts/asr_language_modeling/ngram_lm/ngram_merge.py>`__, which includes all the necessary dependencies.
|
||||
|
||||
This script interpolates two ARPA N-gram language models and creates a KenLM binary file that can be used with the beam search decoders on top of ASR models.
|
||||
You can specify weights (`--alpha` and `--beta`) for each of the models (`--ngram_a` and `--ngram_b`) correspondingly: `alpha` * `ngram_a` + `beta` * `ngram_b`.
|
||||
This script supports both character level and BPE level encodings and models which are detected automatically from the type of the model.
|
||||
|
||||
To combine two N-gram models, you can use the following command:
|
||||
|
||||
.. code-block::
|
||||
|
||||
python ngram_merge.py --kenlm_bin_path <path to the bin folder of KenLM library> \
|
||||
--ngram_bin_path <path to the bin folder of OpenGrm Ngram library> \
|
||||
--arpa_a <path to the ARPA N-gram model file A> \
|
||||
--alpha <weight of N-gram model A> \
|
||||
--ar
|
||||
pa_b <path to the ARPA N-gram model file B> \
|
||||
--beta <weight of N-gram model B> \
|
||||
--out_path <path to folder to store the output files>
|
||||
|
||||
|
||||
|
||||
If you provide `--test_file` and `--nemo_model_file`, This script supports both character-level and BPE-level encodings and models, which are detected automatically based on the type of the model.
|
||||
Note, the result of each step during the process is cached in the temporary file in the `--out_path`, to speed up further run.
|
||||
You can use the `--force` flag to discard the cache and recalculate everything from scratch.
|
||||
|
||||
.. code-block::
|
||||
|
||||
python ngram_merge.py --kenlm_bin_path <path to the bin folder of KenLM library> \
|
||||
--ngram_bin_path <path to the bin folder of OpenGrm Ngram library> \
|
||||
--arpa_a <path to the ARPA N-gram model file A> \
|
||||
--alpha <weight of N-gram model A> \
|
||||
--arpa_b <path to the ARPA N-gram model file B> \
|
||||
--beta <weight of N-gram model B> \
|
||||
--out_path <path to folder to store the output files>
|
||||
--nemo_model_file <path to the .nemo file of the model> \
|
||||
--test_file <path to the test file> \
|
||||
--symbols <path to symbols (.syms) file> \
|
||||
--force <flag to recalculate and rewrite all cached files>
|
||||
|
||||
|
||||
The following is the list of the arguments for the opengrm script:
|
||||
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| **Argument** |**Type**| **Default** | **Description** |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| kenlm_bin_path | str | Required | The path to the bin folder of KenLM library. It is a folder named `bin` under where KenLM is installed. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| ngram_bin_path | str | Required | The path to the bin folder of OpenGrm Ngram. It is a folder named `bin` under where OpenGrm Ngram is installed. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| arpa_a | str | Required | Path to the ARPA N-gram model file A. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| alpha | float | Required | Weight of N-gram model A. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| arpa_b | int | Required | Path to the ARPA N-gram model file B. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| beta | float | Required | Weight of N-gram model B. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| out_path | str | Required | Path for writing temporary and resulting files. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| test_file | str | None | Path to test file to count perplexity if provided. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| symbols | str | None | Path to symbols (.syms) file. Could be calculated if it is not provided. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| nemo_model_file | str | None | The path to '.nemo' file of the ASR model, or name of a pretrained NeMo model. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
| force | bool | ``False`` | Whether to recompile and rewrite all files. |
|
||||
+----------------------+--------+------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||
@@ -0,0 +1,362 @@
|
||||
.. _word_boosting:
|
||||
|
||||
****************************************************
|
||||
Word Boosting
|
||||
****************************************************
|
||||
|
||||
.. _word_boosting_gpupb:
|
||||
|
||||
GPU-PB
|
||||
========================
|
||||
|
||||
GPU-PB is a GPU-accelerated Phrase-Boosting method supported for CTC, RNN-T/TDT, and AED (Canary) models based on NGPU-LM infrastructure.
|
||||
The method supports greedy and beam search decoding, including CUDA graphs mode. GPU-PB is compatible with NGPU-LM at the same decoding run.
|
||||
|
||||
GPU-PB is applied only at the decoding step in shallow fusion mode. You do not need to retrain the ASR model.
|
||||
During greedy or beam search decoding, GPU-PB rescales ASR model scores with a boosting tree at the token level.
|
||||
The boosting tree is built from a context phrases list, which is provided by the user.
|
||||
|
||||
**NOTE**: for ASR models that support capitalization by default (e.g., Canary or parakeet-tdt-0.6b-v2), you need to capitalize all the key phrases in advance (and capitalize the full word for abbreviations).
|
||||
You can use LLM for this task.
|
||||
|
||||
More details about GPU-PB method can be found in the `original paper <https://arxiv.org/abs/2508.07014>`__.
|
||||
|
||||
Usage
|
||||
-----
|
||||
We support three ways to pass the context phrases into the decoding script:
|
||||
|
||||
1. Build a boosting tree for a specific ASR model (step 0.0) and use it for all the decoding evaluation by ``boosting_tree.model_path`` (step 1.1-3.1).
|
||||
2. Provide a file with context phrases ``boosting_tree.key_phrases_file`` - one phrase per line (step 1.1-3.1).
|
||||
3. Provide a python list of context phrases ``boosting_tree.key_phrases_list`` (step 1.1-3.1).
|
||||
|
||||
The use of the Phrase-Boosting tree is controlled by ``boosting_tree`` config (``BoostingTreeModelConfig``) for all the models.
|
||||
For prepared boosting tree use ``boosting_tree.model_path=${PATH_TO_BTREE}``.
|
||||
We recommend to provide the list of context phrases directly into ``speech_to_text_eval.py`` by ``boosting_tree.key_phrases_file=${KEY_WORDS_LIST}``.
|
||||
|
||||
List of the most important parameters:
|
||||
|
||||
* ``strategy`` - The strategy to use for decoding depending on the model type (CTC - greedy_batch or beam_batch; RNN-T/TDT - greedy_batch or malsd_batch; AED - beam).
|
||||
* ``model_path``, ``key_phrases_file``, ``key_phrases_list`` - The way to pass the context phrases into the decoding script.
|
||||
* ``context_score`` - The score for each arc transition in the context graph (1.0 is recommended).
|
||||
* ``depth_scaling`` - The scaling factor for the depth of the context graph (2.0 is recommended for CTC, RNN-T and TDT, 1.0 for Canary).
|
||||
* ``boosting_tree_alpha`` - Weight of the GPU-PB boosting tree during shallow fusion decoding (tune it according to your data).
|
||||
|
||||
**0.0. [Optional] Build the boosting tree for a specific ASR model:**
|
||||
|
||||
.. code-block::
|
||||
|
||||
python scripts/asr_context_biasing/build_gpu_boosting_tree.py \
|
||||
asr_model_path=${ASR_NEMO_MODEL_FILE} \
|
||||
key_phrases_file=${CONTEXT_BIASING_LIST} \
|
||||
save_to=${PATH_TO_SAVE_BTREE} \
|
||||
context_score=${CONTEXT_SCORE} \
|
||||
depth_scaling=${DEPTH_SCALING} \
|
||||
use_triton=True
|
||||
|
||||
**1.1. CTC greedy batch decoding:**
|
||||
|
||||
.. code-block::
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=${MODEL_NAME} \
|
||||
dataset_manifest=${EVAL_MANIFEST} \
|
||||
batch_size=${BATCH_SIZE} \
|
||||
output_filename=${OUT_MANIFEST} \
|
||||
ctc_decoding.strategy="greedy_batch" \
|
||||
ctc_decoding.greedy.boosting_tree.key_phrases_file=${KEY_WORDS_LIST} \
|
||||
ctc_decoding.greedy.boosting_tree.context_score=1.0 \
|
||||
ctc_decoding.greedy.boosting_tree.depth_scaling=2.0 \
|
||||
ctc_decoding.greedy.boosting_tree_alpha=${BT_ALPHA}
|
||||
|
||||
|
||||
**1.2. CTC beam batch decoding:**
|
||||
|
||||
.. code-block::
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=${MODEL_NAME} \
|
||||
dataset_manifest=${EVAL_MANIFEST} \
|
||||
batch_size=${BATCH_SIZE} \
|
||||
output_filename=${OUT_MANIFEST} \
|
||||
ctc_decoding.strategy="beam_batch" \
|
||||
ctc_decoding.beam.beam_size=${BEAM_SIZE} \
|
||||
ctc_decoding.beam.boosting_tree.key_phrases_file=${KEY_WORDS_LIST} \
|
||||
ctc_decoding.beam.boosting_tree.context_score=1.0 \
|
||||
ctc_decoding.beam.boosting_tree.depth_scaling=2.0 \
|
||||
ctc_decoding.beam.boosting_tree_alpha=${BT_ALPHA}
|
||||
|
||||
**2.1. RNN-T/TDT greedy batch decoding:**
|
||||
|
||||
.. code-block::
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=${MODEL_NAME} \
|
||||
dataset_manifest=${EVAL_MANIFEST} \
|
||||
batch_size=${BATCH_SIZE} \
|
||||
output_filename=${OUT_MANIFEST} \
|
||||
rnnt_decoding.strategy="greedy_batch" \
|
||||
rnnt_decoding.greedy.boosting_tree.key_phrases_file=${KEY_WORDS_LIST} \
|
||||
rnnt_decoding.greedy.boosting_tree.context_score=1.0 \
|
||||
rnnt_decoding.greedy.boosting_tree.depth_scaling=2.0 \
|
||||
rnnt_decoding.greedy.boosting_tree_alpha=${BT_ALPHA}
|
||||
|
||||
**2.2. RNN-T/TDT beam (malsd_batch) decoding:**
|
||||
|
||||
.. code-block::
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=${MODEL_NAME} \
|
||||
dataset_manifest=${EVAL_MANIFEST} \
|
||||
batch_size=${BATCH_SIZE} \
|
||||
output_filename=${OUT_MANIFEST} \
|
||||
rnnt_decoding.strategy="malsd_batch" \
|
||||
rnnt_decoding.beam.beam_size=${BEAM_SIZE} \
|
||||
rnnt_decoding.beam.boosting_tree.key_phrases_file=${KEY_WORDS_LIST} \
|
||||
rnnt_decoding.beam.boosting_tree.context_score=1.0 \
|
||||
rnnt_decoding.beam.boosting_tree.depth_scaling=2.0 \
|
||||
rnnt_decoding.beam.boosting_tree_alpha=${BT_ALPHA}
|
||||
|
||||
**3.1. AED (Canary) greedy (beam_size=1) or beam (beam_size>1) decoding:**
|
||||
|
||||
.. code-block::
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
model_path=${MODEL_NAME} \
|
||||
dataset_manifest=${EVAL_MANIFEST} \
|
||||
batch_size=${BATCH_SIZE} \
|
||||
output_filename=${OUT_MANIFEST} \
|
||||
multitask_decoding.strategy="beam" \
|
||||
multitask_decoding.beam.beam_size=${BEAM_SIZE} \
|
||||
multitask_decoding.beam.boosting_tree.key_phrases_file=${CONTEXT_BIASING_LIST} \
|
||||
multitask_decoding.beam.boosting_tree.context_score=1.0 \
|
||||
multitask_decoding.beam.boosting_tree.depth_scaling=1.0 \
|
||||
multitask_decoding.beam.boosting_tree_alpha=${BT_ALPHA} \
|
||||
gt_lang_attr_name="target_lang" \
|
||||
gt_text_attr_name="text"
|
||||
|
||||
Results evaluation
|
||||
------------------
|
||||
|
||||
You can compute the F-score for the list of context phrases directly from the decoding manifest.
|
||||
|
||||
.. code-block::
|
||||
|
||||
python scripts/asr_context_biasing/compute_key_words_fscore.py \
|
||||
--input_manifest=${DECODING_MANIFEST} \
|
||||
--key_words_file=${CONTEXT_PHRASES_LIST}
|
||||
|
||||
|
||||
.. _word_boosting_per_stream:
|
||||
|
||||
Per-Stream Phrase Boosting
|
||||
==========================
|
||||
|
||||
Per-stream (per-utterance) phrase boosting extends GPU-PB to allow specifying different key phrases for each audio stream or utterance in a batch.
|
||||
This is useful when different utterances require different context biasing (e.g., different speaker names, product terms, or domain vocabulary per audio).
|
||||
|
||||
Per-stream boosting is currently supported for **greedy label-looping decoding with Transducers (RNN-T, TDT)**, including cache-aware streaming models.
|
||||
|
||||
Manifest-based Usage
|
||||
--------------------
|
||||
|
||||
Specify per-utterance key phrases in your manifest using the ``biasing_request`` field:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{"audio_filepath": "/data/file1.wav", "text": "ground truth", "biasing_request": {"boosting_model_cfg": {"key_phrases_list": ["one phrase"]}}}
|
||||
{"audio_filepath": "/data/file2.wav", "text": "ground truth", "biasing_request": {"boosting_model_cfg": {"key_phrases_list": ["other phrases", "and this one"]}}}
|
||||
|
||||
Use the streaming inference script with ``use_per_stream_biasing=true``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python examples/asr/asr_streaming_inference/asr_streaming_infer.py \
|
||||
--config-path="../conf/asr_streaming_inference/" \
|
||||
--config-name=cache_aware_rnnt.yaml \
|
||||
audio_file="<manifest_with_boosting_requests>" \
|
||||
output_filename="result.jsonl" \
|
||||
asr.model_name="nvidia/parakeet-rnnt-1.1b" \
|
||||
asr.decoding.greedy.enable_per_stream_biasing=True
|
||||
|
||||
Python API Usage
|
||||
----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from omegaconf import open_dict
|
||||
from nemo.collections.asr.models import EncDecRNNTBPEModel
|
||||
from nemo.collections.asr.parts.context_biasing.biasing_multi_model import BiasingRequestItemConfig
|
||||
from nemo.collections.asr.parts.context_biasing.boosting_graph_batched import BoostingTreeModelConfig
|
||||
from nemo.collections.asr.parts.utils.rnnt_utils import Hypothesis
|
||||
|
||||
asr_model = EncDecRNNTBPEModel.from_pretrained("nvidia/parakeet-rnnt-1.1b")
|
||||
asr_model.to("cuda")
|
||||
|
||||
with open_dict(asr_model.cfg.decoding):
|
||||
asr_model.cfg.decoding.strategy = "greedy_batch"
|
||||
asr_model.cfg.decoding.greedy.loop_labels = True
|
||||
asr_model.cfg.decoding.greedy.enable_per_stream_biasing = True
|
||||
asr_model.change_decoding_strategy(asr_model.cfg.decoding)
|
||||
|
||||
biasing_requests = [
|
||||
BiasingRequestItemConfig(
|
||||
boosting_model_cfg=BoostingTreeModelConfig(key_phrases_list=["one phrase"]),
|
||||
boosting_model_alpha=2.0,
|
||||
),
|
||||
None, # no biasing for this utterance
|
||||
BiasingRequestItemConfig(
|
||||
boosting_model_cfg=BoostingTreeModelConfig(key_phrases_list=["other phrases"]),
|
||||
boosting_model_alpha=1.0,
|
||||
),
|
||||
]
|
||||
|
||||
results = asr_model.transcribe(
|
||||
audio=["file1.wav", "file2.wav", "file3.wav"],
|
||||
partial_hypothesis=[
|
||||
Hypothesis.empty_with_biasing_cfg(biasing_cfg=req) if req else None
|
||||
for req in biasing_requests
|
||||
],
|
||||
return_hypotheses=True,
|
||||
)
|
||||
|
||||
Caching
|
||||
-------
|
||||
|
||||
Building a boosting model from a phrase list has some overhead. NeMo provides caching mechanisms to speed up repeated use of the same phrases:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 15 60 25
|
||||
|
||||
* - Strategy
|
||||
- Description
|
||||
- Recommended For
|
||||
* - Memory
|
||||
- Set ``cache_key`` on ``BiasingRequestItemConfig`` to cache compiled models in memory by a string key.
|
||||
- Repeated phrase sets
|
||||
* - Disk
|
||||
- Set ``model_path`` on ``BoostingTreeModelConfig`` to save/load compiled models from disk.
|
||||
- Persistent caching
|
||||
* - Decoder
|
||||
- Set ``auto_manage_multi_model=False`` and manually manage models in the decoder's multi-model.
|
||||
- Advanced use cases
|
||||
|
||||
With memory caching, per-stream boosting achieves near-zero overhead compared to global (shared) boosting.
|
||||
|
||||
|
||||
.. _word_boosting_flashlight:
|
||||
|
||||
Flashlight-based Word Boosting
|
||||
==============================
|
||||
|
||||
|
||||
The Flashlight decoder supports word boosting during CTC decoding using a KenLM binary and corresponding lexicon. Word boosting only works in lexicon-decoding mode and does not function in lexicon-free mode. It allows you to bias the decoder for certain words by manually increasing or decreasing the probability of emitting specific words. This can be very helpful if you have uncommon or industry-specific terms that you want to ensure are transcribed correctly.
|
||||
|
||||
For more information, go to `word boosting <https://docs.nvidia.com/deeplearning/riva/user-guide/docs/asr/asr-customizing.html#word-boosting>`__
|
||||
|
||||
To use word boosting in NeMo, create a simple tab-separated text file. Each line should contain a word to be boosted, followed by a tab, and then the boosted score for that word.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
nvidia 40
|
||||
geforce 50
|
||||
riva 80
|
||||
turing 30
|
||||
badword -100
|
||||
|
||||
Positive scores boost words higher in the LM decoding step so they show up more frequently, whereas negative scores
|
||||
squelch words so they show up less frequently. The recommended range for the boost score is +/- 20 to 100.
|
||||
|
||||
The boost file handles both in-vocabulary words and OOV words just fine, so you can specify both IV and OOV words with corresponding scores.
|
||||
|
||||
You can then pass this file to your Flashlight config object during decoding:
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Lexicon-based decoding
|
||||
python eval_beamsearch_ngram_ctc.py ... \
|
||||
decoding_strategy="flashlight" \
|
||||
decoding.beam.flashlight_cfg.lexicon_path='/path/to/lexicon.lexicon' \
|
||||
decoding.beam.flashlight_cfg.boost_path='/path/to/my_boost_file.boost' \
|
||||
decoding.beam.flashlight_cfg.beam_size_token = 32 \
|
||||
decoding.beam.flashlight_cfg.beam_threshold = 25.0
|
||||
|
||||
.. _word_boosting_ctcws:
|
||||
|
||||
CTC-WS: Context-biasing (Word Boosting) without External LM
|
||||
===========================================================
|
||||
|
||||
NeMo toolkit supports a fast context-biasing method for CTC and Transducer (RNN-T) ASR models with CTC-based Word Spotter.
|
||||
The method involves decoding CTC log probabilities with a context graph built for words and phrases from the context-biasing list.
|
||||
The spotted context-biasing candidates (with their scores and time intervals) are compared by scores with words from the greedy CTC decoding results to improve recognition accuracy and prevent false accepts of context-biasing.
|
||||
|
||||
A Hybrid Transducer-CTC model (a shared encoder trained together with CTC and Transducer output heads) enables the use of the CTC-WS method for the Transducer model.
|
||||
Context-biasing candidates obtained by CTC-WS are also filtered by the scores with greedy CTC predictions and then merged with greedy Transducer results.
|
||||
|
||||
Scheme of the CTC-WS method:
|
||||
|
||||
.. image:: https://github.com/NVIDIA/NeMo/releases/download/v1.22.0/asset-post-v1.22.0-ctcws_scheme_1.png
|
||||
:align: center
|
||||
:alt: CTC-WS scheme
|
||||
:width: 80%
|
||||
|
||||
High-level overview of the context-biasing words replacement with CTC-WS method:
|
||||
|
||||
.. image:: https://github.com/NVIDIA/NeMo/releases/download/v1.22.0/asset-post-v1.22.0-ctcws_scheme_2.png
|
||||
:align: center
|
||||
:alt: CTC-WS high level overview
|
||||
:width: 80%
|
||||
|
||||
More details about CTC-WS context-biasing can be found in the `tutorial <https://github.com/NVIDIA/NeMo/tree/main/tutorials/asr/ASR_Context_Biasing.ipynb>`__.
|
||||
|
||||
To use CTC-WS context-biasing, you need to create a context-biasing text file that contains words/phrases to be boosted, with its transcriptions (spellings) separated by underscore.
|
||||
Multiple transcriptions can be useful for abbreviations ("gpu" -> "g p u"), compound words ("nvlink" -> "nv link"),
|
||||
or words with common mistakes in the case of our ASR model ("nvidia" -> "n video").
|
||||
|
||||
Example of the context-biasing file:
|
||||
|
||||
.. code-block::
|
||||
|
||||
nvidia_nvidia
|
||||
omniverse_omniverse
|
||||
gpu_gpu_g p u
|
||||
dgx_dgx_d g x_d gx
|
||||
nvlink_nvlink_nv link
|
||||
ray tracing_ray tracing
|
||||
|
||||
The main script for CTC-WS context-biasing in NeMo is:
|
||||
|
||||
.. code-block::
|
||||
|
||||
{NEMO_DIR_PATH}/scripts/asr_context_biasing/eval_greedy_decoding_with_context_biasing.py
|
||||
|
||||
Context-biasing is managed by ``apply_context_biasing`` parameter [true or false].
|
||||
Other important context-biasing parameters are:
|
||||
|
||||
* ``beam_threshold`` - threshold for CTC-WS beam pruning.
|
||||
* ``context_score`` - per token weight for context biasing.
|
||||
* ``ctc_ali_token_weight`` - per token weight for CTC alignment (prevents false acceptances of context-biasing words).
|
||||
|
||||
All the context-biasing parameters are selected according to the default values in the script.
|
||||
You can tune them according to your data and ASR model (list all the values in the [] separated by commas)
|
||||
for example: ``beam_threshold=[7.0,8.0,9.0]``, ``context_score=[3.0,4.0,5.0]``, ``ctc_ali_token_weight=[0.5,0.6,0.7]``.
|
||||
The script will run the recognition with all the combinations of the parameters and will select the best one based on WER value.
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Context-biasing with the CTC-WS method for CTC ASR model
|
||||
python {NEMO_DIR_PATH}/scripts/asr_context_biasing/eval_greedy_decoding_with_context_biasing.py \
|
||||
nemo_model_file={ctc_model_name} \
|
||||
input_manifest={test_nemo_manifest} \
|
||||
preds_output_folder={exp_dir} \
|
||||
decoder_type="ctc" \
|
||||
acoustic_batch_size=64 \
|
||||
apply_context_biasing=true \
|
||||
context_file={cb_list_file_modified} \
|
||||
beam_threshold=[7.0] \
|
||||
context_score=[3.0] \
|
||||
ctc_ali_token_weight=[0.5]
|
||||
|
||||
To use Transducer head of the Hybrid Transducer-CTC model, you need to set ``decoder_type=rnnt``.
|
||||
@@ -0,0 +1,213 @@
|
||||
.. _asr_language_modeling_and_customization:
|
||||
|
||||
#######################################
|
||||
ASR Language Modeling and Customization
|
||||
#######################################
|
||||
|
||||
NeMo supports decoding-time customization techniques such as *language modeling* and *word boosting*,
|
||||
which improve transcription accuracy by incorporating external knowledge or domain-specific vocabulary—without retraining the model.
|
||||
|
||||
|
||||
Decoder Types
|
||||
-------------
|
||||
|
||||
NeMo ASR models use different decoder architectures. The table below summarizes them:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Decoder
|
||||
- Type
|
||||
- Description
|
||||
- Models
|
||||
* - **CTC**
|
||||
- Non-autoregressive
|
||||
- Connectionist Temporal Classification. Fast inference, supports LM fusion and word boosting.
|
||||
- Parakeet-CTC, FastConformer-CTC
|
||||
* - **RNN-T**
|
||||
- Autoregressive
|
||||
- Recurrent Neural Network Transducer. Strong accuracy, streaming-friendly.
|
||||
- Parakeet-RNNT, FastConformer-Transducer
|
||||
* - **TDT**
|
||||
- Autoregressive
|
||||
- Token-and-Duration Transducer. Extends RNN-T with duration prediction for better timestamps.
|
||||
- Parakeet-TDT
|
||||
* - **AED**
|
||||
- Autoregressive
|
||||
- Attention Encoder-Decoder. Multi-task capable (ASR + AST), prompt-based language control.
|
||||
- Canary-1B, Canary-1B-V2, Canary-1B-Flash
|
||||
* - **Hybrid**
|
||||
- Both
|
||||
- Joint RNN-T + CTC training. Use either decoder at inference time.
|
||||
- FastConformer Hybrid models
|
||||
|
||||
|
||||
Language Modeling
|
||||
-----------------
|
||||
|
||||
In NeMo two approaches of external language modeling are supported:
|
||||
|
||||
- **Language Model Fusion:**
|
||||
Language model (LM) fusion integrates scores from an external statistical n-gram model into the ASR decoder.
|
||||
This helps guide decoding toward more likely word sequences based on text corpora.
|
||||
|
||||
NeMo provides two approaches for language model shallow fusion with ASR systems:
|
||||
|
||||
**1. NGPU-LM (Recommended for Production)**
|
||||
GPU-accelerated LM fusion for all major model types: CTC, RNN-T, TDT, and AED models.
|
||||
|
||||
- Customization during both greedy and beam decoding.
|
||||
|
||||
- Fast beam decoding for all major model types, offering only 20% RTFx difference between beam and greedy decoding.
|
||||
|
||||
- Integration with NGPU-LM GPU-based ngram LM.
|
||||
|
||||
For details, please refer to :ref:`ngpulm_ngram_modeling`
|
||||
|
||||
**2. KenLM (Traditional CPU-based)**
|
||||
CPU-based LM fusion using the KenLM library.
|
||||
|
||||
.. note::
|
||||
|
||||
These approaches, especially beam decoding, can be extremely slow and are retained in the repository primarily for backward compatibility.
|
||||
If possible, we recommend using NGPU-LM for improved performance.
|
||||
|
||||
For details, please refer to :ref:`ngram_modeling`
|
||||
|
||||
- **Neural Rescoring:**
|
||||
When using the neural rescoring approach, a neural network is used to score candidates. A candidate is the text transcript predicted by the ASR model’s decoder.
|
||||
The top K candidates produced by beam search decoding (with a beam width of K) are given to a neural language model for ranking.
|
||||
The language model assigns a score to each candidate, which is usually combined with the scores from beam search decoding to produce the final scores and rankings.
|
||||
|
||||
For details, please refer to :ref:`neural_rescoring`.
|
||||
|
||||
|
||||
Word Boosting
|
||||
-------------
|
||||
|
||||
Word boosting increases the likelihood of specific words or phrases during decoding by applying a positive bias, helping the model better recognize names,
|
||||
uncommon terms, and custom vocabulary.
|
||||
|
||||
- :ref:`word_boosting_gpupb` (preferred): GPU-accelerated phrase-boosting for CTC, RNN-T/TDT, and AED (Canary) models supporting greedy and beam search decoding.
|
||||
|
||||
- :ref:`word_boosting_flashlight`: Word-boosting method for CTC models with external n-gram LM.
|
||||
|
||||
- :ref:`word_boosting_ctcws`: Word-boosting method for hybrid (Transducer-CTC) models without LM.
|
||||
|
||||
For details, please refer to: :ref:`word_boosting`.
|
||||
|
||||
|
||||
LM Training
|
||||
-----------
|
||||
|
||||
NeMo provides tools for training n-gram language models that can be used for language model fusion or word-boosting.
|
||||
For details, please refer to: :ref:`ngram-utils`.
|
||||
|
||||
|
||||
CUDA Graphs
|
||||
-----------
|
||||
|
||||
CUDA graphs accelerate decoding by capturing and replaying GPU operations, eliminating kernel launch overhead.
|
||||
Support varies by decoder strategy:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Strategy
|
||||
- Config Parameter
|
||||
- Default
|
||||
- Notes
|
||||
* - ``greedy_batch`` (RNN-T, TDT)
|
||||
- ``use_cuda_graph_decoder``
|
||||
- ``true``
|
||||
- Requires ``loop_labels=True`` and ``blank_as_pad=True``
|
||||
* - ``maes_batch``, ``malsd_batch`` (beam)
|
||||
- ``allow_cuda_graphs``
|
||||
- ``true``
|
||||
- Batched beam search strategies
|
||||
* - Non-batched ``greedy`` / ``beam``
|
||||
- N/A
|
||||
- N/A
|
||||
- Not supported; standard decoding used
|
||||
|
||||
To disable CUDA graphs (e.g. for debugging or when preserving alignments with frame-looping):
|
||||
|
||||
**Via Python (at runtime):**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
model.disable_cuda_graphs()
|
||||
|
||||
**Greedy decoding** — use ``use_cuda_graph_decoder=true/false``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
pretrained_name="nvidia/parakeet-rnnt-1.1b" \
|
||||
dataset_manifest=<dataset_manifest> \
|
||||
batch_size=32 \
|
||||
output_filename=decoded.jsonl \
|
||||
rnnt_decoding.strategy="greedy_batch" \
|
||||
rnnt_decoding.greedy.use_cuda_graph_decoder=true
|
||||
|
||||
**Beam decoding** — use ``allow_cuda_graphs=true/false``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python examples/asr/speech_to_text_eval.py \
|
||||
pretrained_name="nvidia/parakeet-rnnt-1.1b" \
|
||||
dataset_manifest=<dataset_manifest> \
|
||||
batch_size=32 \
|
||||
output_filename=decoded.jsonl \
|
||||
rnnt_decoding.strategy="malsd_batch" \
|
||||
rnnt_decoding.beam.max_symbols_per_step=10 \
|
||||
rnnt_decoding.beam.beam_size=12 \
|
||||
rnnt_decoding.beam.allow_cuda_graphs=true
|
||||
|
||||
When unsupported, NeMo falls back to standard decoding automatically.
|
||||
|
||||
|
||||
Confidence Estimation
|
||||
---------------------
|
||||
|
||||
NeMo supports per-frame, per-token, and per-word confidence scores during decoding.
|
||||
Confidence estimation helps applications decide when to trust ASR output and when to request human review.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
decoding:
|
||||
confidence_cfg:
|
||||
preserve_frame_confidence: false
|
||||
preserve_token_confidence: false
|
||||
preserve_word_confidence: false
|
||||
exclude_blank: true
|
||||
aggregation: "mean" # mean, min, max, prod
|
||||
method_cfg:
|
||||
name: "entropy" # max_prob or entropy
|
||||
entropy_type: "tsallis" # gibbs, tsallis, renyi
|
||||
alpha: 0.33
|
||||
entropy_norm: "exp" # lin or exp
|
||||
|
||||
**Confidence methods:**
|
||||
|
||||
* ``max_prob``: Maximum token probability as confidence. Simple and fast.
|
||||
* ``entropy``: Normalized entropy of the log-likelihood vector (default). Entropy types:
|
||||
|
||||
- ``gibbs``: Standard Gibbs entropy
|
||||
- ``tsallis``: Tsallis entropy (default, recommended)
|
||||
- ``renyi``: Renyi entropy
|
||||
|
||||
**Aggregation** combines frame-level scores into token/word scores: ``mean``, ``min``, ``max``, or ``prod``.
|
||||
|
||||
For TDT models, set ``tdt_include_duration_confidence: true`` to include duration prediction confidence.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
asr_customization/ngpulm_language_modeling_and_customization
|
||||
asr_customization/neural_rescoring
|
||||
asr_customization/legacy_language_modeling_and_customization
|
||||
asr_customization/ngram_utils
|
||||
asr_customization/word_boosting
|
||||
@@ -0,0 +1,925 @@
|
||||
NeMo ASR Configuration Files
|
||||
============================
|
||||
|
||||
This section describes the NeMo configuration file setup that is specific to models in the ASR collection. For general information
|
||||
about how to set up and run experiments that is common to all NeMo models (e.g. Experiment Manager and PyTorch Lightning trainer
|
||||
parameters), see the :doc:`../core/core` section.
|
||||
|
||||
The model section of the NeMo ASR configuration files generally requires information about the dataset(s) being used, the preprocessor
|
||||
for audio files, parameters for any augmentation being performed, as well as the model architecture specification. The sections on
|
||||
this page cover each of these in more detail.
|
||||
|
||||
Example configuration files for all of the NeMo ASR scripts can be found in the
|
||||
`config directory of the examples <https://github.com/NVIDIA/NeMo/tree/stable/examples/asr/conf>`_.
|
||||
|
||||
.. _asr-configs-dataset-configuration:
|
||||
|
||||
Dataset Configuration
|
||||
---------------------
|
||||
|
||||
Training, validation, and test parameters are specified using the ``train_ds``, ``validation_ds``, and
|
||||
``test_ds`` sections in the configuration file, respectively. Depending on the task, there may be arguments specifying the sample rate
|
||||
of the audio files, the vocabulary of the dataset (for character prediction), whether or not to shuffle the dataset, and so on. You may
|
||||
also decide to leave fields such as the ``manifest_filepath`` blank, to be specified via the command-line at runtime.
|
||||
|
||||
Any initialization parameter that is accepted for the Dataset class used in the experiment can be set in the config file.
|
||||
Refer to the :ref:`Datasets <asr-api-datasets>` section of the API for a list of Datasets and their respective parameters.
|
||||
|
||||
An example ASR train and validation configuration should look similar to the following:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Specified at the beginning of the config file
|
||||
labels: &labels [" ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
|
||||
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "'"]
|
||||
|
||||
model:
|
||||
train_ds:
|
||||
manifest_filepath: ???
|
||||
sample_rate: 16000
|
||||
labels: *labels # Uses the labels above
|
||||
batch_size: 32
|
||||
trim_silence: True
|
||||
max_duration: 16.7
|
||||
shuffle: True
|
||||
num_workers: 8
|
||||
pin_memory: true
|
||||
# tarred datasets
|
||||
is_tarred: false # If set to true, uses the tarred version of the Dataset
|
||||
tarred_audio_filepaths: null # Not used if is_tarred is false
|
||||
shuffle_n: 2048 # Not used if is_tarred is false
|
||||
# bucketing params
|
||||
bucketing_strategy: "synced_randomized"
|
||||
bucketing_batch_size: null
|
||||
bucketing_weights: null
|
||||
|
||||
validation_ds:
|
||||
manifest_filepath: ???
|
||||
sample_rate: 16000
|
||||
labels: *labels # Uses the labels above
|
||||
batch_size: 32
|
||||
shuffle: False # No need to shuffle the validation data
|
||||
num_workers: 8
|
||||
pin_memory: true
|
||||
|
||||
There are two ways to test/validate on more than one manifest:
|
||||
|
||||
- Specify a list in the `manifest_filepath` field. Results will be reported for each, the first one being used for overall loss / WER (specify `val_dl_idx` if you wish to change that). In this case, all manifests will share configuration parameters.
|
||||
- Use the ds_item key and pass a list of config objects to it. This allows you to use differently configured datasets for validation, e.g.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
validation_ds:
|
||||
ds_item:
|
||||
- name: dataset1
|
||||
manifest_filepath: ???
|
||||
# Config parameters for dataset1
|
||||
...
|
||||
- name: dataset2
|
||||
manifest_filepath: ???
|
||||
# Config parameters for dataset2
|
||||
...
|
||||
|
||||
By default, dataloaders are set up when the model is instantiated. However, dataloader setup can be deferred to
|
||||
model's `setup()` method by setting ``defer_setup`` in the configuration.
|
||||
|
||||
For example, training data setup can be deferred as follows:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
train_ds:
|
||||
# Configure training data as usual
|
||||
...
|
||||
# Defer train dataloader setup from `__init__` to `setup`
|
||||
defer_setup: true
|
||||
|
||||
|
||||
.. _asr-configs-metric-configuration:
|
||||
|
||||
|
||||
.. _asr-configs-preprocessor-configuration:
|
||||
|
||||
Preprocessor Configuration
|
||||
--------------------------
|
||||
|
||||
If you are loading audio files for your experiment, you will likely want to use a preprocessor to convert from the
|
||||
raw audio signal to features (e.g. mel-spectrogram or MFCC). The ``preprocessor`` section of the config specifies the audio
|
||||
preprocessor to be used via the ``_target_`` field, as well as any initialization parameters for that preprocessor.
|
||||
|
||||
An example of specifying a preprocessor is as follows:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
...
|
||||
preprocessor:
|
||||
# _target_ is the audio preprocessor module you want to use
|
||||
_target_: nemo.collections.asr.modules.AudioToMelSpectrogramPreprocessor
|
||||
normalize: "per_feature"
|
||||
window_size: 0.02
|
||||
...
|
||||
# Other parameters for the preprocessor
|
||||
|
||||
Refer to the :ref:`Audio Preprocessors <asr-audio-preprocessors>` API section for the preprocessor options, expected arguments,
|
||||
and defaults.
|
||||
|
||||
.. _asr-configs-augmentation-configurations:
|
||||
|
||||
Augmentation Configurations
|
||||
---------------------------
|
||||
|
||||
There are a few on-the-fly spectrogram augmentation options for NeMo ASR, which can be specified by the
|
||||
configuration file using a ``spec_augment`` section.
|
||||
|
||||
For example, there are options for `Cutout <https://arxiv.org/abs/1708.04552>`_ and
|
||||
`SpecAugment <https://arxiv.org/abs/1904.08779>`_ available via the ``SpectrogramAugmentation`` module.
|
||||
|
||||
The following example sets up both ``Cutout`` (via the ``rect_*`` parameters) and ``SpecAugment`` (via the ``freq_*``
|
||||
and ``time_*`` parameters).
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
...
|
||||
spec_augment:
|
||||
_target_: nemo.collections.asr.modules.SpectrogramAugmentation
|
||||
# Cutout parameters
|
||||
rect_masks: 5 # Number of rectangles to cut from any given spectrogram
|
||||
rect_freq: 50 # Max cut of size 50 along the frequency dimension
|
||||
rect_time: 120 # Max cut of size 120 along the time dimension
|
||||
# SpecAugment parameters
|
||||
freq_masks: 2 # Cut two frequency bands
|
||||
freq_width: 15 # ... of width 15 at maximum
|
||||
time_masks: 5 # Cut out 10 time bands
|
||||
time_width: 25 # ... of width 25 at maximum
|
||||
|
||||
You can use any combination of ``Cutout``, frequency/time ``SpecAugment``, or neither of them.
|
||||
|
||||
You can also add audio augmentation pipelines via an ``augmentor`` section in ``train_ds``.
|
||||
|
||||
.. caution::
|
||||
The ``augmentor`` pipeline is not supported by the Lhotse dataloader, which provides its own set of augmentation options.
|
||||
See :doc:`Lhotse Dataloading </dataloaders>` for details.
|
||||
|
||||
Augmentors are applied on-the-fly to audio data in the data layer. The following example
|
||||
adds white noise (probability 0.5, level between -50 dB and -10 dB) and room impulse response
|
||||
augmentation (probability 0.3, from a manifest of impulse responses):
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
...
|
||||
train_ds:
|
||||
...
|
||||
augmentor:
|
||||
white_noise:
|
||||
prob: 0.5
|
||||
min_level: -50
|
||||
max_level: -10
|
||||
impulse:
|
||||
prob: 0.3
|
||||
manifest_path: /path/to/impulse_manifest.json
|
||||
|
||||
Refer to the :ref:`Audio Augmentors <asr-api-audio-augmentors>` API section for more details.
|
||||
|
||||
|
||||
Metric Configurations
|
||||
---------------------
|
||||
|
||||
NeMo ASR models supports WER and BLEU metric logging during training and validation. All metrics are based on the TorchMetrics backend, allowing for distributed training without additional code.
|
||||
|
||||
Word Error Rate (WER)
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
WER is the default metric for all ASR models and measures transcription accuracy at the word or character level.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
use_cer: false # Set to true for Character Error Rate instead (default: false)
|
||||
log_prediction: true # Whether to log a sample prediction during training (default: true)
|
||||
batch_dim_index: 0 # Index of batch dimension in prediction tensors output. Set to 1 for RNNT models.
|
||||
|
||||
BLEU Score
|
||||
~~~~~~~~~~
|
||||
|
||||
BLEU score can be used for ASR models to evaluate translation quality. NeMo's BLEU implementation is based on SacreBLEU for standardized, reproducible scoring:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
bleu_tokenizer: "13a" # SacreBLEU tokenizer type (see below). (default: "13a")
|
||||
n_gram: 4 # Maximum n-gram order for BLEU calculation. (default: 4)
|
||||
lowercase: false # Whether to lowercase before computing BLEU. (default: False)
|
||||
weights: null # Optional custom weights for n-gram orders. (default: null)
|
||||
smooth: false # Whether to apply smoothing to BLEU calculation. (default: False)
|
||||
check_cuts_for_bleu_tokenizers: false # Enable per-sample tokenizer selection. (See below for more details.) (default: False)
|
||||
log_prediction: true # Whether to log sample predictions. (default: True)
|
||||
batch_dim_index: 0 # Index of batch dimension in prediction tensors output. Set to 1 for RNNT models. (default: 0)
|
||||
|
||||
BLEU score relies on TorchMetrics' SacreBLEU implementation and supports all SacreBLEU tokenization options. Valid strings may be passed to ``bleu_tokenizer`` parameter to configure base tokenizer behavior during BLEU calculation. Available options are:
|
||||
|
||||
* ``"13a"`` - Default WMT tokenizer (mteval-v13a script compatible)
|
||||
* ``"none"`` - No tokenization applied
|
||||
* ``"intl"`` - International tokenization (mteval-v14 script compatible)
|
||||
* ``"char"`` - Character-level tokenization (language-agnostic)
|
||||
* ``"zh"`` - Chinese tokenization (separates Chinese characters, uses 13a for non-Chinese)
|
||||
* ``"ja-mecab"`` - Japanese tokenization using MeCab morphological analyzer
|
||||
* ``"ko-mecab"`` - Korean tokenization using MeCab-ko morphological analyzer
|
||||
* ``"flores101"`` / ``"flores200"`` - SentencePiece models from Flores datasets
|
||||
|
||||
**Note** Due to their unique orthographies, it is highly recommended to use ``zh``, ``ja-mecab``, or ``ko-mecab`` tokenizers for Chinese, Japanese, and Korean target evaluations, respectively. For more information on SacreBLEU tokenizers, please refer to the `SacreBLEU documentation <https://github.com/mjpost/sacrebleu>`__.
|
||||
|
||||
**Dynamic Tokenizer Selection**
|
||||
|
||||
In multilingual training scenarios, it is somtimes desireable to configure the BLEU tokenizer per sample to avoid sub-optimal parsing (e.g. tokenizing Chinese characters as English words). This can be toggled with ``check_cuts_for_bleu_tokenizers: true``. When enabled with Lhotse dataloading, BLEU will check individual ``cuts`` in a batch's Lhotse ``CutSet`` for the ``bleu_tokenizer`` attribute. If found, the tokenizer will be used for that sample. If not, the default ``bleu_tokenizer`` from config will be used.
|
||||
|
||||
MultiTask Metrics
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Multiple metrics can be configured simultaneously using a ``MultiTaskMetric`` config. This is done by specifying in the config each desired metric as a DictConfig entry with a custom key name and ``_target_`` path, along with desired properties. All properties specified within a metric config will be passed only to the metric class. All properties specified at the top level of the config will be inherited by all submetrics.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
multitask_metrics_config:
|
||||
log_prediction: true
|
||||
metrics:
|
||||
wer:
|
||||
_target_: nemo.collections.asr.metrics.wer.WER
|
||||
use_cer: true
|
||||
constraint: ".task==transcribe" # Only apply WER to transcription samples
|
||||
bleu:
|
||||
_target_: nemo.collections.asr.metrics.bleu.BLEU
|
||||
bleu_tokenizer: flores101
|
||||
lowercase: true
|
||||
check_cuts_for_bleu_tokenizers: true
|
||||
constraint: ".task==translate" # Only apply BLEU to translation samples
|
||||
|
||||
**Metric Constraints**
|
||||
|
||||
Each metric within ``MultiTaskMetric`` can be configured with an optional boolean ``constraint`` pattern that filters batch samples before metric computation. This allows validation to be limited to only applicable samples in a batch (e.g. only apply WER to transcription samples, only apply BLEU to translation samples). Constraint patterns match against property keywords in the batch's Lhotse CutSet.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
multitask_metrics_config:
|
||||
metrics:
|
||||
pnc_wer:
|
||||
_target_: nemo.collections.asr.metrics.wer.WER
|
||||
constraint: ".task==transcribe and .pnc==true"
|
||||
|
||||
multilingual_bleu:
|
||||
_target_: nemo.collections.asr.metrics.bleu.BLEU
|
||||
constraint: "(.source_lang!=.target_lang) or .task==translate"
|
||||
|
||||
**Note:** MultiTaskMetric is currently only supported for AED multitask models.
|
||||
|
||||
|
||||
Tokenizer Configurations
|
||||
------------------------
|
||||
|
||||
Some models utilize sub-word encoding via an external tokenizer instead of explicitly defining their vocabulary.
|
||||
|
||||
For such models, a ``tokenizer`` section is added to the model config. ASR models currently support two types of
|
||||
custom tokenizers:
|
||||
|
||||
- Google Sentencepiece tokenizers (tokenizer type of ``bpe`` in the config)
|
||||
- HuggingFace WordPiece tokenizers (tokenizer type of ``wpe`` in the config)
|
||||
- Aggregate tokenizers ((tokenizer type of ``agg`` in the config), see below)
|
||||
|
||||
In order to build custom tokenizers, refer to the ``ASR_with_Subword_Tokenization`` notebook available in the
|
||||
ASR tutorials directory.
|
||||
|
||||
The following example sets up a ``SentencePiece Tokenizer`` at a path specified by the user:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
...
|
||||
tokenizer:
|
||||
dir: "<path to the directory that contains the custom tokenizer files>"
|
||||
type: "bpe" # can be "bpe" or "wpe"
|
||||
|
||||
The Aggregate (``agg``) tokenizer feature makes it possible to combine tokenizers in order to train multilingual
|
||||
models. The config file would look like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
...
|
||||
tokenizer:
|
||||
type: "agg" # aggregate tokenizer
|
||||
langs:
|
||||
en:
|
||||
dir: "<path to the directory that contains the tokenizer files>"
|
||||
type: "bpe" # can be "bpe" or "wpe"
|
||||
es:
|
||||
dir: "<path to the directory that contains the tokenizer files>"
|
||||
type: "bpe" # can be "bpe" or "wpe"
|
||||
|
||||
In the above config file, each language is associated with its own pre-trained tokenizer, which gets assigned
|
||||
a token id range in the order the tokenizers are listed. To train a multilingual model, one needs to populate the
|
||||
``lang`` field in the manifest file, allowing the routing of each sample to the correct tokenizer. At inference time,
|
||||
the routing is done based on the inferred token id range.
|
||||
|
||||
For models which utilize sub-word tokenization, we share the decoder module (``ConvASRDecoder``) with character tokenization models.
|
||||
All parameters are shared, but for models which utilize sub-word encoding, there are minor differences when setting up the config. For
|
||||
such models, the tokenizer is utilized to fill in the missing information when the model is constructed automatically.
|
||||
|
||||
For example, a decoder config corresponding to a sub-word tokenization model should look similar to the following:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
...
|
||||
decoder:
|
||||
_target_: nemo.collections.asr.modules.ConvASRDecoder
|
||||
feat_in: *enc_final
|
||||
num_classes: -1 # filled with vocabulary size from tokenizer at runtime
|
||||
vocabulary: [] # filled with vocabulary from tokenizer at runtime
|
||||
|
||||
|
||||
On-the-fly Code Switching
|
||||
-------------------------
|
||||
|
||||
Nemo supports creating code-switched synthetic utterances on-the-fly during training/validation/testing. This allows you to create ASR models which
|
||||
support intra-utterance code switching. If you have Nemo formatted audio data on disk (either JSON manifests or tarred audio data), you
|
||||
can easily mix as many of these audio sources together as desired by adding some extra parameters to your `train_ds`, `validation_ds`, and `test_ds`.
|
||||
|
||||
Please note that this allows you to mix any kind of audio sources together to create synthetic utterances which sample from all sources. The most
|
||||
common use case for this is blending different languages together to create a multilingual code-switched model, but you can also blend
|
||||
together different audio sources from the same languages (or language families), to create noise robust data, or mix fast and slow speech from the
|
||||
same language.
|
||||
|
||||
For multilingual code-switched models, we recommend using AggTokenizer for your Tokenizer if mixing different languages.
|
||||
|
||||
The following example shows how to mix 3 different languages: English (en), German (de), and Japanese (ja) added to the `train_ds` model block, however
|
||||
you can add similar logic to your `validation_ds` and `test_ds` blocks for on-the-fly code-switched validation and test data too. This example mixes
|
||||
together 3 languages, but you can use as many as you want. However, be advised that the more languages you add, the higher your `min_duration` and `max_duration`
|
||||
need to be set to ensure all languages are sampled into each synthetic utterance, and setting these hyperparameters higher will use more VRAM per mini-batch during
|
||||
training and evaluation.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
train_ds:
|
||||
manifest_filepath: [/path/to/EN/tarred_manifest.json, /path/to/DE/tarred_manifest.json, /path/to/JA/tarred_manifest.json]
|
||||
tarred_audio_filepaths: ['/path/to/EN/tars/audio__OP_0..511_CL_.tar', '/path/to/DE/tars/audio__OP_0..1023_CL_.tar', '/path/to/JA/tars/audio__OP_0..2047_CL_.tar']
|
||||
is_code_switched: true
|
||||
is_tarred: true
|
||||
shuffle: true
|
||||
code_switched: # add this block for code-switching
|
||||
min_duration: 12 # the minimum number of seconds for each synthetic code-switched utterance
|
||||
max_duration: 20 # the maximum number of seconds for each synthetic code-switched utterance
|
||||
min_monolingual: 0.3 # the minimum percentage of utterances which will be pure monolingual (0.3 = 30%)
|
||||
probs: [0.25, 0.5, 0.25] # the probability to sample each language (matches order of `language` above) if not provided, assumes uniform distribution
|
||||
force_monochannel: true # if your source data is multi-channel, then setting this to True will force the synthetic utterances to be mono-channel
|
||||
sampling_scales: 0.75 # allows you to down/up sample individual languages. Can set this as an array for individual languages, or a scalar for all languages
|
||||
seed: 123 # add a seed for replicability in future runs (highly useful for `validation_ds` and `test_ds`)
|
||||
|
||||
|
||||
Model Architecture Configurations
|
||||
---------------------------------
|
||||
|
||||
Each configuration file should describe the model architecture being used for the experiment. Models in the NeMo ASR collection need
|
||||
an ``encoder`` section and a ``decoder`` section, with the ``_target_`` field specifying the module to use for each.
|
||||
|
||||
Here is the list of the parameters in the model section which are shared among most of the ASR models:
|
||||
|
||||
+-------------------------+------------------+---------------------------------------------------------------------------------------------------------------+---------------------------------+
|
||||
| **Parameter** | **Datatype** | **Description** | **Supported Values** |
|
||||
+=========================+==================+===============================================================================================================+=================================+
|
||||
| :code:`log_prediction` | bool | Whether a random sample should be printed in the output at each step, along with its predicted transcript. | |
|
||||
+-------------------------+------------------+---------------------------------------------------------------------------------------------------------------+---------------------------------+
|
||||
| :code:`ctc_reduction` | string | Specifies the reduction type of CTC loss. Defaults to ``mean_batch`` which would take the average over the | :code:`none`, |
|
||||
| | | batch after taking the average over the length of each sample. | :code:`mean_batch` |
|
||||
| | | | :code:`mean`, :code:`sum` |
|
||||
+-------------------------+------------------+---------------------------------------------------------------------------------------------------------------+---------------------------------+
|
||||
|
||||
For more information about the ASR models, refer to the :doc:`Featured Models <./featured_models>` section.
|
||||
|
||||
|
||||
.. _asr-configs-conformer-ctc:
|
||||
|
||||
Conformer-CTC
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The config files for Conformer-CTC model contain character-based encoding and sub-word encoding at
|
||||
``<NeMo_git_root>/examples/asr/conf/conformer/conformer_ctc_char.yaml`` and ``<NeMo_git_root>/examples/asr/conf/conformer/conformer_ctc_bpe.yaml``
|
||||
respectively. Some components of the configs of :ref:`Conformer-CTC <Conformer-CTC_model>` include the following datasets:
|
||||
|
||||
* ``train_ds``, ``validation_ds``, and ``test_ds``
|
||||
* opimizer (``optim``)
|
||||
* augmentation (``spec_augment``)
|
||||
* ``decoder``
|
||||
* ``trainer``
|
||||
* ``exp_manager``
|
||||
|
||||
There should be a tokenizer section where you can
|
||||
specify the tokenizer if you want to use sub-word encoding instead of character-based encoding.
|
||||
|
||||
|
||||
The encoder section includes the details about the Conformer-CTC encoder architecture. You may find more information in the
|
||||
config files and also :ref:`nemo.collections.asr.modules.ConformerEncoder <conformer-encoder-api>`.
|
||||
|
||||
|
||||
Conformer-Transducer
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Please refer to the model page of :ref:`Conformer-Transducer <Conformer-Transducer_model>` for more information on this model.
|
||||
|
||||
|
||||
Transducer Configurations
|
||||
-------------------------
|
||||
|
||||
All CTC-based ASR model configs can be modified to support Transducer loss training. Below, we discuss the modifications required in the config to enable Transducer training. All modifications are made to the ``model`` config.
|
||||
|
||||
Model Defaults
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
It is a subsection to the model config representing the default values shared across the entire model represented as ``model.model_defaults``.
|
||||
|
||||
There are three values that are primary components of a transducer model. They are :
|
||||
|
||||
* ``enc_hidden``: The hidden dimension of the final layer of the Encoder network.
|
||||
* ``pred_hidden``: The hidden dimension of the final layer of the Prediction network.
|
||||
* ``joint_hidden``: The hidden dimension of the intermediate layer of the Joint network.
|
||||
|
||||
One can access these values inside the config by using OmegaConf interpolation as follows :
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
...
|
||||
model_defaults:
|
||||
enc_hidden: 256
|
||||
pred_hidden: 256
|
||||
joint_hidden: 256
|
||||
...
|
||||
decoder:
|
||||
...
|
||||
prednet:
|
||||
pred_hidden: ${model.model_defaults.pred_hidden}
|
||||
|
||||
Acoustic Encoder Model
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The transducer model is comprised of three models combined. One of these models is the Acoustic (encoder) model. We should be able to drop in any CTC Acoustic model config into this section of the transducer config.
|
||||
|
||||
The only condition that needs to be met is that **the final layer of the acoustic model must have the hidden dimension defined in ``model_defaults.enc_hidden``**.
|
||||
|
||||
Decoder / Prediction Model
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Prediction model is generally an autoregressive, causal model that consumes text tokens and returns embeddings that will be used by the Joint model. The base config for an LSTM based Prediction network can be found in the ``decoder`` section of Transducer architectures. For further information refer to the ``Intro to Transducers`` tutorial in the ASR tutorial section.
|
||||
|
||||
**This config can be copy-pasted into any custom transducer model with no modification.**
|
||||
|
||||
Let us discuss some of the important arguments:
|
||||
|
||||
* ``blank_as_pad``: In ordinary transducer models, the embedding matrix does not acknowledge the ``Transducer Blank`` token (similar to CTC Blank). However, this causes the autoregressive loop to be more complicated and less efficient. Instead, this flag which is set by default, will add the ``Transducer Blank`` token to the embedding matrix - and use it as a pad value (zeros tensor). This enables more efficient inference without harming training. For further information refer to the ``Intro to Transducers`` tutorial in the ASR tutorial section.
|
||||
|
||||
* ``prednet.pred_hidden``: The hidden dimension of the LSTM and the output dimension of the Prediction network.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
decoder:
|
||||
_target_: nemo.collections.asr.modules.RNNTDecoder
|
||||
normalization_mode: null
|
||||
random_state_sampling: false
|
||||
blank_as_pad: true
|
||||
|
||||
prednet:
|
||||
pred_hidden: ${model.model_defaults.pred_hidden}
|
||||
pred_rnn_layers: 1
|
||||
t_max: null
|
||||
dropout: 0.0
|
||||
|
||||
Joint Model
|
||||
~~~~~~~~~~~
|
||||
|
||||
The Joint model is a simple feed-forward Multi-Layer Perceptron network. This MLP accepts the output of the Acoustic and Prediction models and computes a joint probability distribution over the entire vocabulary space. The base config for the Joint network can be found in the ``joint`` section of Transducer architectures. For further information refer to the ``Intro to Transducers`` tutorial in the ASR tutorial section.
|
||||
|
||||
**This config can be copy-pasted into any custom transducer model with no modification.**
|
||||
|
||||
The Joint model config has several essential components which we discuss below :
|
||||
|
||||
* ``log_softmax``: Due to the cost of computing softmax on such large tensors, the Numba CUDA implementation of RNNT loss will implicitly compute the log softmax when called (so its inputs should be logits). The CPU version of the loss doesn't face such memory issues so it requires log-probabilities instead. Since the behaviour is different for CPU-GPU, the ``None`` value will automatically switch behaviour dependent on whether the input tensor is on a CPU or GPU device.
|
||||
|
||||
* ``preserve_memory``: This flag will call ``torch.cuda.empty_cache()`` at certain critical sections when computing the Joint tensor. While this operation might allow us to preserve some memory, the empty_cache() operation is tremendously slow and will slow down training by an order of magnitude or more. It is available to use but not recommended.
|
||||
|
||||
* ``fuse_loss_wer``: This flag performs "batch splitting" and then "fused loss + metric" calculation. It will be discussed in detail in the next tutorial that will train a Transducer model.
|
||||
|
||||
* ``fused_batch_size``: When the above flag is set to True, the model will have two distinct "batch sizes". The batch size provided in the three data loader configs (``model.*_ds.batch_size``) will now be the ``Acoustic model`` batch size, whereas the ``fused_batch_size`` will be the batch size of the ``Prediction model``, the ``Joint model``, the ``transducer loss`` module and the ``decoding`` module.
|
||||
|
||||
* ``jointnet.joint_hidden``: The hidden intermediate dimension of the joint network.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
joint:
|
||||
_target_: nemo.collections.asr.modules.RNNTJoint
|
||||
log_softmax: null # sets it according to cpu/gpu device
|
||||
|
||||
# fused mode
|
||||
fuse_loss_wer: false
|
||||
fused_batch_size: 16
|
||||
|
||||
jointnet:
|
||||
joint_hidden: ${model.model_defaults.joint_hidden}
|
||||
activation: "relu"
|
||||
dropout: 0.0
|
||||
|
||||
Sampled Softmax Joint Model
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are some situations where a large vocabulary with a Transducer model - such as for multilingual models with a large
|
||||
number of languages. In this setting, we need to consider the cost of memory of training Transducer networks which does
|
||||
not allow large vocabulary.
|
||||
|
||||
For such cases, one can instead utilize the ``SampledRNNTJoint`` module instead of the usual ``RNNTJoint`` module, in order
|
||||
to compute the loss using a sampled subset of the vocabulary rather than the full vocabulary file.
|
||||
|
||||
It adds only one additional parameter :
|
||||
|
||||
* ``n_samples``: Specifies the minimum number of tokens to sample from the vocabulary space,
|
||||
excluding the RNNT blank token. If a given value is larger than the entire vocabulary size,
|
||||
then the full vocabulary will be used.
|
||||
|
||||
The only difference in config required is to replace ``nemo.collections.asr.modules.RNNTJoint`` with ``nemo.collections.asr.modules.SampledRNNTJoint``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
joint:
|
||||
_target_: nemo.collections.asr.modules.SampledRNNTJoint
|
||||
n_samples: 500
|
||||
... # All other arguments from RNNTJoint can be used after this.
|
||||
|
||||
|
||||
Effect of Batch Splitting / Fused Batch step
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following information below explain why memory is an issue when training Transducer models and how NeMo tackles the issue with its Fused Batch step. The material can be read for a thorough understanding, otherwise, it can be skipped. You can also follow these steps in the "ASR_with_Transducers" tutorial.
|
||||
|
||||
**Diving deeper into the memory costs of Transducer Joint**
|
||||
|
||||
One of the significant limitations of Transducers is the exorbitant memory cost of computing the Joint module. The Joint module is comprised of two steps.
|
||||
|
||||
1) Projecting the Acoustic and Transcription feature dimensions to some standard hidden dimension (specified by model.model_defaults.joint_hidden)
|
||||
|
||||
2) Projecting this intermediate hidden dimension to the final vocabulary space to obtain the transcription.
|
||||
|
||||
Take the following example.
|
||||
|
||||
BS=32 ; T (after 2x stride) = 800, U (with character encoding) = 400-450 tokens, Vocabulary size V = 28 (26 alphabet chars, space and apostrophe). Let the hidden dimension of the Joint model be 640 (Most Google Transducer papers use hidden dimension of 640).
|
||||
|
||||
* Memory (Hidden, gb) = 32 x 800 x 450 x 640 x 4 = 29.49 gigabytes (4 bytes per float).
|
||||
|
||||
* Memory (Joint, gb) = 32 x 800 x 450 x 28 x 4 = 1.290 gigabytes (4 bytes per float)
|
||||
|
||||
**NOTE**: This is just for the forward pass! We need to double this memory to store gradients! This much memory is also just for the Joint model **alone**. Far more memory is required for the Prediction model as well as the large Acoustic model itself and its gradients!
|
||||
|
||||
Even with mixed precision, that's ~30 GB of GPU RAM for just 1 part of the network + its gradients.
|
||||
|
||||
Effect of Fused Batch Step
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The fundamental problem is that the joint tensor grows in size when ``[T x U]`` grows in size. This growth in memory cost is due to many reasons - either by model construction (downsampling) or the choice of dataset preprocessing (character tokenization vs. sub-word tokenization).
|
||||
|
||||
Another dimension that NeMo can control is **batch**. Due to how we batch our samples, small and large samples all get clumped together into a single batch. So even though the individual samples are not all as long as the maximum length of T and U in that batch, when a batch of such samples is constructed, it will consume a significant amount of memory for the sake of compute efficiency.
|
||||
|
||||
So as is always the case - **trade-off compute speed for memory savings**.
|
||||
|
||||
The fused operation goes as follows :
|
||||
|
||||
1) Forward the entire acoustic model in a single pass. (Use global batch size here for acoustic model - found in ``model.*_ds.batch_size``)
|
||||
|
||||
2) Split the Acoustic Model's logits by ``fused_batch_size`` and loop over these sub-batches.
|
||||
|
||||
3) Construct a sub-batch of same ``fused_batch_size`` for the Prediction model. Now the target sequence length is U_sub-batch < U.
|
||||
|
||||
4) Feed this U_sub-batch into the Joint model, along with a sub-batch from the Acoustic model (with T_sub-batch < T). Remember, we only have to slice off a part of the acoustic model here since we have the full batch of samples (B, T, D) from the acoustic model.
|
||||
|
||||
5) Performing steps (3) and (4) yields T_sub-batch and U_sub-batch. Perform sub-batch joint step - costing an intermediate (B, T_sub-batch, U_sub-batch, V) in memory.
|
||||
|
||||
6) Compute loss on sub-batch and preserve in a list to be later concatenated.
|
||||
|
||||
7) Compute sub-batch metrics (such as Character / Word Error Rate) using the above Joint tensor and sub-batch of ground truth labels. Preserve the scores to be averaged across the entire batch later.
|
||||
|
||||
8) Delete the sub-batch joint matrix (B, T_sub-batch, U_sub-batch, V). Only gradients from .backward() are preserved now in the computation graph.
|
||||
|
||||
9) Repeat steps (3) - (8) until all sub-batches are consumed.
|
||||
|
||||
10) Cleanup step. Compute full batch WER and log. Concatenate loss list and pass to PTL to compute the equivalent of the original (full batch) Joint step. Delete ancillary objects necessary for sub-batching.
|
||||
|
||||
Transducer Decoding
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Models which have been trained with CTC can transcribe text simply by performing a regular argmax over the output of their decoder. For transducer-based models, the three networks must operate in a synchronized manner in order to transcribe the acoustic features. The base config for the Transducer decoding step can be found in the ``decoding`` section of Transducer architectures. For further information refer to the ``Intro to Transducers`` tutorial in the ASR tutorial section.
|
||||
|
||||
**This config can be copy-pasted into any custom transducer model with no modification.**
|
||||
|
||||
The most important component at the top level is the ``strategy``. It can take one of many values:
|
||||
|
||||
* ``greedy``: This is sample-level greedy decoding. It is generally exceptionally slow as each sample in the batch will be decoded independently. For publications, this should be used alongside batch size of 1 for exact results.
|
||||
|
||||
* ``greedy_batch``: This is the general default and should nearly match the ``greedy`` decoding scores (if the acoustic features are not affected by feature mixing in batch mode). Even for small batch sizes, this strategy is significantly faster than ``greedy``.
|
||||
|
||||
* ``beam``: Runs beam search with the implicit language model of the Prediction model. It will generally be quite slow, and might need some tuning of the beam size to get better transcriptions.
|
||||
|
||||
* ``tsd``: Time synchronous decoding. Please refer to the paper: `Alignment-Length Synchronous Decoding for RNN Transducer <https://ieeexplore.ieee.org/document/9053040>`_ for details on the algorithm implemented. Time synchronous decoding (TSD) execution time grows by the factor T * max_symmetric_expansions. For longer sequences, T is greater and can therefore take a long time for beams to obtain good results. TSD also requires more memory to execute.
|
||||
|
||||
* ``alsd``: Alignment-length synchronous decoding. Please refer to the paper: `Alignment-Length Synchronous Decoding for RNN Transducer <https://ieeexplore.ieee.org/document/9053040>`_ for details on the algorithm implemented. Alignment-length synchronous decoding (ALSD) execution time is faster than TSD, with a growth factor of T + U_max, where U_max is the maximum target length expected during execution. Generally, T + U_max < T * max_symmetric_expansions. However, ALSD beams are non-unique. Therefore it is required to use larger beam sizes to achieve the same (or close to the same) decoding accuracy as TSD. For a given decoding accuracy, it is possible to attain faster decoding via ALSD than TSD.
|
||||
|
||||
* ``maes``: Modified Adaptive Expansion Search Decoding. Please refer to the paper `Accelerating RNN Transducer Inference via Adaptive Expansion Search <https://ieeexplore.ieee.org/document/9250505>`_. Modified Adaptive Synchronous Decoding (mAES) execution time is adaptive w.r.t the number of expansions (for tokens) required per timestep. The number of expansions can usually be constrained to 1 or 2, and in most cases 2 is sufficient. This beam search technique can possibly obtain superior WER while sacrificing some evaluation time.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
decoding:
|
||||
strategy: "greedy_batch"
|
||||
|
||||
# preserve decoding alignments
|
||||
preserve_alignments: false
|
||||
|
||||
# Overrides the fused batch size after training.
|
||||
# Setting it to -1 will process whole batch at once when combined with `greedy_batch` decoding strategy
|
||||
fused_batch_size: -1
|
||||
|
||||
# greedy strategy config
|
||||
greedy:
|
||||
max_symbols: 10
|
||||
|
||||
# beam strategy config
|
||||
beam:
|
||||
beam_size: 2
|
||||
score_norm: true
|
||||
softmax_temperature: 1.0 # scale the logits by some temperature prior to softmax
|
||||
tsd_max_sym_exp: 10 # for Time Synchronous Decoding, int > 0
|
||||
alsd_max_target_len: 5.0 # for Alignment-Length Synchronous Decoding, float > 1.0
|
||||
maes_num_steps: 2 # for modified Adaptive Expansion Search, int > 0
|
||||
maes_prefix_alpha: 1 # for modified Adaptive Expansion Search, int > 0
|
||||
maes_expansion_beta: 2 # for modified Adaptive Expansion Search, int >= 0
|
||||
maes_expansion_gamma: 2.3 # for modified Adaptive Expansion Search, float >= 0
|
||||
|
||||
Transducer Loss
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
This section configures the type of Transducer loss itself, along with possible sub-sections. By default, an optimized implementation of Transducer loss will be used which depends on Numba for CUDA acceleration. The base config for the Transducer loss section can be found in the ``loss`` section of Transducer architectures. For further information refer to the ``Intro to Transducers`` tutorial in the ASR tutorial section.
|
||||
|
||||
**This config can be copy-pasted into any custom transducer model with no modification.**
|
||||
|
||||
The loss config is based on a resolver pattern and can be used as follows:
|
||||
|
||||
1) ``loss_name``: ``default`` is generally a good option. Will select one of the available resolved losses and match the kwargs from a sub-configs passed via explicit ``{loss_name}_kwargs`` sub-config.
|
||||
|
||||
2) ``{loss_name}_kwargs``: This sub-config is passed to the resolved loss above and can be used to configure the resolved loss.
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
loss:
|
||||
loss_name: "default"
|
||||
warprnnt_numba_kwargs:
|
||||
fastemit_lambda: 0.0
|
||||
|
||||
FastEmit Regularization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
FastEmit Regularization is supported for the default Numba based WarpRNNT loss. Recently proposed regularization approach - `FastEmit: Low-latency Streaming ASR with Sequence-level Emission Regularization <https://arxiv.org/abs/2010.11148>`_ allows us near-direct control over the latency of transducer models.
|
||||
|
||||
Refer to the above paper for results and recommendations of ``fastemit_lambda``.
|
||||
|
||||
For decoding customization (confidence scores, CUDA graphs, language models, word boosting), see :doc:`ASR Language Modeling and Customization <./asr_language_modeling_and_customization>`.
|
||||
|
||||
|
||||
InterCTC Config
|
||||
---------------
|
||||
|
||||
All CTC-based models also support `InterCTC loss <https://arxiv.org/abs/2102.03216>`_. To use it, you need to specify
|
||||
2 parameters as in example below
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
# ...
|
||||
interctc:
|
||||
loss_weights: [0.3]
|
||||
apply_at_layers: [8]
|
||||
|
||||
which can be used to reproduce the default setup from the paper (assuming the total number of layers is 18).
|
||||
You can also specify multiple CTC losses from different layers, e.g., to get 2 losses from layers 3 and 8 with
|
||||
weights 0.1 and 0.3, specify:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
# ...
|
||||
interctc:
|
||||
loss_weights: [0.1, 0.3]
|
||||
apply_at_layers: [3, 8]
|
||||
|
||||
Note that the final-layer CTC loss weight is automatically computed to normalize
|
||||
all weight to 1 (0.6 in the example above).
|
||||
|
||||
|
||||
Stochastic Depth Config
|
||||
-----------------------
|
||||
|
||||
`Stochastic Depth <https://arxiv.org/abs/2102.03216>`_ is a useful technique for regularizing ASR model training.
|
||||
Currently it's only supported for :ref:`nemo.collections.asr.modules.ConformerEncoder <conformer-encoder-api>`. To
|
||||
use it, specify the following parameters in the encoder config file to reproduce the default setup from the paper:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
# ...
|
||||
encoder:
|
||||
# ...
|
||||
stochastic_depth_drop_prob: 0.3
|
||||
stochastic_depth_mode: linear # linear or uniform
|
||||
stochastic_depth_start_layer: 1
|
||||
|
||||
See :ref:`documentation of ConformerEncoder <conformer-encoder-api>` for more details. Note that stochastic depth
|
||||
is supported for both CTC and Transducer model variations (or any other kind of model/loss that's using
|
||||
conformer as encoder).
|
||||
|
||||
|
||||
.. _Hybrid-Transducer-CTC-Prompt_model__Config:
|
||||
|
||||
Hybrid-Transducer-CTC with Prompt Conditioning Configuration
|
||||
------------------------------------------------------------
|
||||
|
||||
The :ref:`Hybrid-Transducer-CTC model with prompt conditioning <Hybrid-Transducer-CTC-Prompt_model>`
|
||||
(``EncDecHybridRNNTCTCBPEModelWithPrompt``) extends the base hybrid model to support prompt-based multilingual ASR/AST.
|
||||
|
||||
**Key Configuration Parameters:**
|
||||
|
||||
The model introduces several prompt-specific configuration parameters in the ``model_defaults`` section:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
model_defaults:
|
||||
# Prompt Feature Configuration
|
||||
initialize_prompt_feature: true # Enable prompt conditioning
|
||||
num_prompts: 128 # Number of supported prompt categories
|
||||
prompt_dictionary: { # Mapping from identifiers to prompt indices
|
||||
# Language prompts (0-99)
|
||||
'en-US': 0,
|
||||
'de-DE': 1,
|
||||
'fr-FR': 2,
|
||||
'es-ES': 3,
|
||||
# Task/domain prompts (100-127)
|
||||
'pnc': 100, # Punctuation mode
|
||||
'no_pnc': 101, # No punctuation mode
|
||||
}
|
||||
|
||||
**Dataset Configuration:**
|
||||
|
||||
The model requires training data with prompt annotations when using Lhotse datasets:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
train_ds:
|
||||
use_lhotse: true
|
||||
initialize_prompt_feature: true
|
||||
prompt_field: "target_lang" # Field name for prompt extraction
|
||||
prompt_dictionary: ${model.model_defaults.prompt_dictionary}
|
||||
num_prompts: ${model.model_defaults.num_prompts}
|
||||
|
||||
validation_ds:
|
||||
use_lhotse: true
|
||||
initialize_prompt_feature: true
|
||||
prompt_field: "target_lang"
|
||||
prompt_dictionary: ${model.model_defaults.prompt_dictionary}
|
||||
num_prompts: ${model.model_defaults.num_prompts}
|
||||
|
||||
**Manifest Format:**
|
||||
|
||||
Training manifests should include prompt information:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"audio_filepath": "/path/to/audio.wav",
|
||||
"text": "transcription text",
|
||||
"duration": 10.5,
|
||||
"target_lang": "en-US"
|
||||
}
|
||||
|
||||
**Example Configuration:**
|
||||
|
||||
A complete example configuration can be found at:
|
||||
``<NeMo_git_root>/examples/asr/conf/fastconformer/hybrid_transducer_ctc/fastconformer_hybrid_transducer_ctc_bpe_prompt.yaml``
|
||||
|
||||
**Training Command:**
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python <NeMo_git_root>/examples/asr/asr_hybrid_transducer_ctc/speech_to_text_hybrid_rnnt_ctc_bpe_prompt.py \
|
||||
--config-path=<NeMo_git_root>/examples/asr/conf/fastconformer/hybrid_transducer_ctc/ \
|
||||
--config-name=fastconformer_hybrid_transducer_ctc_bpe_prompt.yaml \
|
||||
model.train_ds.manifest_filepath=<path_to_train_manifest> \
|
||||
model.validation_ds.manifest_filepath=<path_to_val_manifest> \
|
||||
model.tokenizer.dir=<path_to_tokenizer> \
|
||||
model.test_ds.manifest_filepath=<path_to_test_manifest>
|
||||
|
||||
|
||||
.. _RNNT-Prompt_model__Config:
|
||||
|
||||
RNN-T with Prompt Conditioning Configuration
|
||||
--------------------------------------------
|
||||
|
||||
The :ref:`RNN-T model with prompt conditioning <RNNT-Prompt_model>`
|
||||
(``EncDecRNNTBPEModelWithPrompt``) is the RNN-T-only counterpart of the hybrid prompt model
|
||||
(no auxiliary CTC head). It targets cache-aware streaming multilingual ASR using the same
|
||||
one-hot language-ID prompt concatenation as the hybrid variant.
|
||||
|
||||
**Key Configuration Parameters:**
|
||||
|
||||
The prompt-specific parameters live in the ``model_defaults`` section, mirroring the hybrid
|
||||
variant:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
model_defaults:
|
||||
# Prompt Feature Configuration
|
||||
initialize_prompt_feature: true # Enable prompt conditioning
|
||||
num_prompts: 128 # Number of supported prompt categories
|
||||
prompt_dictionary: { # Mapping from identifiers to prompt indices
|
||||
'en-US': 0,
|
||||
'de-DE': 1,
|
||||
'fr-FR': 2,
|
||||
'es-ES': 3,
|
||||
# ... additional language codes ...
|
||||
'auto': 127, # Per-sample dynamic language (read from manifest)
|
||||
}
|
||||
|
||||
**Dataset Configuration:**
|
||||
|
||||
The model uses the same index-based Lhotse dataset
|
||||
(``LhotseSpeechToTextBpeDatasetWithPromptIndex``) as the hybrid model:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
model:
|
||||
train_ds:
|
||||
use_lhotse: true
|
||||
initialize_prompt_feature: true
|
||||
prompt_field: "target_lang" # Field name for per-sample prompt extraction
|
||||
prompt_dictionary: ${model.model_defaults.prompt_dictionary}
|
||||
num_prompts: ${model.model_defaults.num_prompts}
|
||||
|
||||
validation_ds:
|
||||
use_lhotse: true
|
||||
initialize_prompt_feature: true
|
||||
prompt_field: "target_lang"
|
||||
prompt_dictionary: ${model.model_defaults.prompt_dictionary}
|
||||
num_prompts: ${model.model_defaults.num_prompts}
|
||||
|
||||
**Manifest Format:**
|
||||
|
||||
Identical to the hybrid model — each entry needs a ``target_lang`` field:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"audio_filepath": "/path/to/audio.wav",
|
||||
"text": "transcription text",
|
||||
"duration": 10.5,
|
||||
"target_lang": "en-US"
|
||||
}
|
||||
|
||||
**Example Configuration:**
|
||||
|
||||
A cache-aware streaming RNN-T prompt config ships at:
|
||||
``<NeMo_git_root>/examples/asr/conf/fastconformer/cache_aware_streaming/fastconformer_transducer_bpe_streaming_prompt.yaml``
|
||||
|
||||
**Training Command:**
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python <NeMo_git_root>/examples/asr/asr_transducer/speech_to_text_rnnt_bpe_prompt.py \
|
||||
--config-path=<NeMo_git_root>/examples/asr/conf/fastconformer/cache_aware_streaming/ \
|
||||
--config-name=fastconformer_transducer_bpe_streaming_prompt.yaml \
|
||||
model.train_ds.manifest_filepath=<path_to_train_manifest> \
|
||||
model.validation_ds.manifest_filepath=<path_to_val_manifest> \
|
||||
model.tokenizer.dir=<path_to_tokenizer> \
|
||||
model.test_ds.manifest_filepath=<path_to_test_manifest>
|
||||
|
||||
**Streaming Inference:**
|
||||
|
||||
The standard cache-aware streaming inference script accepts ``target_lang`` (and the optional
|
||||
``strip_lang_tags`` / ``lang_tag_pattern`` flags) for prompt-conditioned models:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python <NeMo_git_root>/examples/asr/asr_cache_aware_streaming/speech_to_text_cache_aware_streaming_infer.py \
|
||||
model_path=<path_to_nemo_checkpoint> \
|
||||
dataset_manifest=<path_to_manifest> \
|
||||
target_lang=<en-US|auto|...> \
|
||||
strip_lang_tags=true
|
||||
@@ -0,0 +1,2 @@
|
||||
Model Name,Model Base Class,Model Card
|
||||
asrlm_en_transformer_large_ls,TransformerLMModel,"https://ngc.nvidia.com/catalog/models/nvidia:nemo:asrlm_en_transformer_large_ls"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user