拓冰建站拓冰建站
首页 / 资讯中心 / 正文

FunASR 模型注册机制深度指南:从自定义模型接入到安全加载实践

FunASR 模型注册机制深度指南从自定义模型接入到安全加载实践【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASRFunASR 是阿里巴巴达摩院开源的语音识别工具包覆盖训练、推理、流式 ASR、VAD、标点、说话人分离以及 OpenAI 兼容/MCP 服务等完整链路。本文以仓库中 docs/model_registration_zh.md 为核心骨架结合 funasr/register.py、funasr/auto/auto_model.py、funasr/download/download_model_from_hub.py 与 funasr/utils/dynamic_import.py 等源码系统讲解模型注册表的运作原理、自定义模型接入的完整接口约定、两类模型加载路径的差异以及trust_remote_code场景下的安全边界。读完本文你将能独立把一个自定义模型注册进 FunASR并判断何时该用直接注册构造、何时该走模型目录解析。1. 先理解注册到底做了什么注册Registration在 FunASR 中的含义非常具体它只是把「Python 类实现」与「配置名称」连接起来。它不会下载权重、不会自动兼容任意 Transformers 模型、不提供训练或导出能力也不代表任何质量认证。这一点是整个模型注册体系的出发点也是阅读后续所有内容前必须建立的心智模型。注册表的实现集中在 funasr/register.py。该文件定义了一个RegisterTables数据类并导出一个进程级单例tables注册键保存在进程级字典中model_classes、frontend_classes、encoder_classes等均为类属性字典register()是一个装饰器工厂返回原类不会包装或修改类本身装饰器通过inspect.getfile与inspect.getsourcelines记录类的源码位置作为注册元数据*_meta表同类名重复注册时后者会覆盖前者只记录 debug 日志而不报错——因此导入顺序会影响最终生效的实现。从 funasr/register.py 可以看到register的完整逻辑def register(self, register_tables_key: str, key: str None) - callable: def decorator(target_class): if not hasattr(self, register_tables_key): setattr(self, register_tables_key, {}) logging.debug(fNew registry table added: {register_tables_key}) registry getattr(self, register_tables_key) registry_key key if key is not None else target_class.__name__ if registry_key in registry: logging.debug( fKey {registry_key} already exists in {register_tables_key}, re-register ) registry[registry_key] target_class # ... 记录 (register_key, class_name, path:line) 到 *_meta 表 return target_class return decorator注意其中一行if not hasattr(self, register_tables_key)会动态创建新表。这意味着表名拼错不会报错而是生成一张无人使用的表——这是排查「为什么我的注册没生效」时的经典陷阱。1.1 注册键的命名规则使用装饰器时的约定是tables.register(model_classes, YourUniqueModelName)其中tables来自funasr.register第二个参数是区分大小写的精确注册键省略第二个参数时默认使用 Python 类名作为注册键。由于装饰器依赖inspect记录源码位置示例类必须定义在可导入的.py文件中而不能只在 REPL 会话里定义也不能定义在动态生成的类上否则无法追溯源码位置。1.2 同名覆盖与冲突检查因为同名键会被静默覆盖所以使用组织或项目专属的名称例如DocsEchoModelV1、MyTeamAsrV2在注册前主动增加冲突检查例如if NAME in tables.model_classes: raise RuntimeError(...)不要给无关的自定义模型使用SenseVoiceSmall、Paraformer、FunASRNano等已有名称——这会覆盖内置实现并破坏其他使用方。调试时可使用tables.print(model) # 打印 model_classes 的注册元数据注册名、类名、源码位置 tables.model_classes[name] # 取出当前生效的实现print方法遍历vars(self)中所有以_meta结尾的表并格式化输出见 funasr/register.pykey参数用于过滤只查看包含该关键字的表。1.3 不止一张表组件级注册model_classes只是众多注册表之一。从 funasr/register.py 可以看到RegisterTables预定义了注册表用途model_classes完整模型供AutoModel直接构造frontend_classes前端特征提取器encoder_classes/decoder_classes编码器 / 解码器组件tokenizer_classes分词器dataset_classes/index_ds_classes/batch_sampler_classes数据集与批采样specaug_classes/normalize_classes/joint_network_classes/predictor_classes/stride_conv_classes/dataloader_classes其他组件级表关键认知注册 encoder 不等于注册完整模型。每个调用方都有自己的接口约定——AutoModel从model_classes取模型而编码器表、前端表、分词器表分别服务于build_model内部的不同环节详见 funasr/auto/auto_model.py 中 tokenizer/frontend/model 的构建顺序。若你只注册了 encoder 却想用AutoModel(model你的名字)构造会得到 is not registered 的报错。2. 最小本地接口示例零依赖跑通注册文档给出了一个可在任何环境无需 GPU、无需权重、无需下载运行的玩具示例用于验证「注册 → AutoModel 构造 → generate 推理」整条接口链路。将以下代码保存为可导入的custom_model_demo.py然后在已安装当前 checkout 的环境执行python custom_model_demo.pyimport torch from funasr import AutoModel from funasr.register import tables MODEL_NAME DocsEchoModelV1 if MODEL_NAME in tables.model_classes: raise RuntimeError(fRegistry collision: {MODEL_NAME}) tables.register(model_classes, MODEL_NAME) class DocsEchoModel(torch.nn.Module): def __init__(self, **kwargs): super().__init__() self.anchor torch.nn.Parameter(torch.zeros(1), requires_gradFalse) def inference( self, data_in, data_lengthsNone, keyNone, tokenizerNone, frontendNone, **kwargs, ): results [ {key: sample_key, text: str(value)} for sample_key, value in zip(key, data_in) ] return results, {} if __name__ __main__: model AutoModel( modelMODEL_NAME, model_conf{}, devicecpu, disable_updateTrue, disable_pbarTrue, ) result model.generate(input[hello, world], data_typetext) assert [row[text] for row in result] [hello, world] assert all(isinstance(row[key], str) for row in result) print([row[text] for row in result])运行后终端输出应为[hello, world]。这个例子只回显文本、不执行语音识别因此不需要权重、音频、模型下载或 GPU。2.1 示例中的关键细节为什么保留一个参数当前 funasr/auto/auto_model.py 的inference方法在推理结束时执行device next(model.parameters()).device来清理 CUDA 缓存。如果模型是零参数的空nn.Modulenext(model.parameters())会直接抛StopIteration。因此示例在__init__中创建了一个torch.nn.Parameter占位参数self.anchor torch.nn.Parameter(torch.zeros(1), requires_gradFalse)为什么传model_conf{}这是有意为之。看 funasr/auto/auto_model.py 的build_modelif model_conf not in kwargs: logging.info(download models from model hub: {}.format(kwargs.get(hub, ms))) kwargs download_model(**kwargs)只要model_conf键存在build_model就会跳过 hub 下载与配置解析。所以这里的model是已注册的类名而不是目录或 hub ID。必须先自行导入模块。在直接构造路径中传remote_code不会触发导入——remote_code只在 hub 下载路径中被消费见第 4 节。因此示例中from funasr.register import tables之后的装饰器执行就是完成注册的那一步。构造参数的合并规则。解析后的 kwargs 会通过deep_update覆盖合并进model_conf再一起传给构造器funasr/auto/auto_model.pymodel_conf {} deep_update(model_conf, kwargs.get(model_conf, {})) deep_update(model_conf, kwargs) model model_class(**model_conf)这意味着已构造的 tokenizer/frontend、设备、词表大小、输入维度等都会被注入到model_conf中。因此自定义模型应像真实模型一样接收相应的命名参数以及**kwargs兜底避免因多余键而构造失败。2.2 玩具示例的验证边界这段代码不只是文档示例tests/test_training_docs_contract.py中的test_no_download_toy_against_checkout测试会在临时文件中针对当前源码直接执行这段代码设置HF_HUB_OFFLINE1、TRANSFORMERS_OFFLINE1保证离线并断言输出包含[hello, world]见 tests/test_training_docs_contract.py。它只验证接口连通性不验证任何语音模型、不下载权重、不跑 GPU。3. 推理与训练接口约定自定义模型必须遵循 AutoModel 对模型对象的接口约定否则会在运行时以各种隐晦方式失败。下表总结了当前源码约定接口当前源码约定模型对象通常为torch.nn.Module需要支持.to(...)、.eval()、.parameters()构造配置由模型自己决定。inference输入不使用 VAD 时AutoModel 把输入组织成data_in和key列表在torch.no_grad()下调用model.inference(**batch, **kwargs)见 funasr/auto/auto_model.py。单条data_typefbank输入会直接传入特征对象并设data_lengthsinput_lenfunasr/auto/auto_model.py。tokenizer/frontend 是已构造对象或None。模型层返回值返回二元组(results, meta_data)results是list[dict]meta_data是字典。ASR 结果使用字符串key、text并保持输入顺序与标识。不能只返回结果字典列表否则 AutoModel 会把第一条当成整个 batch 的结果见 funasr/auto/auto_model.py 的res[0]/res[1]解包逻辑。元数据可包含load_data、extract_feat、batch_data_time。音频场景的batch_data_time是以秒为单位的正时长不能填毫秒或零——零会导致 RTF 计时代码除零funasr/auto/auto_model.py。省略时使用内部-1哨兵值如上述非音频示例此时 RTF 不代表有效速度测量。公开返回值AutoModel.generate(...)返回展开后的结果列表。时间戳等附加字段由模型自行决定注册并不承诺这些字段。VAD、标点、说话人和流式集成还需要额外兼容实现与独立测试。训练实现可微forward命名张量参数应匹配 dataset collator。训练器解包(loss, stats, weight)见 funasr/train_utils/trainer_ds.pySenseVoice 和 FunASRNano 使用force_gatherable聚合统计量。玩具模型故意不实现训练 forward。导出导出工具调用模型的export方法再使用export_dummy_inputs、输入输出名称、动态轴等模型专属方法见 funasr/utils/export_utils.py。注册本身不会实现这些方法。3.1 附加能力的责任边界自定义模型仍需自行实现所需的音频加载、特征处理、分词、解码及 batching 逻辑。可以参考相近模型的接口实现但不能在缺少实现的情况下宣称具有其能力。例如想接 VAD 长音频切分需要在AutoModel传入vad_model并保证你的模型能消费 VAD 切出的每个片段想接标点恢复需要兼容punc_model的文本接口想接说话人分离需要spk_model及spk_embedding输出约定见 funasr/auto/auto_model.py 中说话人嵌入的收集逻辑训练还需适配数据集与损失函数参见 docs/training_zh.md。4. 加载已审查代码与权重两条路径文档强调加载模型有两条不同的路径理解它们的差异是避免踩坑的关键。4.1 路径一直接注册构造先导入模块触发注册再传注册键和model_conf就像第 2 节的玩具示例一样from funasr import AutoModel import my_custom_model # 先导入模块完成注册 model AutoModel( modelMyRegisteredName, model_conf{...}, # 关键存在该键即跳过 hub 解析 devicecpu, disable_updateTrue, )当需要加载权重时提供兼容的 tokenizer/frontend/配置以及真实存在的init_param。该路径不执行 hub 代码导入因此remote_code在这里无效——你必须自己先完成模块导入。4.2 路径二模型目录解析传已审查的本地目录或 hub ID不传model_conf。此时加载器funasr/download/download_model_from_hub.py会解析名称别名name_maps_ms/name_maps_hf从 ModelScope 或 HuggingFace 下载若非本地路径读取目录中的configuration.json文件元数据或config.yaml解析出模型类名、tokenizer、frontend 配置并加载权重。简单的本地config.yaml目录通常还需要model.pt以及配置引用的分词器、前端文件例如tokens.txt/tokens.json/bpe.model/am.mvn见 funasr/download/download_model_from_hub.py 的config.yaml分支。任意 HF 权重文件夹不会自动成为 FunASR 模型目录——缺少 FunASR 约定的配置结构就无法被解析。第二条路径的 ModelScope 接口示例from funasr import AutoModel model AutoModel( model./models/custom-asr, hubms, trust_remote_codeTrue, remote_code./custom_asr_model.py, devicecpu, disable_updateTrue, ) print(model.generate(inputdata/audio/heldout.wav))这不是「无需准备即可运行」的例子models/custom-asr必须已有兼容且经过审查的配置和权重custom_asr_model.py必须注册配置文件中指定的精确键。4.3 remote_code 的动态导入行为当前download_from_ms在trust_remote_codeTrue时会调用import_module_from_pathfunasr/download/download_model_from_hub.pyremote_code未指定时默认为模块名model。导入器funasr/utils/dynamic_import.py的行为支持模块/文件路径也支持 URLhttp 前缀会先download_from_url下载把文件所在目录加入sys.path后按文件基本名importlib.import_module相对路径按工作目录解析不会自动相对权重目录解析文件基本名冲突及 Python 导入缓存可能选中已加载模块应使用独立模块名并验证当前类该辅助函数打印导入异常而不重新抛出——因此应检查错误日志与注册结果导入失败时AutoModel才会在查表阶段报 not registered。4.4 hubhf 的路径差异hubhf路径与 ModelScope 不同当前 checkout 的download_from_hf在信任开关下会安装模型目录的requirements.txt但不调用import_module_from_path可对照 funasr/download/download_model_from_hub.py其中没有 remote_code 导入逻辑。也就是说不能假定hubhf会执行remote_code。两种正确做法在hubhf构造前显式导入已审查模块使用完整配置的直接注册构造路径路径一。两条路径的本地config.yaml回退对init_param的处理也不同ModelScope保留已有且存在的显式路径if init_param not in kwargs or not os.path.exists(kwargs[init_param])才回退到目录内model.pt见 funasr/download/download_model_from_hub.pyHugging Face指定目录内model.pt无条件覆盖init_param见 funasr/download/download_model_from_hub.py。因此必须检查解析后的真实路径不要假定 checkpoint 覆盖行为始终一致。5. 真实示例与边界5.1 SenseVoiceSmall完整集成的参考SenseVoice 实现是展示「注册 → 训练 → 推理 → 导出」完整集成的范本但它有自己的模型配置与分词器假设。若以它为参考需保持其配置结构与 tokenizer 约定不能只抄注册写法就宣称拥有同等能力。5.2 FunASRNano 的本地实现覆盖examples/industrial_data_pretraining/fun_asr_nano/demo1.py 使用trust_remote_codeTrue、remote_code./model.py、hubms并要求在 recipe 工作目录下运行。其本地实现注册FunASRNanotables.register(model_classes, FunASRNano)并导入同目录的ctc、tools模块——这会覆盖内置实现funasr/models/fun_asr_nano/model.py。两者并非对所有功能可互换尤其不能假定保留内置 LoRA应检查实际加载的类和权重键这也是仓库中多个test_fun_asr_nano_*测试反复验证的主题。5.3 MOSS 适配器第三方模型集成funasr/models/moss_transcribe_diarize/model.py 集成第三方 OpenMOSS 模型其forward明确拒绝训练。这印证了一个边界注册模型不意味着支持微调或导出能力以具体实现为准。5.4 历史文档入口原始注册教程和通用教程保留为历史入口若示例与本文存在差异以本文说明的当前源码行为为准。6. 安全与验证6.1 trust_remote_code 的安全边界trust_remote_codeTrue允许执行 Python 代码hub 加载器还可能安装模型目录的requirements.txtdownload_from_ms与download_from_hf中都有install_requirements调用。因此本地目录也不天然可信——审查源码、依赖与权重序列化方式使用隔离环境容器 / 虚拟环境不加载不可信的 pickle checkpoint不要把不可信 URL、模块名或配置直接接入流程远程 revision 行为不可靠时保留带哈希的已审查本地快照这里的model_revision并非所有加载路径都能统一固定版本对比 funasr/download/download_model_from_hub.py 的master默认值与snapshot_download的 revision 参数使用。6.2 加载前与加载后的检查清单加载前检查当前注册键、类和源码位置tables.print(model)可辅助确认模型、配置、分词器兼容检查缺失或多余权重AutoModel的ignore_init_mismatch默认为True直接传入不存在的init_param只打印错误、不保证构造失败见 funasr/auto/auto_model.py——应自行验证 checkpoint 存在训练、导出、部署前分别测试单条、多条、异常输入与目标流水线注意FunASR 软件的 MIT 许可证不替代模型或上游组件的许可如 SenseVoice、OpenMOSS 等各自的协议。6.3 专项契约测试运行仓库中的专项语法、仓库链接和不下载模型的玩具接口测试python -m pytest -q tests/test_training_docs_contract.py该测试覆盖文档相对链接有效性、代码块语法Python AST / JSON / bash、中英文档示例一致性、加载器与注册表源码契约import_module_from_path只在download_from_ms中被调用、model_conf分支存在、model.inference(**batch, **kwargs)调用约定等见 tests/test_training_docs_contract.py。需要明确的是这些检查不认证任意自定义代码的正确性、真实 ASR 质量、GPU 训练、真实 checkpoint 恢复或导出兼容性——它们只是保证文档与当前源码的接口约定不脱节。7. 快速决策我该用哪条路径场景推荐路径关键参数本地实验、接口连通性验证直接注册构造model注册键model_conf{}已审查的本地模型目录模型目录解析model目录路径 目录含config.yaml/configuration.jsonmodel.pt加载 ModelScope 远程模型模型目录解析hubmstrust_remote_codeTrueremote_code模块路径加载 HuggingFace 远程模型模型目录解析 显式导入hubhf构造前先 import 已审查模块remote_code 不自动执行无论走哪条路径都请记住注册只是「名称 ↔ 实现」的绑定真正的能力边界由你的inference/forward/export实现决定而安全性永远取决于你对所加载代码与权重的审查程度。【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门