我想知道是否有办法知道 tflite 中特定节点的输入和输出列表?我知道我可以获得输入/输出详细信息,但这不允许我重建
Interpreter
内发生的计算过程。所以我要做的是:
interpreter = tf.lite.Interpreter(model_path=model_path)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
interpreter.get_tensor_details()
最后 3 个命令基本上给了我字典,但似乎没有必要的信息。
所以我想知道是否有办法知道每个节点的输出去了哪里?
Interpreter
肯定知道这一点。我们可以吗?谢谢。
注意:这个答案是为 Tensorflow 1.x 编写的,虽然概念和核心思想在 TensorFlow 2.x 中保持不变,但这个答案中的命令可能已被弃用。
TF-Lite 的机制使得检查图和获取内部节点中间值的整个过程变得有点棘手。其他答案建议的
get_tensor(...)
方法不起作用。
TensorFlow Lite 模型可以使用 TensorFlow Lite 存储库中的 visualize.py 脚本进行可视化。你只需要:
使用 bazel 运行
visualize.py
脚本:
bazel run //tensorflow/lite/tools:visualize \
model.tflite \
visualized_model.html
NO! 事实上,TF-Lite 可以修改您的图表,使其变得更加优化。以下是来自 TF-Lite 文档的一些相关内容:
许多 TensorFlow 操作都可以由 TensorFlow Lite 处理,尽管它们没有直接的等效项。这种情况可以简单地从图中删除(tf.identity)、用张量(tf.placeholder)替换或融合到更复杂的操作(tf.nn.bias_add)中。即使某些受支持的操作有时也可能通过这些过程之一被删除。
此外,TF-Lite API目前不允许获取节点对应关系; TF-Lite 的内部格式很难解释。因此,即使没有下面的另一个问题,您也无法获得所需的任何节点的中间输出......
get_tensor(...)
在 TF-Lite 中不起作用。假设在内部表示中,图包含 3 个张量,以及中间的一些密集操作(节点)(您可以将 tensor1
视为模型的输入,将 tensor3
视为模型的输出)。在这个特定图的推理过程中,TF-Lite 仅需要 2 个缓冲区,让我们展示一下如何实现。
首先,使用
tensor1
通过应用tensor2
运算来计算dense
。这只需要 2 个缓冲区来存储值:
dense dense
[tensor1] -------> [tensor2] -------> [tensor3]
^^^^^^^ ^^^^^^^
bufferA bufferB
其次,使用存储在
tensor2
中的bufferB
的值来计算tensor3
...但是等等!我们不再需要 bufferA
,所以让我们用它来存储 tensor3
的值:
dense dense
[tensor1] -------> [tensor2] -------> [tensor3]
^^^^^^^ ^^^^^^^
bufferB bufferA
现在是棘手的部分。
tensor1
的“输出值”仍将指向 bufferA
,它现在保存 tensor3
的值。因此,如果您为第一个张量调用 get_tensor(...)
,您将得到不正确的值。该方法的文档甚至指出:
该函数不能用于读取中间结果。
简单但有限的方法。您可以指定节点的名称,以及您想要在转换过程中获取其值的输出张量:
tflite_convert \
-- # other options of your model
--output_arrays="output_node,intermediate/node/n1,intermediate/node/n2"
困难但灵活的方式。您可以使用Bazel编译TF-Lite(使用此指令)。然后,您实际上可以将一些日志记录代码注入到文件
Interpreter::Invoke()
中的 tensorflow/lite/interpreter.cc
中。一个丑陋的黑客,但它有效。
正如 @FalconUA 所指出的,我们无法直接从 TFlite 模型获取中间输入和输出。但是,我们可以通过修改模型缓冲区来获取各层的输入和输出。 这个存储库展示了它是如何完成的。我们需要修改平面缓冲区模式才能使其工作。修改后的 TFlite 架构(存储库中的
tflite
文件夹)可在存储库中找到。
为了答案的完整性,以下是相关代码:
def buffer_change_output_tensor_to(model_buffer, new_tensor_i):
# from https://github.com/raymond-li/tflite_tensor_outputter
# Set subgraph 0's output(s) to new_tensor_i
# Reads model_buffer as a proper flatbuffer file and gets the offset programatically
# It might be much more efficient if Model.subgraphs[0].outputs[] was set to a list of all the tensor indices.
fb_model_root = tflite_model.Model.GetRootAsModel(model_buffer, 0)
output_tensor_index_offset = fb_model_root.Subgraphs(0).OutputsOffset(0) # Custom added function to return the file offset to this vector
# print("buffer_change_output_tensor_to. output_tensor_index_offset: ")
# print(output_tensor_index_offset)
# output_tensor_index_offset = 0x5ae07e0 # address offset specific to inception_v3.tflite
# output_tensor_index_offset = 0x16C5A5c # address offset specific to inception_v3_quant.tflite
# Flatbuffer scalars are stored in little-endian.
new_tensor_i_bytes = bytes([
new_tensor_i & 0x000000FF, \
(new_tensor_i & 0x0000FF00) >> 8, \
(new_tensor_i & 0x00FF0000) >> 16, \
(new_tensor_i & 0xFF000000) >> 24 \
])
# Replace the 4 bytes corresponding to the first output tensor index
return model_buffer[:output_tensor_index_offset] + new_tensor_i_bytes + model_buffer[output_tensor_index_offset + 4:]
def get_tensor(path_tflite, tensor_id):
with open(path_tflite, 'rb') as fp:
model_buffer = fp.read()
model_buffer = buffer_change_output_tensor_to(model_buffer, int(tensor_id))
interpreter = tf.lite.Interpreter(model_content=model_buffer)
interpreter.allocate_tensors()
tensor_details = interpreter._get_tensor_details(tensor_id)
tensor_name = tensor_details['name']
input_details = interpreter.get_input_details()
interpreter.set_tensor(input_details[0]['index'], input_tensor)
interpreter.invoke()
tensor = interpreter.get_tensor(tensor_id)
return tensor
从最新版本的tensorflow开始,我们有一个实验参数
experimental_preserve_all_tensors
到tf.lite.Interpreter
。此外,您可能希望迭代 interpreter._get_ops_details()
,它返回以下格式的 dict
列表:
op: {'index': 2, 'op_name': 'CONV_2D', 'inputs': array([15, 4, 5], dtype=int32), 'outputs': array([16], dtype=int32)}
这意味着,您可以在索引
16
处找到转换层的中间输出,您可以通过像这样访问它来获得它
layer_index = dict_iter['outputs'][0]
现在获取实际的图层输出:
interpreter.get_tensor(layer_index)