登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

LiteRT SignatureDef 多输入模型怎么按签名准备推理张量

来源:17golang原创

时间:2026-10-13 07:31:46 191浏览 收藏

LiteRT 多输入模型接入时,最稳妥的做法是先读取模型暴露的 SignatureDef,再用签名中的输入名称组织张量。不要根据内部 tensor index 猜测参数顺序:签名 key 决定调用哪个入口,输入名称决定每个张量绑定到哪里,输出名称决定从结果字典里取什么。

官方文档:https://developers.google.com/edge/litert/conversion/tensorflow/signatures

先记住这条排查线索
  • signature key 是入口名称,例如 serving_default。
  • 多输入调用的关键字必须使用签名定义的输入名称,例如 image 和 scale。
  • 返回值是按输出名称组织的字典,先读清单再写绑定代码,能避免顺序猜测。

问题现场:为什么输入数量对了,调用仍然报错

常见现象是模型明明有两个输入,代码也准备了两个 NumPy 数组,但调用时仍出现名称不存在、形状不匹配或结果取不到。初步看像是张量数据有问题,实际上更常见的原因是把“模型内部的张量编号”当成了“应用层的签名参数名”。

SignatureDef 把模型包装成一个有名字的入口:一个模型可以有多个 signature key;每个入口有自己的输入名称和输出名称。多输入模型因此不是“按数组顺序塞进去”,而是“按名称把每个数组交给对应输入”。

先确认 signature 的三层命名

排查时把名称拆成三层,思路会清楚很多:

层级示例作用
signature keyserving_default选择模型的一个入口
输入名称image、scale把应用张量绑定到入口参数
输出名称scores、label从推理结果字典取回对应结果
LiteRT SignatureDef 中 signature key、多输入名称和多输出名称的关系说明图
图1:LiteRT SignatureDef 的入口、输入和输出名称关系说明图,不是截图或运行证据。

这三层名称可能与内部 tensor 名称不同。应用代码应依赖签名契约;只有在做底层诊断时,才有必要继续查看 tensor 的内部信息。

动手查看:先把模型的签名清单打印出来

不要先写 runner 调用。先用解释器读取签名清单,确认到底有哪些入口、输入和输出。下面的示例只展示查看方式,model.tflite 需要替换为自己的模型文件。

import tensorflow as tf

# 加载模型并读取签名元数据;这里不执行推理。
interpreter = tf.lite.Interpreter(model_path="model.tflite")
signature_list = interpreter.get_signature_list()

# 逐个查看入口、输入名和输出名,避免凭经验猜参数。
for signature_key, spec in signature_list.items():
    print("signature:", signature_key)
    print("inputs:", spec["inputs"])
    print("outputs:", spec["outputs"])

如果清单返回类似 {"serving_default": {"inputs": ["image", "scale"], "outputs": ["scores", "label"]}},就说明这个入口需要两个命名输入,并会返回两个命名输出。这里的名称是文章示例,实际值以你的模型清单为准。

定位原因:多输入张量要同时满足名称、形状和类型

有了名称之后,再准备输入张量。名称解决“交给谁”,形状和数据类型解决“交什么”。例如图像输入常见为四维数组,缩放参数可能是单元素数组;这只是结构示意,不能代替模型自身的签名约束。

import numpy as np

# 按模型签名准备两个输入;形状和 dtype 必须换成你的实际定义。
image = np.zeros((1, 224, 224, 3), dtype=np.float32)
scale = np.array([0.5], dtype=np.float32)

# 用名称保存输入,后面交给 signature runner 做绑定。
inputs = {
    "image": image,
    "scale": scale,
}

# 这里打印的是准备结果,不代表已经得到真实推理输出。
for name, tensor in inputs.items():
    print(name, tensor.shape, tensor.dtype)

如果这里的形状与模型实际签名不同,后续调用会失败;如果模型要求整数类型,也不能只把 float32 强行传入。先根据签名和模型文档确定输入规格,再决定是否需要归一化、转型或增加 batch 维度。

