PAYATHON 2026

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 流式推理教程说明了这种调用方式。

迁移时应检查:

  1. 首帧之前如何初始化全部状态输入,未经确认不要全部填零。
  2. 每次推理后,如何将状态输出对应到下一帧的状态输入。
  3. 输出是否包含后续推理需要的全部状态,而不仅是分类结果。
  4. 开始新视频流时,是否重置了上一段视频的状态。

原问题没有提供具体模型文件、signature 定义和依赖版本,因此无法直接给出该模型的输入输出索引表。需要先确认目标 signature 的子图及状态映射,才能判断按索引调用是否适用。

备注:内容仅供参考。