Nemotron-3.5-ASR-Streaming-0.6B模型中文微调实战:从环境搭建到落地踩坑全记录

摘要:本文完整记录 NVIDIA 开源流式语音识别模型 Nemotron-3.5-ASR-Streaming-0.6B 的中文领域适配全过程,覆盖环境搭建、数据处理、配置微调与踩坑排错,所有方案均经过单卡 RTX 3090 实测验证,按本文步骤可在 1~2 天内跑通完整微调流程。


一、模型与数据集说明

1.1 Nemotron-3.5-ASR-Streaming-0.6B 简介

Nemotron-3.5-ASR-Streaming-0.6B 是 NVIDIA 推出的多语言流式语音识别模型,核心特性如下:

项目说明
架构Cache-Aware FastConformer 编码器 + RNNT(循环神经网络变换器)解码器
内置语言 ID 提示条件,可通过 prompt 指定目标语言,也支持自动语言检测
参数量约 6 亿(600M)可训练参数
流式能力缓存感知(Cache-aware)流式架构,消除传统分块流式的重复计算
支持 5 档可配置延迟档位:80ms / 160ms / 320ms / 560ms / 1.12s
可在推理时按需选择延迟 - 精度平衡点,无需重新训练
语言支持总计支持 40 个语言地区(language-locales),按精度分为三个层级:
・转录就绪(19 个):开箱即用最高精度
・广泛覆盖(13 个):生产级可用精度,中文(zh-CN)属于该层级
・适配就绪(8 个):词表可识别,需领域微调解锁完整转录能力
输出特性原生支持标点符号、大小写输出;自动语言检测模式可在输出中附带对应语言标签
训练语料基于 NVIDIA Granary、多语言 LibriSpeech、Mozilla Common Voice、FLEURS、VoxPopuli 等公开多语种语料,叠加内部专有语料联合训练

该模型专为低延迟流式语音场景优化,缓存设计大幅提升了并发推理效率,适合实时语音助手、流式转写等生产场景。

1.2 微调数据集:AISHELL-1

本次微调选用 AISHELL-1 中文普通话语音语料库:

  • 训练数据约 150 小时
  • 标准划分:训练集 / 开发集 / 测试集
  • 音频格式:16 kHz 单声道 WAV

1.3 实验环境

项目配置
GPU单卡 NVIDIA RTX 3090 24 GB
系统Ubuntu 22.04
框架NeMo 1.23 + PyTorch 2.4 + Lhotse

二、环境搭建

2.1 创建 Conda 环境

conda create -n nemo python=3.12
conda activate nemo

2.2 安装核心依赖

# NeMo 主框架
pip install nemo_toolkit[all]

# Lhotse 数据处理库
pip install lhotse

2.3 提前避坑:numba 编译依赖说明

NeMo 的 RNNT 损失默认使用 numba CUDA 即时编译加速,编译 GPU 核函数时依赖 CUDA 的 libnvvm.solibdevice 库。这里有两个高频踩坑点:

  1. 版本适配断层:0.61 及更早版本的原生 numba 内置 CUDA 模块仅支持 CUDA 11.x,在 CUDA 12+ 环境中会直接报库文件缺失、核函数编译失败;
  2. 依赖强绑定:numba 对 NumPy 版本有严格的兼容约束,版本不匹配会触发各类属性缺失、导入报错,还容易与 scipy、librosa 等语音处理依赖包产生连锁版本冲突。

推荐方案(CUDA 12+ 环境标准 pip 解法)
通过 pip 安装 NVIDIA 官方 numba-cuda 独立包,原生支持对应版本 CUDA 环境,完全兼容原有 numba.cuda 调用接口,无需修改 NeMo 业务代码:

# CUDA 12.x 环境
pip install numba-cuda[cu12]

# CUDA 13.x 环境
pip install numba-cuda[cu13]

说明:numba-cuda 会自动适配对应 CUDA 版本的编译依赖,同时锁定兼容的 numpy 版本区间,可同时规避库文件缺失、依赖版本冲突两类高频问题。
若为 CUDA 11.x 环境,直接使用原生 numba 即可:pip install “numba==0.60”


