最新n1n v2.0.1 正式上线!企业级大模型接口聚合平台 (LLM API Gateway),为您接入 500+ AI Models,价格低至 1 折, 立即尝试

使用 ExecuTorch 在 Arm 边缘设备上优化流式语音识别

作者
  • avatar
    姓名
    Nino
    职业
    Senior Tech Editor

当你对着设备说话时,你自然期望文字能够随着你的语速实时显现。这使得流式自动语音识别与批量转录有着本质上的不同,后者允许模型一次性处理整段音频文件。在流式场景中,资源受限的边缘硬件上的延迟、内存占用以及 CPU 或 NPU 的利用率,直接决定了用户体验是流畅还是令人沮丧。将这些能力引入基于 Arm 的边缘设备,需要将轻量级模型架构与 ExecuTorch 等强健的运行时进行紧密集成。

为了在 Arm CPU 和 NPU 硬件上实现实时性能,开发人员必须克服从计算图降级到自定义内存规划等多项技术难关。本篇评测与实现指南探讨了如何使用 ExecuTorch 优化流式 ASR 管道,以及如何通过类似 n1n.ai 的平台整合基础设施,从而简化更广泛的多模型工作流。

边缘硬件上的流式 ASR 挑战

流式语音识别模型(如 Whisper、Conformer 或 RNN-Transducer 的变体)依赖于连续的音频分块。通常,音频被捕获为短时间窗口(例如 20 毫秒到 100 毫秒),转换为对数梅尔滤波器组能量,并以增量方式输入到编码器-解码器或转导器网络中。

在 Arm 边缘设备(从树莓派风格的 Cortex-A 内核到专用的移动端 NPU)上,标准的 PyTorch 运行时会引入过多的开销。Python 解释器、动态调度和未优化的内存分配器会导致延迟飙升,从而破坏流式传输的沉浸感(其中目标延迟理想情况下应小于每块 200 毫秒)。

边缘 ASR 的关键制约因素:

  1. 内存带宽: 流式模型不断通过总线传输权重和历史上下文向量(KV 缓存或状态张量)。
  2. 功耗: 如果不对算子进行量化,移动或物联网设备上的持续推理会迅速耗尽电量。
  3. 冷启动延迟: 初始模型加载必须达到近乎瞬时的水平。

ExecuTorch:面向 PyTorch 的现代边缘运行时

ExecuTorch 旨在通过为移动、嵌入式和边缘设备上的 PyTorch 模型提供轻量级、可移植的运行时来解决这些确切的问题。通过将执行层从 Python 转移到高度优化的 C++ 核心,ExecuTorch 减少了二进制文件的大小和执行开销。

当与 Arm 计算库(ACL)和用于微控制器的 CMSIS-NN 结合使用时,ExecuTorch 允许开发人员将 PyTorch 计算图直接降级为硬件加速指令。

管道架构

音频输入 (麦克风) -> 分块 -> 特征提取 (对数梅尔) -> ExecuTorch 运行时 (Arm NPU/CPU) -> 流式文本输出

为了管理更广泛的 AI 生态系统(例如将转录文本发送给 LLM 进行摘要或意图解析),许多工程团队利用诸如 n1n.ai 这样统一的聚合器来处理 API 速率限制、负载均衡和多提供商故障转移。

分步实现指南

以下是将流式 ASR 编码器块导出并通过 Arm 目标设备上的 ExecuTorch C++ API 运行的技术演练。

第一步:模型准备与导出

首先,确保你的 ASR 模型接受与流式窗口相对应的固定大小张量输入。

import torch
import torch.export
from torch.export import export

class StreamingEncoder(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.conv = torch.nn.Conv1d(80, 512, kernel_size=3, stride=1, padding=1)
        self.rnn = torch.nn.GRU(512, 512, batch_first=True)

    def forward(self, x, hidden_state):
        # x 形状: [Batch, Channels, Time_Chunk]
        x = self.conv(x)
        x = x.permute(0, 2, 1) # [Batch, Time, Channels]
        out, new_hidden = self.rnn(x, hidden_state)
        return out, new_hidden

model = StreamingEncoder().eval()
dummy_x = torch.randn(1, 80, 10)
dummy_hidden = torch.zeros(1, 1, 512)

# 使用 torch.export 进行导出
exported_program = export(model, (dummy_x, dummy_hidden))

第二步:使用 Arm 后端降级到 ExecuTorch

导出后,使用 ExecuTorch 编译器框架将模型降级为针对 Arm 目标优化的 .pte 格式。

from executorch.exir import EdgeProgramManager
from executorch.backends.arm.arm_backend import ArmBackend

# 为 Arm 后端降级导出的程序
edge_manager: EdgeProgramManager = torch.export.to_edge(exported_program)
exec_program = edge_manager.to_executorch()

with open("streaming_encoder.pte