LiteRT InterpreterApi 如何使用 TFLite signatures?
结论
InterpreterApi 没有直接调用 TFLite signatures 的 Java 方法,因此无法将原来的 runSignature() 原样迁移过去。官方将 InterpreterApi 定义为不包含实验性方法的推理接口;runSignature() 则列在 Interpreter 类中,标注为实验性 API。参见 InterpreterApi 文档和 Interpreter 文档。
可行方案取决于模型结构:如果目标 signature 对应主子图,可以改用按输入、输出索引调用;如果需要执行其他子图中的 signature,则应保留支持 runSignature() 的内置运行时,或重新导出模型。
为什么不能直接替换方法
runSignature(inputs, outputs, "serving_default") 使用 signature 中的输入、输出名称,并通过 signature key 选择入口。
runForMultipleInputsOutputs() 接收按位置排列的输入数组,以及以输出位置为键的 Map。它没有选择 signature key 的参数。
TensorFlow 的实现源码区分了这两种情况:目标 signature 位于子图 0 时,先将名称映射为输入、输出位置,再执行普通推理;位于其他子图时,则通过 signature runner 执行。替换调用前需要确认子图和张量映射,不能仅凭 serving_default 这个名称判断。
方案一:将主子图调用改为按索引执行
先检查实际的 .tflite 文件,确认目标 signature 对应主子图,再建立 signature 名称、实际张量名称和输入输出位置之间的映射。
需要注意两点:
- signature 中的名称不一定等于
Tensor.name()返回的名称。 runForMultipleInputsOutputs()使用的是输入、输出列表中的位置,不是模型内部的全局 tensor index。
下面的辅助方法适用于已确认目标入口为主子图的情况。调用者需要提供实际张量名称,以及准备好的数据或输出缓冲区,不能直接假定原来的 signature Map 可以复用。
import java.util.HashMap;
import java.util.Map;
import org.tensorflow.lite.InterpreterApi;
static void runMainGraph(
InterpreterApi interpreter,
Map<String, Object> inputsByTensorName,
Map<String, Object> outputsByTensorName) {
Object[] inputs = new Object[interpreter.getInputTensorCount()];
for (int i = 0; i < inputs.length; i++) {
String name = interpreter.getInputTensor(i).name();
Object value = inputsByTensorName.get(name);
if (value == null) {
throw new IllegalArgumentException("Missing input: " + name);
}
inputs[i] = value;
}
Map<Integer, Object> outputs = new HashMap<>();
for (Map.Entry<String, Object> entry : outputsByTensorName.entrySet()) {
int index = interpreter.getOutputIndex(entry.getKey());
outputs.put(index, entry.getValue());
}
interpreter.runForMultipleInputsOutputs(inputs, outputs);
}
该方法只完成名称到位置的组织,不会选择 signature,也不会自动处理模型预处理、状态初始化或张量形状变化。
使用 Buffer 时,要确保容量、数据类型和读写位置正确。需要改变输入形状时,应先调用 resizeInput(),必要时调用 allocateTensors(),再根据实际输出形状准备缓冲区。详见 InterpreterApi 参数说明。
方案二:继续使用支持 signatures 的内置运行时
如果模型需要多个入口,或者目标 signature 不在主子图,可以保留原有的内置 Interpreter,继续按名称调用:
import org.tensorflow.lite.Interpreter;
// modelFile、inputs、outputs 由应用准备。
try (Interpreter interpreter = new Interpreter(modelFile)) {
interpreter.runSignature(inputs, outputs, "serving_default");
}
这需要实际创建内置运行时的 Interpreter。不能通过将 Play Services 返回的 InterpreterApi 强制转换为 Interpreter 来增加 signature 支持。官方的 signatures Java 示例也使用 Interpreter 执行这类调用。
如果必须保留 Play Services 的 InterpreterApi,可以在具备原始模型导出条件时,将需要的入口重新导出为独立模型,使其成为可直接调用的主子图。重新导出后仍需检查算子兼容性和状态传递方式。
MoViNet 逐帧推理需要保留哪些处理
MoViNet 流式模型通常同时接收当前帧和上一轮状态,并返回预测结果及更新后的状态。官方 MoViNet 流式推理教程说明了这种调用方式。
迁移时应检查:
- 首帧之前如何初始化全部状态输入,未经确认不要全部填零。
- 每次推理后,如何将状态输出对应到下一帧的状态输入。
- 输出是否包含后续推理需要的全部状态,而不仅是分类结果。
- 开始新视频流时,是否重置了上一段视频的状态。
原问题没有提供具体模型文件、signature 定义和依赖版本,因此无法直接给出该模型的输入输出索引表。需要先确认目标 signature 的子图及状态映射,才能判断按索引调用是否适用。
备注:内容仅供参考。
