可视化 TFLite 图并获取特定节点的中间值?
Visualize TFLite graph and get intermediate values of a particular node?
我想知道是否有办法知道 tflite 中特定节点的输入和输出列表?我知道我可以获得 input/outputs 详细信息,但这不允许我重建 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
肯定知道这一点。我们可以吗?谢谢
Note: this answer was written for Tensorflow 1.x and, while the concept and core idea remains the same in TensorFlow 2.x, the commands in this answer might be deprecated.
TF-Lite的机制使得整个检查图和获取内部节点中间值的过程有点棘手。其他答案建议的 get_tensor(...)
方法不起作用。
如何可视化TF-Lite推理图?
可以使用 visualize.py script in the TensorFlow Lite repository 可视化 TensorFlow Lite 模型。您只需要:
-
运行 visualize.py
带有 bazel 的脚本:
bazel run //tensorflow/lite/tools:visualize \
model.tflite \
visualized_model.html
我的 TF 模型中的节点是否在 TF-Lite 中具有等效节点?
NO! 事实上,TF-Lite 可以修改您的图表,使其变得更加优化。以下是 TF-Lite documentation:
中关于它的一些说法
A number of TensorFlow operations can be processed by TensorFlow Lite even though they have no direct equivalent. This is the case for operations that can be simply removed from the graph (tf.identity), replaced by tensors (tf.placeholder), or fused into more complex operations (tf.nn.bias_add). Even some supported operations may sometimes be removed through one of these processes.
此外,TF-Lite API目前不允许获取节点对应关系;很难解释 TF-Lite 的内部格式。因此,即使没有下面的另一个问题,您也无法获得所需任何节点的中间输出...
我可以获取某些 TF-Lite 个节点的中间值吗?
NO! 在这里,我将解释为什么 get_tensor(...)
在 TF-Lite 中不起作用。假设在内部表示中,该图包含 3 个张量,以及一些密集操作(节点)in-between(您可以将 tensor1
视为模型的输入,将 tensor3
视为模型的输出) .在这个特定图形的推理过程中,TF-Lite 仅 需要 2 个缓冲区,让我们展示如何。
首先,使用tensor1
通过应用dense
运算计算tensor2
。这只需要 2 个缓冲区来存储值:
dense dense
[tensor1] -------> [tensor2] -------> [tensor3]
^^^^^^^ ^^^^^^^
bufferA bufferB
其次,使用存储在bufferB
中的tensor2
的值来计算tensor3
...但是等等!我们不再需要 bufferA
,所以让我们用它来存储 tensor3
:
的值
dense dense
[tensor1] -------> [tensor2] -------> [tensor3]
^^^^^^^ ^^^^^^^
bufferB bufferA
现在是棘手的部分。 tensor1
的“输出值”仍将指向 bufferA
,它现在包含 tensor3
的值。因此,如果您为第一个张量调用 get_tensor(...)
,您将得到不正确的值。 此方法的文档甚至指出:
This function cannot be used to read intermediate results.
如何解决这个问题?
简单但有限的方法。您可以指定节点的名称,输出张量,您希望在转换过程中获取其值:
tflite_convert \
-- # other options of your model
--output_arrays="output_node,intermediate/node/n1,intermediate/node/n2"
困难但灵活的方法。 你可以用 Bazel 编译 TF-Lite(使用 this instruction)。然后你实际上可以在文件 tensorflow/lite/interpreter.cc
中向 Interpreter::Invoke()
注入一些日志记录代码。一个丑陋的 hack,但它有效。
正如@FalconUA 所指出的,我们无法直接从 TFlite 模型中获取中间输入和输出。但是,我们可以通过修改模型缓冲区来获取层的输入和输出。 This repo 展示了它是如何完成的。我们需要修改平面缓冲区架构才能使其正常工作。修改后的 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
我想知道是否有办法知道 tflite 中特定节点的输入和输出列表?我知道我可以获得 input/outputs 详细信息,但这不允许我重建 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
肯定知道这一点。我们可以吗?谢谢
Note: this answer was written for Tensorflow 1.x and, while the concept and core idea remains the same in TensorFlow 2.x, the commands in this answer might be deprecated.
TF-Lite的机制使得整个检查图和获取内部节点中间值的过程有点棘手。其他答案建议的 get_tensor(...)
方法不起作用。
如何可视化TF-Lite推理图?
可以使用 visualize.py script in the TensorFlow Lite repository 可视化 TensorFlow Lite 模型。您只需要:
运行
visualize.py
带有 bazel 的脚本:bazel run //tensorflow/lite/tools:visualize \ model.tflite \ visualized_model.html
我的 TF 模型中的节点是否在 TF-Lite 中具有等效节点?
NO! 事实上,TF-Lite 可以修改您的图表,使其变得更加优化。以下是 TF-Lite documentation:
中关于它的一些说法A number of TensorFlow operations can be processed by TensorFlow Lite even though they have no direct equivalent. This is the case for operations that can be simply removed from the graph (tf.identity), replaced by tensors (tf.placeholder), or fused into more complex operations (tf.nn.bias_add). Even some supported operations may sometimes be removed through one of these processes.
此外,TF-Lite API目前不允许获取节点对应关系;很难解释 TF-Lite 的内部格式。因此,即使没有下面的另一个问题,您也无法获得所需任何节点的中间输出...
我可以获取某些 TF-Lite 个节点的中间值吗?
NO! 在这里,我将解释为什么 get_tensor(...)
在 TF-Lite 中不起作用。假设在内部表示中,该图包含 3 个张量,以及一些密集操作(节点)in-between(您可以将 tensor1
视为模型的输入,将 tensor3
视为模型的输出) .在这个特定图形的推理过程中,TF-Lite 仅 需要 2 个缓冲区,让我们展示如何。
首先,使用tensor1
通过应用dense
运算计算tensor2
。这只需要 2 个缓冲区来存储值:
dense dense
[tensor1] -------> [tensor2] -------> [tensor3]
^^^^^^^ ^^^^^^^
bufferA bufferB
其次,使用存储在bufferB
中的tensor2
的值来计算tensor3
...但是等等!我们不再需要 bufferA
,所以让我们用它来存储 tensor3
:
dense dense
[tensor1] -------> [tensor2] -------> [tensor3]
^^^^^^^ ^^^^^^^
bufferB bufferA
现在是棘手的部分。 tensor1
的“输出值”仍将指向 bufferA
,它现在包含 tensor3
的值。因此,如果您为第一个张量调用 get_tensor(...)
,您将得到不正确的值。 此方法的文档甚至指出:
This function cannot be used to read intermediate results.
如何解决这个问题?
简单但有限的方法。您可以指定节点的名称,输出张量,您希望在转换过程中获取其值:
tflite_convert \ -- # other options of your model --output_arrays="output_node,intermediate/node/n1,intermediate/node/n2"
困难但灵活的方法。 你可以用 Bazel 编译 TF-Lite(使用 this instruction)。然后你实际上可以在文件
tensorflow/lite/interpreter.cc
中向Interpreter::Invoke()
注入一些日志记录代码。一个丑陋的 hack,但它有效。
正如@FalconUA 所指出的,我们无法直接从 TFlite 模型中获取中间输入和输出。但是,我们可以通过修改模型缓冲区来获取层的输入和输出。 This repo 展示了它是如何完成的。我们需要修改平面缓冲区架构才能使其正常工作。修改后的 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