【问题标题】:tflite: get_tensor on non-output tensors gives random valuestflite:非输出张量上的 get_tensor 给出随机值
【发布时间】:2019-04-06 04:35:57
【问题描述】:

我正在尝试调试我的 tflite 模型,它使用自定义操作。我找到了操作名称(*.pb)和操作 ID(*.tflite)之间的对应关系,并且我正在进行逐层比较(以确保输出差异始终在@987654324 范围内@(因为最后炸了,我想找到我的自定义层失败的确切位置)如下:


方法一:我使用get_tensor得到输出如下:

from tensorflow.contrib.lite.python import interpreter

# load the model
model = interpreter.Interpreter(model_path='model.tflite')
model.allocate_tensors()

# get tensors
for i in tensor_ids:
    tensor_output[i] = model.get_tensor(i)

它显示完全不充分的随机值(与 TensorFlow 模型的输出相比)。


方法2:*.pb只转换到某一层,然后重复,基本上:

  1. 创建一个*.pb,使其仅包含从inputlayer_1 的网络。

  2. 转换为tflite(因此输出现在为layer_1)并使用TensorFlow检查TF-Lite的输出。

  3. layer_2layer_3、...outputs 重复步骤 1-2。

这种方法需要更多的工作和执行,但它正确地表明,对于内置操作,tflitepb 模型的输出是相同的,只是在我的自定义操作中开始有所不同(而在 方法 1 输出立即从第一层发散)。


问题:为什么get_tensor的行为如此奇怪?可能是因为我用的是tensorflow 1.9(当时TF-Lite还没有发布,只在开发者预览版中可用)?

PS:我知道 TF-Lite 的发布,但我已经为我的项目手动编译了 TensorFlow 1.9,现在很难更改版本。

【问题讨论】:

    标签: python tensorflow keras tensorflow-lite


    【解决方案1】:

    几个月前我遇到了同样的问题。问题是,TF-Lite 与 TensorFlow 完全不同——它使用静态内存和执行计划、内存映射文件以加快加载速度,并且它应该优化网络前向传播管道中的一切可能。

    我不是 TF-Lite 的开发人员,但我认为它通过重新使用用于先前计算的操作的内存区域来保持其内存占用极低。让我们看看下图的想法:


    第 1 步:首先,我们将输入提供给符号张量 I(在括号中)。假设它的值存储在名为buffer_1 的缓冲区中。

         op1       op2       op3
    (I) ---->  A  ---->  B  ---->  O
    _________________________________
    ^^^        ^^^^^^^^^^^^       ^^^
    input      intermediate    output
    tensor     tensors         tensor
    

    第 2 步: 现在,我们需要在符号张量 I 上计算 op1 以获得符号张量 A。我们在 buffer_1 上进行计算,并将符号张量 A 的值存储在名为 buffer_2 的缓冲区中。

        [op1]      op2       op3
    (I) ----> (A) ---->  B  ---->  O
    

    第 3 步:现在,我们在符号张量 A 上计算 op2 以获得符号张量 B。我们在 buffer_2 上进行计算,并将符号张量 B 的值存储在名为 buffer_3 的缓冲区中......

         op1      [op2]      op3
     I  ----> (A) ----> (B) ---->  O
    

    但是等等!如果我们现在有未使用的buffer_1,并且现在它的值对于获取输出O 无用,为什么还要浪费我们的内存来存储buffer_3?所以,我们实际上将这个操作的结果存储在buffer_1中,而不是存储在buffer_3中!

    这是高效内存重用的基本思想,我认为这是在 TF-Lite 中实现的,因为它在 toco 和其他东西中内置了静态图形分析器。这就是为什么你不能简单地将get_tensor 应用于非输出张量。


    更简单的调试方法?

    你提到你正在编写一个自定义操作,所以我想你已经用bazel 构建了tflite,对吧?然后,您实际上可以在文件tensorflow/lite/interpreter.cc 中向Interpreter::Invoke() 注入一些日志记录代码。一个丑陋的黑客,但它有效。

    PS:如果有 TensorFlow Lite 开发人员遇到并对此发表评论,我会很高兴 :)

    【讨论】:

      【解决方案2】:

      是的,中间张量可以被覆盖,除非指定为输出。

      编辑: 我设法通过在转换期间使所有操作都在输出列表中来解决问题。然后在运行时保存它们,并且可以正确读取值。

      见:

      Obtaining quantized activations in tensorflow lite

      【讨论】:

      • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 how to answer
      【解决方案3】:

      我在想将 TFLite 文件转换为另一个框架时遇到了类似的问题,而无法访问用于制作 TFLite 文件的原始 TF 图。因为我的转换输出与 TFLite 模型的输出不同,所以我想查看中间层的输出。感谢这个关于 SO 的话题,我了解到get_tensor() 不是一种可靠的方法。

      最简单的解决方案是在十六进制编辑器中编辑 TFLite 文件!

      模型的输出是模型中张量之一的索引。在我的例子中是张量 175(你可以用 get_tensor_details() 看到这个。它作为 little-endian int32 存储在 TFLite 文件的某处。对于张量 175,TFLite 值将包含一个值 0xAF000000。

      我希望模型输出改用张量 3,因此我在十六进制编辑器中打开了 TFLite 文件,搜索了 0xAF000000,并将其替换为 0x03000000。保存文件并使用 TFLite 解释器再次加载它。奇迹般有效。您只需要注意该文件可能包含多次出现的 0xAF000000 (或您要查找的任何内容)。在我的 TFLite 文件中,它存储在末尾附近。

      我希望这个技巧对某人有用。 :-)

      【讨论】:

        【解决方案4】:

        默认情况下,TFLite 不保留中间张量,这是因为它优化内存使用并根据数据流依赖性重用张量的分配内存。 您可以使用新添加的调试功能来保留所有张量

        interpreter = tf.lite.Interpreter(
            model_path="test.tflite",
            experimental_preserve_all_tensors=True)
        

        现在您可以在此解释器上检查中间张量。

        【讨论】:

          【解决方案5】:

          卡里姆的回答

          解释器 = tf.lite.Interpreter( model_path="test.tflite", Experimental_preserve_all_tensors=True)

          是最直接的解决方案,但你必须在 tensorflow>=2.5.0 上。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-14
            • 2021-11-29
            • 2015-03-08
            • 1970-01-01
            • 2020-12-28
            相关资源
            最近更新 更多