三、数据准备

3.1 数据集目录结构

原始数据形态
AiShell1 原始数据集以说话人为单位分包发布,每个压缩包对应一位说话人(命名格式为 SXXXX.tar.gz,如 S0002.tar.gz)。全部解压后按 train / dev / test 三个子集划分目录,每个子集下以说话人 ID 命名子文件夹,内部存放对应说话人的所有 wav 音频文件;转录标注统一存放在 transcript 目录下。
原始目录层级如下:

data_aishell/
├── wav/
│   ├── train/
│   │   ├── S0002/
│   │   │   ├── BAC009S0002W0122.wav
│   │   │   └── ...
│   │   └── SXXXX/
│   ├── dev/
│   └── test/
└── transcript/
    └── aishell_transcript_v0.8.txt

训练用目录结构
完成数据预处理后,最终用于 NeMo 训练的目录结构如下。索引文件采用标准 JSONL 格式,每条记录对应一条音频样本,与 NeMo 训练 / 评估脚本原生兼容:

data/
├── aishell_wav/
│   ├── train/
│   ├── dev/
│   └── test/
└── manifests/
    ├── aishell_train_manifest.jsonl
    ├── aishell_dev_manifest.jsonl
    └── aishell_test_manifest.jsonl

3.2 生成 NeMo 格式 Manifest

NeMo 流式 ASR 训练采用 JSONL 格式的 manifest 索引文件,每条样本必须包含 target_lang 字段,用于模型的语言条件控制,中文对应的标准标签为 zh-CN。
单条样本的标准格式如下:

{"audio_filepath": "/path/to/audio.wav", "text": "对应的转录文本", "duration": 3.25, "target_lang": "zh-CN"}

批量生成脚本

针对 AiShell1 的目录结构,可通过以下脚本一次性生成 train / dev / test 三个子集的 manifest 文件,自动完成转录匹配、时长计算、字段补全与格式校验:

import os
import json
import soundfile as sf
from pathlib import Path

# ===================== 路径配置 =====================
BASE_DIR = Path("./data/data_aishell")          # 数据集根目录
TRANSCRIPT_PATH = BASE_DIR / "transcript" / "aishell_transcript_v0.8.txt"
WAV_ROOT = BASE_DIR / "wav"                     # 音频文件根目录
OUTPUT_DIR = Path("./data/manifests")            # manifest 文件输出目录
TARGET_LANG = "zh-CN"                             # 模型可识别的中文语言标签
# =====================================================