修复调用:用 signature runner 按名称传入多个输入

确认入口名后,通过 get_signature_runner 取得该入口的调用对象。多输入时把每个签名输入写成一个关键字参数;这样即使内部 tensor index 变化,应用代码仍然围绕稳定的签名名称工作。

# 选择清单中已经确认的入口。
runner = interpreter.get_signature_runner("serving_default")

# 关键字名称必须与 signature 的 inputs 完全一致。
result = runner(
    image=image,
    scale=scale,
)

# 输出同样按签名名称读取,避免假设返回值的顺序。
scores = result["scores"]
label = result["label"]
print("scores shape:", scores.shape)
print("label:", label)

这里的 result 是按输出名称组织的映射。不要把它当成固定位置的列表,也不要把 image 与 scale 的变量顺序当成模型契约;真正的契约是签名中的名称与输入输出定义。

LiteRT SignatureDef 输入名称映射到内部 tensor index 并按名称执行推理的结构说明图
图2:签名输入名称映射到内部 tensor index 的静态说明图,不代表实际模型执行时序。

继续排查:三类错误应该看哪里

现象先检查修复方向
找不到 signature keyget_signature_list() 的顶层键使用实际入口名,不要默认写 serving_default
输入名称不存在当前入口的 inputs 列表把调用关键字改成签名名称,避免使用 tensor index
形状或类型错误对应输入的 shape、dtype 和 batch 约束调整数组形状、类型或预处理,不要只改参数顺序
输出取值失败当前入口的 outputs 列表按输出名称读取字典,不猜测返回位置

如果模型包含多个 signature,还要确认当前 runner 选的是正确入口。两个入口可能使用不同的输入输出集合;只看模型“总共有几个输入”并不能判断某一次调用需要哪些参数。

并发边界:同一个 interpreter 不要让 runner 同时执行

签名调用解决的是参数绑定,不等于自动获得线程安全。官方 LiteRT 签名文档明确提示,同一个 interpreter 的 signature runner 不保证可以并发执行。服务端或多线程场景应为并发请求设计清晰的 interpreter 生命周期,例如每个工作单元使用独立实例,或在共享实例外层建立可靠的串行保护。

import threading

interpreter_lock = threading.Lock()

def infer_one(runner, image, scale):
    # 共享 interpreter 时,把一次完整调用放在同一个临界区。
    with interpreter_lock:
        return runner(image=image, scale=scale)

# 若需要更高并发,应评估创建多个 interpreter,而不是移除这把锁。

这段代码表达的是并发边界,不是性能结论。是否使用锁、实例池还是请求串行化,取决于模型大小、设备资源和服务的延迟目标;本文不把某一种部署方式当成通用答案。

总结:把签名当成多输入模型的调用契约

遇到 LiteRT 多输入调用问题时,按“读取清单—确认入口—准备张量—按名调用—按名取结果—复查并发”的顺序处理。只要不把 tensor index、数组顺序和签名名称混为一谈,绝大多数名称绑定问题都能在调用前定位。

常见问题

必须先调用 allocate_tensors() 才能使用 signature runner 吗?

本文的重点是签名名称绑定;是否需要额外分配步骤要以你所用 LiteRT API 和运行时版本的官方接口为准。先完成签名清单读取,再按照对应 API 的生命周期要求初始化解释器。

输入名称可以直接写成 tensor index 吗?

不可以。签名 runner 接收的是签名定义的输入名称;内部 index 只是模型实现层信息,不能替代调用契约。

多输入的顺序需要和模型转换时一致吗?

应用调用应按名称绑定,而不是依赖参数出现顺序。真正需要一致的是每个名称对应的数据形状、类型和语义。

为什么输出字典里没有我预期的字段?

先打印当前 signature 的 outputs,再确认选中的入口是否正确。不同 signature 可能暴露不同输出名称,不要从另一个入口复制字段名。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>