From a082a27ae30c81cdf6929167f00ab98cf50681bd Mon Sep 17 00:00:00 2001 From: LiuZi816 <17274908160@163.com> Date: Tue, 15 Sep 2026 12:10:03 +0800 Subject: [PATCH 1/3] Create ascend_npu.md --- docs/ascend_npu.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 docs/ascend_npu.md diff --git a/docs/ascend_npu.md b/docs/ascend_npu.md new file mode 100644 index 000000000..2fee79aef --- /dev/null +++ b/docs/ascend_npu.md @@ -0,0 +1,71 @@ +# 在昇腾 NPU(Ascend)上运行 FunASR + +适用环境:Atlas 推理/训练卡 + CANN + torch_npu。 +已在 Atlas 300I Pro(Ascend 310P3)、CANN 9.1.0-beta.1、torch 2.10.0+cpu、torch_npu 2.10.0、funasr 1.3.14 上完整验证。 + +## 1. 安装注意 + +昇腾环境要求 torch 为 CPU 版并与 torch_npu 严格配对(如 torch 2.10.0+cpu + torch_npu 2.10.0)。 +建议使用 `--no-deps` 安装 funasr 以避免依赖解析替换 torch,随后手动补齐运行时依赖: + +```bash +pip install funasr --no-deps +pip install torch_complex kaldiio omegaconf librosa kaldi-native-fbank \ + editdistance jieba zhconv tgt umap-learn praat-parselmouth \ + tensorboardX onnxruntime sentencepiece +``` + +## 2. 设备指定 + +```python +import torch_npu # 必须先导入,注册 npu 设备 +from funasr import AutoModel + +model = AutoModel( + model=<模型路径>, + device="npu:0", + disable_update=True, + disable_log=True, + disable_pbar=True, +) +``` + +## 3. CAM++(说话人模型)已知问题与等价规避 + +`funasr/models/campplus/components.py` 的 `seg_pooling` 使用 +`F.avg_pool1d(kernel_size=100, stride=100, ceil_mode=True)`。昇腾上该调用被 +lower 为 `AvgPoolV2`,其融合算子仅支持 stride ∈ [1,63],图编译失败并崩溃。 + +运行时等价替换(数值一致,仅用 NPU 支持的基础算子),需在首次前向之前执行: + +```python +from funasr.models.campplus import components as cp +import torch.nn.functional as F + +def seg_pooling(self, x, seg_len=100, stype="avg"): + B, C, T = x.shape + pad = (-T) % seg_len # 等价 ceil_mode + xp = F.pad(x, (0, pad), value=0.0) if pad else x + xg = xp.reshape(B, C, (T + pad) // seg_len, seg_len) + seg = xg.mean(dim=-1) if stype == "avg" else xg.max(dim=-1).values + shape = seg.shape + seg = seg.unsqueeze(-1).expand(*shape, seg_len).reshape(*shape[:-1], -1) + return seg[..., :T] + +cp.CAMLayer.seg_pooling = seg_pooling +``` + +## 4. 调试技巧 + +NPU 算子异步下发,报错堆栈可能指向下一个同步点的无关模型(容易误判)。 +定位真实出错算子: + +```bash +export ASCEND_LAUNCH_BLOCKING=1 # 仅调试用,会显著降速,定位后取消 +``` + +## 5. 预期行为 + +- 首次推理包含 NPU 图编译开销(几十秒量级),属正常现象,非卡死; +- fsmn-vad / paraformer / ct-transformer 可直接运行; +- cam++ 需上述 seg_pooling 补丁后运行。 From ead45aed1789c80b75e8f81b313b607cdaeaa7f0 Mon Sep 17 00:00:00 2001 From: LiuZi816 <17274908160@163.com> Date: Tue, 15 Sep 2026 14:24:23 +0800 Subject: [PATCH 2/3] Update ascend_npu.md --- docs/ascend_npu.md | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/docs/ascend_npu.md b/docs/ascend_npu.md index 2fee79aef..4f2b62082 100644 --- a/docs/ascend_npu.md +++ b/docs/ascend_npu.md @@ -33,21 +33,33 @@ model = AutoModel( ## 3. CAM++(说话人模型)已知问题与等价规避 `funasr/models/campplus/components.py` 的 `seg_pooling` 使用 -`F.avg_pool1d(kernel_size=100, stride=100, ceil_mode=True)`。昇腾上该调用被 +`F.avg_pool1d` / `F.max_pool1d`(kernel_size=100, stride=100, ceil_mode=True)。昇腾上该调用被 lower 为 `AvgPoolV2`,其融合算子仅支持 stride ∈ [1,63],图编译失败并崩溃。 -运行时等价替换(数值一致,仅用 NPU 支持的基础算子),需在首次前向之前执行: +运行时等价替换(仅用 NPU 支持的基础算子),需在首次前向之前执行。 +实现要点:完整段正常池化;**不完整尾段仅对其真实帧归约(显式补零会污染尾段均值/最大值, +与 ceil_mode 语义不一致)**: ```python from funasr.models.campplus import components as cp import torch.nn.functional as F def seg_pooling(self, x, seg_len=100, stype="avg"): + # numerically equivalent to avg_pool1d/max_pool1d(kernel_size=seg_len, + # stride=seg_len, ceil_mode=True): the incomplete tail segment is + # reduced over its REAL frames only (no zero padding). B, C, T = x.shape - pad = (-T) % seg_len # 等价 ceil_mode - xp = F.pad(x, (0, pad), value=0.0) if pad else x - xg = xp.reshape(B, C, (T + pad) // seg_len, seg_len) - seg = xg.mean(dim=-1) if stype == "avg" else xg.max(dim=-1).values + n_full = T // seg_len + tail = T - n_full * seg_len + segs = [] + if n_full > 0: + full = x[:, :, : n_full * seg_len].reshape(B, C, n_full, seg_len) + segs.append(full.mean(dim=-1) if stype == "avg" else full.max(dim=-1).values) + if tail > 0: + tail_x = x[:, :, n_full * seg_len:] + segs.append(tail_x.mean(dim=-1, keepdim=True) if stype == "avg" + else tail_x.max(dim=-1, keepdim=True).values) + seg = torch.cat(segs, dim=-1) if len(segs) > 1 else segs[0] # [B, C, nseg] shape = seg.shape seg = seg.unsqueeze(-1).expand(*shape, seg_len).reshape(*shape[:-1], -1) return seg[..., :T] @@ -55,6 +67,12 @@ def seg_pooling(self, x, seg_len=100, stype="avg"): cp.CAMLayer.seg_pooling = seg_pooling ``` +**等价性回归**(CPU,torch 2.10.0,对照 `avg_pool1d`/`max_pool1d` + `ceil_mode=True`): +长度 {50, 99, 100, 101, 110, 150, 199, 200, 201, 250, 1000} × {avg, max} × +{全 1、全负、randn} 共 66 组用例,`torch.allclose(rtol=1e-4, atol=1e-6)` 全部通过; +残差仅为 float32 累加顺序噪声(最大 ~1.2e-7)。代表例:T=150 全 1 输入 avg 模式尾帧 += 1.0(与原版一致,错误补零实现会得到 0.5)。 + ## 4. 调试技巧 NPU 算子异步下发,报错堆栈可能指向下一个同步点的无关模型(容易误判)。 From 27fc1ed6d6c45ee6e51b5e79983ff88b43462e64 Mon Sep 17 00:00:00 2001 From: LiuZi816 <17274908160@163.com> Date: Tue, 15 Sep 2026 15:34:23 +0800 Subject: [PATCH 3/3] Update ascend_npu.md --- docs/ascend_npu.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/ascend_npu.md b/docs/ascend_npu.md index 4f2b62082..c56c1c724 100644 --- a/docs/ascend_npu.md +++ b/docs/ascend_npu.md @@ -38,16 +38,19 @@ lower 为 `AvgPoolV2`,其融合算子仅支持 stride ∈ [1,63],图编译 运行时等价替换(仅用 NPU 支持的基础算子),需在首次前向之前执行。 实现要点:完整段正常池化;**不完整尾段仅对其真实帧归约(显式补零会污染尾段均值/最大值, -与 ceil_mode 语义不一致)**: +与 ceil_mode 语义不一致)**;未知 stype 与原版一致抛出 ValueError: ```python -from funasr.models.campplus import components as cp +import torch import torch.nn.functional as F +from funasr.models.campplus import components as cp def seg_pooling(self, x, seg_len=100, stype="avg"): # numerically equivalent to avg_pool1d/max_pool1d(kernel_size=seg_len, # stride=seg_len, ceil_mode=True): the incomplete tail segment is # reduced over its REAL frames only (no zero padding). + if stype not in ("avg", "max"): + raise ValueError("Wrong segment pooling type.") B, C, T = x.shape n_full = T // seg_len tail = T - n_full * seg_len @@ -67,12 +70,15 @@ def seg_pooling(self, x, seg_len=100, stype="avg"): cp.CAMLayer.seg_pooling = seg_pooling ``` -**等价性回归**(CPU,torch 2.10.0,对照 `avg_pool1d`/`max_pool1d` + `ceil_mode=True`): +**等价性回归**(仅 CPU 语义检查,torch 2.10.0+cpu,对照 `avg_pool1d`/`max_pool1d` + `ceil_mode=True`): 长度 {50, 99, 100, 101, 110, 150, 199, 200, 201, 250, 1000} × {avg, max} × {全 1、全负、randn} 共 66 组用例,`torch.allclose(rtol=1e-4, atol=1e-6)` 全部通过; 残差仅为 float32 累加顺序噪声(最大 ~1.2e-7)。代表例:T=150 全 1 输入 avg 模式尾帧 = 1.0(与原版一致,错误补零实现会得到 0.5)。 +**范围说明**:上述回归验证的是 CPU 上的归约语义等价;NPU 侧的兼容性与精度 +需在实际昇腾环境中另行验证(本仓库初测见上文运行环境)。 + ## 4. 调试技巧 NPU 算子异步下发,报错堆栈可能指向下一个同步点的无关模型(容易误判)。