def load_aishell_transcript(transcript_path: Path) -> dict:
    """
    解析 AiShell1 转录文件,返回 {音频ID: 纯文本} 的映射
    原始格式:音频ID 词1 词2 词3 ...
    处理后:合并分词空格,还原为连续自然语句
    """
    transcript_dict = {}
    with open(transcript_path, "r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            parts = line.split()
            audio_id = parts[0]
            text = "".join(parts[1:])
            transcript_dict[audio_id] = text
    print(f"加载转录文本完成,共 {len(transcript_dict)} 条")
    return transcript_dict

def generate_subset_manifest(
    subset: str,
    transcript_dict: dict,
    wav_root: Path,
    output_dir: Path
):
    """
    生成指定子集(train/dev/test)的 NeMo 标准 manifest 文件
    """
    subset_wav_dir = wav_root / subset
    output_file = output_dir / f"aishell_{subset}_manifest.jsonl"
    output_dir.mkdir(parents=True, exist_ok=True)
    valid_count = 0
    skip_count = 0

    with open(output_file, "w", encoding="utf-8") as out_f:
        # 遍历所有说话人文件夹
        for speaker_dir in subset_wav_dir.iterdir():
            if not speaker_dir.is_dir():
                continue
            
            # 遍历当前说话人下的所有 wav 文件
            for wav_file in speaker_dir.glob("*.wav"):
                audio_id = wav_file.stem
                
                # 匹配转录文本,无匹配则跳过
                if audio_id not in transcript_dict:
                    skip_count += 1
                    continue
                
                # 计算音频时长,损坏文件跳过
                try:
                    with sf.SoundFile(wav_file) as f:
                        duration = len(f) / f.samplerate
                except Exception as e:
                    print(f"跳过损坏文件 {wav_file.name}: {e}")
                    skip_count += 1
                    continue
                
                # 构造标准样本并写入
                sample = {
                    "audio_filepath": str(wav_file.resolve()),
                    "duration": round(duration, 3),
                    "text": transcript_dict[audio_id],
                    "target_lang": TARGET_LANG
                }
                out_f.write(json.dumps(sample, ensure_ascii=False) + "\n")
                valid_count += 1

    print(f"【{subset}集】处理完成:有效样本 {valid_count} 条,跳过 {skip_count} 条")
    print(f"输出文件:{output_file.resolve()}\n")
    return output_file

if __name__ == "__main__":
    # 加载转录字典
    transcript_dict = load_aishell_transcript(TRANSCRIPT_PATH)
    
    # 批量生成三个子集的 manifest
    for subset in ["train", "dev", "test"]:
        generate_subset_manifest(subset, transcript_dict, WAV_ROOT, OUTPUT_DIR)
    
    print("全部数据集处理完成!")

脚本执行后会在 manifests 目录下生成三个子集的 JSONL 索引文件,路径、时长、文本、语言标签字段完整,可直接用于 NeMo 的训练、验证与测试流程。

3.3 Tarred 数据集尝试与放弃

NeMo 官方提供了 Tarred 数据集打包工具,可将音频文件与 manifest 打包为单一 tar 归档,理论上可提升超大规模数据集的顺序读取 IO 性能。但在 AiShell1 微调场景下实际验证后,存在两个明显问题:

  1. 路径扁平化不兼容:Tar 包会将内部音频路径扁平化(目录分隔符 / 被替换为 _),导致 manifest 中的原始绝对路径与包内文件名不匹配,需要额外开发路径映射逻辑,适配成本较高;
  2. 性能收益可忽略:AiShell1 仅约 150 小时数据量,单卡训练场景下磁盘 IO 不会成为性能瓶颈,打包 tar 的收益远低于适配成本。

结论:针对 AiShell1 量级的微调任务,直接使用普通 JSONL manifest 即可,无需额外打包 Tarred 数据集。


四、微调配置详解

配置文件路径:train/conf/asr_finetune/speech_to_text_finetune.yaml
整体基于 NeMo + Lightning 训练框架,单卡微调范式,核心原则是保留原生流式缓存能力 + 冻结词表解码器 + 轻量中文适配,避免破坏预训练声学特征与流式推理逻辑。

4.1 模型主体配置

基座模型加载入口

注意:init_from_nemo_model 为根级别参数,不能嵌套在 model 字段内,用于加载预训练基座模型权重。

name: "nemotron_3.5_streaming_0.6b_aishell_zh_finetune"
# 微调基座模型入口(根级别)
init_from_nemo_model: "/path/to/nemotron-3.5-asr-streaming-0.6b.nemo"

核心模型字段

model:
  sample_rate: 16000
  # -------------------- Nemotron 特有:语言条件控制(必须开启) --------------------
  language_conditioning:
    enabled: true
    lang_embed_mode: "prompt"
    default_lang: "zh-CN"

  # -------------------- 编码器:保留原生流式能力 --------------------
  encoder:
    cache_aware: true
    att_context_size: [56, 3]  # [左上下文帧数, 右上下文帧数],训练用均衡模式;推理可按需切换延迟档位

  # -------------------- 词表/分词器(保持预训练状态,禁止更新) --------------------
  char_labels:
    update_labels: false
    labels: null
  tokenizer:
    update_tokenizer: false  # 关键:必须为 false,复用基座模型词表与解码器权重
    dir: null
    type: bpe

  # -------------------- 频谱增强(微调建议减弱强度) --------------------
  spec_augment:
    _target_: nemo.collections.asr.modules.SpectrogramAugmentation
    freq_masks: 1
    time_masks: 5
    freq_width: 27
    time_width: 0.05

4.2 数据集配置

训练、验证、测试集均采用 Lhotse 数据链路,支持语言 prompt 字段注入;关闭动态分桶与 Tarred 打包,适配 AiShell1 中小规模数据量。

训练集配置

  train_ds:
    manifest_filepath: "/path/to/aishell_train_manifest.jsonl"
    sample_rate: ${model.sample_rate}
    batch_size: 8
    shuffle: true
    num_workers: 8
    pin_memory: true
    max_duration: 30.0
    min_duration: 0.5
    normalize_text: false
    # 核心:必须开启 Lhotse,才支持语言 prompt 注入
    use_lhotse: true
    shard_manifests: true
    use_bucketing: false
    is_tarred: false
    # 语言条件双字段配置(Lhotse 数据集支持)
    prompt_field: "target_lang"
    lang_field: "target_lang"
    # 必须显式指定:语言标签 → 索引映射(与模型内置字典一致)
    prompt_dictionary:
      zh-CN: 4
      zh-ZH: 4

验证集和测试集配置类似,关闭 shuffle,缩减数据加载并行数:

  validation_ds:
    manifest_filepath: "/path/to/aishell_dev_manifest.jsonl"
    sample_rate: ${model.sample_rate}
    batch_size: 8
    shuffle: false
    use_start_end_token: false
    num_workers: 4
    pin_memory: true
    normalize_text: false
    use_lhotse: true
    use_bucketing: false
    prompt_field: "target_lang"
    lang_field: "target_lang"
    prompt_dictionary:
      zh-CN: 4
      zh-ZH: 4

  test_ds:
    manifest_filepath: "/path/to/aishell_test_manifest.jsonl"
    sample_rate: ${model.sample_rate}
    batch_size: 8
    shuffle: false
    use_start_end_token: false
    num_workers: 4
    pin_memory: true
    normalize_text: false
    use_lhotse: true
    use_bucketing: false
    prompt_field: "target_lang"
    lang_field: "target_lang"
    prompt_dictionary:
      zh-CN: 4
      zh-ZH: 4

说明:测试集为可选配置,训练过程不自动执行评估;训练完成后可单独调用评估脚本使用。

4.3 优化器与学习率调度器

 optim:
    name: adamw
    lr: 5e-5               # 微调推荐范围:3e-5 ~ 1e-4,基线默认 5e-5
    betas: [0.9, 0.98]
    weight_decay: 0.01
    eps: 1e-9
    sched:
      name: CosineAnnealing
      warmup_steps: 500
      min_lr: 1e-6

4.4 训练器与实验管理

训练器配置

trainer:
  devices: 1                # GPU 数量,单卡填 1,-1 为使用全部
  num_nodes: 1
  max_epochs: -1            # 禁用 epoch 模式,使用固定步数控制训练
  max_steps: 12000          # 总训练步数,AiShell1 150小时推荐 8000~15000 步
  val_check_interval: 1000  # 每 1000 步执行一次验证
  check_val_every_n_epoch: 1
  accelerator: "gpu"
  use_distributed_sampler: false
  strategy:
    _target_: lightning.pytorch.strategies.DDPStrategy
    gradient_as_bucket_view: true
    accumulate_grad_batches: 1
  gradient_clip_val: 1.0    # 梯度裁剪,微调必备,防止梯度爆炸
  gradient_clip_algorithm: norm
  precision: "32"           # 先纯 FP32 跑通基线,后续可优化混合精度
  log_every_n_steps: 50
  enable_progress_bar: true
  num_sanity_val_steps: 0
  sync_batchnorm: true
  enable_checkpointing: false  # 由 exp_manager 统一管理检查点
  logger: false               # 由 exp_manager 统一管理日志
  benchmark: false

实验管理配置

exp_manager:
  exp_dir: "./experiments"
  name: ${name}
  create_tensorboard_logger: true
  create_checkpoint_callback: true
  checkpoint_callback_params:
    monitor: "val_wer"
    mode: "min"
    save_top_k: 3
    always_save_nemo: true
    resume_if_exists: false
    resume_ignore_no_checkpoint: false
  create_wandb_logger: false
  wandb_logger_kwargs:
    name: null
    project: null


五、启动训练

5.1 启动命令

python -u speech_to_text_finetune.py

文件为examples/asr/speech_to_text_finetune.py

5.2 正常启动日志特征

  | Name              | Type                              | Params | Mode 
--------------------------------------------------------------------------------
0 | preprocessor      | AudioToMelSpectrogramPreprocessor | 0      | train
1 | encoder           | ConformerEncoder                  | 609 M  | train
2 | decoder           | RNNTDecoder                       | 14.9 M | train
3 | joint             | RNNTJoint                         | 9.5 M  | train
4 | loss              | RNNTLoss                          | 0      | train
5 | spec_augmentation | SpectrogramAugmentation           | 0      | train
6 | wer               | WER                               | 0      | train
7 | prompt_kernel     | Sequential                        | 4.5 M  | train
8 | spec_augment      | SpectrogramAugmentation           | 0      | train
--------------------------------------------------------------------------------
637 M     Trainable params
0         Non-trainable params
637 M     Total params
2,551.988 Total estimated model params size (MB)
712       Modules in train mode
0         Modules in eval mode

Training: |          | 0/? [00:00<?, ?it/s]
Training: |          | 0/? [00:00<?, ?it/s]
Epoch 0: |          | 0/? [00:00<?, ?it/s] [NeMo I 2026-08-31 18:07:34 asr_model:198] CUDA graphs disabled for EncDecRNNTBPEModelWithPrompt::RNNTBPEDecoding::GreedyBatchedRNNTInfer
[NeMo W 2026-08-31 18:07:37 nemo_logging:364] /home/yangdi/miniconda3/envs/nemo/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:748: NumbaPerformanceWarning: [1mGrid size 2 will likely result in GPU under-utilization due to low occupancy.[0m
      warn(errors.NumbaPerformanceWarning(msg))
    
[NeMo W 2026-08-31 18:07:37 nemo_logging:364] /home/yangdi/miniconda3/envs/nemo/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:748: NumbaPerformanceWarning: [1mGrid size 2 will likely result in GPU under-utilization due to low occupancy.[0m
      warn(errors.NumbaPerformanceWarning(msg))
    
[NeMo W 2026-08-31 18:07:38 nemo_logging:364] /home/yangdi/miniconda3/envs/nemo/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:748: NumbaPerformanceWarning: [1mGrid size 1 will likely result in GPU under-utilization due to low occupancy.[0m
      warn(errors.NumbaPerformanceWarning(msg))
    
[NeMo W 2026-08-31 18:07:38 nemo_logging:364] /home/yangdi/miniconda3/envs/nemo/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:748: NumbaPerformanceWarning: [1mGrid size 2 will likely result in GPU under-utilization due to low occupancy.[0m
      warn(errors.NumbaPerformanceWarning(msg))
    
[NeMo W 2026-08-31 18:07:38 nemo_logging:364] /home/yangdi/miniconda3/envs/nemo/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:748: NumbaPerformanceWarning: [1mGrid size 1 will likely result in GPU under-utilization due to low occupancy.[0m
      warn(errors.NumbaPerformanceWarning(msg))
    
[NeMo W 2026-08-31 18:07:38 nemo_logging:364] /home/yangdi/miniconda3/envs/nemo/lib/python3.12/site-packages/torch/autograd/graph.py:979: UserWarning: The AccumulateGrad node's stream does not match the stream of the node that produced the incoming gradient. This may incur unnecessary synchronization and break CUDA graph capture if the AccumulateGrad node's stream is the default stream. This mismatch is caused by an AccumulateGrad node created prior to the current iteration being kept alive. This can happen if the autograd graph is still being kept alive by tensors such as the loss, or if you are using DDP, which will stash a reference to the node. To resolve the mismatch, delete all references to the autograd graph or ensure that DDP initialization is performed under the same stream as subsequent forwards. If the mismatch is intentional, you can use torch.autograd.graph.set_warn_on_accumulate_grad_stream_mismatch(False) to suppress this warning. (Triggered internally at /__w/pytorch/pytorch/torch/csrc/autograd/input_buffer.cpp:310.)
      return Variable._execution_engine.run_backward(  # Calls into the C++ engine to run the backward pass
    

Epoch 0: |          | 1/? [00:04<00:00,  0.21it/s]

随后出现进度条,loss 逐步下降即表示训练正常。

5.3 训练监控

训练过程的指标、日志与模型检查点由 NeMo 实验管理器统一管理,所有产物输出到以时间戳命名的单次实验目录中,可从以下维度监控训练状态:

  • 训练损失:训练端实时输出 RNNT 损失值,可通过events.out.tfevents.* TensorBoard 事件文件,正常微调过程中损失呈稳步下降趋势。
  • 验证 WER:按照配置的 val_check_interval 步长间隔自动执行验证,计算验证集字错误率(WER),并作为检查点筛选的核心监控指标。
  • 检查点保存规则:模型检查点统一存放于实验目录下的 checkpoints/ 子目录,默认遵循 val_wer 最小化原则,自动保留性能最优的 3 个模型,同时保留最新步数的 last 检查点。

单次实验的完整输出目录结构如下:

experiments/nemotron_3.5_streaming_0.6b_aishell_zh_finetune/
└── 2026-08-31_18-06-27/       # 单次实验时间戳目录
    ├── checkpoints/                # 模型检查点目录
    │   ├── nemotron_3.5_streaming_0.6b_aishell_zh_finetune.nemo
    │   │                             # NeMo 格式完整模型,含权重+配置+词表,可直接用于推理
    │   ├── nemotron_3.5_streaming_0.6b_aishell_zh_finetune--val_wer=0.5090-epoch=0.ckpt
    │   │                             # Top1 最优检查点(Lightning 格式,按 WER 升序保存)
    │   ├── nemotron_3.5_streaming_0.6b_aishell_zh_finetune--val_wer=0.5109-epoch=0.ckpt
    │   │                             # Top2 检查点
    │   ├── nemotron_3.5_streaming_0.6b_aishell_zh_finetune--val_wer=0.5130-epoch=0.ckpt
    │   │                             # Top3 检查点
    │   └── nemotron_3.5_streaming_0.6b_aishell_zh_finetune--val_wer=0.5109-epoch=0-last.ckpt
    │                                 # 最新步数检查点,用于断点续训
    ├── events.out.tfevents.*       # TensorBoard 可视化日志文件
    ├── cmd-args.log                # 训练启动参数完整记录
    ├── hparams.yaml               # 完整训练配置快照
    ├── lightning_logs.txt         # Lightning 框架训练运行日志
    └── nemo_error_log.txt         # 错误与告警专项日志

六、全程踩坑实录与解决方案

这是本次微调最核心的部分,几乎每一步都会遇到框架兼容问题,以下按出现顺序整理。

坑 1:原生数据集不支持语言 prompt 参数

现象:直接使用 AudioToBPEDataset 传入 prompt_field 报错,参数不识别。

原因:语言条件控制所需的第 5 个 prompt_indices 元素,只有 Lhotse 版数据集才会生成,原生数据集仅返回 4 元组(音频、音频长度、文本、文本长度)。

解决:设置 use_lhotse: true,启用 Lhotse 数据链路。


坑 2:Lhotse 数据集没有 len 方法

现象:训练启动时报错 TypeError: object of type 'LhotseSpeechToTextBpeDatasetWithPromptIndex' has no len()

原因:Lhotse 流式迭代数据集设计为流式读取,原生不支持 len(),模型计算 epoch 步数时调用失败。

解决:改用 max_steps 固定步数模式,绕过 len() 调用;


** 坑 3:分布式采样器长度报错**

现象DistributedSamplerWrapper 要求采样器必须有 __len__ 方法。

原因:单卡训练不需要分布式采样器,Lightning 默认开启导致包装失败。

解决trainer.use_distributed_sampler: false


** 坑 4:numba CUDA 编译依赖缺失与版本兼容问题**

现象:训练启动阶段连续出现多类报错,典型报错包括:

  • 编译库缺失:libnvvm.so: cannot open shared object fileMissing libdevice file
  • NumPy 属性缺失:AttributeError: module 'numpy' has no attribute 'row_stack' / no attribute 'trapz'
  • 依赖冲突:pip 安装后提示 numba 与 numpy、scipy、librosa 等包版本不兼容

原因

  1. NeMo 的 RNNT 损失默认启用 numba CUDA 即时编译加速,该功能依赖 CUDA 的 libnvvm.so 编译运行时库与 libdevice 设备数学库;
  2. 原版 numba(0.61 及更早版本)内置的 CUDA 模块仅支持 CUDA 11.x,在 CUDA 12/13 环境下会直接找不到编译库文件;
  3. numba 对 NumPy 版本有强绑定要求,低版本 numba 不兼容 NumPy 2.x,会出现各类属性缺失报错;若环境中同时存在 librosa、scipy 等高版本科学计算包,会进一步触发依赖冲突;

解决

安装 NVIDIA 官方 numba-cuda 独立包

这是 CUDA 12+ 环境的标准解法。numba-cuda 是 NVIDIA 独立维护的 numba CUDA 扩展包,完全兼容原有 numba.cuda API,原生支持 CUDA 12.x / 13.x,同时修复了大量 NumPy 版本兼容问题。

Conda 安装(最稳定,推荐):自动匹配 CUDA 版本与依赖,避免二进制 ABI 冲突。

# CUDA 12.x 环境
conda install -y -c conda-forge numba-cuda "cuda-version=12"

# CUDA 13.x 环境
conda install -y -c conda-forge numba-cuda "cuda-version=13"

Pip 安装:注意包名中间为英文半角连字符,复制全角字符会导致 pip 解析报错。

# CUDA 12.x 环境
pip install numba-cuda[cu12]

# CUDA 13.x 环境
pip install numba-cuda[cu13]

NumPy 版本兼容配套

numba-cuda 版本NumPy 兼容推荐配套
< 0.25.0仅支持 NumPy 1.xnumpy>=1.24,<2.0 + scipy 1.13.1 + librosa 0.10.2
pip install -U "numpy>=1.24,<2.0"

坑 5:AMP 混合精度梯度 NaN

现象:开启 16-mixed 精度后,loss 出现 inf/nan,梯度被置零,最终 AMP 梯度缩放器断言报错。

原因:RNNT 损失在低精度计算时数值稳定性差,容易出现溢出 / 下溢。

解决

  1. 先用 precision: 32 纯 FP32 跑通,确认收敛;
  2. 后续再尝试混合精度 + 增大损失 clamp 阈值提升稳定性。

坑 6:Tarred 数据集路径不匹配
现象:Tarred 数据集加载时提示音频文件不存在,原因是文件名全部被路径扁平化处理。
原因:打包脚本将路径中的 / 替换为 _ 作为 Tar 包内文件名,导致 manifest 中的原始路径无法与包内文件匹配。
解决:中小数据集直接使用普通 manifest 即可,无需进行 Tarred 打包。


七、总结与优化建议

整体总结

Nemotron-3.5 流式模型的中文微调整体可行,主要坑点集中在 Lhotse 数据链路的兼容性numba 加速的环境依赖 两部分。按照本文的步骤和避坑方案,可以在 1~2 天内完成从环境搭建到跑通训练的全流程。

优化方向建议

方向具体措施预期收益
速度优化安装 numba-cuda 启用完整 CUDA 加速 + 16 位混合精度训练速度提升 50%+
IO 优化数据量大于 500 小时后,再考虑生成标准 Tarred 分片数据集缓解大规模数据 IO 瓶颈
效果优化加入 SpecAugment 增强、调整学习率、增加训练步数进一步收敛降低 WER,提升领域适配效果

最终建议

  1. 微调优先保证跑通,再逐步优化性能
  2. 单卡小数据集场景,优先保证稳定性,不用过度追求 Tarred、分桶、numba 等高级特性;
  3. 所有报错优先从「数据链路」和「损失计算」两个方向排查,80% 的问题都集中在这两处。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