我正在尝试调试使用自定义操作的tflite
模型。我发现操作名称(在*.pb
中和操作ID(在*.tflite
中)之间的对应关系,并且正在进行逐层比较(以确保输出差异始终是在范围1e-4
中(由于它最终会炸毁,我想找到我的自定义层失败的确切位置),如下所示:
方法1:我使用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
转换到特定层,然后重复进行,基本上:
创建一个*.pb
,使其仅包含从input
到layer_1
的网络。
转换为tflite
(现在输出为layer_1
)并使用TensorFlow检查TF-Lite的输出。
为layer_2
,layer_3
,... outputs
重复步骤1-2。
此方法需要更多的工作和执行,但是正确地表明,对于内置操作,tflite
和pb
模型的输出是相同的,并且在我的自定义操作中只是开始有所不同(而在方法1 中,输出从第一层开始就发散了。
问题:为什么
get_tensor
的行为如此奇怪?也许是因为我正在使用tensorflow 1.9
(当TF-Lite仍未发布且仅在开发人员预览中可用时)?
PS:我知道TF-Lite的发布,但是我已经为我的项目手动编译了TensorFlow 1.9,现在很难更改版本。
答案 0 :(得分:1)
几个月前,我遇到了同样的问题。事实是,TF-Lite与TensorFlow完全不同–它使用静态内存和执行计划,内存映射文件以加快加载速度,并且应该优化网络前向传播管道中的所有可能。
我不是TF-Lite的开发人员,但我想它通过重新使用先前计算出的操作所占用的内存区域来使内存占用极低。让我们在下图上看到这个想法:
第1步:首先,我们将输入馈送到符号张量I
(在括号中)。假设它的值存储在名为buffer_1
的缓冲区中。
op1 op2 op3
(I) ----> A ----> B ----> O
_________________________________
^^^ ^^^^^^^^^^^^ ^^^
input intermediate output
tensor tensors tensor
步骤2:现在,我们需要在符号张量op1
上计算I
,以获得符号张量A
。我们在buffer_1
上进行计算,并将符号张量A
的值存储在名为buffer_2
的缓冲区中。
[op1] op2 op3
(I) ----> (A) ----> B ----> O
第3步:现在,我们在符号张量op2
上计算A
,以获得符号张量B
。我们在buffer_2
上进行计算,并将符号张量B
的值存储在名为buffer_3
...
op1 [op2] op3
I ----> (A) ----> (B) ----> O
但是等等!如果我们现在有buffer_3
未使用,并且其值现在对于获取输出无效,那么为什么浪费我们的内存来存储在buffer_1
中O
?因此,我们实际上将存储此操作的结果而不是存储在buffer_3
中!
这是有效的内存重用的基本思想,我认为它是在TF-Lite中实现的,这要归功于buffer_1
中内置的静态图形分析器和其他功能。这就是为什么您不能简单地在非输出张量上应用toco
。
一种更简便的调试方法?
您提到您正在编写自定义操作,所以我想您已经用get_tensor
构建了tflite
,对吗?然后,您实际上可以向文件bazel
中的Interpreter::Invoke()
注入一些日志记录代码。难看的骇客,但行得通。
PS:如果有任何TensorFlow Lite开发人员遇到并对此发表评论,我将感到高兴:)
答案 1 :(得分:0)
是的,除非指定为输出,否则中间张量可以被覆盖。
编辑: 我设法通过在转换过程中将所有操作都放在输出列表中来解决此问题。然后将它们保存在运行时,并且可以正确读取值。
请参阅:
答案 2 :(得分:0)
我想将一个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文件中,它存储在末尾。
我希望这个技巧对某人有用。 :-)
答案 3 :(得分:0)
默认情况下,TFLite 不保留中间张量,这是因为它优化了内存使用并根据数据流依赖性重用了张量的已分配内存。 您可以使用新添加的调试功能来保留所有张量
interpreter = tf.lite.Interpreter(
model_path="test.tflite",
experimental_preserve_all_tensors=True)
现在您可以在此解释器上检查中间张量。
答案 4 :(得分:0)
卡里姆的回答
<块引用>解释器 = tf.lite.Interpreter( model_path="test.tflite", Experiment_preserve_all_tensors=True)
是最直接的解决方案,但您必须使用 tensorflow>=2.5.0。