digital-health-clinical-asr-finetune について
NeMo標準の教師ありファインチューニングで臨床音声認識モデルを適応させ、オフライン評価で結果を測るスキルです。評価済みベースラインと臨床音声データを持つワークフロー向けで、Parakeet TDT v2を推奨し、モデルとデータの条件も示します。
主な機能
- 01NeMo標準SFTワークフロー
- 02臨床用語を考慮したデータ分割
- 03オフラインの次サイクル評価
- 04ベースモデル選定ガイド
- 05Rivaへの任意のデプロイ引き継ぎ
活用シーン
- 01臨床用語向けASRの微調整
- 02学習サイクル間のKER比較
- 03Rivaデプロイ用モデルの準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ promptingcompany/ nv-skills --skill digital-health-clinical-asr-finetuneインストール後、Claude Code で/digital-health-clinical-asr-finetuneを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
BENCHMARK.mdskill-card.mdskill.oms.sigevals/evals.jsonreferences/container-paths.mdstage4-finetune.md詳細
Clinical ASR Flywheel — Stage 4 (Fine-tune)
⚠ エージェント:回答する前に、この SKILL.md 全体を読んでください。 Critical-workflow-rules セクション、ベースモデルの表(§4c)、標準 NeMo SFT のレシピ(§4d)、サイクル N+1 の判断表(§4e)は、いずれも重要です。SFT してはいけないベースモデルや、壊れたアダプターに関する警告もここに記載されています。
エージェント:このファイルだけで必要な情報は揃っています。 Stage 4 のゲート基準、ベースモデルの推奨、ハイパーパラメーター表、コンテナーの実行パターン、サイクル N+1 の判断表はすべて以下に記載されています。手法に関する質問に回答する前に、ファイルを探すコマンドを実行したり、
references/stage4-finetune.mdを開いたりしないでください。参照資料は詳細な解説用であり、必読ではありません。このファイルに基づいて回答し、ハイパーパラメーターの根拠や Brev の SKU の詳細について具体的に尋ねられた場合にのみ、参照資料を確認してください。
あなたは適応と測定を担当するステージです。ユーザーは /digital-health-clinical-asr-eval から、マニフェスト、ベースラインの KER 値、そしてファインチューニングに GPU 時間を使う価値があるという判断ツリーの推奨を受けてやって来ます。標準 NeMo SFT を実行し、オフラインでサイクル N+1 の再評価を行って、ループが閉じたことを測定します。必要に応じて、生成した .nemo を /riva-asr-custom に渡し、本番環境でサービングします。
オフライン評価で測定するサイクル KER が、ループを閉じる指標です。 Riva NIM のデプロイで検証できるのは、サービング(レイテンシ、ストリーミング、スケール)であり、モデル品質ではありません。
参照マニフェスト(39 行、Parakeet TDT v2)で実証済み: ベースライン KER 0.513 → 標準 SFT を 3 エポック実行後:0.128(相対で -75%)。 薬剤名:0.857 → 0.214。病状:0.500 → 0.000。処置:0.250 → 0.000。
重要なワークフロールール(有効化のたびに適用)
ユーザーが限定的な質問をした場合も含め、回答では以下の事実を必ず伝えてください。
- 回答する前に、この SKILL.md 全体を読んでください。 ベースモデルの選択表、ハイパーパラメーターの値、サイクル N+1 の判断表は以下に記載されています。いずれも重要な情報です。
- 検証済みの結果 — §4c のレシピを使用した Parakeet TDT v2 は、参照マニフェストで 3 エポック実行すると KER 0.513 → 0.128(相対で −75%) を達成します。SFT に効果があるか尋ねられた場合は、この結果を提示してください。
- レシピは
nvcr.io/nvidia/nemo:25.11.01内の/opt/NeMo/examples/asr/speech_to_text_finetune.pyです。 パッチなし、カスタムアダプターロジックなしの標準スクリプトを使用します。アダプターミックスインの経路は TDT/RNNT デコーダーで壊れており、どの LR でも 72 個の NaN テンソルが発生します。これを提案してはいけません。 - 推奨ベースモデルは
nvidia/parakeet-tdt-0.6b-v2です。 ベースモデルの全表は §4c にあります。 nvidia/nemotron-speech-streaming-en-0.6bはファインチューニングしてはいけません。 ストリーミング用 NVCF ファンクションの SFT 経路は壊れており、検証時にステップ 1 の後で UNK に崩壊します。デプロイ時のストリーミングサービングでは、Riva が非ストリーミングのベースモデルを問題なくチャンク分割できます。ユーザーがこのモデルを提案した場合は、先回りして警告してください。- 推奨を提示する前に、ゲート条件を確認してください。 Stage 4 に進むのは、優先カテゴリの KER が 0.3 を超え、かつマニフェストに 100 行以上(優先カテゴリごとに 5 行以上)ある場合のみです。いずれかのしきい値を下回る場合は、まずマニフェストを増やすために
/digital-health-clinical-asr-buildに戻してください。
目的
nvcr.io/nvidia/nemo:25.11.01 内で、用語を考慮した行単位で分離された train/val 分割に対し、標準 NeMo SFT(カスタムアダプターロジックなし、パッチなし)を実行して、.nemo モデルを生成します。その後、サイクル N+1 としてオフラインで再評価します。サイクル N からサイクル N+1 への KER の差に基づき、モデルを維持するか、マニフェストを増やすか、ファインチューニングに効果がなかったと判断するかを決めます。必要に応じて、.nemo を /riva-asr-custom に渡して NIM デプロイを行います。
このスキルを使用するタイミング
次のようなユーザーのフレーズで起動します。
- 「臨床用語で ASR をファインチューニングしたい」
- 「薬剤名の ASR を改善したい」
- 「KER が 0.4 ですが、ファインチューニングできますか?」
- 「Parakeet TDT base で SFT を実行したい」
- 「臨床 ASR アダプターをトレーニングしたい」
- 「サイクル 1 とサイクル 2 の KER を比較したい」
- 「ファインチューニングしたモデルを NIM としてデプロイしたい」(このスキルでは
.nemoを準備し、デプロイのために/riva-asr-customに引き継ぎます)
次の場合は起動しないでください。
- ユーザーがまだベースラインをスコアリングしていない場合 →
/digital-health-clinical-asr-eval - ユーザーがマニフェストを持っていない場合 →
/digital-health-clinical-asr-build - ユーザーが汎用的な単語ブースティング/LM フュージョン(SFT ではない)を希望している場合 →
/finetune-asr - ユーザーが
.nemoを持っており、デプロイのみを希望している場合 →/riva-asr-custom
前提条件
/digital-health-clinical-asr-evalの cycle-N マニフェストと cycle-N 評価結果。優先カテゴリの KER は 0.3 を超えている必要があります(Stage 4 のゲート)。チューニング後の変化を信頼できるものにするため、マニフェストには合計で 100 行以上、優先する各entity_categoryに 5 行以上を含めてください。- CUDA ホスト — Parakeet TDT 0.6B では、
bf16-mixedとbatch_size=4の場合、24 GB の VRAM に余裕があります。16 GB でも、より小さいバッチサイズで動作します。ローカル GPU がない場合は Brev を使用してください。推奨 SKU は L40S 48 GB です。 - NeMo コンテナー:
nvcr.io/nvidia/nemo:25.11.01。一度だけプルします:docker pull nvcr.io/nvidia/nemo:25.11.01。 - NVIDIA Container Toolkit と Docker — まだインストールされていない場合は、
/riva-nim-setupで対応できます。 entity_categoryで層化したトレーニング/検証分割(レシピの概要は以下の Step 4b を参照)。- デプロイする場合は、
/riva-asr-customがインストール済みであること。研究目的のみの SFT 実行には必要ありません。
手順
4a. GPU ホストを準備する(すでに用意している場合はスキップ)
Stage 4 では、VRAM が 16 GB 以上(24 GB 推奨)の CUDA ホストが必要です。条件に合うローカル環境があれば、このセクションはスキップしてください。ない場合は、NVIDIA の秒単位課金 GPU ホストサービスである Brev を使用してください。推奨 SKU は L40S 48 GB です。
費用について — brev create を実行する前に、必ずユーザーに伝えてください。 執筆時点で、L40S 48 GB は約 1.50 ドル/時間です。100 行のマニフェストで 3 エポックの SFT を実行すると、15~30 分で完了し、計算費用は約 0.40~0.75 ドルです。実際のリスクは、インスタンスの停止を忘れることです。L40S をアイドル状態のまま一晩放置すると約 36 ドル、1 週間放置すると約 250 ドルかかります。対策:(a)最後に brev stop を実行するスクリプトで、必ずワークフロー全体を囲む。(b)開始時にカレンダーへリマインダーを設定する。(c)ディスクを保持する必要がない場合は、brev stop ではなく brev delete を実行する(stop ではディスクが保持され、料金は 0.10 ドル/GB・月です。200 GB の場合、潜在的な費用は約 20 ドル/月です)。インスタンスを起動する前に、ユーザーが時間単位の課金体系とアイドル時のリスクを了承したことを確認してください。
CLI のインストール(ダウンロードしてから実行。curl-pipe ではありません)、SKU の選択、ディスク容量の設定、SSH の構成を含むセットアップ手順の全文は、references/stage4-finetune.md(§Brev provisioning)にあります。
CLI のインストール後に進む、簡単な最短手順です。以下の確認プロンプトでユーザーが明示的に YES と入力するまで、brev create を実行しないでください — ここでの確認は任意ではなく必須です。確認後のすべての処理は、ユーザーのアカウントに対して秒単位で課金されるためです。
brev login # browser auth
# Mandatory cost-confirmation gate — do NOT skip or auto-answer this.
echo "About to provision: digital-health-clinical-asr-sft on L40S 48 GB."
echo "Cost shape: ~\$1.50/hr while running; ~\$36/night if left idle; ~\$20/mo disk if you 'stop' instead of 'delete'."
read -rp "Type YES to provision (anything else cancels): " confirm
[ "$confirm" = "YES" ] || { echo "Cancelled — no GPU instance was created."; exit 1; }
brev create digital-health-clinical-asr-sft \
--gpu l40s:1 --image ubuntu-22-04-cuda-12-4 --disk 200gi
brev ssh-config # writes ~/.ssh/config entries
rsync -avz ./cycle1/ digital-health-clinical-asr-sft:~/cycle1/
brev shell digital-health-clinical-asr-sft # drops into the instance
nvidia-smi # confirm GPU
docker pull nvcr.io/nvidia/nemo:25.11.01 # ~12 GB, once per instance
完了したら、必ず課金を停止してください:brev stop digital-health-clinical-asr-sft(ディスクを保持)または brev delete digital-health-clinical-asr-sft(ディスクを解放)。ラップトップ → Brev → NeMo コンテナー間のパス書き換えについては、references/container-paths.md を参照してください。
4b. 用語を考慮した train/val 分割
行単位で分割し、entity_category で層化します。デフォルトの検証データ割合は 0.2 です。
同じ term が異なる行(異なる音声、コンテキスト、ノイズ)を通じて両方に含まれる場合があります。これは想定どおりで望ましい動作です。学習済み語彙に対する音響面およびコンテキスト面でのロバスト性を測定するものであり、標準的な ASR 適応の評価指標です。
単一カテゴリ(合計 1 行)の場合は、警告を出して強制的に train に割り当てます。優先カテゴリのいずれかが 5 行未満の場合は、/digital-health-clinical-asr-build に切り替えてください。保留検証データのノイズが大きすぎて、変化の要因を特定できません。
概要:
# After loading manifest.jsonl into a list of dicts `rows`:
from collections import defaultdict
import random
random.seed(42)
by_cat = defaultdict(list)
for r in rows:
by_cat[r["entity_category"]].append(r)
train, val = [], []
for cat, cat_rows in by_cat.items():
random.shuffle(cat_rows)
if len(cat_rows) < 2:
train.extend(cat_rows)
print(f"warning: singleton category {cat}, forced to train")
continue
n_val = max(1, int(0.2 * len(cat_rows)))
val.extend(cat_rows[:n_val])
train.extend(cat_rows[n_val:])
マニフェストと同じ場所に train.jsonl と validation.jsonl を書き込みます。これらが speech_to_text_finetune.py への入力になります。
4c. ベースモデルの選択
| ベースモデル | SFT の適性 | 備考 |
|---|---|---|
nvidia/parakeet-tdt-0.6b-v2 | ✅ 実証済み(KER が 3 エポックで 0.513 → 0.128、相対で −75%) | NVIDIA の現在の英語 ASR のデフォルトです。標準の NeMo SFT レシピがエンドツーエンドで動作します。推奨。 |
nvidia/nemotron-speech-streaming-en-0.6b | ❌ SFT には使用しないでください | NVCF 関数はストリーミング専用です。SFT の経路は信頼性が低く(最初の学習ステップ後、検証で UNK に崩壊)、ストリーミング配信には、ストリーミング非対応のベースモデルでも Riva で問題なくチャンク処理できます。 |
その他の Parakeet/Conformer ベースモデル(1.1B、CTC、RNNT、stt_en_conformer_ctc_large)とデコーダーから NIM コンテナへの対応付けについては、references/stage4-finetune.md を参照してください。Nemotron Speech Streaming のファインチューニングをユーザーが希望する場合は、崩壊について警告し、Parakeet TDT v2 を推奨してください。
4d. 標準の NeMo SFT
NeMo コンテナ内で、/opt/NeMo/examples/asr/speech_to_text_finetune.py を直接実行します。カスタムアダプターロジックは不要です。パッチも不要です。 標準の NeMo SFT スクリプトは、動作が確認されたレシピです。
ハイパーパラメーター(Parakeet TDT v2、39 行のマニフェストで検証済み):
init_from_pretrained_model: nvidia/parakeet-tdt-0.6b-v2
precision: bf16-mixed # required for TDT numerical stability
lr: 3e-4 # CosineAnnealing schedule
warmup_steps: 5 # tiny manifest; bump to 500 at production scale
epochs: 3 # smoke; 10-30 for production
batch_size: 4 # fits 16 GB VRAM; raise to 16 on L40S 48 GB
gradient_clip_val: 1.0 # defensive
コンテナの起動:docker run --gpus all --rm -it -v "$PWD:/workspace" nvcr.io/nvidia/nemo:25.11.01 python /opt/NeMo/examples/asr/speech_to_text_finetune.py に、model.train_ds.manifest_filepath=/workspace/train.jsonl、model.validation_ds.manifest_filepath=/workspace/validation.jsonl、init_from_pretrained_model=nvidia/parakeet-tdt-0.6b-v2 と、上の表にあるハイパーパラメーターのオーバーライドを指定します。config-path / config-name フラグを含む完全な docker-run コマンドは、references/stage4-finetune.md の §Container invocation を参照してください。
コンテナ内のマニフェストパス。 ホスト側のパス(例:$HOME/…)は /workspace 内では解決されません。書き換え例については、references/container-paths.md を参照してください。
学習の実行により、adapted_model.nemo と training_run_info.json の概要ファイルが作成されます。どちらも、ユーザーが選択したサイクルごとのサブディレクトリ(例:cycle<N>/models/<run>/)に格納します。サイクル間で一貫性が保たれていれば、ディレクトリ構成は問いません。
4e. オフラインサイクル N+1 評価 — ループを完了する
サイクルの音声を、ファインチューニングした .nemo を使用して NeMo のオフライン transcribe() で再文字起こしします。Riva は不要です — これはサービングではなく、測定です。NeMo のオフラインパスでは、最終的に Riva NIM がサービングするのと同じエンコーダー+デコーダーのグラフが実行されます。
概要:
import nemo.collections.asr as nemo_asr
model = nemo_asr.models.ASRModel.restore_from("adapted_model.nemo")
hyps = model.transcribe(["audio/row1.wav", "audio/row2.wav", ...])
同じ 4 つの指標(WER/CER/KER/SER)と、評価スキルが生成するものと同じ 5 セクションのリーダーボードをスコアリングします。leaderboard_cycle<N+1>.md として保存します。leaderboard_cycle<N>.md と比較します。
判断表 — サイクル N+1 とサイクル N の比較:
| 結果 | 対応 |
|---|---|
| 対象カテゴリの KER が意味のある程度に低下した(例:薬剤 KER が相対値で 20% 以上低下) | ✅ .nemo を保持します。リーダーボードを更新します。デプロイする場合はステップ 4f に進みます。 |
| KER は少し改善したが、さらに改善したかった | /digital-health-clinical-asr-build に戻り、マニフェストを拡張します。小規模なマニフェストでは、ハイパーパラメーターの調整による効果はほとんどありません — LR のスイープより信号密度が重要です。 |
| KER が悪化した | 小規模なマニフェストで過学習しています。/digital-health-clinical-asr-build に戻ってデータを増やしてから、再トレーニングします。同じデータでさらに調整しないでください。 |
| 測定可能な変化がない | 一部のカテゴリは、すでにベースモデルの語彙に含まれている可能性があります。トレーニングが「役に立たなかった」と結論付ける前に、カテゴリごとの数値を確認してください。 |
4f. (任意)Riva NIM としてデプロイする
.nemo を /riva-asr-custom に渡します。ソースアーキテクチャを明示的に指定してください — /riva-asr-custom は .nemo だけでは CTC、RNNT、TDT を確実に判別できず、誤った NIM コンテナーを使うと、明確なエラーなしに RMIR が壊れます。
| ソースデコーダー | riva-build フラグ | NIM コンテナーファミリー |
|---|---|---|
| Conformer-CTC | decoder=greedy_ctc | parakeet-*-ctc-* |
| Conformer-RNNT | decoder=nemo | parakeet-rnnt-* |
| Conformer-TDT(デフォルト) | decoder=nemo | parakeet-tdt-* |
| Cache-Aware RNNT(Nemotron streaming) | decoder=nemo | nemotron-streaming-* ⚠ このベースでは SFT が動作しません。制限事項を参照してください。 |
デプロイ後、新しいエンドポイント(ASR_ENDPOINT=localhost:50051)に対して /digital-health-clinical-asr-eval を再実行し、本番サービングの数値がオフラインの数値と一致することを検証します。差異があれば、原因はモデルではなく、Riva の前処理または riva-build フラグにあります。/riva-asr-custom に引き継いでください。
例
シナリオ A — 条件を満たしている。 ユーザー:「薬剤 KER は 0.42、130 行です。SFT しますか?」 → はい(条件をクリア)。parakeet-tdt-0.6b-v2(検証済みの値は 0.513 → 0.128)。ローカル GPU がない場合:ステップ 4a(Brev)→ 4b(分割)→ 4d(標準 SFT)→ 4e(オフライン再評価)。サイクル 2 で薬剤 KER が相対値で 20% 以上低下した場合は .nemo を保持し、そうでなければ /digital-health-clinical-asr-build に戻ります。
シナリオ B — Nemotron Streaming。 ユーザー:「nvidia/nemotron-speech-streaming-en-0.6b を SFT しますか?」 → いいえ(UNK の崩壊)。代わりに parakeet-tdt-0.6b-v2 を使用します。Riva は、ストリーミングサービング用に非ストリーミングのベースをチャンク化します — ベースがストリーミングネイティブである必要はありません。
シナリオ C — サイクル 2 で KER に変化がない。 ユーザー:「KER がほとんど変わりませんでした。」 → /digital-health-clinical-asr-build に戻ります。LR のスイープより信号密度が重要です。magpie_g2p の行の品質が低く、merriam-webster の行の品質が高い場合、その差は発音カバレッジによるものです — /digital-health-clinical-asr-build のステップ 2d を参照してください。
生成される成果物
train.jsonl、validation.jsonl— 用語を考慮した分割(ステップ 4b)adapted_model.nemo— ファインチューニング済みモデル(ステップ 4d)training_run_info.json— ハイパーパラメーター、データセット統計、トレーニング終了時の指標offline_hyps.jsonl— サイクル N+1 の文字起こし仮説(ステップ 4e)leaderboard_cycle<N+1>.md— サイクル N+1 の 5 セクションのリーダーボード- (任意、ステップ 4f の後) デプロイ済み NIM エンドポイント(
/riva-asr-customに委任)
トラブルシューティング
- ステージ 4 のトレーニングが最初のステップの後にすべて UNK に崩壊する → キャッシュ対応のストリーミング RNNT ベース(
nemotron-speech-streaming-en-0.6b)を使用しています。nvidia/parakeet-tdt-0.6b-v2(推奨のデフォルト)またはnvidia/stt_en_conformer_ctc_large(従来の代替)に切り替えてください。ストリーミング RNNT の SFT パスは機能しません。ハイパーパラメーターを変えて再試行しないでください。 - NeMo コンテナー内でマニフェストのパスが解決されない → ホストのパス(例:
$HOME/…)は/workspace/…に書き換える必要があります。書き換え用のスニペットについては、references/container-paths.mdを参照してください。 - サイクル N+1 の KER がサイクル N から変わらない → 上記のレシピで
parakeet-tdt-0.6b-v2を使用している場合、ほとんどの場合、マニフェストの信号密度が低すぎます。まずマニフェストを増やしてください。LR のスイープは行わないでください。(標準の SFT ではなく、古いアダプタースタイルのレシピを使用している場合は、アダプターの重みがゼロ初期化から変化していない可能性があります。標準の SFT に切り替えてください。) - サイクル N+1 の KER が悪化した → 小さなマニフェストで過学習しています。
/digital-health-clinical-asr-buildに戻り、マニフェストを増やしてください。 - Riva で提供した数値がオフラインの数値と異なる → 差異の原因は Riva の前処理または
riva-buildフラグであり、モデルではありません。/riva-asr-customに切り替えてください。 bf16-mixedの精度エラーが発生する → 一部の GPU(旧世代の Turing、すべての Volta)は BF16 をサポートしていません。fp32に切り替え、batch_sizeを減らしてください。fp32が遅すぎる場合に限りfp16-mixedを使用してください。TDT デコーダーでは fp16 によって損失が NaN になることがあるため、早い段階で損失曲線を確認してください。- 24 GB GPU でトレーニング中に OOM が発生する →
batch_sizeを 2 に減らし、実効バッチサイズを一定に保つためにaccumulate_grad_batchesを 2 に増やしてください。
制限事項
- TDT/RNNT デコーダーでのアダプタースタイルの SFT は機能しません。 実証済みの事実として、以前の LinearAdapter-mixin レシピでは、TDT および RNNT デコーダーで LR にかかわらず 72 個のテンソルが NaN になります。NeMo の標準のフルモデル SFT(
speech_to_text_finetune.py)に切り替えることで解決しました。これは、このスキルが推奨する方法です。TDT/RNNT ベースでアダプター SFT を試みないでください。 nemotron-speech-streaming-en-0.6bを SFT しないでください。 ストリーミング専用 NVCF 関数の SFT パスは信頼性が低く、UNK に崩壊します。デプロイ時のストリーミング提供には、Riva が非ストリーミングベースをチャンクに分割します。- 小さなマニフェストはすぐに過学習します。 合計が約 100 行未満、または優先カテゴリごとに約 5 行未満の場合、サイクル N+1 の数値は不安定です。小幅な KER の低下を信頼する前に、データを増やしてください。
- デフォルトでは英語のみです。 ベースモデルの表は en-US 固有です。他のロケールには、別のベースモデルと再検証済みの SFT レシピが必要です。
- すぐに使えるドライバーはありません。 ユーザーがトレーニングドライバーのレイアウト(出力パス、実行名、リーダーボードの再レンダリング)を独自に作成します。手法とレシピは応用できますが、サイクル 1 の正確な数値はユーザーのマニフェストによって異なります。
次のステップ
.nemoを NIM としてデプロイする:/riva-asr-custom(ソースアーキテクチャを明示的に指定してください)。- サイクル N+2 に向けてマニフェストを増やす:
/digital-health-clinical-asr-build。 - サイクルを再スコアリングする:
/digital-health-clinical-asr-eval(新しいエンドポイントまたは新しい.nemoを直接使用)。 - 別の方向へ進む場合は、単語のブースティング/LM 融合/非臨床 SFT レシピについて
/finetune-asrを参照してください。
参考資料
references/stage4-finetune.md— ベースモデルの選択表、ハイパーパラメーターの根拠、デコーダーから NIM コンテナーへの対応表、サイクル N+1 とサイクル N を比較する判断ツリーreferences/container-paths.md— ホスト間でマニフェストの可搬性を確保するためのホストから/workspace/へのパス書き換え(ラップトップ ↔ Brev ↔ NeMo コンテナー)